wgpu_examples/conservative_raster/
mod.rs

1const RENDER_TARGET_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
2
3struct Example {
4    low_res_target: wgpu::TextureView,
5    bind_group_upscale: wgpu::BindGroup,
6
7    pipeline_triangle_conservative: wgpu::RenderPipeline,
8    pipeline_triangle_regular: wgpu::RenderPipeline,
9    pipeline_upscale: wgpu::RenderPipeline,
10    pipeline_lines: Option<wgpu::RenderPipeline>,
11    bind_group_layout_upscale: wgpu::BindGroupLayout,
12}
13
14impl Example {
15    fn create_low_res_target(
16        config: &wgpu::SurfaceConfiguration,
17        device: &wgpu::Device,
18        bind_group_layout_upscale: &wgpu::BindGroupLayout,
19    ) -> (wgpu::TextureView, wgpu::BindGroup) {
20        let texture_view = device
21            .create_texture(&wgpu::TextureDescriptor {
22                label: Some("Low Resolution Target"),
23                size: wgpu::Extent3d {
24                    width: (config.width / 16).max(1),
25                    height: (config.height / 16).max(1),
26                    depth_or_array_layers: 1,
27                },
28                mip_level_count: 1,
29                sample_count: 1,
30                dimension: wgpu::TextureDimension::D2,
31                format: RENDER_TARGET_FORMAT,
32                usage: wgpu::TextureUsages::TEXTURE_BINDING
33                    | wgpu::TextureUsages::RENDER_ATTACHMENT,
34                view_formats: &[],
35            })
36            .create_view(&Default::default());
37
38        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
39            label: Some("Nearest Neighbor Sampler"),
40            mag_filter: wgpu::FilterMode::Nearest,
41            min_filter: wgpu::FilterMode::Nearest,
42            ..Default::default()
43        });
44
45        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
46            label: Some("upscale bind group"),
47            layout: bind_group_layout_upscale,
48            entries: &[
49                wgpu::BindGroupEntry {
50                    binding: 0,
51                    resource: wgpu::BindingResource::TextureView(&texture_view),
52                },
53                wgpu::BindGroupEntry {
54                    binding: 1,
55                    resource: wgpu::BindingResource::Sampler(&sampler),
56                },
57            ],
58        });
59
60        (texture_view, bind_group)
61    }
62}
63
64impl crate::framework::Example for Example {
65    fn required_features() -> wgpu::Features {
66        wgpu::Features::CONSERVATIVE_RASTERIZATION
67    }
68    fn optional_features() -> wgpu::Features {
69        wgpu::Features::POLYGON_MODE_LINE
70    }
71    fn init(
72        config: &wgpu::SurfaceConfiguration,
73        _adapter: &wgpu::Adapter,
74        device: &wgpu::Device,
75        _queue: &wgpu::Queue,
76    ) -> Self {
77        let pipeline_layout_empty =
78            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
79                label: None,
80                bind_group_layouts: &[],
81                push_constant_ranges: &[],
82            });
83
84        let shader_triangle_and_lines =
85            device.create_shader_module(wgpu::include_wgsl!("triangle_and_lines.wgsl"));
86
87        let pipeline_triangle_conservative =
88            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
89                label: Some("Conservative Rasterization"),
90                layout: Some(&pipeline_layout_empty),
91                vertex: wgpu::VertexState {
92                    module: &shader_triangle_and_lines,
93                    entry_point: Some("vs_main"),
94                    compilation_options: Default::default(),
95                    buffers: &[],
96                },
97                fragment: Some(wgpu::FragmentState {
98                    module: &shader_triangle_and_lines,
99                    entry_point: Some("fs_main_red"),
100                    compilation_options: Default::default(),
101                    targets: &[Some(RENDER_TARGET_FORMAT.into())],
102                }),
103                primitive: wgpu::PrimitiveState {
104                    conservative: true,
105                    ..Default::default()
106                },
107                depth_stencil: None,
108                multisample: wgpu::MultisampleState::default(),
109                multiview: None,
110                cache: None,
111            });
112
113        let pipeline_triangle_regular =
114            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
115                label: Some("Regular Rasterization"),
116                layout: Some(&pipeline_layout_empty),
117                vertex: wgpu::VertexState {
118                    module: &shader_triangle_and_lines,
119                    entry_point: Some("vs_main"),
120                    compilation_options: Default::default(),
121                    buffers: &[],
122                },
123                fragment: Some(wgpu::FragmentState {
124                    module: &shader_triangle_and_lines,
125                    entry_point: Some("fs_main_blue"),
126                    compilation_options: Default::default(),
127                    targets: &[Some(RENDER_TARGET_FORMAT.into())],
128                }),
129                primitive: wgpu::PrimitiveState::default(),
130                depth_stencil: None,
131                multisample: wgpu::MultisampleState::default(),
132                multiview: None,
133                cache: None,
134            });
135
136        let pipeline_lines = if device
137            .features()
138            .contains(wgpu::Features::POLYGON_MODE_LINE)
139        {
140            Some(
141                device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
142                    label: Some("Lines"),
143                    layout: Some(&pipeline_layout_empty),
144                    vertex: wgpu::VertexState {
145                        module: &shader_triangle_and_lines,
146                        entry_point: Some("vs_main"),
147                        compilation_options: Default::default(),
148                        buffers: &[],
149                    },
150                    fragment: Some(wgpu::FragmentState {
151                        module: &shader_triangle_and_lines,
152                        entry_point: Some("fs_main_white"),
153                        compilation_options: Default::default(),
154                        targets: &[Some(config.view_formats[0].into())],
155                    }),
156                    primitive: wgpu::PrimitiveState {
157                        polygon_mode: wgpu::PolygonMode::Line,
158                        topology: wgpu::PrimitiveTopology::LineStrip,
159                        ..Default::default()
160                    },
161                    depth_stencil: None,
162                    multisample: wgpu::MultisampleState::default(),
163                    multiview: None,
164                    cache: None,
165                }),
166            )
167        } else {
168            None
169        };
170
171        let (pipeline_upscale, bind_group_layout_upscale) = {
172            let bind_group_layout =
173                device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
174                    label: Some("upscale bindgroup"),
175                    entries: &[
176                        wgpu::BindGroupLayoutEntry {
177                            binding: 0,
178                            visibility: wgpu::ShaderStages::FRAGMENT,
179                            ty: wgpu::BindingType::Texture {
180                                sample_type: wgpu::TextureSampleType::Float { filterable: false },
181                                view_dimension: wgpu::TextureViewDimension::D2,
182                                multisampled: false,
183                            },
184                            count: None,
185                        },
186                        wgpu::BindGroupLayoutEntry {
187                            binding: 1,
188                            visibility: wgpu::ShaderStages::FRAGMENT,
189                            ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
190                            count: None,
191                        },
192                    ],
193                });
194
195            let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
196                label: None,
197                bind_group_layouts: &[&bind_group_layout],
198                push_constant_ranges: &[],
199            });
200            let shader = device.create_shader_module(wgpu::include_wgsl!("upscale.wgsl"));
201            (
202                device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
203                    label: Some("Upscale"),
204                    layout: Some(&pipeline_layout),
205                    vertex: wgpu::VertexState {
206                        module: &shader,
207                        entry_point: Some("vs_main"),
208                        compilation_options: Default::default(),
209                        buffers: &[],
210                    },
211                    fragment: Some(wgpu::FragmentState {
212                        module: &shader,
213                        entry_point: Some("fs_main"),
214                        compilation_options: Default::default(),
215                        targets: &[Some(config.view_formats[0].into())],
216                    }),
217                    primitive: wgpu::PrimitiveState::default(),
218                    depth_stencil: None,
219                    multisample: wgpu::MultisampleState::default(),
220                    multiview: None,
221                    cache: None,
222                }),
223                bind_group_layout,
224            )
225        };
226
227        let (low_res_target, bind_group_upscale) =
228            Self::create_low_res_target(config, device, &bind_group_layout_upscale);
229
230        Self {
231            low_res_target,
232            bind_group_upscale,
233
234            pipeline_triangle_conservative,
235            pipeline_triangle_regular,
236            pipeline_upscale,
237            pipeline_lines,
238            bind_group_layout_upscale,
239        }
240    }
241
242    fn resize(
243        &mut self,
244        config: &wgpu::SurfaceConfiguration,
245        device: &wgpu::Device,
246        _queue: &wgpu::Queue,
247    ) {
248        let (low_res_target, bind_group_upscale) =
249            Self::create_low_res_target(config, device, &self.bind_group_layout_upscale);
250        self.low_res_target = low_res_target;
251        self.bind_group_upscale = bind_group_upscale;
252    }
253
254    fn update(&mut self, _event: winit::event::WindowEvent) {}
255
256    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
257        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
258            label: Some("primary"),
259        });
260
261        {
262            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
263                label: Some("low resolution"),
264                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
265                    view: &self.low_res_target,
266                    depth_slice: None,
267                    resolve_target: None,
268                    ops: wgpu::Operations {
269                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
270                        store: wgpu::StoreOp::Store,
271                    },
272                })],
273                depth_stencil_attachment: None,
274                timestamp_writes: None,
275                occlusion_query_set: None,
276            });
277
278            rpass.set_pipeline(&self.pipeline_triangle_conservative);
279            rpass.draw(0..3, 0..1);
280            rpass.set_pipeline(&self.pipeline_triangle_regular);
281            rpass.draw(0..3, 0..1);
282        }
283        {
284            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
285                label: Some("full resolution"),
286                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
287                    view,
288                    depth_slice: None,
289                    resolve_target: None,
290                    ops: wgpu::Operations {
291                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
292                        store: wgpu::StoreOp::Store,
293                    },
294                })],
295                depth_stencil_attachment: None,
296                timestamp_writes: None,
297                occlusion_query_set: None,
298            });
299
300            rpass.set_pipeline(&self.pipeline_upscale);
301            rpass.set_bind_group(0, &self.bind_group_upscale, &[]);
302            rpass.draw(0..3, 0..1);
303
304            if let Some(pipeline_lines) = &self.pipeline_lines {
305                rpass.set_pipeline(pipeline_lines);
306                rpass.draw(0..4, 0..1);
307            }
308        }
309
310        queue.submit(Some(encoder.finish()));
311    }
312}
313
314pub fn main() {
315    crate::framework::run::<Example>("conservative-raster");
316}
317
318#[cfg(test)]
319#[wgpu_test::gpu_test]
320pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
321    name: "conservative-raster",
322    image_path: "/examples/features/src/conservative_raster/screenshot.png",
323    width: 1024,
324    height: 768,
325    optional_features: wgpu::Features::default(),
326    base_test_parameters: wgpu_test::TestParameters::default(),
327    comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],
328    _phantom: std::marker::PhantomData::<Example>,
329};