Add option to draw wireframe batch bounds

Committed: https://skia.googlesource.com/skia/+/26489ef21ff5df33b8cb5943fddfd4604e203960

Review URL: https://codereview.chromium.org/1494473005
diff --git a/dm/DMGpuSupport.h b/dm/DMGpuSupport.h
index 9f20dd5..bccf112 100644
--- a/dm/DMGpuSupport.h
+++ b/dm/DMGpuSupport.h
@@ -13,6 +13,10 @@
 
 #include "SkSurface.h"
 
+// This should be safe to include even in no-gpu builds. Include by relative path so it
+// can be found in non-gpu builds.
+#include "../include/gpu/GrContextOptions.h"
+
 #if SK_SUPPORT_GPU
 
 // Ganesh is available.  Yippee!
@@ -55,11 +59,6 @@
     void dumpGpuStats(SkString*) const {}
 };
 
-struct GrContextOptions {
-    bool fImmediateMode;
-    bool fClipBatchToBounds;
-};
-
 class GrContextFactory {
 public:
     GrContextFactory() {};
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 2861ae8..26ba13b 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -807,6 +807,7 @@
 
 DEFINE_bool(imm, false, "Run gpu configs in immediate mode.");
 DEFINE_bool(batchClip, false, "Clip each GrBatch to its device bounds for testing.");
+DEFINE_bool(batchBounds, false, "Draw a wireframe bounds of each GrBatch.");
 
 Error GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString* log) const {
     GrContextOptions options;
@@ -816,6 +817,9 @@
     if (FLAGS_batchClip) {
         options.fClipBatchToBounds = true;
     }
+    if (FLAGS_batchBounds) {
+        options.fDrawBatchBounds = true;
+    }
     src.modifyGrContextOptions(&options);
 
     GrContextFactory factory(options);
diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h
index 52173eb..2874b52 100644
--- a/include/gpu/GrContextOptions.h
+++ b/include/gpu/GrContextOptions.h
@@ -21,6 +21,7 @@
         , fUseDrawInsteadOfPartialRenderTargetWrite(false)
         , fImmediateMode(false)
         , fClipBatchToBounds(false)
+        , fDrawBatchBounds(false)
         , fUseShaderSwizzling(false) {}
 
     // EXPERIMENTAL
@@ -57,6 +58,11 @@
         verify that the clip bounds are conservative. */
     bool fClipBatchToBounds;
 
+    /** For debugging purposes draw a wireframe device bounds rect for each GrBatch. The wire
+        frame rect is draw before the GrBatch in order to visualize batches that draw outside
+        of their dev bounds. */
+    bool fDrawBatchBounds;
+
     /** Force us to do all swizzling manually in the shader and don't rely on extensions to do
         swizzling. */
     bool fUseShaderSwizzling;
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index ad89672..a4887e3 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -88,6 +88,7 @@
 
     GrDrawTarget::Options dtOptions;
     dtOptions.fClipBatchToBounds = options.fClipBatchToBounds;
+    dtOptions.fDrawBatchBounds = options.fDrawBatchBounds;
     fDrawingManager.reset(new GrDrawingManager(this, dtOptions));
 
     // GrBatchFontCache will eventually replace GrFontCache
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 9b7fa31..4f91a3c 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -43,6 +43,8 @@
     fContext = fGpu->getContext();
     fClipMaskManager.reset(new GrClipMaskManager(this, options.fClipBatchToBounds));
 
+    fDrawBatchBounds = options.fDrawBatchBounds;
+
     rt->setLastDrawTarget(this);
 
 #ifdef SK_DEBUG
@@ -195,7 +197,18 @@
 
 void GrDrawTarget::drawBatches(GrBatchFlushState* flushState) {
     // Draw all the generated geometry.
+    SkRandom random;
     for (int i = 0; i < fBatches.count(); ++i) {
+        if (fDrawBatchBounds) {
+            const SkRect& bounds = fBatches[i]->bounds();
+            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, ibounds, 0xFF000000 | random.nextU());
+            }
+        }
         fBatches[i]->draw(flushState);
     }
 
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index 7b0159e..dc38b5d 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -45,8 +45,9 @@
 public:
     /** Options for GrDrawTarget behavior. */
     struct Options {
-        Options () : fClipBatchToBounds(false) {}
+        Options () : fClipBatchToBounds(false), fDrawBatchBounds(false) {}
         bool fClipBatchToBounds;
+        bool fDrawBatchBounds;
     };
 
     GrDrawTarget(GrRenderTarget*, GrGpu*, GrResourceProvider*, const Options&);
