Add MultisampleStateExpandResolveTextureDawn chained struct.

This chained struct is used to indicate that a render pipeline will be
compatible with the render pass using `ExpandResolveTexture` load op.

This is needed for vulkan backend where `ExpandResolveTexture` is
implemented using 2 subpasses. And as such every render pipeline used in
that render pass must operate in 2nd subpass.

Deprecated DawnMultisampleStateRenderToSingleSampled chained struct
since we are not going to implement MSAARenderToSingleSampled using 2
subpasses anymore.

Bug: dawn:1710
Change-Id: I15a7dbddcc4575a63d656aae8f2738a3e85311d9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/187561
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Quyen Le <lehoangquyen@chromium.org>
diff --git a/docs/dawn/features/dawn_load_resolve_texture.md b/docs/dawn/features/dawn_load_resolve_texture.md
index 62dc2b3..e962e9d 100644
--- a/docs/dawn/features/dawn_load_resolve_texture.md
+++ b/docs/dawn/features/dawn_load_resolve_texture.md
@@ -4,6 +4,7 @@
 
 Additional functionalities:
  - Adds `wgpu::LoadOp::ExpandResolveTexture` enum value to specify that the MSAA attachment will load the pixels from its corresponding resolve texture. This is cheaper than `wgpu::LoadOp::Load` which will load the existing pixels of the MSAA attachment itself.
