Use clipped bounds for reordering decisions
authorbsalomon <bsalomon@google.com>
Fri, 8 Jul 2016 18:31:22 +0000 (11:31 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 8 Jul 2016 18:31:23 +0000 (11:31 -0700)
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2137543002

NOTREECHECKS=true
NOPRESUBMIT=true

Review-Url: https://codereview.chromium.org/2137543002

include/gpu/GrClip.h
src/gpu/GrClip.cpp
src/gpu/GrClipMaskManager.cpp
src/gpu/GrDrawTarget.cpp
src/gpu/GrDrawTarget.h

index 26aab70..e585281 100644 (file)
@@ -21,48 +21,78 @@ class GrPipelineBuilder;
  */
 class GrAppliedClip : public SkNoncopyable {
 public:
-    GrAppliedClip() : fHasStencilClip(false) {}
+    GrAppliedClip() : fHasStencilClip(false), fDeviceBounds(SkRect::MakeLargest()) {}
     GrFragmentProcessor* getClipCoverageFragmentProcessor() const {
         return fClipCoverageFP.get();
     }
     const GrScissorState& scissorState() const { return fScissorState; }
     bool hasStencilClip() const { return fHasStencilClip; }
 
-    void makeStencil(bool hasStencil) {
+    void makeStencil(bool hasStencil, const SkRect& deviceBounds) {
         fClipCoverageFP = nullptr;
         fScissorState.setDisabled();
         fHasStencilClip = hasStencil;
+        fDeviceBounds = deviceBounds;
     }
 
-    void makeScissoredStencil(bool hasStencil, const SkIRect& scissor) {
+    /**
+     * The device bounds of the clip defaults to the scissor rect, but a tighter bounds (based
+     * on the known effect of the stencil values) can be provided.
+     */
+    void makeScissoredStencil(const SkIRect& scissor, const SkRect* deviceBounds = nullptr) {
         fClipCoverageFP = nullptr;
         fScissorState.set(scissor);
-        fHasStencilClip = hasStencil;
+        fHasStencilClip = true;
+        if (deviceBounds) {
+            fDeviceBounds = *deviceBounds;
+            SkASSERT(scissor.contains(*deviceBounds))
+        } else {
+            fDeviceBounds = SkRect::Make(scissor);
+        }
     }
 
-    void makeFPBased(sk_sp<GrFragmentProcessor> fp) {
+    void makeFPBased(sk_sp<GrFragmentProcessor> fp, const SkRect& deviceBounds) {
         fClipCoverageFP = fp;
         fScissorState.setDisabled();
         fHasStencilClip = false;
+        fDeviceBounds = deviceBounds;
     }
 
     void makeScissored(SkIRect& scissor) {
         fClipCoverageFP.reset();
         fScissorState.set(scissor);
         fHasStencilClip = false;
+        fDeviceBounds = SkRect::Make(scissor);
     }
 
-    void makeScissoredFPBased(sk_sp<GrFragmentProcessor> fp, SkIRect& scissor) {
+    /**
+     * The device bounds of the clip defaults to the scissor rect, but a tighter bounds (based
+     * on the known effect of the fragment processor) can be provided.
+     */
+    void makeScissoredFPBased(sk_sp<GrFragmentProcessor> fp, const SkIRect& scissor,
+                              const SkRect* deviceBounds = nullptr) {
         fClipCoverageFP = fp;
         fScissorState.set(scissor);
         fHasStencilClip = false;
+        if (deviceBounds) {
+            fDeviceBounds = *deviceBounds;
+            SkASSERT(scissor.contains(*deviceBounds))
+        } else {
+            fDeviceBounds = SkRect::Make(scissor);
+        }
     }
 
+    /**
+     * Returns the device bounds of the applied clip. Ideally this considers the combined effect of
+     * all clipping techniques in play (scissor, stencil, and/or coverage fp).
+     */
+    const SkRect& deviceBounds() const { return fDeviceBounds; }
+
 private:
     sk_sp<GrFragmentProcessor> fClipCoverageFP;
     GrScissorState             fScissorState;
     bool                       fHasStencilClip;
-
+    SkRect                     fDeviceBounds;
     typedef SkNoncopyable INHERITED;
 };
 
@@ -99,20 +129,48 @@ private:
  */
 class GrFixedClip final : public GrClip {
 public:
-    GrFixedClip() : fHasStencilClip(false) {}
-    GrFixedClip(const SkIRect& scissorRect) : fScissorState(scissorRect), fHasStencilClip(false) {}
+    GrFixedClip() : fDeviceBounds(SkRect::MakeLargest()), fHasStencilClip(false) {}
+    GrFixedClip(const SkIRect& scissorRect)
+        : fScissorState(scissorRect)
+        , fDeviceBounds(SkRect::Make(scissorRect))
+        , fHasStencilClip(false) {}
 
     void reset() {
         fScissorState.setDisabled();
+        fDeviceBounds.setLargest();
         fHasStencilClip = false;
     }
 
     void reset(const SkIRect& scissorRect) {
         fScissorState.set(scissorRect);
+        fDeviceBounds = SkRect::Make(scissorRect);
         fHasStencilClip = false;
     }
 
-    void enableStencilClip(bool enable) { fHasStencilClip = enable; }
+    /**
+     * Enables stenciling. The stencil bounds is the device space bounds where the stencil test
+     * may pass.
+     */
+    void enableStencilClip(const SkRect& stencilBounds) {
+        fHasStencilClip = true;
+        fDeviceBounds = stencilBounds;
+        if (fScissorState.enabled()) {
+            const SkIRect& s = fScissorState.rect();
+            fDeviceBounds.fLeft   = SkTMax(fDeviceBounds.fLeft,   SkIntToScalar(s.fLeft));
+            fDeviceBounds.fTop    = SkTMax(fDeviceBounds.fTop,    SkIntToScalar(s.fTop));
+            fDeviceBounds.fRight  = SkTMin(fDeviceBounds.fRight,  SkIntToScalar(s.fRight));
+            fDeviceBounds.fBottom = SkTMin(fDeviceBounds.fBottom, SkIntToScalar(s.fBottom));
+        }
+    }
+
+    void disableStencilClip() {
+        fHasStencilClip = false;
+        if (fScissorState.enabled()) {
+            fDeviceBounds = SkRect::Make(fScissorState.rect());
+        } else {
+            fDeviceBounds.setLargest();
+        }
+    }
 
     const GrScissorState& scissorState() const { return fScissorState; }
     bool hasStencilClip() const { return fHasStencilClip; }
@@ -126,6 +184,7 @@ private:
                const SkRect* devBounds, GrAppliedClip* out) const final;
 
     GrScissorState   fScissorState;
+    SkRect           fDeviceBounds;
     bool             fHasStencilClip;
 };
 
index d74d935..b0577c5 100644 (file)
@@ -44,6 +44,7 @@ void GrFixedClip::getConservativeBounds(int width, int height, SkIRect* devResul
 bool GrFixedClip::apply(GrContext*, const GrPipelineBuilder& pipelineBuilder,
                         GrDrawContext* drawContext,
                         const SkRect* devBounds, GrAppliedClip* out) const {
+    SkASSERT(!fDeviceBounds.isLargest());
     if (fScissorState.enabled()) {
         SkIRect tightScissor;
         if (!tightScissor.intersect(fScissorState.rect(),
@@ -53,11 +54,15 @@ bool GrFixedClip::apply(GrContext*, const GrPipelineBuilder& pipelineBuilder,
         if (devBounds && !devBounds->intersects(SkRect::Make(tightScissor))) {
             return false;
         }
-        out->makeScissoredStencil(fHasStencilClip, tightScissor);
+        if (fHasStencilClip) {
+            out->makeScissoredStencil(tightScissor, &fDeviceBounds);
+        } else {
+            out->makeScissored(tightScissor);
+        }
         return true;
     }
 
-    out->makeStencil(fHasStencilClip);
+    out->makeStencil(fHasStencilClip, fDeviceBounds);
     return true;
 }
 
index cce0b20..c591bf1 100644 (file)
@@ -306,7 +306,7 @@ bool GrClipMaskManager::SetupClipping(GrContext* context,
                 out->makeScissoredFPBased(std::move(clipFP), scissorSpaceIBounds);
                 return true;
             }
-            out->makeFPBased(std::move(clipFP));
+            out->makeFPBased(std::move(clipFP), SkRect::Make(scissorSpaceIBounds));
             return true;
         }
     }
@@ -347,7 +347,8 @@ bool GrClipMaskManager::SetupClipping(GrContext* context,
             // clipSpace bounds. We determine the mask's position WRT to the render target here.
             SkIRect rtSpaceMaskBounds = clipSpaceIBounds;
             rtSpaceMaskBounds.offset(-clip.origin());
-            out->makeFPBased(create_fp_for_mask(result.get(), rtSpaceMaskBounds));
+            out->makeFPBased(create_fp_for_mask(result.get(), rtSpaceMaskBounds),
+                             SkRect::Make(rtSpaceMaskBounds));
             return true;
         }
         // if alpha clip mask creation fails fall through to the non-AA code paths
@@ -368,7 +369,7 @@ bool GrClipMaskManager::SetupClipping(GrContext* context,
     // use both stencil and scissor test to the bounds for the final draw.
     SkIRect scissorSpaceIBounds(clipSpaceIBounds);
     scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset);
-    out->makeScissoredStencil(true, scissorSpaceIBounds);
+    out->makeScissoredStencil(scissorSpaceIBounds);
     return true;
 }
 
@@ -594,7 +595,7 @@ bool GrClipMaskManager::CreateStencilClipMask(GrContext* context,
 
             bool fillInverted = false;
             // enabled at bottom of loop
-            clip.enableStencilClip(false);
+            clip.disableStencilClip();
 
             // This will be used to determine whether the clip shape can be rendered into the
             // stencil with arbitrary stencil settings.
@@ -690,16 +691,20 @@ bool GrClipMaskManager::CreateStencilClipMask(GrContext* context,
 
             // now we modify the clip bit by rendering either the clip
             // element directly or a bounding rect of the entire clip.
-            clip.enableStencilClip(true);
             for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {
 
                 if (drawDirectToClip) {
                     if (Element::kRect_Type == element->getType()) {
+                        clip.enableStencilClip(element->getRect().makeOffset(translate.fX,
+                                                                             translate.fY));
                         drawContext->drawContextPriv().stencilRect(clip, *pass, useHWAA, viewMatrix,
                                                                    element->getRect());
                     } else {
                         GrShape shape(clipPath, GrStyle::SimpleFill());
                         GrPaint paint;
+                        SkRect bounds = clipPath.getBounds();
+                        bounds.offset(translate.fX, translate.fY);
+                        clip.enableStencilClip(bounds);
                         paint.setXPFactory(GrDisableColorXPFactory::Make());
                         paint.setAntiAlias(element->isAA());
                         GrPathRenderer::DrawPathArgs args;
@@ -717,6 +722,9 @@ bool GrClipMaskManager::CreateStencilClipMask(GrContext* context,
                 } else {
                     // The view matrix is setup to do clip space -> stencil space translation, so
                     // draw rect in clip space.
+                    SkRect bounds = SkRect::Make(clipSpaceIBounds);
+                    bounds.offset(translate.fX, translate.fY);
+                    clip.enableStencilClip(bounds);
                     drawContext->drawContextPriv().stencilRect(clip, *pass, false, viewMatrix,
                                                                SkRect::Make(clipSpaceIBounds));
                 }
index 356c480..8beaade 100644 (file)
@@ -115,15 +115,19 @@ void GrDrawTarget::dump() const {
         SkDebugf("%d, ", fDependencies[i]->fDebugID);
     }
     SkDebugf("\n");
-    SkDebugf("batches (%d):\n", fBatches.count());
-    for (int i = 0; i < fBatches.count(); ++i) {
+    SkDebugf("batches (%d):\n", fRecordedBatches.count());
+    for (int i = 0; i < fRecordedBatches.count(); ++i) {
         SkDebugf("*******************************\n");
-        if (!fBatches[i]) {
+        if (!fRecordedBatches[i].fBatch) {
             SkDebugf("%d: <combined forward>\n", i);
         } else {
-            SkDebugf("%d: %s\n", i, fBatches[i]->name());
-            SkString str = fBatches[i]->dumpInfo();
+            SkDebugf("%d: %s\n", i, fRecordedBatches[i].fBatch->name());
+            SkString str = fRecordedBatches[i].fBatch->dumpInfo();
             SkDebugf("%s\n", str.c_str());
+            const SkRect& clippedBounds = fRecordedBatches[i].fClippedBounds;
+            SkDebugf("ClippedBounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
+                     clippedBounds.fLeft, clippedBounds.fTop, clippedBounds.fRight,
+                     clippedBounds.fBottom);
         }
     }
 }
@@ -199,9 +203,9 @@ void GrDrawTarget::prepareBatches(GrBatchFlushState* flushState) {
     this->makeClosed();
 
     // Loop over the batches that haven't yet generated their geometry
-    for (int i = 0; i < fBatches.count(); ++i) {
-        if (fBatches[i]) {
-            fBatches[i]->prepare(flushState);
+    for (int i = 0; i < fRecordedBatches.count(); ++i) {
+        if (fRecordedBatches[i].fBatch) {
+            fRecordedBatches[i].fBatch->prepare(flushState);
         }
     }
 
@@ -216,11 +220,11 @@ void GrDrawTarget::drawBatches(GrBatchFlushState* flushState) {
     GrRenderTarget* currentRT = nullptr;
     SkAutoTDelete<GrGpuCommandBuffer> commandBuffer;
     SkRect bounds = SkRect::MakeEmpty();
-    for (int i = 0; i < fBatches.count(); ++i) {
-        if (!fBatches[i]) {
+    for (int i = 0; i < fRecordedBatches.count(); ++i) {
+        if (!fRecordedBatches[i].fBatch) {
             continue;
         }
-        if (fBatches[i]->renderTarget() != currentRT) {
+        if (fRecordedBatches[i].fBatch->renderTarget() != currentRT) {
             if (commandBuffer) {
                 commandBuffer->end();
                 if (bounds.intersect(0, 0,
@@ -233,7 +237,7 @@ void GrDrawTarget::drawBatches(GrBatchFlushState* flushState) {
                 commandBuffer.reset();
             }
             bounds.setEmpty();
-            currentRT = fBatches[i]->renderTarget();
+            currentRT = fRecordedBatches[i].fBatch->renderTarget();
             if (currentRT) {
                 static const GrGpuCommandBuffer::LoadAndStoreInfo kBasicLoadStoreInfo
                     { GrGpuCommandBuffer::LoadOp::kLoad,GrGpuCommandBuffer::StoreOp::kStore,
@@ -245,19 +249,19 @@ void GrDrawTarget::drawBatches(GrBatchFlushState* flushState) {
             flushState->setCommandBuffer(commandBuffer);
         }
         if (commandBuffer) {
-            bounds.join(fBatches[i]->bounds());
+            bounds.join(fRecordedBatches[i].fClippedBounds);
         }
         if (fDrawBatchBounds) {
-            const SkRect& batchBounds = fBatches[i]->bounds();
-            SkIRect iBatchBounds;
-            batchBounds.roundOut(&iBatchBounds);
+            const SkRect& bounds = fRecordedBatches[i].fClippedBounds;
+            SkIRect ibounds;
+            bounds.roundOut(&ibounds);
             // In multi-draw buffer all the batches use the same render target and we won't need to
             // get the batchs bounds.
-            if (GrRenderTarget* rt = fBatches[i]->renderTarget()) {
-                fGpu->drawDebugWireRect(rt, iBatchBounds, 0xFF000000 | random.nextU());
+            if (GrRenderTarget* rt = fRecordedBatches[i].fBatch->renderTarget()) {
+                fGpu->drawDebugWireRect(rt, ibounds, 0xFF000000 | random.nextU());
             }
         }
-        fBatches[i]->draw(flushState);
+        fRecordedBatches[i].fBatch->draw(flushState);
     }
     if (commandBuffer) {
         commandBuffer->end();
@@ -275,7 +279,7 @@ void GrDrawTarget::drawBatches(GrBatchFlushState* flushState) {
 }
 
 void GrDrawTarget::reset() {
-    fBatches.reset();
+    fRecordedBatches.reset();
     if (fInstancedRendering) {
         fInstancedRendering->endFlush();
     }
@@ -307,6 +311,16 @@ static void batch_bounds(SkRect* bounds, const GrBatch* batch) {
     }
 }
 
+static inline bool intersect(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   = SkTMax(a.fLeft,   b.fLeft);
+    out->fTop    = SkTMax(a.fTop,    b.fTop);
+    out->fRight  = SkTMin(a.fRight,  b.fRight);
+    out->fBottom = SkTMin(a.fBottom, b.fBottom);
+    return (out->fLeft <= out->fRight && out->fTop <= out->fBottom);
+}
+
 void GrDrawTarget::drawBatch(const GrPipelineBuilder& pipelineBuilder,
                              GrDrawContext* drawContext,
                              const GrClip& clip,
@@ -384,8 +398,9 @@ void GrDrawTarget::drawBatch(const GrPipelineBuilder& pipelineBuilder,
     SkASSERT(fRenderTarget);
     batch->pipeline()->addDependenciesTo(fRenderTarget);
 #endif
-
-    this->recordBatch(batch);
+    SkRect clippedBounds;
+    SkAssertResult(intersect(&clippedBounds, bounds, appliedClip.deviceBounds()));
+    this->recordBatch(batch, clippedBounds);
 }
 
 void GrDrawTarget::stencilPath(const GrPipelineBuilder& pipelineBuilder,
@@ -424,7 +439,7 @@ void GrDrawTarget::stencilPath(const GrPipelineBuilder& pipelineBuilder,
                                                 appliedClip.scissorState(),
                                                 drawContext->accessRenderTarget(),
                                                 path);
-    this->recordBatch(batch);
+    this->recordBatch(batch, appliedClip.deviceBounds());
     batch->unref();
 }
 
@@ -465,7 +480,7 @@ void GrDrawTarget::clear(const SkIRect* rect,
         this->drawBatch(pipelineBuilder, drawContext, GrNoClip(), batch);
     } else {
         GrBatch* batch = new GrClearBatch(*rect, color, drawContext->accessRenderTarget());
-        this->recordBatch(batch);
+        this->recordBatch(batch, batch->bounds());
         batch->unref();
     }
 }
@@ -473,7 +488,7 @@ void GrDrawTarget::clear(const SkIRect* rect,
 void GrDrawTarget::discard(GrRenderTarget* renderTarget) {
     if (this->caps()->discardRenderTargetSupport()) {
         GrBatch* batch = new GrDiscardBatch(renderTarget);
-        this->recordBatch(batch);
+        this->recordBatch(batch, batch->bounds());
         batch->unref();
     }
 }
@@ -492,25 +507,26 @@ bool GrDrawTarget::copySurface(GrSurface* dst,
     this->addDependency(src);
 #endif
 
-    this->recordBatch(batch);
+    this->recordBatch(batch, batch->bounds());
     batch->unref();
     return true;
 }
 
-static inline bool exclusive_no_intersection(const SkRect& a, const SkRect& b) {
+static inline bool can_reorder(const SkRect& a, const SkRect& b) {
     return a.fRight <= b.fLeft || a.fBottom <= b.fTop ||
            b.fRight <= a.fLeft || b.fBottom <= a.fTop;
 }
 
-static inline bool can_reorder(const GrBatch* a, const GrBatch* b) {
-    SkRect ra;
-    SkRect rb;
-    batch_bounds(&ra, a);
-    batch_bounds(&rb, a);
-    return exclusive_no_intersection(ra, rb);
+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);
 }
 
-void GrDrawTarget::recordBatch(GrBatch* batch) {
+void GrDrawTarget::recordBatch(GrBatch* batch, const SkRect& clippedBounds) {
     // A closed drawTarget should never receive new/more batches
     SkASSERT(!this->isClosed());
 
@@ -526,12 +542,15 @@ void GrDrawTarget::recordBatch(GrBatch* batch) {
         batch->bounds().fLeft, batch->bounds().fRight,
         batch->bounds().fTop, batch->bounds().fBottom);
     GrBATCH_INFO(SkTabString(batch->dumpInfo(), 1).c_str());
+    GrBATCH_INFO("\tClipped Bounds: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
+                 clippedBounds.fLeft, clippedBounds.fTop, clippedBounds.fRight,
+                 clippedBounds.fBottom);
     GrBATCH_INFO("\tOutcome:\n");
-    int maxCandidates = SkTMin(fMaxBatchLookback, fBatches.count());
+    int maxCandidates = SkTMin(fMaxBatchLookback, fRecordedBatches.count());
     if (maxCandidates) {
         int i = 0;
         while (true) {
-            GrBatch* candidate = fBatches.fromBack(i);
+            GrBatch* candidate = fRecordedBatches.fromBack(i).fBatch.get();
             // We cannot continue to search backwards if the render target changes
             if (candidate->renderTargetUniqueID() != batch->renderTargetUniqueID()) {
                 GrBATCH_INFO("\t\tBreaking because of (%s, B%u) Rendertarget\n",
@@ -542,12 +561,13 @@ void GrDrawTarget::recordBatch(GrBatch* batch) {
                 GrBATCH_INFO("\t\tCombining with (%s, B%u)\n", candidate->name(),
                     candidate->uniqueID());
                 GR_AUDIT_TRAIL_BATCHING_RESULT_COMBINED(fAuditTrail, candidate, batch);
+                join(&fRecordedBatches.fromBack(i).fClippedBounds,
+                     fRecordedBatches.fromBack(i).fClippedBounds, clippedBounds);
                 return;
             }
             // Stop going backwards if we would cause a painter's order violation.
-            // TODO: The bounds used here do not fully consider the clip. It may be advantageous
-            // to clip each batch's bounds to the clip.
-            if (!can_reorder(candidate, batch)) {
+            const SkRect& candidateBounds = fRecordedBatches.fromBack(i).fClippedBounds;
+            if (!can_reorder(candidateBounds, clippedBounds)) {
                 GrBATCH_INFO("\t\tIntersects with (%s, B%u)\n", candidate->name(),
                     candidate->uniqueID());
                 break;
@@ -562,16 +582,17 @@ void GrDrawTarget::recordBatch(GrBatch* batch) {
         GrBATCH_INFO("\t\tFirstBatch\n");
     }
     GR_AUDIT_TRAIL_BATCHING_RESULT_NEW(fAuditTrail, batch);
-    fBatches.push_back().reset(SkRef(batch));
+    fRecordedBatches.emplace_back(RecordedBatch{sk_ref_sp(batch), clippedBounds});
 }
 
 void GrDrawTarget::forwardCombine() {
-    for (int i = 0; i < fBatches.count() - 2; ++i) {
-        GrBatch* batch = fBatches[i];
-        int maxCandidateIdx = SkTMin(i + fMaxBatchLookahead, fBatches.count() - 1);
+    for (int i = 0; i < fRecordedBatches.count() - 2; ++i) {
+        GrBatch* batch = fRecordedBatches[i].fBatch.get();
+        const SkRect& batchBounds = fRecordedBatches[i].fClippedBounds;
+        int maxCandidateIdx = SkTMin(i + fMaxBatchLookahead, fRecordedBatches.count() - 1);
         int j = i + 1;
         while (true) {
-            GrBatch* candidate = fBatches[j];
+            GrBatch* candidate = fRecordedBatches[j].fBatch.get();
             // We cannot continue to search if the render target changes
             if (candidate->renderTargetUniqueID() != batch->renderTargetUniqueID()) {
                 GrBATCH_INFO("\t\tBreaking because of (%s, B%u) Rendertarget\n",
@@ -586,14 +607,14 @@ void GrDrawTarget::forwardCombine() {
                 GrBATCH_INFO("\t\tCombining with (%s, B%u)\n", candidate->name(),
                              candidate->uniqueID());
                 GR_AUDIT_TRAIL_BATCHING_RESULT_COMBINED(fAuditTrail, batch, candidate);
-                fBatches[j].reset(SkRef(batch));
-                fBatches[i].reset(nullptr);
+                fRecordedBatches[j].fBatch = std::move(fRecordedBatches[i].fBatch);
+                join(&fRecordedBatches[j].fClippedBounds, fRecordedBatches[j].fClippedBounds,
+                     batchBounds);
                 break;
             }
             // Stop going traversing if we would cause a painter's order violation.
-            // TODO: The bounds used here do not fully consider the clip. It may be advantageous
-            // to clip each batch's bounds to the clip.
-            if (!can_reorder(candidate, batch)) {
+            const SkRect& candidateBounds = fRecordedBatches[j].fClippedBounds;
+            if (!can_reorder(candidateBounds, batchBounds)) {
                 GrBATCH_INFO("\t\tIntersects with (%s, B%u)\n", candidate->name(),
                              candidate->uniqueID());
                 break;
@@ -611,6 +632,6 @@ void GrDrawTarget::forwardCombine() {
 
 void GrDrawTarget::clearStencilClip(const SkIRect& rect, bool insideClip, GrRenderTarget* rt) {
     GrBatch* batch = new GrClearStencilClipBatch(rect, insideClip, rt);
-    this->recordBatch(batch);
+    this->recordBatch(batch, batch->bounds());
     batch->unref();
 }
index 2a5bbde..7b268c2 100644 (file)
@@ -196,7 +196,7 @@ private:
         }
     };
 
-    void recordBatch(GrBatch*);
+    void recordBatch(GrBatch*, const SkRect& clippedBounds);
     void forwardCombine();
 
     // Makes a copy of the dst if it is necessary for the draw. Returns false if a copy is required
@@ -214,7 +214,11 @@ private:
     // Used only by drawContextPriv.
     void clearStencilClip(const SkIRect&, bool insideClip, GrRenderTarget*);
 
-    SkSTArray<256, SkAutoTUnref<GrBatch>, true>     fBatches;
+    struct RecordedBatch {
+        sk_sp<GrBatch> fBatch;
+        SkRect         fClippedBounds;
+    };
+    SkSTArray<256, RecordedBatch, true>             fRecordedBatches;
     // The context is only in service of the clip mask manager, remove once CMM doesn't need this.
     GrContext*                                      fContext;
     GrGpu*                                          fGpu;