Store clipped bounds on GrOp rather than in parallel in GrRenderTargetOpList.

When the op is recorded we update its bounds to incorporate both clipping and aa bloat.

Change-Id: Ia205e058ebeda5fcdb2cd5e9b8a30a4972672b14
Reviewed-on: https://skia-review.googlesource.com/9233
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/gm/beziereffects.cpp b/gm/beziereffects.cpp
index 661e770..d2d4aa9 100644
--- a/gm/beziereffects.cpp
+++ b/gm/beziereffects.cpp
@@ -34,16 +34,16 @@
 
     const char* name() const override { return "BezierCubicOrConicTestOp"; }
 
-    static std::unique_ptr<GrDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& bounds,
+    static std::unique_ptr<GrDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
                                           GrColor color, const SkScalar klmEqs[9], SkScalar sign) {
         return std::unique_ptr<GrDrawOp>(
-                new BezierCubicOrConicTestOp(gp, bounds, color, klmEqs, sign));
+                new BezierCubicOrConicTestOp(gp, rect, color, klmEqs, sign));
     }
 
 private:
-    BezierCubicOrConicTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& bounds, GrColor color,
+    BezierCubicOrConicTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color,
                              const SkScalar klmEqs[9], SkScalar sign)
-            : INHERITED(ClassID(), bounds, color), fGeometryProcessor(std::move(gp)) {
+            : INHERITED(ClassID(), rect, color), fRect(rect), fGeometryProcessor(std::move(gp)) {
         for (int i = 0; i < 9; i++) {
             fKlmEqs[i] = klmEqs[i];
         }
@@ -62,8 +62,7 @@
         if (!verts) {
             return;
         }
-        const SkRect& bounds = this->bounds();
-        verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
+        verts[0].fPosition.setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
                                       sizeof(Vertex));
         for (int v = 0; v < 4; ++v) {
             verts[v].fKLM[0] = eval_line(verts[v].fPosition, fKlmEqs + 0, fSign);
@@ -73,8 +72,9 @@
         helper.recordDraw(target, fGeometryProcessor.get());
     }
 
-    SkScalar                   fKlmEqs[9];
-    SkScalar                   fSign;
+    SkScalar fKlmEqs[9];
+    SkScalar fSign;
+    SkRect fRect;
     sk_sp<GrGeometryProcessor> fGeometryProcessor;
 
     static constexpr int kVertsPerCubic = 4;
@@ -394,16 +394,17 @@
     DEFINE_OP_CLASS_ID
     const char* name() const override { return "BezierQuadTestOp"; }
 
-    static std::unique_ptr<GrDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& bounds,
+    static std::unique_ptr<GrDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
                                           GrColor color, const GrPathUtils::QuadUVMatrix& devToUV) {
-        return std::unique_ptr<GrDrawOp>(new BezierQuadTestOp(gp, bounds, color, devToUV));
+        return std::unique_ptr<GrDrawOp>(new BezierQuadTestOp(gp, rect, color, devToUV));
     }
 
 private:
-    BezierQuadTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& bounds, GrColor color,
+    BezierQuadTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color,
                      const GrPathUtils::QuadUVMatrix& devToUV)
-            : INHERITED(ClassID(), bounds, color)
+            : INHERITED(ClassID(), rect, color)
             , fDevToUV(devToUV)
+            , fRect(rect)
             , fGeometryProcessor(std::move(gp)) {}
 
     struct Vertex {
@@ -419,14 +420,14 @@
         if (!verts) {
             return;
         }
-        const SkRect& bounds = this->bounds();
-        verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
+        verts[0].fPosition.setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
                                       sizeof(Vertex));
         fDevToUV.apply<4, sizeof(Vertex), sizeof(SkPoint)>(verts);
         helper.recordDraw(target, fGeometryProcessor.get());
     }
 
