Add option to draw wireframe batch bounds
Review URL: https://codereview.chromium.org/1494473005
This commit is contained in:
parent
222b30d3a8
commit
26489ef21f
@ -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 @@ public:
|
||||
void dumpGpuStats(SkString*) const {}
|
||||
};
|
||||
|
||||
struct GrContextOptions {
|
||||
bool fImmediateMode;
|
||||
bool fClipBatchToBounds;
|
||||
};
|
||||
|
||||
class GrContextFactory {
|
||||
public:
|
||||
GrContextFactory() {};
|
||||
|
@ -807,6 +807,7 @@ void PreAbandonGpuContextErrorHandler(SkError, void*) {}
|
||||
|
||||
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 @@ Error GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString* log) co
|
||||
if (FLAGS_batchClip) {
|
||||
options.fClipBatchToBounds = true;
|
||||
}
|
||||
if (FLAGS_batchBounds) {
|
||||
options.fDrawBatchBounds = true;
|
||||
}
|
||||
src.modifyGrContextOptions(&options);
|
||||
|
||||
GrContextFactory factory(options);
|
||||
|
@ -21,6 +21,7 @@ struct GrContextOptions {
|
||||
, fUseDrawInsteadOfPartialRenderTargetWrite(false)
|
||||
, fImmediateMode(false)
|
||||
, fClipBatchToBounds(false)
|
||||
, fDrawBatchBounds(false)
|
||||
, fUseShaderSwizzling(false) {}
|
||||
|
||||
// EXPERIMENTAL
|
||||
@ -57,6 +58,11 @@ struct GrContextOptions {
|
||||
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;
|
||||
|
@ -88,6 +88,7 @@ void GrContext::initCommon(const GrContextOptions& options) {
|
||||
|
||||
GrDrawTarget::Options dtOptions;
|
||||
dtOptions.fClipBatchToBounds = options.fClipBatchToBounds;
|
||||
dtOptions.fDrawBatchBounds = options.fDrawBatchBounds;
|
||||
fDrawingManager.reset(new GrDrawingManager(this, dtOptions));
|
||||
|
||||
// GrBatchFontCache will eventually replace GrFontCache
|
||||
|
@ -43,6 +43,8 @@ GrDrawTarget::GrDrawTarget(GrRenderTarget* rt, GrGpu* gpu, GrResourceProvider* r
|
||||
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::prepareBatches(GrBatchFlushState* flushState) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -45,8 +45,9 @@ class GrDrawTarget final : public SkRefCnt {
|
||||
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 @@ private:
|
||||
SkTDArray<GrDrawTarget*> fDependencies;
|
||||
GrRenderTarget* fRenderTarget;
|
||||
|
||||
bool fDrawBatchBounds;
|
||||
|
||||
typedef SkRefCnt INHERITED;
|
||||
};
|
||||
|
||||
|
@ -404,6 +404,8 @@ public:
|
||||
// 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
|
||||
|
@ -284,6 +284,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override {};
|
||||
|
||||
private:
|
||||
void onResetContext(uint32_t resetBits) override {}
|
||||
|
||||
|
@ -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 @@ public:
|
||||
/** 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
|
||||
|
@ -28,6 +28,7 @@ public:
|
||||
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 @@ public:
|
||||
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;
|
||||
|
@ -26,6 +26,7 @@ public:
|
||||
GrRenderTarget* rt = fDst.get()->asRenderTarget();
|
||||
return rt ? rt->getUniqueID() : 0;
|
||||
}
|
||||
GrRenderTarget* renderTarget() const override { return fDst.get()->asRenderTarget(); }
|
||||
|
||||
SkString dumpInfo() const override {
|
||||
SkString string;
|
||||
|
@ -26,6 +26,7 @@ public:
|
||||
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;
|
||||
|
@ -63,6 +63,11 @@ public:
|
||||
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());
|
||||
|
@ -31,6 +31,7 @@ public:
|
||||
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;
|
||||
|
@ -232,8 +232,8 @@ GrGLGpu::GrGLGpu(GrGLContext* ctx, GrContext* context)
|
||||
if (this->glCaps().shaderCaps()->pathRenderingSupport()) {
|
||||
fPathRendering.reset(new GrGLPathRendering(this));
|
||||
}
|
||||
|
||||
this->createCopyPrograms();
|
||||
fWireRectProgram.fProgram = 0;
|
||||
}
|
||||
|
||||
GrGLGpu::~GrGLGpu() {
|
||||
@ -258,10 +258,19 @@ GrGLGpu::~GrGLGpu() {
|
||||
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 +285,8 @@ void GrGLGpu::contextAbandoned() {
|
||||
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 +3027,6 @@ bool GrGLGpu::onCopySurface(GrSurface* dst,
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void GrGLGpu::createCopyPrograms() {
|
||||
for (size_t i = 0; i < SK_ARRAY_COUNT(fCopyPrograms); ++i) {
|
||||
fCopyPrograms[i].fProgram = 0;
|
||||
@ -3037,7 +3047,7 @@ void GrGLGpu::createCopyPrograms() {
|
||||
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 +3143,160 @@ void GrGLGpu::createCopyPrograms() {
|
||||
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 +3321,7 @@ void GrGLGpu::copySurfaceAsDraw(GrSurface* dst,
|
||||
|
||||
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)
|
||||
|
@ -131,6 +131,8 @@ public:
|
||||
|
||||
void resetShaderCacheForTesting() const override;
|
||||
|
||||
void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) override;
|
||||
|
||||
private:
|
||||
GrGLGpu(GrGLContext* ctx, GrContext* context);
|
||||
|
||||
@ -328,6 +330,8 @@ private:
|
||||
SkAutoTUnref<GrGLContext> fGLContext;
|
||||
|
||||
void createCopyPrograms();
|
||||
void createWireRectProgram();
|
||||
void createUnitRectBuffer();
|
||||
|
||||
// GL program-related state
|
||||
ProgramCache* fProgramCache;
|
||||
@ -505,6 +509,13 @@ private:
|
||||
} 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;
|
||||
|
Loading…
Reference in New Issue
Block a user