Reland "GraphiteDawn: use dawn's LoadResolveTexture extension." x2

This is a reland of commit 6165e26f33aca1de3e6d87674113174b7153a136

What're changed:
- Include "load-resolve-to-msaa" bit flag in graphics pipeline keys.
The chromium test failures were caused by a cached pipeline being
returned but not compatible with the current render pass.

Original change's description:
> Reland "GraphiteDawn: use dawn's LoadResolveTexture extension."
>
> This is a reland of commit cb016dd7154aa0d8fd8a178d9f830ea3ba8f2a4b
>
> What're changed:
> - Restructure DawnCaps to avoid EMSCRIPTEN #ifdef
> - Use MultisampleStateExpandResolveTextureDawn chained struct
> to create graphics pipeline that is compatible with a render pass
> loading a resove texture.
>
> Original change's description:
> > GraphiteDawn: use dawn's LoadResolveTexture extension.
> >
> > This extension allows loading the resolve texture to MSAA texture
> > using a load op. The MSAA texture's memory will be controlled by
> > graphite unlike the old MSAARenderToSingleSampled feature.
> >
> > Bug: b/258652999
> > Change-Id: I3f537e278bd7787afc7404e611518cfa75136379
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/849496
> > Reviewed-by: Michael Ludwig <michaelludwig@google.com>
> > Commit-Queue: Quyen Le <lehoangquyen@chromium.org>
> > Reviewed-by: Greg Daniel <egdaniel@google.com>
>
> Bug: b/258652999
> Change-Id: I8b4c4b4392256b509505cfca6116de2c4ce19178
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/853476
> Reviewed-by: Greg Daniel <egdaniel@google.com>
> Commit-Queue: Quyen Le <lehoangquyen@chromium.org>
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>

Bug: b/258652999
Change-Id: I631195b2883b0414099df03a6f625509b718fb8c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/855616
Reviewed-by: Greg Daniel <egdaniel@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Quyen Le <lehoangquyen@chromium.org>
diff --git a/src/gpu/graphite/dawn/DawnCaps.cpp b/src/gpu/graphite/dawn/DawnCaps.cpp
index 934e2f6..153e16f 100644
--- a/src/gpu/graphite/dawn/DawnCaps.cpp
+++ b/src/gpu/graphite/dawn/DawnCaps.cpp
@@ -270,11 +270,10 @@
     info.fViewFormat = singleSpec.fFormat;
     info.fUsage       = wgpu::TextureUsage::RenderAttachment;
 
-#if !defined(__EMSCRIPTEN__)
-    if (fTransientAttachmentSupport && discardable == Discardable::kYes) {
-        info.fUsage |= wgpu::TextureUsage::TransientAttachment;
+    if (fSupportedTransientAttachmentUsage != wgpu::TextureUsage::None &&
+        discardable == Discardable::kYes) {
+        info.fUsage |= fSupportedTransientAttachmentUsage;
     }
-#endif
 
     return info;
 }
@@ -290,11 +289,9 @@
     info.fViewFormat = info.fFormat;
     info.fUsage       = wgpu::TextureUsage::RenderAttachment;
 
-#if !defined(__EMSCRIPTEN__)
-    if (fTransientAttachmentSupport) {
-        info.fUsage |= wgpu::TextureUsage::TransientAttachment;
+    if (fSupportedTransientAttachmentUsage != wgpu::TextureUsage::None) {
+        info.fUsage |= fSupportedTransientAttachmentUsage;
     }
-#endif
 
     return info;
 }
@@ -467,8 +464,12 @@
     fMSAARenderToSingleSampledSupport =
             backendContext.fDevice.HasFeature(wgpu::FeatureName::MSAARenderToSingleSampled);
 
-    fTransientAttachmentSupport =
-            backendContext.fDevice.HasFeature(wgpu::FeatureName::TransientAttachments);
+    if (backendContext.fDevice.HasFeature(wgpu::FeatureName::TransientAttachments)) {
+        fSupportedTransientAttachmentUsage = wgpu::TextureUsage::TransientAttachment;
+    }
+    if (backendContext.fDevice.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture)) {
+        fSupportedResolveTextureLoadOp = wgpu::LoadOp::ExpandResolveTexture;
+    }
 #endif
 
     if (!backendContext.fTick) {
@@ -841,17 +842,36 @@
     }
 }
 
