Initializing stencil reference for render pass in D3D12

When creating a new render pass in D3D12, call the OMSetStencilRef
to set the stencil reference value to zero.

Bug: dawn:1097
Change-Id: I5dd94691b1b354b601c06a02d3d5fa619d8b58ee
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/63360
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 50c5236..638f17d 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -1304,6 +1304,8 @@
 
             static constexpr std::array<float, 4> defaultBlendFactor = {0, 0, 0, 0};
             commandList->OMSetBlendFactor(&defaultBlendFactor[0]);
+
+            commandList->OMSetStencilRef(0);
         }
 
         RenderPipeline* lastPipeline = nullptr;
diff --git a/src/tests/end2end/DepthStencilStateTests.cpp b/src/tests/end2end/DepthStencilStateTests.cpp
index b65ab02..0d96819 100644
--- a/src/tests/end2end/DepthStencilStateTests.cpp
+++ b/src/tests/end2end/DepthStencilStateTests.cpp
@@ -92,6 +92,7 @@
         float depth;
         uint32_t stencil;
         wgpu::FrontFace frontFace = wgpu::FrontFace::CCW;
+        bool setStencilReference = true;
     };
 
     // Check whether a depth comparison function works as expected
@@ -265,7 +266,8 @@
     // expected colors for the frontfaces and backfaces
     void DoTest(const std::vector<TestSpec>& testParams,
                 const RGBA8& expectedFront,
-                const RGBA8& expectedBack) {
+                const RGBA8& expectedBack,
+                bool isSingleEncoderMultiplePass = false) {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
 
         struct TriangleData {
@@ -274,11 +276,32 @@
         };
 
         utils::ComboRenderPassDescriptor renderPass({renderTargetView}, depthTextureView);
-        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
+        wgpu::RenderPassEncoder pass;
+
+        if (isSingleEncoderMultiplePass) {
+            // The render pass to clear up the depthTextureView (using LoadOp = clear)
+            utils::ComboRenderPassDescriptor clearingPass({renderTargetView}, depthTextureView);
+
+            // The render pass to do the test with depth and stencil result kept
+            renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
+            renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
+
+            // Clear the depthStencilView at the beginning
+            {
+                pass = encoder.BeginRenderPass(&renderPass);
+                pass.EndPass();
+            }
+        } else {
+            pass = encoder.BeginRenderPass(&renderPass);
+        }
 
         for (size_t i = 0; i < testParams.size(); ++i) {
             const TestSpec& test = testParams[i];
 
+            if (isSingleEncoderMultiplePass) {
+                pass = encoder.BeginRenderPass(&renderPass);
+            }
+
             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},
@@ -305,12 +328,21 @@
                 device, pipeline.GetBindGroupLayout(0), {{0, buffer, 0, sizeof(TriangleData)}});
 
             pass.SetPipeline(pipeline);
-            pass.SetStencilReference(test.stencil);  // Set the stencil reference
+            if (test.setStencilReference) {
+                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);
+
+            if (isSingleEncoderMultiplePass) {
+                pass.EndPass();
+            }
         }
-        pass.EndPass();
+
+        if (!isSingleEncoderMultiplePass) {
+            pass.EndPass();
+        }
 
         wgpu::CommandBuffer commands = encoder.Finish();
         queue.Submit(1, &commands);
@@ -321,8 +353,10 @@
             << "Back face check failed";
     }
 
-    void DoTest(const std::vector<TestSpec>& testParams, const RGBA8& expected) {
-        DoTest(testParams, expected, expected);
+    void DoTest(const std::vector<TestSpec>& testParams,
+                const RGBA8& expected,
+                bool isSingleEncoderMultiplePass = false) {
+        DoTest(testParams, expected, expected, isSingleEncoderMultiplePass);
     }
 
     wgpu::Texture renderTarget;
@@ -752,6 +786,51 @@
     DoTest({{state, RGBA8::kRed, 0.f, 0u, wgpu::FrontFace::CW}}, RGBA8::kZero, RGBA8::kRed);
 }
 
+// Test that the depth reference of a new render pass is initialized to default value 0
+TEST_P(DepthStencilStateTest, StencilReferenceInitialized) {
+    wgpu::DepthStencilState stencilAlwaysReplaceState;
+    stencilAlwaysReplaceState.stencilFront.compare = wgpu::CompareFunction::Always;
+    stencilAlwaysReplaceState.stencilFront.passOp = wgpu::StencilOperation::Replace;
+    stencilAlwaysReplaceState.stencilBack.compare = wgpu::CompareFunction::Always;
+    stencilAlwaysReplaceState.stencilBack.passOp = wgpu::StencilOperation::Replace;
+
+    wgpu::DepthStencilState stencilEqualKeepState;
+    stencilEqualKeepState.stencilFront.compare = wgpu::CompareFunction::Equal;
+    stencilEqualKeepState.stencilFront.passOp = wgpu::StencilOperation::Keep;
+    stencilEqualKeepState.stencilBack.compare = wgpu::CompareFunction::Equal;
+    stencilEqualKeepState.stencilBack.passOp = wgpu::StencilOperation::Keep;
+
+    // Test that stencil reference is not inherited
+    {
+        // First pass sets the stencil to 0x1, and the second pass tests the stencil
+        // Only set the stencil reference in the first pass, and test that for other pass it should
+        // be default value rather than inherited
+        std::vector<TestSpec> testParams = {
+            {stencilAlwaysReplaceState, RGBA8::kRed, 0.f, 0x1, wgpu::FrontFace::CCW, true},
+            {stencilEqualKeepState, RGBA8::kGreen, 0.f, 0x0, wgpu::FrontFace::CCW, false}};
+
+        // Since the stencil reference is not inherited, second draw won't pass the stencil test
+        std::pair<RGBA8, RGBA8> expectation = {RGBA8::kZero, RGBA8::kZero};
+
+        DoTest(testParams, expectation.first, expectation.second, true);
+    }
+
+    // Test that stencil reference is initialized as zero for new render pass
+    {
+        // First pass sets the stencil to 0x1, the second pass sets the stencil to its default
+        // value, and the third pass tests if the stencil is zero
+        std::vector<TestSpec> testParams = {
+            {stencilAlwaysReplaceState, RGBA8::kRed, 0.f, 0x1, wgpu::FrontFace::CCW, true},
+            {stencilAlwaysReplaceState, RGBA8::kGreen, 0.f, 0x1, wgpu::FrontFace::CCW, false},
+            {stencilEqualKeepState, RGBA8::kBlue, 0.f, 0x0, wgpu::FrontFace::CCW, true}};
+
+        // The third draw should pass the stencil test since the second pass set it to default zero
+        std::pair<RGBA8, RGBA8> expectation = {RGBA8::kBlue, RGBA8::kBlue};
+
+        DoTest(testParams, expectation.first, expectation.second, true);
+    }
+}
+
 DAWN_INSTANTIATE_TEST(DepthStencilStateTest,
                       D3D12Backend(),
                       MetalBackend(),