Define clear regions in terms of GrFixedClip

Updates clear APIs to take GrFixedClip instead of a rectangle. This
will allow us to use window rectangles with clears. Removes stencil
knobs from GrFixedClip.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2262473003

Review-Url: https://codereview.chromium.org/2262473003
diff --git a/include/gpu/GrDrawContext.h b/include/gpu/GrDrawContext.h
index 29c7d1c..cfdb910 100644
--- a/include/gpu/GrDrawContext.h
+++ b/include/gpu/GrDrawContext.h
@@ -25,6 +25,7 @@
 class GrDrawPathBatchBase;
 class GrDrawingManager;
 class GrDrawTarget;
+class GrFixedClip;
 class GrPaint;
 class GrPathProcessor;
 class GrPipelineBuilder;
@@ -353,6 +354,8 @@
     friend class GrStencilAndCoverPathRenderer;  // for access to drawBatch
     friend class GrTessellatingPathRenderer;     // for access to drawBatch
 
+    void internalClear(const GrFixedClip&, const GrColor, bool canIgnoreClip);
+
     bool drawFilledDRRect(const GrClip& clip,
                           const GrPaint& paint,
                           const SkMatrix& viewMatrix,
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index 8233786..23c2b42 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -489,10 +489,12 @@
     // The texture may be larger than necessary, this rect represents the part of the texture
     // we populate with a rasterization of the clip.
     SkIRect maskSpaceIBounds = SkIRect::MakeWH(reducedClip.width(), reducedClip.height());
+    GrFixedClip clip(maskSpaceIBounds);
 
     // The scratch texture that we are drawing into can be substantially larger than the mask. Only
     // clear the part that we care about.
-    dc->clear(&maskSpaceIBounds, InitialState::kAllIn == reducedClip.initialState() ? -1 : 0, true);
+    GrColor initialCoverage = InitialState::kAllIn == reducedClip.initialState() ? -1 : 0;
+    dc->drawContextPriv().clear(clip, initialCoverage, true);
 
     // Set the matrix so that rendered clip elements are transformed to mask space from clip
     // space.
@@ -509,8 +511,6 @@
         SkRegion::Op op = element->getOp();
         bool invert = element->isInverseFilled();
         if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
-            GrFixedClip clip(maskSpaceIBounds);
-
             // draw directly into the result with the stencil set to make the pixels affected
             // by the clip shape be non-zero.
             static constexpr GrUserStencilSettings kStencilInElement(
@@ -549,7 +549,7 @@
             paint.setAntiAlias(element->isAA());
             paint.setCoverageSetOpXPFactory(op, false);
 
-            draw_element(dc.get(), GrNoClip(), paint, translate, element);
+            draw_element(dc.get(), clip, paint, translate, element);
         }
     }
 
