Make loapOp/storeOp undefined valid

It is valid for depth/stencil attachment if the attachments are
readonly. It is not valid for color attachments. Make the enum valid,
and update validation.

Caught in CTS roll
https://chromium-review.googlesource.com/c/chromium/src/+/3499286

Bug: dawn:1269
Change-Id: Ib849ed757ccca145f85cadea6f92a1f2a5082d49
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/82540
Reviewed-by: Loko Kung <lokokung@google.com>
Reviewed-by: Brandon Jones <bajones@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/dawn.json b/dawn.json
index 87810c8..812093f 100644
--- a/dawn.json
+++ b/dawn.json
@@ -1456,7 +1456,7 @@
         "category": "enum",
         "emscripten_no_enum_table": true,
         "values": [
-            {"value": 0, "name": "undefined", "valid": false, "jsrepr": "undefined"},
+            {"value": 0, "name": "undefined", "jsrepr": "undefined"},
             {"value": 1, "name": "clear"},
             {"value": 2, "name": "load"}
         ]
@@ -1472,7 +1472,7 @@
     "store op": {
         "category": "enum",
         "values": [
-            {"value": 0, "name": "undefined", "valid": false, "jsrepr": "undefined"},
+            {"value": 0, "name": "undefined", "jsrepr": "undefined"},
             {"value": 1, "name": "store"},
             {"value": 2, "name": "discard"}
         ]
diff --git a/src/dawn/native/CommandEncoder.cpp b/src/dawn/native/CommandEncoder.cpp
index 4c3e714..6cdb86b 100644
--- a/src/dawn/native/CommandEncoder.cpp
+++ b/src/dawn/native/CommandEncoder.cpp
@@ -241,6 +241,10 @@
 
             DAWN_TRY(ValidateLoadOp(colorAttachment.loadOp));
             DAWN_TRY(ValidateStoreOp(colorAttachment.storeOp));
+            DAWN_INVALID_IF(colorAttachment.loadOp == wgpu::LoadOp::Undefined,
+                            "loadOp must be set.");
+            DAWN_INVALID_IF(colorAttachment.storeOp == wgpu::StoreOp::Undefined,
+                            "storeOp must be set.");
 
             // TODO(dawn:1269): Remove after the deprecation period.
             bool useClearColor = HasDeprecatedColor(colorAttachment);
@@ -303,47 +307,77 @@
                 "is 'all'.",
                 depthStencilAttachment->depthReadOnly, depthStencilAttachment->stencilReadOnly);
 
-            if (depthStencilAttachment->depthReadOnly) {
+            // Read only, or depth doesn't exist.
+            if (depthStencilAttachment->depthReadOnly ||
+                !IsSubset(Aspect::Depth, attachment->GetAspects())) {
                 if (depthStencilAttachment->depthLoadOp == wgpu::LoadOp::Load &&
                     depthStencilAttachment->depthStoreOp == wgpu::StoreOp::Store) {
                     // TODO(dawn:1269): Remove this branch after the deprecation period.
                     device->EmitDeprecationWarning(
-                        "Setting depthLoadOp and depthStore when "
-                        "depthReadOnly is true is deprecated.");
+                        "Setting depthLoadOp and depthStoreOp when "
+                        "the attachment has no depth aspect or depthReadOnly is true is "
+                        "deprecated.");
                 } else {
                     DAWN_INVALID_IF(depthStencilAttachment->depthLoadOp != wgpu::LoadOp::Undefined,
-                                    "depthLoadOp (%s) must not be set when depthReadOnly is true.",
-                                    depthStencilAttachment->depthLoadOp);
+                                    "depthLoadOp (%s) must not be set if the attachment (%s) has "
+                                    "no depth aspect or depthReadOnly (%u) is true.",
+                                    depthStencilAttachment->depthLoadOp, attachment,
+                                    depthStencilAttachment->depthReadOnly);
                     DAWN_INVALID_IF(
                         depthStencilAttachment->depthStoreOp != wgpu::StoreOp::Undefined,
-                        "depthStoreOp (%s) must not be set when depthReadOnly is true.",
-                        depthStencilAttachment->depthStoreOp);
+                        "depthStoreOp (%s) must not be set if the attachment (%s) has no depth "
+                        "aspect or depthReadOnly (%u) is true.",
+                        depthStencilAttachment->depthStoreOp, attachment,
+                        depthStencilAttachment->depthReadOnly);
                 }
             } else {
                 DAWN_TRY(ValidateLoadOp(depthStencilAttachment->depthLoadOp));
+                DAWN_INVALID_IF(depthStencilAttachment->depthLoadOp == wgpu::LoadOp::Undefined,
+                                "depthLoadOp must be set if the attachment (%s) has a depth aspect "
+                                "and depthReadOnly (%u) is false.",
+                                attachment, depthStencilAttachment->depthReadOnly);
                 DAWN_TRY(ValidateStoreOp(depthStencilAttachment->depthStoreOp));
+                DAWN_INVALID_IF(depthStencilAttachment->depthStoreOp == wgpu::StoreOp::Undefined,
+                                "depthStoreOp must be set if the attachment (%s) has a depth "
+                                "aspect and depthReadOnly (%u) is false.",
+                                attachment, depthStencilAttachment->depthReadOnly);
             }
 
