rootvg_msaa/
pipeline.rs

1// The following code was copied and modified from
2// https://github.com/iced-rs/iced/blob/31d1d5fecbef50fa319cabd5d4194f1e4aaefa21/wgpu/src/triangle/msaa.rs
3// Iced license (MIT): https://github.com/iced-rs/iced/blob/31d1d5fecbef50fa319cabd5d4194f1e4aaefa21/LICENSE
4
5use rootvg_core::math::PhysicalSizeI32;
6use wgpu::PipelineCompilationOptions;
7
8#[derive(Debug)]
9pub struct MsaaPipeline {
10    format: wgpu::TextureFormat,
11    pipeline: wgpu::RenderPipeline,
12    constants: wgpu::BindGroup,
13    texture_layout: wgpu::BindGroupLayout,
14    sample_count: u32,
15    targets: Option<Targets>,
16}
17
18impl MsaaPipeline {
19    pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat, sample_count: u32) -> Self {
20        let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default());
21
22        let constant_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
23            label: Some("rootvg-msaa uniforms layout"),
24            entries: &[wgpu::BindGroupLayoutEntry {
25                binding: 0,
26                visibility: wgpu::ShaderStages::FRAGMENT,
27                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
28                count: None,
29            }],
30        });
31
32        let constant_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
33            label: Some("rootvg-msaa uniforms bind group"),
34            layout: &constant_layout,
35            entries: &[wgpu::BindGroupEntry {
36                binding: 0,
37                resource: wgpu::BindingResource::Sampler(&sampler),
38            }],
39        });
40
41        let texture_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
42            label: Some("rootvg-msaa texture layout"),
43            entries: &[wgpu::BindGroupLayoutEntry {
44                binding: 0,
45                visibility: wgpu::ShaderStages::FRAGMENT,
46                ty: wgpu::BindingType::Texture {
47                    sample_type: wgpu::TextureSampleType::Float { filterable: false },
48                    view_dimension: wgpu::TextureViewDimension::D2,
49                    multisampled: false,
50                },
51                count: None,
52            }],
53        });
54
55        let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
56            label: Some("rootvg-msaa pipeline layout"),
57            push_constant_ranges: &[],
58            bind_group_layouts: &[&constant_layout, &texture_layout],
59        });
60
61        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
62            label: Some("rootvg triangle blit_shader"),
63            source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!(
64                "shader/msaa.wgsl"
65            ))),
66        });
67
68        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
69            label: Some("rootvg-msaa pipeline"),
70            layout: Some(&layout),
71            vertex: wgpu::VertexState {
72                module: &shader,
73                entry_point: "vs_main",
74                buffers: &[],
75                compilation_options: PipelineCompilationOptions::default(),
76            },
77            fragment: Some(wgpu::FragmentState {
78                module: &shader,
79                entry_point: "fs_main",
80                targets: &[Some(wgpu::ColorTargetState {
81                    format,
82                    blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
83                    write_mask: wgpu::ColorWrites::ALL,
84                })],
85                compilation_options: PipelineCompilationOptions::default(),
86            }),
87            primitive: wgpu::PrimitiveState {
88                topology: wgpu::PrimitiveTopology::TriangleList,
89                front_face: wgpu::FrontFace::Cw,
90                ..Default::default()
91            },
92            depth_stencil: None,
93            multisample: wgpu::MultisampleState {
94                count: 1,
95                mask: !0,
96                alpha_to_coverage_enabled: false,
97            },
98            multiview: None,
99        });
100
101        Self {
102            format,
103            pipeline,
104            constants: constant_bind_group,
105            texture_layout,
106            sample_count,
107            targets: None,
108        }
109    }
110
111    pub fn targets(
112        &mut self,
113        device: &wgpu::Device,
114        size: PhysicalSizeI32,
115    ) -> (&wgpu::TextureView, &wgpu::TextureView) {
116        match &mut self.targets {
117            None => {
118                self.targets = Some(Targets::new(
119                    device,
120                    self.format,
121                    &self.texture_layout,
122                    self.sample_count,
123                    size,
124                ));
125            }
126            Some(targets) => {
127                if targets.size != size {
128                    self.targets = Some(Targets::new(
129                        device,
130                        self.format,
131                        &self.texture_layout,
132                        self.sample_count,
133                        size,
134                    ));
135                }
136            }
137        }
138
139        let targets = self.targets.as_ref().unwrap();
140
141        (&targets.attachment, &targets.resolve)
142    }
143
144    pub fn render_to_target(
145        &self,
146        target: &wgpu::TextureView,
147        clear_color: Option<wgpu::Color>,
148        encoder: &mut wgpu::CommandEncoder,
149    ) {
150        let load = if let Some(color) = clear_color {
151            wgpu::LoadOp::Clear(color)
152        } else {
153            wgpu::LoadOp::Load
154        };
155
156        let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
157            label: Some("rootvg-msaa render pass"),
158            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
159                view: target,
160                resolve_target: None,
161                ops: wgpu::Operations {
162                    load,
163                    store: wgpu::StoreOp::Store,
164                },
165            })],
166            depth_stencil_attachment: None,
167            timestamp_writes: None,
168            occlusion_query_set: None,
169        });
170
171        render_pass.set_pipeline(&self.pipeline);
172        render_pass.set_bind_group(0, &self.constants, &[]);
173        render_pass.set_bind_group(1, &self.targets.as_ref().unwrap().bind_group, &[]);
174        render_pass.draw(0..6, 0..1);
175    }
176}
177
178#[derive(Debug)]
179struct Targets {
180    attachment: wgpu::TextureView,
181    resolve: wgpu::TextureView,
182    bind_group: wgpu::BindGroup,
183    size: PhysicalSizeI32,
184}
185
186impl Targets {
187    pub fn new(
188        device: &wgpu::Device,
189        format: wgpu::TextureFormat,
190        texture_layout: &wgpu::BindGroupLayout,
191        sample_count: u32,
192        size: PhysicalSizeI32,
193    ) -> Targets {
194        assert!(size.width > 0);
195        assert!(size.height > 0);
196
197        let extent = wgpu::Extent3d {
198            width: size.width as u32,
199            height: size.height as u32,
200            depth_or_array_layers: 1,
201        };
202
203        let attachment = device.create_texture(&wgpu::TextureDescriptor {
204            label: Some("rootvg-msaa attachment"),
205            size: extent,
206            mip_level_count: 1,
207            sample_count,
208            dimension: wgpu::TextureDimension::D2,
209            format,
210            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
211            view_formats: &[],
212        });
213
214        let resolve = device.create_texture(&wgpu::TextureDescriptor {
215            label: Some("rootvg-msaa resolve target"),
216            size: extent,
217            mip_level_count: 1,
218            sample_count: 1,
219            dimension: wgpu::TextureDimension::D2,
220            format,
221            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
222            view_formats: &[],
223        });
224
225        let attachment = attachment.create_view(&wgpu::TextureViewDescriptor::default());
226
227        let resolve = resolve.create_view(&wgpu::TextureViewDescriptor::default());
228
229        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
230            label: Some("rootvg-msaa texture bind group"),
231            layout: texture_layout,
232            entries: &[wgpu::BindGroupEntry {
233                binding: 0,
234                resource: wgpu::BindingResource::TextureView(&resolve),
235            }],
236        });
237
238        Targets {
239            attachment,
240            resolve,
241            bind_group,
242            size,
243        }
244    }
245}