1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "tests/DawnTest.h"
16
17 #include "common/Assert.h"
18 #include "utils/ComboRenderPipelineDescriptor.h"
19 #include "utils/WGPUHelpers.h"
20
21 constexpr static unsigned int kRTSize = 64;
22
23 class DepthStencilStateTest : public DawnTest {
24 protected:
SetUp()25 void SetUp() override {
26 DawnTest::SetUp();
27
28 // TODO(crbug.com/dawn/737): Test output is wrong with D3D12 + WARP.
29 DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsWARP());
30
31 wgpu::TextureDescriptor renderTargetDescriptor;
32 renderTargetDescriptor.dimension = wgpu::TextureDimension::e2D;
33 renderTargetDescriptor.size.width = kRTSize;
34 renderTargetDescriptor.size.height = kRTSize;
35 renderTargetDescriptor.size.depthOrArrayLayers = 1;
36 renderTargetDescriptor.sampleCount = 1;
37 renderTargetDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
38 renderTargetDescriptor.mipLevelCount = 1;
39 renderTargetDescriptor.usage =
40 wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
41 renderTarget = device.CreateTexture(&renderTargetDescriptor);
42
43 renderTargetView = renderTarget.CreateView();
44
45 wgpu::TextureDescriptor depthDescriptor;
46 depthDescriptor.dimension = wgpu::TextureDimension::e2D;
47 depthDescriptor.size.width = kRTSize;
48 depthDescriptor.size.height = kRTSize;
49 depthDescriptor.size.depthOrArrayLayers = 1;
50 depthDescriptor.sampleCount = 1;
51 depthDescriptor.format = wgpu::TextureFormat::Depth24PlusStencil8;
52 depthDescriptor.mipLevelCount = 1;
53 depthDescriptor.usage = wgpu::TextureUsage::RenderAttachment;
54 depthTexture = device.CreateTexture(&depthDescriptor);
55
56 depthTextureView = depthTexture.CreateView();
57
58 vsModule = utils::CreateShaderModule(device, R"(
59 [[block]] struct UBO {
60 color : vec3<f32>;
61 depth : f32;
62 };
63 [[group(0), binding(0)]] var<uniform> ubo : UBO;
64
65 [[stage(vertex)]]
66 fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
67 var pos = array<vec2<f32>, 6>(
68 vec2<f32>(-1.0, 1.0),
69 vec2<f32>(-1.0, -1.0),
70 vec2<f32>( 1.0, -1.0), // front-facing
71 vec2<f32>(-1.0, 1.0),
72 vec2<f32>( 1.0, 1.0),
73 vec2<f32>( 1.0, -1.0)); // back-facing
74 return vec4<f32>(pos[VertexIndex], ubo.depth, 1.0);
75 })");
76
77 fsModule = utils::CreateShaderModule(device, R"(
78 [[block]] struct UBO {
79 color : vec3<f32>;
80 depth : f32;
81 };
82 [[group(0), binding(0)]] var<uniform> ubo : UBO;
83
84 [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
85 return vec4<f32>(ubo.color, 1.0);
86 })");
87 }
88
89 struct TestSpec {
90 const wgpu::DepthStencilState& depthStencil;
91 RGBA8 color;
92 float depth;
93 uint32_t stencil;
94 wgpu::FrontFace frontFace = wgpu::FrontFace::CCW;
95 bool setStencilReference = true;
96 };
97
98 // Check whether a depth comparison function works as expected
99 // The less, equal, greater booleans denote wether the respective triangle should be visible
100 // based on the comparison function
CheckDepthCompareFunction(wgpu::CompareFunction compareFunction,bool less,bool equal,bool greater)101 void CheckDepthCompareFunction(wgpu::CompareFunction compareFunction,
102 bool less,
103 bool equal,
104 bool greater) {
105 wgpu::StencilFaceState stencilFace;
106 stencilFace.compare = wgpu::CompareFunction::Always;
107 stencilFace.failOp = wgpu::StencilOperation::Keep;
108 stencilFace.depthFailOp = wgpu::StencilOperation::Keep;
109 stencilFace.passOp = wgpu::StencilOperation::Keep;
110
111 wgpu::DepthStencilState baseState;
112 baseState.depthWriteEnabled = true;
113 baseState.depthCompare = wgpu::CompareFunction::Always;
114 baseState.stencilBack = stencilFace;
115 baseState.stencilFront = stencilFace;
116 baseState.stencilReadMask = 0xff;
117 baseState.stencilWriteMask = 0xff;
118
119 wgpu::DepthStencilState state;
120 state.depthWriteEnabled = true;
121 state.depthCompare = compareFunction;
122 state.stencilBack = stencilFace;
123 state.stencilFront = stencilFace;
124 state.stencilReadMask = 0xff;
125 state.stencilWriteMask = 0xff;
126
127 RGBA8 baseColor = RGBA8(255, 255, 255, 255);
128 RGBA8 lessColor = RGBA8(255, 0, 0, 255);
129 RGBA8 equalColor = RGBA8(0, 255, 0, 255);
130 RGBA8 greaterColor = RGBA8(0, 0, 255, 255);
131
132 // Base triangle at depth 0.5, depth always, depth write enabled
133 TestSpec base = {baseState, baseColor, 0.5f, 0u};
134
135 // Draw the base triangle, then a triangle in stencilFront of the base triangle with the
136 // given depth comparison function
137 DoTest({base, {state, lessColor, 0.f, 0u}}, less ? lessColor : baseColor);
138
139 // Draw the base triangle, then a triangle in at the same depth as the base triangle with
140 // the given depth comparison function
141 DoTest({base, {state, equalColor, 0.5f, 0u}}, equal ? equalColor : baseColor);
142
143 // Draw the base triangle, then a triangle behind the base triangle with the given depth
144 // comparison function
145 DoTest({base, {state, greaterColor, 1.0f, 0u}}, greater ? greaterColor : baseColor);
146 }
147
148 // Check whether a stencil comparison function works as expected
149 // The less, equal, greater booleans denote wether the respective triangle should be visible
150 // based on the comparison function
CheckStencilCompareFunction(wgpu::CompareFunction compareFunction,bool less,bool equal,bool greater)151 void CheckStencilCompareFunction(wgpu::CompareFunction compareFunction,
152 bool less,
153 bool equal,
154 bool greater) {
155 wgpu::StencilFaceState baseStencilFaceDescriptor;
156 baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always;
157 baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
158 baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
159 baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace;
160 wgpu::DepthStencilState baseState;
161 baseState.depthWriteEnabled = false;
162 baseState.depthCompare = wgpu::CompareFunction::Always;
163 baseState.stencilBack = baseStencilFaceDescriptor;
164 baseState.stencilFront = baseStencilFaceDescriptor;
165 baseState.stencilReadMask = 0xff;
166 baseState.stencilWriteMask = 0xff;
167
168 wgpu::StencilFaceState stencilFaceDescriptor;
169 stencilFaceDescriptor.compare = compareFunction;
170 stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
171 stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
172 stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep;
173 wgpu::DepthStencilState state;
174 state.depthWriteEnabled = false;
175 state.depthCompare = wgpu::CompareFunction::Always;
176 state.stencilBack = stencilFaceDescriptor;
177 state.stencilFront = stencilFaceDescriptor;
178 state.stencilReadMask = 0xff;
179 state.stencilWriteMask = 0xff;
180
181 RGBA8 baseColor = RGBA8(255, 255, 255, 255);
182 RGBA8 lessColor = RGBA8(255, 0, 0, 255);
183 RGBA8 equalColor = RGBA8(0, 255, 0, 255);
184 RGBA8 greaterColor = RGBA8(0, 0, 255, 255);
185
186 // Base triangle with stencil reference 1
187 TestSpec base = {baseState, baseColor, 0.0f, 1u};
188
189 // Draw the base triangle, then a triangle with stencil reference 0 with the given stencil
190 // comparison function
191 DoTest({base, {state, lessColor, 0.f, 0u}}, less ? lessColor : baseColor);
192
193 // Draw the base triangle, then a triangle with stencil reference 1 with the given stencil
194 // comparison function
195 DoTest({base, {state, equalColor, 0.f, 1u}}, equal ? equalColor : baseColor);
196
197 // Draw the base triangle, then a triangle with stencil reference 2 with the given stencil
198 // comparison function
199 DoTest({base, {state, greaterColor, 0.f, 2u}}, greater ? greaterColor : baseColor);
200 }
201
202 // Given the provided `initialStencil` and `reference`, check that applying the
203 // `stencilOperation` produces the `expectedStencil`
CheckStencilOperation(wgpu::StencilOperation stencilOperation,uint32_t initialStencil,uint32_t reference,uint32_t expectedStencil)204 void CheckStencilOperation(wgpu::StencilOperation stencilOperation,
205 uint32_t initialStencil,
206 uint32_t reference,
207 uint32_t expectedStencil) {
208 wgpu::StencilFaceState baseStencilFaceDescriptor;
209 baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always;
210 baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
211 baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
212 baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace;
213 wgpu::DepthStencilState baseState;
214 baseState.depthWriteEnabled = false;
215 baseState.depthCompare = wgpu::CompareFunction::Always;
216 baseState.stencilBack = baseStencilFaceDescriptor;
217 baseState.stencilFront = baseStencilFaceDescriptor;
218 baseState.stencilReadMask = 0xff;
219 baseState.stencilWriteMask = 0xff;
220
221 wgpu::StencilFaceState stencilFaceDescriptor;
222 stencilFaceDescriptor.compare = wgpu::CompareFunction::Always;
223 stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
224 stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
225 stencilFaceDescriptor.passOp = stencilOperation;
226 wgpu::DepthStencilState state;
227 state.depthWriteEnabled = false;
228 state.depthCompare = wgpu::CompareFunction::Always;
229 state.stencilBack = stencilFaceDescriptor;
230 state.stencilFront = stencilFaceDescriptor;
231 state.stencilReadMask = 0xff;
232 state.stencilWriteMask = 0xff;
233
234 CheckStencil(
235 {
236 // Wipe the stencil buffer with the initialStencil value
237 {baseState, RGBA8(255, 255, 255, 255), 0.f, initialStencil},
238
239 // Draw a triangle with the provided stencil operation and reference
240 {state, RGBA8(255, 0, 0, 255), 0.f, reference},
241 },
242 expectedStencil);
243 }
244
245 // Draw a list of test specs, and check if the stencil value is equal to the expected value
CheckStencil(std::vector<TestSpec> testParams,uint32_t expectedStencil)246 void CheckStencil(std::vector<TestSpec> testParams, uint32_t expectedStencil) {
247 wgpu::StencilFaceState stencilFaceDescriptor;
248 stencilFaceDescriptor.compare = wgpu::CompareFunction::Equal;
249 stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
250 stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
251 stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep;
252 wgpu::DepthStencilState state;
253 state.depthWriteEnabled = false;
254 state.depthCompare = wgpu::CompareFunction::Always;
255 state.stencilBack = stencilFaceDescriptor;
256 state.stencilFront = stencilFaceDescriptor;
257 state.stencilReadMask = 0xff;
258 state.stencilWriteMask = 0xff;
259
260 testParams.push_back({state, RGBA8(0, 255, 0, 255), 0, expectedStencil});
261 DoTest(testParams, RGBA8(0, 255, 0, 255));
262 }
263
264 // Each test param represents a pair of triangles with a color, depth, stencil value, and
265 // depthStencil state, one frontfacing, one backfacing Draw the triangles in order and check the
266 // expected colors for the frontfaces and backfaces
DoTest(const std::vector<TestSpec> & testParams,const RGBA8 & expectedFront,const RGBA8 & expectedBack,bool isSingleEncoderMultiplePass=false)267 void DoTest(const std::vector<TestSpec>& testParams,
268 const RGBA8& expectedFront,
269 const RGBA8& expectedBack,
270 bool isSingleEncoderMultiplePass = false) {
271 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
272
273 struct TriangleData {
274 float color[3];
275 float depth;
276 };
277
278 utils::ComboRenderPassDescriptor renderPass({renderTargetView}, depthTextureView);
279 wgpu::RenderPassEncoder pass;
280
281 if (isSingleEncoderMultiplePass) {
282 // The render pass to clear up the depthTextureView (using LoadOp = clear)
283 utils::ComboRenderPassDescriptor clearingPass({renderTargetView}, depthTextureView);
284
285 // The render pass to do the test with depth and stencil result kept
286 renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
287 renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
288
289 // Clear the depthStencilView at the beginning
290 {
291 pass = encoder.BeginRenderPass(&renderPass);
292 pass.EndPass();
293 }
294 } else {
295 pass = encoder.BeginRenderPass(&renderPass);
296 }
297
298 for (size_t i = 0; i < testParams.size(); ++i) {
299 const TestSpec& test = testParams[i];
300
301 if (isSingleEncoderMultiplePass) {
302 pass = encoder.BeginRenderPass(&renderPass);
303 }
304
305 TriangleData data = {
306 {static_cast<float>(test.color.r) / 255.f, static_cast<float>(test.color.g) / 255.f,
307 static_cast<float>(test.color.b) / 255.f},
308 test.depth,
309 };
310 // Upload a buffer for each triangle's depth and color data
311 wgpu::Buffer buffer = utils::CreateBufferFromData(device, &data, sizeof(TriangleData),
312 wgpu::BufferUsage::Uniform);
313
314 // Create a pipeline for the triangles with the test spec's depth stencil state
315
316 utils::ComboRenderPipelineDescriptor descriptor;
317 descriptor.vertex.module = vsModule;
318 descriptor.cFragment.module = fsModule;
319 wgpu::DepthStencilState* depthStencil = descriptor.EnableDepthStencil();
320 *depthStencil = test.depthStencil;
321 depthStencil->format = wgpu::TextureFormat::Depth24PlusStencil8;
322 descriptor.primitive.frontFace = test.frontFace;
323
324 wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
325
326 // Create a bind group for the data
327 wgpu::BindGroup bindGroup = utils::MakeBindGroup(
328 device, pipeline.GetBindGroupLayout(0), {{0, buffer, 0, sizeof(TriangleData)}});
329
330 pass.SetPipeline(pipeline);
331 if (test.setStencilReference) {
332 pass.SetStencilReference(test.stencil); // Set the stencil reference
333 }
334 pass.SetBindGroup(0,
335 bindGroup); // Set the bind group which contains color and depth data
336 pass.Draw(6);
337
338 if (isSingleEncoderMultiplePass) {
339 pass.EndPass();
340 }
341 }
342
343 if (!isSingleEncoderMultiplePass) {
344 pass.EndPass();
345 }
346
347 wgpu::CommandBuffer commands = encoder.Finish();
348 queue.Submit(1, &commands);
349
350 EXPECT_PIXEL_RGBA8_EQ(expectedFront, renderTarget, kRTSize / 4, kRTSize / 2)
351 << "Front face check failed";
352 EXPECT_PIXEL_RGBA8_EQ(expectedBack, renderTarget, 3 * kRTSize / 4, kRTSize / 2)
353 << "Back face check failed";
354 }
355
DoTest(const std::vector<TestSpec> & testParams,const RGBA8 & expected,bool isSingleEncoderMultiplePass=false)356 void DoTest(const std::vector<TestSpec>& testParams,
357 const RGBA8& expected,
358 bool isSingleEncoderMultiplePass = false) {
359 DoTest(testParams, expected, expected, isSingleEncoderMultiplePass);
360 }
361
362 wgpu::Texture renderTarget;
363 wgpu::Texture depthTexture;
364 wgpu::TextureView renderTargetView;
365 wgpu::TextureView depthTextureView;
366 wgpu::ShaderModule vsModule;
367 wgpu::ShaderModule fsModule;
368 };
369
370 // Test compilation and usage of the fixture
TEST_P(DepthStencilStateTest,Basic)371 TEST_P(DepthStencilStateTest, Basic) {
372 wgpu::StencilFaceState stencilFace;
373 stencilFace.compare = wgpu::CompareFunction::Always;
374 stencilFace.failOp = wgpu::StencilOperation::Keep;
375 stencilFace.depthFailOp = wgpu::StencilOperation::Keep;
376 stencilFace.passOp = wgpu::StencilOperation::Keep;
377
378 wgpu::DepthStencilState state;
379 state.depthWriteEnabled = false;
380 state.depthCompare = wgpu::CompareFunction::Always;
381 state.stencilBack = stencilFace;
382 state.stencilFront = stencilFace;
383 state.stencilReadMask = 0xff;
384 state.stencilWriteMask = 0xff;
385
386 DoTest(
387 {
388 {state, RGBA8(0, 255, 0, 255), 0.5f, 0u},
389 },
390 RGBA8(0, 255, 0, 255));
391 }
392
393 // Test defaults: depth and stencil tests disabled
TEST_P(DepthStencilStateTest,DepthStencilDisabled)394 TEST_P(DepthStencilStateTest, DepthStencilDisabled) {
395 wgpu::StencilFaceState stencilFace;
396 stencilFace.compare = wgpu::CompareFunction::Always;
397 stencilFace.failOp = wgpu::StencilOperation::Keep;
398 stencilFace.depthFailOp = wgpu::StencilOperation::Keep;
399 stencilFace.passOp = wgpu::StencilOperation::Keep;
400
401 wgpu::DepthStencilState state;
402 state.depthWriteEnabled = false;
403 state.depthCompare = wgpu::CompareFunction::Always;
404 state.stencilBack = stencilFace;
405 state.stencilFront = stencilFace;
406 state.stencilReadMask = 0xff;
407 state.stencilWriteMask = 0xff;
408
409 TestSpec specs[3] = {
410 {state, RGBA8(255, 0, 0, 255), 0.0f, 0u},
411 {state, RGBA8(0, 255, 0, 255), 0.5f, 0u},
412 {state, RGBA8(0, 0, 255, 255), 1.0f, 0u},
413 };
414
415 // Test that for all combinations, the last triangle drawn is the one visible
416 // We check against three triangles because the stencil test may modify results
417 for (uint32_t last = 0; last < 3; ++last) {
418 uint32_t i = (last + 1) % 3;
419 uint32_t j = (last + 2) % 3;
420 DoTest({specs[i], specs[j], specs[last]}, specs[last].color);
421 DoTest({specs[j], specs[i], specs[last]}, specs[last].color);
422 }
423 }
424
425 // The following tests check that each depth comparison function works
TEST_P(DepthStencilStateTest,DepthAlways)426 TEST_P(DepthStencilStateTest, DepthAlways) {
427 CheckDepthCompareFunction(wgpu::CompareFunction::Always, true, true, true);
428 }
429
TEST_P(DepthStencilStateTest,DepthEqual)430 TEST_P(DepthStencilStateTest, DepthEqual) {
431 CheckDepthCompareFunction(wgpu::CompareFunction::Equal, false, true, false);
432 }
433
TEST_P(DepthStencilStateTest,DepthGreater)434 TEST_P(DepthStencilStateTest, DepthGreater) {
435 CheckDepthCompareFunction(wgpu::CompareFunction::Greater, false, false, true);
436 }
437
TEST_P(DepthStencilStateTest,DepthGreaterEqual)438 TEST_P(DepthStencilStateTest, DepthGreaterEqual) {
439 CheckDepthCompareFunction(wgpu::CompareFunction::GreaterEqual, false, true, true);
440 }
441
TEST_P(DepthStencilStateTest,DepthLess)442 TEST_P(DepthStencilStateTest, DepthLess) {
443 CheckDepthCompareFunction(wgpu::CompareFunction::Less, true, false, false);
444 }
445
TEST_P(DepthStencilStateTest,DepthLessEqual)446 TEST_P(DepthStencilStateTest, DepthLessEqual) {
447 CheckDepthCompareFunction(wgpu::CompareFunction::LessEqual, true, true, false);
448 }
449
TEST_P(DepthStencilStateTest,DepthNever)450 TEST_P(DepthStencilStateTest, DepthNever) {
451 CheckDepthCompareFunction(wgpu::CompareFunction::Never, false, false, false);
452 }
453
TEST_P(DepthStencilStateTest,DepthNotEqual)454 TEST_P(DepthStencilStateTest, DepthNotEqual) {
455 CheckDepthCompareFunction(wgpu::CompareFunction::NotEqual, true, false, true);
456 }
457
458 // Test that disabling depth writes works and leaves the depth buffer unchanged
TEST_P(DepthStencilStateTest,DepthWriteDisabled)459 TEST_P(DepthStencilStateTest, DepthWriteDisabled) {
460 wgpu::StencilFaceState stencilFace;
461 stencilFace.compare = wgpu::CompareFunction::Always;
462 stencilFace.failOp = wgpu::StencilOperation::Keep;
463 stencilFace.depthFailOp = wgpu::StencilOperation::Keep;
464 stencilFace.passOp = wgpu::StencilOperation::Keep;
465
466 wgpu::DepthStencilState baseState;
467 baseState.depthWriteEnabled = true;
468 baseState.depthCompare = wgpu::CompareFunction::Always;
469 baseState.stencilBack = stencilFace;
470 baseState.stencilFront = stencilFace;
471 baseState.stencilReadMask = 0xff;
472 baseState.stencilWriteMask = 0xff;
473
474 wgpu::DepthStencilState noDepthWrite;
475 noDepthWrite.depthWriteEnabled = false;
476 noDepthWrite.depthCompare = wgpu::CompareFunction::Always;
477 noDepthWrite.stencilBack = stencilFace;
478 noDepthWrite.stencilFront = stencilFace;
479 noDepthWrite.stencilReadMask = 0xff;
480 noDepthWrite.stencilWriteMask = 0xff;
481
482 wgpu::DepthStencilState checkState;
483 checkState.depthWriteEnabled = false;
484 checkState.depthCompare = wgpu::CompareFunction::Equal;
485 checkState.stencilBack = stencilFace;
486 checkState.stencilFront = stencilFace;
487 checkState.stencilReadMask = 0xff;
488 checkState.stencilWriteMask = 0xff;
489
490 DoTest(
491 {
492 {baseState, RGBA8(255, 255, 255, 255), 1.f,
493 0u}, // Draw a base triangle with depth enabled
494 {noDepthWrite, RGBA8(0, 0, 0, 255), 0.f,
495 0u}, // Draw a second triangle without depth enabled
496 {checkState, RGBA8(0, 255, 0, 255), 1.f,
497 0u}, // Draw a third triangle which should occlude the second even though it is behind
498 // it
499 },
500 RGBA8(0, 255, 0, 255));
501 }
502
503 // The following tests check that each stencil comparison function works
TEST_P(DepthStencilStateTest,StencilAlways)504 TEST_P(DepthStencilStateTest, StencilAlways) {
505 CheckStencilCompareFunction(wgpu::CompareFunction::Always, true, true, true);
506 }
507
TEST_P(DepthStencilStateTest,StencilEqual)508 TEST_P(DepthStencilStateTest, StencilEqual) {
509 CheckStencilCompareFunction(wgpu::CompareFunction::Equal, false, true, false);
510 }
511
TEST_P(DepthStencilStateTest,StencilGreater)512 TEST_P(DepthStencilStateTest, StencilGreater) {
513 CheckStencilCompareFunction(wgpu::CompareFunction::Greater, false, false, true);
514 }
515
TEST_P(DepthStencilStateTest,StencilGreaterEqual)516 TEST_P(DepthStencilStateTest, StencilGreaterEqual) {
517 CheckStencilCompareFunction(wgpu::CompareFunction::GreaterEqual, false, true, true);
518 }
519
TEST_P(DepthStencilStateTest,StencilLess)520 TEST_P(DepthStencilStateTest, StencilLess) {
521 CheckStencilCompareFunction(wgpu::CompareFunction::Less, true, false, false);
522 }
523
TEST_P(DepthStencilStateTest,StencilLessEqual)524 TEST_P(DepthStencilStateTest, StencilLessEqual) {
525 CheckStencilCompareFunction(wgpu::CompareFunction::LessEqual, true, true, false);
526 }
527
TEST_P(DepthStencilStateTest,StencilNever)528 TEST_P(DepthStencilStateTest, StencilNever) {
529 CheckStencilCompareFunction(wgpu::CompareFunction::Never, false, false, false);
530 }
531
TEST_P(DepthStencilStateTest,StencilNotEqual)532 TEST_P(DepthStencilStateTest, StencilNotEqual) {
533 CheckStencilCompareFunction(wgpu::CompareFunction::NotEqual, true, false, true);
534 }
535
536 // The following tests check that each stencil operation works
TEST_P(DepthStencilStateTest,StencilKeep)537 TEST_P(DepthStencilStateTest, StencilKeep) {
538 CheckStencilOperation(wgpu::StencilOperation::Keep, 1, 3, 1);
539 }
540
TEST_P(DepthStencilStateTest,StencilZero)541 TEST_P(DepthStencilStateTest, StencilZero) {
542 CheckStencilOperation(wgpu::StencilOperation::Zero, 1, 3, 0);
543 }
544
TEST_P(DepthStencilStateTest,StencilReplace)545 TEST_P(DepthStencilStateTest, StencilReplace) {
546 CheckStencilOperation(wgpu::StencilOperation::Replace, 1, 3, 3);
547 }
548
TEST_P(DepthStencilStateTest,StencilInvert)549 TEST_P(DepthStencilStateTest, StencilInvert) {
550 CheckStencilOperation(wgpu::StencilOperation::Invert, 0xf0, 3, 0x0f);
551 }
552
TEST_P(DepthStencilStateTest,StencilIncrementClamp)553 TEST_P(DepthStencilStateTest, StencilIncrementClamp) {
554 CheckStencilOperation(wgpu::StencilOperation::IncrementClamp, 1, 3, 2);
555 CheckStencilOperation(wgpu::StencilOperation::IncrementClamp, 0xff, 3, 0xff);
556 }
557
TEST_P(DepthStencilStateTest,StencilIncrementWrap)558 TEST_P(DepthStencilStateTest, StencilIncrementWrap) {
559 CheckStencilOperation(wgpu::StencilOperation::IncrementWrap, 1, 3, 2);
560 CheckStencilOperation(wgpu::StencilOperation::IncrementWrap, 0xff, 3, 0);
561 }
562
TEST_P(DepthStencilStateTest,StencilDecrementClamp)563 TEST_P(DepthStencilStateTest, StencilDecrementClamp) {
564 CheckStencilOperation(wgpu::StencilOperation::DecrementClamp, 1, 3, 0);
565 CheckStencilOperation(wgpu::StencilOperation::DecrementClamp, 0, 3, 0);
566 }
567
TEST_P(DepthStencilStateTest,StencilDecrementWrap)568 TEST_P(DepthStencilStateTest, StencilDecrementWrap) {
569 CheckStencilOperation(wgpu::StencilOperation::DecrementWrap, 1, 3, 0);
570 CheckStencilOperation(wgpu::StencilOperation::DecrementWrap, 0, 3, 0xff);
571 }
572
573 // Check that the setting a stencil read mask works
TEST_P(DepthStencilStateTest,StencilReadMask)574 TEST_P(DepthStencilStateTest, StencilReadMask) {
575 wgpu::StencilFaceState baseStencilFaceDescriptor;
576 baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always;
577 baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
578 baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
579 baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace;
580 wgpu::DepthStencilState baseState;
581 baseState.depthWriteEnabled = false;
582 baseState.depthCompare = wgpu::CompareFunction::Always;
583 baseState.stencilBack = baseStencilFaceDescriptor;
584 baseState.stencilFront = baseStencilFaceDescriptor;
585 baseState.stencilReadMask = 0xff;
586 baseState.stencilWriteMask = 0xff;
587
588 wgpu::StencilFaceState stencilFaceDescriptor;
589 stencilFaceDescriptor.compare = wgpu::CompareFunction::Equal;
590 stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
591 stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
592 stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep;
593 wgpu::DepthStencilState state;
594 state.depthWriteEnabled = false;
595 state.depthCompare = wgpu::CompareFunction::Always;
596 state.stencilBack = stencilFaceDescriptor;
597 state.stencilFront = stencilFaceDescriptor;
598 state.stencilReadMask = 0x2;
599 state.stencilWriteMask = 0xff;
600
601 RGBA8 baseColor = RGBA8(255, 255, 255, 255);
602 RGBA8 red = RGBA8(255, 0, 0, 255);
603 RGBA8 green = RGBA8(0, 255, 0, 255);
604
605 TestSpec base = {baseState, baseColor, 0.5f, 3u}; // Base triangle to set the stencil to 3
606 DoTest({base, {state, red, 0.f, 1u}}, baseColor); // Triangle with stencil reference 1 and read
607 // mask 2 does not draw because (3 & 2 != 1)
608 DoTest({base, {state, green, 0.f, 2u}},
609 green); // Triangle with stencil reference 2 and read mask 2 draws because (3 & 2 == 2)
610 }
611
612 // Check that setting a stencil write mask works
TEST_P(DepthStencilStateTest,StencilWriteMask)613 TEST_P(DepthStencilStateTest, StencilWriteMask) {
614 wgpu::StencilFaceState baseStencilFaceDescriptor;
615 baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always;
616 baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
617 baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
618 baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace;
619 wgpu::DepthStencilState baseState;
620 baseState.depthWriteEnabled = false;
621 baseState.depthCompare = wgpu::CompareFunction::Always;
622 baseState.stencilBack = baseStencilFaceDescriptor;
623 baseState.stencilFront = baseStencilFaceDescriptor;
624 baseState.stencilReadMask = 0xff;
625 baseState.stencilWriteMask = 0x1;
626
627 wgpu::StencilFaceState stencilFaceDescriptor;
628 stencilFaceDescriptor.compare = wgpu::CompareFunction::Equal;
629 stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
630 stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
631 stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep;
632 wgpu::DepthStencilState state;
633 state.depthWriteEnabled = false;
634 state.depthCompare = wgpu::CompareFunction::Always;
635 state.stencilBack = stencilFaceDescriptor;
636 state.stencilFront = stencilFaceDescriptor;
637 state.stencilReadMask = 0xff;
638 state.stencilWriteMask = 0xff;
639
640 RGBA8 baseColor = RGBA8(255, 255, 255, 255);
641 RGBA8 green = RGBA8(0, 255, 0, 255);
642
643 TestSpec base = {baseState, baseColor, 0.5f,
644 3u}; // Base triangle with stencil reference 3 and mask 1 to set the stencil 1
645 DoTest({base, {state, green, 0.f, 2u}},
646 baseColor); // Triangle with stencil reference 2 does not draw because 2 != (3 & 1)
647 DoTest({base, {state, green, 0.f, 1u}},
648 green); // Triangle with stencil reference 1 draws because 1 == (3 & 1)
649 }
650
651 // Test that the stencil operation is executed on stencil fail
TEST_P(DepthStencilStateTest,StencilFail)652 TEST_P(DepthStencilStateTest, StencilFail) {
653 wgpu::StencilFaceState baseStencilFaceDescriptor;
654 baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always;
655 baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
656 baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
657 baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace;
658 wgpu::DepthStencilState baseState;
659 baseState.depthWriteEnabled = false;
660 baseState.depthCompare = wgpu::CompareFunction::Always;
661 baseState.stencilBack = baseStencilFaceDescriptor;
662 baseState.stencilFront = baseStencilFaceDescriptor;
663 baseState.stencilReadMask = 0xff;
664 baseState.stencilWriteMask = 0xff;
665
666 wgpu::StencilFaceState stencilFaceDescriptor;
667 stencilFaceDescriptor.compare = wgpu::CompareFunction::Less;
668 stencilFaceDescriptor.failOp = wgpu::StencilOperation::Replace;
669 stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
670 stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep;
671 wgpu::DepthStencilState state;
672 state.depthWriteEnabled = false;
673 state.depthCompare = wgpu::CompareFunction::Always;
674 state.stencilBack = stencilFaceDescriptor;
675 state.stencilFront = stencilFaceDescriptor;
676 state.stencilReadMask = 0xff;
677 state.stencilWriteMask = 0xff;
678
679 CheckStencil(
680 {
681 {baseState, RGBA8(255, 255, 255, 255), 1.f, 1}, // Triangle to set stencil value to 1
682 {state, RGBA8(0, 0, 0, 255), 0.f,
683 2} // Triangle with stencil reference 2 fails the Less comparison function
684 },
685 2); // Replace the stencil on failure, so it should be 2
686 }
687
688 // Test that the stencil operation is executed on stencil pass, depth fail
TEST_P(DepthStencilStateTest,StencilDepthFail)689 TEST_P(DepthStencilStateTest, StencilDepthFail) {
690 wgpu::StencilFaceState baseStencilFaceDescriptor;
691 baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always;
692 baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
693 baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
694 baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace;
695 wgpu::DepthStencilState baseState;
696 baseState.depthWriteEnabled = true;
697 baseState.depthCompare = wgpu::CompareFunction::Always;
698 baseState.stencilBack = baseStencilFaceDescriptor;
699 baseState.stencilFront = baseStencilFaceDescriptor;
700 baseState.stencilReadMask = 0xff;
701 baseState.stencilWriteMask = 0xff;
702
703 wgpu::StencilFaceState stencilFaceDescriptor;
704 stencilFaceDescriptor.compare = wgpu::CompareFunction::Greater;
705 stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
706 stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Replace;
707 stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep;
708 wgpu::DepthStencilState state;
709 state.depthWriteEnabled = true;
710 state.depthCompare = wgpu::CompareFunction::Less;
711 state.stencilBack = stencilFaceDescriptor;
712 state.stencilFront = stencilFaceDescriptor;
713 state.stencilReadMask = 0xff;
714 state.stencilWriteMask = 0xff;
715
716 CheckStencil({{baseState, RGBA8(255, 255, 255, 255), 0.f,
717 1}, // Triangle to set stencil value to 1. Depth is 0
718 {state, RGBA8(0, 0, 0, 255), 1.f,
719 2}}, // Triangle with stencil reference 2 passes the Greater comparison
720 // function. At depth 1, it fails the Less depth test
721 2); // Replace the stencil on stencil pass, depth failure, so it should be 2
722 }
723
724 // Test that the stencil operation is executed on stencil pass, depth pass
TEST_P(DepthStencilStateTest,StencilDepthPass)725 TEST_P(DepthStencilStateTest, StencilDepthPass) {
726 wgpu::StencilFaceState baseStencilFaceDescriptor;
727 baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always;
728 baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
729 baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
730 baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace;
731 wgpu::DepthStencilState baseState;
732 baseState.depthWriteEnabled = true;
733 baseState.depthCompare = wgpu::CompareFunction::Always;
734 baseState.stencilBack = baseStencilFaceDescriptor;
735 baseState.stencilFront = baseStencilFaceDescriptor;
736 baseState.stencilReadMask = 0xff;
737 baseState.stencilWriteMask = 0xff;
738
739 wgpu::StencilFaceState stencilFaceDescriptor;
740 stencilFaceDescriptor.compare = wgpu::CompareFunction::Greater;
741 stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep;
742 stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep;
743 stencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace;
744 wgpu::DepthStencilState state;
745 state.depthWriteEnabled = true;
746 state.depthCompare = wgpu::CompareFunction::Less;
747 state.stencilBack = stencilFaceDescriptor;
748 state.stencilFront = stencilFaceDescriptor;
749 state.stencilReadMask = 0xff;
750 state.stencilWriteMask = 0xff;
751
752 CheckStencil({{baseState, RGBA8(255, 255, 255, 255), 1.f,
753 1}, // Triangle to set stencil value to 1. Depth is 0
754 {state, RGBA8(0, 0, 0, 255), 0.f,
755 2}}, // Triangle with stencil reference 2 passes the Greater comparison
756 // function. At depth 0, it pass the Less depth test
757 2); // Replace the stencil on stencil pass, depth pass, so it should be 2
758 }
759
760 // Test that creating a render pipeline works with for all depth and combined formats
TEST_P(DepthStencilStateTest,CreatePipelineWithAllFormats)761 TEST_P(DepthStencilStateTest, CreatePipelineWithAllFormats) {
762 constexpr wgpu::TextureFormat kDepthStencilFormats[] = {
763 wgpu::TextureFormat::Depth32Float,
764 wgpu::TextureFormat::Depth24PlusStencil8,
765 wgpu::TextureFormat::Depth24Plus,
766 wgpu::TextureFormat::Depth16Unorm,
767 };
768
769 for (wgpu::TextureFormat depthStencilFormat : kDepthStencilFormats) {
770 utils::ComboRenderPipelineDescriptor descriptor;
771 descriptor.vertex.module = vsModule;
772 descriptor.cFragment.module = fsModule;
773 descriptor.EnableDepthStencil(depthStencilFormat);
774
775 device.CreateRenderPipeline(&descriptor);
776 }
777 }
778
779 // Test that the front and back stencil states are set correctly (and take frontFace into account)
TEST_P(DepthStencilStateTest,StencilFrontAndBackFace)780 TEST_P(DepthStencilStateTest, StencilFrontAndBackFace) {
781 wgpu::DepthStencilState state;
782 state.stencilFront.compare = wgpu::CompareFunction::Always;
783 state.stencilBack.compare = wgpu::CompareFunction::Never;
784
785 // The front facing triangle passes the stencil comparison but the back facing one doesn't.
786 DoTest({{state, RGBA8::kRed, 0.f, 0u, wgpu::FrontFace::CCW}}, RGBA8::kRed, RGBA8::kZero);
787 DoTest({{state, RGBA8::kRed, 0.f, 0u, wgpu::FrontFace::CW}}, RGBA8::kZero, RGBA8::kRed);
788 }
789
790 // Test that the depth reference of a new render pass is initialized to default value 0
TEST_P(DepthStencilStateTest,StencilReferenceInitialized)791 TEST_P(DepthStencilStateTest, StencilReferenceInitialized) {
792 wgpu::DepthStencilState stencilAlwaysReplaceState;
793 stencilAlwaysReplaceState.stencilFront.compare = wgpu::CompareFunction::Always;
794 stencilAlwaysReplaceState.stencilFront.passOp = wgpu::StencilOperation::Replace;
795 stencilAlwaysReplaceState.stencilBack.compare = wgpu::CompareFunction::Always;
796 stencilAlwaysReplaceState.stencilBack.passOp = wgpu::StencilOperation::Replace;
797
798 wgpu::DepthStencilState stencilEqualKeepState;
799 stencilEqualKeepState.stencilFront.compare = wgpu::CompareFunction::Equal;
800 stencilEqualKeepState.stencilFront.passOp = wgpu::StencilOperation::Keep;
801 stencilEqualKeepState.stencilBack.compare = wgpu::CompareFunction::Equal;
802 stencilEqualKeepState.stencilBack.passOp = wgpu::StencilOperation::Keep;
803
804 // Test that stencil reference is not inherited
805 {
806 // First pass sets the stencil to 0x1, and the second pass tests the stencil
807 // Only set the stencil reference in the first pass, and test that for other pass it should
808 // be default value rather than inherited
809 std::vector<TestSpec> testParams = {
810 {stencilAlwaysReplaceState, RGBA8::kRed, 0.f, 0x1, wgpu::FrontFace::CCW, true},
811 {stencilEqualKeepState, RGBA8::kGreen, 0.f, 0x0, wgpu::FrontFace::CCW, false}};
812
813 // Since the stencil reference is not inherited, second draw won't pass the stencil test
814 std::pair<RGBA8, RGBA8> expectation = {RGBA8::kZero, RGBA8::kZero};
815
816 DoTest(testParams, expectation.first, expectation.second, true);
817 }
818
819 // Test that stencil reference is initialized as zero for new render pass
820 {
821 // First pass sets the stencil to 0x1, the second pass sets the stencil to its default
822 // value, and the third pass tests if the stencil is zero
823 std::vector<TestSpec> testParams = {
824 {stencilAlwaysReplaceState, RGBA8::kRed, 0.f, 0x1, wgpu::FrontFace::CCW, true},
825 {stencilAlwaysReplaceState, RGBA8::kGreen, 0.f, 0x1, wgpu::FrontFace::CCW, false},
826 {stencilEqualKeepState, RGBA8::kBlue, 0.f, 0x0, wgpu::FrontFace::CCW, true}};
827
828 // The third draw should pass the stencil test since the second pass set it to default zero
829 std::pair<RGBA8, RGBA8> expectation = {RGBA8::kBlue, RGBA8::kBlue};
830
831 DoTest(testParams, expectation.first, expectation.second, true);
832 }
833 }
834
835 DAWN_INSTANTIATE_TEST(DepthStencilStateTest,
836 D3D12Backend(),
837 MetalBackend(),
838 OpenGLBackend(),
839 OpenGLESBackend(),
840 VulkanBackend({"vulkan_use_d32s8"}, {}),
841 VulkanBackend({}, {"vulkan_use_d32s8"}));
842