@@ -319,6 +320,8 @@
     SkTDArray<GrDrawTarget*>                    fDependencies;
     GrRenderTarget*                             fRenderTarget;
 
+    bool                                        fDrawBatchBounds;
+
     typedef SkRefCnt INHERITED;
 };
 
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 9c36d05..d1b0802 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -404,6 +404,8 @@
     // clears target's entire stencil buffer to 0
     virtual void clearStencil(GrRenderTarget* target) = 0;
 
+    // draws an outline rectangle for debugging/visualization purposes.
+    virtual void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) = 0;
 
     // Determines whether a copy of a texture must be made in order to be compatible with
     // a given GrTextureParams. If so, the width, height and filter used for the copy are
diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp
index a3823dd..e7b2ae7 100644
--- a/src/gpu/GrTest.cpp
+++ b/src/gpu/GrTest.cpp
@@ -284,6 +284,8 @@
         return false;
     }
 
+    void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override {};
+
 private:
     void onResetContext(uint32_t resetBits) override {}
 
diff --git a/src/gpu/batches/GrBatch.h b/src/gpu/batches/GrBatch.h
index 03e396a..a66d6ae 100644
--- a/src/gpu/batches/GrBatch.h
+++ b/src/gpu/batches/GrBatch.h
@@ -16,6 +16,7 @@
 
 class GrCaps;
 class GrBatchFlushState;
+class GrRenderTarget;
 
 /**
  * GrBatch is the base class for all Ganesh deferred geometry generators.  To facilitate
@@ -113,6 +114,9 @@
     /** Used for spewing information about batches when debugging. */
     virtual SkString dumpInfo() const = 0;
 
+    /** Can remove this when multi-draw-buffer lands */
+    virtual GrRenderTarget* renderTarget() const = 0;
+
 protected:
     // NOTE, compute some bounds, even if extremely conservative.  Do *NOT* setLargest on the bounds
     // rect because we outset it for dst copy textures
diff --git a/src/gpu/batches/GrClearBatch.h b/src/gpu/batches/GrClearBatch.h
index 944485c..c38372c 100644
--- a/src/gpu/batches/GrClearBatch.h
+++ b/src/gpu/batches/GrClearBatch.h
@@ -28,6 +28,7 @@
     const char* name() const override { return "Clear"; }
 
     uint32_t renderTargetUniqueID() const override { return fRenderTarget.get()->getUniqueID(); }
