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;