add support for clears to Metal backend

Bug: skia:
Change-Id: I35d9fcf096d078d6dc04093e3591df0ac12079b7
Reviewed-on: https://skia-review.googlesource.com/c/161821
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index 0027ee9..918ac14 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -253,6 +253,10 @@
         return fDynamicStateArrayGeometryProcessorTextureSupport;
     }
 
+    virtual bool performPartialClearsAsDraws() const {
+        return false;
+    }
+
     /**
      * This is can be called before allocating a texture to be a dst for copySurface. This is only
      * used for doing dst copies needed in blends, thus the src is always a GrRenderTargetProxy. It
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index ddefb79..4beae87 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -240,9 +240,9 @@
                      bool canDiscardOutsideDstRect = false);
 
     // Returns a GrGpuRTCommandBuffer which GrOpLists send draw commands to instead of directly
-    // to the Gpu object.
+    // to the Gpu object. The 'bounds' rect is the content rect of the destination.
     virtual GrGpuRTCommandBuffer* getCommandBuffer(
-            GrRenderTarget*, GrSurfaceOrigin,
+            GrRenderTarget*, GrSurfaceOrigin, const SkRect& bounds,
             const GrGpuRTCommandBuffer::LoadAndStoreInfo&,
             const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo&) = 0;
 
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 3333328..369eee8 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -294,12 +294,26 @@
     // This path doesn't handle coalescing of full screen clears b.c. it
     // has to clear the entire render target - not just the content area.
     // It could be done but will take more finagling.
-    std::unique_ptr<GrOp> op(GrClearOp::Make(fRenderTargetContext->fContext, rtRect,
-                                             color, !clearRect));
-    if (!op) {
-        return;
+    if (clearRect && fRenderTargetContext->caps()->performPartialClearsAsDraws()) {
+        GrPaint paint;
+        paint.setColor4f(GrColor4f::FromGrColor(color));
+        SkRect scissor = SkRect::Make(rtRect);
+        std::unique_ptr<GrDrawOp> op(GrRectOpFactory::MakeNonAAFill(fRenderTargetContext->fContext,
+                                                                    std::move(paint), SkMatrix::I(),
+                                                                    scissor, GrAAType::kNone));
+        if (!op) {
+            return;
+        }
+        fRenderTargetContext->addDrawOp(GrFixedClip(), std::move(op));
     }
-    fRenderTargetContext->getRTOpList()->addOp(std::move(op), *fRenderTargetContext->caps());
+    else {
+        std::unique_ptr<GrOp> op(GrClearOp::Make(fRenderTargetContext->fContext, rtRect,
+                                                 color, !clearRect));
+        if (!op) {
+            return;
+        }
+        fRenderTargetContext->getRTOpList()->addOp(std::move(op), *fRenderTargetContext->caps());
+    }
 }
 
 void GrRenderTargetContextPriv::clear(const GrFixedClip& clip,
@@ -329,11 +343,26 @@
     if (isFull) {
         this->getRTOpList()->fullClear(fContext, color);
     } else {
-        std::unique_ptr<GrOp> op(GrClearOp::Make(fContext, clip, color, this->asSurfaceProxy()));
-        if (!op) {
-            return;
+        if (this->caps()->performPartialClearsAsDraws()) {
+            GrPaint paint;
+            paint.setColor4f(GrColor4f::FromGrColor(color));
+            SkRect scissor = SkRect::Make(clip.scissorRect());
+            std::unique_ptr<GrDrawOp> op(GrRectOpFactory::MakeNonAAFill(fContext, std::move(paint),
+                                                                        SkMatrix::I(), scissor,
+                                                                        GrAAType::kNone));
+            if (!op) {
+                return;
+            }
+            this->addDrawOp(clip, std::move(op));
         }
-        this->getRTOpList()->addOp(std::move(op), *this->caps());
+        else {
+            std::unique_ptr<GrOp> op(GrClearOp::Make(fContext, clip, color,
+                                                     this->asSurfaceProxy()));
+            if (!op) {
+                return;
+            }
+            this->getRTOpList()->addOp(std::move(op), *this->caps());
+        }
     }
 }
 
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index 8215df7..41f4a67 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -118,6 +118,7 @@
 static GrGpuRTCommandBuffer* create_command_buffer(GrGpu* gpu,
                                                    GrRenderTarget* rt,
                                                    GrSurfaceOrigin origin,
+                                                   const SkRect& bounds,
                                                    GrLoadOp colorLoadOp,
                                                    GrColor loadClearColor,
                                                    GrLoadOp stencilLoadOp) {
@@ -137,7 +138,7 @@
         GrStoreOp::kStore,
     };
 
-    return gpu->getCommandBuffer(rt, origin, kColorLoadStoreInfo, stencilLoadAndStoreInfo);
+    return gpu->getCommandBuffer(rt, origin, bounds, kColorLoadStoreInfo, stencilLoadAndStoreInfo);
 }
 
 // TODO: this is where GrOp::renderTarget is used (which is fine since it
@@ -164,6 +165,7 @@
                                                     flushState->gpu(),
                                                     fTarget.get()->peekRenderTarget(),
                                                     fTarget.get()->origin(),
+                                                    fTarget.get()->getBoundsRect(),
                                                     fColorLoadOp,
                                                     fLoadClearColor,
                                                     fStencilLoadOp);
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index d5a8d42..938ead1 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -2174,7 +2174,7 @@
 }
 
 GrGpuRTCommandBuffer* GrGLGpu::getCommandBuffer(
-        GrRenderTarget* rt, GrSurfaceOrigin origin,
+        GrRenderTarget* rt, GrSurfaceOrigin origin, const SkRect& bounds,
         const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo,
         const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo) {
     if (!fCachedRTCommandBuffer) {
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 4fa1752..87f452f 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -121,7 +121,7 @@
     void clearStencil(GrRenderTarget*, int clearValue);
 
     GrGpuRTCommandBuffer* getCommandBuffer(
-            GrRenderTarget*, GrSurfaceOrigin,
+            GrRenderTarget*, GrSurfaceOrigin, const SkRect&,
             const GrGpuRTCommandBuffer::LoadAndStoreInfo&,
             const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo&) override;
 
diff --git a/src/gpu/mock/GrMockGpu.cpp b/src/gpu/mock/GrMockGpu.cpp
index 8a8aee6..a7a83e3 100644
--- a/src/gpu/mock/GrMockGpu.cpp
+++ b/src/gpu/mock/GrMockGpu.cpp
@@ -48,7 +48,7 @@
 }
 
 GrGpuRTCommandBuffer* GrMockGpu::getCommandBuffer(
-                                GrRenderTarget* rt, GrSurfaceOrigin origin,
+                                GrRenderTarget* rt, GrSurfaceOrigin origin, const SkRect& bounds,
                                 const GrGpuRTCommandBuffer::LoadAndStoreInfo&,
                                 const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo&) {
     return new GrMockGpuRTCommandBuffer(this, rt, origin);
diff --git a/src/gpu/mock/GrMockGpu.h b/src/gpu/mock/GrMockGpu.h
index 8155e62..64b267b 100644
--- a/src/gpu/mock/GrMockGpu.h
+++ b/src/gpu/mock/GrMockGpu.h
@@ -25,7 +25,7 @@
     ~GrMockGpu() override {}
 
     GrGpuRTCommandBuffer* getCommandBuffer(
-                                    GrRenderTarget*, GrSurfaceOrigin,
+                                    GrRenderTarget*, GrSurfaceOrigin, const SkRect&,
                                     const GrGpuRTCommandBuffer::LoadAndStoreInfo&,
                                     const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo&) override;
 
diff --git a/src/gpu/mtl/GrMtlCaps.h b/src/gpu/mtl/GrMtlCaps.h
index c10f656..e72ea83 100644
--- a/src/gpu/mtl/GrMtlCaps.h
+++ b/src/gpu/mtl/GrMtlCaps.h
@@ -80,6 +80,10 @@
         return false;
     }
 
+    bool performPartialClearsAsDraws() const override {
+        return true;
+    }
+
 private:
     void initFeatureSet(MTLFeatureSet featureSet);
 
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index 5c5d257..28036bf 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -88,7 +88,7 @@
                        bool canDiscardOutsideDstRect) override;
 
     GrGpuRTCommandBuffer* getCommandBuffer(
-                                    GrRenderTarget*, GrSurfaceOrigin,
+                                    GrRenderTarget*, GrSurfaceOrigin, const SkRect& bounds,
                                     const GrGpuRTCommandBuffer::LoadAndStoreInfo&,
                                     const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo&) override;
 
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index 6624eef..37e7608 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -107,10 +107,10 @@
 }
 
 GrGpuRTCommandBuffer* GrMtlGpu::getCommandBuffer(
-            GrRenderTarget* renderTarget, GrSurfaceOrigin origin,
+            GrRenderTarget* renderTarget, GrSurfaceOrigin origin, const SkRect& bounds,
             const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo,
             const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo) {
-    return new GrMtlGpuRTCommandBuffer(this, renderTarget, origin, colorInfo, stencilInfo);
+    return new GrMtlGpuRTCommandBuffer(this, renderTarget, origin, bounds, colorInfo, stencilInfo);
 }
 
 GrGpuTextureCommandBuffer* GrMtlGpu::getCommandBuffer(GrTexture* texture,
diff --git a/src/gpu/mtl/GrMtlGpuCommandBuffer.h b/src/gpu/mtl/GrMtlGpuCommandBuffer.h
index 83efb07..db63860 100644
--- a/src/gpu/mtl/GrMtlGpuCommandBuffer.h
+++ b/src/gpu/mtl/GrMtlGpuCommandBuffer.h
@@ -43,6 +43,7 @@
 class GrMtlGpuRTCommandBuffer : public GrGpuRTCommandBuffer, private GrMesh::SendToGpuImpl {
 public:
     GrMtlGpuRTCommandBuffer(GrMtlGpu* gpu, GrRenderTarget* rt, GrSurfaceOrigin origin,
+                            const SkRect& bounds,
                             const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo,
                             const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo);
 
@@ -83,7 +84,7 @@
                 int meshCount,
                 const SkRect& bounds) override;
 
-    void onClear(const GrFixedClip& clip, GrColor color) override {}
+    void onClear(const GrFixedClip& clip, GrColor color) override;
 
     void onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) override;
 
@@ -119,6 +120,8 @@
                                        int baseInstance, GrPrimitiveRestart) final;
 
     GrMtlGpu*                                     fGpu;
+    // GrRenderTargetProxy bounds
+    SkRect                                        fBounds;
     GrGpuRTCommandBuffer::LoadAndStoreInfo        fColorLoadAndStoreInfo;
     GrGpuRTCommandBuffer::StencilLoadAndStoreInfo fStencilLoadAndStoreInfo;
 
diff --git a/src/gpu/mtl/GrMtlGpuCommandBuffer.mm b/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
index 2b3b020..9864690 100644
--- a/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
+++ b/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
@@ -15,11 +15,12 @@
 #include "GrRenderTargetPriv.h"
 
 GrMtlGpuRTCommandBuffer::GrMtlGpuRTCommandBuffer(
-        GrMtlGpu* gpu, GrRenderTarget* rt, GrSurfaceOrigin origin,
+        GrMtlGpu* gpu, GrRenderTarget* rt, GrSurfaceOrigin origin, const SkRect& bounds,
         const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo,
         const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo)
         : INHERITED(rt, origin)
         , fGpu(gpu)
+        , fBounds(bounds)
         , fColorLoadAndStoreInfo(colorInfo)
         , fStencilLoadAndStoreInfo(stencilInfo)
         , fRenderPassDesc(this->createRenderPassDesc()) {
@@ -178,6 +179,19 @@
     fCommandBufferInfo.fBounds.join(bounds);
 }
 
+void GrMtlGpuRTCommandBuffer::onClear(const GrFixedClip& clip, GrColor color) {
+    // if we end up here from absClear, the clear bounds may be bigger than the RT proxy bounds -
+    // but in that case, scissor should be enabled, so this check should still succeed
+    SkASSERT(!clip.scissorEnabled() || clip.scissorRect().contains(fBounds));
+    const auto& clear = GrColor4f::FromGrColor(color).fRGBA;
+    fRenderPassDesc.colorAttachments[0].clearColor = MTLClearColorMake(clear[0], clear[1], clear[2],
+                                                                       clear[3]);
+    fRenderPassDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
+    this->internalBegin();
+    this->internalEnd();
+    fRenderPassDesc.colorAttachments[0].loadAction = MTLLoadActionLoad;
+}
+
 void GrMtlGpuRTCommandBuffer::onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
     SkASSERT(!clip.hasWindowRectangles());
 
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index d002955..2e6d97d 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -281,7 +281,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 GrGpuRTCommandBuffer* GrVkGpu::getCommandBuffer(
-            GrRenderTarget* rt, GrSurfaceOrigin origin,
+            GrRenderTarget* rt, GrSurfaceOrigin origin, const SkRect& bounds,
             const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo,
             const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo) {
     if (!fCachedRTCommandBuffer) {
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 80645a3..3b9a2e5 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -92,7 +92,7 @@
                                                                 int height) override;
 
     GrGpuRTCommandBuffer* getCommandBuffer(
-            GrRenderTarget*, GrSurfaceOrigin,
+            GrRenderTarget*, GrSurfaceOrigin, const SkRect&,
             const GrGpuRTCommandBuffer::LoadAndStoreInfo&,
             const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo&) override;