1use crate::application::Application;
10use crate::texture::render_texture::RenderTexture;
11
12pub(crate) struct BlitPipeline {
13 pipeline: wgpu::RenderPipeline,
16 pipeline_over: wgpu::RenderPipeline,
20 bind_group_layout: wgpu::BindGroupLayout,
21 sampler: wgpu::Sampler,
22}
23
24impl BlitPipeline {
25 pub(crate) fn new(app: &Application, output_format: wgpu::TextureFormat) -> Self {
26 let device = app.device();
27
28 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
29 label: Some("wisp::blit bg layout"),
30 entries: &[
31 wgpu::BindGroupLayoutEntry {
32 binding: 0,
33 visibility: wgpu::ShaderStages::FRAGMENT,
34 ty: wgpu::BindingType::Texture {
35 sample_type: wgpu::TextureSampleType::Float { filterable: true },
36 view_dimension: wgpu::TextureViewDimension::D2,
37 multisampled: false,
38 },
39 count: None,
40 },
41 wgpu::BindGroupLayoutEntry {
42 binding: 1,
43 visibility: wgpu::ShaderStages::FRAGMENT,
44 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
45 count: None,
46 },
47 ],
48 });
49
50 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
51 label: Some("wisp::blit pipeline layout"),
52 bind_group_layouts: &[&bind_group_layout],
53 push_constant_ranges: &[],
54 });
55
56 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
57 label: Some("wisp::blit shader"),
58 source: wgpu::ShaderSource::Wgsl(include_str!("../../shaders/blit.wgsl").into()),
59 });
60
61 let make_pipeline = |label: &str, blend: wgpu::BlendState| {
62 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
63 label: Some(label),
64 layout: Some(&pipeline_layout),
65 vertex: wgpu::VertexState {
66 module: &shader,
67 entry_point: Some("main_vs"),
68 buffers: &[],
69 compilation_options: wgpu::PipelineCompilationOptions::default(),
70 },
71 fragment: Some(wgpu::FragmentState {
72 module: &shader,
73 entry_point: Some("main_fs"),
74 targets: &[Some(wgpu::ColorTargetState {
75 format: output_format,
76 blend: Some(blend),
77 write_mask: wgpu::ColorWrites::ALL,
78 })],
79 compilation_options: wgpu::PipelineCompilationOptions::default(),
80 }),
81 primitive: wgpu::PrimitiveState::default(),
82 depth_stencil: None,
83 multisample: wgpu::MultisampleState::default(),
84 multiview: None,
85 cache: None,
86 })
87 };
88 let pipeline = make_pipeline("wisp::blit pipeline", wgpu::BlendState::REPLACE);
89 let pipeline_over = make_pipeline(
90 "wisp::blit pipeline (over)",
91 wgpu::BlendState::ALPHA_BLENDING,
92 );
93
94 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
95 label: Some("wisp::blit sampler"),
96 address_mode_u: wgpu::AddressMode::ClampToEdge,
97 address_mode_v: wgpu::AddressMode::ClampToEdge,
98 address_mode_w: wgpu::AddressMode::ClampToEdge,
99 mag_filter: wgpu::FilterMode::Linear,
100 min_filter: wgpu::FilterMode::Linear,
101 mipmap_filter: wgpu::FilterMode::Nearest,
102 ..Default::default()
103 });
104
105 Self {
106 pipeline,
107 pipeline_over,
108 bind_group_layout,
109 sampler,
110 }
111 }
112
113 pub(crate) fn blit(
116 &self,
117 app: &Application,
118 src: &RenderTexture,
119 target_view: &wgpu::TextureView,
120 ) {
121 self.run(app, src, target_view, &self.pipeline, true);
122 }
123
124 pub(crate) fn compose_over(
129 &self,
130 app: &Application,
131 src: &RenderTexture,
132 target_rt: &RenderTexture,
133 ) {
134 self.run(
135 app,
136 src,
137 target_rt.view(),
138 &self.pipeline_over,
139 false,
140 );
141 }
142
143 fn run(
144 &self,
145 app: &Application,
146 src: &RenderTexture,
147 target_view: &wgpu::TextureView,
148 pipeline: &wgpu::RenderPipeline,
149 clear: bool,
150 ) {
151 let bg = app.device().create_bind_group(&wgpu::BindGroupDescriptor {
152 label: Some("wisp::blit bg"),
153 layout: &self.bind_group_layout,
154 entries: &[
155 wgpu::BindGroupEntry {
156 binding: 0,
157 resource: wgpu::BindingResource::TextureView(src.view()),
158 },
159 wgpu::BindGroupEntry {
160 binding: 1,
161 resource: wgpu::BindingResource::Sampler(&self.sampler),
162 },
163 ],
164 });
165
166 let mut encoder = app
167 .device()
168 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
169 label: Some("wisp::blit encoder"),
170 });
171 {
172 let load = if clear {
173 wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT)
174 } else {
175 wgpu::LoadOp::Load
176 };
177 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
178 label: Some("wisp::blit pass"),
179 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
180 view: target_view,
181 resolve_target: None,
182 ops: wgpu::Operations {
183 load,
184 store: wgpu::StoreOp::Store,
185 },
186 })],
187 depth_stencil_attachment: None,
188 timestamp_writes: None,
189 occlusion_query_set: None,
190 });
191 pass.set_pipeline(pipeline);
192 pass.set_bind_group(0, &bg, &[]);
193 pass.draw(0..3, 0..1);
194 }
195 app.queue().submit(std::iter::once(encoder.finish()));
196 }
197}