-            if (depthStencilAttachment->stencilReadOnly) {
+            // Read only, or stencil doesn't exist.
+            if (depthStencilAttachment->stencilReadOnly ||
+                !IsSubset(Aspect::Stencil, attachment->GetAspects())) {
                 if (depthStencilAttachment->stencilLoadOp == wgpu::LoadOp::Load &&
                     depthStencilAttachment->stencilStoreOp == wgpu::StoreOp::Store) {
                     // TODO(dawn:1269): Remove this branch after the deprecation period.
                     device->EmitDeprecationWarning(
                         "Setting stencilLoadOp and stencilStoreOp when "
-                        "stencilReadOnly is true is deprecated.");
+                        "the attachment has no stencil aspect or stencilReadOnly is true is "
+                        "deprecated.");
                 } else {
                     DAWN_INVALID_IF(
                         depthStencilAttachment->stencilLoadOp != wgpu::LoadOp::Undefined,
-                        "stencilLoadOp (%s) must not be set when stencilReadOnly is true.",
-                        depthStencilAttachment->stencilLoadOp);
+                        "stencilLoadOp (%s) must not be set if the attachment (%s) has no stencil "
+                        "aspect or stencilReadOnly (%u) is true.",
+                        depthStencilAttachment->stencilLoadOp, attachment,
+                        depthStencilAttachment->stencilReadOnly);
                     DAWN_INVALID_IF(
                         depthStencilAttachment->stencilStoreOp != wgpu::StoreOp::Undefined,
-                        "stencilStoreOp (%s) must not be set when stencilReadOnly is true.",
-                        depthStencilAttachment->stencilStoreOp);
+                        "stencilStoreOp (%s) must not be set if the attachment (%s) has no stencil "
+                        "aspect or stencilReadOnly (%u) is true.",
+                        depthStencilAttachment->stencilStoreOp, attachment,
+                        depthStencilAttachment->stencilReadOnly);
                 }
             } else {
                 DAWN_TRY(ValidateLoadOp(depthStencilAttachment->stencilLoadOp));
+                DAWN_INVALID_IF(depthStencilAttachment->stencilLoadOp == wgpu::LoadOp::Undefined,
+                                "stencilLoadOp must be set if the attachment (%s) has a stencil "
+                                "aspect and stencilReadOnly (%u) is false.",
+                                attachment, depthStencilAttachment->stencilReadOnly);
                 DAWN_TRY(ValidateStoreOp(depthStencilAttachment->stencilStoreOp));
+                DAWN_INVALID_IF(depthStencilAttachment->depthStoreOp == wgpu::StoreOp::Undefined,
+                                "stencilStoreOp must be set if the attachment (%s) has a stencil "
+                                "aspect and stencilReadOnly (%u) is false.",
+                                attachment, depthStencilAttachment->stencilReadOnly);
             }
 
             if (!std::isnan(depthStencilAttachment->clearDepth)) {
@@ -823,7 +857,9 @@
                     cmd->depthStencilAttachment.stencilReadOnly =
                         descriptor->depthStencilAttachment->stencilReadOnly;
 
-                    if (descriptor->depthStencilAttachment->depthReadOnly) {
+                    if (descriptor->depthStencilAttachment->depthReadOnly ||
+                        !IsSubset(Aspect::Depth,
+                                  descriptor->depthStencilAttachment->view->GetAspects())) {
                         cmd->depthStencilAttachment.depthLoadOp = wgpu::LoadOp::Load;
                         cmd->depthStencilAttachment.depthStoreOp = wgpu::StoreOp::Store;
                     } else {
@@ -833,7 +869,9 @@
                             descriptor->depthStencilAttachment->depthStoreOp;
                     }
 
-                    if (descriptor->depthStencilAttachment->stencilReadOnly) {
+                    if (descriptor->depthStencilAttachment->stencilReadOnly ||
+                        !IsSubset(Aspect::Stencil,
+                                  descriptor->depthStencilAttachment->view->GetAspects())) {
                         cmd->depthStencilAttachment.stencilLoadOp = wgpu::LoadOp::Load;
                         cmd->depthStencilAttachment.stencilStoreOp = wgpu::StoreOp::Store;
                     } else {
diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp
index 0256bb8..93a0dca 100644
--- a/src/dawn/tests/DawnTest.cpp
+++ b/src/dawn/tests/DawnTest.cpp
@@ -1399,6 +1399,20 @@
                                                     texture.CreateView(&viewDesc));
     passDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
     passDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
+    switch (format) {
+        case wgpu::TextureFormat::Depth24Plus:
+        case wgpu::TextureFormat::Depth32Float:
+        case wgpu::TextureFormat::Depth16Unorm:
+            passDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+            passDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
+            break;
+        case wgpu::TextureFormat::Stencil8:
+            passDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Undefined;
+            passDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Undefined;
+            break;
+        default:
+            break;
+    }
 
     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
 
diff --git a/src/dawn/tests/end2end/CopyTests.cpp b/src/dawn/tests/end2end/CopyTests.cpp
index f63ddb1..ed661d3 100644
--- a/src/dawn/tests/end2end/CopyTests.cpp
+++ b/src/dawn/tests/end2end/CopyTests.cpp
@@ -1039,6 +1039,7 @@
     // Initialize the depth texture with 0.5f.
     constexpr float kClearDepthValue = 0.5f;
     utils::ComboRenderPassDescriptor renderPass({}, texture.CreateView());
+    renderPass.UnsetDepthStencilLoadStoreOpsForFormat(kFormat);
     renderPass.cDepthStencilAttachmentInfo.depthClearValue = kClearDepthValue;
     renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
     renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
diff --git a/src/dawn/tests/end2end/DepthBiasTests.cpp b/src/dawn/tests/end2end/DepthBiasTests.cpp
index 1294e23..f1b31de 100644
--- a/src/dawn/tests/end2end/DepthBiasTests.cpp
+++ b/src/dawn/tests/end2end/DepthBiasTests.cpp
@@ -92,6 +92,7 @@
         // Create a render pass which clears depth to depthClear
         utils::ComboRenderPassDescriptor renderPassDesc({mRenderTarget.CreateView()},
                                                         mDepthTexture.CreateView());
+        renderPassDesc.UnsetDepthStencilLoadStoreOpsForFormat(depthFormat);
         renderPassDesc.cDepthStencilAttachmentInfo.depthClearValue = depthClear;
 
         // Create a render pipeline to render the quad
diff --git a/src/dawn/tests/end2end/DepthStencilCopyTests.cpp b/src/dawn/tests/end2end/DepthStencilCopyTests.cpp
index ae75ed7..2dfd8e5 100644
--- a/src/dawn/tests/end2end/DepthStencilCopyTests.cpp
+++ b/src/dawn/tests/end2end/DepthStencilCopyTests.cpp
@@ -142,6 +142,7 @@
     // The texture will be cleared to the "clear" value, and then bottom left corner will
     // be written with the "region" value.
     void InitializeDepthTextureRegion(wgpu::Texture texture,
+                                      wgpu::TextureFormat depthFormat,
                                       float clearDepth,
                                       float regionDepth,
                                       uint32_t mipLevel = 0) {
@@ -150,6 +151,7 @@
         viewDesc.mipLevelCount = 1;
 
         utils::ComboRenderPassDescriptor renderPassDesc({}, texture.CreateView(&viewDesc));
+        renderPassDesc.UnsetDepthStencilLoadStoreOpsForFormat(depthFormat);
         renderPassDesc.cDepthStencilAttachmentInfo.depthClearValue = clearDepth;
 
         utils::ComboRenderPipelineDescriptor renderPipelineDesc;
@@ -455,7 +457,7 @@
         kWidth, kHeight, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc);
 
     constexpr float kInitDepth = 0.2f;
-    InitializeDepthTextureRegion(texture, 0.f, kInitDepth);
+    InitializeDepthTextureRegion(texture, GetParam().mTextureFormat, 0.f, kInitDepth);
 
     // This expectation is the test as it performs the CopyTextureToBuffer.
     if (GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm) {
@@ -495,7 +497,7 @@
         9, 9, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, 2);
 
     constexpr float kInitDepth = 0.4f;
-    InitializeDepthTextureRegion(depthTexture, 0.f, kInitDepth, 1);
+    InitializeDepthTextureRegion(depthTexture, GetParam().mTextureFormat, 0.f, kInitDepth, 1);
 
     // This expectation is the test as it performs the CopyTextureToBuffer.
     if (GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm) {
diff --git a/src/dawn/tests/end2end/DepthStencilLoadOpTests.cpp b/src/dawn/tests/end2end/DepthStencilLoadOpTests.cpp
index a3753e5..f9714cf 100644
--- a/src/dawn/tests/end2end/DepthStencilLoadOpTests.cpp
+++ b/src/dawn/tests/end2end/DepthStencilLoadOpTests.cpp
@@ -85,6 +85,7 @@
                 textureViews[mipLevel] = texture.CreateView(&textureViewDesc);
 
                 utils::ComboRenderPassDescriptor renderPassDescriptor({}, textureViews[mipLevel]);
+                renderPassDescriptor.UnsetDepthStencilLoadStoreOpsForFormat(GetParam().mFormat);
                 renderPassDescriptor.cDepthStencilAttachmentInfo.depthClearValue =
                     kDepthValues[mipLevel];
                 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilClearValue =
diff --git a/src/dawn/tests/end2end/DepthStencilSamplingTests.cpp b/src/dawn/tests/end2end/DepthStencilSamplingTests.cpp
index ff038c5..2a9ce3f 100644
--- a/src/dawn/tests/end2end/DepthStencilSamplingTests.cpp
+++ b/src/dawn/tests/end2end/DepthStencilSamplingTests.cpp
@@ -264,8 +264,10 @@
 
     void UpdateInputDepth(wgpu::CommandEncoder commandEncoder,
                           wgpu::Texture texture,
+                          wgpu::TextureFormat format,
                           float depthValue) {
         utils::ComboRenderPassDescriptor passDescriptor({}, texture.CreateView());
+        passDescriptor.UnsetDepthStencilLoadStoreOpsForFormat(format);
         passDescriptor.cDepthStencilAttachmentInfo.depthClearValue = depthValue;
 
         wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
@@ -274,8 +276,10 @@
 
     void UpdateInputStencil(wgpu::CommandEncoder commandEncoder,
                             wgpu::Texture texture,
+                            wgpu::TextureFormat format,
                             uint8_t stencilValue) {
         utils::ComboRenderPassDescriptor passDescriptor({}, texture.CreateView());
+        passDescriptor.UnsetDepthStencilLoadStoreOpsForFormat(format);
         passDescriptor.cDepthStencilAttachmentInfo.stencilClearValue = stencilValue;
 
         wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
@@ -311,10 +315,10 @@
             wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
             switch (aspect) {
                 case TestAspect::Depth:
-                    UpdateInputDepth(commandEncoder, inputTexture, textureValues[i]);
+                    UpdateInputDepth(commandEncoder, inputTexture, format, textureValues[i]);
                     break;
                 case TestAspect::Stencil:
-                    UpdateInputStencil(commandEncoder, inputTexture, textureValues[i]);
+                    UpdateInputStencil(commandEncoder, inputTexture, format, textureValues[i]);
                     break;
             }
 
@@ -366,10 +370,10 @@
             wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
             switch (aspect) {
                 case TestAspect::Depth:
-                    UpdateInputDepth(commandEncoder, inputTexture, textureValues[i]);
+                    UpdateInputDepth(commandEncoder, inputTexture, format, textureValues[i]);
                     break;
                 case TestAspect::Stencil:
-                    UpdateInputStencil(commandEncoder, inputTexture, textureValues[i]);
+                    UpdateInputStencil(commandEncoder, inputTexture, format, textureValues[i]);
                     break;
             }
 
@@ -522,7 +526,7 @@
         for (float textureValue : textureValues) {
             // Set the input depth texture to the provided texture value
             wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
-            UpdateInputDepth(commandEncoder, inputTexture, textureValue);
+            UpdateInputDepth(commandEncoder, inputTexture, format, textureValue);
 
             // Render into the output texture
             {
@@ -569,7 +573,7 @@
         for (float textureValue : textureValues) {
             // Set the input depth texture to the provided texture value
             wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
-            UpdateInputDepth(commandEncoder, inputTexture, textureValue);
+            UpdateInputDepth(commandEncoder, inputTexture, format, textureValue);
 
             // Sample into the output buffer
             {
diff --git a/src/dawn/tests/end2end/MultisampledSamplingTests.cpp b/src/dawn/tests/end2end/MultisampledSamplingTests.cpp
index a2136aa..c6c6d6c 100644
--- a/src/dawn/tests/end2end/MultisampledSamplingTests.cpp
+++ b/src/dawn/tests/end2end/MultisampledSamplingTests.cpp
@@ -199,6 +199,8 @@
 
             utils::ComboRenderPassDescriptor renderPass({colorView}, depthView);
             renderPass.cDepthStencilAttachmentInfo.depthClearValue = 0.f;
+            renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+            renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
 
             wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
             renderPassEncoder.SetPipeline(drawPipeline);
diff --git a/src/dawn/tests/end2end/ReadOnlyDepthStencilAttachmentTests.cpp b/src/dawn/tests/end2end/ReadOnlyDepthStencilAttachmentTests.cpp
index 6009ba0..fd6690e 100644
--- a/src/dawn/tests/end2end/ReadOnlyDepthStencilAttachmentTests.cpp
+++ b/src/dawn/tests/end2end/ReadOnlyDepthStencilAttachmentTests.cpp
@@ -149,6 +149,7 @@
         // Note that we must encompass all aspects for texture view used in attachment.
         wgpu::TextureView depthStencilViewInAttachment = depthStencilTexture.CreateView();
         utils::ComboRenderPassDescriptor passDescriptorInit({}, depthStencilViewInAttachment);
+        passDescriptorInit.UnsetDepthStencilLoadStoreOpsForFormat(format);
         if (aspect == wgpu::TextureAspect::DepthOnly) {
             passDescriptorInit.cDepthStencilAttachmentInfo.depthClearValue = values->depthInitValue;
         } else {
diff --git a/src/dawn/tests/end2end/SubresourceRenderAttachmentTests.cpp b/src/dawn/tests/end2end/SubresourceRenderAttachmentTests.cpp
index 9808226..b351c25 100644
--- a/src/dawn/tests/end2end/SubresourceRenderAttachmentTests.cpp
+++ b/src/dawn/tests/end2end/SubresourceRenderAttachmentTests.cpp
@@ -56,11 +56,13 @@
                 }
                 case Type::Depth: {
                     utils::ComboRenderPassDescriptor renderPass({}, renderTargetView);
+                    renderPass.UnsetDepthStencilLoadStoreOpsForFormat(format);
                     renderPass.cDepthStencilAttachmentInfo.depthClearValue = expectedDepth;
                     return renderPass;
                 }
                 case Type::Stencil: {
                     utils::ComboRenderPassDescriptor renderPass({}, renderTargetView);
+                    renderPass.UnsetDepthStencilLoadStoreOpsForFormat(format);
                     renderPass.cDepthStencilAttachmentInfo.stencilClearValue = expectedStencil;
                     return renderPass;
                 }
diff --git a/src/dawn/tests/end2end/ViewportTests.cpp b/src/dawn/tests/end2end/ViewportTests.cpp
index 8d4b2ad..df2d98a 100644
--- a/src/dawn/tests/end2end/ViewportTests.cpp
+++ b/src/dawn/tests/end2end/ViewportTests.cpp
@@ -119,6 +119,8 @@
         utils::ComboRenderPassDescriptor rpDesc({}, depthTexture.CreateView());
         rpDesc.cDepthStencilAttachmentInfo.depthClearValue = 0.0f;
         rpDesc.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
+        rpDesc.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+        rpDesc.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&rpDesc);
         pass.SetPipeline(pipeline);
diff --git a/src/dawn/tests/unittests/validation/RenderBundleValidationTests.cpp b/src/dawn/tests/unittests/validation/RenderBundleValidationTests.cpp
index 7474fd3..6256e59 100644
--- a/src/dawn/tests/unittests/validation/RenderBundleValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/RenderBundleValidationTests.cpp
@@ -1033,6 +1033,8 @@
     // Test the success case
     {
         utils::ComboRenderPassDescriptor renderPass({tex0.CreateView()}, tex1.CreateView());
+        renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+        renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
 
         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
         wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
@@ -1044,6 +1046,8 @@
     // Test the failure case for mismatched format
     {
         utils::ComboRenderPassDescriptor renderPass({tex0.CreateView()}, tex2.CreateView());
+        renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+        renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
 
         wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
         wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass);
diff --git a/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp b/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
index cd10233..d311f38 100644
--- a/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
@@ -414,6 +414,9 @@
             wgpu::TextureView renderView =
                 Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth32Float);
             utils::ComboRenderPassDescriptor renderPass({}, renderView);
+            renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+            renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
+
             AssertBeginRenderPassSuccess(&renderPass);
         }
 
@@ -427,6 +430,9 @@
             wgpu::TextureView sampledView = sampledTex.CreateView();
 
             utils::ComboRenderPassDescriptor renderPass({}, sampledView);
+            renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+            renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
+
             AssertBeginRenderPassError(&renderPass);
         }
     }
@@ -861,6 +867,8 @@
             wgpu::TextureView depth =
                 Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus);
             utils::ComboRenderPassDescriptor renderPass({color}, depth);
+            renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+            renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
             renderPass.cDepthStencilAttachmentInfo.depthClearValue = NAN;
             AssertBeginRenderPassError(&renderPass);
         }
@@ -870,6 +878,8 @@
             wgpu::TextureView depth =
                 Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus);
             utils::ComboRenderPassDescriptor renderPass({color}, depth);
+            renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+            renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
             renderPass.cDepthStencilAttachmentInfo.depthClearValue = INFINITY;
             AssertBeginRenderPassSuccess(&renderPass);
         }
@@ -899,7 +909,7 @@
         }
 
         // Tests that a pass with mismatched depthReadOnly and stencilReadOnly values passes when
-        // there is no stencil component in the format.
+        // there is no stencil component in the format (deprecated).
         {
             utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilViewNoStencil);
             renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Undefined;
@@ -908,7 +918,29 @@
             renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
             renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store;
             renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = false;
-            AssertBeginRenderPassSuccess(&renderPass);
+            EXPECT_DEPRECATION_WARNING(AssertBeginRenderPassSuccess(&renderPass));
+        }
+
+        // Tests that a pass with mismatched depthReadOnly and stencilReadOnly values fails when
+        // there there is no stencil component in the format and stencil loadOp/storeOp are passed.
+        {
+            utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilViewNoStencil);
+            renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Undefined;
+            renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Undefined;
+            renderPass.cDepthStencilAttachmentInfo.depthReadOnly = true;
+            renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear;
+            renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store;
+            renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = false;
+            AssertBeginRenderPassError(&renderPass);
+
+            renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+            renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store;
+            AssertBeginRenderPassError(&renderPass);
+
+            renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear;
+            renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
+            renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = false;
+            AssertBeginRenderPassError(&renderPass);
         }
 
         // Tests that a pass with depthReadOnly=true and stencilReadOnly=true can pass
