Add support for clipstack to Gr. GrClip is now a list of rects and paths with set operations to combine them. The stencil buffer is used to perform the set operations to put the clip into the stencil buffer. Building Gr's clip from Skia's clipStack is currently disabled due to the fact that Skia's clipStack is relative to the root layer not the current layer. This will be fixed in a subsequent CL.
git-svn-id: http://skia.googlecode.com/svn/trunk@878 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index c47b6e5..3e2b4b3 100644
--- a/gpu/src/GrPathRenderer.cpp
+++ b/gpu/src/GrPathRenderer.cpp
@@ -6,15 +6,145 @@
#include "GrMemory.h"
#include "GrTexture.h"
-
-
-GrDefaultPathRenderer::GrDefaultPathRenderer(bool singlePassWindingStencil)
- : fSinglePassWindingStencil(singlePassWindingStencil) {
+GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
+ bool stencilWrapOpsSupport)
+ : fSeparateStencil(separateStencilSupport),
+ fStencilWrapOps(stencilWrapOpsSupport) {
}
////////////////////////////////////////////////////////////////////////////////
-// Helpers for draw Path
+// 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
#define STENCIL_OFF 0 // Always disable stencil (even when needed)
static const GrScalar gTolerance = GR_Scalar1;
@@ -146,11 +276,11 @@
return true;
#else
if (kEvenOdd_PathFill == fill) {
- GrPathIter::ConvexHint hint = path.hint();
+ GrPathIter::ConvexHint hint = path.convexHint();
return hint == GrPathIter::kConvex_ConvexHint ||
hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint;
} else if (kWinding_PathFill == fill) {
- GrPathIter::ConvexHint hint = path.hint();
+ GrPathIter::ConvexHint hint = path.convexHint();
return hint == GrPathIter::kConvex_ConvexHint ||
hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint ||
(hint == GrPathIter::kSameWindingConvexPieces_ConvexHint &&
@@ -161,13 +291,17 @@
#endif
}
-void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
- GrDrawTarget::StageBitfield stages,
- GrPathIter* path,
- GrPathFill fill,
- const GrPoint* translate) {
+void GrDefaultPathRenderer::drawPathHelper(GrDrawTarget* target,
+ GrDrawTarget::StageBitfield stages,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate,
+ bool stencilOnly) {
GrDrawTarget::AutoStateRestore asr(target);
+ bool colorWritesWereDisabled = target->isColorWriteDisabled();
+ // face culling doesn't make sense here
+ GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
GrMatrix viewM = target->getViewMatrix();
// In order to tesselate the path we get a bound on how much the matrix can
@@ -185,6 +319,8 @@
}
GrScalar tolSqd = GrMul(tol, tol);
+ path->rewind();
+
int subpathCnt;
int maxPts = worst_case_point_count(path,
&subpathCnt,
@@ -211,42 +347,91 @@
// TODO: use primitve restart if available rather than multiple draws
GrPrimitiveType type;
int passCount = 0;
- GrDrawTarget::StencilPass passes[3];
+ const GrStencilSettings* passes[3];
+ GrDrawTarget::DrawFace drawFace[3];
bool reverse = false;
+ bool lastPassIsBounds;
if (kHairLine_PathFill == fill) {
type = kLineStrip_PrimitiveType;
passCount = 1;
- passes[0] = GrDrawTarget::kNone_StencilPass;
+ if (stencilOnly) {
+ passes[0] = &gDirectToStencil;
+ } else {
+ passes[0] = &GrStencilSettings::gDisabled;
+ }
+ lastPassIsBounds = false;
+ drawFace[0] = GrDrawTarget::kBoth_DrawFace;
} else {
type = kTriangleFan_PrimitiveType;
if (single_pass_path(*path, fill, *target)) {
passCount = 1;
- passes[0] = GrDrawTarget::kNone_StencilPass;
+ if (stencilOnly) {
+ passes[0] = &gDirectToStencil;
+ } else {
+ passes[0] = &GrStencilSettings::gDisabled;
+ }
+ drawFace[0] = GrDrawTarget::kBoth_DrawFace;
+ lastPassIsBounds = false;
} else {
switch (fill) {
case kInverseEvenOdd_PathFill:
reverse = true;
// fallthrough
case kEvenOdd_PathFill:
- passCount = 2;
- passes[0] = GrDrawTarget::kEvenOddStencil_StencilPass;
- passes[1] = GrDrawTarget::kEvenOddColor_StencilPass;
+ 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:
- passes[0] = GrDrawTarget::kWindingStencil1_StencilPass;
- if (fSinglePassWindingStencil) {
- passes[1] = GrDrawTarget::kWindingColor_StencilPass;
+ if (fSeparateStencil) {
+ if (fStencilWrapOps) {
+ passes[0] = &gWindStencilSeparateWithWrap;
+ } else {
+ passes[0] = &gWindStencilSeparateNoWrap;
+ }
passCount = 2;
+ drawFace[0] = GrDrawTarget::kBoth_DrawFace;
} else {
- passes[1] = GrDrawTarget::kWindingStencil2_StencilPass;
- passes[2] = GrDrawTarget::kWindingColor_StencilPass;
+ 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 fill!");
@@ -254,7 +439,6 @@
}
}
}
- target->setReverseFill(reverse);
GrPoint pts[4];
@@ -309,11 +493,12 @@
}
}
- // arbitrary path complexity cutoff
- bool useBounds = fill != kHairLine_PathFill &&
- (reverse || (vert - base) > 8);
- GrPoint* boundsVerts = base + maxPts;
- if (useBounds) {
+ // if we're stenciling we will follow with a pass that draws
+ // a bounding rect to set the color. We're stenciling when
+ // passCount > 1.
+ const int& boundVertexStart = maxPts;
+ GrPoint* boundsVerts = base + boundVertexStart;
+ if (lastPassIsBounds) {
GrRect bounds;
if (reverse) {
GrAssert(NULL != target->getRenderTarget());
@@ -333,20 +518,44 @@
}
for (int p = 0; p < passCount; ++p) {
- target->setStencilPass(passes[p]);
- if (useBounds && (GrDrawTarget::kEvenOddColor_StencilPass == passes[p] ||
- GrDrawTarget::kWindingColor_StencilPass == passes[p])) {
+ target->setDrawFace(drawFace[p]);
+ target->setStencil(*passes[p]);
+
+ if (lastPassIsBounds && (p == passCount-1)) {
+ if (!colorWritesWereDisabled) {
+ target->disableState(GrDrawTarget::kNoColorWrites_StateBit);
+ }
target->drawNonIndexed(kTriangleFan_PrimitiveType,
- maxPts, 4);
+ boundVertexStart, 4);
} else {
+ if (passCount > 1) {
+ target->enableState(GrDrawTarget::kNoColorWrites_StateBit);
+ }
int baseVertex = 0;
for (int sp = 0; sp < subpathCnt; ++sp) {
target->drawNonIndexed(type,
- baseVertex,
- subpathVertCount[sp]);
+ baseVertex,
+ subpathVertCount[sp]);
baseVertex += subpathVertCount[sp];
}
}
}
}
+
+void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
+ GrDrawTarget::StageBitfield stages,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ this->drawPathHelper(target, stages, path, fill, translate, false);
+}
+
+void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ GrAssert(kInverseEvenOdd_PathFill != fill);
+ GrAssert(kInverseWinding_PathFill != fill);
+ this->drawPathHelper(target, 0, path, fill, translate, true);
+ }