Make it possible to draw multiple paths at once to a draw target

Add interface to draw multiple paths in a single "command" to a draw
target. Implement this interface in GrGpuGL with NVPR "instanced"
calls.

The instanced calls accept list of paths and list of transformations as
their parameters. The transformations are at this moment expected to be
2d affine transformations, as the functions are called only for text
rendering.

This will be used when drawing fonts. Later it can be maybe be used in
GrInOrderDrawBuffer to aggregate many draw calls into one instanced draw
call, similar to drawing rects.

R=jvanverth@google.com, bsalomon@google.com

Author: kkinnunen@nvidia.com

Review URL: https://codereview.chromium.org/209413006

git-svn-id: http://skia.googlecode.com/svn/trunk@13930 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2014-03-25 11:59:40 +00:00
parent 5c260f41af
commit 9b62aa156b
9 changed files with 258 additions and 5 deletions

View File

@ -548,6 +548,36 @@ void GrDrawTarget::drawPath(const GrPath* path, SkPath::FillType fill) {
this->onDrawPath(path, fill, dstCopy.texture() ? &dstCopy : NULL);
}
void GrDrawTarget::drawPaths(size_t pathCount, const GrPath** paths,
const SkMatrix* transforms,
SkPath::FillType fill, SkStrokeRec::Style stroke) {
SkASSERT(pathCount > 0);
SkASSERT(NULL != paths);
SkASSERT(NULL != paths[0]);
SkASSERT(this->caps()->pathRenderingSupport());
SkASSERT(!SkPath::IsInverseFillType(fill));
const GrDrawState* drawState = &getDrawState();
SkRect devBounds;
for (size_t i = 0; i < pathCount; ++i) {
SkRect mappedPathBounds;
transforms[i].mapRect(&mappedPathBounds, paths[i]->getBounds());
devBounds.join(mappedPathBounds);
}
SkMatrix viewM = drawState->getViewMatrix();
viewM.mapRect(&devBounds);
GrDeviceCoordTexture dstCopy;
if (!this->setupDstReadIfNecessary(&dstCopy, &devBounds)) {
return;
}
this->onDrawPaths(pathCount, paths, transforms, fill, stroke,
dstCopy.texture() ? &dstCopy : NULL);
}
void GrDrawTarget::instantGpuTraceEvent(const char* marker) {
if (this->caps()->gpuTracingSupport()) {
this->onInstantGpuTraceEvent(marker);

View File

@ -15,6 +15,7 @@
#include "SkClipStack.h"
#include "SkMatrix.h"
#include "SkPath.h"
#include "SkStrokeRec.h"
#include "SkTArray.h"
#include "SkTLazy.h"
#include "SkTypes.h"
@ -24,7 +25,6 @@ class GrClipData;
class GrDrawTargetCaps;
class GrPath;
class GrVertexBuffer;
class SkStrokeRec;
class GrDrawTarget : public SkRefCnt {
protected:
@ -343,6 +343,19 @@ public:
*/
void drawPath(const GrPath*, SkPath::FillType fill);
/**
* Draws many paths. It will respect the HW
* antialias flag on the draw state (if possible in the 3D API).
*
* @param transforms array of 2d affine transformations, one for each path.
* @param fill the fill type for drawing all the paths. Fill must not be a
* hairline.
* @param stroke the stroke for drawing all the paths.
*/
void drawPaths(size_t pathCount, const GrPath** paths,
const SkMatrix* transforms, SkPath::FillType fill,
SkStrokeRec::Style stroke);
/**
* Helper function for drawing rects. It performs a geometry src push and pop
* and thus will finalize any reserved geometry.
@ -487,6 +500,16 @@ public:
this->onDrawPath(path, fill, dstCopy);
}
/**
* For subclass internal use to invoke a call to onDrawPaths().
*/
void executeDrawPaths(size_t pathCount, const GrPath** paths,
const SkMatrix* transforms, SkPath::FillType fill,
SkStrokeRec::Style stroke,
const GrDeviceCoordTexture* dstCopy) {
this->onDrawPaths(pathCount, paths, transforms, fill, stroke, dstCopy);
}
////////////////////////////////////////////////////////////////////////////
/**
@ -867,6 +890,9 @@ private:
virtual void onStencilPath(const GrPath*, SkPath::FillType) = 0;
virtual void onDrawPath(const GrPath*, SkPath::FillType,
const GrDeviceCoordTexture* dstCopy) = 0;
virtual void onDrawPaths(size_t, const GrPath**, const SkMatrix*,
SkPath::FillType, SkStrokeRec::Style,
const GrDeviceCoordTexture* dstCopy) = 0;
virtual void onInstantGpuTraceEvent(const char* marker) = 0;
virtual void onPushGpuTraceEvent(const char* marker) = 0;

View File

@ -409,6 +409,22 @@ void GrGpu::onDrawPath(const GrPath* path, SkPath::FillType fill,
this->onGpuDrawPath(path, fill);
}
void GrGpu::onDrawPaths(size_t pathCount, const GrPath** paths,
const SkMatrix* transforms, SkPath::FillType fill,
SkStrokeRec::Style style,
const GrDeviceCoordTexture* dstCopy) {
this->handleDirtyContext();
drawState()->setDefaultVertexAttribs();
GrDrawState::AutoRestoreEffects are;
if (!this->setupClipAndFlushState(kDrawPaths_DrawType, dstCopy, &are, NULL)) {
return;
}
this->onGpuDrawPaths(pathCount, paths, transforms, fill, style);
}
void GrGpu::finalizeReservedVertices() {
SkASSERT(NULL != fVertexPool);
fVertexPool->unlock();

View File

@ -337,6 +337,7 @@ protected:
kDrawTriangles_DrawType,
kStencilPath_DrawType,
kDrawPath_DrawType,
kDrawPaths_DrawType,
};
DrawType PrimTypeToDrawType(GrPrimitiveType type) {
@ -438,6 +439,8 @@ private:
// overridden by backend-specific derived class to perform the path stenciling.
virtual void onGpuStencilPath(const GrPath*, SkPath::FillType) = 0;
virtual void onGpuDrawPath(const GrPath*, SkPath::FillType) = 0;
virtual void onGpuDrawPaths(size_t, const GrPath**, const SkMatrix*,
SkPath::FillType, SkStrokeRec::Style) = 0;
// overridden by backend-specific derived class to perform flush
virtual void onForceRenderTargetFlush() = 0;
@ -483,6 +486,9 @@ private:
virtual void onStencilPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
virtual void onDrawPath(const GrPath*, SkPath::FillType,
const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
virtual void onDrawPaths(size_t, const GrPath**, const SkMatrix*,
SkPath::FillType, SkStrokeRec::Style,
const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
// readies the pools to provide vertex/index data.
void prepareVertexPool();

View File

@ -386,6 +386,16 @@ void GrInOrderDrawBuffer::onDraw(const DrawInfo& info) {
GrInOrderDrawBuffer::StencilPath::StencilPath() {}
GrInOrderDrawBuffer::DrawPath::DrawPath() {}
GrInOrderDrawBuffer::DrawPaths::DrawPaths() {}
GrInOrderDrawBuffer::DrawPaths::~DrawPaths() {
if (fTransforms) {
SkDELETE_ARRAY(fTransforms);
}
for (size_t i = 0; i < fPathCount; ++i) {
fPaths[i]->unref();
}
SkDELETE_ARRAY(fPaths);
}
void GrInOrderDrawBuffer::onStencilPath(const GrPath* path, SkPath::FillType fill) {
if (this->needsNewClip()) {
@ -419,6 +429,38 @@ void GrInOrderDrawBuffer::onDrawPath(const GrPath* path,
}
}
void GrInOrderDrawBuffer::onDrawPaths(size_t pathCount, const GrPath** paths,
const SkMatrix* transforms,
SkPath::FillType fill,
SkStrokeRec::Style stroke,
const GrDeviceCoordTexture* dstCopy) {
SkASSERT(pathCount);
if (this->needsNewClip()) {
this->recordClip();
}
if (this->needsNewState()) {
this->recordState();
}
DrawPaths* dp = this->recordDrawPaths();
dp->fPathCount = pathCount;
dp->fPaths = SkNEW_ARRAY(const GrPath*, pathCount);
memcpy(dp->fPaths, paths, sizeof(GrPath*) * pathCount);
for (size_t i = 0; i < pathCount; ++i) {
dp->fPaths[i]->ref();
}
dp->fTransforms = SkNEW_ARRAY(SkMatrix, pathCount);
memcpy(dp->fTransforms, transforms, sizeof(SkMatrix) * pathCount);
dp->fFill = fill;
dp->fStroke = stroke;
if (NULL != dstCopy) {
dp->fDstCopy = *dstCopy;
}
}
void GrInOrderDrawBuffer::clear(const SkIRect* rect, GrColor color,
bool canIgnoreRect, GrRenderTarget* renderTarget) {
SkIRect r;
@ -467,6 +509,7 @@ void GrInOrderDrawBuffer::reset() {
fCmds.reset();
fDraws.reset();
fStencilPaths.reset();
fDrawPath.reset();
fDrawPaths.reset();
fStates.reset();
fClears.reset();
@ -513,6 +556,7 @@ void GrInOrderDrawBuffer::flush() {
int currDraw = 0;
int currStencilPath = 0;
int currDrawPath = 0;
int currDrawPaths = 0;
int currCopySurface = 0;
for (int c = 0; c < numCmds; ++c) {
@ -535,12 +579,22 @@ void GrInOrderDrawBuffer::flush() {
break;
}
case kDrawPath_Cmd: {
const DrawPath& cp = fDrawPaths[currDrawPath];
const DrawPath& cp = fDrawPath[currDrawPath];
fDstGpu->executeDrawPath(cp.fPath.get(), cp.fFill,
NULL != cp.fDstCopy.texture() ? &cp.fDstCopy : NULL);
++currDrawPath;
break;
}
case kDrawPaths_Cmd: {
DrawPaths& dp = fDrawPaths[currDrawPaths];
const GrDeviceCoordTexture* dstCopy =
NULL != dp.fDstCopy.texture() ? &dp.fDstCopy : NULL;
fDstGpu->executeDrawPaths(dp.fPathCount, dp.fPaths,
dp.fTransforms, dp.fFill, dp.fStroke,
dstCopy);
++currDrawPaths;
break;
}
case kSetState_Cmd:
fStates[currState].restoreTo(&playbackState);
++currState;
@ -853,6 +907,11 @@ GrInOrderDrawBuffer::StencilPath* GrInOrderDrawBuffer::recordStencilPath() {
GrInOrderDrawBuffer::DrawPath* GrInOrderDrawBuffer::recordDrawPath() {
fCmds.push_back(kDrawPath_Cmd);
return &fDrawPath.push_back();
}
GrInOrderDrawBuffer::DrawPaths* GrInOrderDrawBuffer::recordDrawPaths() {
fCmds.push_back(kDrawPaths_Cmd);
return &fDrawPaths.push_back();
}

View File

@ -88,6 +88,7 @@ private:
kClear_Cmd = 5,
kCopySurface_Cmd = 6,
kDrawPath_Cmd = 7,
kDrawPaths_Cmd = 8,
};
class DrawRecord : public DrawInfo {
@ -112,6 +113,18 @@ private:
GrDeviceCoordTexture fDstCopy;
};
struct DrawPaths : public ::SkNoncopyable {
DrawPaths();
~DrawPaths();
size_t fPathCount;
const GrPath** fPaths;
SkMatrix* fTransforms;
SkPath::FillType fFill;
SkStrokeRec::Style fStroke;
GrDeviceCoordTexture fDstCopy;
};
struct Clear : public ::SkNoncopyable {
Clear() : fRenderTarget(NULL) {}
~Clear() { SkSafeUnref(fRenderTarget); }
@ -139,6 +152,9 @@ private:
virtual void onStencilPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
virtual void onDrawPath(const GrPath*, SkPath::FillType,
const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
virtual void onDrawPaths(size_t, const GrPath**, const SkMatrix*,
SkPath::FillType, SkStrokeRec::Style,
const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
virtual bool onReserveVertexSpace(size_t vertexSize,
int vertexCount,
@ -188,6 +204,7 @@ private:
DrawRecord* recordDraw(const DrawInfo&);
StencilPath* recordStencilPath();
DrawPath* recordDrawPath();
DrawPaths* recordDrawPaths();
Clear* recordClear();
CopySurface* recordCopySurface();
@ -197,6 +214,7 @@ private:
kDrawPreallocCnt = 8,
kStencilPathPreallocCnt = 8,
kDrawPathPreallocCnt = 8,
kDrawPathsPreallocCnt = 8,
kStatePreallocCnt = 8,
kClipPreallocCnt = 8,
kClearPreallocCnt = 4,
@ -206,8 +224,9 @@ private:
SkSTArray<kCmdPreallocCnt, uint8_t, true> fCmds;
GrSTAllocator<kDrawPreallocCnt, DrawRecord> fDraws;
GrSTAllocator<kStatePreallocCnt, StencilPath> fStencilPaths;
GrSTAllocator<kStatePreallocCnt, DrawPath> fDrawPaths;
GrSTAllocator<kStencilPathPreallocCnt, StencilPath> fStencilPaths;
GrSTAllocator<kDrawPathPreallocCnt, DrawPath> fDrawPath;
GrSTAllocator<kDrawPathsPreallocCnt, DrawPaths> fDrawPaths;
GrSTAllocator<kStatePreallocCnt, GrDrawState::DeferredState> fStates;
GrSTAllocator<kClearPreallocCnt, Clear> fClears;
GrSTAllocator<kCopySurfacePreallocCnt, CopySurface> fCopySurfaces;

View File

@ -1695,6 +1695,99 @@ void GrGpuGL::onGpuDrawPath(const GrPath* path, SkPath::FillType fill) {
}
}
void GrGpuGL::onGpuDrawPaths(size_t pathCount, const GrPath** paths,
const SkMatrix* transforms,
SkPath::FillType fill,
SkStrokeRec::Style stroke) {
SkASSERT(this->caps()->pathRenderingSupport());
SkASSERT(NULL != this->drawState()->getRenderTarget());
SkASSERT(NULL != this->drawState()->getRenderTarget()->getStencilBuffer());
SkASSERT(!fCurrentProgram->hasVertexShader());
SkASSERT(stroke != SkStrokeRec::kHairline_Style);
SkAutoMalloc pathData(pathCount * sizeof(GrGLuint));
SkAutoMalloc transformData(pathCount * sizeof(GrGLfloat) * 6);
GrGLfloat* transformValues =
reinterpret_cast<GrGLfloat*>(transformData.get());
GrGLuint* pathIDs = reinterpret_cast<GrGLuint*>(pathData.get());
for (size_t i = 0; i < pathCount; ++i) {
SkASSERT(transforms[i].asAffine(NULL));
const SkMatrix& m = transforms[i];
transformValues[i * 6] = m.getScaleX();
transformValues[i * 6 + 1] = m.getSkewY();
transformValues[i * 6 + 2] = m.getSkewX();
transformValues[i * 6 + 3] = m.getScaleY();
transformValues[i * 6 + 4] = m.getTranslateX();
transformValues[i * 6 + 5] = m.getTranslateY();
pathIDs[i] = static_cast<const GrGLPath*>(paths[i])->pathID();
}
flushPathStencilSettings(fill);
SkPath::FillType nonInvertedFill =
SkPath::ConvertToNonInverseFillType(fill);
SkASSERT(!fHWPathStencilSettings.isTwoSided());
GrGLenum fillMode =
gr_stencil_op_to_gl_path_rendering_fill_mode(
fHWPathStencilSettings.passOp(GrStencilSettings::kFront_Face));
GrGLint writeMask =
fHWPathStencilSettings.writeMask(GrStencilSettings::kFront_Face);
bool doFill = stroke == SkStrokeRec::kFill_Style
|| stroke == SkStrokeRec::kStrokeAndFill_Style;
bool doStroke = stroke == SkStrokeRec::kStroke_Style
|| stroke == SkStrokeRec::kStrokeAndFill_Style;
if (doFill) {
GL_CALL(StencilFillPathInstanced(pathCount, GR_GL_UNSIGNED_INT,
pathIDs, 0,
fillMode, writeMask,
GR_GL_AFFINE_2D, transformValues));
}
if (doStroke) {
GL_CALL(StencilStrokePathInstanced(pathCount, GR_GL_UNSIGNED_INT,
pathIDs, 0,
0xffff, writeMask,
GR_GL_AFFINE_2D, transformValues));
}
if (nonInvertedFill == fill) {
if (doStroke) {
GL_CALL(CoverStrokePathInstanced(
pathCount, GR_GL_UNSIGNED_INT, pathIDs, 0,
GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
GR_GL_AFFINE_2D, transformValues));
} else {
GL_CALL(CoverFillPathInstanced(
pathCount, GR_GL_UNSIGNED_INT, pathIDs, 0,
GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES,
GR_GL_AFFINE_2D, transformValues));
}
} else {
GrDrawState* drawState = this->drawState();
GrDrawState::AutoViewMatrixRestore avmr;
SkRect bounds = SkRect::MakeLTRB(0, 0,
SkIntToScalar(drawState->getRenderTarget()->width()),
SkIntToScalar(drawState->getRenderTarget()->height()));
SkMatrix vmi;
// mapRect through persp matrix may not be correct
if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) {
vmi.mapRect(&bounds);
// theoretically could set bloat = 0, instead leave it because of matrix inversion
// precision.
SkScalar bloat = drawState->getViewMatrix().getMaxStretch() * SK_ScalarHalf;
bounds.outset(bloat, bloat);
} else {
avmr.setIdentity(drawState);
}
this->drawSimpleRect(bounds, NULL);
}
}
void GrGpuGL::onResolveRenderTarget(GrRenderTarget* target) {
GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(target);
if (rt->needsResolve()) {

View File

@ -156,6 +156,9 @@ private:
virtual void onGpuStencilPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
virtual void onGpuDrawPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
virtual void onGpuDrawPaths(size_t, const GrPath**, const SkMatrix*,
SkPath::FillType,
SkStrokeRec::Style) SK_OVERRIDE;
virtual void clearStencil() SK_OVERRIDE;
virtual void clearStencilClip(const SkIRect& rect,

View File

@ -251,7 +251,8 @@ bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstC
return false;
}
SkASSERT(kDrawPath_DrawType != type || !fCurrentProgram->hasVertexShader());
SkASSERT((kDrawPath_DrawType != type && kDrawPaths_DrawType != type)
|| !fCurrentProgram->hasVertexShader());
fCurrentProgram.get()->ref();