wgpu_examples/stencil_triangles/
mod.rs

1use bytemuck::{Pod, Zeroable};
2use wgpu::util::DeviceExt;
3
4#[repr(C)]
5#[derive(Clone, Copy, Pod, Zeroable)]
6struct Vertex {
7    _pos: [f32; 4],
8}
9
10fn vertex(x: f32, y: f32) -> Vertex {
11    Vertex {
12        _pos: [x, y, 0.0, 1.0],
13    }
14}
15
16struct Example {
17    outer_vertex_buffer: wgpu::Buffer,
18    mask_vertex_buffer: wgpu::Buffer,
19    outer_pipeline: wgpu::RenderPipeline,
20    mask_pipeline: wgpu::RenderPipeline,
21    stencil_buffer: wgpu::Texture,
22}
23
24impl crate::framework::Example for Example {
25    fn init(
26        config: &wgpu::SurfaceConfiguration,
27        _adapter: &wgpu::Adapter,
28        device: &wgpu::Device,
29        _queue: &wgpu::Queue,
30    ) -> Self {
31        // Create the vertex and index buffers
32        let vertex_size = size_of::<Vertex>();
33        let outer_vertices = [vertex(-1.0, -1.0), vertex(1.0, -1.0), vertex(0.0, 1.0)];
34        let mask_vertices = [vertex(-0.5, 0.0), vertex(0.0, -1.0), vertex(0.5, 0.0)];
35
36        let outer_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
37            label: Some("Outer Vertex Buffer"),
38            contents: bytemuck::cast_slice(&outer_vertices),
39            usage: wgpu::BufferUsages::VERTEX,
40        });
41
42        let mask_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
43            label: Some("Mask Vertex Buffer"),
44            contents: bytemuck::cast_slice(&mask_vertices),
45            usage: wgpu::BufferUsages::VERTEX,
46        });
47
48        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
49            label: None,
50            bind_group_layouts: &[],
51            push_constant_ranges: &[],
52        });
53
54        let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
55
56        let vertex_buffers = [wgpu::VertexBufferLayout {
57            array_stride: vertex_size as wgpu::BufferAddress,
58            step_mode: wgpu::VertexStepMode::Vertex,
59            attributes: &[wgpu::VertexAttribute {
60                format: wgpu::VertexFormat::Float32x4,
61                offset: 0,
62                shader_location: 0,
63            }],
64        }];
65
66        let mask_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
67            label: None,
68            layout: Some(&pipeline_layout),
69            vertex: wgpu::VertexState {
70                module: &shader,
71                entry_point: Some("vs_main"),
72                compilation_options: Default::default(),
73                buffers: &vertex_buffers,
74            },
75            fragment: Some(wgpu::FragmentState {
76                module: &shader,
77                entry_point: Some("fs_main"),
78                compilation_options: Default::default(),
79                targets: &[Some(wgpu::ColorTargetState {
80                    format: config.view_formats[0],
81                    blend: None,
82                    write_mask: wgpu::ColorWrites::empty(),
83                })],
84            }),
85            primitive: Default::default(),
86            depth_stencil: Some(wgpu::DepthStencilState {
87                format: wgpu::TextureFormat::Stencil8,
88                depth_write_enabled: false,
89                depth_compare: wgpu::CompareFunction::Always,
90                stencil: wgpu::StencilState {
91                    front: wgpu::StencilFaceState {
92                        compare: wgpu::CompareFunction::Always,
93                        pass_op: wgpu::StencilOperation::Replace,
94                        ..Default::default()
95                    },
96                    back: wgpu::StencilFaceState::IGNORE,
97                    read_mask: !0,
98                    write_mask: !0,
99                },
100                bias: Default::default(),
101            }),
102            multisample: wgpu::MultisampleState::default(),
103            multiview: None,
104            cache: None,
105        });
106
107        let outer_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
108            label: None,
109            layout: Some(&pipeline_layout),
110            vertex: wgpu::VertexState {
111                module: &shader,
112                entry_point: Some("vs_main"),
113                compilation_options: Default::default(),
114                buffers: &vertex_buffers,
115            },
116            fragment: Some(wgpu::FragmentState {
117                module: &shader,
118                entry_point: Some("fs_main"),
119                compilation_options: Default::default(),
120                targets: &[Some(config.view_formats[0].into())],
121            }),
122            primitive: Default::default(),
123            depth_stencil: Some(wgpu::DepthStencilState {
124                format: wgpu::TextureFormat::Stencil8,
125                depth_write_enabled: false,
126                depth_compare: wgpu::CompareFunction::Always,
127                stencil: wgpu::StencilState {
128                    front: wgpu::StencilFaceState {
129                        compare: wgpu::CompareFunction::Greater,
130                        ..Default::default()
131                    },
132                    back: wgpu::StencilFaceState::IGNORE,
133                    read_mask: !0,
134                    write_mask: !0,
135                },
136                bias: Default::default(),
137            }),
138            multisample: wgpu::MultisampleState::default(),
139            multiview: None,
140            cache: None,
141        });
142
143        let stencil_buffer = device.create_texture(&wgpu::TextureDescriptor {
144            label: Some("Stencil buffer"),
145            size: wgpu::Extent3d {
146                width: config.width,
147                height: config.height,
148                depth_or_array_layers: 1,
149            },
150            mip_level_count: 1,
151            sample_count: 1,
152            dimension: wgpu::TextureDimension::D2,
153            format: wgpu::TextureFormat::Stencil8,
154            view_formats: &[],
155            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
156        });
157
158        // Done
159        Example {
160            outer_vertex_buffer,
161            mask_vertex_buffer,
162            outer_pipeline,
163            mask_pipeline,
164            stencil_buffer,
165        }
166    }
167
168    fn update(&mut self, _event: winit::event::WindowEvent) {
169        // empty
170    }
171
172    fn resize(
173        &mut self,
174        config: &wgpu::SurfaceConfiguration,
175        device: &wgpu::Device,
176        _queue: &wgpu::Queue,
177    ) {
178        self.stencil_buffer = device.create_texture(&wgpu::TextureDescriptor {
179            label: Some("Stencil buffer"),
180            size: wgpu::Extent3d {
181                width: config.width,
182                height: config.height,
183                depth_or_array_layers: 1,
184            },
185            mip_level_count: 1,
186            sample_count: 1,
187            dimension: wgpu::TextureDimension::D2,
188            format: wgpu::TextureFormat::Stencil8,
189            view_formats: &[],
190            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
191        });
192    }
193
194    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
195        let mut encoder =
196            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
197        {
198            let depth_view = self.stencil_buffer.create_view(&Default::default());
199            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
200                label: None,
201                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
202                    view,
203                    depth_slice: None,
204                    resolve_target: None,
205                    ops: wgpu::Operations {
206                        load: wgpu::LoadOp::Clear(wgpu::Color {
207                            r: 0.1,
208                            g: 0.2,
209                            b: 0.3,
210                            a: 1.0,
211                        }),
212                        store: wgpu::StoreOp::Store,
213                    },
214                })],
215                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
216                    view: &depth_view,
217                    depth_ops: None,
218                    stencil_ops: Some(wgpu::Operations {
219                        load: wgpu::LoadOp::Clear(0),
220                        store: wgpu::StoreOp::Store,
221                    }),
222                }),
223                timestamp_writes: None,
224                occlusion_query_set: None,
225            });
226
227            rpass.set_stencil_reference(1);
228
229            rpass.set_pipeline(&self.mask_pipeline);
230            rpass.set_vertex_buffer(0, self.mask_vertex_buffer.slice(..));
231            rpass.draw(0..3, 0..1);
232
233            rpass.set_pipeline(&self.outer_pipeline);
234            rpass.set_vertex_buffer(0, self.outer_vertex_buffer.slice(..));
235            rpass.draw(0..3, 0..1);
236        }
237
238        queue.submit(Some(encoder.finish()));
239    }
240}
241
242pub fn main() {
243    crate::framework::run::<Example>("stencil-triangles");
244}
245
246#[cfg(test)]
247#[wgpu_test::gpu_test]
248pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
249    name: "stencil-triangles",
250    image_path: "/examples/features/src/stencil_triangles/screenshot.png",
251    width: 1024,
252    height: 768,
253    optional_features: wgpu::Features::default(),
254    base_test_parameters: wgpu_test::TestParameters::default(),
255    comparisons: &[wgpu_test::ComparisonType::Mean(0.03)],
256    _phantom: std::marker::PhantomData::<Example>,
257};