Rename MultisampleStateExpandResolveTextureDawn

and make it per color target.

Pipeline compatibility should have full knowledge of which color
attachment uses ExpandResolveTexture load op.

Bug: dawn:1710
Change-Id: I435330337f15af9fa5dc28459ca5bb2d048c0f74
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/188281
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Auto-Submit: Quyen Le <lehoangquyen@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/docs/dawn/features/dawn_load_resolve_texture.md b/docs/dawn/features/dawn_load_resolve_texture.md
index e962e9d..8cd0241 100644
--- a/docs/dawn/features/dawn_load_resolve_texture.md
+++ b/docs/dawn/features/dawn_load_resolve_texture.md
@@ -4,7 +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.
+ - Adds `wgpu::ColorTargetStateExpandResolveTextureDawn` as chained struct for `wgpu::RenderPipelineDescriptor::FragmentState::ColorTargetState`. It has `enabled` flag to indicate that the render pipeline is going to be used in a render pass with `ExpandResolveTexture` load op in the respective color attachment.
 
 Example Usage:
 ```
@@ -34,13 +34,14 @@
 renderPassEncoder.Draw(3);
 renderPassEncoder.End();
 
-// Create a render pipeline with wgpu::MultisampleStateExpandResolveTextureDawn.
-wgpu::MultisampleStateExpandResolveTextureDawn pipelineExpandResolveTextureState;
+// Create a render pipeline with wgpu::ColorTargetStateExpandResolveTextureDawn.
+wgpu::ColorTargetStateExpandResolveTextureDawn pipelineExpandResolveTextureState;
 pipelineExpandResolveTextureState.enabled = true;
 
 wgpu::RenderPipelineDescriptor pipelineDesc = ...;
 pipelineDesc.multisample.count = 4;
-pipelineDesc.multisample.nextInChain = &pipelineExpandResolveTextureState;
+
+pipelineDesc->fragment->targets[0].nextInChain = &pipelineExpandResolveTextureState;
 
 auto pipeline = device.CreateRenderPipeline(&pipelineDesc);
 
@@ -64,7 +65,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.
+ - If `wgpu::ColorTargetStateExpandResolveTextureDawn` chained struct is not included in a `wgpu::RenderPipelineDescriptor::FragmentState::ColorTargetState`  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 for that respective color attachment.
+   - Similarly, a render pipeline created with `wgpu::ColorTargetStateExpandResolveTextureDawn`'s `enabled` flag = `true` won't be able to be used in render passes that don't use `ExpandResolveTexture` load op for the respective color attachment.
  - 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/src/dawn/dawn.json b/src/dawn/dawn.json
index b1366b8..d28a123 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -3369,16 +3369,6 @@
         ]
     },
 
-    "multisample state expand resolve texture dawn": {
-        "tags": ["dawn"],
-        "category": "structure",
-        "chained": "in",
-        "chain roots": ["multisample state"],
-        "members": [
-            {"name": "enabled", "type": "bool", "default": "false"}
-        ]
-    },
-
     "fragment state": {
         "category": "structure",
         "extensible": "in",
@@ -3400,6 +3390,15 @@
             {"name": "write mask", "type": "color write mask", "default": "all"}
         ]
     },
+    "color target state expand resolve texture dawn": {
+        "tags": ["dawn"],
+        "category": "structure",
+        "chained": "in",
+        "chain roots": ["color target state"],
+        "members": [
+            {"name": "enabled", "type": "bool", "default": "false"}
+        ]
+    },
     "blend state": {
         "category": "structure",
         "extensible": false,
@@ -3787,7 +3786,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": 1027, "name": "color target 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 18bae63..efd4206 100644
--- a/src/dawn/native/AttachmentState.cpp
+++ b/src/dawn/native/AttachmentState.cpp
@@ -63,12 +63,6 @@
                                  const UnpackedPtr<RenderPipelineDescriptor>& descriptor,
                                  const PipelineLayoutBase* layout)
     : ObjectBase(device), mSampleCount(descriptor->multisample.count) {
-    UnpackedPtr<MultisampleState> unpackedMultisampleState = Unpack(&descriptor->multisample);
-    if (auto* msaaExpandResolveState =
-            unpackedMultisampleState.Get<MultisampleStateExpandResolveTextureDawn>()) {
-        mHasExpandResolveLoadOp = msaaExpandResolveState->enabled;
-    }
-
     if (descriptor->fragment != nullptr) {
         DAWN_ASSERT(descriptor->fragment->targetCount <= kMaxColorAttachments);
         auto targets = ityp::SpanFromUntyped<ColorAttachmentIndex>(
@@ -79,6 +73,12 @@
             if (format != wgpu::TextureFormat::Undefined) {
                 mColorAttachmentsSet.set(i);
                 mColorFormats[i] = format;
+
+                UnpackedPtr<ColorTargetState> unpackedTarget = Unpack(&target);
+                if (auto* expandResolveState =
+                        unpackedTarget.Get<ColorTargetStateExpandResolveTextureDawn>()) {
+                    mAttachmentsToExpandResolve.set(i, expandResolveState->enabled);
+                }
             }
         }
     }
@@ -123,7 +123,7 @@
         }
 
         if (colorAttachment.loadOp == wgpu::LoadOp::ExpandResolveTexture) {
-            mHasExpandResolveLoadOp = true;
+            mAttachmentsToExpandResolve.set(i);
         }
     }
 
@@ -166,7 +166,7 @@
     mColorFormats = blueprint.mColorFormats;
     mDepthStencilFormat = blueprint.mDepthStencilFormat;
     mSampleCount = blueprint.mSampleCount;
-    mHasExpandResolveLoadOp = blueprint.mHasExpandResolveLoadOp;
+    mAttachmentsToExpandResolve = blueprint.mAttachmentsToExpandResolve;
     mHasPLS = blueprint.mHasPLS;
     mStorageAttachmentSlots = blueprint.mStorageAttachmentSlots;
     SetContentHash(blueprint.GetContentHash());
@@ -202,7 +202,7 @@
     }
 
     // Both attachment state must either enable MSAA render to single sampled or disable it.
-    if (a->mHasExpandResolveLoadOp != b->mHasExpandResolveLoadOp) {
+    if (a->mAttachmentsToExpandResolve != b->mAttachmentsToExpandResolve) {
         return false;
     }
 
@@ -238,7 +238,7 @@
     HashCombine(&hash, mSampleCount);
 
     // Hash MSAA render to single sampled flag
-    HashCombine(&hash, mHasExpandResolveLoadOp);
+    HashCombine(&hash, mAttachmentsToExpandResolve);
 
     // Hash the PLS state
     HashCombine(&hash, mHasPLS);
@@ -271,8 +271,8 @@
     return mSampleCount;
 }
 
-bool AttachmentState::HasExpandResolveLoadOp() const {
-    return mHasExpandResolveLoadOp;
+ColorAttachmentMask AttachmentState::GetExpandResolveUsingAttachmentsMask() const {
+    return mAttachmentsToExpandResolve;
 }
 
 bool AttachmentState::HasPixelLocalStorage() const {
diff --git a/src/dawn/native/AttachmentState.h b/src/dawn/native/AttachmentState.h
index 7f66669..a320818 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 HasExpandResolveLoadOp() const;
+    ColorAttachmentMask GetExpandResolveUsingAttachmentsMask() const;
     bool HasPixelLocalStorage() const;
     const std::vector<wgpu::TextureFormat>& GetStorageAttachmentSlots() const;
     std::vector<ColorAttachmentIndex> ComputeStorageAttachmentPackingInColorAttachments() const;
@@ -86,7 +86,7 @@
     wgpu::TextureFormat mDepthStencilFormat = wgpu::TextureFormat::Undefined;
     uint32_t mSampleCount = 0;
 
-    bool mHasExpandResolveLoadOp = false;
+    ColorAttachmentMask mAttachmentsToExpandResolve;
     bool mHasPLS = false;
     std::vector<wgpu::TextureFormat> mStorageAttachmentSlots;
 };
diff --git a/src/dawn/native/BlitColorToColorWithDraw.cpp b/src/dawn/native/BlitColorToColorWithDraw.cpp
index dca0223..3d65525 100644
--- a/src/dawn/native/BlitColorToColorWithDraw.cpp
+++ b/src/dawn/native/BlitColorToColorWithDraw.cpp
@@ -114,6 +114,9 @@
 
     fragmentState.targetCount = 1;
     fragmentState.targets = &colorTarget;
+    wgpu::ColorTargetStateExpandResolveTextureDawn msaaExpandResolveState;
+    msaaExpandResolveState.enabled = true;
+    colorTarget.nextInChain = &msaaExpandResolveState;
 
     RenderPipelineDescriptor renderPipelineDesc = {};
     renderPipelineDesc.label = "blit_color_to_color";
@@ -134,9 +137,6 @@
     // 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 212180c..89d6553 100644
--- a/src/dawn/native/RenderPipeline.cpp
+++ b/src/dawn/native/RenderPipeline.cpp
@@ -377,21 +377,6 @@
 }
 
 MaybeError ValidateMultisampleState(const DeviceBase* device, const MultisampleState* descriptor) {
-    UnpackedPtr<MultisampleState> unpacked;
-    DAWN_TRY_ASSIGN(unpacked, ValidateAndUnpack(descriptor));
-    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 MultisampleStateExpandResolveTextureDawn struct is used while multisample count "
-            "(%u) is not > 1.",
-            descriptor->count);
-    }
-
     DAWN_INVALID_IF(!IsValidSampleCount(descriptor->count),
                     "Multisample count (%u) is not supported.", descriptor->count);
 
@@ -462,8 +447,22 @@
     const ColorTargetState& descriptor,
     const Format* format,
     bool fragmentWritten,
-    const EntryPointMetadata::FragmentRenderAttachmentInfo& fragmentOutputVariable) {
-    DAWN_INVALID_IF(descriptor.nextInChain != nullptr, "nextInChain must be nullptr.");
+    const EntryPointMetadata::FragmentRenderAttachmentInfo& fragmentOutputVariable,
+    const MultisampleState& multisample) {
+    UnpackedPtr<ColorTargetState> unpacked;
+    DAWN_TRY_ASSIGN(unpacked, ValidateAndUnpack(&descriptor));
+    if (unpacked.Get<ColorTargetStateExpandResolveTextureDawn>()) {
+        DAWN_INVALID_IF(!device->HasFeature(Feature::DawnLoadResolveTexture),
+                        "The ColorTargetStateExpandResolveTextureDawn struct is used while the "
+                        "%s feature is not enabled.",
+                        ToAPI(Feature::DawnLoadResolveTexture));
+
+        DAWN_INVALID_IF(
+            multisample.count <= 1,
+            "The ColorTargetStateExpandResolveTextureDawn struct is used while multisample count "
+            "(%u) is not > 1.",
+            multisample.count);
+    }
 
     if (descriptor.blend) {
         DAWN_TRY_CONTEXT(ValidateBlendState(device, descriptor.blend), "validating blend state.");
@@ -635,9 +634,9 @@
         const Format* format;
         DAWN_TRY_ASSIGN(format, device->GetInternalFormat(targets[i].format));
 
-        DAWN_TRY_CONTEXT(ValidateColorTargetState(device, targets[i], format,
-                                                  fragmentMetadata.fragmentOutputMask[i],
-                                                  fragmentMetadata.fragmentOutputVariables[i]),
+        DAWN_TRY_CONTEXT(ValidateColorTargetState(
+                             device, targets[i], format, fragmentMetadata.fragmentOutputMask[i],
+                             fragmentMetadata.fragmentOutputVariables[i], multisample),
                          "validating targets[%u] framebuffer output.", i);
         colorAttachmentFormats.push_back(&device->GetValidInternalFormat(targets[i].format));
 
diff --git a/src/dawn/native/webgpu_absl_format.cpp b/src/dawn/native/webgpu_absl_format.cpp
index 4a7febf..4b78122 100644
--- a/src/dawn/native/webgpu_absl_format.cpp
+++ b/src/dawn/native/webgpu_absl_format.cpp
@@ -325,7 +325,7 @@
         return {true};
     }
 
-    s->Append("{ colorFormats: [");
+    s->Append("{ colorTargets: [");
 
     ColorAttachmentIndex nextColorIndex{};
 
@@ -336,12 +336,18 @@
         }
 
         while (nextColorIndex < i) {
-            s->Append(absl::StrFormat("%s, ", wgpu::TextureFormat::Undefined));
+            s->Append(absl::StrFormat("{format: %s}, ", wgpu::TextureFormat::Undefined));
             nextColorIndex++;
             needsComma = false;
         }
 
-        s->Append(absl::StrFormat("%s", value->GetColorAttachmentFormat(i)));
+        s->Append(absl::StrFormat("{format:%s", value->GetColorAttachmentFormat(i)));
+
+        if (value->GetDevice()->HasFeature(Feature::DawnLoadResolveTexture)) {
+            s->Append(absl::StrFormat(", expandResolveTexture:%v",
+                                      value->GetExpandResolveUsingAttachmentsMask().test(i)));
+        }
+        s->Append("}");
 
         nextColorIndex++;
         needsComma = true;
@@ -355,10 +361,6 @@
 
     s->Append(absl::StrFormat("sampleCount: %u", value->GetSampleCount()));
 
-    if (value->GetDevice()->HasFeature(Feature::DawnLoadResolveTexture)) {
-        s->Append(absl::StrFormat(", hasExpandResolveLoadOp: %d", value->HasExpandResolveLoadOp()));
-    }
-
     if (value->HasPixelLocalStorage()) {
         const std::vector<wgpu::TextureFormat>& plsSlots = value->GetStorageAttachmentSlots();
         s->Append(absl::StrFormat(", totalPixelLocalStorageSize: %d",
diff --git a/src/dawn/tests/end2end/MultisampledRenderingTests.cpp b/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
index b6675cc..15487d0 100644
--- a/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
+++ b/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
@@ -319,12 +319,6 @@
         pipelineDescriptor.multisample.mask = sampleMask;
         pipelineDescriptor.multisample.alphaToCoverageEnabled = alphaToCoverageEnabled;
 
-        wgpu::MultisampleStateExpandResolveTextureDawn msaaExpandResolveDesc;
-        if (enableExpandResolveLoadOp) {
-            msaaExpandResolveDesc.enabled = true;
-            pipelineDescriptor.multisample.nextInChain = &msaaExpandResolveDesc;
-        }
-
         pipelineDescriptor.cFragment.targetCount = numColorAttachments + firstAttachmentLocation;
         for (uint32_t i = 0; i < numColorAttachments + firstAttachmentLocation; ++i) {
             if (i < firstAttachmentLocation) {
@@ -335,6 +329,13 @@
             }
         }
 
+        // TODO(dawn:1710): support multiple targets with ExpandResolveTexture load op.
+        wgpu::ColorTargetStateExpandResolveTextureDawn msaaExpandResolveDesc;
+        if (enableExpandResolveLoadOp) {
+            msaaExpandResolveDesc.enabled = true;
+            pipelineDescriptor.cTargets[0].nextInChain = &msaaExpandResolveDesc;
+        }
+
         wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
         return pipeline;
     }
diff --git a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
index e4292ac..8da4cc7 100644
--- a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
@@ -1902,18 +1902,18 @@
     }
 }
 
-// Creating render pipeline with MultisampleStateExpandResolveTextureDawn without enabling
+// Creating render pipeline with ColorTargetStateExpandResolveTextureDawn without enabling
 // LoadResolveTexture feature should result in error.
 TEST_F(RenderPipelineValidationTest, LoadResolveTextureOnUnsupportedDevice) {
     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsModule;
-
-    wgpu::MultisampleStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
-    pipelineMSAAExpandResolveDesc.enabled = true;
-    pipelineDescriptor.multisample.nextInChain = &pipelineMSAAExpandResolveDesc;
     pipelineDescriptor.multisample.count = 4;
 
+    wgpu::ColorTargetStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
+    pipelineMSAAExpandResolveDesc.enabled = true;
+    pipelineDescriptor.cTargets[0].nextInChain = &pipelineMSAAExpandResolveDesc;
+
     ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&pipelineDescriptor),
                         testing::HasSubstr("feature is not enabled"));
 }
@@ -2361,7 +2361,7 @@
     wgpu::ShaderModule fsWithTextureModule;
 };
 
-// Test that creating and using a render pipeline with MultisampleStateExpandResolveTextureDawn
+// Test that creating and using a render pipeline with ColorTargetStateExpandResolveTextureDawn
 // chained struct should success.
 TEST_F(LoadResolveTexturePipelineDescriptorValidationTest, ValidUse) {
     constexpr uint32_t kSampleCount = 4;
@@ -2384,12 +2384,12 @@
     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsWithTextureModule;
-
-    wgpu::MultisampleStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
-    pipelineMSAAExpandResolveDesc.enabled = true;
-    pipelineDescriptor.multisample.nextInChain = &pipelineMSAAExpandResolveDesc;
     pipelineDescriptor.multisample.count = kSampleCount;
 
+    wgpu::ColorTargetStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
+    pipelineMSAAExpandResolveDesc.enabled = true;
+    pipelineDescriptor.cTargets[0].nextInChain = &pipelineMSAAExpandResolveDesc;
+
     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
 
     // Input texture.
@@ -2405,28 +2405,28 @@
     encoder.Finish();
 }
 
-// If a render pipeline's MultisampleState contains MultisampleStateExpandResolveTextureDawn
+// If a render pipeline's MultisampleState contains ColorTargetStateExpandResolveTextureDawn
 // chained struct. Then its sampleCount must be > 1.
 TEST_F(LoadResolveTexturePipelineDescriptorValidationTest,
        PipelineSampleCountMustBeGreaterThanOne) {
     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsModule;
-
-    wgpu::MultisampleStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
-    pipelineMSAAExpandResolveDesc.enabled = true;
-    pipelineDescriptor.multisample.nextInChain = &pipelineMSAAExpandResolveDesc;
     pipelineDescriptor.multisample.count = 1;
 
+    wgpu::ColorTargetStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
+    pipelineMSAAExpandResolveDesc.enabled = true;
+    pipelineDescriptor.cTargets[0].nextInChain = &pipelineMSAAExpandResolveDesc;
+
     ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&pipelineDescriptor),
                         testing::HasSubstr("multisample count (1) is not > 1"));
 }
 
-// If a render pipeline is created with MultisampleStateExpandResolveTextureDawn.enabled =
+// If a render pipeline is created with ColorTargetStateExpandResolveTextureDawn.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) {
+       ExpandResolveTexturePipeline_UseIn_NormalRenderPass_Error) {
     constexpr uint32_t kSampleCount = 4;
 
     // Create MSAA texture.
@@ -2442,12 +2442,12 @@
     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsModule;
-
-    wgpu::MultisampleStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
-    pipelineMSAAExpandResolveDesc.enabled = true;
-    pipelineDescriptor.multisample.nextInChain = &pipelineMSAAExpandResolveDesc;
     pipelineDescriptor.multisample.count = kSampleCount;
 
+    wgpu::ColorTargetStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
+    pipelineMSAAExpandResolveDesc.enabled = true;
+    pipelineDescriptor.cTargets[0].nextInChain = &pipelineMSAAExpandResolveDesc;
+
     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
     renderPass.SetPipeline(pipeline);
     renderPass.End();
@@ -2476,7 +2476,7 @@
     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
     wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
 
-    // Create render pipeline (without MultisampleStateExpandResolveTextureDawn)
+    // Create render pipeline (without ColorTargetStateExpandResolveTextureDawn)
     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsModule;
@@ -2514,12 +2514,12 @@
     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
     pipelineDescriptor.vertex.module = vsModule;
     pipelineDescriptor.cFragment.module = fsWithTextureModule;
-
-    wgpu::MultisampleStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
-    pipelineMSAAExpandResolveDesc.enabled = true;
-    pipelineDescriptor.multisample.nextInChain = &pipelineMSAAExpandResolveDesc;
     pipelineDescriptor.multisample.count = kSampleCount;
 
+    wgpu::ColorTargetStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
+    pipelineMSAAExpandResolveDesc.enabled = true;
+    pipelineDescriptor.cTargets[0].nextInChain = &pipelineMSAAExpandResolveDesc;
+
     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
 
     // Use resolve attachment as input texture.