@@ -560,8 +560,37 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
-// (as opposed to canvas) coordinates
+// Create a 1-bit clip mask in the stencil buffer.
+
+class StencilClip final : public GrClip {
+public:
+    StencilClip(const SkIRect& scissorRect) : fFixedClip(scissorRect) {}
+    const GrFixedClip& fixedClip() const { return fFixedClip; }
+
+private:
+    bool quickContains(const SkRect&) const final {
+        return false;
+    }
+    void getConservativeBounds(int width, int height, SkIRect* devResult, bool* iior) const final {
+        fFixedClip.getConservativeBounds(width, height, devResult, iior);
+    }
+    bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const final {
+        return false;
+    }
+    bool apply(GrContext* context, GrDrawContext* drawContext, bool useHWAA,
+               bool hasUserStencilSettings, GrAppliedClip* out) const final {
+        if (!fFixedClip.apply(context, drawContext, useHWAA, hasUserStencilSettings, out)) {
+            return false;
+        }
+        out->addStencilClip();
+        return true;
+    }
+
+    GrFixedClip fFixedClip;
+
+    typedef GrClip INHERITED;
+};
+
 bool GrClipStackClip::CreateStencilClipMask(GrContext* context,
                                             GrDrawContext* drawContext,
                                             const GrReducedClip& reducedClip,
@@ -590,10 +619,10 @@
         // We set the current clip to the bounds so that our recursive draws are scissored to them.
         SkIRect stencilSpaceIBounds(reducedClip.ibounds());
         stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
-        GrFixedClip clip(stencilSpaceIBounds);
+        StencilClip stencilClip(stencilSpaceIBounds);
 
-        bool insideClip = InitialState::kAllIn == reducedClip.initialState();
-        drawContext->drawContextPriv().clearStencilClip(stencilSpaceIBounds, insideClip);
+        bool initialState = InitialState::kAllIn == reducedClip.initialState();
+        drawContext->drawContextPriv().clearStencilClip(stencilClip.fixedClip(), initialState);
 
         // walk through each clip element and perform its set op
         // with the existing clip.
@@ -602,8 +631,6 @@
             bool useHWAA = element->isAA() && drawContext->isStencilBufferMultisampled();
 
             bool fillInverted = false;
-            // enabled at bottom of loop
-            clip.disableStencilClip();
 
             // This will be used to determine whether the clip shape can be rendered into the
             // stencil with arbitrary stencil settings.
@@ -663,7 +690,8 @@
                          0xffff>()
                 );
                 if (Element::kRect_Type == element->getType()) {
-                    drawContext->drawContextPriv().stencilRect(clip, &kDrawToStencil, useHWAA,
+                    drawContext->drawContextPriv().stencilRect(stencilClip.fixedClip(),
+                                                               &kDrawToStencil, useHWAA,
                                                                viewMatrix, element->getRect());
                 } else {
                     if (!clipPath.isEmpty()) {
@@ -678,7 +706,7 @@
                             args.fPaint = &paint;
                             args.fUserStencilSettings = &kDrawToStencil;
                             args.fDrawContext = drawContext;
-                            args.fClip = &clip;
+                            args.fClip = &stencilClip.fixedClip();
                             args.fViewMatrix = &viewMatrix;
                             args.fShape = &shape;
                             args.fAntiAlias = false;
@@ -688,7 +716,7 @@
                             GrPathRenderer::StencilPathArgs args;
                             args.fResourceProvider = context->resourceProvider();
                             args.fDrawContext = drawContext;
-                            args.fClip = &clip;
+                            args.fClip = &stencilClip.fixedClip();
                             args.fViewMatrix = &viewMatrix;
                             args.fIsAA = element->isAA();
                             args.fShape = &shape;
@@ -698,16 +726,13 @@
                 }
             }
 
-            // Just enable stencil clip. The passes choose whether or not they will actually use it.
-            clip.enableStencilClip();
-
             // now we modify the clip bit by rendering either the clip
             // element directly or a bounding rect of the entire clip.
             for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {
                 if (drawDirectToClip) {
                     if (Element::kRect_Type == element->getType()) {
-                        drawContext->drawContextPriv().stencilRect(clip, *pass, useHWAA, viewMatrix,
-                                                                   element->getRect());
+                        drawContext->drawContextPriv().stencilRect(stencilClip, *pass, useHWAA,
+                                                                   viewMatrix, element->getRect());
                     } else {
                         GrShape shape(clipPath, GrStyle::SimpleFill());
                         GrPaint paint;
@@ -718,7 +743,7 @@
                         args.fPaint = &paint;
                         args.fUserStencilSettings = *pass;
                         args.fDrawContext = drawContext;
-                        args.fClip = &clip;
+                        args.fClip = &stencilClip;
                         args.fViewMatrix = &viewMatrix;
                         args.fShape = &shape;
                         args.fAntiAlias = false;
@@ -728,7 +753,8 @@
                 } else {
                     // The view matrix is setup to do clip space -> stencil space translation, so
                     // draw rect in clip space.
-                    drawContext->drawContextPriv().stencilRect(clip, *pass, false, viewMatrix,
+                    drawContext->drawContextPriv().stencilRect(stencilClip, *pass,
+                                                               false, viewMatrix,
                                                                SkRect::Make(reducedClip.ibounds()));
                 }
             }
diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp
index 2bb9caa..0ca07d1 100644
--- a/src/gpu/GrDrawContext.cpp
+++ b/src/gpu/GrDrawContext.cpp
@@ -196,45 +196,55 @@
     GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::clear");
 
     AutoCheckFlush acf(fDrawingManager);
+    this->internalClear(rect ? GrFixedClip(*rect) : GrFixedClip::Disabled(), color, canIgnoreRect);
+}
 
-    const SkIRect rtRect = SkIRect::MakeWH(this->width(), this->height());
-    SkIRect clippedRect;
-    bool isFull = false;
-    if (!rect ||
-        (canIgnoreRect && fContext->caps()->fullClearIsFree()) ||
-        rect->contains(rtRect)) {
-        rect = &rtRect;
-        isFull = true;
-    } else {
-        clippedRect = *rect;
-        if (!clippedRect.intersect(rtRect)) {
-            return;
-        }
-        rect = &clippedRect;
-    }
+void GrDrawContextPriv::clear(const GrFixedClip& clip,
+                              const GrColor color,
+                              bool canIgnoreClip) {
+    ASSERT_SINGLE_OWNER_PRIV
+    RETURN_IF_ABANDONED_PRIV
+    SkDEBUGCODE(fDrawContext->validate();)
+    GR_AUDIT_TRAIL_AUTO_FRAME(fDrawContext->fAuditTrail, "GrDrawContextPriv::clear");
+
+    AutoCheckFlush acf(fDrawContext->fDrawingManager);
+    fDrawContext->internalClear(clip, color, canIgnoreClip);
+}
+
+void GrDrawContext::internalClear(const GrFixedClip& clip,
+                                  const GrColor color,
+                                  bool canIgnoreClip) {
+    bool isFull = !clip.scissorEnabled() ||
+                  (canIgnoreClip && fContext->caps()->fullClearIsFree()) ||
+                  clip.scissorRect().contains(SkIRect::MakeWH(this->width(), this->height()));
 
     if (fContext->caps()->useDrawInsteadOfClear()) {
         // This works around a driver bug with clear by drawing a rect instead.
         // The driver will ignore a clear if it is the only thing rendered to a
         // target before the target is read.
-        if (rect == &rtRect) {
+        SkRect clearRect = SkRect::MakeIWH(this->width(), this->height());
+        if (isFull) {
             this->discard();
+        } else if (!clearRect.intersect(SkRect::Make(clip.scissorRect()))) {
+            return;
         }
 
         GrPaint paint;
         paint.setColor4f(GrColor4f::FromGrColor(color));
         paint.setXPFactory(GrPorterDuffXPFactory::Make(SkXfermode::kSrc_Mode));
 
-        this->drawRect(GrNoClip(), paint, SkMatrix::I(), SkRect::Make(*rect));
+        this->drawRect(clip, paint, SkMatrix::I(), clearRect);
     } else if (isFull) {
         this->getDrawTarget()->fullClear(this->accessRenderTarget(), color);
     } else {
-        sk_sp<GrBatch> batch(GrClearBatch::Make(*rect, color, this->accessRenderTarget()));
+        sk_sp<GrBatch> batch(GrClearBatch::Make(clip, color, this->accessRenderTarget()));
+        if (!batch) {
+            return;
+        }
         this->getDrawTarget()->addBatch(std::move(batch));
     }
 }
 
-
 void GrDrawContext::drawPaint(const GrClip& clip,
                               const GrPaint& origPaint,
                               const SkMatrix& viewMatrix) {
@@ -543,14 +553,14 @@
     this->internalDrawPath(clip, paint, viewMatrix, path, *style);
 }
 
-void GrDrawContextPriv::clearStencilClip(const SkIRect& rect, bool insideClip) {
+void GrDrawContextPriv::clearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
     ASSERT_SINGLE_OWNER_PRIV
     RETURN_IF_ABANDONED_PRIV
     SkDEBUGCODE(fDrawContext->validate();)
     GR_AUDIT_TRAIL_AUTO_FRAME(fDrawContext->fAuditTrail, "GrDrawContextPriv::clearStencilClip");
 
     AutoCheckFlush acf(fDrawContext->fDrawingManager);
-    fDrawContext->getDrawTarget()->clearStencilClip(rect, insideClip,
+    fDrawContext->getDrawTarget()->clearStencilClip(clip, insideStencilMask,
                                                     fDrawContext->accessRenderTarget());
 }
 
@@ -561,7 +571,7 @@
     fDrawContext->getDrawTarget()->stencilPath(fDrawContext, clip, useHWAA, viewMatrix, path);
 }
 
-void GrDrawContextPriv::stencilRect(const GrFixedClip& clip,
+void GrDrawContextPriv::stencilRect(const GrClip& clip,
                                     const GrUserStencilSettings* ss,
                                     bool useHWAA,
                                     const SkMatrix& viewMatrix,
@@ -580,7 +590,7 @@
     fDrawContext->drawNonAAFilledRect(clip, paint, viewMatrix, rect, nullptr, nullptr, ss, useHWAA);
 }
 
-bool GrDrawContextPriv::drawAndStencilRect(const GrFixedClip& clip,
+bool GrDrawContextPriv::drawAndStencilRect(const GrClip& clip,
                                            const GrUserStencilSettings* ss,
                                            SkRegion::Op op,
                                            bool invert,
@@ -1232,7 +1242,7 @@
     this->internalDrawPath(clip, paint, viewMatrix, path, style);
 }
 
-bool GrDrawContextPriv::drawAndStencilPath(const GrFixedClip& clip,
+bool GrDrawContextPriv::drawAndStencilPath(const GrClip& clip,
                                            const GrUserStencilSettings* ss,
                                            SkRegion::Op op,
                                            bool invert,
diff --git a/src/gpu/GrDrawContextPriv.h b/src/gpu/GrDrawContextPriv.h
index 31d8481..63eae12 100644
--- a/src/gpu/GrDrawContextPriv.h
+++ b/src/gpu/GrDrawContextPriv.h
@@ -25,9 +25,11 @@
         return fDrawContext->getDrawTarget()->instancedRendering();
     }
 
-    void clearStencilClip(const SkIRect& rect, bool insideClip);
+    void clear(const GrFixedClip&, const GrColor, bool canIgnoreClip);
 
-    void stencilRect(const GrFixedClip& clip,
+    void clearStencilClip(const GrFixedClip&, bool insideStencilMask);
+
+    void stencilRect(const GrClip& clip,
                      const GrUserStencilSettings* ss,
                      bool useHWAA,
                      const SkMatrix& viewMatrix,
@@ -38,7 +40,7 @@
                      const SkMatrix& viewMatrix,
                      const GrPath*);
 
-    bool drawAndStencilRect(const GrFixedClip&,
+    bool drawAndStencilRect(const GrClip&,
                             const GrUserStencilSettings*,
                             SkRegion::Op op,
                             bool invert,
@@ -46,7 +48,7 @@
                             const SkMatrix& viewMatrix,
                             const SkRect&);
 
-    bool drawAndStencilPath(const GrFixedClip&,
+    bool drawAndStencilPath(const GrClip&,
                             const GrUserStencilSettings*,
                             SkRegion::Op op,
                             bool invert,
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 45ec9ac..f568f7e 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -459,9 +459,7 @@
         fLastFullClearBatch->setColor(color);
         return;
     }
-    sk_sp<GrClearBatch> batch(GrClearBatch::Make(SkIRect::MakeWH(renderTarget->width(),
-                                                                 renderTarget->height()),
-                                                 color, renderTarget));
+    sk_sp<GrClearBatch> batch(GrClearBatch::Make(GrFixedClip::Disabled(), color, renderTarget));
     if (batch.get() == this->recordBatch(batch.get(), batch->bounds())) {
         fLastFullClearBatch = batch.get();
     }
@@ -619,8 +617,10 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void GrDrawTarget::clearStencilClip(const SkIRect& rect, bool insideClip, GrRenderTarget* rt) {
-    GrBatch* batch = new GrClearStencilClipBatch(rect, insideClip, rt);
+void GrDrawTarget::clearStencilClip(const GrFixedClip& clip,
+                                    bool insideStencilMask,
+                                    GrRenderTarget* rt) {
+    GrBatch* batch = new GrClearStencilClipBatch(clip, insideStencilMask, rt);
     this->recordBatch(batch, batch->bounds());
     batch->unref();
 }
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index dfc1489..35de239 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -215,7 +215,7 @@
     void addDependency(GrDrawTarget* dependedOn);
 
     // Used only by drawContextPriv.
-    void clearStencilClip(const SkIRect&, bool insideClip, GrRenderTarget*);
+    void clearStencilClip(const GrFixedClip&, bool insideStencilMask, GrRenderTarget*);
 
     struct RecordedBatch {
         sk_sp<GrBatch> fBatch;
diff --git a/src/gpu/GrFixedClip.cpp b/src/gpu/GrFixedClip.cpp
index c42214b..ebdd49b 100644
--- a/src/gpu/GrFixedClip.cpp
+++ b/src/gpu/GrFixedClip.cpp
@@ -10,13 +10,6 @@
 #include "GrAppliedClip.h"
 #include "GrDrawContext.h"
 
-bool GrFixedClip::quickContains(const SkRect& rect) const {
-    if (fHasStencilClip) {
-        return false;
-    }
-    return !fScissorState.enabled() || GrClip::IsInsideClip(fScissorState.rect(), rect);
-}
-
 void GrFixedClip::getConservativeBounds(int width, int height, SkIRect* devResult,
                                         bool* isIntersectionOfRects) const {
     devResult->setXYWH(0, 0, width, height);
@@ -46,9 +39,10 @@
         }
     }
 
-    if (fHasStencilClip) {
-        out->addStencilClip();
-    }
-
     return true;
 }
+
+const GrFixedClip& GrFixedClip::Disabled() {
+    static const GrFixedClip disabled = GrFixedClip();
+    return disabled;
+}
diff --git a/src/gpu/GrFixedClip.h b/src/gpu/GrFixedClip.h
index 705b2ea..8b3a9c1 100644
--- a/src/gpu/GrFixedClip.h
+++ b/src/gpu/GrFixedClip.h
@@ -12,37 +12,30 @@
 #include "GrTypesPriv.h"
 
 /**
- * GrFixedClip is a clip that can be represented by fixed-function hardware. It never modifies the
- * stencil buffer itself, but can be configured to use whatever clip is already there.
+ * GrFixedClip is a clip that gets implemented by fixed-function hardware.
  */
 class GrFixedClip final : public GrClip {
 public:
-    GrFixedClip() : fHasStencilClip(false) {}
-    GrFixedClip(const SkIRect& scissorRect)
-        : fScissorState(scissorRect)
-        , fHasStencilClip(false) {}
+    GrFixedClip() = default;
+    explicit GrFixedClip(const SkIRect& scissorRect) : fScissorState(scissorRect) {}
 
-    void reset() {
-        fScissorState.setDisabled();
-        fHasStencilClip = false;
+    const GrScissorState& scissorState() const { return fScissorState; }
+    bool scissorEnabled() const { return fScissorState.enabled(); }
+    const SkIRect& scissorRect() const { SkASSERT(scissorEnabled()); return fScissorState.rect(); }
+
+    void disableScissor() { fScissorState.setDisabled(); }
+
+    bool SK_WARN_UNUSED_RESULT intersect(const SkIRect& irect) {
+        return fScissorState.intersect(irect);
     }
 
-    void reset(const SkIRect& scissorRect) {
-        fScissorState.set(scissorRect);
-        fHasStencilClip = false;
+    bool quickContains(const SkRect& rect) const final {
+        return !fScissorState.enabled() || GrClip::IsInsideClip(fScissorState.rect(), rect);
     }
-
-    void enableStencilClip() { fHasStencilClip = true; }
-    void disableStencilClip() { fHasStencilClip = false; }
-
-    bool quickContains(const SkRect&) const final;
     void getConservativeBounds(int width, int height, SkIRect* devResult,
                                bool* isIntersectionOfRects) const final;
 
     bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const override {
-        if (fHasStencilClip) {
-            return false;
-        }
         if (fScissorState.enabled()) {
             SkRect rect = SkRect::Make(fScissorState.rect());
             if (!rect.intersects(rtBounds)) {
@@ -55,12 +48,13 @@
         return false;
     };
 
-private:
     bool apply(GrContext*, GrDrawContext*, bool useHWAA, bool hasUserStencilSettings,
                GrAppliedClip* out) const final;
 
+    static const GrFixedClip& Disabled();
+
+private:
     GrScissorState   fScissorState;
-    bool             fHasStencilClip;
 };
 
 #endif
diff --git a/src/gpu/GrGpuCommandBuffer.cpp b/src/gpu/GrGpuCommandBuffer.cpp
index af0993e..022c166 100644
--- a/src/gpu/GrGpuCommandBuffer.cpp
+++ b/src/gpu/GrGpuCommandBuffer.cpp
@@ -8,6 +8,7 @@
 #include "GrGpuCommandBuffer.h"
 
 #include "GrCaps.h"
+#include "GrFixedClip.h"
 #include "GrGpu.h"
 #include "GrPrimitiveProcessor.h"
 #include "GrRenderTarget.h"
@@ -18,17 +19,19 @@
     this->onSubmit(bounds);
 }
 
-void GrGpuCommandBuffer::clear(const SkIRect& rect, GrColor color, GrRenderTarget* renderTarget) {
-    SkASSERT(renderTarget);
-    SkASSERT(SkIRect::MakeWH(renderTarget->width(), renderTarget->height()).contains(rect));
-    this->onClear(renderTarget, rect, color);
+void GrGpuCommandBuffer::clear(const GrFixedClip& clip, GrColor color, GrRenderTarget* rt) {
+    SkASSERT(rt);
+    SkASSERT(!clip.scissorEnabled() ||
+             (SkIRect::MakeWH(rt->width(), rt->height()).contains(clip.scissorRect()) &&
+              SkIRect::MakeWH(rt->width(), rt->height()) != clip.scissorRect()));
+    this->onClear(rt, clip, color);
 }
 
-void GrGpuCommandBuffer::clearStencilClip(const SkIRect& rect,
-                                          bool insideClip,
-                                          GrRenderTarget* renderTarget) {
-    SkASSERT(renderTarget);
-    this->onClearStencilClip(renderTarget, rect, insideClip);
+void GrGpuCommandBuffer::clearStencilClip(const GrFixedClip& clip,
+                                          bool insideStencilMask,
+                                          GrRenderTarget* rt) {
+    SkASSERT(rt);
+    this->onClearStencilClip(rt, clip, insideStencilMask);
 }
 
 
diff --git a/src/gpu/GrGpuCommandBuffer.h b/src/gpu/GrGpuCommandBuffer.h
index 9abcc20..2336dc5 100644
--- a/src/gpu/GrGpuCommandBuffer.h
+++ b/src/gpu/GrGpuCommandBuffer.h
@@ -10,6 +10,7 @@
 
 #include "GrColor.h"
 
+class GrFixedClip;
 class GrGpu;
 class GrMesh;
 class GrPipeline;
@@ -65,9 +66,9 @@
     /**
     * Clear the passed in render target. Ignores the draw state and clip.
     */
-    void clear(const SkIRect& rect, GrColor color, GrRenderTarget* renderTarget);
+    void clear(const GrFixedClip&, GrColor, GrRenderTarget*);
 
-    void clearStencilClip(const SkIRect& rect, bool insideClip, GrRenderTarget* renderTarget);
+    void clearStencilClip(const GrFixedClip&, bool insideStencilMask, GrRenderTarget*);
     /**
     * Discards the contents render target. nullptr indicates that the current render target should
     * be discarded.
@@ -86,9 +87,11 @@
                         int meshCount) = 0;
 
     // overridden by backend-specific derived class to perform the clear.
-    virtual void onClear(GrRenderTarget*, const SkIRect& rect, GrColor color) = 0;
+    virtual void onClear(GrRenderTarget*, const GrFixedClip&, GrColor) = 0;
 
-    virtual void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) = 0;
+    virtual void onClearStencilClip(GrRenderTarget*,
+                                    const GrFixedClip&,
+                                    bool insideStencilMask) = 0;
 
 };
 
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index 52ecc59..37cc3f9 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -185,7 +185,7 @@
     struct StencilPathArgs {
         GrResourceProvider* fResourceProvider;
         GrDrawContext*      fDrawContext;
-        const GrFixedClip*  fClip;
+        const GrClip*       fClip;
         const SkMatrix*     fViewMatrix;
         bool                fIsAA;
         const GrShape*      fShape;
diff --git a/src/gpu/batches/GrClearBatch.h b/src/gpu/batches/GrClearBatch.h
index 8ebf85a..7d84bc3 100644
--- a/src/gpu/batches/GrClearBatch.h
+++ b/src/gpu/batches/GrClearBatch.h
@@ -10,6 +10,7 @@
 
 #include "GrBatch.h"
 #include "GrBatchFlushState.h"
+#include "GrFixedClip.h"
 #include "GrGpu.h"
 #include "GrGpuCommandBuffer.h"
 #include "GrRenderTarget.h"
@@ -18,8 +19,12 @@
 public:
     DEFINE_BATCH_CLASS_ID
 
-    static sk_sp<GrClearBatch> Make(const SkIRect& rect,  GrColor color, GrRenderTarget* rt) {
-        return sk_sp<GrClearBatch>(new GrClearBatch(rect, color, rt));
+    static sk_sp<GrClearBatch> Make(const GrFixedClip& clip, GrColor color, GrRenderTarget* rt) {
+        sk_sp<GrClearBatch> batch(new GrClearBatch(clip, color, rt));
+        if (!batch->renderTarget()) {
+            return nullptr; // The clip did not contain any pixels within the render target.
+        }
+        return batch;
     }
 
     const char* name() const override { return "Clear"; }
@@ -28,10 +33,12 @@
     GrRenderTarget* renderTarget() const override { return fRenderTarget.get(); }
 
     SkString dumpInfo() const override {
-        SkString string;
-        string.printf("Color: 0x%08x, Rect [L: %d, T: %d, R: %d, B: %d], RT: %d",
-                      fColor, fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
-                      fRenderTarget.get()->getUniqueID());
+        SkString string("Scissor [");
+        if (fClip.scissorEnabled()) {
+            const SkIRect& r = fClip.scissorRect();
+            string.appendf("L: %d, T: %d, R: %d, B: %d", r.fLeft, r.fTop, r.fRight, r.fBottom);
+        }
+        string.appendf("], Color: 0x%08x, RT: %d", fColor, fRenderTarget.get()->getUniqueID());
         string.append(INHERITED::dumpInfo());
         return string;
     }
@@ -39,12 +46,23 @@
     void setColor(GrColor color) { fColor = color; }
 
 private:
-    GrClearBatch(const SkIRect& rect,  GrColor color, GrRenderTarget* rt)
+    GrClearBatch(const GrFixedClip& clip, GrColor color, GrRenderTarget* rt)
         : INHERITED(ClassID())
-        , fRect(rect)
-        , fColor(color)
-        , fRenderTarget(rt) {
-        this->setBounds(SkRect::Make(rect), HasAABloat::kNo, IsZeroArea::kNo);
+        , fClip(clip)
+        , fColor(color) {
+        SkIRect rtRect = SkIRect::MakeWH(rt->width(), rt->height());
+        if (fClip.scissorEnabled()) {
+            // Don't let scissors extend outside the RT. This may improve batching.
+            if (!fClip.intersect(rtRect)) {
+                return;
+            }
+            if (fClip.scissorRect() == rtRect) {
+                fClip.disableScissor();
+            }
+        }
+        this->setBounds(SkRect::Make(fClip.scissorEnabled() ? fClip.scissorRect() : rtRect),
+                        HasAABloat::kNo, IsZeroArea::kNo);
+        fRenderTarget.reset(rt);
     }
 
     bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
@@ -53,24 +71,31 @@
         // same color.
         GrClearBatch* cb = t->cast<GrClearBatch>();
         SkASSERT(cb->fRenderTarget == fRenderTarget);
-        if (cb->fRect.contains(fRect)) {
-            fRect = cb->fRect;
+        if (cb->contains(this)) {
+            fClip = cb->fClip;
             this->replaceBounds(*t);
             fColor = cb->fColor;
             return true;
-        } else if (cb->fColor == fColor && fRect.contains(cb->fRect)) {
+        } else if (cb->fColor == fColor && this->contains(cb)) {
             return true;
         }
         return false;
     }
 
+    bool contains(const GrClearBatch* that) const {
+        // The constructor ensures that scissor gets disabled on any clip that fills the entire RT.
+        return !fClip.scissorEnabled() ||
+               (that->fClip.scissorEnabled() &&
+                fClip.scissorRect().contains(that->fClip.scissorRect()));
+    }
+
     void onPrepare(GrBatchFlushState*) override {}
 
     void onDraw(GrBatchFlushState* state) override {
-        state->commandBuffer()->clear(fRect, fColor, fRenderTarget.get());
+        state->commandBuffer()->clear(fClip, fColor, fRenderTarget.get());
     }
 
-    SkIRect                                                 fRect;
+    GrFixedClip                                             fClip;
     GrColor                                                 fColor;
     GrPendingIOResource<GrRenderTarget, kWrite_GrIOType>    fRenderTarget;
 
diff --git a/src/gpu/batches/GrClearStencilClipBatch.h b/src/gpu/batches/GrClearStencilClipBatch.h
index aa4d4af..d13d3dc 100644
--- a/src/gpu/batches/GrClearStencilClipBatch.h
+++ b/src/gpu/batches/GrClearStencilClipBatch.h
@@ -10,6 +10,7 @@
 
 #include "GrBatch.h"
 #include "GrBatchFlushState.h"
+#include "GrFixedClip.h"
 #include "GrGpu.h"
 #include "GrGpuCommandBuffer.h"
 #include "GrRenderTarget.h"
@@ -18,12 +19,14 @@
 public:
     DEFINE_BATCH_CLASS_ID
 
-    GrClearStencilClipBatch(const SkIRect& rect, bool insideClip, GrRenderTarget* rt)
+    GrClearStencilClipBatch(const GrFixedClip& clip, bool insideStencilMask, GrRenderTarget* rt)
         : INHERITED(ClassID())
-        , fRect(rect)
-        , fInsideClip(insideClip)
+        , fClip(clip)
+        , fInsideStencilMask(insideStencilMask)
         , fRenderTarget(rt) {
-        this->setBounds(SkRect::Make(rect), HasAABloat::kNo, IsZeroArea::kNo);
+        const SkRect& bounds = fClip.scissorEnabled() ? SkRect::Make(fClip.scissorRect())
+                                                      : SkRect::MakeIWH(rt->width(), rt->height());
+        this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
     }
 
     const char* name() const override { return "ClearStencilClip"; }
@@ -32,10 +35,12 @@
     GrRenderTarget* renderTarget() const override { return fRenderTarget.get(); }
 
     SkString dumpInfo() const override {
-        SkString string;
-        string.printf("Rect [L: %d, T: %d, R: %d, B: %d], IC: %d, RT: %d",
-                      fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, fInsideClip,
-                      fRenderTarget.get()->getUniqueID());
+        SkString string("Scissor [");
+        if (fClip.scissorEnabled()) {
+            const SkIRect& r = fClip.scissorRect();
+            string.appendf("L: %d, T: %d, R: %d, B: %d", r.fLeft, r.fTop, r.fRight, r.fBottom);
+        }
+        string.appendf("], IC: %d, RT: %d", fInsideStencilMask, fRenderTarget.get()->getUniqueID());
         string.append(INHERITED::dumpInfo());
         return string;
     }
@@ -46,11 +51,11 @@
     void onPrepare(GrBatchFlushState*) override {}
 
     void onDraw(GrBatchFlushState* state) override {
-        state->commandBuffer()->clearStencilClip(fRect, fInsideClip, fRenderTarget.get());
+        state->commandBuffer()->clearStencilClip(fClip, fInsideStencilMask, fRenderTarget.get());
     }
 
-    SkIRect                                                 fRect;
-    bool                                                    fInsideClip;
+    const GrFixedClip                                       fClip;
+    const bool                                              fInsideStencilMask;
     GrPendingIOResource<GrRenderTarget, kWrite_GrIOType>    fRenderTarget;
 
     typedef GrBatch INHERITED;
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 1370bf9..a141767 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -11,6 +11,7 @@
 #include "GrGLGpuCommandBuffer.h"
 #include "GrGLStencilAttachment.h"
 #include "GrGLTextureRenderTarget.h"
+#include "GrFixedClip.h"
 #include "GrGpuResourcePriv.h"
 #include "GrMesh.h"
 #include "GrPipeline.h"
@@ -2197,17 +2198,15 @@
     }
 }
 
-void GrGLGpu::clear(const SkIRect& rect, GrColor color, GrRenderTarget* target) {
+void GrGLGpu::clear(const GrFixedClip& clip, GrColor color, GrRenderTarget* target) {
     this->handleDirtyContext();
 
     // parent class should never let us get here with no RT
     SkASSERT(target);
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(target);
 
-    this->flushRenderTarget(glRT, &rect);
-    GrScissorState scissorState;
-    scissorState.set(rect);
-    this->flushScissor(scissorState, glRT->getViewport(), glRT->origin());
+    this->flushRenderTarget(glRT, clip.scissorEnabled() ? &clip.scissorRect() : nullptr);
+    this->flushScissor(clip.scissorState(), glRT->getViewport(), glRT->origin());
     this->disableWindowRectangles();
 
     GrGLfloat r, g, b, a;
@@ -2240,7 +2239,9 @@
     fHWStencilSettings.invalidate();
 }
 
-void GrGLGpu::clearStencilClip(const SkIRect& rect, bool insideClip, GrRenderTarget* target) {
+void GrGLGpu::clearStencilClip(const GrFixedClip& clip,
+                               bool insideStencilMask,
+                               GrRenderTarget* target) {
     SkASSERT(target);
     this->handleDirtyContext();
 
@@ -2261,7 +2262,7 @@
     static const GrGLint clipStencilMask  = ~0;
 #endif
     GrGLint value;
-    if (insideClip) {
+    if (insideStencilMask) {
         value = (1 << (stencilBitCount - 1));
     } else {
         value = 0;
@@ -2269,9 +2270,7 @@
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(target);
     this->flushRenderTarget(glRT, &SkIRect::EmptyIRect());
 
-    GrScissorState scissorState;
-    scissorState.set(rect);
-    this->flushScissor(scissorState, glRT->getViewport(), glRT->origin());
+    this->flushScissor(clip.scissorState(), glRT->getViewport(), glRT->origin());
     this->disableWindowRectangles();
 
     GL_CALL(StencilMask((uint32_t) clipStencilMask));
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 448c292..3fe4204 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -105,12 +105,12 @@
     // The GrGLGpuCommandBuffer does not buffer up draws before submitting them to the gpu.
     // Thus this is the implementation of the clear call for the corresponding passthrough function
     // on GrGLGpuCommandBuffer.
-    void clear(const SkIRect& rect, GrColor color, GrRenderTarget* renderTarget);
+    void clear(const GrFixedClip&, GrColor, GrRenderTarget*);
 
     // The GrGLGpuCommandBuffer does not buffer up draws before submitting them to the gpu.
     // Thus this is the implementation of the clearStencil call for the corresponding passthrough
     // function on GrGLGpuCommandBuffer.
-    void clearStencilClip(const SkIRect& rect, bool insideClip, GrRenderTarget* renderTarget);
+    void clearStencilClip(const GrFixedClip&, bool insideStencilMask, GrRenderTarget*);
 
     const GrGLContext* glContextForTesting() const override {
         return &this->glContext();
diff --git a/src/gpu/gl/GrGLGpuCommandBuffer.h b/src/gpu/gl/GrGLGpuCommandBuffer.h
index c5f0eea..4ad2b13 100644
--- a/src/gpu/gl/GrGLGpuCommandBuffer.h
+++ b/src/gpu/gl/GrGLGpuCommandBuffer.h
@@ -39,12 +39,14 @@
         fGpu->draw(pipeline, primProc, mesh, meshCount);
     }
 
-    void onClear(GrRenderTarget* rt, const SkIRect& rect, GrColor color) override {
-        fGpu->clear(rect, color, rt);
+    void onClear(GrRenderTarget* rt, const GrFixedClip& clip, GrColor color) override {
+        fGpu->clear(clip, color, rt);
     }
 
-    void onClearStencilClip(GrRenderTarget* rt, const SkIRect& rect, bool insideClip) override {
-        fGpu->clearStencilClip(rect, insideClip, rt);
+    void onClearStencilClip(GrRenderTarget* rt,
+                            const GrFixedClip& clip,
+                            bool insideStencilMask) override {
+        fGpu->clearStencilClip(clip, insideStencilMask, rt);
     }
 
     GrGLGpu*                    fGpu;
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index f49b180..d7a030d 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -7,6 +7,7 @@
 
 #include "GrVkGpuCommandBuffer.h"
 
+#include "GrFixedClip.h"
 #include "GrMesh.h"
 #include "GrPipeline.h"
 #include "GrRenderTargetPriv.h"
@@ -166,8 +167,8 @@
 }
 
 void GrVkGpuCommandBuffer::onClearStencilClip(GrRenderTarget* target,
-                                              const SkIRect& rect,
-                                              bool insideClip) {
+                                              const GrFixedClip& clip,
+                                              bool insideStencilMask) {
     SkASSERT(target);
 
     GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(target);
@@ -182,7 +183,7 @@
 
     VkClearDepthStencilValue vkStencilColor;
     memset(&vkStencilColor, 0, sizeof(VkClearDepthStencilValue));
-    if (insideClip) {
+    if (insideStencilMask) {
         vkStencilColor.stencil = (1 << (stencilBitCount - 1));
     } else {
         vkStencilColor.stencil = 0;
@@ -190,11 +191,15 @@
 
     VkClearRect clearRect;
     // Flip rect if necessary
-    SkIRect vkRect = rect;
-
-    if (kBottomLeft_GrSurfaceOrigin == vkRT->origin()) {
-        vkRect.fTop = vkRT->height() - rect.fBottom;
-        vkRect.fBottom = vkRT->height() - rect.fTop;
+    SkIRect vkRect;
+    if (!clip.scissorEnabled()) {
+        vkRect.setXYWH(0, 0, vkRT->width(), vkRT->height());
+    } else if (kBottomLeft_GrSurfaceOrigin != vkRT->origin()) {
+        vkRect = clip.scissorRect();
+    } else {
+        const SkIRect& scissor = clip.scissorRect();
+        vkRect.setLTRB(scissor.fLeft, vkRT->height() - scissor.fBottom,
+                       scissor.fRight, vkRT->height() - scissor.fTop);
     }
 
     clearRect.rect.offset = { vkRect.fLeft, vkRect.fTop };
@@ -215,7 +220,7 @@
     fIsEmpty = false;
 }
 
-void GrVkGpuCommandBuffer::onClear(GrRenderTarget* target, const SkIRect& rect, GrColor color) {
+void GrVkGpuCommandBuffer::onClear(GrRenderTarget* target, const GrFixedClip& clip, GrColor color) {
     // parent class should never let us get here with no RT
     SkASSERT(target);
 
@@ -224,7 +229,7 @@
 
     GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(target);
 
-    if (fIsEmpty && rect.width() == target->width() && rect.height() == target->height()) {
+    if (fIsEmpty && !clip.scissorEnabled()) {
         // We will change the render pass to do a clear load instead
         GrVkRenderPass::LoadStoreOps vkColorOps(VK_ATTACHMENT_LOAD_OP_CLEAR,
                                                 VK_ATTACHMENT_STORE_OP_STORE);
@@ -259,10 +264,15 @@
     // We always do a sub rect clear with clearAttachments since we are inside a render pass
     VkClearRect clearRect;
     // Flip rect if necessary
-    SkIRect vkRect = rect;
-    if (kBottomLeft_GrSurfaceOrigin == vkRT->origin()) {
-        vkRect.fTop = vkRT->height() - rect.fBottom;
-        vkRect.fBottom = vkRT->height() - rect.fTop;
+    SkIRect vkRect;
+    if (!clip.scissorEnabled()) {
+        vkRect.setXYWH(0, 0, vkRT->width(), vkRT->height());
+    } else if (kBottomLeft_GrSurfaceOrigin != vkRT->origin()) {
+        vkRect = clip.scissorRect();
+    } else {
+        const SkIRect& scissor = clip.scissorRect();
+        vkRect.setLTRB(scissor.fLeft, vkRT->height() - scissor.fBottom,
+                       scissor.fRight, vkRT->height() - scissor.fTop);
     }
     clearRect.rect.offset = { vkRect.fLeft, vkRect.fTop };
     clearRect.rect.extent = { (uint32_t)vkRect.width(), (uint32_t)vkRect.height() };
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.h b/src/gpu/vk/GrVkGpuCommandBuffer.h
index 8459e8e3..506d020 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.h
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.h
@@ -52,9 +52,9 @@
                 const GrMesh* mesh,
                 int meshCount) override;
 
-    void onClear(GrRenderTarget* rt, const SkIRect& rect, GrColor color) override;
+    void onClear(GrRenderTarget* rt, const GrFixedClip&, GrColor color) override;
 
-    void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) override;
+    void onClearStencilClip(GrRenderTarget*, const GrFixedClip&, bool insideStencilMask) override;
 
     const GrVkRenderPass*       fRenderPass;
     GrVkSecondaryCommandBuffer* fCommandBuffer;