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 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 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 }
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)], _phantom: std::marker::PhantomData::<Example>,
252};