diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h index 2f649966b5..ee1632160a 100644 --- a/gpu/include/GrContext.h +++ b/gpu/include/GrContext.h @@ -16,7 +16,6 @@ // remove. #include "GrRenderTarget.h" -class GrDefaultPathRenderer; class GrDrawTarget; class GrFontCache; class GrGpu; @@ -25,6 +24,7 @@ class GrIndexBuffer; class GrIndexBufferAllocPool; class GrInOrderDrawBuffer; class GrPathRenderer; +class GrPathRendererChain; class GrResourceEntry; class GrResourceCache; class GrStencilBuffer; @@ -556,8 +556,7 @@ private: GrResourceCache* fTextureCache; GrFontCache* fFontCache; - GrPathRenderer* fCustomPathRenderer; - GrDefaultPathRenderer* fDefaultPathRenderer; + GrPathRendererChain* fPathRendererChain; GrVertexBufferAllocPool* fDrawBufferVBAllocPool; GrIndexBufferAllocPool* fDrawBufferIBAllocPool; @@ -592,9 +591,9 @@ private: GrDrawTarget* prepareToDraw(const GrPaint& paint, DrawCategory drawType); - void drawClipIntoStencil(); - - GrPathRenderer* getPathRenderer(const GrPath&, GrPathFill); + GrPathRenderer* getPathRenderer(const GrDrawTarget* target, + const GrPath& path, + GrPathFill fill); struct OffscreenRecord; diff --git a/gpu/src/GrAddPathRenderers_none.cpp b/gpu/src/GrAddPathRenderers_none.cpp new file mode 100644 index 0000000000..46855db7ff --- /dev/null +++ b/gpu/src/GrAddPathRenderers_none.cpp @@ -0,0 +1,15 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "GrPathRenderer.h" + + +void GrPathRenderer::AddPathRenderers(GrContext*, + GrPathRendererChain::UsageFlags, + GrPathRendererChain*) {} diff --git a/gpu/src/GrAddPathRenderers_tesselated.cpp b/gpu/src/GrAddPathRenderers_tesselated.cpp new file mode 100644 index 0000000000..a1cde13155 --- /dev/null +++ b/gpu/src/GrAddPathRenderers_tesselated.cpp @@ -0,0 +1,17 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "GrTesselatedPathRenderer.h" + + +void GrPathRenderer::AddPathRenderers(GrContext*, + GrPathRendererChain::UsageFlags flags, + GrPathRendererChain* chain) { + chain->addPathRenderer(new GrTesselatedPathRenderer())->unref(); +} diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp index b3e902d798..506cb9f365 100644 --- a/gpu/src/GrContext.cpp +++ b/gpu/src/GrContext.cpp @@ -61,11 +61,11 @@ GrContext::~GrContext() { delete fDrawBuffer; delete fDrawBufferVBAllocPool; delete fDrawBufferIBAllocPool; - GrSafeUnref(fDefaultPathRenderer); - GrSafeUnref(fCustomPathRenderer); + GrSafeUnref(fAAFillRectIndexBuffer); GrSafeUnref(fAAStrokeRectIndexBuffer); fGpu->unref(); + GrSafeUnref(fPathRendererChain); } void GrContext::contextLost() { @@ -78,6 +78,10 @@ void GrContext::contextDestroyed() { // don't try to free the resources in the API. fGpu->abandonResources(); + // a path renderer may be holding onto resources that + // are now unusable + GrSafeSetNull(fPathRendererChain); + delete fDrawBuffer; fDrawBuffer = NULL; @@ -103,6 +107,8 @@ void GrContext::freeGpuResources() { this->flush(); fTextureCache->removeAll(); fFontCache->freeAll(); + // a path renderer may be holding onto resources + GrSafeSetNull(fPathRendererChain); } //////////////////////////////////////////////////////////////////////////////// @@ -1411,7 +1417,12 @@ void GrContext::drawPath(const GrPaint& paint, const GrPath& path, GrPathFill fill, const GrPoint* translate) { GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory); - GrPathRenderer* pr = this->getPathRenderer(path, fill); + GrPathRenderer* pr = this->getPathRenderer(target, path, fill); + if (NULL == pr) { + GrPrintf("Unable to find path renderer compatible with path.\n"); + return; + } + GrPathRenderer::AutoClearPath arp(pr, target, &path, fill, translate); GrDrawTarget::StageBitfield stageMask = paint.getActiveStageMask(); @@ -1658,6 +1669,16 @@ GrDrawTarget* GrContext::prepareToDraw(const GrPaint& paint, return target; } +GrPathRenderer* GrContext::getPathRenderer(const GrDrawTarget* target, + const GrPath& path, + GrPathFill fill) { + if (NULL == fPathRendererChain) { + fPathRendererChain = + new GrPathRendererChain(this, GrPathRendererChain::kNone_UsageFlag); + } + return fPathRendererChain->getPathRenderer(target, path, fill); +} + //////////////////////////////////////////////////////////////////////////////// void GrContext::setRenderTarget(GrRenderTarget* target) { @@ -1712,11 +1733,7 @@ GrContext::GrContext(GrGpu* gpu) { fGpu->ref(); fGpu->setContext(this); - fDefaultPathRenderer = - new GrDefaultPathRenderer(gpu->supportsTwoSidedStencil(), - gpu->supportsStencilWrapOps()); - fCustomPathRenderer = GrPathRenderer::CreatePathRenderer(); - fGpu->setClipPathRenderer(fCustomPathRenderer); + fPathRendererChain = NULL; fTextureCache = new GrResourceCache(MAX_TEXTURE_CACHE_COUNT, MAX_TEXTURE_CACHE_BYTES); @@ -1780,17 +1797,6 @@ const GrIndexBuffer* GrContext::getQuadIndexBuffer() const { return fGpu->getQuadIndexBuffer(); } -GrPathRenderer* GrContext::getPathRenderer(const GrPath& path, - GrPathFill fill) { - if (NULL != fCustomPathRenderer && - fCustomPathRenderer->canDrawPath(path, fill)) { - return fCustomPathRenderer; - } else { - GrAssert(fDefaultPathRenderer->canDrawPath(path, fill)); - return fDefaultPathRenderer; - } -} - void GrContext::convolveInX(GrTexture* texture, const SkRect& rect, const float* kernel, diff --git a/gpu/src/GrCreatePathRenderer_none.cpp b/gpu/src/GrCreatePathRenderer_none.cpp deleted file mode 100644 index 5072afa0ab..0000000000 --- a/gpu/src/GrCreatePathRenderer_none.cpp +++ /dev/null @@ -1,13 +0,0 @@ - -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "GrPathRenderer.h" - - -GrPathRenderer* GrPathRenderer::CreatePathRenderer() { return NULL; } diff --git a/gpu/src/GrCreatePathRenderer_tesselated.cpp b/gpu/src/GrCreatePathRenderer_tesselated.cpp deleted file mode 100644 index 38d7aa260e..0000000000 --- a/gpu/src/GrCreatePathRenderer_tesselated.cpp +++ /dev/null @@ -1,13 +0,0 @@ - -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "GrTesselatedPathRenderer.h" - - -GrPathRenderer* GrPathRenderer::CreatePathRenderer() { return new GrTesselatedPathRenderer(); } diff --git a/gpu/src/GrDefaultPathRenderer.cpp b/gpu/src/GrDefaultPathRenderer.cpp new file mode 100644 index 0000000000..b8b7f6253c --- /dev/null +++ b/gpu/src/GrDefaultPathRenderer.cpp @@ -0,0 +1,562 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrDefaultPathRenderer.h" + +#include "GrContext.h" +#include "GrPathUtils.h" +#include "SkTrace.h" + + +GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport, + bool stencilWrapOpsSupport) + : fSeparateStencil(separateStencilSupport) + , fStencilWrapOps(stencilWrapOpsSupport) + , fSubpathCount(0) + , fSubpathVertCount(0) + , fPreviousSrcTol(-GR_Scalar1) + , fPreviousStages(-1) { + fTarget = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// Stencil rules for paths + +////// Even/Odd + +static const GrStencilSettings gEOStencilPass = { + kInvert_StencilOp, kInvert_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff +}; + +// ok not to check clip b/c stencil pass only wrote inside clip +static const GrStencilSettings gEOColorPass = { + kZero_StencilOp, kZero_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kNotEqual_StencilFunc, kNotEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +// have to check clip b/c outside clip will always be zero. +static const GrStencilSettings gInvEOColorPass = { + kZero_StencilOp, kZero_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +////// Winding + +// when we have separate stencil we increment front faces / decrement back faces +// when we don't have wrap incr and decr we use the stencil test to simulate +// them. + +static const GrStencilSettings gWindStencilSeparateWithWrap = { + kIncWrap_StencilOp, kDecWrap_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff +}; + +// if inc'ing the max value, invert to make 0 +// if dec'ing zero invert to make all ones. +// we can't avoid touching the stencil on both passing and +// failing, so we can't resctrict ourselves to the clip. +static const GrStencilSettings gWindStencilSeparateNoWrap = { + kInvert_StencilOp, kInvert_StencilOp, + kIncClamp_StencilOp, kDecClamp_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0x0, + 0xffffffff, 0xffffffff +}; + +// When there are no separate faces we do two passes to setup the winding rule +// stencil. First we draw the front faces and inc, then we draw the back faces +// and dec. These are same as the above two split into the incrementing and +// decrementing passes. +static const GrStencilSettings gWindSingleStencilWithWrapInc = { + kIncWrap_StencilOp, kIncWrap_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff +}; +static const GrStencilSettings gWindSingleStencilWithWrapDec = { + kDecWrap_StencilOp, kDecWrap_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff +}; +static const GrStencilSettings gWindSingleStencilNoWrapInc = { + kInvert_StencilOp, kInvert_StencilOp, + kIncClamp_StencilOp, kIncClamp_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff +}; +static const GrStencilSettings gWindSingleStencilNoWrapDec = { + kInvert_StencilOp, kInvert_StencilOp, + kDecClamp_StencilOp, kDecClamp_StencilOp, + kEqual_StencilFunc, kEqual_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +static const GrStencilSettings gWindColorPass = { + kZero_StencilOp, kZero_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +static const GrStencilSettings gInvWindColorPass = { + kZero_StencilOp, kZero_StencilOp, + kZero_StencilOp, kZero_StencilOp, + kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +////// Normal render to stencil + +// Sometimes the default path renderer can draw a path directly to the stencil +// buffer without having to first resolve the interior / exterior. +static const GrStencilSettings gDirectToStencil = { + kZero_StencilOp, kZero_StencilOp, + kIncClamp_StencilOp, kIncClamp_StencilOp, + kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, + 0xffffffff, 0xffffffff, + 0x0, 0x0, + 0xffffffff, 0xffffffff +}; + +//////////////////////////////////////////////////////////////////////////////// +// Helpers for drawPath + +static GrConvexHint getConvexHint(const SkPath& path) { + return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint; +} + +#define STENCIL_OFF 0 // Always disable stencil (even when needed) + +static inline bool single_pass_path(const GrDrawTarget& target, + const GrPath& path, + GrPathFill fill) { +#if STENCIL_OFF + return true; +#else + if (kEvenOdd_PathFill == fill) { + GrConvexHint hint = getConvexHint(path); + return hint == kConvex_ConvexHint || + hint == kNonOverlappingConvexPieces_ConvexHint; + } else if (kWinding_PathFill == fill) { + GrConvexHint hint = getConvexHint(path); + return hint == kConvex_ConvexHint || + hint == kNonOverlappingConvexPieces_ConvexHint || + (hint == kSameWindingConvexPieces_ConvexHint && + target.canDisableBlend() && !target.isDitherState()); + + } + return false; +#endif +} + +bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target, + const GrPath& path, + GrPathFill fill) const { + return !single_pass_path(*target, path, fill); +} + +void GrDefaultPathRenderer::pathWillClear() { + fSubpathVertCount.realloc(0); + fTarget->resetVertexSource(); + if (fUseIndexedDraw) { + fTarget->resetIndexSource(); + } + fPreviousSrcTol = -GR_Scalar1; + fPreviousStages = -1; +} + +static inline void append_countour_edge_indices(GrPathFill fillType, + uint16_t fanCenterIdx, + uint16_t edgeV0Idx, + uint16_t** indices) { + // when drawing lines we're appending line segments along + // the contour. When applying the other fill rules we're + // drawing triangle fans around fanCenterIdx. + if (kHairLine_PathFill != fillType) { + *((*indices)++) = fanCenterIdx; + } + *((*indices)++) = edgeV0Idx; + *((*indices)++) = edgeV0Idx + 1; +} + +bool GrDefaultPathRenderer::createGeom(GrScalar srcSpaceTol, + GrDrawTarget::StageBitfield stages) { + { + SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom"); + + GrScalar srcSpaceTolSqd = GrMul(srcSpaceTol, srcSpaceTol); + int maxPts = GrPathUtils::worstCasePointCount(*fPath, &fSubpathCount, + srcSpaceTol); + + if (maxPts <= 0) { + return false; + } + if (maxPts > ((int)SK_MaxU16 + 1)) { + GrPrintf("Path not rendered, too many verts (%d)\n", maxPts); + return false; + } + + GrVertexLayout layout = 0; + for (int s = 0; s < GrDrawTarget::kNumStages; ++s) { + if ((1 << s) & stages) { + layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s); + } + } + + fUseIndexedDraw = fSubpathCount > 1; + + int maxIdxs = 0; + if (kHairLine_PathFill == fFill) { + if (fUseIndexedDraw) { + maxIdxs = 2 * maxPts; + fPrimitiveType = kLines_PrimitiveType; + } else { + fPrimitiveType = kLineStrip_PrimitiveType; + } + } else { + if (fUseIndexedDraw) { + maxIdxs = 3 * maxPts; + fPrimitiveType = kTriangles_PrimitiveType; + } else { + fPrimitiveType = kTriangleFan_PrimitiveType; + } + } + + GrPoint* base; + if (!fTarget->reserveVertexSpace(layout, maxPts, (void**)&base)) { + return false; + } + GrAssert(NULL != base); + GrPoint* vert = base; + + uint16_t* idxBase = NULL; + uint16_t* idx = NULL; + uint16_t subpathIdxStart = 0; + if (fUseIndexedDraw) { + if (!fTarget->reserveIndexSpace(maxIdxs, (void**)&idxBase)) { + fTarget->resetVertexSource(); + return false; + } + GrAssert(NULL != idxBase); + idx = idxBase; + } + + fSubpathVertCount.realloc(fSubpathCount); + + GrPoint pts[4]; + + bool first = true; + int subpath = 0; + + SkPath::Iter iter(*fPath, false); + + for (;;) { + GrPathCmd cmd = (GrPathCmd)iter.next(pts); + switch (cmd) { + case kMove_PathCmd: + if (!first) { + uint16_t currIdx = (uint16_t) (vert - base); + fSubpathVertCount[subpath] = currIdx - subpathIdxStart; + subpathIdxStart = currIdx; + ++subpath; + } + *vert = pts[0]; + vert++; + break; + case kLine_PathCmd: + if (fUseIndexedDraw) { + uint16_t prevIdx = (uint16_t)(vert - base) - 1; + append_countour_edge_indices(fFill, subpathIdxStart, + prevIdx, &idx); + } + *(vert++) = pts[1]; + break; + case kQuadratic_PathCmd: { + // first pt of quad is the pt we ended on in previous step + uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1; + uint16_t numPts = (uint16_t) + GrPathUtils::generateQuadraticPoints( + pts[0], pts[1], pts[2], + srcSpaceTolSqd, &vert, + GrPathUtils::quadraticPointCount(pts, srcSpaceTol)); + if (fUseIndexedDraw) { + for (uint16_t i = 0; i < numPts; ++i) { + append_countour_edge_indices(fFill, subpathIdxStart, + firstQPtIdx + i, &idx); + } + } + break; + } + case kCubic_PathCmd: { + // first pt of cubic is the pt we ended on in previous step + uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1; + uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( + pts[0], pts[1], pts[2], pts[3], + srcSpaceTolSqd, &vert, + GrPathUtils::cubicPointCount(pts, srcSpaceTol)); + if (fUseIndexedDraw) { + for (uint16_t i = 0; i < numPts; ++i) { + append_countour_edge_indices(fFill, subpathIdxStart, + firstCPtIdx + i, &idx); + } + } + break; + } + case kClose_PathCmd: + break; + case kEnd_PathCmd: + uint16_t currIdx = (uint16_t) (vert - base); + fSubpathVertCount[subpath] = currIdx - subpathIdxStart; + goto FINISHED; + } + first = false; + } +FINISHED: + GrAssert((vert - base) <= maxPts); + GrAssert((idx - idxBase) <= maxIdxs); + + fVertexCnt = vert - base; + fIndexCnt = idx - idxBase; + + if (fTranslate.fX || fTranslate.fY) { + int count = vert - base; + for (int i = 0; i < count; i++) { + base[i].offset(fTranslate.fX, fTranslate.fY); + } + } + } + // set these at the end so if we failed on first drawPath inside a + // setPath/clearPath block we won't assume geom was created on a subsequent + // drawPath in the same block. + fPreviousSrcTol = srcSpaceTol; + fPreviousStages = stages; + return true; +} + +void GrDefaultPathRenderer::onDrawPath(GrDrawTarget::StageBitfield stages, + bool stencilOnly) { + + SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath", + "points", SkStringPrintf("%i", path.countPoints()).c_str()); + + GrMatrix viewM = fTarget->getViewMatrix(); + // In order to tesselate the path we get a bound on how much the matrix can + // stretch when mapping to screen coordinates. + GrScalar stretch = viewM.getMaxStretch(); + bool useStretch = stretch > 0; + GrScalar tol = fCurveTolerance; + + if (!useStretch) { + // TODO: deal with perspective in some better way. + tol /= 10; + } else { + tol = GrScalarDiv(tol, stretch); + } + // FIXME: It's really dumb that we recreate the verts for a new vertex + // layout. We only do that because the GrDrawTarget API doesn't allow + // us to change the vertex layout after reserveVertexSpace(). We won't + // actually change the vertex data when the layout changes since all the + // stages reference the positions (rather than having separate tex coords) + // and we don't ever have per-vert colors. In practice our call sites + // won't change the stages in use inside a setPath / removePath pair. But + // it is a silly limitation of the GrDrawTarget design that should be fixed. + if (tol != fPreviousSrcTol || + stages != fPreviousStages) { + if (!this->createGeom(tol, stages)) { + return; + } + } + + GrAssert(NULL != fTarget); + GrDrawTarget::AutoStateRestore asr(fTarget); + bool colorWritesWereDisabled = fTarget->isColorWriteDisabled(); + // face culling doesn't make sense here + GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace()); + + int passCount = 0; + const GrStencilSettings* passes[3]; + GrDrawTarget::DrawFace drawFace[3]; + bool reverse = false; + bool lastPassIsBounds; + + if (kHairLine_PathFill == fFill) { + passCount = 1; + if (stencilOnly) { + passes[0] = &gDirectToStencil; + } else { + passes[0] = NULL; + } + lastPassIsBounds = false; + drawFace[0] = GrDrawTarget::kBoth_DrawFace; + } else { + if (single_pass_path(*fTarget, *fPath, fFill)) { + passCount = 1; + if (stencilOnly) { + passes[0] = &gDirectToStencil; + } else { + passes[0] = NULL; + } + drawFace[0] = GrDrawTarget::kBoth_DrawFace; + lastPassIsBounds = false; + } else { + switch (fFill) { + case kInverseEvenOdd_PathFill: + reverse = true; + // fallthrough + case kEvenOdd_PathFill: + passes[0] = &gEOStencilPass; + if (stencilOnly) { + passCount = 1; + lastPassIsBounds = false; + } else { + passCount = 2; + lastPassIsBounds = true; + if (reverse) { + passes[1] = &gInvEOColorPass; + } else { + passes[1] = &gEOColorPass; + } + } + drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace; + break; + + case kInverseWinding_PathFill: + reverse = true; + // fallthrough + case kWinding_PathFill: + if (fSeparateStencil) { + if (fStencilWrapOps) { + passes[0] = &gWindStencilSeparateWithWrap; + } else { + passes[0] = &gWindStencilSeparateNoWrap; + } + passCount = 2; + drawFace[0] = GrDrawTarget::kBoth_DrawFace; + } else { + if (fStencilWrapOps) { + passes[0] = &gWindSingleStencilWithWrapInc; + passes[1] = &gWindSingleStencilWithWrapDec; + } else { + passes[0] = &gWindSingleStencilNoWrapInc; + passes[1] = &gWindSingleStencilNoWrapDec; + } + // which is cw and which is ccw is arbitrary. + drawFace[0] = GrDrawTarget::kCW_DrawFace; + drawFace[1] = GrDrawTarget::kCCW_DrawFace; + passCount = 3; + } + if (stencilOnly) { + lastPassIsBounds = false; + --passCount; + } else { + lastPassIsBounds = true; + drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace; + if (reverse) { + passes[passCount-1] = &gInvWindColorPass; + } else { + passes[passCount-1] = &gWindColorPass; + } + } + break; + default: + GrAssert(!"Unknown path fFill!"); + return; + } + } + } + + { + SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath::renderPasses", + "verts", SkStringPrintf("%i", vert - base).c_str()); + for (int p = 0; p < passCount; ++p) { + fTarget->setDrawFace(drawFace[p]); + if (NULL != passes[p]) { + fTarget->setStencil(*passes[p]); + } + + if (lastPassIsBounds && (p == passCount-1)) { + if (!colorWritesWereDisabled) { + fTarget->disableState(GrDrawTarget::kNoColorWrites_StateBit); + } + GrRect bounds; + if (reverse) { + GrAssert(NULL != fTarget->getRenderTarget()); + // draw over the whole world. + bounds.setLTRB(0, 0, + GrIntToScalar(fTarget->getRenderTarget()->width()), + GrIntToScalar(fTarget->getRenderTarget()->height())); + GrMatrix vmi; + if (fTarget->getViewInverse(&vmi)) { + vmi.mapRect(&bounds); + } + } else { + bounds = fPath->getBounds(); + bounds.offset(fTranslate); + } + GrDrawTarget::AutoGeometryPush agp(fTarget); + fTarget->drawSimpleRect(bounds, NULL, stages); + } else { + if (passCount > 1) { + fTarget->enableState(GrDrawTarget::kNoColorWrites_StateBit); + } + if (fUseIndexedDraw) { + fTarget->drawIndexed(fPrimitiveType, 0, 0, + fVertexCnt, fIndexCnt); + } else { + int baseVertex = 0; + for (int sp = 0; sp < fSubpathCount; ++sp) { + fTarget->drawNonIndexed(fPrimitiveType, baseVertex, + fSubpathVertCount[sp]); + baseVertex += fSubpathVertCount[sp]; + } + } + } + } + } +} + +void GrDefaultPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) { + this->onDrawPath(stages, false); +} + +void GrDefaultPathRenderer::drawPathToStencil() { + GrAssert(kInverseEvenOdd_PathFill != fFill); + GrAssert(kInverseWinding_PathFill != fFill); + this->onDrawPath(0, true); +} diff --git a/gpu/src/GrDefaultPathRenderer.h b/gpu/src/GrDefaultPathRenderer.h new file mode 100644 index 0000000000..e11716ed61 --- /dev/null +++ b/gpu/src/GrDefaultPathRenderer.h @@ -0,0 +1,58 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrDefaultPathRenderer_DEFINED +#define GrDefaultPathRenderer_DEFINED + +#include "GrPathRenderer.h" +#include "SkTemplates.h" + +/** + * Subclass that renders the path using the stencil buffer to resolve fill + * rules (e.g. winding, even-odd) + */ +class GR_API GrDefaultPathRenderer : public GrPathRenderer { +public: + GrDefaultPathRenderer(bool separateStencilSupport, + bool stencilWrapOpsSupport); + + virtual bool canDrawPath(const SkPath& path, + GrPathFill fill) const { return true; } + + virtual bool requiresStencilPass(const GrDrawTarget* target, + const SkPath& path, + GrPathFill fill) const; + + virtual void drawPath(GrDrawTarget::StageBitfield stages); + virtual void drawPathToStencil(); + +protected: + virtual void pathWillClear(); + +private: + + void onDrawPath(GrDrawTarget::StageBitfield stages, bool stencilOnly); + + bool createGeom(GrScalar srcSpaceTol, + GrDrawTarget::StageBitfield stages); + + bool fSeparateStencil; + bool fStencilWrapOps; + + int fSubpathCount; + SkAutoSTMalloc<8, uint16_t> fSubpathVertCount; + int fIndexCnt; + int fVertexCnt; + GrScalar fPreviousSrcTol; + GrDrawTarget::StageBitfield fPreviousStages; + GrPrimitiveType fPrimitiveType; + bool fUseIndexedDraw; + + typedef GrPathRenderer INHERITED; +}; + +#endif diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp index cde851d49f..7744e6a129 100644 --- a/gpu/src/GrGpu.cpp +++ b/gpu/src/GrGpu.cpp @@ -40,8 +40,7 @@ GrGpu::GrGpu() , fGeomPoolStateStack(&fGeoSrcStateStackStorage) , fQuadIndexBuffer(NULL) , fUnitSquareVertexBuffer(NULL) - , fDefaultPathRenderer(NULL) - , fClientPathRenderer(NULL) + , fPathRendererChain(NULL) , fContextIsDirty(true) , fResourceHead(NULL) { @@ -62,8 +61,6 @@ GrGpu::GrGpu() GrGpu::~GrGpu() { this->releaseResources(); - GrSafeUnref(fDefaultPathRenderer); - GrSafeUnref(fClientPathRenderer); } void GrGpu::abandonResources() { @@ -81,6 +78,8 @@ void GrGpu::abandonResources() { fVertexPool = NULL; delete fIndexPool; fIndexPool = NULL; + // in case path renderer has any GrResources, start from scratch + GrSafeSetNull(fPathRendererChain); } void GrGpu::releaseResources() { @@ -98,6 +97,8 @@ void GrGpu::releaseResources() { fVertexPool = NULL; delete fIndexPool; fIndexPool = NULL; + // in case path renderer has any GrResources, start from scratch + GrSafeSetNull(fPathRendererChain); } void GrGpu::insertResource(GrResource* resource) { @@ -521,6 +522,11 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) { fill = NonInvertedFill(fill); clipPath = &clip.getPath(c); pr = this->getClipPathRenderer(*clipPath, fill); + if (NULL == pr) { + fClipInStencil = false; + fClip = clip; + return false; + } canRenderDirectToStencil = !pr->requiresStencilPass(this, *clipPath, fill); arp.set(pr, this, clipPath, fill, NULL); @@ -602,18 +608,12 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) { GrPathRenderer* GrGpu::getClipPathRenderer(const GrPath& path, GrPathFill fill) { - if (NULL != fClientPathRenderer && - fClientPathRenderer->canDrawPath(path, fill)) { - return fClientPathRenderer; - } else { - if (NULL == fDefaultPathRenderer) { - fDefaultPathRenderer = - new GrDefaultPathRenderer(this->supportsTwoSidedStencil(), - this->supportsStencilWrapOps()); - } - GrAssert(fDefaultPathRenderer->canDrawPath(path, fill)); - return fDefaultPathRenderer; + if (NULL == fPathRendererChain) { + fPathRendererChain = + new GrPathRendererChain(this->getContext(), + GrPathRendererChain::kNonAAOnly_UsageFlag); } + return fPathRendererChain->getPathRenderer(this, path, fill); } diff --git a/gpu/src/GrGpu.h b/gpu/src/GrGpu.h index d3c6c9d663..e1e85f773c 100644 --- a/gpu/src/GrGpu.h +++ b/gpu/src/GrGpu.h @@ -11,13 +11,14 @@ #define GrGpu_DEFINED #include "GrDrawTarget.h" -#include "GrPathRenderer.h" #include "GrRect.h" #include "GrRefCnt.h" #include "GrTexture.h" class GrContext; class GrIndexBufferAllocPool; +class GrPathRenderer; +class GrPathRendererChain; class GrResource; class GrStencilBuffer; class GrVertexBufferAllocPool; @@ -258,14 +259,6 @@ public: virtual void clear(const GrIRect* rect, GrColor color); - /** - * Installs a path renderer that will be used to draw paths that are - * part of the clip. - */ - void setClipPathRenderer(GrPathRenderer* pathRenderer) { - GrSafeAssign(fClientPathRenderer, pathRenderer); - } - /** * Returns an index buffer that can be used to render quads. * Six indices per quad: 0, 1, 2, 0, 2, 3, etc. @@ -520,8 +513,9 @@ private: mutable GrVertexBuffer* fUnitSquareVertexBuffer; // mutable so it can be // created on-demand - GrDefaultPathRenderer* fDefaultPathRenderer; - GrPathRenderer* fClientPathRenderer; + // must be instantiated after GrGpu object has been given its owning + // GrContext ptr. (GrGpu is constructed first then handed off to GrContext). + GrPathRendererChain* fPathRendererChain; bool fContextIsDirty; diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp index 26c92b8400..06a00e44b7 100644 --- a/gpu/src/GrPathRenderer.cpp +++ b/gpu/src/GrPathRenderer.cpp @@ -5,24 +5,15 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ + #include "GrPathRenderer.h" -#include "GrPoint.h" -#include "GrDrawTarget.h" -#include "GrPathUtils.h" -#include "GrTexture.h" - -#include "SkString.h" -#include "SkTemplates.h" -#include "SkTrace.h" - GrPathRenderer::GrPathRenderer() : fCurveTolerance (GR_Scalar1) , fPath(NULL) , fTarget(NULL) { } - void GrPathRenderer::setPath(GrDrawTarget* target, const SkPath* path, GrPathFill fill, @@ -49,553 +40,3 @@ void GrPathRenderer::clearPath() { fTarget = NULL; fPath = NULL; } - -//////////////////////////////////////////////////////////////////////////////// - -GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport, - bool stencilWrapOpsSupport) - : fSeparateStencil(separateStencilSupport) - , fStencilWrapOps(stencilWrapOpsSupport) - , fSubpathCount(0) - , fSubpathVertCount(0) - , fPreviousSrcTol(-GR_Scalar1) - , fPreviousStages(-1) { - fTarget = NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -// Stencil rules for paths - -////// Even/Odd - -static const GrStencilSettings gEOStencilPass = { - kInvert_StencilOp, kInvert_StencilOp, - kKeep_StencilOp, kKeep_StencilOp, - kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, - 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff -}; - -// ok not to check clip b/c stencil pass only wrote inside clip -static const GrStencilSettings gEOColorPass = { - kZero_StencilOp, kZero_StencilOp, - kZero_StencilOp, kZero_StencilOp, - kNotEqual_StencilFunc, kNotEqual_StencilFunc, - 0xffffffff, 0xffffffff, - 0x0, 0x0, - 0xffffffff, 0xffffffff -}; - -// have to check clip b/c outside clip will always be zero. -static const GrStencilSettings gInvEOColorPass = { - kZero_StencilOp, kZero_StencilOp, - kZero_StencilOp, kZero_StencilOp, - kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc, - 0xffffffff, 0xffffffff, - 0x0, 0x0, - 0xffffffff, 0xffffffff -}; - -////// Winding - -// when we have separate stencil we increment front faces / decrement back faces -// when we don't have wrap incr and decr we use the stencil test to simulate -// them. - -static const GrStencilSettings gWindStencilSeparateWithWrap = { - kIncWrap_StencilOp, kDecWrap_StencilOp, - kKeep_StencilOp, kKeep_StencilOp, - kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, - 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff -}; - -// if inc'ing the max value, invert to make 0 -// if dec'ing zero invert to make all ones. -// we can't avoid touching the stencil on both passing and -// failing, so we can't resctrict ourselves to the clip. -static const GrStencilSettings gWindStencilSeparateNoWrap = { - kInvert_StencilOp, kInvert_StencilOp, - kIncClamp_StencilOp, kDecClamp_StencilOp, - kEqual_StencilFunc, kEqual_StencilFunc, - 0xffffffff, 0xffffffff, - 0xffffffff, 0x0, - 0xffffffff, 0xffffffff -}; - -// When there are no separate faces we do two passes to setup the winding rule -// stencil. First we draw the front faces and inc, then we draw the back faces -// and dec. These are same as the above two split into the incrementing and -// decrementing passes. -static const GrStencilSettings gWindSingleStencilWithWrapInc = { - kIncWrap_StencilOp, kIncWrap_StencilOp, - kKeep_StencilOp, kKeep_StencilOp, - kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, - 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff -}; -static const GrStencilSettings gWindSingleStencilWithWrapDec = { - kDecWrap_StencilOp, kDecWrap_StencilOp, - kKeep_StencilOp, kKeep_StencilOp, - kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, - 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff -}; -static const GrStencilSettings gWindSingleStencilNoWrapInc = { - kInvert_StencilOp, kInvert_StencilOp, - kIncClamp_StencilOp, kIncClamp_StencilOp, - kEqual_StencilFunc, kEqual_StencilFunc, - 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff -}; -static const GrStencilSettings gWindSingleStencilNoWrapDec = { - kInvert_StencilOp, kInvert_StencilOp, - kDecClamp_StencilOp, kDecClamp_StencilOp, - kEqual_StencilFunc, kEqual_StencilFunc, - 0xffffffff, 0xffffffff, - 0x0, 0x0, - 0xffffffff, 0xffffffff -}; - -static const GrStencilSettings gWindColorPass = { - kZero_StencilOp, kZero_StencilOp, - kZero_StencilOp, kZero_StencilOp, - kNonZeroIfInClip_StencilFunc, kNonZeroIfInClip_StencilFunc, - 0xffffffff, 0xffffffff, - 0x0, 0x0, - 0xffffffff, 0xffffffff -}; - -static const GrStencilSettings gInvWindColorPass = { - kZero_StencilOp, kZero_StencilOp, - kZero_StencilOp, kZero_StencilOp, - kEqualIfInClip_StencilFunc, kEqualIfInClip_StencilFunc, - 0xffffffff, 0xffffffff, - 0x0, 0x0, - 0xffffffff, 0xffffffff -}; - -////// Normal render to stencil - -// Sometimes the default path renderer can draw a path directly to the stencil -// buffer without having to first resolve the interior / exterior. -static const GrStencilSettings gDirectToStencil = { - kZero_StencilOp, kZero_StencilOp, - kIncClamp_StencilOp, kIncClamp_StencilOp, - kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc, - 0xffffffff, 0xffffffff, - 0x0, 0x0, - 0xffffffff, 0xffffffff -}; - -//////////////////////////////////////////////////////////////////////////////// -// Helpers for drawPath - -static GrConvexHint getConvexHint(const SkPath& path) { - return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint; -} - -#define STENCIL_OFF 0 // Always disable stencil (even when needed) - -static inline bool single_pass_path(const GrDrawTarget& target, - const GrPath& path, - GrPathFill fill) { -#if STENCIL_OFF - return true; -#else - if (kEvenOdd_PathFill == fill) { - GrConvexHint hint = getConvexHint(path); - return hint == kConvex_ConvexHint || - hint == kNonOverlappingConvexPieces_ConvexHint; - } else if (kWinding_PathFill == fill) { - GrConvexHint hint = getConvexHint(path); - return hint == kConvex_ConvexHint || - hint == kNonOverlappingConvexPieces_ConvexHint || - (hint == kSameWindingConvexPieces_ConvexHint && - target.canDisableBlend() && !target.isDitherState()); - - } - return false; -#endif -} - -bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target, - const GrPath& path, - GrPathFill fill) const { - return !single_pass_path(*target, path, fill); -} - -void GrDefaultPathRenderer::pathWillClear() { - fSubpathVertCount.realloc(0); - fTarget->resetVertexSource(); - if (fUseIndexedDraw) { - fTarget->resetIndexSource(); - } - fPreviousSrcTol = -GR_Scalar1; - fPreviousStages = -1; -} - -static inline void append_countour_edge_indices(GrPathFill fillType, - uint16_t fanCenterIdx, - uint16_t edgeV0Idx, - uint16_t** indices) { - // when drawing lines we're appending line segments along - // the contour. When applying the other fill rules we're - // drawing triangle fans around fanCenterIdx. - if (kHairLine_PathFill != fillType) { - *((*indices)++) = fanCenterIdx; - } - *((*indices)++) = edgeV0Idx; - *((*indices)++) = edgeV0Idx + 1; -} - -bool GrDefaultPathRenderer::createGeom(GrScalar srcSpaceTol, - GrDrawTarget::StageBitfield stages) { - { - SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom"); - - GrScalar srcSpaceTolSqd = GrMul(srcSpaceTol, srcSpaceTol); - int maxPts = GrPathUtils::worstCasePointCount(*fPath, &fSubpathCount, - srcSpaceTol); - - if (maxPts <= 0) { - return false; - } - if (maxPts > ((int)SK_MaxU16 + 1)) { - GrPrintf("Path not rendered, too many verts (%d)\n", maxPts); - return false; - } - - GrVertexLayout layout = 0; - for (int s = 0; s < GrDrawTarget::kNumStages; ++s) { - if ((1 << s) & stages) { - layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s); - } - } - - fUseIndexedDraw = fSubpathCount > 1; - - int maxIdxs = 0; - if (kHairLine_PathFill == fFill) { - if (fUseIndexedDraw) { - maxIdxs = 2 * maxPts; - fPrimitiveType = kLines_PrimitiveType; - } else { - fPrimitiveType = kLineStrip_PrimitiveType; - } - } else { - if (fUseIndexedDraw) { - maxIdxs = 3 * maxPts; - fPrimitiveType = kTriangles_PrimitiveType; - } else { - fPrimitiveType = kTriangleFan_PrimitiveType; - } - } - - GrPoint* base; - if (!fTarget->reserveVertexSpace(layout, maxPts, (void**)&base)) { - return false; - } - GrAssert(NULL != base); - GrPoint* vert = base; - - uint16_t* idxBase = NULL; - uint16_t* idx = NULL; - uint16_t subpathIdxStart = 0; - if (fUseIndexedDraw) { - if (!fTarget->reserveIndexSpace(maxIdxs, (void**)&idxBase)) { - fTarget->resetVertexSource(); - return false; - } - GrAssert(NULL != idxBase); - idx = idxBase; - } - - fSubpathVertCount.realloc(fSubpathCount); - - GrPoint pts[4]; - - bool first = true; - int subpath = 0; - - SkPath::Iter iter(*fPath, false); - - for (;;) { - GrPathCmd cmd = (GrPathCmd)iter.next(pts); - switch (cmd) { - case kMove_PathCmd: - if (!first) { - uint16_t currIdx = (uint16_t) (vert - base); - fSubpathVertCount[subpath] = currIdx - subpathIdxStart; - subpathIdxStart = currIdx; - ++subpath; - } - *vert = pts[0]; - vert++; - break; - case kLine_PathCmd: - if (fUseIndexedDraw) { - uint16_t prevIdx = (uint16_t)(vert - base) - 1; - append_countour_edge_indices(fFill, subpathIdxStart, - prevIdx, &idx); - } - *(vert++) = pts[1]; - break; - case kQuadratic_PathCmd: { - // first pt of quad is the pt we ended on in previous step - uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1; - uint16_t numPts = (uint16_t) - GrPathUtils::generateQuadraticPoints( - pts[0], pts[1], pts[2], - srcSpaceTolSqd, &vert, - GrPathUtils::quadraticPointCount(pts, srcSpaceTol)); - if (fUseIndexedDraw) { - for (uint16_t i = 0; i < numPts; ++i) { - append_countour_edge_indices(fFill, subpathIdxStart, - firstQPtIdx + i, &idx); - } - } - break; - } - case kCubic_PathCmd: { - // first pt of cubic is the pt we ended on in previous step - uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1; - uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( - pts[0], pts[1], pts[2], pts[3], - srcSpaceTolSqd, &vert, - GrPathUtils::cubicPointCount(pts, srcSpaceTol)); - if (fUseIndexedDraw) { - for (uint16_t i = 0; i < numPts; ++i) { - append_countour_edge_indices(fFill, subpathIdxStart, - firstCPtIdx + i, &idx); - } - } - break; - } - case kClose_PathCmd: - break; - case kEnd_PathCmd: - uint16_t currIdx = (uint16_t) (vert - base); - fSubpathVertCount[subpath] = currIdx - subpathIdxStart; - goto FINISHED; - } - first = false; - } -FINISHED: - GrAssert((vert - base) <= maxPts); - GrAssert((idx - idxBase) <= maxIdxs); - - fVertexCnt = vert - base; - fIndexCnt = idx - idxBase; - - if (fTranslate.fX || fTranslate.fY) { - int count = vert - base; - for (int i = 0; i < count; i++) { - base[i].offset(fTranslate.fX, fTranslate.fY); - } - } - } - // set these at the end so if we failed on first drawPath inside a - // setPath/clearPath block we won't assume geom was created on a subsequent - // drawPath in the same block. - fPreviousSrcTol = srcSpaceTol; - fPreviousStages = stages; - return true; -} - -void GrDefaultPathRenderer::onDrawPath(GrDrawTarget::StageBitfield stages, - bool stencilOnly) { - - SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath", - "points", SkStringPrintf("%i", path.countPoints()).c_str()); - - GrMatrix viewM = fTarget->getViewMatrix(); - // In order to tesselate the path we get a bound on how much the matrix can - // stretch when mapping to screen coordinates. - GrScalar stretch = viewM.getMaxStretch(); - bool useStretch = stretch > 0; - GrScalar tol = fCurveTolerance; - - if (!useStretch) { - // TODO: deal with perspective in some better way. - tol /= 10; - } else { - tol = GrScalarDiv(tol, stretch); - } - // FIXME: It's really dumb that we recreate the verts for a new vertex - // layout. We only do that because the GrDrawTarget API doesn't allow - // us to change the vertex layout after reserveVertexSpace(). We won't - // actually change the vertex data when the layout changes since all the - // stages reference the positions (rather than having separate tex coords) - // and we don't ever have per-vert colors. In practice our call sites - // won't change the stages in use inside a setPath / removePath pair. But - // it is a silly limitation of the GrDrawTarget design that should be fixed. - if (tol != fPreviousSrcTol || - stages != fPreviousStages) { - if (!this->createGeom(tol, stages)) { - return; - } - } - - GrAssert(NULL != fTarget); - GrDrawTarget::AutoStateRestore asr(fTarget); - bool colorWritesWereDisabled = fTarget->isColorWriteDisabled(); - // face culling doesn't make sense here - GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace()); - - int passCount = 0; - const GrStencilSettings* passes[3]; - GrDrawTarget::DrawFace drawFace[3]; - bool reverse = false; - bool lastPassIsBounds; - - if (kHairLine_PathFill == fFill) { - passCount = 1; - if (stencilOnly) { - passes[0] = &gDirectToStencil; - } else { - passes[0] = NULL; - } - lastPassIsBounds = false; - drawFace[0] = GrDrawTarget::kBoth_DrawFace; - } else { - if (single_pass_path(*fTarget, *fPath, fFill)) { - passCount = 1; - if (stencilOnly) { - passes[0] = &gDirectToStencil; - } else { - passes[0] = NULL; - } - drawFace[0] = GrDrawTarget::kBoth_DrawFace; - lastPassIsBounds = false; - } else { - switch (fFill) { - case kInverseEvenOdd_PathFill: - reverse = true; - // fallthrough - case kEvenOdd_PathFill: - passes[0] = &gEOStencilPass; - if (stencilOnly) { - passCount = 1; - lastPassIsBounds = false; - } else { - passCount = 2; - lastPassIsBounds = true; - if (reverse) { - passes[1] = &gInvEOColorPass; - } else { - passes[1] = &gEOColorPass; - } - } - drawFace[0] = drawFace[1] = GrDrawTarget::kBoth_DrawFace; - break; - - case kInverseWinding_PathFill: - reverse = true; - // fallthrough - case kWinding_PathFill: - if (fSeparateStencil) { - if (fStencilWrapOps) { - passes[0] = &gWindStencilSeparateWithWrap; - } else { - passes[0] = &gWindStencilSeparateNoWrap; - } - passCount = 2; - drawFace[0] = GrDrawTarget::kBoth_DrawFace; - } else { - if (fStencilWrapOps) { - passes[0] = &gWindSingleStencilWithWrapInc; - passes[1] = &gWindSingleStencilWithWrapDec; - } else { - passes[0] = &gWindSingleStencilNoWrapInc; - passes[1] = &gWindSingleStencilNoWrapDec; - } - // which is cw and which is ccw is arbitrary. - drawFace[0] = GrDrawTarget::kCW_DrawFace; - drawFace[1] = GrDrawTarget::kCCW_DrawFace; - passCount = 3; - } - if (stencilOnly) { - lastPassIsBounds = false; - --passCount; - } else { - lastPassIsBounds = true; - drawFace[passCount-1] = GrDrawTarget::kBoth_DrawFace; - if (reverse) { - passes[passCount-1] = &gInvWindColorPass; - } else { - passes[passCount-1] = &gWindColorPass; - } - } - break; - default: - GrAssert(!"Unknown path fFill!"); - return; - } - } - } - - { - SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath::renderPasses", - "verts", SkStringPrintf("%i", vert - base).c_str()); - for (int p = 0; p < passCount; ++p) { - fTarget->setDrawFace(drawFace[p]); - if (NULL != passes[p]) { - fTarget->setStencil(*passes[p]); - } - - if (lastPassIsBounds && (p == passCount-1)) { - if (!colorWritesWereDisabled) { - fTarget->disableState(GrDrawTarget::kNoColorWrites_StateBit); - } - GrRect bounds; - if (reverse) { - GrAssert(NULL != fTarget->getRenderTarget()); - // draw over the whole world. - bounds.setLTRB(0, 0, - GrIntToScalar(fTarget->getRenderTarget()->width()), - GrIntToScalar(fTarget->getRenderTarget()->height())); - GrMatrix vmi; - if (fTarget->getViewInverse(&vmi)) { - vmi.mapRect(&bounds); - } - } else { - bounds = fPath->getBounds(); - bounds.offset(fTranslate); - } - GrDrawTarget::AutoGeometryPush agp(fTarget); - fTarget->drawSimpleRect(bounds, NULL, stages); - } else { - if (passCount > 1) { - fTarget->enableState(GrDrawTarget::kNoColorWrites_StateBit); - } - if (fUseIndexedDraw) { - fTarget->drawIndexed(fPrimitiveType, 0, 0, - fVertexCnt, fIndexCnt); - } else { - int baseVertex = 0; - for (int sp = 0; sp < fSubpathCount; ++sp) { - fTarget->drawNonIndexed(fPrimitiveType, baseVertex, - fSubpathVertCount[sp]); - baseVertex += fSubpathVertCount[sp]; - } - } - } - } - } -} - -void GrDefaultPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) { - this->onDrawPath(stages, false); -} - -void GrDefaultPathRenderer::drawPathToStencil() { - GrAssert(kInverseEvenOdd_PathFill != fFill); - GrAssert(kInverseWinding_PathFill != fFill); - this->onDrawPath(0, true); -} diff --git a/gpu/src/GrPathRenderer.h b/gpu/src/GrPathRenderer.h index 67c3a93cb5..25dfafb032 100644 --- a/gpu/src/GrPathRenderer.h +++ b/gpu/src/GrPathRenderer.h @@ -11,9 +11,11 @@ #define GrPathRenderer_DEFINED #include "GrDrawTarget.h" -#include "SkTemplates.h" +#include "GrTArray.h" +#include "GrPathRendererChain.h" class SkPath; + struct GrPoint; /** @@ -29,6 +31,23 @@ struct GrPoint; */ class GR_API GrPathRenderer : public GrRefCnt { public: + + /** + * This is called to install custom path renderers in every GrContext at + * create time. The default implementation in GrCreatePathRenderer_none.cpp + * does not add any additional renderers. Link against another + * implementation to install your own. The most recently added is the + * most preferred path renderer. + * + * @param context the context that will use the path renderer + * @param flags flags indicating how path renderers will be used + * @param prChain the chain to add path renderers to. + */ + static void AddPathRenderers(GrContext* context, + GrPathRendererChain::UsageFlags flags, + GrPathRendererChain* prChain); + + GrPathRenderer(void); /** * Returns true if this path renderer is able to render the path. @@ -71,7 +90,7 @@ public: * having FSAA enabled for a render target). Target is provided to * communicate the draw state (blend mode, stage settings, etc). */ - virtual bool supportsAA(GrDrawTarget* target, + virtual bool supportsAA(const GrDrawTarget* target, const SkPath& path, GrPathFill fill) { return false; } @@ -135,13 +154,6 @@ public: GrCrash("Unexpected call to drawPathToStencil."); } - /** - * This is called to install a custom path renderer in every GrContext at - * create time. The default implementation in GrCreatePathRenderer_none.cpp - * returns NULL. Link against another implementation to install your own. - */ - static GrPathRenderer* CreatePathRenderer(); - /** * Multiply curve tolerance by the given value, increasing or decreasing * the maximum error permitted in tesselating curves with short straight @@ -208,49 +220,5 @@ private: typedef GrRefCnt INHERITED; }; -/** - * Subclass that renders the path using the stencil buffer to resolve fill - * rules (e.g. winding, even-odd) - */ -class GR_API GrDefaultPathRenderer : public GrPathRenderer { -public: - GrDefaultPathRenderer(bool separateStencilSupport, - bool stencilWrapOpsSupport); - - virtual bool canDrawPath(const SkPath& path, - GrPathFill fill) const { return true; } - - virtual bool requiresStencilPass(const GrDrawTarget* target, - const SkPath& path, - GrPathFill fill) const; - - virtual void drawPath(GrDrawTarget::StageBitfield stages); - virtual void drawPathToStencil(); - -protected: - virtual void pathWillClear(); - -private: - - void onDrawPath(GrDrawTarget::StageBitfield stages, bool stencilOnly); - - bool createGeom(GrScalar srcSpaceTol, - GrDrawTarget::StageBitfield stages); - - bool fSeparateStencil; - bool fStencilWrapOps; - - int fSubpathCount; - SkAutoSTMalloc<8, uint16_t> fSubpathVertCount; - int fIndexCnt; - int fVertexCnt; - GrScalar fPreviousSrcTol; - GrDrawTarget::StageBitfield fPreviousStages; - GrPrimitiveType fPrimitiveType; - bool fUseIndexedDraw; - - typedef GrPathRenderer INHERITED; -}; - #endif diff --git a/gpu/src/GrPathRendererChain.cpp b/gpu/src/GrPathRendererChain.cpp new file mode 100644 index 0000000000..afae912c75 --- /dev/null +++ b/gpu/src/GrPathRendererChain.cpp @@ -0,0 +1,65 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "GrPathRendererChain.h" + +#include "GrContext.h" +#include "GrDefaultPathRenderer.h" +#include "GrGpu.h" + +GrPathRendererChain::GrPathRendererChain(GrContext* context, UsageFlags flags) + : fInit(false) + , fFlags(flags) + , fOwner(context) + , fChain(fStorage.get(), kPreAllocCount) { + fInit = false; +} + +GrPathRendererChain::~GrPathRendererChain() { + for (int i = 0; i < fChain.count(); ++i) { + fChain[i]->unref(); + } +} + +GrPathRenderer* GrPathRendererChain::addPathRenderer(GrPathRenderer* pr) { + fChain.push_back() = pr; + pr->ref(); + return pr; +} + +GrPathRenderer* GrPathRendererChain::getPathRenderer(const GrDrawTarget* target, + const GrPath& path, + GrPathFill fill) { + if (!fInit) { + this->init(); + } + bool preferAA = target->isAntialiasState() && + !target->getRenderTarget()->isMultisampled(); + GrPathRenderer* nonAAPR = NULL; + for (int i = 0; i < fChain.count(); ++i) { + if (fChain[i]->canDrawPath(path, fill)) { + if (!preferAA || fChain[i]->supportsAA(target, path, fill)) { + return fChain[i]; + } else { + nonAAPR = fChain[i]; + } + } + } + return nonAAPR; +} + +void GrPathRendererChain::init() { + GrAssert(!fInit); + GrGpu* gpu = fOwner->getGpu(); + this->addPathRenderer( + new GrDefaultPathRenderer(gpu->supportsTwoSidedStencil(), + gpu->supportsStencilWrapOps()))->unref(); + GrPathRenderer::AddPathRenderers(fOwner, fFlags, this); + fInit = true; +} diff --git a/gpu/src/GrPathRendererChain.h b/gpu/src/GrPathRendererChain.h new file mode 100644 index 0000000000..5fa8c2eec2 --- /dev/null +++ b/gpu/src/GrPathRendererChain.h @@ -0,0 +1,64 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#ifndef GrPathRendererChain_DEFINED +#define GrPathRendererChain_DEFINED + +#include "GrRefCnt.h" +#include "GrTArray.h" + +class GrContext; +class GrDrawTarget; +class SkPath; +class GrPathRenderer; + +/** + * Keeps track of a ordered list of path renderers. When a path needs to be + * drawn this list is scanned to find the most preferred renderer. To add your + * path renderer to the list implement the GrPathRenderer::AddPathRenderers + * function. + */ +class GrPathRendererChain : public SkRefCnt { +public: + + enum UsageFlags { + kNone_UsageFlag = 0, + kNonAAOnly_UsageFlag = 1, + }; + + GrPathRendererChain(GrContext* context, UsageFlags flags); + + ~GrPathRendererChain(); + + // takes a ref and unrefs in destructor + GrPathRenderer* addPathRenderer(GrPathRenderer* pr); + + GrPathRenderer* getPathRenderer(const GrDrawTarget* target, + const SkPath& path, + GrPathFill fill); + +private: + + GrPathRendererChain(); + + void init(); + + enum { + kPreAllocCount = 8, + }; + bool fInit; + GrContext* fOwner; + UsageFlags fFlags; + GrAlignedSTStorage fStorage; + GrTArray fChain; +}; + +GR_MAKE_BITFIELD_OPS(GrPathRendererChain::UsageFlags) + +#endif \ No newline at end of file diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp index 9a721016e6..aca6ec2f4c 100644 --- a/gpu/src/GrTesselatedPathRenderer.cpp +++ b/gpu/src/GrTesselatedPathRenderer.cpp @@ -608,8 +608,8 @@ void GrTesselatedPathRenderer::drawPathToStencil() { GrAlwaysAssert(!"multipass stencil should not be needed"); } -bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target, - const SkPath& path, - GrPathFill fill) { +bool GrTesselatedPathRenderer::supportsAA(const GrDrawTarget* target, + const SkPath& path, + GrPathFill fill) { return true; } diff --git a/gpu/src/GrTesselatedPathRenderer.h b/gpu/src/GrTesselatedPathRenderer.h index 5791be6959..c815f50857 100644 --- a/gpu/src/GrTesselatedPathRenderer.h +++ b/gpu/src/GrTesselatedPathRenderer.h @@ -24,7 +24,7 @@ public: const GrPath& path, GrPathFill fill) const { return false; } virtual void drawPathToStencil(); - virtual bool supportsAA(GrDrawTarget* target, + virtual bool supportsAA(const GrDrawTarget* target, const GrPath& path, GrPathFill fill); }; diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp index 7330cf3cff..b7fa1c85e1 100644 --- a/gyp/gpu.gyp +++ b/gyp/gpu.gyp @@ -126,6 +126,7 @@ '../gpu/include/GrTypes.h', '../gpu/include/GrUserConfig.h', + '../gpu/src/GrAddPathRenderers_none.cpp', '../gpu/src/GrAllocPool.cpp', '../gpu/src/GrAtlas.cpp', '../gpu/src/GrBinHashKey.h', @@ -133,7 +134,8 @@ '../gpu/src/GrBufferAllocPool.h', '../gpu/src/GrClip.cpp', '../gpu/src/GrContext.cpp', - '../gpu/src/GrCreatePathRenderer_none.cpp', + '../gpu/src/GrDefaultPathRenderer.cpp', + '../gpu/src/GrDefaultPathRenderer.h', '../gpu/src/GrDrawTarget.cpp', '../gpu/src/GrDrawTarget.h', '../gpu/src/GrGeometryBuffer.h', @@ -167,6 +169,8 @@ '../gpu/src/GrInOrderDrawBuffer.h', '../gpu/src/GrMatrix.cpp', '../gpu/src/GrMemory.cpp', + '../gpu/src/GrPathRendererChain.cpp', + '../gpu/src/GrPathRendererChain.h', '../gpu/src/GrPathRenderer.cpp', '../gpu/src/GrPathRenderer.h', '../gpu/src/GrPathUtils.cpp',