+    GrRenderTarget* renderTarget() const override { return fRenderTarget.get(); }
 
     SkString dumpInfo() const override {
         SkString string;
@@ -71,6 +72,7 @@
     const char* name() const override { return "ClearStencilClip"; }
 
     uint32_t renderTargetUniqueID() const override { return fRenderTarget.get()->getUniqueID(); }
+    GrRenderTarget* renderTarget() const override { return fRenderTarget.get(); }
 
     SkString dumpInfo() const override {
         SkString string;
diff --git a/src/gpu/batches/GrCopySurfaceBatch.h b/src/gpu/batches/GrCopySurfaceBatch.h
index ed5e77f..7bf8d8d 100644
--- a/src/gpu/batches/GrCopySurfaceBatch.h
+++ b/src/gpu/batches/GrCopySurfaceBatch.h
@@ -26,6 +26,7 @@
         GrRenderTarget* rt = fDst.get()->asRenderTarget();
         return rt ? rt->getUniqueID() : 0;
     }
+    GrRenderTarget* renderTarget() const override { return fDst.get()->asRenderTarget(); }
 
     SkString dumpInfo() const override {
         SkString string;
diff --git a/src/gpu/batches/GrDiscardBatch.h b/src/gpu/batches/GrDiscardBatch.h
index aa443cf..65b6c4b 100644
--- a/src/gpu/batches/GrDiscardBatch.h
+++ b/src/gpu/batches/GrDiscardBatch.h
@@ -26,6 +26,7 @@
     const char* name() const override { return "Discard"; }
 
     uint32_t renderTargetUniqueID() const override { return fRenderTarget.get()->getUniqueID(); }
+    GrRenderTarget* renderTarget() const override { return fRenderTarget.get(); }
 
     SkString dumpInfo() const override {
         SkString string;
diff --git a/src/gpu/batches/GrDrawBatch.h b/src/gpu/batches/GrDrawBatch.h
index 8e73878..9401c67 100644
--- a/src/gpu/batches/GrDrawBatch.h
+++ b/src/gpu/batches/GrDrawBatch.h
@@ -63,6 +63,11 @@
         return this->pipeline()->getRenderTarget()->getUniqueID();
     }
 
+    GrRenderTarget* renderTarget() const final {
+        SkASSERT(fPipelineInstalled);
+        return this->pipeline()->getRenderTarget();
+    }
+
     SkString dumpInfo() const override {
         SkString string;
         string.appendf("RT: %d\n", this->renderTargetUniqueID());
diff --git a/src/gpu/batches/GrStencilPathBatch.h b/src/gpu/batches/GrStencilPathBatch.h
index a0dcadb..33189c2 100644
--- a/src/gpu/batches/GrStencilPathBatch.h
+++ b/src/gpu/batches/GrStencilPathBatch.h
@@ -31,6 +31,7 @@
     const char* name() const override { return "StencilPath"; }
 
     uint32_t renderTargetUniqueID() const override { return fRenderTarget.get()->getUniqueID(); }
+    GrRenderTarget* renderTarget() const override { return fRenderTarget.get(); }
 
     SkString dumpInfo() const override {
         SkString string;
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 918bee8..5b6ae7c 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -232,8 +232,9 @@
     if (this->glCaps().shaderCaps()->pathRenderingSupport()) {
         fPathRendering.reset(new GrGLPathRendering(this));
     }
-
     this->createCopyPrograms();
+    fWireRectProgram.fProgram = 0;
+    fWireRectArrayBuffer = 0;
 }
 
 GrGLGpu::~GrGLGpu() {
@@ -258,10 +259,19 @@
             GL_CALL(DeleteProgram(fCopyPrograms[i].fProgram));
         }
     }
+
     if (0 != fCopyProgramArrayBuffer) {
         GL_CALL(DeleteBuffers(1, &fCopyProgramArrayBuffer));
     }
 
+    if (0 != fWireRectProgram.fProgram) {
+        GL_CALL(DeleteProgram(fWireRectProgram.fProgram));
+    }
+
+    if (0 != fWireRectArrayBuffer) {
+        GL_CALL(DeleteBuffers(1, &fWireRectArrayBuffer));
+    }
+
     delete fProgramCache;
 }
 
@@ -276,6 +286,8 @@
     for (size_t i = 0; i < SK_ARRAY_COUNT(fCopyPrograms); ++i) {
         fCopyPrograms[i].fProgram = 0;
     }
+    fWireRectProgram.fProgram = 0;
+    fWireRectArrayBuffer = 0;
     if (this->glCaps().shaderCaps()->pathRenderingSupport()) {
         this->glPathRendering()->abandonGpuResources();
     }
@@ -3016,7 +3028,6 @@
     return false;
 }
 
-
 void GrGLGpu::createCopyPrograms() {
     for (size_t i = 0; i < SK_ARRAY_COUNT(fCopyPrograms); ++i) {
         fCopyPrograms[i].fProgram = 0;
@@ -3037,7 +3048,7 @@
                                   GrShaderVar::kVaryingOut_TypeModifier);
         GrGLSLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType,
                                    GrShaderVar::kOut_TypeModifier);
-    
+
         SkString vshaderTxt(version);
         aVertex.appendDecl(this->glCaps().glslCaps(), &vshaderTxt);
         vshaderTxt.append(";");
@@ -3133,6 +3144,160 @@
                              GR_GL_STATIC_DRAW));
 }
 