-uint64_t DawnCaps::getRenderPassDescKey(const RenderPassDesc& renderPassDesc) const {
+uint64_t DawnCaps::getRenderPassDescKeyForPipeline(const RenderPassDesc& renderPassDesc) const {
     DawnTextureInfo colorInfo, depthStencilInfo;
     renderPassDesc.fColorAttachment.fTextureInfo.getDawnTextureInfo(&colorInfo);
     renderPassDesc.fDepthStencilAttachment.fTextureInfo.getDawnTextureInfo(&depthStencilInfo);
     SkASSERT(static_cast<uint32_t>(colorInfo.getViewFormat()) <= 0xffff &&
-             static_cast<uint32_t>(depthStencilInfo.getViewFormat()) <= 0xffff);
-    uint32_t colorAttachmentKey =
-            static_cast<uint32_t>(colorInfo.getViewFormat()) << 16 | colorInfo.fSampleCount;
+             static_cast<uint32_t>(depthStencilInfo.getViewFormat()) <= 0xffff &&
+             colorInfo.fSampleCount < 0x7fff);
+
+    // Note: if Dawn supports ExpandResolveTexture load op and the render pass uses it to load
+    // the resolve texture, a render pipeline will need to be created with
+    // wgpu::ColorTargetStateExpandResolveTextureDawn chained struct in order to be compatible.
+    // Hence a render pipeline created for a render pass using ExpandResolveTexture load op will
+    // be different from the one created for a render pass not using that load op.
+    // So we need to include a bit flag to differentiate the two kinds of pipelines.
+    // Also avoid returning a cached pipeline that is not compatible with the render pass using
+    // ExpandResolveTexture load op and vice versa.
+    const bool shouldIncludeLoadResolveAttachmentBit = this->resolveTextureLoadOp().has_value();
+    uint32_t loadResolveAttachmentKey = 0;
+    if (shouldIncludeLoadResolveAttachmentBit &&
+        renderPassDesc.fColorResolveAttachment.fTextureInfo.isValid() &&
+        renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad) {
+        loadResolveAttachmentKey = 1;
+    }
+
+    uint32_t colorAttachmentKey = static_cast<uint32_t>(colorInfo.getViewFormat()) << 16 |
+                                  colorInfo.fSampleCount << 1 | loadResolveAttachmentKey;
+
     uint32_t dsAttachmentKey = static_cast<uint32_t>(depthStencilInfo.getViewFormat()) << 16 |
                                depthStencilInfo.fSampleCount;
-    return (((uint64_t) colorAttachmentKey) << 32) | dsAttachmentKey;
+    return (((uint64_t)colorAttachmentKey) << 32) | dsAttachmentKey;
 }
 
 UniqueKey DawnCaps::makeGraphicsPipelineKey(const GraphicsPipelineDesc& pipelineDesc,
@@ -865,8 +885,8 @@
         builder[0] = pipelineDesc.renderStepID();
         builder[1] = pipelineDesc.paintParamsID().asUInt();
 
-        // add RenderPassDesc key
-        uint64_t renderPassKey = this->getRenderPassDescKey(renderPassDesc);
+        // Add RenderPassDesc key.
+        uint64_t renderPassKey = this->getRenderPassDescKeyForPipeline(renderPassDesc);
         builder[2] = renderPassKey & 0xFFFFFFFF;
         builder[3] = (renderPassKey >> 32) & 0xFFFFFFFF;
         builder[4] = renderPassDesc.fWriteSwizzle.asKey();
diff --git a/src/gpu/graphite/dawn/DawnCaps.h b/src/gpu/graphite/dawn/DawnCaps.h
index 2f5515c..2d8ef2a 100644
--- a/src/gpu/graphite/dawn/DawnCaps.h
+++ b/src/gpu/graphite/dawn/DawnCaps.h
@@ -27,6 +27,11 @@
     bool useAsyncPipelineCreation() const { return fUseAsyncPipelineCreation; }
     bool allowScopedErrorChecks() const { return fAllowScopedErrorChecks; }
 
+    // If this has no value then loading the resolve texture via a LoadOp is not supported.
+    std::optional<wgpu::LoadOp> resolveTextureLoadOp() const {
+        return fSupportedResolveTextureLoadOp;
+    }
+
     TextureInfo getDefaultSampledTextureInfo(SkColorType,
                                              Mipmapped mipmapped,
                                              Protected,
@@ -55,7 +60,7 @@
                             ResourceType,
                             Shareable,
                             GraphiteResourceKey*) const override;
-    uint64_t getRenderPassDescKey(const RenderPassDesc& renderPassDesc) const;
+    uint64_t getRenderPassDescKeyForPipeline(const RenderPassDesc& renderPassDesc) const;
 
     static constexpr size_t kFormatCnt = 17;
 
@@ -123,9 +128,11 @@
     wgpu::TextureFormat fColorTypeToFormatTable[kSkColorTypeCnt];
     void setColorType(SkColorType, std::initializer_list<wgpu::TextureFormat> formats);
 
-#if !defined(__EMSCRIPTEN__)
-    bool fTransientAttachmentSupport = false;
-#endif
+    // When supported, this value will hold the TransientAttachment usage symbol that is only
+    // defined in Dawn native builds and not EMSCRIPTEN but this avoids having to #define guard it.
+    wgpu::TextureUsage fSupportedTransientAttachmentUsage = wgpu::TextureUsage::None;
+    // When supported this holds the ExpandResolveTexture load op, otherwise holds no value.
+    std::optional<wgpu::LoadOp> fSupportedResolveTextureLoadOp;
 
     bool fUseAsyncPipelineCreation = true;
     bool fAllowScopedErrorChecks = true;
diff --git a/src/gpu/graphite/dawn/DawnCommandBuffer.cpp b/src/gpu/graphite/dawn/DawnCommandBuffer.cpp
index b04fe0c..3cd0d60 100644
--- a/src/gpu/graphite/dawn/DawnCommandBuffer.cpp
+++ b/src/gpu/graphite/dawn/DawnCommandBuffer.cpp
@@ -165,7 +165,7 @@
     wgpu::RenderPassDepthStencilAttachment wgpuDepthStencilAttachment;
 
     // Set up color attachment.
-#ifndef __EMSCRIPTEN__
+#if !defined(__EMSCRIPTEN__)
     wgpu::DawnRenderPassColorAttachmentRenderToSingleSampled mssaRenderToSingleSampledDesc;
 #endif
 
@@ -199,8 +199,16 @@
             SkASSERT(wgpuColorAttachment.storeOp == wgpu::StoreOp::Discard);
 
             // But it also means we have to load the resolve texture into the MSAA color attachment
-            loadMSAAFromResolveExplicitly =
-                    renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad;
+            if (renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad) {
+                std::optional<wgpu::LoadOp> resolveLoadOp =
+                        fSharedContext->dawnCaps()->resolveTextureLoadOp();
+                if (resolveLoadOp.has_value()) {
+                    wgpuColorAttachment.loadOp = *resolveLoadOp;
+                } else {
+                    // No Dawn built-in support, we need to manually load the resolve texture.
+                    loadMSAAFromResolveExplicitly = true;
+                }
+            }
             // TODO: If the color resolve texture is read-only we can use a private (vs. memoryless)
             // msaa attachment that's coupled to the framebuffer and the StoreAndMultisampleResolve
             // action instead of loading as a draw.
diff --git a/src/gpu/graphite/dawn/DawnGraphicsPipeline.cpp b/src/gpu/graphite/dawn/DawnGraphicsPipeline.cpp
index ea3b820..49fb848 100644
--- a/src/gpu/graphite/dawn/DawnGraphicsPipeline.cpp
+++ b/src/gpu/graphite/dawn/DawnGraphicsPipeline.cpp
@@ -362,6 +362,20 @@
     colorTarget.writeMask = blendInfo.fWritesColor && hasFragmentSkSL ? wgpu::ColorWriteMask::All
                                                                       : wgpu::ColorWriteMask::None;
 
+#if !defined(__EMSCRIPTEN__)
+    const bool loadMsaaFromResolve =
+            renderPassDesc.fColorResolveAttachment.fTextureInfo.isValid() &&
+            renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad;
+    // Special case: a render pass loading resolve texture requires additional settings for the
+    // pipeline to make it compatible.
+    wgpu::ColorTargetStateExpandResolveTextureDawn pipelineMSAALoadResolveTextureDesc;
+    if (loadMsaaFromResolve && sharedContext->dawnCaps()->resolveTextureLoadOp().has_value()) {
+        SkASSERT(device.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture));
+        colorTarget.nextInChain = &pipelineMSAALoadResolveTextureDesc;
+        pipelineMSAALoadResolveTextureDesc.enabled = true;
+    }
+#endif
+
     wgpu::FragmentState fragment;
     // Dawn doesn't allow having a color attachment but without fragment shader, so have to use a
     // noop fragment shader, if fragment shader is null.
