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            immediate_size: 0,
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::stencil(
87                wgpu::TextureFormat::Stencil8,
88                wgpu::StencilState {
89                    front: wgpu::StencilFaceState {
90                        compare: wgpu::CompareFunction::Always,
91                        pass_op: wgpu::StencilOperation::Replace,
92                        ..Default::default()
93                    },
94                    back: wgpu::StencilFaceState::IGNORE,
95                    read_mask: !0,
96                    write_mask: !0,
97                },
98            )),
99            multisample: wgpu::MultisampleState::default(),
100            multiview_mask: None,
101            cache: None,
102        });
103
104        let outer_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
105            label: None,
106            layout: Some(&pipeline_layout),
107            vertex: wgpu::VertexState {
108                module: &shader,
109                entry_point: Some("vs_main"),
110                compilation_options: Default::default(),
111                buffers: &vertex_buffers,
112            },
113            fragment: Some(wgpu::FragmentState {
114                module: &shader,
115                entry_point: Some("fs_main"),
116                compilation_options: Default::default(),
117                targets: &[Some(config.view_formats[0].into())],
118            }),
119            primitive: Default::default(),
120            depth_stencil: Some(wgpu::DepthStencilState::stencil(
121                wgpu::TextureFormat::Stencil8,
122                wgpu::StencilState {
123                    front: wgpu::StencilFaceState {
124                        compare: wgpu::CompareFunction::Greater,
125                        ..Default::default()
126                    },
127                    back: wgpu::StencilFaceState::IGNORE,
128                    read_mask: !0,
129                    write_mask: !0,
130                },
131            )),
132            multisample: wgpu::MultisampleState::default(),
133            multiview_mask: None,
134            cache: None,
135        });
136
137        let stencil_buffer = device.create_texture(&wgpu::TextureDescriptor {
138            label: Some("Stencil buffer"),
139            size: wgpu::Extent3d {
140                width: config.width,
141                height: config.height,
142                depth_or_array_layers: 1,
143            },
144            mip_level_count: 1,
145            sample_count: 1,
146            dimension: wgpu::TextureDimension::D2,
147            format: wgpu::TextureFormat::Stencil8,
148            view_formats: &[],
149            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
150        });
151
152        // Done
153        Example {
154            outer_vertex_buffer,
155            mask_vertex_buffer,
156            outer_pipeline,
157            mask_pipeline,
158            stencil_buffer,
159        }
160    }
161
162    fn update(&mut self, _event: winit::event::WindowEvent) {
163        // empty
164    }
165
166    fn resize(
167        &mut self,
168        config: &wgpu::SurfaceConfiguration,
169        device: &wgpu::Device,
170        _queue: &wgpu::Queue,
171    ) {
172        self.stencil_buffer = device.create_texture(&wgpu::TextureDescriptor {
173            label: Some("Stencil buffer"),
174            size: wgpu::Extent3d {
175                width: config.width,
176                height: config.height,
177                depth_or_array_layers: 1,
178            },
179            mip_level_count: 1,
180            sample_count: 1,
181            dimension: wgpu::TextureDimension::D2,
182            format: wgpu::TextureFormat::Stencil8,
183            view_formats: &[],
184            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
185        });
186    }
187
188    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
189        let mut encoder =
190            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
191        {
192            let depth_view = self.stencil_buffer.create_view(&Default::default());
193            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
194                label: None,
195                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
196                    view,
197                    depth_slice: None,
198                    resolve_target: None,
199                    ops: wgpu::Operations {
200                        load: wgpu::LoadOp::Clear(wgpu::Color {
201                            r: 0.1,
202                            g: 0.2,
203                            b: 0.3,
204                            a: 1.0,
205                        }),
206                        store: wgpu::StoreOp::Store,
207                    },
208                })],
209                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
210                    view: &depth_view,
211                    depth_ops: None,
212                    stencil_ops: Some(wgpu::Operations {
213                        load: wgpu::LoadOp::Clear(0),
214                        store: wgpu::StoreOp::Store,
215                    }),
216                }),
217                timestamp_writes: None,
218                occlusion_query_set: None,
219                multiview_mask: None,
220            });
221
222            rpass.set_stencil_reference(1);
223
224            rpass.set_pipeline(&self.mask_pipeline);
225            rpass.set_vertex_buffer(0, self.mask_vertex_buffer.slice(..));
226            rpass.draw(0..3, 0..1);
227
228            rpass.set_pipeline(&self.outer_pipeline);
229            rpass.set_vertex_buffer(0, self.outer_vertex_buffer.slice(..));
230            rpass.draw(0..3, 0..1);
231        }
232
233        queue.submit(Some(encoder.finish()));
234    }
235}
236
237pub fn main() {
238    crate::framework::run::<Example>("stencil-triangles");
239}
240
241#[cfg(test)]
242#[wgpu_test::gpu_test]
243pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
244    name: "stencil-triangles",
245    image_path: "/examples/features/src/stencil_triangles/screenshot.png",
246    width: 1024,
247    height: 768,
248    optional_features: wgpu::Features::default(),
249    base_test_parameters: wgpu_test::TestParameters::default(),
250    comparisons: &[wgpu_test::ComparisonType::Mean(0.04)], // Bounded by Apple A9
251    _phantom: std::marker::PhantomData::<Example>,
252};