+ - Adds `wgpu::MultisampleStateExpandResolveTextureDawn` as chained struct for `wgpu::RenderPipelineDescriptor::MultisampleState`. It has `enabled` flag to indicate that the render pipeline is going to be used in a render pass with `ExpandResolveTexture` load op.
 
 Example Usage:
 ```
@@ -33,6 +34,15 @@
 renderPassEncoder.Draw(3);
 renderPassEncoder.End();
 
+// Create a render pipeline with wgpu::MultisampleStateExpandResolveTextureDawn.
+wgpu::MultisampleStateExpandResolveTextureDawn pipelineExpandResolveTextureState;
+pipelineExpandResolveTextureState.enabled = true;
+
+wgpu::RenderPipelineDescriptor pipelineDesc = ...;
+pipelineDesc.multisample.count = 4;
+pipelineDesc.multisample.nextInChain = &pipelineExpandResolveTextureState;
+
+auto pipeline = device.CreateRenderPipeline(&pipelineDesc);
 
 // Create another render pass with "ExpandResolveTexture" LoadOp.
 // Even though we discard the previous content of the MSAA texture,
@@ -46,6 +56,7 @@
  = wgpu::LoadOp::ExpandResolveTexture;
 
 auto renderPassEncoder2 = encoder.BeginRenderPass(&renderPassDesc2);
+renderPassEncoder2.SetPipeline(pipeline);
 renderPassEncoder2.Draw(3);
 renderPassEncoder2.End();
 
@@ -53,5 +64,7 @@
 
 Notes:
  - If a resolve texture is used in a `wgpu::LoadOp::ExpandResolveTexture` operation, it must have `wgpu::TextureUsage::TextureBinding` usage.
+ - If `wgpu::MultisampleStateExpandResolveTextureDawn` chained struct is not included in a `wgpu::RenderPipelineDescriptor::MultisampleState`  or if it is included but `enabled` boolean flag is false, then the result render pipeline cannot be used in a render pass using `ExpandResolveTexture` load op.
+   - Similarly, a render pipeline created with `wgpu::MultisampleStateExpandResolveTextureDawn`'s `enabled` flag = `true` won't be able to be used in normal render passes.
  - Currently only one color attachment is supported and the `ExpandResolveTexture` LoadOp only works on color attachment, this could be changed in future.
  - The texture is not supported if it is not resolvable by WebGPU standard. This means this feature currently doesn't work with integer textures.
diff --git a/docs/dawn/features/msaa_render_to_single_samples.md b/docs/dawn/features/msaa_render_to_single_samples.md
index a03c12d..7c13f17 100644
--- a/docs/dawn/features/msaa_render_to_single_samples.md
+++ b/docs/dawn/features/msaa_render_to_single_samples.md
@@ -14,14 +14,8 @@
 
 auto texture = device.CreateTexture(&desc);
 
-// Create a render pipeline to be used in a "implicit multi-sampled" render pass.
-wgpu::DawnMultisampleStateRenderToSingleSampled pipelineMSAARenderToSingleSampledDesc;
-pipelineMSAARenderToSingleSampledDesc.enabled = true;
-
+// Create a render pipeline
 wgpu::RenderPipelineDescriptor pipelineDesc = ...;
-pipelineDesc.multisample.count = 4;
-pipelineDesc.multisample.nextInChain = &pipelineMSAARenderToSingleSampledDesc;
-
 auto pipeline = device.CreateRenderPipeline(&pipelineDesc);
 
 // Create a render pass with "implicit multi-sampled" enabled.
@@ -43,8 +37,6 @@
 
 Notes:
  - If a texture needs to be used as an attachment in a "implicit multi-sampled" render pass, it must have `wgpu::TextureUsage::TextureBinding` usage.
- - If `wgpu::DawnMultisampleStateRenderToSingleSampled` chained struct is not included in a `wgpu::RenderPipelineDescriptor::MultisampleState`  or if it is included but `enabled` boolean flag is false, then the result render pipeline cannot be used in a "implicit multi-sampled" render pass.
-   - Similarly, a render pipeline created with `wgpu::DawnMultisampleStateRenderToSingleSampled`'s `enabled` flag = `true` won't be able to be used in normal render passes.
  - If a texture is attached to a "implicit multi-sampled" render pass. It must be single-sampled. It mustn't be assigned to the `resolveTarget` field of the the render pass' color attachment.
  - Depth stencil textures can be attached to a "implicit multi-sampled" render pass. But its sample count must match the number specified in one color attachment's `wgpu::DawnRenderPassColorAttachmentRenderToSingleSampled`'s `implicitSampleCount` field.
  - Currently only one color attachment is supported, this could be changed in future.
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index 761f46f..2acb36d 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -3337,6 +3337,16 @@
     },
 
     "dawn multisample state render to single sampled": {
+        "tags": ["dawn", "deprecated"],
+        "category": "structure",
+        "chained": "in",
+        "chain roots": ["multisample state"],
+        "members": [
+            {"name": "enabled", "type": "bool", "default": "false"}
+        ]
+    },
+
+    "multisample state expand resolve texture dawn": {
         "tags": ["dawn"],
         "category": "structure",
         "chained": "in",
@@ -3740,7 +3750,7 @@
             {"value": 1010, "name": "request adapter options LUID", "tags": ["dawn", "native"]},
             {"value": 1011, "name": "request adapter options get GL proc", "tags": ["dawn", "native"]},
             {"value": 1012, "name": "request adapter options D3D11 device", "tags": ["dawn", "native"]},
-            {"value": 1013, "name": "dawn multisample state render to single sampled", "tags": ["dawn"]},
+            {"value": 1013, "name": "dawn multisample state render to single sampled", "tags": ["dawn", "deprecated"]},
             {"value": 1014, "name": "dawn render pass color attachment render to single sampled", "tags": ["dawn"]},
             {"value": 1015, "name": "render pass pixel local storage", "tags": ["dawn"]},
             {"value": 1016, "name": "pipeline layout pixel local storage", "tags": ["dawn"]},
@@ -3754,6 +3764,7 @@
             {"value": 1024, "name": "dawn WGSL blocklist", "tags": ["dawn", "native"]},
             {"value": 1025, "name": "drm format capabilities", "tags": ["dawn"]},
             {"value": 1026, "name": "shader module compilation options", "tags": ["dawn"]},
+            {"value": 1027, "name": "multisample state expand resolve texture dawn", "tags": ["dawn"]},
 
             {"value": 1100, "name": "shared texture memory vk image descriptor", "tags": ["dawn", "native"]},
             {"value": 1101, "name": "shared texture memory vk dedicated allocation descriptor", "tags": ["dawn", "native"]},
diff --git a/src/dawn/native/AttachmentState.cpp b/src/dawn/native/AttachmentState.cpp
index 099c70d..18bae63 100644
--- a/src/dawn/native/AttachmentState.cpp
+++ b/src/dawn/native/AttachmentState.cpp
@@ -64,9 +64,9 @@
                                  const PipelineLayoutBase* layout)
     : ObjectBase(device), mSampleCount(descriptor->multisample.count) {
     UnpackedPtr<MultisampleState> unpackedMultisampleState = Unpack(&descriptor->multisample);
-    if (auto* msaaRenderToSingleSampledDesc =
-            unpackedMultisampleState.Get<DawnMultisampleStateRenderToSingleSampled>()) {
-        mIsMSAARenderToSingleSampledEnabled = msaaRenderToSingleSampledDesc->enabled;
+    if (auto* msaaExpandResolveState =
+            unpackedMultisampleState.Get<MultisampleStateExpandResolveTextureDawn>()) {
+        mHasExpandResolveLoadOp = msaaExpandResolveState->enabled;
     }
 
     if (descriptor->fragment != nullptr) {
@@ -112,7 +112,6 @@
         if (msaaRenderToSingleSampledDesc != nullptr &&
             msaaRenderToSingleSampledDesc->implicitSampleCount > 1) {
             attachmentSampleCount = msaaRenderToSingleSampledDesc->implicitSampleCount;
-            mIsMSAARenderToSingleSampledEnabled = true;
         } else {
             attachmentSampleCount = attachment->GetTexture()->GetSampleCount();
         }
@@ -122,6 +121,10 @@
         } else {
             DAWN_ASSERT(mSampleCount == attachmentSampleCount);
         }
+
+        if (colorAttachment.loadOp == wgpu::LoadOp::ExpandResolveTexture) {
+            mHasExpandResolveLoadOp = true;
+        }
     }
 
     // Gather the depth-stencil information.
@@ -163,7 +166,7 @@
     mColorFormats = blueprint.mColorFormats;
     mDepthStencilFormat = blueprint.mDepthStencilFormat;
     mSampleCount = blueprint.mSampleCount;
-    mIsMSAARenderToSingleSampledEnabled = blueprint.mIsMSAARenderToSingleSampledEnabled;
+    mHasExpandResolveLoadOp = blueprint.mHasExpandResolveLoadOp;
     mHasPLS = blueprint.mHasPLS;
     mStorageAttachmentSlots = blueprint.mStorageAttachmentSlots;
     SetContentHash(blueprint.GetContentHash());
@@ -199,7 +202,7 @@
     }
 
     // Both attachment state must either enable MSAA render to single sampled or disable it.
-    if (a->mIsMSAARenderToSingleSampledEnabled != b->mIsMSAARenderToSingleSampledEnabled) {
+    if (a->mHasExpandResolveLoadOp != b->mHasExpandResolveLoadOp) {
         return false;
     }
 
@@ -235,7 +238,7 @@
     HashCombine(&hash, mSampleCount);
 
     // Hash MSAA render to single sampled flag
-    HashCombine(&hash, mIsMSAARenderToSingleSampledEnabled);
+    HashCombine(&hash, mHasExpandResolveLoadOp);
 
     // Hash the PLS state
     HashCombine(&hash, mHasPLS);
@@ -268,8 +271,8 @@
     return mSampleCount;
 }
 
-bool AttachmentState::IsMSAARenderToSingleSampledEnabled() const {
-    return mIsMSAARenderToSingleSampledEnabled;
+bool AttachmentState::HasExpandResolveLoadOp() const {
+    return mHasExpandResolveLoadOp;
 }
 
 bool AttachmentState::HasPixelLocalStorage() const {
diff --git a/src/dawn/native/AttachmentState.h b/src/dawn/native/AttachmentState.h
index 5513ad3..7f66669 100644
--- a/src/dawn/native/AttachmentState.h
+++ b/src/dawn/native/AttachmentState.h
@@ -66,7 +66,7 @@
     bool HasDepthStencilAttachment() const;
     wgpu::TextureFormat GetDepthStencilFormat() const;
     uint32_t GetSampleCount() const;
-    bool IsMSAARenderToSingleSampledEnabled() const;
+    bool HasExpandResolveLoadOp() const;
     bool HasPixelLocalStorage() const;
     const std::vector<wgpu::TextureFormat>& GetStorageAttachmentSlots() const;
     std::vector<ColorAttachmentIndex> ComputeStorageAttachmentPackingInColorAttachments() const;
@@ -86,11 +86,7 @@
     wgpu::TextureFormat mDepthStencilFormat = wgpu::TextureFormat::Undefined;
     uint32_t mSampleCount = 0;
 
-    // TODO(dawn:1710): This flag needs to be revised. Either remove MSAARenderToSingleSampled
-    // feature or implement it using native extension such as
-    // VK_EXT_multisampled_render_to_single_sampled. The native implementation doesn't need graphics
-    // pipeline and render pass to be strictly compatible.
-    bool mIsMSAARenderToSingleSampledEnabled = false;
+    bool mHasExpandResolveLoadOp = false;
     bool mHasPLS = false;
     std::vector<wgpu::TextureFormat> mStorageAttachmentSlots;
 };
diff --git a/src/dawn/native/BlitColorToColorWithDraw.cpp b/src/dawn/native/BlitColorToColorWithDraw.cpp
index 920562e..dca0223 100644
--- a/src/dawn/native/BlitColorToColorWithDraw.cpp
+++ b/src/dawn/native/BlitColorToColorWithDraw.cpp
@@ -134,6 +134,9 @@
     // Multisample state.
     DAWN_ASSERT(sampleCount > 1);
     renderPipelineDesc.multisample.count = sampleCount;
+    wgpu::MultisampleStateExpandResolveTextureDawn msaaExpandResolveState;
+    msaaExpandResolveState.enabled = true;
+    renderPipelineDesc.multisample.nextInChain = &msaaExpandResolveState;
 
     // Bind group layout.
     Ref<BindGroupLayoutBase> bindGroupLayout;
diff --git a/src/dawn/native/RenderPipeline.cpp b/src/dawn/native/RenderPipeline.cpp
index 5a49efc..eff2434 100644
--- a/src/dawn/native/RenderPipeline.cpp
+++ b/src/dawn/native/RenderPipeline.cpp
@@ -353,15 +353,17 @@
 MaybeError ValidateMultisampleState(const DeviceBase* device, const MultisampleState* descriptor) {
     UnpackedPtr<MultisampleState> unpacked;
     DAWN_TRY_ASSIGN(unpacked, ValidateAndUnpack(descriptor));
-    if (unpacked.Get<DawnMultisampleStateRenderToSingleSampled>()) {
-        DAWN_INVALID_IF(!device->HasFeature(Feature::MSAARenderToSingleSampled),
-                        "The msaaRenderToSingleSampledDesc is not empty while the "
-                        "msaa-render-to-single-sampled feature is not enabled.");
+    if (unpacked.Get<MultisampleStateExpandResolveTextureDawn>()) {
+        DAWN_INVALID_IF(!device->HasFeature(Feature::DawnLoadResolveTexture),
+                        "The MultisampleStateExpandResolveTextureDawn struct is used while the "
+                        "%s feature is not enabled.",
+                        ToAPI(Feature::DawnLoadResolveTexture));
 
-        DAWN_INVALID_IF(descriptor->count <= 1,
-                        "The msaaRenderToSingleSampledDesc is not empty while multisample count "
-                        "(%u) is not > 1.",
-                        descriptor->count);
+        DAWN_INVALID_IF(
+            descriptor->count <= 1,
+            "The MultisampleStateExpandResolveTextureDawn struct is used while multisample count "
+            "(%u) is not > 1.",
+            descriptor->count);
     }
 
     DAWN_INVALID_IF(!IsValidSampleCount(descriptor->count),
diff --git a/src/dawn/native/webgpu_absl_format.cpp b/src/dawn/native/webgpu_absl_format.cpp
index dfe7353..4a7febf 100644
--- a/src/dawn/native/webgpu_absl_format.cpp
+++ b/src/dawn/native/webgpu_absl_format.cpp
@@ -355,9 +355,8 @@
 
     s->Append(absl::StrFormat("sampleCount: %u", value->GetSampleCount()));
 
-    if (value->GetDevice()->HasFeature(Feature::MSAARenderToSingleSampled)) {
-        s->Append(absl::StrFormat(", msaaRenderToSingleSampled: %d",
-                                  value->IsMSAARenderToSingleSampledEnabled()));
+    if (value->GetDevice()->HasFeature(Feature::DawnLoadResolveTexture)) {
+        s->Append(absl::StrFormat(", hasExpandResolveLoadOp: %d", value->HasExpandResolveLoadOp()));
     }
 
     if (value->HasPixelLocalStorage()) {
diff --git a/src/dawn/tests/end2end/MultisampledRenderingTests.cpp b/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
index f7ecf3c..b6675cc 100644
--- a/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
+++ b/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
@@ -62,7 +62,7 @@
         uint32_t sampleMask = 0xFFFFFFFF,
         bool alphaToCoverageEnabled = false,
         bool flipTriangle = false,
-        bool enableMSAARenderToSingleSampled = false) {
+        bool enableExpandResolveLoadOp = false) {
         const char* kFsOneOutputWithDepth = R"(
             struct U {
                 color : vec4f,
@@ -95,13 +95,13 @@
         const char* fs = testDepth ? kFsOneOutputWithDepth : kFsOneOutputWithoutDepth;
 
         return CreateRenderPipelineForTest(fs, 1, testDepth, sampleMask, alphaToCoverageEnabled,
-                                           flipTriangle, enableMSAARenderToSingleSampled);
+                                           flipTriangle, enableExpandResolveLoadOp);
     }
 
     wgpu::RenderPipeline CreateRenderPipelineWithTwoOutputsForTest(
         uint32_t sampleMask = 0xFFFFFFFF,
         bool alphaToCoverageEnabled = false,
-        bool enableMSAARenderToSingleSampled = false) {
+        bool enableExpandResolveLoadOp = false) {
         const char* kFsTwoOutputs = R"(
             struct U {
                 color0 : vec4f,
@@ -123,13 +123,13 @@
 
         return CreateRenderPipelineForTest(kFsTwoOutputs, 2, false, sampleMask,
                                            alphaToCoverageEnabled, /*flipTriangle=*/false,
-                                           enableMSAARenderToSingleSampled);
+                                           enableExpandResolveLoadOp);
     }
 
     wgpu::RenderPipeline CreateRenderPipelineWithNonZeroLocationOutputForTest(
         uint32_t sampleMask = 0xFFFFFFFF,
         bool alphaToCoverageEnabled = false,
-        bool enableMSAARenderToSingleSampled = false) {
+        bool enableExpandResolveLoadOp = false) {
         const char* kFsNonZeroLocationOutputs = R"(
             struct U {
                 color : vec4f
@@ -142,7 +142,7 @@
 
         return CreateRenderPipelineForTest(kFsNonZeroLocationOutputs, 1, false, sampleMask,
                                            alphaToCoverageEnabled, /*flipTriangle=*/false,
-                                           enableMSAARenderToSingleSampled, 1);
+                                           enableExpandResolveLoadOp, 1);
     }
 
     wgpu::Texture CreateTextureForRenderAttachment(wgpu::TextureFormat format,
@@ -271,7 +271,7 @@
                                                      uint32_t sampleMask = 0xFFFFFFFF,
                                                      bool alphaToCoverageEnabled = false,
                                                      bool flipTriangle = false,
-                                                     bool enableMSAARenderToSingleSampled = false,
+                                                     bool enableExpandResolveLoadOp = false,
                                                      uint32_t firstAttachmentLocation = 0) {
         utils::ComboRenderPipelineDescriptor pipelineDescriptor;
 
@@ -319,10 +319,10 @@
         pipelineDescriptor.multisample.mask = sampleMask;
         pipelineDescriptor.multisample.alphaToCoverageEnabled = alphaToCoverageEnabled;
 
-        wgpu::DawnMultisampleStateRenderToSingleSampled msaaRenderToSingleSampledDesc;
-        if (enableMSAARenderToSingleSampled) {
-            msaaRenderToSingleSampledDesc.enabled = true;
-            pipelineDescriptor.multisample.nextInChain = &msaaRenderToSingleSampledDesc;
+        wgpu::MultisampleStateExpandResolveTextureDawn msaaExpandResolveDesc;
+        if (enableExpandResolveLoadOp) {
+            msaaExpandResolveDesc.enabled = true;
+            pipelineDescriptor.multisample.nextInChain = &msaaExpandResolveDesc;
         }
 
         pipelineDescriptor.cFragment.targetCount = numColorAttachments + firstAttachmentLocation;
@@ -1373,7 +1373,7 @@
     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(
         /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false,
-        /*flipTriangle=*/false, /*enableMSAARenderToSingleSampled=*/true);
+        /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false);
 
     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
 
@@ -1420,7 +1420,7 @@
     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(
         /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false,
-        /*flipTriangle=*/false, /*enableMSAARenderToSingleSampled=*/true);
+        /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false);
 
     constexpr wgpu::Color kRed = {1.0f, 0.0f, 0.0f, 1.0f};
     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
@@ -1474,7 +1474,7 @@
     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(
         /*testDepth=*/true, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false,
-        /*flipTriangle=*/false, /*enableMSAARenderToSingleSampled=*/true);
+        /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false);
 
     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
     constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
@@ -1555,7 +1555,7 @@
     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
     wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(
         /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false,
-        /*flipTriangle=*/false, /*enableMSAARenderToSingleSampled=*/false);
+        /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false);
 
     constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
 
@@ -1587,6 +1587,79 @@
     VerifyResolveTarget(kGreen, singleSampledTexture);
 }
 
+// Test ExpandResolveTexture load op rendering with depth test works correctly.
+TEST_P(DawnLoadResolveTextureTest, DrawWithDepthTest) {
+    auto multiSampledTexture = CreateTextureForRenderAttachment(
+        kColorFormat, 4, 1, 1,
+        /*transientAttachment=*/device.HasFeature(wgpu::FeatureName::TransientAttachments),
+        /*supportsTextureBinding=*/false);
+    auto multiSampledTextureView = multiSampledTexture.CreateView();
+
+    auto singleSampledTexture =
+        CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false,
+                                         /*supportsTextureBinding=*/true);
+
+    auto singleSampledTextureView = singleSampledTexture.CreateView();
+
+    wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(
+        /*testDepth=*/true, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false,
+        /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/true);
+
+    wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
+
+    constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
+    constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
+
+    // In first render pass we clear the render pass without drawing anything
+    {
+        utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
+            {multiSampledTextureView}, {singleSampledTextureView}, wgpu::LoadOp::Clear,
+            wgpu::LoadOp::Clear,
+            /*testDepth=*/true);
+        renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard;
+
+        wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass);
+        renderPassEncoder.End();
+    }
+
+    // In 2nd render pass we draw a green triangle with depth value == 0.2f.
+    {
+        utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
+            {multiSampledTextureView}, {singleSampledTextureView},
+            wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Clear,
+            /*testDepth=*/true);
+        renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard;
+
+        std::array<float, 8> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a,  // Color
+                                             0.2f};                                   // depth
+        constexpr uint32_t kSize = sizeof(kUniformData);
+        EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
+    }
+
+    // In 3rd render pass we draw a red triangle with depth value == 0.5f.
+    // This red triangle should not be displayed because it is behind the green one that is drawn in
+    // the last render pass.
+    {
+        utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
+            {multiSampledTextureView}, {singleSampledTextureView},
+            wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Load,
+            /*testDepth=*/true);
+        renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard;
+
+        std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a,  // color
+                                             0.5f};                           // depth
+        constexpr uint32_t kSize = sizeof(kUniformData);
+        EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
+    }
+
+    wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
+    queue.Submit(1, &commandBuffer);
+
+    // The color of the pixel in the middle of mResolveTexture should be green if MSAA resolve runs
+    // correctly with depth test.
+    VerifyResolveTarget(kGreen, singleSampledTexture);
+}
+
 DAWN_INSTANTIATE_TEST(MultisampledRenderingTest,
                       D3D11Backend(),
                       D3D12Backend(),
diff --git a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
index 4d67aac..a43dbe4 100644
--- a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
@@ -1866,16 +1866,16 @@
     }
 }
 
-// Creating render pipeline with DawnMultisampleStateRenderToSingleSampled without enabling
-// MSAARenderToSingleSampled feature should result in error.
-TEST_F(RenderPipelineValidationTest, MSAARenderToSingleSampledOnUnsupportedDevice) {
+// Creating render pipeline with MultisampleStateExpandResolveTextureDawn without enabling
+// LoadResolveTexture feature should result in error.
+TEST_F(RenderPipelineValidationTest, LoadResolveTextureOnUnsupportedDevice) {
     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsModule;
 
-    wgpu::DawnMultisampleStateRenderToSingleSampled pipelineMSAARenderToSingleSampledDesc;
-    pipelineMSAARenderToSingleSampledDesc.enabled = true;
-    pipelineDescriptor.multisample.nextInChain = &pipelineMSAARenderToSingleSampledDesc;
+    wgpu::MultisampleStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
+    pipelineMSAAExpandResolveDesc.enabled = true;
+    pipelineDescriptor.multisample.nextInChain = &pipelineMSAAExpandResolveDesc;
     pipelineDescriptor.multisample.count = 4;
 
     ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&pipelineDescriptor),
@@ -2289,8 +2289,7 @@
     ASSERT_DEVICE_ERROR(encoder.Finish());
 }
 
-class MSAARenderToSingleSampledPipelineDescriptorValidationTest
-    : public RenderPipelineValidationTest {
+class LoadResolveTexturePipelineDescriptorValidationTest : public RenderPipelineValidationTest {
   protected:
     void SetUp() override {
         RenderPipelineValidationTest::SetUp();
@@ -2305,7 +2304,7 @@
 
     WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter,
                                 wgpu::DeviceDescriptor descriptor) override {
-        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::MSAARenderToSingleSampled};
+        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::DawnLoadResolveTexture};
         descriptor.requiredFeatures = requiredFeatures;
         descriptor.requiredFeatureCount = 1;
         return dawnAdapter.CreateDevice(&descriptor);
@@ -2326,22 +2325,21 @@
     wgpu::ShaderModule fsWithTextureModule;
 };
 
-// Test that creating and using a render pipeline with DawnMultisampleStateRenderToSingleSampled
+// Test that creating and using a render pipeline with MultisampleStateExpandResolveTextureDawn
 // chained struct should success.
-TEST_F(MSAARenderToSingleSampledPipelineDescriptorValidationTest, ValidUse) {
+TEST_F(LoadResolveTexturePipelineDescriptorValidationTest, ValidUse) {
     constexpr uint32_t kSampleCount = 4;
 
+    auto msaaTexture = CreateTexture(wgpu::TextureUsage::RenderAttachment, kSampleCount);
+
     // Create single sampled texture.
     auto texture =
         CreateTexture(wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding, 1);
 
-    // Create render pass (with DawnRenderPassColorAttachmentRenderToSingleSampled).
-    utils::ComboRenderPassDescriptor renderPassDescriptor({texture.CreateView()});
-    renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
-
-    wgpu::DawnRenderPassColorAttachmentRenderToSingleSampled renderPassRenderToSingleSampledDesc;
-    renderPassRenderToSingleSampledDesc.implicitSampleCount = kSampleCount;
-    renderPassDescriptor.cColorAttachments[0].nextInChain = &renderPassRenderToSingleSampledDesc;
+    // Create render pass (with ExpandResolveTexture load op).
+    utils::ComboRenderPassDescriptor renderPassDescriptor({msaaTexture.CreateView()});
+    renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture;
+    renderPassDescriptor.cColorAttachments[0].resolveTarget = texture.CreateView();
 
     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
     wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
@@ -2351,9 +2349,9 @@
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsWithTextureModule;
 
-    wgpu::DawnMultisampleStateRenderToSingleSampled pipelineMSAARenderToSingleSampledDesc;
-    pipelineMSAARenderToSingleSampledDesc.enabled = true;
-    pipelineDescriptor.multisample.nextInChain = &pipelineMSAARenderToSingleSampledDesc;
+    wgpu::MultisampleStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
+    pipelineMSAAExpandResolveDesc.enabled = true;
+    pipelineDescriptor.multisample.nextInChain = &pipelineMSAAExpandResolveDesc;
     pipelineDescriptor.multisample.count = kSampleCount;
 
     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
@@ -2371,33 +2369,34 @@
     encoder.Finish();
 }
 
-// If a render pipeline's MultisampleState contains DawnMultisampleStateRenderToSingleSampled
+// If a render pipeline's MultisampleState contains MultisampleStateExpandResolveTextureDawn
 // chained struct. Then its sampleCount must be > 1.
-TEST_F(MSAARenderToSingleSampledPipelineDescriptorValidationTest,
+TEST_F(LoadResolveTexturePipelineDescriptorValidationTest,
        PipelineSampleCountMustBeGreaterThanOne) {
     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsModule;
 
-    wgpu::DawnMultisampleStateRenderToSingleSampled pipelineMSAARenderToSingleSampledDesc;
-    pipelineMSAARenderToSingleSampledDesc.enabled = true;
-    pipelineDescriptor.multisample.nextInChain = &pipelineMSAARenderToSingleSampledDesc;
+    wgpu::MultisampleStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
+    pipelineMSAAExpandResolveDesc.enabled = true;
+    pipelineDescriptor.multisample.nextInChain = &pipelineMSAAExpandResolveDesc;
     pipelineDescriptor.multisample.count = 1;
 
     ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&pipelineDescriptor),
                         testing::HasSubstr("multisample count (1) is not > 1"));
 }
 
-// If a render pipeline is created with MSAA render to single sampled enabled , then it cannot be
-// used in a render pass that wasn't created with that feature enabled.
-TEST_F(MSAARenderToSingleSampledPipelineDescriptorValidationTest,
+// If a render pipeline is created with MultisampleStateExpandResolveTextureDawn.enabled =
+// true, then it cannot be used in a render pass that wasn't created with ExpandResolveTexture load
+// op.
+TEST_F(LoadResolveTexturePipelineDescriptorValidationTest,
        MSAARenderToSingleSampledPipeline_UseIn_NormalRenderPass_Error) {
     constexpr uint32_t kSampleCount = 4;
 
     // Create MSAA texture.
     auto texture = CreateTexture(wgpu::TextureUsage::RenderAttachment, 4);
 
-    // Create render pass (without DawnRenderPassColorAttachmentRenderToSingleSampled).
+    // Create render pass (without ExpandResolveTexture load op).
     utils::ComboRenderPassDescriptor renderPassDescriptor({texture.CreateView()});
 
     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
@@ -2408,9 +2407,9 @@
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsModule;
 
-    wgpu::DawnMultisampleStateRenderToSingleSampled pipelineMSAARenderToSingleSampledDesc;
-    pipelineMSAARenderToSingleSampledDesc.enabled = true;
-    pipelineDescriptor.multisample.nextInChain = &pipelineMSAARenderToSingleSampledDesc;
+    wgpu::MultisampleStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
+    pipelineMSAAExpandResolveDesc.enabled = true;
+    pipelineDescriptor.multisample.nextInChain = &pipelineMSAAExpandResolveDesc;
     pipelineDescriptor.multisample.count = kSampleCount;
 
     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
@@ -2420,26 +2419,28 @@
     ASSERT_DEVICE_ERROR(encoder.Finish());
 }
 
-// Using a normal render pipeline in a MSAA render to single sampled render pass should result in
+// Using a normal render pipeline in a ExpandResolveTexture render pass should result in
 // incompatible error.
-TEST_F(MSAARenderToSingleSampledPipelineDescriptorValidationTest,
-       NormalPipeline_Use_In_MSAARenderToSingleSampledRenderPass_Error) {
+TEST_F(LoadResolveTexturePipelineDescriptorValidationTest,
+       NormalPipeline_Use_In_ExpandResolveTextureRenderPass_Error) {
     constexpr uint32_t kSampleCount = 4;
 
+    // Create multi sampled texture.
+    auto msaaTexture = CreateTexture(wgpu::TextureUsage::RenderAttachment, kSampleCount);
+
     // Create single sampled texture.
     auto texture =
         CreateTexture(wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding, 1);
 
-    // Create render pass (with DawnRenderPassColorAttachmentRenderToSingleSampled).
-    utils::ComboRenderPassDescriptor renderPassDescriptor({texture.CreateView()});
-    wgpu::DawnRenderPassColorAttachmentRenderToSingleSampled renderPassRenderToSingleSampledDesc;
-    renderPassRenderToSingleSampledDesc.implicitSampleCount = kSampleCount;
-    renderPassDescriptor.cColorAttachments[0].nextInChain = &renderPassRenderToSingleSampledDesc;
+    // Create render pass (with ExpandResolveTexture load op).
+    utils::ComboRenderPassDescriptor renderPassDescriptor({msaaTexture.CreateView()});
+    renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture;
+    renderPassDescriptor.cColorAttachments[0].resolveTarget = texture.CreateView();
 
     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
     wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
 
-    // Create render pipeline (without DawnMultisampleStateRenderToSingleSampled)
+    // Create render pipeline (without MultisampleStateExpandResolveTextureDawn)
     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsModule;
@@ -2453,25 +2454,22 @@
     ASSERT_DEVICE_ERROR(encoder.Finish());
 }
 
-// Bind color attachment in the MSAA render to single sampled render pass as texture should result
+// Bind resolve attachment in a ExpandResolveTexture render pass as texture should result
 // in error.
-TEST_F(MSAARenderToSingleSampledPipelineDescriptorValidationTest,
-       BindColorAttachmentAsTextureError) {
+TEST_F(LoadResolveTexturePipelineDescriptorValidationTest, BindColorAttachmentAsTextureError) {
     constexpr uint32_t kSampleCount = 4;
 
+    // Create multi sampled texture.
+    auto msaaTexture = CreateTexture(wgpu::TextureUsage::RenderAttachment, kSampleCount);
+
     // Create single sampled texture.
-    auto renderTexture =
-        CreateTexture(wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding |
-                          wgpu::TextureUsage::TextureBinding,
-                      1);
+    auto resolveTexture =
+        CreateTexture(wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding, 1);
 
-    // Create render pass (with DawnRenderPassColorAttachmentRenderToSingleSampled).
-    utils::ComboRenderPassDescriptor renderPassDescriptor({renderTexture.CreateView()});
-    renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
-
-    wgpu::DawnRenderPassColorAttachmentRenderToSingleSampled renderPassRenderToSingleSampledDesc;
-    renderPassRenderToSingleSampledDesc.implicitSampleCount = kSampleCount;
-    renderPassDescriptor.cColorAttachments[0].nextInChain = &renderPassRenderToSingleSampledDesc;
+    // Create render pass (with ExpandResolveTexture load op).
+    utils::ComboRenderPassDescriptor renderPassDescriptor({msaaTexture.CreateView()});
+    renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture;
+    renderPassDescriptor.cColorAttachments[0].resolveTarget = resolveTexture.CreateView();
 
     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
     wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
@@ -2481,16 +2479,16 @@
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsWithTextureModule;
 
-    wgpu::DawnMultisampleStateRenderToSingleSampled pipelineMSAARenderToSingleSampledDesc;
-    pipelineMSAARenderToSingleSampledDesc.enabled = true;
-    pipelineDescriptor.multisample.nextInChain = &pipelineMSAARenderToSingleSampledDesc;
+    wgpu::MultisampleStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
+    pipelineMSAAExpandResolveDesc.enabled = true;
+    pipelineDescriptor.multisample.nextInChain = &pipelineMSAAExpandResolveDesc;
     pipelineDescriptor.multisample.count = kSampleCount;
 
     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
 
-    // Use color attachment's texture as input texture.
+    // Use resolve attachment as input texture.
     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
-                                                     {{0, renderTexture.CreateView()}});
+                                                     {{0, resolveTexture.CreateView()}});
 
     renderPass.SetPipeline(pipeline);
     renderPass.SetBindGroup(0, bindGroup);