@@ -508,7 +522,7 @@
     // Other state
     descriptor.primitive.frontFace = wgpu::FrontFace::CCW;
     descriptor.primitive.cullMode = wgpu::CullMode::None;
-    switch(step->primitiveType()) {
+    switch (step->primitiveType()) {
         case PrimitiveType::kTriangles:
             descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
             break;
@@ -523,24 +537,6 @@
 
     // Multisampled state
     descriptor.multisample.count = renderPassDesc.fSampleCount;
-    [[maybe_unused]] bool isMSAARenderToSingleSampled =
-            renderPassDesc.fSampleCount > 1 &&
-            renderPassDesc.fColorAttachment.fTextureInfo.isValid() &&
-            renderPassDesc.fColorAttachment.fTextureInfo.numSamples() == 1;
-#if defined(__EMSCRIPTEN__)
-    SkASSERT(!isMSAARenderToSingleSampled);
-#else
-    wgpu::DawnMultisampleStateRenderToSingleSampled pipelineMSAARenderToSingleSampledDesc;
-    if (isMSAARenderToSingleSampled) {
-        // If render pass is multi sampled but the color attachment is single sampled, we need
-        // to activate multisampled render to single sampled feature for this graphics pipeline.
-        SkASSERT(device.HasFeature(wgpu::FeatureName::MSAARenderToSingleSampled));
-
-        descriptor.multisample.nextInChain = &pipelineMSAARenderToSingleSampledDesc;
-        pipelineMSAARenderToSingleSampledDesc.enabled = true;
-    }
-#endif
-
     descriptor.multisample.mask = 0xFFFFFFFF;
     descriptor.multisample.alphaToCoverageEnabled = false;
 
diff --git a/src/gpu/graphite/dawn/DawnResourceProvider.cpp b/src/gpu/graphite/dawn/DawnResourceProvider.cpp
index ee35c7f..a343641 100644
--- a/src/gpu/graphite/dawn/DawnResourceProvider.cpp
+++ b/src/gpu/graphite/dawn/DawnResourceProvider.cpp
@@ -162,7 +162,7 @@
 wgpu::RenderPipeline DawnResourceProvider::findOrCreateBlitWithDrawPipeline(
         const RenderPassDesc& renderPassDesc) {
     uint64_t renderPassKey =
-            this->dawnSharedContext()->dawnCaps()->getRenderPassDescKey(renderPassDesc);
+            this->dawnSharedContext()->dawnCaps()->getRenderPassDescKeyForPipeline(renderPassDesc);
     wgpu::RenderPipeline pipeline = fBlitWithDrawPipelines[renderPassKey];
     if (!pipeline) {
         static constexpr char kVertexShaderText[] = R"(
diff --git a/tools/graphite/dawn/GraphiteDawnTestContext.cpp b/tools/graphite/dawn/GraphiteDawnTestContext.cpp
index e2ef3d0..f056197 100644
--- a/tools/graphite/dawn/GraphiteDawnTestContext.cpp
+++ b/tools/graphite/dawn/GraphiteDawnTestContext.cpp
@@ -121,6 +121,9 @@
     if (adapter.HasFeature(wgpu::FeatureName::R8UnormStorage)) {
         features.push_back(wgpu::FeatureName::R8UnormStorage);
     }
+    if (adapter.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture)) {
+        features.push_back(wgpu::FeatureName::DawnLoadResolveTexture);
+    }
 
     wgpu::DeviceDescriptor desc;
     desc.requiredFeatureCount  = features.size();
diff --git a/tools/window/GraphiteDawnWindowContext.cpp b/tools/window/GraphiteDawnWindowContext.cpp
index fe09208..41cb938 100644
--- a/tools/window/GraphiteDawnWindowContext.cpp
+++ b/tools/window/GraphiteDawnWindowContext.cpp
@@ -162,6 +162,9 @@
     if (adapter.HasFeature(wgpu::FeatureName::R8UnormStorage)) {
         features.push_back(wgpu::FeatureName::R8UnormStorage);
     }
+    if (adapter.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture)) {
+        features.push_back(wgpu::FeatureName::DawnLoadResolveTexture);
+    }
 
     wgpu::DeviceDescriptor deviceDescriptor;
     deviceDescriptor.requiredFeatures = features.data();