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