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};