From 9b62aa156bcf1db6f11af9302bf8bb8ef2567142 Mon Sep 17 00:00:00 2001 From: "commit-bot@chromium.org" Date: Tue, 25 Mar 2014 11:59:40 +0000 Subject: [PATCH] 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 --- src/gpu/GrDrawTarget.cpp | 30 +++++++++++ src/gpu/GrDrawTarget.h | 28 +++++++++- src/gpu/GrGpu.cpp | 16 ++++++ src/gpu/GrGpu.h | 6 +++ src/gpu/GrInOrderDrawBuffer.cpp | 61 ++++++++++++++++++++- src/gpu/GrInOrderDrawBuffer.h | 23 +++++++- src/gpu/gl/GrGpuGL.cpp | 93 +++++++++++++++++++++++++++++++++ src/gpu/gl/GrGpuGL.h | 3 ++ src/gpu/gl/GrGpuGL_program.cpp | 3 +- 9 files changed, 258 insertions(+), 5 deletions(-) diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index fb4c9a1509..3502542079 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -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); diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h index 19ce16f64a..03e603a5b5 100644 --- a/src/gpu/GrDrawTarget.h +++ b/src/gpu/GrDrawTarget.h @@ -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; diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp index 3a342b6137..328b40ed83 100644 --- a/src/gpu/GrGpu.cpp +++ b/src/gpu/GrGpu.cpp @@ -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(); diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h index 00791be054..e8ccc26b1d 100644 --- a/src/gpu/GrGpu.h +++ b/src/gpu/GrGpu.h @@ -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(); diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp index 44bf4edab7..d9cbc914b0 100644 --- a/src/gpu/GrInOrderDrawBuffer.cpp +++ b/src/gpu/GrInOrderDrawBuffer.cpp @@ -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(); } diff --git a/src/gpu/GrInOrderDrawBuffer.h b/src/gpu/GrInOrderDrawBuffer.h index 60e01f01b9..4576484181 100644 --- a/src/gpu/GrInOrderDrawBuffer.h +++ b/src/gpu/GrInOrderDrawBuffer.h @@ -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 fCmds; GrSTAllocator fDraws; - GrSTAllocator fStencilPaths; - GrSTAllocator fDrawPaths; + GrSTAllocator fStencilPaths; + GrSTAllocator fDrawPath; + GrSTAllocator fDrawPaths; GrSTAllocator fStates; GrSTAllocator fClears; GrSTAllocator fCopySurfaces; diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp index 5fb268863b..0aa072cd84 100644 --- a/src/gpu/gl/GrGpuGL.cpp +++ b/src/gpu/gl/GrGpuGL.cpp @@ -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(transformData.get()); + GrGLuint* pathIDs = reinterpret_cast(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(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(target); if (rt->needsResolve()) { diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h index 05802550af..9c823f90ab 100644 --- a/src/gpu/gl/GrGpuGL.h +++ b/src/gpu/gl/GrGpuGL.h @@ -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, diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp index 50ecf0b332..10f10393e3 100644 --- a/src/gpu/gl/GrGpuGL_program.cpp +++ b/src/gpu/gl/GrGpuGL_program.cpp @@ -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();