@@ -1077,6 +1109,9 @@
 
             wgpu::TextureView view = device.CreateTexture(&texDesc).CreateView(&viewDesc);
             utils::ComboRenderPassDescriptor renderPass({}, view);
+            renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+            renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
+
             AssertBeginRenderPassSuccess(&renderPass);
         }
 
diff --git a/src/dawn/tests/unittests/validation/ResourceUsageTrackingTests.cpp b/src/dawn/tests/unittests/validation/ResourceUsageTrackingTests.cpp
index 53adeb6..1f78492 100644
--- a/src/dawn/tests/unittests/validation/ResourceUsageTrackingTests.cpp
+++ b/src/dawn/tests/unittests/validation/ResourceUsageTrackingTests.cpp
@@ -902,6 +902,8 @@
         utils::ComboRenderPassDescriptor passDescriptor({}, view);
         passDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
         passDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
+        passDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+        passDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
 
         // It is invalid to use the texture as both sampled and writeable depth/stencil attachment
         // in the same pass
diff --git a/src/dawn/utils/WGPUHelpers.cpp b/src/dawn/utils/WGPUHelpers.cpp
index f9f8433..537cdcb 100644
--- a/src/dawn/utils/WGPUHelpers.cpp
+++ b/src/dawn/utils/WGPUHelpers.cpp
@@ -138,6 +138,23 @@
 
         return *this;
     }
