Make stencilRect() in GrRTCPriv more flexible.

All GrFillRectOps in core code go through a GrRTC call.

Also adds a utility constructor for non-AA rects since this simplifies clears
and tests/GMs that called GrFillRectOp directly. This helps preserve
readability without forcing them to go through the full rect drawing pipeline.

Change-Id: I6694c1aded3b89d773a897c483f7f2a51b06e6e8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/215089
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index 1b46553..d71d0c6 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -690,11 +690,14 @@
         case SkClipStack::Element::DeviceSpaceType::kEmpty:
             SkDEBUGFAIL("Should never get here with an empty element.");
             break;
-        case SkClipStack::Element::DeviceSpaceType::kRect:
-            return rtc->priv().drawAndStencilRect(clip, ss, (SkRegion::Op)element->getOp(),
-                                                  element->isInverseFilled(), aa, viewMatrix,
-                                                  element->getDeviceSpaceRect());
-            break;
+        case SkClipStack::Element::DeviceSpaceType::kRect: {
+            GrPaint paint;
+            paint.setCoverageSetOpXPFactory((SkRegion::Op)element->getOp(),
+                                            element->isInverseFilled());
+            rtc->priv().stencilRect(clip, ss, std::move(paint), aa, viewMatrix,
+                                    element->getDeviceSpaceRect());
+            return true;
+        }
         default: {
             SkPath path;
             element->asDeviceSpacePath(&path);
@@ -704,13 +707,22 @@
 
             return rtc->priv().drawAndStencilPath(clip, ss, (SkRegion::Op)element->getOp(),
                                                   element->isInverseFilled(), aa, viewMatrix, path);
-            break;
         }
     }
 
     return false;
 }
 
+static void stencil_device_rect(GrRenderTargetContext* rtc,
+                                const GrHardClip& clip,
+                                const GrUserStencilSettings* ss,
+                                GrAA aa,
+                                const SkRect& rect) {
+    GrPaint paint;
+    paint.setXPFactory(GrDisableColorXPFactory::Get());
+    rtc->priv().stencilRect(clip, ss, std::move(paint), aa, SkMatrix::I(), rect);
+}
+
 static void draw_element(GrRenderTargetContext* rtc,
                          const GrClip& clip,  // TODO: can this just always be WideOpen?
                          GrPaint&& paint,
@@ -790,10 +802,11 @@
                      GrUserStencilOp::kZero,
                      0xffff>()
             );
-            if (!rtc->priv().drawAndStencilRect(clip, &kDrawOutsideElement, op, !invert, GrAA::kNo,
-                                                translate, SkRect::Make(fScissor))) {
-                return false;
-            }
+
+            GrPaint paint;
+            paint.setCoverageSetOpXPFactory(op, !invert);
+            rtc->priv().stencilRect(clip, &kDrawOutsideElement, std::move(paint), GrAA::kNo,
+                                    translate, SkRect::Make(fScissor));
         } else {
             // all the remaining ops can just be directly draw into the accumulation buffer
             GrPaint paint;
@@ -891,9 +904,8 @@
                      0xffff>()
             );
             if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
