Use a prioritized list of path renderers in Gr.
http://codereview.appspot.com/4867058
git-svn-id: http://skia.googlecode.com/svn/trunk@2143 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index 26c92b8..06a00e4 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 @@
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);
-}