| // Copyright 2017 The Dawn Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "tests/DawnTest.h" |
| |
| #include "common/Assert.h" |
| #include "utils/ComboRenderPipelineDescriptor.h" |
| #include "utils/WGPUHelpers.h" |
| |
| constexpr static unsigned int kRTSize = 64; |
| |
| class DepthStencilStateTest : public DawnTest { |
| protected: |
| void SetUp() override { |
| DawnTest::SetUp(); |
| |
| wgpu::TextureDescriptor renderTargetDescriptor; |
| renderTargetDescriptor.dimension = wgpu::TextureDimension::e2D; |
| renderTargetDescriptor.size.width = kRTSize; |
| renderTargetDescriptor.size.height = kRTSize; |
| renderTargetDescriptor.size.depth = 1; |
| renderTargetDescriptor.sampleCount = 1; |
| renderTargetDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; |
| renderTargetDescriptor.mipLevelCount = 1; |
| renderTargetDescriptor.usage = |
| wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; |
| renderTarget = device.CreateTexture(&renderTargetDescriptor); |
| |
| renderTargetView = renderTarget.CreateView(); |
| |
| wgpu::TextureDescriptor depthDescriptor; |
| depthDescriptor.dimension = wgpu::TextureDimension::e2D; |
| depthDescriptor.size.width = kRTSize; |
| depthDescriptor.size.height = kRTSize; |
| depthDescriptor.size.depth = 1; |
| depthDescriptor.sampleCount = 1; |
| depthDescriptor.format = wgpu::TextureFormat::Depth24PlusStencil8; |
| depthDescriptor.mipLevelCount = 1; |
| depthDescriptor.usage = wgpu::TextureUsage::OutputAttachment; |
| depthTexture = device.CreateTexture(&depthDescriptor); |
| |
| depthTextureView = depthTexture.CreateView(); |
| |
| vsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( |
| #version 450 |
| layout(set = 0, binding = 0) uniform myBlock { |
| vec3 color; |
| float depth; |
| } myUbo; |
| void main() { |
| const vec2 pos[6] = vec2[6]( |
| vec2(-1.f, 1.f), vec2(-1.f, -1.f), vec2(1.f, -1.f), // front-facing |
| vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(1.f, -1.f) // back-facing |
| ); |
| gl_Position = vec4(pos[gl_VertexIndex], myUbo.depth, 1.f); |
| } |
| )"); |
| |
| fsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( |
| #version 450 |
| layout(set = 0, binding = 0) uniform myBlock { |
| vec3 color; |
| float depth; |
| } myUbo; |
| layout(location = 0) out vec4 fragColor; |
| void main() { |
| fragColor = vec4(myUbo.color, 1.f); |
| } |
| )"); |
| } |
| |
| struct TestSpec { |
| const wgpu::DepthStencilStateDescriptor& depthStencilState; |
| RGBA8 color; |
| float depth; |
| uint32_t stencil; |
| }; |
| |
| // Check whether a depth comparison function works as expected |
| // The less, equal, greater booleans denote wether the respective triangle should be visible based on the comparison function |
| void CheckDepthCompareFunction(wgpu::CompareFunction compareFunction, |
| bool less, |
| bool equal, |
| bool greater) { |
| wgpu::StencilStateFaceDescriptor stencilFace; |
| stencilFace.compare = wgpu::CompareFunction::Always; |
| stencilFace.failOp = wgpu::StencilOperation::Keep; |
| stencilFace.depthFailOp = wgpu::StencilOperation::Keep; |
| stencilFace.passOp = wgpu::StencilOperation::Keep; |
| |
| wgpu::DepthStencilStateDescriptor baseState; |
| baseState.depthWriteEnabled = true; |
| baseState.depthCompare = wgpu::CompareFunction::Always; |
| baseState.stencilBack = stencilFace; |
| baseState.stencilFront = stencilFace; |
| baseState.stencilReadMask = 0xff; |
| baseState.stencilWriteMask = 0xff; |
| |
| wgpu::DepthStencilStateDescriptor state; |
| state.depthWriteEnabled = true; |
| state.depthCompare = compareFunction; |
| state.stencilBack = stencilFace; |
| state.stencilFront = stencilFace; |
| state.stencilReadMask = 0xff; |
| state.stencilWriteMask = 0xff; |
| |
| RGBA8 baseColor = RGBA8(255, 255, 255, 255); |
| RGBA8 lessColor = RGBA8(255, 0, 0, 255); |
| RGBA8 equalColor = RGBA8(0, 255, 0, 255); |
| RGBA8 greaterColor = RGBA8(0, 0, 255, 255); |
| |
| // Base triangle at depth 0.5, depth always, depth write enabled |
| TestSpec base = { baseState, baseColor, 0.5f, 0u }; |
| |
| // Draw the base triangle, then a triangle in stencilFront of the base triangle with the |
| // given depth comparison function |
| DoTest({ base, { state, lessColor, 0.f, 0u } }, less ? lessColor : baseColor); |
| |
| // Draw the base triangle, then a triangle in at the same depth as the base triangle with the given depth comparison function |
| DoTest({ base, { state, equalColor, 0.5f, 0u } }, equal ? equalColor : baseColor); |
| |
| // Draw the base triangle, then a triangle behind the base triangle with the given depth comparison function |
| DoTest({ base, { state, greaterColor, 1.0f, 0u } }, greater ? greaterColor : baseColor); |
| } |
| |
| // Check whether a stencil comparison function works as expected |
| // The less, equal, greater booleans denote wether the respective triangle should be visible based on the comparison function |
| void CheckStencilCompareFunction(wgpu::CompareFunction compareFunction, |
| bool less, |
| bool equal, |
| bool greater) { |
| wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; |
| baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; |
| baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; |
| wgpu::DepthStencilStateDescriptor baseState; |
| baseState.depthWriteEnabled = false; |
| baseState.depthCompare = wgpu::CompareFunction::Always; |
| baseState.stencilBack = baseStencilFaceDescriptor; |
| baseState.stencilFront = baseStencilFaceDescriptor; |
| baseState.stencilReadMask = 0xff; |
| baseState.stencilWriteMask = 0xff; |
| |
| wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; |
| stencilFaceDescriptor.compare = compareFunction; |
| stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; |
| wgpu::DepthStencilStateDescriptor state; |
| state.depthWriteEnabled = false; |
| state.depthCompare = wgpu::CompareFunction::Always; |
| state.stencilBack = stencilFaceDescriptor; |
| state.stencilFront = stencilFaceDescriptor; |
| state.stencilReadMask = 0xff; |
| state.stencilWriteMask = 0xff; |
| |
| RGBA8 baseColor = RGBA8(255, 255, 255, 255); |
| RGBA8 lessColor = RGBA8(255, 0, 0, 255); |
| RGBA8 equalColor = RGBA8(0, 255, 0, 255); |
| RGBA8 greaterColor = RGBA8(0, 0, 255, 255); |
| |
| // Base triangle with stencil reference 1 |
| TestSpec base = { baseState, baseColor, 0.0f, 1u }; |
| |
| // Draw the base triangle, then a triangle with stencil reference 0 with the given stencil comparison function |
| DoTest({ base, { state, lessColor, 0.f, 0u } }, less ? lessColor : baseColor); |
| |
| // Draw the base triangle, then a triangle with stencil reference 1 with the given stencil comparison function |
| DoTest({ base, { state, equalColor, 0.f, 1u } }, equal ? equalColor : baseColor); |
| |
| // Draw the base triangle, then a triangle with stencil reference 2 with the given stencil comparison function |
| DoTest({ base, { state, greaterColor, 0.f, 2u } }, greater ? greaterColor : baseColor); |
| } |
| |
| // Given the provided `initialStencil` and `reference`, check that applying the `stencilOperation` produces the `expectedStencil` |
| void CheckStencilOperation(wgpu::StencilOperation stencilOperation, |
| uint32_t initialStencil, |
| uint32_t reference, |
| uint32_t expectedStencil) { |
| wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; |
| baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; |
| baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; |
| wgpu::DepthStencilStateDescriptor baseState; |
| baseState.depthWriteEnabled = false; |
| baseState.depthCompare = wgpu::CompareFunction::Always; |
| baseState.stencilBack = baseStencilFaceDescriptor; |
| baseState.stencilFront = baseStencilFaceDescriptor; |
| baseState.stencilReadMask = 0xff; |
| baseState.stencilWriteMask = 0xff; |
| |
| wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; |
| stencilFaceDescriptor.compare = wgpu::CompareFunction::Always; |
| stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.passOp = stencilOperation; |
| wgpu::DepthStencilStateDescriptor state; |
| state.depthWriteEnabled = false; |
| state.depthCompare = wgpu::CompareFunction::Always; |
| state.stencilBack = stencilFaceDescriptor; |
| state.stencilFront = stencilFaceDescriptor; |
| state.stencilReadMask = 0xff; |
| state.stencilWriteMask = 0xff; |
| |
| CheckStencil({ |
| // Wipe the stencil buffer with the initialStencil value |
| { baseState, RGBA8(255, 255, 255, 255), 0.f, initialStencil }, |
| |
| // Draw a triangle with the provided stencil operation and reference |
| { state, RGBA8(255, 0, 0, 255), 0.f, reference }, |
| }, expectedStencil); |
| } |
| |
| // Draw a list of test specs, and check if the stencil value is equal to the expected value |
| void CheckStencil(std::vector<TestSpec> testParams, uint32_t expectedStencil) { |
| wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; |
| stencilFaceDescriptor.compare = wgpu::CompareFunction::Equal; |
| stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; |
| wgpu::DepthStencilStateDescriptor state; |
| state.depthWriteEnabled = false; |
| state.depthCompare = wgpu::CompareFunction::Always; |
| state.stencilBack = stencilFaceDescriptor; |
| state.stencilFront = stencilFaceDescriptor; |
| state.stencilReadMask = 0xff; |
| state.stencilWriteMask = 0xff; |
| |
| testParams.push_back({ state, RGBA8(0, 255, 0, 255), 0, expectedStencil }); |
| DoTest(testParams, RGBA8(0, 255, 0, 255)); |
| } |
| |
| // Each test param represents a pair of triangles with a color, depth, stencil value, and depthStencil state, one frontfacing, one backfacing |
| // Draw the triangles in order and check the expected colors for the frontfaces and backfaces |
| void DoTest(const std::vector<TestSpec> &testParams, const RGBA8& expectedFront, const RGBA8& expectedBack) { |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); |
| |
| struct TriangleData { |
| float color[3]; |
| float depth; |
| }; |
| |
| utils::ComboRenderPassDescriptor renderPass({renderTargetView}, depthTextureView); |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); |
| |
| for (size_t i = 0; i < testParams.size(); ++i) { |
| const TestSpec& test = testParams[i]; |
| |
| TriangleData data = { |
| { static_cast<float>(test.color.r) / 255.f, static_cast<float>(test.color.g) / 255.f, static_cast<float>(test.color.b) / 255.f }, |
| test.depth, |
| }; |
| // Upload a buffer for each triangle's depth and color data |
| wgpu::Buffer buffer = utils::CreateBufferFromData( |
| device, &data, sizeof(TriangleData), wgpu::BufferUsage::Uniform); |
| |
| // Create a pipeline for the triangles with the test spec's depth stencil state |
| |
| utils::ComboRenderPipelineDescriptor descriptor(device); |
| descriptor.vertexStage.module = vsModule; |
| descriptor.cFragmentStage.module = fsModule; |
| descriptor.cDepthStencilState = test.depthStencilState; |
| descriptor.cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; |
| descriptor.depthStencilState = &descriptor.cDepthStencilState; |
| |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); |
| |
| // Create a bind group for the data |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup( |
| device, pipeline.GetBindGroupLayout(0), {{0, buffer, 0, sizeof(TriangleData)}}); |
| |
| pass.SetPipeline(pipeline); |
| pass.SetStencilReference(test.stencil); // Set the stencil reference |
| pass.SetBindGroup( |
| 0, bindGroup); // Set the bind group which contains color and depth data |
| pass.Draw(6); |
| } |
| pass.EndPass(); |
| |
| wgpu::CommandBuffer commands = encoder.Finish(); |
| queue.Submit(1, &commands); |
| |
| EXPECT_PIXEL_RGBA8_EQ(expectedFront, renderTarget, kRTSize / 4, kRTSize / 2) << "Front face check failed"; |
| EXPECT_PIXEL_RGBA8_EQ(expectedBack, renderTarget, 3 * kRTSize / 4, kRTSize / 2) << "Back face check failed"; |
| } |
| |
| void DoTest(const std::vector<TestSpec> &testParams, const RGBA8& expected) { |
| DoTest(testParams, expected, expected); |
| } |
| |
| wgpu::Texture renderTarget; |
| wgpu::Texture depthTexture; |
| wgpu::TextureView renderTargetView; |
| wgpu::TextureView depthTextureView; |
| wgpu::ShaderModule vsModule; |
| wgpu::ShaderModule fsModule; |
| }; |
| |
| // Test compilation and usage of the fixture |
| TEST_P(DepthStencilStateTest, Basic) { |
| wgpu::StencilStateFaceDescriptor stencilFace; |
| stencilFace.compare = wgpu::CompareFunction::Always; |
| stencilFace.failOp = wgpu::StencilOperation::Keep; |
| stencilFace.depthFailOp = wgpu::StencilOperation::Keep; |
| stencilFace.passOp = wgpu::StencilOperation::Keep; |
| |
| wgpu::DepthStencilStateDescriptor state; |
| state.depthWriteEnabled = false; |
| state.depthCompare = wgpu::CompareFunction::Always; |
| state.stencilBack = stencilFace; |
| state.stencilFront = stencilFace; |
| state.stencilReadMask = 0xff; |
| state.stencilWriteMask = 0xff; |
| |
| DoTest({ |
| { state, RGBA8(0, 255, 0, 255), 0.5f, 0u }, |
| }, RGBA8(0, 255, 0, 255)); |
| } |
| |
| // Test defaults: depth and stencil tests disabled |
| TEST_P(DepthStencilStateTest, DepthStencilDisabled) { |
| wgpu::StencilStateFaceDescriptor stencilFace; |
| stencilFace.compare = wgpu::CompareFunction::Always; |
| stencilFace.failOp = wgpu::StencilOperation::Keep; |
| stencilFace.depthFailOp = wgpu::StencilOperation::Keep; |
| stencilFace.passOp = wgpu::StencilOperation::Keep; |
| |
| wgpu::DepthStencilStateDescriptor state; |
| state.depthWriteEnabled = false; |
| state.depthCompare = wgpu::CompareFunction::Always; |
| state.stencilBack = stencilFace; |
| state.stencilFront = stencilFace; |
| state.stencilReadMask = 0xff; |
| state.stencilWriteMask = 0xff; |
| |
| TestSpec specs[3] = { |
| { state, RGBA8(255, 0, 0, 255), 0.0f, 0u }, |
| { state, RGBA8(0, 255, 0, 255), 0.5f, 0u }, |
| { state, RGBA8(0, 0, 255, 255), 1.0f, 0u }, |
| }; |
| |
| // Test that for all combinations, the last triangle drawn is the one visible |
| // We check against three triangles because the stencil test may modify results |
| for (uint32_t last = 0; last < 3; ++last) { |
| uint32_t i = (last + 1) % 3; |
| uint32_t j = (last + 2) % 3; |
| DoTest({ specs[i], specs[j], specs[last] }, specs[last].color); |
| DoTest({ specs[j], specs[i], specs[last] }, specs[last].color); |
| } |
| } |
| |
| // The following tests check that each depth comparison function works |
| TEST_P(DepthStencilStateTest, DepthAlways) { |
| CheckDepthCompareFunction(wgpu::CompareFunction::Always, true, true, true); |
| } |
| |
| TEST_P(DepthStencilStateTest, DepthEqual) { |
| CheckDepthCompareFunction(wgpu::CompareFunction::Equal, false, true, false); |
| } |
| |
| TEST_P(DepthStencilStateTest, DepthGreater) { |
| CheckDepthCompareFunction(wgpu::CompareFunction::Greater, false, false, true); |
| } |
| |
| TEST_P(DepthStencilStateTest, DepthGreaterEqual) { |
| CheckDepthCompareFunction(wgpu::CompareFunction::GreaterEqual, false, true, true); |
| } |
| |
| TEST_P(DepthStencilStateTest, DepthLess) { |
| CheckDepthCompareFunction(wgpu::CompareFunction::Less, true, false, false); |
| } |
| |
| TEST_P(DepthStencilStateTest, DepthLessEqual) { |
| CheckDepthCompareFunction(wgpu::CompareFunction::LessEqual, true, true, false); |
| } |
| |
| TEST_P(DepthStencilStateTest, DepthNever) { |
| CheckDepthCompareFunction(wgpu::CompareFunction::Never, false, false, false); |
| } |
| |
| TEST_P(DepthStencilStateTest, DepthNotEqual) { |
| CheckDepthCompareFunction(wgpu::CompareFunction::NotEqual, true, false, true); |
| } |
| |
| // Test that disabling depth writes works and leaves the depth buffer unchanged |
| TEST_P(DepthStencilStateTest, DepthWriteDisabled) { |
| wgpu::StencilStateFaceDescriptor stencilFace; |
| stencilFace.compare = wgpu::CompareFunction::Always; |
| stencilFace.failOp = wgpu::StencilOperation::Keep; |
| stencilFace.depthFailOp = wgpu::StencilOperation::Keep; |
| stencilFace.passOp = wgpu::StencilOperation::Keep; |
| |
| wgpu::DepthStencilStateDescriptor baseState; |
| baseState.depthWriteEnabled = true; |
| baseState.depthCompare = wgpu::CompareFunction::Always; |
| baseState.stencilBack = stencilFace; |
| baseState.stencilFront = stencilFace; |
| baseState.stencilReadMask = 0xff; |
| baseState.stencilWriteMask = 0xff; |
| |
| wgpu::DepthStencilStateDescriptor noDepthWrite; |
| noDepthWrite.depthWriteEnabled = false; |
| noDepthWrite.depthCompare = wgpu::CompareFunction::Always; |
| noDepthWrite.stencilBack = stencilFace; |
| noDepthWrite.stencilFront = stencilFace; |
| noDepthWrite.stencilReadMask = 0xff; |
| noDepthWrite.stencilWriteMask = 0xff; |
| |
| wgpu::DepthStencilStateDescriptor checkState; |
| checkState.depthWriteEnabled = false; |
| checkState.depthCompare = wgpu::CompareFunction::Equal; |
| checkState.stencilBack = stencilFace; |
| checkState.stencilFront = stencilFace; |
| checkState.stencilReadMask = 0xff; |
| checkState.stencilWriteMask = 0xff; |
| |
| DoTest({ |
| { baseState, RGBA8(255, 255, 255, 255), 1.f, 0u }, // Draw a base triangle with depth enabled |
| { noDepthWrite, RGBA8(0, 0, 0, 255), 0.f, 0u }, // Draw a second triangle without depth enabled |
| { checkState, RGBA8(0, 255, 0, 255), 1.f, 0u }, // Draw a third triangle which should occlude the second even though it is behind it |
| }, RGBA8(0, 255, 0, 255)); |
| } |
| |
| // The following tests check that each stencil comparison function works |
| TEST_P(DepthStencilStateTest, StencilAlways) { |
| CheckStencilCompareFunction(wgpu::CompareFunction::Always, true, true, true); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilEqual) { |
| CheckStencilCompareFunction(wgpu::CompareFunction::Equal, false, true, false); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilGreater) { |
| CheckStencilCompareFunction(wgpu::CompareFunction::Greater, false, false, true); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilGreaterEqual) { |
| CheckStencilCompareFunction(wgpu::CompareFunction::GreaterEqual, false, true, true); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilLess) { |
| CheckStencilCompareFunction(wgpu::CompareFunction::Less, true, false, false); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilLessEqual) { |
| CheckStencilCompareFunction(wgpu::CompareFunction::LessEqual, true, true, false); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilNever) { |
| CheckStencilCompareFunction(wgpu::CompareFunction::Never, false, false, false); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilNotEqual) { |
| CheckStencilCompareFunction(wgpu::CompareFunction::NotEqual, true, false, true); |
| } |
| |
| // The following tests check that each stencil operation works |
| TEST_P(DepthStencilStateTest, StencilKeep) { |
| CheckStencilOperation(wgpu::StencilOperation::Keep, 1, 3, 1); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilZero) { |
| CheckStencilOperation(wgpu::StencilOperation::Zero, 1, 3, 0); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilReplace) { |
| CheckStencilOperation(wgpu::StencilOperation::Replace, 1, 3, 3); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilInvert) { |
| CheckStencilOperation(wgpu::StencilOperation::Invert, 0xf0, 3, 0x0f); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilIncrementClamp) { |
| CheckStencilOperation(wgpu::StencilOperation::IncrementClamp, 1, 3, 2); |
| CheckStencilOperation(wgpu::StencilOperation::IncrementClamp, 0xff, 3, 0xff); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilIncrementWrap) { |
| CheckStencilOperation(wgpu::StencilOperation::IncrementWrap, 1, 3, 2); |
| CheckStencilOperation(wgpu::StencilOperation::IncrementWrap, 0xff, 3, 0); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilDecrementClamp) { |
| CheckStencilOperation(wgpu::StencilOperation::DecrementClamp, 1, 3, 0); |
| CheckStencilOperation(wgpu::StencilOperation::DecrementClamp, 0, 3, 0); |
| } |
| |
| TEST_P(DepthStencilStateTest, StencilDecrementWrap) { |
| CheckStencilOperation(wgpu::StencilOperation::DecrementWrap, 1, 3, 0); |
| CheckStencilOperation(wgpu::StencilOperation::DecrementWrap, 0, 3, 0xff); |
| } |
| |
| // Check that the setting a stencil read mask works |
| TEST_P(DepthStencilStateTest, StencilReadMask) { |
| wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; |
| baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; |
| baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; |
| wgpu::DepthStencilStateDescriptor baseState; |
| baseState.depthWriteEnabled = false; |
| baseState.depthCompare = wgpu::CompareFunction::Always; |
| baseState.stencilBack = baseStencilFaceDescriptor; |
| baseState.stencilFront = baseStencilFaceDescriptor; |
| baseState.stencilReadMask = 0xff; |
| baseState.stencilWriteMask = 0xff; |
| |
| wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; |
| stencilFaceDescriptor.compare = wgpu::CompareFunction::Equal; |
| stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; |
| wgpu::DepthStencilStateDescriptor state; |
| state.depthWriteEnabled = false; |
| state.depthCompare = wgpu::CompareFunction::Always; |
| state.stencilBack = stencilFaceDescriptor; |
| state.stencilFront = stencilFaceDescriptor; |
| state.stencilReadMask = 0x2; |
| state.stencilWriteMask = 0xff; |
| |
| RGBA8 baseColor = RGBA8(255, 255, 255, 255); |
| RGBA8 red = RGBA8(255, 0, 0, 255); |
| RGBA8 green = RGBA8(0, 255, 0, 255); |
| |
| TestSpec base = { baseState, baseColor, 0.5f, 3u }; // Base triangle to set the stencil to 3 |
| DoTest({ base, { state, red, 0.f, 1u } }, baseColor); // Triangle with stencil reference 1 and read mask 2 does not draw because (3 & 2 != 1) |
| DoTest({ base, { state, green, 0.f, 2u } }, green); // Triangle with stencil reference 2 and read mask 2 draws because (3 & 2 == 2) |
| } |
| |
| // Check that setting a stencil write mask works |
| TEST_P(DepthStencilStateTest, StencilWriteMask) { |
| wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; |
| baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; |
| baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; |
| wgpu::DepthStencilStateDescriptor baseState; |
| baseState.depthWriteEnabled = false; |
| baseState.depthCompare = wgpu::CompareFunction::Always; |
| baseState.stencilBack = baseStencilFaceDescriptor; |
| baseState.stencilFront = baseStencilFaceDescriptor; |
| baseState.stencilReadMask = 0xff; |
| baseState.stencilWriteMask = 0x1; |
| |
| wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; |
| stencilFaceDescriptor.compare = wgpu::CompareFunction::Equal; |
| stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; |
| wgpu::DepthStencilStateDescriptor state; |
| state.depthWriteEnabled = false; |
| state.depthCompare = wgpu::CompareFunction::Always; |
| state.stencilBack = stencilFaceDescriptor; |
| state.stencilFront = stencilFaceDescriptor; |
| state.stencilReadMask = 0xff; |
| state.stencilWriteMask = 0xff; |
| |
| RGBA8 baseColor = RGBA8(255, 255, 255, 255); |
| RGBA8 green = RGBA8(0, 255, 0, 255); |
| |
| TestSpec base = { baseState, baseColor, 0.5f, 3u }; // Base triangle with stencil reference 3 and mask 1 to set the stencil 1 |
| DoTest({ base, { state, green, 0.f, 2u } }, baseColor); // Triangle with stencil reference 2 does not draw because 2 != (3 & 1) |
| DoTest({ base, { state, green, 0.f, 1u } }, green); // Triangle with stencil reference 1 draws because 1 == (3 & 1) |
| } |
| |
| // Test that the stencil operation is executed on stencil fail |
| TEST_P(DepthStencilStateTest, StencilFail) { |
| wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; |
| baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; |
| baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; |
| wgpu::DepthStencilStateDescriptor baseState; |
| baseState.depthWriteEnabled = false; |
| baseState.depthCompare = wgpu::CompareFunction::Always; |
| baseState.stencilBack = baseStencilFaceDescriptor; |
| baseState.stencilFront = baseStencilFaceDescriptor; |
| baseState.stencilReadMask = 0xff; |
| baseState.stencilWriteMask = 0xff; |
| |
| wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; |
| stencilFaceDescriptor.compare = wgpu::CompareFunction::Less; |
| stencilFaceDescriptor.failOp = wgpu::StencilOperation::Replace; |
| stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; |
| wgpu::DepthStencilStateDescriptor state; |
| state.depthWriteEnabled = false; |
| state.depthCompare = wgpu::CompareFunction::Always; |
| state.stencilBack = stencilFaceDescriptor; |
| state.stencilFront = stencilFaceDescriptor; |
| state.stencilReadMask = 0xff; |
| state.stencilWriteMask = 0xff; |
| |
| CheckStencil({ |
| { baseState, RGBA8(255, 255, 255, 255), 1.f, 1 }, // Triangle to set stencil value to 1 |
| { state, RGBA8(0, 0, 0, 255), 0.f, 2 } // Triangle with stencil reference 2 fails the Less comparison function |
| }, 2); // Replace the stencil on failure, so it should be 2 |
| } |
| |
| // Test that the stencil operation is executed on stencil pass, depth fail |
| TEST_P(DepthStencilStateTest, StencilDepthFail) { |
| wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; |
| baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; |
| baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; |
| wgpu::DepthStencilStateDescriptor baseState; |
| baseState.depthWriteEnabled = true; |
| baseState.depthCompare = wgpu::CompareFunction::Always; |
| baseState.stencilBack = baseStencilFaceDescriptor; |
| baseState.stencilFront = baseStencilFaceDescriptor; |
| baseState.stencilReadMask = 0xff; |
| baseState.stencilWriteMask = 0xff; |
| |
| wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; |
| stencilFaceDescriptor.compare = wgpu::CompareFunction::Greater; |
| stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Replace; |
| stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; |
| wgpu::DepthStencilStateDescriptor state; |
| state.depthWriteEnabled = true; |
| state.depthCompare = wgpu::CompareFunction::Less; |
| state.stencilBack = stencilFaceDescriptor; |
| state.stencilFront = stencilFaceDescriptor; |
| state.stencilReadMask = 0xff; |
| state.stencilWriteMask = 0xff; |
| |
| CheckStencil({ |
| { baseState, RGBA8(255, 255, 255, 255), 0.f, 1 }, // Triangle to set stencil value to 1. Depth is 0 |
| { state, RGBA8(0, 0, 0, 255), 1.f, 2 } }, // Triangle with stencil reference 2 passes the Greater comparison function. At depth 1, it fails the Less depth test |
| 2); // Replace the stencil on stencil pass, depth failure, so it should be 2 |
| } |
| |
| // Test that the stencil operation is executed on stencil pass, depth pass |
| TEST_P(DepthStencilStateTest, StencilDepthPass) { |
| wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; |
| baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; |
| baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; |
| wgpu::DepthStencilStateDescriptor baseState; |
| baseState.depthWriteEnabled = true; |
| baseState.depthCompare = wgpu::CompareFunction::Always; |
| baseState.stencilBack = baseStencilFaceDescriptor; |
| baseState.stencilFront = baseStencilFaceDescriptor; |
| baseState.stencilReadMask = 0xff; |
| baseState.stencilWriteMask = 0xff; |
| |
| wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; |
| stencilFaceDescriptor.compare = wgpu::CompareFunction::Greater; |
| stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; |
| stencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; |
| wgpu::DepthStencilStateDescriptor state; |
| state.depthWriteEnabled = true; |
| state.depthCompare = wgpu::CompareFunction::Less; |
| state.stencilBack = stencilFaceDescriptor; |
| state.stencilFront = stencilFaceDescriptor; |
| state.stencilReadMask = 0xff; |
| state.stencilWriteMask = 0xff; |
| |
| CheckStencil({ |
| { baseState, RGBA8(255, 255, 255, 255), 1.f, 1 }, // Triangle to set stencil value to 1. Depth is 0 |
| { state, RGBA8(0, 0, 0, 255), 0.f, 2 } }, // Triangle with stencil reference 2 passes the Greater comparison function. At depth 0, it pass the Less depth test |
| 2); // Replace the stencil on stencil pass, depth pass, so it should be 2 |
| } |
| |
| // Test that creating a render pipeline works with for all depth and combined formats |
| TEST_P(DepthStencilStateTest, CreatePipelineWithAllFormats) { |
| constexpr wgpu::TextureFormat kDepthStencilFormats[] = { |
| wgpu::TextureFormat::Depth32Float, |
| wgpu::TextureFormat::Depth24PlusStencil8, |
| wgpu::TextureFormat::Depth24Plus, |
| }; |
| |
| for (wgpu::TextureFormat depthStencilFormat : kDepthStencilFormats) { |
| utils::ComboRenderPipelineDescriptor descriptor(device); |
| descriptor.vertexStage.module = vsModule; |
| descriptor.cFragmentStage.module = fsModule; |
| descriptor.cDepthStencilState.format = depthStencilFormat; |
| descriptor.depthStencilState = &descriptor.cDepthStencilState; |
| |
| device.CreateRenderPipeline(&descriptor); |
| } |
| } |
| |
| DAWN_INSTANTIATE_TEST(DepthStencilStateTest, |
| D3D12Backend(), |
| MetalBackend(), |
| OpenGLBackend(), |
| VulkanBackend({"vulkan_use_d32s8"}, {}), |
| VulkanBackend({}, {"vulkan_use_d32s8"})); |