Fix texture barriers on DMSAA

Bug: skia:11396
Change-Id: Iad74958c05ed086fe85656b9dc5418d5ab4589e7
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/419838
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrSurfaceDrawContext.cpp b/src/gpu/GrSurfaceDrawContext.cpp
index b3af050..8544d0d 100644
--- a/src/gpu/GrSurfaceDrawContext.cpp
+++ b/src/gpu/GrSurfaceDrawContext.cpp
@@ -1931,11 +1931,43 @@
     // Must be called before setDstProxyView so that it sees the final bounds of the op.
     op->setClippedBounds(bounds);
 
+    // Determine if the Op will trigger the use of a separate DMSAA attachment that requires manual
+    // resolves.
+    // TODO: Currently usesAttachmentIfDMSAA checks if this is a textureProxy or not. This check is
+    // really only for GL which uses a normal texture sampling when using barriers. For Vulkan it
+    // is possible to use the msaa buffer as an input attachment even if this is not a texture.
+    // However, support for that is not fully implemented yet in Vulkan. Once it is, this check
+    // should change to a virtual caps check that returns whether we need to break up an OpsTask
+    // if it has barriers and we are about to promote to MSAA.
+    bool usesAttachmentIfDMSAA =
+            fCanUseDynamicMSAA &&
+            (!this->caps()->msaaResolvesAutomatically() || !this->asTextureProxy());
+    bool opRequiresDMSAAAttachment = usesAttachmentIfDMSAA && usesMSAA;
+    bool opTriggersDMSAAAttachment =
+            opRequiresDMSAAAttachment && !this->getOpsTask()->usesMSAASurface();
+    if (opTriggersDMSAAAttachment) {
+        // This will be the op that actually triggers use of a DMSAA attachment. Texture barriers
+        // can't be moved to a DMSAA attachment, so if there already are any on the current opsTask
+        // then we need to split.
+        if (this->getOpsTask()->renderPassXferBarriers() & GrXferBarrierFlags::kTexture) {
+            SkASSERT(!this->getOpsTask()->isColorNoOp());
+            this->replaceOpsTask()->setCannotMergeBackward();
+        }
+    }
+
     GrDstProxyView dstProxyView;
     if (analysis.requiresDstTexture()) {
-        if (!this->setupDstProxyView(*op, &dstProxyView)) {
+        if (!this->setupDstProxyView(drawOp->bounds(), usesMSAA, &dstProxyView)) {
             return;
         }
+#ifdef SK_DEBUG
+        if (fCanUseDynamicMSAA && usesMSAA && !this->caps()->msaaResolvesAutomatically()) {
+            // Since we aren't literally writing to the render target texture while using a DMSAA
+            // attachment, we need to resolve that texture before sampling it. Ensure the current
+            // opsTask got closed off in order to initiate an implicit resolve.
+            SkASSERT(this->getOpsTask()->isEmpty());
+        }
+#endif
     }
 
     auto opsTask = this->getOpsTask();
@@ -1963,7 +1995,9 @@
 #endif
 }
 
