Add debug option to clip each GrBatch to its device bounds

Review URL: https://codereview.chromium.org/1471083002
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index fa9d189..303aecc 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -126,9 +126,10 @@
     return pr;
 }
 
-GrClipMaskManager::GrClipMaskManager(GrDrawTarget* drawTarget)
+GrClipMaskManager::GrClipMaskManager(GrDrawTarget* drawTarget, bool debugClipBatchToBounds)
     : fDrawTarget(drawTarget)
-    , fClipMode(kIgnoreClip_StencilClipMode) {
+    , fClipMode(kIgnoreClip_StencilClipMode)
+    , fDebugClipBatchToBounds(debugClipBatchToBounds) {
 }
 
 GrContext* GrClipMaskManager::getContext() {
@@ -272,6 +273,38 @@
     return !failed;
 }
 
+static void add_rect_to_clip(const GrClip& clip, const SkRect& devRect, GrClip* out) {
+    switch (clip.clipType()) {
+        case GrClip::kClipStack_ClipType: {
+            SkClipStack* stack = new SkClipStack;
+            *stack = *clip.clipStack();
+            // The stack is actually in clip space not device space.
+            SkRect clipRect = devRect;
+            SkPoint origin = { SkIntToScalar(clip.origin().fX), SkIntToScalar(clip.origin().fY) };
+            clipRect.offset(origin);
+            SkIRect iclipRect;
+            clipRect.roundOut(&iclipRect);
+            clipRect = SkRect::Make(iclipRect);
+            stack->clipDevRect(clipRect, SkRegion::kIntersect_Op, false);
+            out->setClipStack(stack, &clip.origin());
+            break;
+        }
+        case GrClip::kWideOpen_ClipType:
+            *out = GrClip(devRect);
+            break;
+        case GrClip::kIRect_ClipType: {
+            SkIRect intersect;
+            devRect.roundOut(&intersect);
+            if (intersect.intersect(clip.irect())) {
+                *out = GrClip(intersect);
+            } else {
+                *out = clip;
+            }
+            break;
+        }
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // sort out what kind of clip mask needs to be created: alpha, stencil,
 // scissor, or entirely software
@@ -294,7 +327,13 @@
     SkASSERT(rt);
 
     SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height());
-    const GrClip& clip = pipelineBuilder.clip();
+    GrClip devBoundsClip;
+    bool doDevBoundsClip = fDebugClipBatchToBounds && devBounds;
+    if (doDevBoundsClip) {
+        add_rect_to_clip(pipelineBuilder.clip(), *devBounds, &devBoundsClip);
+    }
+    const GrClip& clip = doDevBoundsClip ? devBoundsClip : pipelineBuilder.clip();
+
     if (clip.isWideOpen(clipSpaceRTIBounds)) {
         this->setPipelineBuilderStencil(pipelineBuilder, ars);
         return true;
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index 98fd3af..6b9b727 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -55,7 +55,7 @@
  */
 class GrClipMaskManager : SkNoncopyable {
 public:
-    GrClipMaskManager(GrDrawTarget* owner);
+    GrClipMaskManager(GrDrawTarget* owner, bool debugClipBatchToBounds);
 
     /**
      * Creates a clip mask if necessary as a stencil buffer or alpha texture
@@ -171,6 +171,7 @@
 
     GrDrawTarget*   fDrawTarget;    // This is our owning draw target.
     StencilClipMode fClipMode;
+    bool            fDebugClipBatchToBounds;
 
     typedef SkNoncopyable INHERITED;
 };
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 920e3c4..ad89672 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -72,11 +72,11 @@
     if (!fGpu) {
         return false;
     }
-    this->initCommon();
+    this->initCommon(options);
     return true;
 }
 
-void GrContext::initCommon() {
+void GrContext::initCommon(const GrContextOptions& options) {
     fCaps = SkRef(fGpu->caps());
     fResourceCache = new GrResourceCache(fCaps);
     fResourceCache->setOverBudgetCallback(OverBudgetCB, this);
@@ -86,7 +86,9 @@
 
     fDidTestPMConversions = false;
 
-    fDrawingManager.reset(new GrDrawingManager(this));
+    GrDrawTarget::Options dtOptions;
+    dtOptions.fClipBatchToBounds = options.fClipBatchToBounds;
+    fDrawingManager.reset(new GrDrawingManager(this, dtOptions));
 
     // GrBatchFontCache will eventually replace GrFontCache
     fBatchFontCache = new GrBatchFontCache(this);
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index f34e6fc..e41a1b7 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -32,7 +32,8 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrDrawTarget::GrDrawTarget(GrRenderTarget* rt, GrGpu* gpu, GrResourceProvider* resourceProvider)
+GrDrawTarget::GrDrawTarget(GrRenderTarget* rt, GrGpu* gpu, GrResourceProvider* resourceProvider,
+                           const Options& options)
     : fGpu(SkRef(gpu))
     , fResourceProvider(resourceProvider)
     , fFlushing(false)
@@ -40,7 +41,7 @@
     , fRenderTarget(rt) {
     // TODO: Stop extracting the context (currently needed by GrClipMaskManager)
     fContext = fGpu->getContext();
-    fClipMaskManager.reset(new GrClipMaskManager(this));
+    fClipMaskManager.reset(new GrClipMaskManager(this, options.fClipBatchToBounds));
 
     rt->setLastDrawTarget(this);
 
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index 240731e..7b0159e 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -43,9 +43,13 @@
 
 class GrDrawTarget final : public SkRefCnt {
 public:
-    // The context may not be fully constructed and should not be used during GrDrawTarget
-    // construction.
-    GrDrawTarget(GrRenderTarget* rt, GrGpu* gpu, GrResourceProvider*);
+    /** Options for GrDrawTarget behavior. */
+    struct Options {
+        Options () : fClipBatchToBounds(false) {}
+        bool fClipBatchToBounds;
+    };
+
+    GrDrawTarget(GrRenderTarget*, GrGpu*, GrResourceProvider*, const Options&);
 
     ~GrDrawTarget() override;
 
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index 73b7d16..13167d4 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -152,7 +152,8 @@
     }
 #endif
 
-    GrDrawTarget* dt = new GrDrawTarget(rt, fContext->getGpu(), fContext->resourceProvider());
+    GrDrawTarget* dt = new GrDrawTarget(rt, fContext->getGpu(), fContext->resourceProvider(),
+                                        fOptionsForDrawTargets);
 
     *fDrawTargets.append() = dt;
 
diff --git a/src/gpu/GrDrawingManager.h b/src/gpu/GrDrawingManager.h
index 41d8ab5..672f7b0 100644
--- a/src/gpu/GrDrawingManager.h
+++ b/src/gpu/GrDrawingManager.h
@@ -53,8 +53,9 @@
     static bool ProgramUnitTest(GrContext* context, int maxStages);
 
 private:
-    GrDrawingManager(GrContext* context)
+    GrDrawingManager(GrContext* context, const GrDrawTarget::Options& optionsForDrawTargets)
         : fContext(context)
+        , fOptionsForDrawTargets(optionsForDrawTargets)
         , fAbandoned(false)
         , fNVPRTextContext(nullptr)
         , fPathRendererChain(nullptr)
@@ -74,6 +75,7 @@
     static const int kNumDFTOptions = 2;      // DFT or no DFT
 
     GrContext*                  fContext;
+    GrDrawTarget::Options       fOptionsForDrawTargets;
 
     bool                        fAbandoned;
     SkTDArray<GrDrawTarget*>    fDrawTargets;
diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp
index f0f6370..b0d92fe 100644
--- a/src/gpu/GrTest.cpp
+++ b/src/gpu/GrTest.cpp
@@ -318,7 +318,7 @@
     SkASSERT(nullptr == fGpu);
     fGpu = new MockGpu(this, options);
     SkASSERT(fGpu);
-    this->initCommon();
+    this->initCommon(options);
 
     // We delete these because we want to test the cache starting with zero resources. Also, none of
     // these objects are required for any of tests that use this context. TODO: make stop allocating