+    void ComboRenderPassDescriptor::UnsetDepthStencilLoadStoreOpsForFormat(
+        wgpu::TextureFormat format) {
+        switch (format) {
+            case wgpu::TextureFormat::Depth24Plus:
+            case wgpu::TextureFormat::Depth32Float:
+            case wgpu::TextureFormat::Depth16Unorm:
+                cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
+                cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
+                break;
+            case wgpu::TextureFormat::Stencil8:
+                cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Undefined;
+                cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Undefined;
+                break;
+            default:
+                break;
+        }
+    }
 
     BasicRenderPass::BasicRenderPass()
         : width(0),
diff --git a/src/dawn/utils/WGPUHelpers.h b/src/dawn/utils/WGPUHelpers.h
index f08c142..bfffae2 100644
--- a/src/dawn/utils/WGPUHelpers.h
+++ b/src/dawn/utils/WGPUHelpers.h
@@ -66,6 +66,8 @@
         const ComboRenderPassDescriptor& operator=(
             const ComboRenderPassDescriptor& otherRenderPass);
 
+        void UnsetDepthStencilLoadStoreOpsForFormat(wgpu::TextureFormat format);
+
         std::array<wgpu::RenderPassColorAttachment, kMaxColorAttachments> cColorAttachments;
         wgpu::RenderPassDepthStencilAttachment cDepthStencilAttachmentInfo = {};
     };