+void GrGLGpu::createWireRectProgram() {
+    SkASSERT(!fWireRectProgram.fProgram);
+    GrGLSLShaderVar uColor("u_color", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier);
+    GrGLSLShaderVar uRect("u_rect", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier);
+    GrGLSLShaderVar aVertex("a_vertex", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier);
+    const char* version = this->glCaps().glslCaps()->versionDeclString();
+
+    // The rect uniform specifies the rectangle in NDC space as a vec4 (left,top,right,bottom). The
+    // program is used with a vbo containing the unit square. Vertices are computed from the rect
+    // uniform using the 4 vbo vertices.
+    SkString vshaderTxt(version);
+    aVertex.appendDecl(this->glCaps().glslCaps(), &vshaderTxt);
+    vshaderTxt.append(";");
+    uRect.appendDecl(this->glCaps().glslCaps(), &vshaderTxt);
+    vshaderTxt.append(";");
+    vshaderTxt.append(
+        "// Wire Rect Program VS\n"
+        "void main() {"
+        "  gl_Position.x = u_rect.x + a_vertex.x * (u_rect.z - u_rect.x);"
+        "  gl_Position.y = u_rect.y + a_vertex.y * (u_rect.w - u_rect.y);"
+        "  gl_Position.zw = vec2(0, 1);"
+        "}"
+    );
+
+    GrGLSLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier);
+
+    SkString fshaderTxt(version);
+    GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision,
+                                                 *this->glCaps().glslCaps(),
+                                                 &fshaderTxt);
+    uColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
+    fshaderTxt.append(";");
+    const char* fsOutName;
+    if (this->glCaps().glslCaps()->mustDeclareFragmentShaderOutput()) {
+        oFragColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
+        fshaderTxt.append(";");
+        fsOutName = oFragColor.c_str();
+    } else {
+        fsOutName = "gl_FragColor";
+    }
+    fshaderTxt.appendf(
+        "// Write Rect Program FS\n"
+        "void main() {"
+        "  %s = %s;"
+        "}",
+        fsOutName,
+        uColor.c_str()
+    );
+
+    GL_CALL_RET(fWireRectProgram.fProgram, CreateProgram());
+    const char* str;
+    GrGLint length;
+
+    str = vshaderTxt.c_str();
+    length = SkToInt(vshaderTxt.size());
+    GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fWireRectProgram.fProgram,
+                                                  GR_GL_VERTEX_SHADER, &str, &length, 1,
+                                                  &fStats);
+
+    str = fshaderTxt.c_str();
+    length = SkToInt(fshaderTxt.size());
+    GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fWireRectProgram.fProgram,
+                                                  GR_GL_FRAGMENT_SHADER, &str, &length, 1,
+                                                  &fStats);
+
+    GL_CALL(LinkProgram(fWireRectProgram.fProgram));
+
+    GL_CALL_RET(fWireRectProgram.fColorUniform,
+                GetUniformLocation(fWireRectProgram.fProgram, "u_color"));
+    GL_CALL_RET(fWireRectProgram.fRectUniform,
+                GetUniformLocation(fWireRectProgram.fProgram, "u_rect"));
+    GL_CALL(BindAttribLocation(fWireRectProgram.fProgram, 0, "a_vertex"));
+
+    GL_CALL(DeleteShader(vshader));
+    GL_CALL(DeleteShader(fshader));
+    GL_CALL(GenBuffers(1, &fWireRectArrayBuffer));
+    fHWGeometryState.setVertexBufferID(this, fWireRectArrayBuffer);
+    static const GrGLfloat vdata[] = {
+        0, 0,
+        0, 1,
+        1, 1,
+        1, 0,
+    };
+    GL_ALLOC_CALL(this->glInterface(),
+                  BufferData(GR_GL_ARRAY_BUFFER,
+                             (GrGLsizeiptr) sizeof(vdata),
+                             vdata,  // data ptr
+                             GR_GL_STATIC_DRAW));
+}
+
+void GrGLGpu::drawDebugWireRect(GrRenderTarget* rt, const SkIRect& rect, GrColor color) {
+    this->handleDirtyContext();
+    if (!fWireRectProgram.fProgram) {
+        this->createWireRectProgram();
+    }
+
+    int w = rt->width();
+    int h = rt->height();
+
+    // Compute the edges of the rectangle (top,left,right,bottom) in NDC space. Must consider
+    // whether the render target is flipped or not.
+    GrGLfloat edges[4];
+    edges[0] = SkIntToScalar(rect.fLeft) + 0.5f;
+    edges[2] = SkIntToScalar(rect.fRight) - 0.5f;
+    if (kBottomLeft_GrSurfaceOrigin == rt->origin()) {
+        edges[1] = h - (SkIntToScalar(rect.fTop) + 0.5f);
+        edges[3] = h - (SkIntToScalar(rect.fBottom) - 0.5f);
+    } else {
+        edges[1] = SkIntToScalar(rect.fTop) + 0.5f;
+        edges[3] = SkIntToScalar(rect.fBottom) - 0.5f;
+    }
+    edges[0] = 2 * edges[0] / w - 1.0f;
+    edges[1] = 2 * edges[1] / h - 1.0f;
+    edges[2] = 2 * edges[2] / w - 1.0f;
+    edges[3] = 2 * edges[3] / h - 1.0f;
+
+    GrGLfloat channels[4];
+    static const GrGLfloat scale255 = 1.f / 255.f;
+    channels[0] = GrColorUnpackR(color) * scale255;
+    channels[1] = GrColorUnpackG(color) * scale255;
+    channels[2] = GrColorUnpackB(color) * scale255;
+    channels[3] = GrColorUnpackA(color) * scale255;
+
+    GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(rt->asRenderTarget());
+    this->flushRenderTarget(glRT, &rect);
+
+    GL_CALL(UseProgram(fWireRectProgram.fProgram));
+    fHWProgramID = fWireRectProgram.fProgram;
+
+    fHWGeometryState.setVertexArrayID(this, 0);
+
+    GrGLAttribArrayState* attribs =
+        fHWGeometryState.bindArrayAndBufferToDraw(this, fWireRectArrayBuffer);
+    attribs->set(this, 0, fWireRectArrayBuffer, 2, GR_GL_FLOAT, false, 2 * sizeof(GrGLfloat), 0);
+    attribs->disableUnusedArrays(this, 0x1);
+
+    GL_CALL(Uniform4fv(fWireRectProgram.fRectUniform, 1, edges));
+    GL_CALL(Uniform4fv(fWireRectProgram.fColorUniform, 1, channels));
+
+    GrXferProcessor::BlendInfo blendInfo;
+    blendInfo.reset();
+    this->flushBlend(blendInfo);
+    this->flushColorWrite(true);
+    this->flushDrawFace(GrPipelineBuilder::kBoth_DrawFace);
+    this->flushHWAAState(glRT, false);
+    this->disableScissor();
+    GrStencilSettings stencil;
+    stencil.setDisabled();
+    this->flushStencil(stencil);
+
+    GL_CALL(DrawArrays(GR_GL_LINE_LOOP, 0, 4));
+}
+
+
 void GrGLGpu::copySurfaceAsDraw(GrSurface* dst,
                                 GrSurface* src,
                                 const SkIRect& srcRect,
@@ -3157,8 +3322,7 @@
 
     GrGLAttribArrayState* attribs =
         fHWGeometryState.bindArrayAndBufferToDraw(this, fCopyProgramArrayBuffer);
-    attribs->set(this, 0, fCopyProgramArrayBuffer, 2, GR_GL_FLOAT, false,
-                    2 * sizeof(GrGLfloat), 0);
+    attribs->set(this, 0, fCopyProgramArrayBuffer, 2, GR_GL_FLOAT, false, 2 * sizeof(GrGLfloat), 0);
     attribs->disableUnusedArrays(this, 0x1);
 
     // dst rect edges in NDC (-1 to 1)
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 6ec3881..d802f08 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -131,6 +131,8 @@
 
     void resetShaderCacheForTesting() const override;
 
+    void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override;
+
 private:
     GrGLGpu(GrGLContext* ctx, GrContext* context);
 
@@ -328,6 +330,8 @@
     SkAutoTUnref<GrGLContext>  fGLContext;
 
     void createCopyPrograms();
+    void createWireRectProgram();
+    void createUnitRectBuffer();
 
     // GL program-related state
     ProgramCache*               fProgramCache;
@@ -505,6 +509,13 @@
     }                           fCopyPrograms[2];
     GrGLuint                    fCopyProgramArrayBuffer;
 
+    struct {
+        GrGLuint fProgram;
+        GrGLint  fColorUniform;
+        GrGLint  fRectUniform;
+    }                           fWireRectProgram;
+    GrGLuint                    fWireRectArrayBuffer;
+
     static int TextureTargetToCopyProgramIdx(GrGLenum target) {
         if (target == GR_GL_TEXTURE_2D) {
             return 0;