-    GrPathUtils::QuadUVMatrix  fDevToUV;
+    GrPathUtils::QuadUVMatrix fDevToUV;
+    SkRect fRect;
     sk_sp<GrGeometryProcessor> fGeometryProcessor;
 
     static constexpr int kVertsPerCubic = 4;
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index 6a7f757..2b05a16 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -84,10 +84,9 @@
             SkDebugf("%d: %s\n", i, fRecordedOps[i].fOp->name());
             SkString str = fRecordedOps[i].fOp->dumpInfo();
             SkDebugf("%s\n", str.c_str());
-            const SkRect& clippedBounds = fRecordedOps[i].fClippedBounds;
-            SkDebugf("ClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
-                     clippedBounds.fLeft, clippedBounds.fTop, clippedBounds.fRight,
-                     clippedBounds.fBottom);
+            const SkRect& bounds = fRecordedOps[i].fOp->bounds();
+            SkDebugf("ClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", bounds.fLeft,
+                     bounds.fTop, bounds.fRight, bounds.fBottom);
         }
     }
 }
@@ -97,9 +96,6 @@
                                            const GrClip& clip,
                                            const SkRect& opBounds,
                                            GrXferProcessor::DstTexture* dstTexture) {
-    SkRect bounds = opBounds;
-    bounds.outset(0.5f, 0.5f);
-
     if (this->caps()->textureBarrierSupport()) {
         if (GrTexture* rtTex = rt->asTexture()) {
             // The render target is a texture, so we can read from it directly in the shader. The XP
@@ -114,7 +110,7 @@
     clip.getConservativeBounds(rt->width(), rt->height(), &copyRect);
 
     SkIRect drawIBounds;
-    bounds.roundOut(&drawIBounds);
+    opBounds.roundOut(&drawIBounds);
     if (!copyRect.intersect(drawIBounds)) {
 #ifdef SK_DEBUG
         GrCapsDebugf(this->caps(), "Missed an early reject. "
@@ -198,7 +194,7 @@
             }
             flushState->setCommandBuffer(commandBuffer.get());
         }
-        fRecordedOps[i].fOp->execute(flushState, fRecordedOps[i].fClippedBounds);
+        fRecordedOps[i].fOp->execute(flushState);
     }
     if (commandBuffer) {
         commandBuffer->end();
@@ -304,7 +300,7 @@
     }
 
     if (pipelineBuilder.willXPNeedDstTexture(*this->caps(), analysis)) {
-        this->setupDstTexture(renderTargetContext->accessRenderTarget(), clip, op->bounds(),
+        this->setupDstTexture(renderTargetContext->accessRenderTarget(), clip, bounds,
                               &args.fDstTexture);
         if (!args.fDstTexture.texture()) {
             return;
@@ -316,7 +312,8 @@
     SkASSERT(fSurface);
     op->pipeline()->addDependenciesTo(fSurface);
 #endif
-    this->recordOp(std::move(op), renderTargetContext, appliedClip.clippedDrawBounds());
+    op->setClippedBounds(appliedClip.clippedDrawBounds());
+    this->recordOp(std::move(op), renderTargetContext);
 }
 
 void GrRenderTargetOpList::stencilPath(GrRenderTargetContext* renderTargetContext,
@@ -361,7 +358,8 @@
                                                      appliedClip.scissorState(),
                                                      renderTargetContext->accessRenderTarget(),
                                                      path);
-    this->recordOp(std::move(op), renderTargetContext, appliedClip.clippedDrawBounds());
+    op->setClippedBounds(appliedClip.clippedDrawBounds());
+    this->recordOp(std::move(op), renderTargetContext);
 }
 
 void GrRenderTargetOpList::fullClear(GrRenderTargetContext* renderTargetContext, GrColor color) {
@@ -420,18 +418,8 @@
            b.fRight <= a.fLeft || b.fBottom <= a.fTop;
 }
 
-static void join(SkRect* out, const SkRect& a, const SkRect& b) {
-    SkASSERT(a.fLeft <= a.fRight && a.fTop <= a.fBottom);
-    SkASSERT(b.fLeft <= b.fRight && b.fTop <= b.fBottom);
-    out->fLeft   = SkTMin(a.fLeft,   b.fLeft);
-    out->fTop    = SkTMin(a.fTop,    b.fTop);
-    out->fRight  = SkTMax(a.fRight,  b.fRight);
-    out->fBottom = SkTMax(a.fBottom, b.fBottom);
-}
-
 GrOp* GrRenderTargetOpList::recordOp(std::unique_ptr<GrOp> op,
-                                     GrRenderTargetContext* renderTargetContext,
-                                     const SkRect& clippedBounds) {
+                                     GrRenderTargetContext* renderTargetContext) {
     GrRenderTarget* renderTarget =
             renderTargetContext ? renderTargetContext->accessRenderTarget()
                                 : nullptr;
@@ -451,9 +439,8 @@
                op->bounds().fLeft, op->bounds().fRight,
                op->bounds().fTop, op->bounds().fBottom);
     GrOP_INFO(SkTabString(op->dumpInfo(), 1).c_str());
-    GrOP_INFO("\tClipped Bounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
-              clippedBounds.fLeft, clippedBounds.fTop, clippedBounds.fRight,
-              clippedBounds.fBottom);
+    GrOP_INFO("\tClipped Bounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", op->bounds().fLeft,
+              op->bounds().fTop, op->bounds().fRight, op->bounds().fBottom);
     GrOP_INFO("\tOutcome:\n");
     int maxCandidates = SkTMin(fMaxOpLookback, fRecordedOps.count());
     // If we don't have a valid destination render target then we cannot reorder.
@@ -473,13 +460,10 @@
                 GrOP_INFO("\t\t\tCombined op info:\n");
                 GrOP_INFO(SkTabString(candidate.fOp->dumpInfo(), 4).c_str());
                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, candidate.fOp.get(), op.get());
-                join(&fRecordedOps.fromBack(i).fClippedBounds,
-                     fRecordedOps.fromBack(i).fClippedBounds, clippedBounds);
                 return candidate.fOp.get();
             }
             // Stop going backwards if we would cause a painter's order violation.
-            const SkRect& candidateBounds = fRecordedOps.fromBack(i).fClippedBounds;
-            if (!can_reorder(candidateBounds, clippedBounds)) {
+            if (!can_reorder(fRecordedOps.fromBack(i).fOp->bounds(), op->bounds())) {
                 GrOP_INFO("\t\tIntersects with (%s, B%u)\n", candidate.fOp->name(),
                           candidate.fOp->uniqueID());
                 break;
@@ -494,7 +478,7 @@
         GrOP_INFO("\t\tFirstOp\n");
     }
     GR_AUDIT_TRAIL_OP_RESULT_NEW(fAuditTrail, op);
-    fRecordedOps.emplace_back(std::move(op), clippedBounds, renderTarget);
+    fRecordedOps.emplace_back(std::move(op), renderTarget);
     fLastFullClearOp = nullptr;
     fLastFullClearRenderTargetID.makeInvalid();
     return fRecordedOps.back().fOp.get();
@@ -511,7 +495,6 @@
         if (!renderTarget) {
             continue;
         }
-        const SkRect& opBounds = fRecordedOps[i].fClippedBounds;
         int maxCandidateIdx = SkTMin(i + fMaxOpLookahead, fRecordedOps.count() - 1);
         int j = i + 1;
         while (true) {
@@ -534,12 +517,10 @@
                           candidate.fOp->uniqueID());
                 GR_AUDIT_TRAIL_OPS_RESULT_COMBINED(fAuditTrail, op, candidate.fOp.get());
                 fRecordedOps[j].fOp = std::move(fRecordedOps[i].fOp);
-                join(&fRecordedOps[j].fClippedBounds, fRecordedOps[j].fClippedBounds, opBounds);
                 break;
             }
             // Stop going traversing if we would cause a painter's order violation.
-            const SkRect& candidateBounds = fRecordedOps[j].fClippedBounds;
-            if (!can_reorder(candidateBounds, opBounds)) {
+            if (!can_reorder(fRecordedOps[j].fOp->bounds(), op->bounds())) {
                 GrOP_INFO("\t\tIntersects with (%s, B%u)\n", candidate.fOp->name(),
                           candidate.fOp->uniqueID());
                 break;
diff --git a/src/gpu/GrRenderTargetOpList.h b/src/gpu/GrRenderTargetOpList.h
index 8ecf232..36bdc6b 100644
--- a/src/gpu/GrRenderTargetOpList.h
+++ b/src/gpu/GrRenderTargetOpList.h
@@ -132,13 +132,7 @@
 
     // If the input op is combined with an earlier op, this returns the combined op. Otherwise, it
     // returns the input op.
-    GrOp* recordOp(std::unique_ptr<GrOp> op, GrRenderTargetContext* renderTargetContext) {
-        SkRect bounds = op->bounds();
-        return this->recordOp(std::move(op), renderTargetContext, bounds);
-    }
-
-    // Variant that allows an explicit bounds (computed from the Op's bounds and a clip).
-    GrOp* recordOp(std::unique_ptr<GrOp>, GrRenderTargetContext*, const SkRect& clippedBounds);
+    GrOp* recordOp(std::unique_ptr<GrOp>, GrRenderTargetContext*);
 
     void forwardCombine();
 
@@ -154,10 +148,9 @@
     void clearStencilClip(const GrFixedClip&, bool insideStencilMask, GrRenderTargetContext*);
 
     struct RecordedOp {
-        RecordedOp(std::unique_ptr<GrOp> op, const SkRect& clippedBounds, GrRenderTarget* rt)
-                : fOp(std::move(op)), fClippedBounds(clippedBounds), fRenderTarget(rt) {}
+        RecordedOp(std::unique_ptr<GrOp> op, GrRenderTarget* rt)
+                : fOp(std::move(op)), fRenderTarget(rt) {}
         std::unique_ptr<GrOp> fOp;
-        SkRect fClippedBounds;
         // TODO: These ops will all to target the same render target and this won't be needed.
         GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> fRenderTarget;
     };
diff --git a/src/gpu/GrTextureOpList.cpp b/src/gpu/GrTextureOpList.cpp
index 61990de..d70daa2 100644
--- a/src/gpu/GrTextureOpList.cpp
+++ b/src/gpu/GrTextureOpList.cpp
@@ -65,7 +65,7 @@
     }
 
     for (int i = 0; i < fRecordedOps.count(); ++i) {
-        fRecordedOps[i]->execute(flushState, fRecordedOps[i]->bounds());
+        fRecordedOps[i]->execute(flushState);
     }
 
     fGpu->finishOpList();
diff --git a/src/gpu/instanced/InstancedRendering.cpp b/src/gpu/instanced/InstancedRendering.cpp
index f142d99..1ffaaa4 100644
--- a/src/gpu/instanced/InstancedRendering.cpp
+++ b/src/gpu/instanced/InstancedRendering.cpp
@@ -454,7 +454,7 @@
     this->onBeginFlush(rp);
 }
 
-void InstancedRendering::Op::onExecute(GrOpFlushState* state, const SkRect& bounds) {
+void InstancedRendering::Op::onExecute(GrOpFlushState* state) {
     SkASSERT(State::kFlushing == fInstancedRendering->fState);
     SkASSERT(state->gpu() == fInstancedRendering->gpu());
 
diff --git a/src/gpu/instanced/InstancedRendering.h b/src/gpu/instanced/InstancedRendering.h
index 03120d2..8002d50 100644
--- a/src/gpu/instanced/InstancedRendering.h
+++ b/src/gpu/instanced/InstancedRendering.h
@@ -160,7 +160,7 @@
         void applyPipelineOptimizations(const GrPipelineOptimizations&) override;
         bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override;
         void onPrepare(GrOpFlushState*) override {}
-        void onExecute(GrOpFlushState*, const SkRect& bounds) override;
+        void onExecute(GrOpFlushState*) override;
 
         typedef GrDrawOp INHERITED;
 
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 89329b5..b7e7ea3 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -136,22 +136,6 @@
         // now copy all vertices
         memcpy(currVertex, blobVertices, byteCount);
 
-#ifdef SK_DEBUG
-        // bounds sanity check
-        SkRect rect;
-        rect.setLargestInverted();
-        SkPoint* vertex = (SkPoint*)((char*)blobVertices);
-        rect.growToInclude(vertex, vertexStride, kVerticesPerGlyph * subRunGlyphCount);
-
-        if (this->usesDistanceFields()) {
-            args.fViewMatrix.mapRect(&rect);
-        }
-        // Allow for small numerical error in the bounds.
-        SkRect bounds = this->bounds();
-        bounds.outset(0.001f, 0.001f);
-        SkASSERT(bounds.contains(rect));
-#endif
-
         currVertex += byteCount;
     }
 
diff --git a/src/gpu/ops/GrClearOp.h b/src/gpu/ops/GrClearOp.h
index b40b615..218dcd1 100644
--- a/src/gpu/ops/GrClearOp.h
+++ b/src/gpu/ops/GrClearOp.h
@@ -109,7 +109,7 @@
 
     void onPrepare(GrOpFlushState*) override {}
 
-    void onExecute(GrOpFlushState* state, const SkRect& /*bounds*/) override {
+    void onExecute(GrOpFlushState* state) override {
         state->commandBuffer()->clear(fRenderTarget.get(), fClip, fColor);
     }
 
diff --git a/src/gpu/ops/GrClearStencilClipOp.h b/src/gpu/ops/GrClearStencilClipOp.h
index afbd72d..ef46a4c 100644
--- a/src/gpu/ops/GrClearStencilClipOp.h
+++ b/src/gpu/ops/GrClearStencilClipOp.h
@@ -53,7 +53,7 @@
 
     void onPrepare(GrOpFlushState*) override {}
 
-    void onExecute(GrOpFlushState* state, const SkRect& /*bounds*/) override {
+    void onExecute(GrOpFlushState* state) override {
         state->commandBuffer()->clearStencilClip(fRenderTarget.get(), fClip, fInsideStencilMask);
     }
 
diff --git a/src/gpu/ops/GrCopySurfaceOp.h b/src/gpu/ops/GrCopySurfaceOp.h
index e74b7e5..4c4500b 100644
--- a/src/gpu/ops/GrCopySurfaceOp.h
+++ b/src/gpu/ops/GrCopySurfaceOp.h
@@ -57,7 +57,7 @@
 
     void onPrepare(GrOpFlushState*) override {}
 
-    void onExecute(GrOpFlushState* state, const SkRect& /*bounds*/) override {
+    void onExecute(GrOpFlushState* state) override {
         if (!state->commandBuffer()) {
             state->gpu()->copySurface(fDst.get(), fSrc.get(), fSrcRect, fDstPoint);
         } else {
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index 41ec8fb..f2be968 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -264,7 +264,7 @@
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         DefaultPathOp* that = t->cast<DefaultPathOp>();
         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                     that->bounds(), caps)) {
+                                    that->bounds(), caps)) {
             return false;
         }
 
diff --git a/src/gpu/ops/GrDiscardOp.h b/src/gpu/ops/GrDiscardOp.h
index f9be33d..098df63 100644
--- a/src/gpu/ops/GrDiscardOp.h
+++ b/src/gpu/ops/GrDiscardOp.h
@@ -41,7 +41,7 @@
 
     void onPrepare(GrOpFlushState*) override {}
 
-    void onExecute(GrOpFlushState* state, const SkRect& /*bounds*/) override {
+    void onExecute(GrOpFlushState* state) override {
         state->commandBuffer()->discard(fRenderTarget.get());
     }
 
diff --git a/src/gpu/ops/GrDrawPathOp.cpp b/src/gpu/ops/GrDrawPathOp.cpp
index c2b4fdb..85fb147 100644
--- a/src/gpu/ops/GrDrawPathOp.cpp
+++ b/src/gpu/ops/GrDrawPathOp.cpp
@@ -27,7 +27,7 @@
     return string;
 }
 
-void GrDrawPathOp::onExecute(GrOpFlushState* state, const SkRect& bounds) {
+void GrDrawPathOp::onExecute(GrOpFlushState* state) {
     GrProgramDesc desc;
 
     sk_sp<GrPathProcessor> pathProc(
@@ -115,7 +115,7 @@
     return true;
 }
 
-void GrDrawPathRangeOp::onExecute(GrOpFlushState* state, const SkRect& bounds) {
+void GrDrawPathRangeOp::onExecute(GrOpFlushState* state) {
     const Draw& head = *fDraws.head();
 
     SkMatrix drawMatrix(this->viewMatrix());
diff --git a/src/gpu/ops/GrDrawPathOp.h b/src/gpu/ops/GrDrawPathOp.h
index 3e84ffe..ffe8768 100644
--- a/src/gpu/ops/GrDrawPathOp.h
+++ b/src/gpu/ops/GrDrawPathOp.h
@@ -78,7 +78,7 @@
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { return false; }
 
-    void onExecute(GrOpFlushState* state, const SkRect& bounds) override;
+    void onExecute(GrOpFlushState* state) override;
 
     GrPendingIOResource<const GrPath, kRead_GrIOType> fPath;
 
@@ -172,7 +172,7 @@
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override;
 
-    void onExecute(GrOpFlushState* state, const SkRect& bounds) override;
+    void onExecute(GrOpFlushState* state) override;
 
     struct Draw {
         void set(const InstanceData* instanceData, SkScalar x, SkScalar y) {
diff --git a/src/gpu/ops/GrMSAAPathRenderer.cpp b/src/gpu/ops/GrMSAAPathRenderer.cpp
index 78a80b1..b149b4c 100644
--- a/src/gpu/ops/GrMSAAPathRenderer.cpp
+++ b/src/gpu/ops/GrMSAAPathRenderer.cpp
@@ -452,7 +452,7 @@
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         MSAAPathOp* that = t->cast<MSAAPathOp>();
         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                     that->bounds(), caps)) {
+                                    that->bounds(), caps)) {
             return false;
         }
 
diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp
index a8dd8b1..5d83bd5 100644
--- a/src/gpu/ops/GrMeshDrawOp.cpp
+++ b/src/gpu/ops/GrMeshDrawOp.cpp
@@ -59,7 +59,7 @@
                                  quadsToDraw);
 }
 
-void GrMeshDrawOp::onExecute(GrOpFlushState* state, const SkRect& bounds) {
+void GrMeshDrawOp::onExecute(GrOpFlushState* state) {
     int currUploadIdx = 0;
     int currMeshIdx = 0;
 
@@ -73,7 +73,7 @@
         }
         const QueuedDraw& draw = fQueuedDraws[currDrawIdx];
         state->commandBuffer()->draw(*this->pipeline(), *draw.fGeometryProcessor.get(),
-                                     fMeshes.begin() + currMeshIdx, draw.fMeshCnt, bounds);
+                                     fMeshes.begin() + currMeshIdx, draw.fMeshCnt, this->bounds());
         currMeshIdx += draw.fMeshCnt;
         state->flushToken();
     }
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index 25fed02..a7d312f 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -64,7 +64,7 @@
 
 private:
     void onPrepare(GrOpFlushState* state) final;
-    void onExecute(GrOpFlushState* state, const SkRect& bounds) final;
+    void onExecute(GrOpFlushState* state) final;
 
     virtual void onPrepareDraws(Target*) const = 0;
 
diff --git a/src/gpu/ops/GrOp.cpp b/src/gpu/ops/GrOp.cpp
index 1d86419..7762a5a 100644
--- a/src/gpu/ops/GrOp.cpp
+++ b/src/gpu/ops/GrOp.cpp
@@ -56,7 +56,6 @@
     : fClassID(classID)
     , fUniqueID(kIllegalOpID) {
     SkASSERT(classID == SkToU32(fClassID));
-    SkDEBUGCODE(fUsed = false;)
     SkDEBUGCODE(fBoundsFlags = kUninitialized_BoundsFlag);
 }
 
diff --git a/src/gpu/ops/GrOp.h b/src/gpu/ops/GrOp.h
index 3a37ad5..11198dd 100644
--- a/src/gpu/ops/GrOp.h
+++ b/src/gpu/ops/GrOp.h
@@ -76,6 +76,12 @@
         return fBounds;
     }
 
+    void setClippedBounds(const SkRect& clippedBounds) {
+        fBounds = clippedBounds;
+        // The clipped bounds already incorporate any effect of the bounds flags.
+        fBoundsFlags = 0;
+    }
+
     bool hasAABloat() const {
         SkASSERT(fBoundsFlags != kUninitialized_BoundsFlag);
         return SkToBool(fBoundsFlags & kAABloat_BoundsFlag);
@@ -118,7 +124,6 @@
         }
         return fUniqueID;
     }
-    SkDEBUGCODE(bool isUsed() const { return fUsed; })
 
     /**
      * Called prior to executing. The op should perform any resource creation or data transfers
@@ -127,7 +132,7 @@
     void prepare(GrOpFlushState* state) { this->onPrepare(state); }
 
     /** Issues the op's commands to GrGpu. */
-    void execute(GrOpFlushState* state, const SkRect& bounds) { this->onExecute(state, bounds); }
+    void execute(GrOpFlushState* state) { this->onExecute(state); }
 
     /** Used for spewing information about ops when debugging. */
     virtual SkString dumpInfo() const {
@@ -186,7 +191,7 @@
     virtual bool onCombineIfPossible(GrOp*, const GrCaps& caps) = 0;
 
     virtual void onPrepare(GrOpFlushState*) = 0;
-    virtual void onExecute(GrOpFlushState*, const SkRect& bounds) = 0;
+    virtual void onExecute(GrOpFlushState*) = 0;
 
     static uint32_t GenID(int32_t* idCounter) {
         // The atomic inc returns the old value not the incremented value. So we add
@@ -215,7 +220,6 @@
         SkDEBUGCODE(kUninitialized_BoundsFlag   = 0x4)
     };
 
-    SkDEBUGCODE(bool                    fUsed;)
     const uint16_t                      fClassID;
     uint16_t                            fBoundsFlags;
 
diff --git a/src/gpu/ops/GrStencilPathOp.h b/src/gpu/ops/GrStencilPathOp.h
index 462b626..ac19b5a 100644
--- a/src/gpu/ops/GrStencilPathOp.h
+++ b/src/gpu/ops/GrStencilPathOp.h
@@ -66,7 +66,7 @@
 
     void onPrepare(GrOpFlushState*) override {}
 
-    void onExecute(GrOpFlushState* state, const SkRect& bounds) override {
+    void onExecute(GrOpFlushState* state) override {
         GrPathRendering::StencilPathArgs args(fUseHWAA, fRenderTarget.get(), &fViewMatrix,
                                               &fScissor, &fStencil);
         state->gpu()->pathRendering()->stencilPath(args, fPath.get());