-                renderTargetContext->priv().stencilRect(
-                        stencilClip.fixedClip(), &kDrawToStencil, GrAA(doStencilMSAA),
-                        SkMatrix::I(), element->getDeviceSpaceRect());
+                stencil_device_rect(renderTargetContext, stencilClip.fixedClip(), &kDrawToStencil,
+                                    GrAA(doStencilMSAA), element->getDeviceSpaceRect());
             } else {
                 if (!clipPath.isEmpty()) {
                     GrShape shape(clipPath, GrStyle::SimpleFill());
@@ -932,9 +944,8 @@
         for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {
             if (drawDirectToClip) {
                 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
-                    renderTargetContext->priv().stencilRect(
-                            stencilClip, *pass, GrAA(doStencilMSAA), SkMatrix::I(),
-                            element->getDeviceSpaceRect());
+                    stencil_device_rect(renderTargetContext, stencilClip, *pass,
+                                        GrAA(doStencilMSAA), element->getDeviceSpaceRect());
                 } else {
                     GrShape shape(clipPath, GrStyle::SimpleFill());
                     GrPaint paint;
@@ -954,9 +965,8 @@
             } else {
                 // The view matrix is setup to do clip space -> stencil space translation, so
                 // draw rect in clip space.
-                renderTargetContext->priv().stencilRect(
-                        stencilClip, *pass, GrAA(doStencilMSAA), SkMatrix::I(),
-                        SkRect::Make(fScissor));
+                stencil_device_rect(renderTargetContext, stencilClip, *pass, GrAA(doStencilMSAA),
+                                    SkRect::Make(fScissor));
             }
         }
     }
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index a64c5f8..c936949 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -347,9 +347,8 @@
             GrPaint paint;
             clear_to_grpaint(color, &paint);
             this->addDrawOp(GrFixedClip::Disabled(),
-                            GrFillRectOp::MakeGeneric(fContext, std::move(paint),
-                                                      GrAAType::kNone, GrQuadAAFlags::kNone,
-                                                      GrPerspQuad(rtRect), GrPerspQuad(rtRect)));
+                            GrFillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(),
+                                                        rtRect));
         } else {
             this->getRTOpList()->addOp(GrClearOp::Make(fContext, SkIRect::MakeEmpty(), color,
                                                        /* fullscreen */ true), *this->caps());
@@ -357,14 +356,12 @@
     } else {
         if (this->caps()->performPartialClearsAsDraws()) {
             // performPartialClearsAsDraws() also returns true if any clear has to be a draw.
-            SkRect scissor = SkRect::Make(clip.scissorRect());
             GrPaint paint;
             clear_to_grpaint(color, &paint);
 
             this->addDrawOp(clip,
-                            GrFillRectOp::MakeGeneric(fContext, std::move(paint),
-                                                      GrAAType::kNone, GrQuadAAFlags::kNone,
-                                                      GrPerspQuad(scissor), GrPerspQuad(scissor)));
+                            GrFillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(),
+                                                        SkRect::Make(clip.scissorRect())));
         } else {
             std::unique_ptr<GrOp> op(GrClearOp::Make(fContext, clip, color,
                                                      this->asSurfaceProxy()));
@@ -408,14 +405,13 @@
         if (fRenderTargetContext->caps()->performPartialClearsAsDraws()) {
             GrPaint paint;
             clear_to_grpaint(color, &paint);
-            GrPerspQuad clearQuad(SkRect::Make(rtRect));
+
             // Use the disabled clip; the rect geometry already matches the clear rectangle and
             // if it were added to a scissor, that would be intersected with the logical surface
             // bounds and not the worst case dimensions required here.
             fRenderTargetContext->addDrawOp(GrFixedClip::Disabled(),
-                    GrFillRectOp::MakeGeneric(fRenderTargetContext->fContext, std::move(paint),
-                                              GrAAType::kNone, GrQuadAAFlags::kNone,
-                                              clearQuad, clearQuad));
+                    GrFillRectOp::MakeNonAARect(fRenderTargetContext->fContext, std::move(paint),
+                                                SkMatrix::I(), SkRect::Make(rtRect)));
         } else {
             // Must use the ClearOp factory that takes a boolean (false) instead of a surface
             // proxy. The surface proxy variant would intersect the clip rect with its logical
@@ -435,11 +431,9 @@
             // width and height like in internalClear().
             GrPaint paint;
             clear_to_grpaint(color, &paint);
-            GrPerspQuad clearQuad(SkRect::Make(rtRect));
             fRenderTargetContext->addDrawOp(GrFixedClip::Disabled(),
-                    GrFillRectOp::MakeGeneric(fRenderTargetContext->fContext, std::move(paint),
-                                              GrAAType::kNone, GrQuadAAFlags::kNone,
-                                              clearQuad, clearQuad));
+                    GrFillRectOp::MakeNonAARect(fRenderTargetContext->fContext, std::move(paint),
+                                                SkMatrix::I(), SkRect::Make(rtRect)));
         } else {
             // Nothing special about this path in absClear compared to internalClear()
             fRenderTargetContext->getRTOpList()->addOp(
@@ -840,9 +834,8 @@
         // Mark stencil usage here before addDrawOp() so that it doesn't try to re-call
         // internalStencilClear() just because the op has stencil settings.
         this->setNeedsStencil();
-        GrPerspQuad clearRect(rtRect);
-        this->addDrawOp(clip, GrFillRectOp::MakeGeneric(fContext, std::move(paint), GrAAType::kNone,
-                GrQuadAAFlags::kNone, clearRect, clearRect, ss));
+        this->addDrawOp(clip, GrFillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(),
+                                                          rtRect, ss));
     } else {
         std::unique_ptr<GrOp> op(GrClearStencilClipOp::Make(fContext, clip, insideStencilMask,
                                                             fRenderTargetProxy.get()));
@@ -894,11 +887,13 @@
     fRenderTargetContext->getRTOpList()->addOp(std::move(op), *fRenderTargetContext->caps());
 }
 
