iced_wgpu/triangle/
msaa.rs

1use crate::core::{Size, Transformation};
2use crate::graphics;
3
4use std::num::NonZeroU64;
5use std::sync::{Arc, RwLock};
6
7#[derive(Debug, Clone)]
8pub struct Pipeline {
9    format: wgpu::TextureFormat,
10    sampler: wgpu::Sampler,
11    raw: wgpu::RenderPipeline,
12    constant_layout: wgpu::BindGroupLayout,
13    texture_layout: wgpu::BindGroupLayout,
14    sample_count: u32,
15    targets: Arc<RwLock<Option<Targets>>>,
16}
17
18impl Pipeline {
19    pub fn new(
20        device: &wgpu::Device,
21        format: wgpu::TextureFormat,
22        antialiasing: graphics::Antialiasing,
23    ) -> Pipeline {
24        let sampler =
25            device.create_sampler(&wgpu::SamplerDescriptor::default());
26
27        let constant_layout =
28            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
29                label: Some("iced_wgpu::triangle:msaa uniforms layout"),
30                entries: &[
31                    wgpu::BindGroupLayoutEntry {
32                        binding: 0,
33                        visibility: wgpu::ShaderStages::FRAGMENT,
34                        ty: wgpu::BindingType::Sampler(
35                            wgpu::SamplerBindingType::NonFiltering,
36                        ),
37                        count: None,
38                    },
39                    wgpu::BindGroupLayoutEntry {
40                        binding: 1,
41                        visibility: wgpu::ShaderStages::VERTEX,
42                        ty: wgpu::BindingType::Buffer {
43                            ty: wgpu::BufferBindingType::Uniform,
44                            has_dynamic_offset: false,
45                            min_binding_size: None,
46                        },
47                        count: None,
48                    },
49                ],
50            });
51
52        let texture_layout =
53            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
54                label: Some("iced_wgpu::triangle::msaa texture layout"),
55                entries: &[wgpu::BindGroupLayoutEntry {
56                    binding: 0,
57                    visibility: wgpu::ShaderStages::FRAGMENT,
58                    ty: wgpu::BindingType::Texture {
59                        sample_type: wgpu::TextureSampleType::Float {
60                            filterable: false,
61                        },
62                        view_dimension: wgpu::TextureViewDimension::D2,
63                        multisampled: false,
64                    },
65                    count: None,
66                }],
67            });
68
69        let layout =
70            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
71                label: Some("iced_wgpu::triangle::msaa pipeline layout"),
72                push_constant_ranges: &[],
73                bind_group_layouts: &[&constant_layout, &texture_layout],
74            });
75
76        let shader =
77            device.create_shader_module(wgpu::ShaderModuleDescriptor {
78                label: Some("iced_wgpu triangle blit_shader"),
79                source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
80                    include_str!("../shader/blit.wgsl"),
81                )),
82            });
83
84        let pipeline =
85            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
86                label: Some("iced_wgpu::triangle::msaa pipeline"),
87                layout: Some(&layout),
88                vertex: wgpu::VertexState {
89                    module: &shader,
90                    entry_point: Some("vs_main"),
91                    buffers: &[],
92                    compilation_options:
93                        wgpu::PipelineCompilationOptions::default(),
94                },
95                fragment: Some(wgpu::FragmentState {
96                    module: &shader,
97                    entry_point: Some("fs_main"),
98                    targets: &[Some(wgpu::ColorTargetState {
99                        format,
100                        blend: Some(
101                            wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING,
102                        ),
103                        write_mask: wgpu::ColorWrites::ALL,
104                    })],
105                    compilation_options:
106                        wgpu::PipelineCompilationOptions::default(),
107                }),
108                primitive: wgpu::PrimitiveState {
109                    topology: wgpu::PrimitiveTopology::TriangleList,
110                    front_face: wgpu::FrontFace::Cw,
111                    ..Default::default()
112                },
113                depth_stencil: None,
114                multisample: wgpu::MultisampleState {
115                    count: 1,
116                    mask: !0,
117                    alpha_to_coverage_enabled: false,
118                },
119                multiview: None,
120                cache: None,
121            });
122
123        Self {
124            format,
125            sampler,
126            raw: pipeline,
127            constant_layout,
128            texture_layout,
129            sample_count: antialiasing.sample_count(),
130            targets: Arc::new(RwLock::new(None)),
131        }
132    }
133
134    fn targets(
135        &self,
136        device: &wgpu::Device,
137        region_size: Size<u32>,
138    ) -> Targets {
139        let mut targets = self.targets.write().expect("Write MSAA targets");
140
141        match targets.as_mut() {
142            Some(targets)
143                if region_size.width <= targets.size.width
144                    && region_size.height <= targets.size.height => {}
145            _ => {
146                *targets = Some(Targets::new(
147                    device,
148                    self.format,
149                    &self.texture_layout,
150                    self.sample_count,
151                    region_size,
152                ));
153            }
154        }
155
156        targets.as_ref().unwrap().clone()
157    }
158
159    pub fn render_pass<'a>(
160        &self,
161        encoder: &'a mut wgpu::CommandEncoder,
162    ) -> wgpu::RenderPass<'a> {
163        let targets = self.targets.read().expect("Read MSAA targets");
164        let targets = targets.as_ref().unwrap();
165
166        encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
167            label: Some("iced_wgpu.triangle.render_pass"),
168            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
169                view: &targets.attachment,
170                depth_slice: None,
171                resolve_target: Some(&targets.resolve),
172                ops: wgpu::Operations {
173                    load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
174                    store: wgpu::StoreOp::Store,
175                },
176            })],
177            depth_stencil_attachment: None,
178            timestamp_writes: None,
179            occlusion_query_set: None,
180        })
181    }
182}
183
184#[derive(Debug, Clone)]
185struct Targets {
186    attachment: wgpu::TextureView,
187    resolve: wgpu::TextureView,
188    bind_group: wgpu::BindGroup,
189    size: Size<u32>,
190}
191
192impl Targets {
193    pub fn new(
194        device: &wgpu::Device,
195        format: wgpu::TextureFormat,
196        texture_layout: &wgpu::BindGroupLayout,
197        sample_count: u32,
198        size: Size<u32>,
199    ) -> Targets {
200        let extent = wgpu::Extent3d {
201            width: size.width,
202            height: size.height,
203            depth_or_array_layers: 1,
204        };
205
206        let attachment = device.create_texture(&wgpu::TextureDescriptor {
207            label: Some("iced_wgpu::triangle::msaa attachment"),
208            size: extent,
209            mip_level_count: 1,
210            sample_count,
211            dimension: wgpu::TextureDimension::D2,
212            format,
213            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
214            view_formats: &[],
215        });
216
217        let resolve = device.create_texture(&wgpu::TextureDescriptor {
218            label: Some("iced_wgpu::triangle::msaa resolve target"),
219            size: extent,
220            mip_level_count: 1,
221            sample_count: 1,
222            dimension: wgpu::TextureDimension::D2,
223            format,
224            usage: wgpu::TextureUsages::RENDER_ATTACHMENT
225                | wgpu::TextureUsages::TEXTURE_BINDING,
226            view_formats: &[],
227        });
228
229        let attachment =
230            attachment.create_view(&wgpu::TextureViewDescriptor::default());
231
232        let resolve =
233            resolve.create_view(&wgpu::TextureViewDescriptor::default());
234
235        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
236            label: Some("iced_wgpu::triangle::msaa texture bind group"),
237            layout: texture_layout,
238            entries: &[wgpu::BindGroupEntry {
239                binding: 0,
240                resource: wgpu::BindingResource::TextureView(&resolve),
241            }],
242        });
243
244        Targets {
245            attachment,
246            resolve,
247            bind_group,
248            size,
249        }
250    }
251}
252
253#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
254#[repr(C)]
255struct Ratio {
256    u: f32,
257    v: f32,
258    // Padding field for 16-byte alignment.
259    // See https://docs.rs/wgpu/latest/wgpu/struct.DownlevelFlags.html#associatedconstant.BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED
260    _padding: [f32; 2],
261}
262
263pub struct State {
264    ratio: wgpu::Buffer,
265    constants: wgpu::BindGroup,
266    last_ratio: Option<Ratio>,
267}
268
269impl State {
270    pub fn new(device: &wgpu::Device, pipeline: &Pipeline) -> Self {
271        let ratio = device.create_buffer(&wgpu::BufferDescriptor {
272            label: Some("iced_wgpu::triangle::msaa ratio"),
273            size: std::mem::size_of::<Ratio>() as u64,
274            usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,
275            mapped_at_creation: false,
276        });
277
278        let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
279            label: Some("iced_wgpu::triangle::msaa uniforms bind group"),
280            layout: &pipeline.constant_layout,
281            entries: &[
282                wgpu::BindGroupEntry {
283                    binding: 0,
284                    resource: wgpu::BindingResource::Sampler(&pipeline.sampler),
285                },
286                wgpu::BindGroupEntry {
287                    binding: 1,
288                    resource: ratio.as_entire_binding(),
289                },
290            ],
291        });
292
293        Self {
294            ratio,
295            constants,
296            last_ratio: None,
297        }
298    }
299
300    pub fn prepare(
301        &mut self,
302        device: &wgpu::Device,
303        encoder: &mut wgpu::CommandEncoder,
304        belt: &mut wgpu::util::StagingBelt,
305        pipeline: &Pipeline,
306        region_size: Size<u32>,
307    ) -> Transformation {
308        let targets = pipeline.targets(device, region_size);
309
310        let ratio = Ratio {
311            u: region_size.width as f32 / targets.size.width as f32,
312            v: region_size.height as f32 / targets.size.height as f32,
313            _padding: [0.0; 2],
314        };
315
316        if Some(ratio) != self.last_ratio {
317            belt.write_buffer(
318                encoder,
319                &self.ratio,
320                0,
321                NonZeroU64::new(std::mem::size_of::<Ratio>() as u64)
322                    .expect("non-empty ratio"),
323                device,
324            )
325            .copy_from_slice(bytemuck::bytes_of(&ratio));
326
327            self.last_ratio = Some(ratio);
328        }
329
330        Transformation::orthographic(targets.size.width, targets.size.height)
331    }
332
333    pub fn render(
334        &self,
335        pipeline: &Pipeline,
336        encoder: &mut wgpu::CommandEncoder,
337        target: &wgpu::TextureView,
338    ) {
339        let mut render_pass =
340            encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
341                label: Some("iced_wgpu::triangle::msaa render pass"),
342                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
343                    view: target,
344                    depth_slice: None,
345                    resolve_target: None,
346                    ops: wgpu::Operations {
347                        load: wgpu::LoadOp::Load,
348                        store: wgpu::StoreOp::Store,
349                    },
350                })],
351                depth_stencil_attachment: None,
352                timestamp_writes: None,
353                occlusion_query_set: None,
354            });
355
356        render_pass.set_pipeline(&pipeline.raw);
357        render_pass.set_bind_group(0, &self.constants, &[]);
358        render_pass.set_bind_group(
359            1,
360            &pipeline
361                .targets
362                .read()
363                .expect("Read MSAA targets")
364                .as_ref()
365                .unwrap()
366                .bind_group,
367            &[],
368        );
369        render_pass.draw(0..6, 0..1);
370    }
371}