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:
parent
5c260f41af
commit
9b62aa156b
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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()) {
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user