-void GrRenderTargetContextPriv::stencilRect(const GrHardClip& clip,
+void GrRenderTargetContextPriv::stencilRect(const GrClip& clip,
                                             const GrUserStencilSettings* ss,
+                                            GrPaint&& paint,
                                             GrAA doStencilMSAA,
                                             const SkMatrix& viewMatrix,
-                                            const SkRect& rect) {
+                                            const SkRect& rect,
+                                            const SkMatrix* localMatrix) {
     ASSERT_SINGLE_OWNER_PRIV
     RETURN_IF_ABANDONED_PRIV
     SkDEBUGCODE(fRenderTargetContext->validate();)
@@ -907,38 +902,16 @@
 
     AutoCheckFlush acf(fRenderTargetContext->drawingManager());
 
-    GrPaint paint;
-    paint.setXPFactory(GrDisableColorXPFactory::Get());
     auto aaType = (GrAA::kYes == doStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
+
+    GrPerspQuad localQuad = localMatrix ? GrPerspQuad::MakeFromRect(rect, *localMatrix)
+                                        : GrPerspQuad(rect);
     std::unique_ptr<GrDrawOp> op = GrFillRectOp::MakeGeneric(
             fRenderTargetContext->fContext, std::move(paint), aaType, GrQuadAAFlags::kNone,
-            GrPerspQuad::MakeFromRect(rect, viewMatrix), GrPerspQuad(rect), ss);
+            GrPerspQuad::MakeFromRect(rect, viewMatrix), localQuad, ss);
     fRenderTargetContext->addDrawOp(clip, std::move(op));
 }
 
-bool GrRenderTargetContextPriv::drawAndStencilRect(const GrHardClip& clip,
-                                                   const GrUserStencilSettings* ss,
-                                                   SkRegion::Op op,
-                                                   bool invert,
-                                                   GrAA aa,
-                                                   const SkMatrix& viewMatrix,
-                                                   const SkRect& rect) {
-    ASSERT_SINGLE_OWNER_PRIV
-    RETURN_FALSE_IF_ABANDONED_PRIV
-    SkDEBUGCODE(fRenderTargetContext->validate();)
-    GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContextPriv", "drawAndStencilRect",
-                                   fRenderTargetContext->fContext);
-
-    AutoCheckFlush acf(fRenderTargetContext->drawingManager());
-
-    GrPaint paint;
-    paint.setCoverageSetOpXPFactory(op, invert);
-
-    // This will always succeed to draw a rectangle
-    fRenderTargetContext->drawFilledRect(clip, std::move(paint), aa, viewMatrix, rect, ss);
-    return true;
-}
-
 void GrRenderTargetContext::fillRectWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa,
                                                GrQuadAAFlags edgeAA, const SkMatrix& viewMatrix,
                                                const SkRect& rect, const SkRect* localRect) {
@@ -2293,9 +2266,11 @@
                                    fRenderTargetContext->fContext);
 
     if (path.isEmpty() && path.isInverseFillType()) {
-        this->drawAndStencilRect(clip, ss, op, invert, GrAA::kNo, SkMatrix::I(),
-                                 SkRect::MakeIWH(fRenderTargetContext->width(),
-                                                 fRenderTargetContext->height()));
+        GrPaint paint;
+        paint.setCoverageSetOpXPFactory(op, invert);
+        this->stencilRect(clip, ss, std::move(paint), GrAA::kNo, SkMatrix::I(),
+                          SkRect::MakeIWH(fRenderTargetContext->width(),
+                                          fRenderTargetContext->height()));
         return true;
     }
 
diff --git a/src/gpu/GrRenderTargetContextPriv.h b/src/gpu/GrRenderTargetContextPriv.h
index 95da3de..fa1d9fd 100644
--- a/src/gpu/GrRenderTargetContextPriv.h
+++ b/src/gpu/GrRenderTargetContextPriv.h
@@ -62,26 +62,17 @@
      */
     void absClear(const SkIRect* rect, const SkPMColor4f& color);
 
