text: // 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 TestSetUp() override {
              DawnTest::TestSetUp();
  
              wgpu::TextureDescriptor renderTargetDescriptor;
              renderTargetDescriptor.dimension = wgpu::TextureDimension::e2D;
              renderTargetDescriptor.size.width = kRTSize;
              renderTargetDescriptor.size.height = kRTSize;
              renderTargetDescriptor.size.depth = 1;
              renderTargetDescriptor.arrayLayerCount = 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.arrayLayerCount = 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, 1, 0, 0);
              }
              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
  }
  
  DAWN_INSTANTIATE_TEST(DepthStencilStateTest,
                        D3D12Backend,
                        MetalBackend,
                        OpenGLBackend,
                        ForceToggles(VulkanBackend, {"vulkan_use_d32s8"}, {}),
                        ForceToggles(VulkanBackend, {}, {"vulkan_use_d32s8"}));
  