-bool GrSurfaceDrawContext::setupDstProxyView(const GrOp& op, GrDstProxyView* dstProxyView) {
+bool GrSurfaceDrawContext::setupDstProxyView(const SkRect& opBounds,
+                                             bool opRequiresMSAA,
+                                             GrDstProxyView* dstProxyView) {
     // If we are wrapping a vulkan secondary command buffer, we can't make a dst copy because we
     // don't actually have a VkImage to make a copy of. Additionally we don't have the power to
     // start and stop the render pass in order to make the copy.
@@ -1971,16 +2005,56 @@
         return false;
     }
 
-    auto dstSampleFlags = this->caps()->getDstSampleFlagsForProxy(this->asRenderTargetProxy());
+    // First get the dstSampleFlags as if we will put the draw into the current GrOpsTask
+    auto dstSampleFlags = this->caps()->getDstSampleFlagsForProxy(
+            this->asRenderTargetProxy(), this->getOpsTask()->usesMSAASurface() || opRequiresMSAA);
+
+    // If we don't have barriers for this draw then we will definitely be breaking up the GrOpsTask.
+    // However, if using dynamic MSAA, the new GrOpsTask will not have MSAA already enabled on it
+    // and that may allow us to use texture barriers. So we check if we can use barriers on the new
+    // ops task and then break it up if so.
+    if (!(dstSampleFlags & GrDstSampleFlags::kRequiresTextureBarrier) &&
+        fCanUseDynamicMSAA && this->getOpsTask()->usesMSAASurface() && !opRequiresMSAA) {
+        auto newFlags =
+                this->caps()->getDstSampleFlagsForProxy(this->asRenderTargetProxy(),
+                                                        false/*=opRequiresMSAA*/);
+        if (newFlags & GrDstSampleFlags::kRequiresTextureBarrier) {
+            // We can't have an empty ops task if we are in DMSAA and the ops task already returns
+            // true for usesMSAASurface.
+            SkASSERT(!this->getOpsTask()->isColorNoOp());
+            this->replaceOpsTask()->setCannotMergeBackward();
+            dstSampleFlags = newFlags;
+        }
+    }
 
     if (dstSampleFlags & GrDstSampleFlags::kRequiresTextureBarrier) {
-        // If we require a barrier to sample the dst it means we are sampling the RT itself either
-        // as a texture or input attachment.
+        // If we require a barrier to sample the dst it means we are sampling the RT itself
+        // either as a texture or input attachment. In this case we don't need to break up the
+        // GrOpsTask.
         dstProxyView->setProxyView(this->readSurfaceView());
         dstProxyView->setOffset(0, 0);
         dstProxyView->setDstSampleFlags(dstSampleFlags);
         return true;
     }
+    SkASSERT(dstSampleFlags == GrDstSampleFlags::kNone);
+
+    // We are using a different surface from the main color attachment to sample the dst from. If we
+    // are in DMSAA we can just use the single sampled RT texture itself. Otherwise, we must do a
+    // copy.
+    // We do have to check if we ended up here becasue we don't have texture barriers but do have
+    // msaaResolvesAutomatically (i.e. render-to-msaa-texture). In that case there will be no op or
+    // barrier between draws to flush the render target before being used as a texture in the next
+    // draw. So in that case we just fall through to doing a copy.
+    if (fCanUseDynamicMSAA && opRequiresMSAA && this->asTextureProxy() &&
+        !this->caps()->msaaResolvesAutomatically()) {
+        this->replaceOpsTaskIfModifiesColor()->setCannotMergeBackward();
+        dstProxyView->setProxyView(this->readSurfaceView());
+        dstProxyView->setOffset(0, 0);
+        dstProxyView->setDstSampleFlags(dstSampleFlags);
+        return true;
+    }
+
+    // Now we fallback to doing a copy.
 
     GrColorType colorType = this->colorInfo().colorType();
     // MSAA consideration: When there is support for reading MSAA samples in the shader we could
@@ -1993,7 +2067,7 @@
         // If we don't need the whole source, restrict to the op's bounds. We add an extra pixel
         // of padding to account for AA bloat and the unpredictable rounding of coords near pixel
         // centers during rasterization.
-        SkIRect conservativeDrawBounds = op.bounds().roundOut();
+        SkIRect conservativeDrawBounds = opBounds.roundOut();
         conservativeDrawBounds.outset(1, 1);
         SkAssertResult(copyRect.intersect(conservativeDrawBounds));
     }
@@ -2022,3 +2096,11 @@
     dstProxyView->setDstSampleFlags(dstSampleFlags);
     return true;
 }
+
+GrOpsTask* GrSurfaceDrawContext::replaceOpsTaskIfModifiesColor() {
+    if (!this->getOpsTask()->isColorNoOp()) {
+        this->replaceOpsTask();
+    }
+    return this->getOpsTask();
+}
+