+    // While this can take a general clip, since GrReducedClip relies on this function, it must take
+    // care to only provide hard clips or we could get stuck in a loop. The general clip is needed
+    // so that path renderers can use this function.
     void stencilRect(
-            const GrHardClip&, const GrUserStencilSettings* ss, GrAA doStencilMSAA,
-            const SkMatrix& viewMatrix, const SkRect& rect);
+            const GrClip&, const GrUserStencilSettings* ss, GrPaint&& paint, GrAA doStencilMSAA,
+            const SkMatrix& viewMatrix, const SkRect& rect, const SkMatrix* localMatrix = nullptr);
 
     void stencilPath(
             const GrHardClip&, GrAA doStencilMSAA, const SkMatrix& viewMatrix, const GrPath*);
 
     /**
-     * Draws a rect, either AA or not, and touches the stencil buffer with the user stencil settings
-     * for each color sample written.
-     */
-    bool drawAndStencilRect(const GrHardClip&,
-                            const GrUserStencilSettings*,
-                            SkRegion::Op op,
-                            bool invert,
-                            GrAA doStencilMSAA,
-                            const SkMatrix& viewMatrix,
-                            const SkRect&);
-
-    /**
      * Draws a path, either AA or not, and touches the stencil buffer with the user stencil settings
      * for each color sample written.
      */
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index 8ee9b6f..68c7f48 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -19,12 +19,12 @@
 #include "src/gpu/GrOpFlushState.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrRecordingContextPriv.h"
+#include "src/gpu/GrRenderTargetContextPriv.h"
 #include "src/gpu/GrSWMaskHelper.h"
 #include "src/gpu/GrShape.h"
 #include "src/gpu/GrSoftwarePathRenderer.h"
 #include "src/gpu/GrSurfaceContextPriv.h"
 #include "src/gpu/ops/GrDrawOp.h"
-#include "src/gpu/ops/GrFillRectOp.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 GrPathRenderer::CanDrawPath
@@ -100,11 +100,8 @@
                                            const SkMatrix& viewMatrix,
                                            const SkRect& rect,
                                            const SkMatrix& localMatrix) {
-    auto context = renderTargetContext->surfPriv().getContext();
-    renderTargetContext->addDrawOp(clip,
-                                   GrFillRectOp::MakeWithLocalMatrix(
-                                           context, std::move(paint), GrAAType::kNone, viewMatrix,
-                                           localMatrix, rect, &userStencilSettings));
+    renderTargetContext->priv().stencilRect(clip, &userStencilSettings, std::move(paint), GrAA::kNo,
+                                            viewMatrix, rect, &localMatrix);
 }
 
 void GrSoftwarePathRenderer::DrawAroundInvPath(GrRenderTargetContext* renderTargetContext,
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index a314ae7..20e28fd 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -19,10 +19,10 @@
 #include "src/gpu/GrMesh.h"
 #include "src/gpu/GrOpFlushState.h"
 #include "src/gpu/GrPathUtils.h"
+#include "src/gpu/GrRenderTargetContextPriv.h"
 #include "src/gpu/GrShape.h"
 #include "src/gpu/GrStyle.h"
 #include "src/gpu/GrSurfaceContextPriv.h"
-#include "src/gpu/ops/GrFillRectOp.h"
 #include "src/gpu/ops/GrMeshDrawOp.h"
 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
 
@@ -613,10 +613,8 @@
                                                                                viewMatrix;
             // This is a non-coverage aa rect op since we assert aaType != kCoverage at the start
             assert_alive(paint);
-            renderTargetContext->addDrawOp(
-                    clip,
-                    GrFillRectOp::MakeWithLocalMatrix(context, std::move(paint), aaType, viewM,
-                                                      localMatrix, bounds, passes[p]));
+            renderTargetContext->priv().stencilRect(clip, passes[p], std::move(paint),
+                    GrAA(aaType == GrAAType::kMSAA), viewM, bounds, &localMatrix);
         } else {
             bool stencilPass = stencilOnly || passCount > 1;
             std::unique_ptr<GrDrawOp> op;
diff --git a/src/gpu/ops/GrFillRectOp.cpp b/src/gpu/ops/GrFillRectOp.cpp
index 9cdf3f0..50dd1c0 100644
--- a/src/gpu/ops/GrFillRectOp.cpp
+++ b/src/gpu/ops/GrFillRectOp.cpp
@@ -356,6 +356,15 @@
                             deviceQuad, localQuad);
 }
 
+std::unique_ptr<GrDrawOp> MakeNonAARect(GrRecordingContext* context,
+                                        GrPaint&& paint,
+                                        const SkMatrix& view,
+                                        const SkRect& rect,
+                                        const GrUserStencilSettings* stencil) {
+    return MakeGeneric(context, std::move(paint), GrAAType::kNone, GrQuadAAFlags::kNone,
+                       GrPerspQuad::MakeFromRect(rect, view), GrPerspQuad(rect), stencil);
+}
+
 std::unique_ptr<GrDrawOp> MakePerEdge(GrRecordingContext* context,
                                       GrPaint&& paint,
                                       GrAAType aaType,
diff --git a/src/gpu/ops/GrFillRectOp.h b/src/gpu/ops/GrFillRectOp.h
index 824fd8b..d472d6b 100644
--- a/src/gpu/ops/GrFillRectOp.h
+++ b/src/gpu/ops/GrFillRectOp.h
@@ -36,6 +36,14 @@
                                       const GrPerspQuad& localQuad,
                                       const GrUserStencilSettings* stencil = nullptr);
 
+// Utility function to create a non-AA rect transformed by view. This is used commonly enough in
+// testing and GMs that manage ops without going through GrRTC that it's worth the convenience.
+std::unique_ptr<GrDrawOp> MakeNonAARect(GrRecordingContext* context,
+                                        GrPaint&& paint,
+                                        const SkMatrix& view,
+                                        const SkRect& rect,
+                                        const GrUserStencilSettings* stencil = nullptr);
+
 // FIXME(michaelludwig) - To be removed after usages replaced with MakeGeneric
 // General purpose factory functions that handle per-edge anti-aliasing
 std::unique_ptr<GrDrawOp> MakePerEdge(GrRecordingContext* context,
diff --git a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
index b7f78c5..36995b8 100644
--- a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
+++ b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
@@ -16,7 +16,6 @@
 #include "src/gpu/GrStencilClip.h"
 #include "src/gpu/GrStyle.h"
 #include "src/gpu/ops/GrDrawPathOp.h"
-#include "src/gpu/ops/GrFillRectOp.h"
 #include "src/gpu/ops/GrStencilAndCoverPathRenderer.h"
 #include "src/gpu/ops/GrStencilPathOp.h"
 
@@ -157,18 +156,13 @@
 
             // We have to suppress enabling MSAA for mixed samples or we will get seams due to
             // coverage modulation along the edge where two triangles making up the rect meet.
-            GrAAType coverAAType = GrAAType::kNone;
+            GrAA doStencilMSAA = GrAA::kNo;
             if (AATypeFlags::kMSAA & args.fAATypeFlags) {
                 SkASSERT(!(AATypeFlags::kMixedSampledStencilThenCover & args.fAATypeFlags));
-                coverAAType = GrAAType::kMSAA;
+                doStencilMSAA = GrAA::kYes;
             }
-            // This is a non-coverage aa rect operation
-            SkASSERT(coverAAType == GrAAType::kNone || coverAAType == GrAAType::kMSAA);
-            std::unique_ptr<GrDrawOp> op = GrFillRectOp::MakeWithLocalMatrix(
-                    args.fContext, std::move(args.fPaint), coverAAType, coverMatrix, localMatrix,
-                    coverBounds, &kInvertedCoverPass);
-
-            args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
+            args.fRenderTargetContext->priv().stencilRect(*args.fClip, &kInvertedCoverPass,
+                    std::move(args.fPaint), doStencilMSAA, coverMatrix, coverBounds, &localMatrix);
         }
     } else {
         std::unique_ptr<GrDrawOp> op = GrDrawPathOp::Make(
diff --git a/src/gpu/ops/GrStrokeRectOp.cpp b/src/gpu/ops/GrStrokeRectOp.cpp
index 6b516f7..407c323 100644
--- a/src/gpu/ops/GrStrokeRectOp.cpp
+++ b/src/gpu/ops/GrStrokeRectOp.cpp
@@ -774,8 +774,10 @@
         if (devOutside.isEmpty()) {
             return nullptr;
         }
-        return GrFillRectOp::Make(context, std::move(paint), GrAAType::kCoverage, viewMatrix,
-                                  rects[0]);
+        return GrFillRectOp::MakeGeneric(context, std::move(paint), GrAAType::kCoverage,
+                                         GrQuadAAFlags::kAll,
+                                         GrPerspQuad::MakeFromRect(rects[0], viewMatrix),
+                                         GrPerspQuad(rects[0]));
     }
 
     return AAStrokeRectOp::Make(context, std::move(paint), viewMatrix, devOutside, devInside);