aboutsummaryrefslogtreecommitdiffhomepage
path: root/gpu/src
diff options
context:
space:
mode:
authorGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-03-03 13:54:13 +0000
committerGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-03-03 13:54:13 +0000
commitd302f1401b3c9aea094804bad4e76de98782cfe8 (patch)
treeb46ec6c4de175842aef051d7b812785dacbd1d73 /gpu/src
parent1d12b1fd66e5be27fb4769ee09ce4fcd6bcc5979 (diff)
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
Diffstat (limited to 'gpu/src')
-rw-r--r--gpu/src/GrClip.cpp167
-rw-r--r--gpu/src/GrContext.cpp21
-rw-r--r--gpu/src/GrDrawTarget.cpp8
-rw-r--r--gpu/src/GrGLTexture.cpp6
-rw-r--r--gpu/src/GrGpu.cpp315
-rw-r--r--gpu/src/GrGpuGL.cpp480
-rw-r--r--gpu/src/GrGpuGL.h3
-rw-r--r--gpu/src/GrInOrderDrawBuffer.cpp26
-rw-r--r--gpu/src/GrPath.cpp25
-rw-r--r--gpu/src/GrPathRenderer.cpp279
-rw-r--r--gpu/src/GrPathRenderer.h84
-rw-r--r--gpu/src/GrStencil.cpp398
-rw-r--r--gpu/src/GrTextContext.cpp20
-rw-r--r--gpu/src/gr_files.mk3
-rw-r--r--gpu/src/gr_unittests.cpp62
15 files changed, 1393 insertions, 504 deletions
diff --git a/gpu/src/GrClip.cpp b/gpu/src/GrClip.cpp
index b7a839bfd2..924c01b9f8 100644
--- a/gpu/src/GrClip.cpp
+++ b/gpu/src/GrClip.cpp
@@ -17,18 +17,30 @@
#include "GrClip.h"
-GrClip::GrClip() {
+GrClip::GrClip()
+ : fList(fListMemory, kPreAllocElements) {
fBounds.setEmpty();
- this->validate();
+ fBoundsValid = true;
}
-GrClip::GrClip(const GrClip& src) {
+GrClip::GrClip(const GrClip& src)
+ : fList(fListMemory, kPreAllocElements) {
*this = src;
}
-GrClip::GrClip(GrClipIterator* iter) {
- fBounds.setEmpty();
- this->setFromIterator(iter);
+GrClip::GrClip(const GrIRect& rect)
+ : fList(fListMemory, kPreAllocElements) {
+ this->setFromIRect(rect);
+}
+
+GrClip::GrClip(const GrRect& rect)
+ : fList(fListMemory, kPreAllocElements) {
+ this->setFromRect(rect);
+}
+
+GrClip::GrClip(GrClipIterator* iter, const GrRect* bounds)
+ : fList(fListMemory, kPreAllocElements) {
+ this->setFromIterator(iter, bounds);
}
GrClip::~GrClip() {}
@@ -36,101 +48,100 @@ GrClip::~GrClip() {}
GrClip& GrClip::operator=(const GrClip& src) {
fList = src.fList;
fBounds = src.fBounds;
- this->validate();
+ fBoundsValid = src.fBoundsValid;
return *this;
}
void GrClip::setEmpty() {
fList.reset();
fBounds.setEmpty();
- this->validate();
+ fBoundsValid = true;
}
-void GrClip::setRect(const GrIRect& r) {
+void GrClip::setFromRect(const GrRect& r) {
fList.reset();
-
- // we need a canonical "empty" rect, so that our operator== will behave
- // correctly with two empty clips.
if (r.isEmpty()) {
- fBounds.setEmpty();
+ // use a canonical empty rect for == testing.
+ setEmpty();
} else {
+ fList.push_back();
+ fList.back().fRect = r;
+ fList.back().fType = kRect_ClipType;
fBounds = r;
- }
- this->validate();
-}
-
-void GrClip::addRect(const GrIRect& r) {
- if (!r.isEmpty()) {
- this->validate();
- if (this->isEmpty()) {
- GrAssert(fList.count() == 0);
- fBounds = r;
- } else {
- if (this->isRect()) {
- *fList.append() = fBounds;
- }
- *fList.append() = r;
- fBounds.unionWith(r);
- }
- this->validate();
+ fBoundsValid = true;
}
}
-void GrClip::setFromIterator(GrClipIterator* iter) {
- this->setEmpty();
- if (iter) {
- for (iter->rewind(); !iter->isDone(); iter->next()) {
- GrIRect r;
- iter->getRect(&r);
- this->addRect(r);
- }
+void GrClip::setFromIRect(const GrIRect& r) {
+ fList.reset();
+ if (r.isEmpty()) {
+ // use a canonical empty rect for == testing.
+ setEmpty();
+ } else {
+ fList.push_back();
+ fList.back().fRect.set(r);
+ fList.back().fType = kRect_ClipType;
+ fBounds.set(r);
+ fBoundsValid = true;
}
}
-///////////////////////////////////////////////////////////////////////////////
+void GrClip::setFromIterator(GrClipIterator* iter, const GrRect* bounds) {
+ fList.reset();
-void GrClipIter::reset(const GrClip& clip) { fClip = &clip; fIndex = 0; }
+ int rectCount = 0;
-bool GrClipIter::isDone() {
- return (NULL == fClip) || (fIndex >= fClip->countRects());
-}
+ // compute bounds for common case of series of intersecting rects.
+ bool isectRectValid = true;
-void GrClipIter::rewind() { fIndex = 0; }
-void GrClipIter::getRect(GrIRect* r) { *r = fClip->getRects()[fIndex]; }
-void GrClipIter::next() { fIndex += 1; }
-void GrClipIter::computeBounds(GrIRect* r) {
- if (NULL == fClip) {
- r->setEmpty();
- } else {
- *r = fClip->getBounds();
+ if (iter) {
+ for (iter->rewind(); !iter->isDone(); iter->next()) {
+ Element& e = fList.push_back();
+ e.fType = iter->getType();
+ e.fOp = iter->getOp();
+ // iterators should not emit replace
+ GrAssert(kReplace_SetOp != e.fOp);
+ switch (e.fType) {
+ case kRect_ClipType:
+ iter->getRect(&e.fRect);
+ ++rectCount;
+ if (isectRectValid) {
+ if (1 == rectCount || kIntersect_SetOp == e.fOp) {
+ GrAssert(fList.count() <= 2);
+ if (fList.count() > 1) {
+ GrAssert(2 == rectCount);
+ rectCount = 1;
+ fList.pop_back();
+ GrAssert(kRect_ClipType == fList.back().fType);
+ fList.back().fRect.intersectWith(e.fRect);
+ }
+ } else {
+ isectRectValid = false;
+ }
+ }
+ break;
+ case kPath_ClipType:
+ e.fPath.resetFromIter(iter->getPathIter());
+ e.fPathFill = iter->getPathFill();
+ isectRectValid = false;
+ break;
+ default:
+ GrCrash("Unknown clip element type.");
+ }
+ }
}
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#if GR_DEBUG
-
-void GrClip::validate() const {
- if (fBounds.isEmpty()) {
- GrAssert(0 == fBounds.fLeft);
- GrAssert(0 == fBounds.fTop);
- GrAssert(0 == fBounds.fRight);
- GrAssert(0 == fBounds.fBottom);
- GrAssert(0 == fList.count());
- } else {
- int count = fList.count();
- if (count > 0) {
- GrAssert(count > 1);
- GrAssert(!fList[0].isEmpty());
- GrIRect bounds = fList[0];
- for (int i = 1; i < count; i++) {
- GrAssert(!fList[i].isEmpty());
- bounds.unionWith(fList[i]);
+ fBoundsValid = false;
+ if (NULL == bounds) {
+ if (isectRectValid) {
+ fBoundsValid = true;
+ if (rectCount > 0) {
+ fBounds = fList[0].fRect;
+ } else {
+ fBounds.setEmpty();
}
- GrAssert(fBounds == bounds);
}
+ } else {
+ fBounds = *bounds;
+ fBoundsValid = true;
}
}
-
-#endif
-
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 840e773313..a047895ced 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -145,7 +145,7 @@ GrTextureEntry* GrContext::createAndLockTexture(GrTextureKey* key,
GrDrawTarget::AutoStateRestore asr(fGpu);
fGpu->setRenderTarget(texture->asRenderTarget());
fGpu->setTexture(0, clampEntry->texture());
- fGpu->setStencilPass(GrDrawTarget::kNone_StencilPass);
+ fGpu->disableStencil();
fGpu->setViewMatrix(GrMatrix::I());
fGpu->setAlpha(0xff);
fGpu->setBlendFunc(kOne_BlendCoeff, kZero_BlendCoeff);
@@ -284,7 +284,7 @@ void GrContext::setClip(const GrClip& clip) {
void GrContext::setClip(const GrIRect& rect) {
GrClip clip;
- clip.setRect(rect);
+ clip.setFromIRect(rect);
fGpu->setClip(clip);
}
@@ -297,7 +297,10 @@ void GrContext::eraseColor(GrColor color) {
void GrContext::drawPaint(const GrPaint& paint) {
// set rect to be big enough to fill the space, but not super-huge, so we
// don't overflow fixed-point implementations
- GrRect r(fGpu->getClip().getBounds());
+ GrRect r;
+ r.setLTRB(0, 0,
+ GrIntToScalar(getRenderTarget()->width()),
+ GrIntToScalar(getRenderTarget()->height()));
GrMatrix inverse;
if (fGpu->getViewInverse(&inverse)) {
inverse.mapRect(&r);
@@ -546,6 +549,15 @@ void GrContext::drawPath(const GrPaint& paint,
fPathRenderer->drawPath(target, enabledStages, path, fill, translate);
}
+void GrContext::drawPath(const GrPaint& paint,
+ const GrPath& path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ GrPath::Iter iter(path);
+ this->drawPath(paint, &iter, fill, translate);
+}
+
+
////////////////////////////////////////////////////////////////////////////////
void GrContext::flush(bool flushRenderTarget) {
@@ -751,7 +763,8 @@ GrContext::GrContext(GrGpu* gpu) {
#if BATCH_RECT_TO_RECT
fDrawBuffer->setQuadIndexBuffer(this->getQuadIndexBuffer());
#endif
- fPathRenderer = new GrDefaultPathRenderer(fGpu->supportsSingleStencilPassWinding());
+ fPathRenderer = new GrDefaultPathRenderer(fGpu->supportsTwoSidedStencil(),
+ fGpu->supportsStencilWrapOps());
}
bool GrContext::finalizeTextureKey(GrTextureKey* key,
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index ca525b3b02..7973361890 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -358,14 +358,6 @@ void GrDrawTarget::setSamplerState(int stage, const GrSamplerState& state) {
fCurrDrawState.fSamplerStates[stage] = state;
}
-void GrDrawTarget::setStencilPass(StencilPass pass) {
- fCurrDrawState.fStencilPass = pass;
-}
-
-void GrDrawTarget::setReverseFill(bool reverse) {
- fCurrDrawState.fReverseFill = reverse;
-}
-
void GrDrawTarget::enableState(uint32_t bits) {
fCurrDrawState.fFlagBits |= bits;
}
diff --git a/gpu/src/GrGLTexture.cpp b/gpu/src/GrGLTexture.cpp
index 9fd1f57edf..ae991e8362 100644
--- a/gpu/src/GrGLTexture.cpp
+++ b/gpu/src/GrGLTexture.cpp
@@ -22,12 +22,14 @@ GrGLRenderTarget::GrGLRenderTarget(const GLRenderTargetIDs& ids,
GLuint stencilBits,
const GrGLIRect& viewport,
GrGLTexture* texture,
- GrGpuGL* gl) : INHERITED(texture) {
+ GrGpuGL* gl) : INHERITED(texture,
+ viewport.fWidth,
+ viewport.fHeight,
+ stencilBits) {
fGL = gl;
fRTFBOID = ids.fRTFBOID;
fTexFBOID = ids.fTexFBOID;
fStencilRenderbufferID = ids.fStencilRenderbufferID;
- fStencilBits = stencilBits;
fMSColorRenderbufferID = ids.fMSColorRenderbufferID;
fNeedsResolve = false;
fViewport = viewport;
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index 019c99ff6d..f42e316082 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -22,12 +22,13 @@
#include "GrIndexBuffer.h"
#include "GrVertexBuffer.h"
#include "GrBufferAllocPool.h"
+#include "GrPathRenderer.h"
// probably makes no sense for this to be less than a page
static const size_t VERTEX_POOL_VB_SIZE = 1 << 12;
static const int VERTEX_POOL_VB_COUNT = 1;
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
size_t GrTexture::BytesPerPixel(PixelConfig config) {
switch (config) {
@@ -56,7 +57,7 @@ bool GrTexture::PixelConfigIsOpaque(PixelConfig config) {
}
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
extern void gr_run_unittests();
@@ -68,7 +69,10 @@ GrGpu::GrGpu() : f8bitPaletteSupport(false),
fVertexPool(NULL),
fIndexPool(NULL),
fQuadIndexBuffer(NULL),
- fUnitSquareVertexBuffer(NULL) {
+ fUnitSquareVertexBuffer(NULL),
+ fPathRenderer(NULL),
+ fVertexPoolInUse(false),
+ fIndexPoolInUse(false) {
#if GR_DEBUG
// gr_run_unittests();
#endif
@@ -80,6 +84,7 @@ GrGpu::~GrGpu() {
GrSafeUnref(fUnitSquareVertexBuffer);
delete fVertexPool;
delete fIndexPool;
+ delete fPathRenderer;
}
void GrGpu::resetContext() {
@@ -91,7 +96,7 @@ void GrGpu::unimpl(const char msg[]) {
#endif
}
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
static const int MAX_QUADS = 1 << 12; // max possible: (1 << 14) - 1;
@@ -159,7 +164,7 @@ const GrVertexBuffer* GrGpu::getUnitSquareVertexBuffer() const {
return fUnitSquareVertexBuffer;
}
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
void GrGpu::clipWillBeSet(const GrClip& newClip) {
if (newClip != fClip) {
@@ -167,8 +172,113 @@ void GrGpu::clipWillBeSet(const GrClip& newClip) {
}
}
+////////////////////////////////////////////////////////////////////////////////
+
+// stencil settings to use when clip is in stencil
+const GrStencilSettings GrGpu::gClipStencilSettings = {
+ kKeep_StencilOp, kKeep_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlwaysIfInClip_StencilFunc, kAlwaysIfInClip_StencilFunc,
+ 0, 0,
+ 0, 0,
+ 0, 0
+};
+
+// converts special stencil func to
+static const GrStencilFunc gGrClipToNormalStencilFunc[2][kClipStencilFuncCount] = {
+ {// Stencil-Clipping is DISABLED, effectively always inside the clip
+ // In the Clip Funcs
+ kAlways_StencilFunc, // kAlwaysIfInClip_StencilFunc
+ kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
+ kLess_StencilFunc, // kLessIfInClip_StencilFunc
+ kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
+ // Special in the clip func that forces user's ref to be 0.
+ kNotEqual_StencilFunc, // kNonZeroIfInClip_StencilFunc
+ // make ref 0 and do normal nequal.
+ },
+ {// Stencil-Clipping is ENABLED
+ // In the Clip Funcs
+ kEqual_StencilFunc, // kAlwaysIfInClip_StencilFunc
+ // eq stencil clip bit, mask
+ // out user bits.
+
+ kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
+ // add stencil bit to mask and ref
+
+ kLess_StencilFunc, // kLessIfInClip_StencilFunc
+ kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
+ // for both of these we can add
+ // the clip bit to the mask and
+ // ref and compare as normal
+ // Special in the clip func that forces user's ref to be 0.
+ kLess_StencilFunc, // kNonZeroIfInClip_StencilFunc
+ // make ref have only the clip bit set
+ // and make comparison be less
+ // 10..0 < 1..user_bits..
+ }
+};
+
+GrStencilFunc GrGpu::ConvertStencilFunc(bool stencilInClip, GrStencilFunc func) {
+ GrAssert(func >= 0);
+ if (func >= kBasicStencilFuncCount) {
+ GrAssert(func < kStencilFuncCount);
+ func = gGrClipToNormalStencilFunc[stencilInClip ? 1 : 0][func - kBasicStencilFuncCount];
+ GrAssert(func >= 0 && func < kBasicStencilFuncCount);
+ }
+ return func;
+}
+
+void GrGpu::ConvertStencilFuncAndMask(GrStencilFunc func,
+ bool clipInStencil,
+ unsigned int clipBit,
+ unsigned int userBits,
+ unsigned int* ref,
+ unsigned int* mask) {
+ if (func < kBasicStencilFuncCount) {
+ *mask &= userBits;
+ *ref &= userBits;
+ } else {
+ if (clipInStencil) {
+ switch (func) {
+ case kAlwaysIfInClip_StencilFunc:
+ *mask = clipBit;
+ *ref = clipBit;
+ break;
+ case kEqualIfInClip_StencilFunc:
+ case kLessIfInClip_StencilFunc:
+ case kLEqualIfInClip_StencilFunc:
+ *mask = (*mask & userBits) | clipBit;
+ *ref = (*ref & userBits) | clipBit;
+ break;
+ case kNonZeroIfInClip_StencilFunc:
+ *mask = (*mask & userBits) | clipBit;
+ *ref = clipBit;
+ break;
+ default:
+ GrCrash("Unknown stencil func");
+ }
+ } else {
+ *mask &= userBits;
+ *ref &= userBits;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define VISUALIZE_COMPLEX_CLIP 0
+
+#if VISUALIZE_COMPLEX_CLIP
+ #include "GrRandom.h"
+ GrRandom gRandom;
+ #define SET_RANDOM_COLOR this->setColor(0xff000000 | gRandom.nextU());
+#else
+ #define SET_RANDOM_COLOR
+#endif
+
bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
const GrIRect* r = NULL;
+ GrIRect clipRect;
// we check this early because we need a valid
// render target to setup stencil clipping
@@ -179,80 +289,135 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
}
if (fCurrDrawState.fFlagBits & kClip_StateBit) {
- fClipState.fClipInStencil = fClip.countRects() > 1;
+ GrRenderTarget& rt = *fCurrDrawState.fRenderTarget;
+
+ GrRect bounds;
+ GrRect rtRect;
+ rtRect.setLTRB(0, 0,
+ GrIntToScalar(rt.width()), GrIntToScalar(rt.height()));
+ if (fClip.hasBounds()) {
+ bounds = fClip.getBounds();
+ bounds.intersectWith(rtRect);
+ } else {
+ bounds = rtRect;
+ }
+
+ bounds.roundOut(&clipRect);
+ if (clipRect.isEmpty()) {
+ clipRect.setLTRB(0,0,0,0);
+ }
+ r = &clipRect;
+
+ fClipState.fClipInStencil = !fClip.isRect() &&
+ !fClip.isEmpty() &&
+ !bounds.isEmpty();
if (fClipState.fClipInStencil &&
(fClipState.fClipIsDirty ||
- fClipState.fStencilClipTarget != fCurrDrawState.fRenderTarget)) {
+ fClip != rt.fLastStencilClip)) {
+
+ rt.fLastStencilClip = fClip;
+ // we set the current clip to the bounds so that our recursive
+ // draws are scissored to them. We use the copy of the complex clip
+ // in the rt to render
+ const GrClip& clip = rt.fLastStencilClip;
+ fClip.setFromRect(bounds);
AutoStateRestore asr(this);
- AutoGeometrySrcRestore agsr(this);
-
- // We have to use setVertexSourceToBuffer (and index) in order
- // to ensure we correctly restore the client's geom sources.
- // We tack the clip verts onto the vertex pool but we don't
- // use the various helper functions because of their side effects.
-
- int rectTotal = fClip.countRects();
- if (NULL == fVertexPool) {
- fVertexPool = new GrVertexBufferAllocPool(this,
- true,
- VERTEX_POOL_VB_SIZE,
- VERTEX_POOL_VB_COUNT);
- } else if (kBuffer_GeometrySrcType == fGeometrySrc.fVertexSrc) {
- // we can't reset if vertex source is array or reserved
- // because then the client data is in the pool!
- fVertexPool->reset();
- }
- const GrVertexBuffer* vertexBuffer;
- int vStart;
- GrPoint* rectVertices =
- reinterpret_cast<GrPoint*>(fVertexPool->makeSpace(0,
- rectTotal * 4,
- &vertexBuffer,
- &vStart));
- for (int r = 0; r < rectTotal; ++r) {
- const GrIRect& rect = fClip.getRects()[r];
- rectVertices[4 * r].setIRectFan(rect.fLeft, rect.fTop,
- rect.fRight, rect.fBottom);
- }
- fVertexPool->unlock();
- this->setVertexSourceToBuffer(0, vertexBuffer);
- this->setIndexSourceToBuffer(getQuadIndexBuffer());
+ AutoInternalDrawGeomRestore aidgr(this);
+
this->setViewMatrix(GrMatrix::I());
- // don't clip the clip or recurse!
- this->disableState(kClip_StateBit);
- this->eraseStencilClip();
- this->setStencilPass((GrDrawTarget::StencilPass)kSetClip_StencilPass);
- int currRect = 0;
- while (currRect < rectTotal) {
- int rectCount = GrMin(MAX_QUADS,
- rectTotal - currRect);
- this->drawIndexed(kTriangles_PrimitiveType,
- vStart + currRect * 4,
- 0,
- rectCount*4,
- rectCount*6);
- currRect += rectCount;
+ this->eraseStencilClip(clipRect);
+ this->flushScissor(NULL);
+#if !VISUALIZE_COMPLEX_CLIP
+ this->enableState(kNoColorWrites_StateBit);
+#else
+ this->disableState(kNoColorWrites_StateBit);
+#endif
+ int count = clip.getElementCount();
+ int clipBit = rt.stencilBits();
+ clipBit = (1 << (clipBit-1));
+
+ // walk through each clip element and perform its set op
+ // with the existing clip.
+ for (int c = 0; c < count; ++c) {
+ GrPathFill fill;
+ // enabled at bottom of loop
+ this->disableState(kModifyStencilClip_StateBit);
+
+ bool canDrawDirectToClip;
+ if (kRect_ClipType == clip.getElementType(c)) {
+ canDrawDirectToClip = true;
+ fill = kEvenOdd_PathFill;
+ } else {
+ fill = clip.getPathFill(c);
+ canDrawDirectToClip = getPathRenderer()->requiresStencilPass(clip.getPath(c));
+ }
+
+ GrSetOp op = 0 == c ? kReplace_SetOp : clip.getOp(c);
+ int passes;
+ GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
+
+ canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canDrawDirectToClip,
+ clipBit, &fill,
+ &passes, stencilSettings);
+
+ // draw the element to the client stencil bits if necessary
+ if (!canDrawDirectToClip) {
+ if (kRect_ClipType == clip.getElementType(c)) {
+ static const GrStencilSettings gDrawToStencil = {
+ kIncClamp_StencilOp, kIncClamp_StencilOp,
+ kIncClamp_StencilOp, kIncClamp_StencilOp,
+ kAlways_StencilFunc, kAlways_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000,
+ 0xffffffff, 0xffffffff,
+ };
+ this->setStencil(gDrawToStencil);
+ SET_RANDOM_COLOR
+ this->drawSimpleRect(clip.getRect(c), NULL, 0);
+ } else {
+ SET_RANDOM_COLOR
+ getPathRenderer()->drawPathToStencil(this, clip.getPath(c), fill, NULL);
+ }
+ }
+
+ // now we modify the clip bit by rendering either the clip
+ // element directly or a bounding rect of the entire clip.
+ this->enableState(kModifyStencilClip_StateBit);
+ for (int p = 0; p < passes; ++p) {
+ this->setStencil(stencilSettings[p]);
+ if (canDrawDirectToClip) {
+ if (kRect_ClipType == clip.getElementType(c)) {
+ SET_RANDOM_COLOR
+ this->drawSimpleRect(clip.getRect(c), NULL, 0);
+ } else {
+ SET_RANDOM_COLOR
+ getPathRenderer()->drawPath(this, 0, clip.getPath(c), fill, NULL);
+ }
+ } else {
+ SET_RANDOM_COLOR
+ this->drawSimpleRect(bounds, 0, NULL);
+ }
+ }
}
- fClipState.fStencilClipTarget = fCurrDrawState.fRenderTarget;
+ fClip = clip;
+ // recusive draws would have disabled this.
+ fClipState.fClipInStencil = true;
}
fClipState.fClipIsDirty = false;
- if (!fClipState.fClipInStencil) {
- r = &fClip.getBounds();
- }
}
+
// Must flush the scissor after graphics state
- if (!flushGraphicsState(type)) {
+ if (!this->flushGraphicsState(type)) {
return false;
}
- flushScissor(r);
+ this->flushScissor(r);
return true;
}
-
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
void GrGpu::drawIndexed(GrPrimitiveType type,
int startVertex,
@@ -314,10 +479,11 @@ void GrGpu::finalizeReservedIndices() {
void GrGpu::prepareVertexPool() {
if (NULL == fVertexPool) {
- fVertexPool = new GrVertexBufferAllocPool(this, true,
- VERTEX_POOL_VB_SIZE,
+ fVertexPool = new GrVertexBufferAllocPool(this, true,
+ VERTEX_POOL_VB_SIZE,
VERTEX_POOL_VB_COUNT);
- } else {
+ } else if (!fVertexPoolInUse) {
+ // the client doesn't have valid data in the pool
fVertexPool->reset();
}
}
@@ -325,7 +491,8 @@ void GrGpu::prepareVertexPool() {
void GrGpu::prepareIndexPool() {
if (NULL == fVertexPool) {
fIndexPool = new GrIndexBufferAllocPool(this, true, 0, 1);
- } else {
+ } else if (!fIndexPoolInUse) {
+ // the client doesn't have valid data in the pool
fIndexPool->reset();
}
}
@@ -339,7 +506,7 @@ bool GrGpu::acquireGeometryHelper(GrVertexLayout vertexLayout,
if (fReservedGeometry.fVertexCount) {
GrAssert(NULL != vertices);
- prepareVertexPool();
+ this->prepareVertexPool();
*vertices = fVertexPool->makeSpace(vertexLayout,
fReservedGeometry.fVertexCount,
@@ -354,7 +521,7 @@ bool GrGpu::acquireGeometryHelper(GrVertexLayout vertexLayout,
if (fReservedGeometry.fIndexCount) {
GrAssert(NULL != indices);
- prepareIndexPool();
+ this->prepareIndexPool();
*indices = fIndexPool->makeSpace(fReservedGeometry.fIndexCount,
&fCurrPoolIndexBuffer,
@@ -397,7 +564,17 @@ void GrGpu::setIndexSourceToArrayHelper(const void* indexArray, int indexCount)
GR_DEBUGASSERT(success);
}
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+GrPathRenderer* GrGpu::getPathRenderer() {
+ if (NULL == fPathRenderer) {
+ fPathRenderer = new GrDefaultPathRenderer(this->supportsTwoSidedStencil(),
+ this->supportsStencilWrapOps());
+ }
+ return fPathRenderer;
+}
+
+////////////////////////////////////////////////////////////////////////////////
const GrGpu::Stats& GrGpu::getStats() const {
return fStats;
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index b30dd027cf..2574b157a6 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -49,8 +49,8 @@ static const GLenum gXfermodeCoeff2Blend[] = {
///////////////////////////////////////////////////////////////////////////////
-void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
- GrSamplerState::SampleMode mode,
+void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
+ GrSamplerState::SampleMode mode,
GrMatrix* matrix) {
GrAssert(NULL != texture);
GrAssert(NULL != matrix);
@@ -80,7 +80,7 @@ void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
}
}
-bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture,
+bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture,
const GrSamplerState& sampler) {
GrAssert(NULL != texture);
if (!sampler.getMatrix().isIdentity()) {
@@ -104,7 +104,7 @@ bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture,
static bool gPrintStartupSpew;
-bool fbo_test(GrGLExts exts, int w, int h) {
+static bool fbo_test(GrGLExts exts, int w, int h) {
GLint savedFBO;
GLint savedTexUnit;
@@ -169,8 +169,6 @@ GrGpuGL::GrGpuGL() {
GrAssert(maxTextureUnits > kNumStages);
#endif
- fCurrDrawState = fHWDrawState;
-
////////////////////////////////////////////////////////////////////////////
// Check for supported features.
@@ -263,14 +261,24 @@ GrGpuGL::GrGpuGL() {
// we could also look for GL_ATI_separate_stencil extension or
// GL_EXT_stencil_two_side but they use different function signatures
// than GL2.0+ (and than each other).
- fSingleStencilPassForWinding = (major >= 2);
+ fTwoSidedStencilSupport = (major >= 2);
+ // supported on GL 1.4 and higher or by extension
+ fStencilWrapOpsSupport = (major > 1) ||
+ (1 == major) && (minor >= 4) ||
+ has_gl_extension("GL_EXT_stencil_wrap");
#else
// ES 2 has two sided stencil but 1.1 doesn't. There doesn't seem to be
// an ES1 extension.
- fSingleStencilPassForWinding = (major >= 2);
+ fTwoSidedStencilSupport = (major >= 2);
+ // stencil wrap support is in ES2, ES1 requires extension.
+ fStencilWrapOpsSupport = (major > 1) ||
+ has_gl_extension("GL_OES_stencil_wrap");
+
#endif
if (gPrintStartupSpew) {
- GrPrintf("Single Stencil Pass For Winding: %s\n", (fSingleStencilPassForWinding ? "YES" : "NO"));
+ GrPrintf("Stencil Caps: TwoSide: %s, Wrap: %s\n",
+ (fTwoSidedStencilSupport ? "YES" : "NO"),
+ (fStencilWrapOpsSupport ? "YES" : "NO"));
}
#if GR_SUPPORT_GLDESKTOP
@@ -424,8 +432,9 @@ void GrGpuGL::resetContextHelper() {
fHWBlendDisabled = false;
GR_GL(Enable(GL_BLEND));
- // this is always disabled
GR_GL(Disable(GL_CULL_FACE));
+ GR_GL(FrontFace(GL_CCW));
+ fHWDrawState.fDrawFace = kBoth_DrawFace;
GR_GL(Disable(GL_DITHER));
#if GR_SUPPORT_GLDESKTOP
@@ -434,14 +443,15 @@ void GrGpuGL::resetContextHelper() {
GR_GL(Disable(GL_MULTISAMPLE));
#endif
+ GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
+ fHWDrawState.fFlagBits = 0;
+
// we only ever use lines in hairline mode
GR_GL(LineWidth(1));
// invalid
fActiveTextureUnitIdx = -1;
- fHWDrawState.fFlagBits = 0;
-
// illegal values
fHWDrawState.fSrcBlend = (GrBlendCoeff)-1;
fHWDrawState.fDstBlend = (GrBlendCoeff)-1;
@@ -454,7 +464,7 @@ void GrGpuGL::resetContextHelper() {
fHWDrawState.fSamplerStates[s].setRadial2Params(-GR_ScalarMax,
-GR_ScalarMax,
true);
-
+
fHWDrawState.fSamplerStates[s].setMatrix(GrMatrix::InvalidMatrix());
}
@@ -463,16 +473,9 @@ void GrGpuGL::resetContextHelper() {
GR_GL(Disable(GL_SCISSOR_TEST));
fHWBounds.fViewportRect.invalidate();
- // disabling the stencil test also disables
- // stencil buffer writes
- GR_GL(Disable(GL_STENCIL_TEST));
- GR_GL(StencilMask(0xffffffff));
- GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- fHWDrawState.fReverseFill = false;
- fHWDrawState.fStencilPass = kNone_StencilPass;
+ fHWDrawState.fStencilSettings.invalidate();
fHWStencilClip = false;
fClipState.fClipIsDirty = true;
- fClipState.fStencilClipTarget = NULL;
fHWGeometryState.fIndexBuffer = NULL;
fHWGeometryState.fVertexBuffer = NULL;
@@ -480,6 +483,7 @@ void GrGpuGL::resetContextHelper() {
GR_GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
fHWGeometryState.fArrayPtrsDirty = true;
+ GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
fHWDrawState.fRenderTarget = NULL;
}
@@ -491,7 +495,7 @@ void GrGpuGL::resetContext() {
GrRenderTarget* GrGpuGL::createPlatformRenderTarget(
intptr_t platformRenderTarget,
int stencilBits,
- int width,
+ int width,
int height) {
GrGLRenderTarget::GLRenderTargetIDs rtIDs;
rtIDs.fStencilRenderbufferID = 0;
@@ -1030,11 +1034,11 @@ GrIndexBuffer* GrGpuGL::createIndexBuffer(uint32_t size, bool dynamic) {
void GrGpuGL::flushScissor(const GrIRect* rect) {
GrAssert(NULL != fCurrDrawState.fRenderTarget);
const GrGLIRect& vp =
- ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->viewport();
+ ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getViewport();
GrGLIRect scissor;
if (NULL != rect) {
- scissor.setRelativeTo(vp, rect->fLeft, rect->fTop,
+ scissor.setRelativeTo(vp, rect->fLeft, rect->fTop,
rect->width(), rect->height());
if (scissor.contains(vp)) {
rect = NULL;
@@ -1068,12 +1072,12 @@ void GrGpuGL::eraseColor(GrColor color) {
fHWBounds.fScissorEnabled = false;
}
GR_GL(ColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE));
+ fHWDrawState.fFlagBits &= ~kNoColorWrites_StateBit;
GR_GL(ClearColor(GrColorUnpackR(color)/255.f,
GrColorUnpackG(color)/255.f,
GrColorUnpackB(color)/255.f,
GrColorUnpackA(color)/255.f));
GR_GL(Clear(GL_COLOR_BUFFER_BIT));
- fDirtyFlags.fWriteMaskChanged = true;
}
void GrGpuGL::eraseStencil(uint32_t value, uint32_t mask) {
@@ -1088,15 +1092,21 @@ void GrGpuGL::eraseStencil(uint32_t value, uint32_t mask) {
GR_GL(StencilMask(mask));
GR_GL(ClearStencil(value));
GR_GL(Clear(GL_STENCIL_BUFFER_BIT));
- fDirtyFlags.fWriteMaskChanged = true;
+ fHWDrawState.fStencilSettings.invalidate();
}
-void GrGpuGL::eraseStencilClip() {
+void GrGpuGL::eraseStencilClip(const GrIRect& rect) {
GrAssert(NULL != fCurrDrawState.fRenderTarget);
- GLint stencilBitCount = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getStencilBits();
+ GLint stencilBitCount = fCurrDrawState.fRenderTarget->stencilBits();
GrAssert(stencilBitCount > 0);
GLint clipStencilMask = (1 << (stencilBitCount - 1));
- eraseStencil(0, clipStencilMask);
+
+ flushRenderTarget();
+ flushScissor(&rect);
+ GR_GL(StencilMask(clipStencilMask));
+ GR_GL(ClearStencil(0));
+ GR_GL(Clear(GL_STENCIL_BUFFER_BIT));
+ fHWDrawState.fStencilSettings.invalidate();
}
void GrGpuGL::forceRenderTargetFlush() {
@@ -1117,13 +1127,13 @@ bool GrGpuGL::readPixels(int left, int top, int width, int height,
}
flushRenderTarget();
- const GrGLIRect& glvp = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->viewport();
-
+ const GrGLIRect& glvp = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getViewport();
+
// the read rect is viewport-relative
GrGLIRect readRect;
readRect.setRelativeTo(glvp, left, top, width, height);
GR_GL(ReadPixels(readRect.fLeft, readRect.fBottom,
- readRect.fWidth, readRect.fHeight,
+ readRect.fWidth, readRect.fHeight,
format, type, buffer));
// now reverse the order of the rows, since GL's are bottom-to-top, but our
@@ -1166,7 +1176,7 @@ void GrGpuGL::flushRenderTarget() {
#endif
fDirtyFlags.fRenderTargetChanged = true;
fHWDrawState.fRenderTarget = fCurrDrawState.fRenderTarget;
- const GrGLIRect& vp = rt->viewport();
+ const GrGLIRect& vp = rt->getViewport();
if (true || fHWBounds.fViewportRect != vp) {
vp.pushToGLViewport();
fHWBounds.fViewportRect = vp;
@@ -1183,6 +1193,27 @@ GLenum gPrimitiveType2GLMode[] = {
GL_LINE_STRIP
};
+#define SWAP_PER_DRAW 0
+
+#if SWAP_PER_DRAW
+ #if GR_MAC_BUILD
+ #include <AGL/agl.h>
+ #elif GR_WIN32_BUILD
+ void SwapBuf() {
+ DWORD procID = GetCurrentProcessId();
+ HWND hwnd = GetTopWindow(GetDesktopWindow());
+ while(hwnd) {
+ DWORD wndProcID = 0;
+ GetWindowThreadProcessId(hwnd, &wndProcID);
+ if(wndProcID == procID) {
+ SwapBuffers(GetDC(hwnd));
+ }
+ hwnd = GetNextWindow(hwnd, GW_HWNDNEXT);
+ }
+ }
+ #endif
+#endif
+
void GrGpuGL::drawIndexedHelper(GrPrimitiveType type,
uint32_t startVertex,
uint32_t startIndex,
@@ -1201,6 +1232,18 @@ void GrGpuGL::drawIndexedHelper(GrPrimitiveType type,
GR_GL(DrawElements(gPrimitiveType2GLMode[type], indexCount,
GL_UNSIGNED_SHORT, indices));
+#if SWAP_PER_DRAW
+ glFlush();
+ #if GR_MAC_BUILD
+ aglSwapBuffers(aglGetCurrentContext());
+ int set_a_break_pt_here = 9;
+ aglSwapBuffers(aglGetCurrentContext());
+ #elif GR_WIN32_BUILD
+ SwapBuf();
+ int set_a_break_pt_here = 9;
+ SwapBuf();
+ #endif
+#endif
}
void GrGpuGL::drawNonIndexedHelper(GrPrimitiveType type,
@@ -1218,6 +1261,18 @@ void GrGpuGL::drawNonIndexedHelper(GrPrimitiveType type,
// account for startVertex in the DrawElements case. So we always
// rely on setupGeometry to have accounted for startVertex.
GR_GL(DrawArrays(gPrimitiveType2GLMode[type], 0, vertexCount));
+#if SWAP_PER_DRAW
+ glFlush();
+ #if GR_MAC_BUILD
+ aglSwapBuffers(aglGetCurrentContext());
+ int set_a_break_pt_here = 9;
+ aglSwapBuffers(aglGetCurrentContext());
+ #elif GR_WIN32_BUILD
+ SwapBuf();
+ int set_a_break_pt_here = 9;
+ SwapBuf();
+ #endif
+#endif
}
void GrGpuGL::resolveTextureRenderTarget(GrGLTexture* texture) {
@@ -1257,203 +1312,162 @@ void GrGpuGL::resolveTextureRenderTarget(GrGLTexture* texture) {
}
}
+static const GLenum grToGLStencilFunc[] = {
+ GL_ALWAYS, // kAlways_StencilFunc
+ GL_NEVER, // kNever_StencilFunc
+ GL_GREATER, // kGreater_StencilFunc
+ GL_GEQUAL, // kGEqual_StencilFunc
+ GL_LESS, // kLess_StencilFunc
+ GL_LEQUAL, // kLEqual_StencilFunc,
+ GL_EQUAL, // kEqual_StencilFunc,
+ GL_NOTEQUAL, // kNotEqual_StencilFunc,
+};
+GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilFunc) == kBasicStencilFuncCount);
+GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
+GR_STATIC_ASSERT(1 == kNever_StencilFunc);
+GR_STATIC_ASSERT(2 == kGreater_StencilFunc);
+GR_STATIC_ASSERT(3 == kGEqual_StencilFunc);
+GR_STATIC_ASSERT(4 == kLess_StencilFunc);
+GR_STATIC_ASSERT(5 == kLEqual_StencilFunc);
+GR_STATIC_ASSERT(6 == kEqual_StencilFunc);
+GR_STATIC_ASSERT(7 == kNotEqual_StencilFunc);
+
+static const GLenum grToGLStencilOp[] = {
+ GL_KEEP, // kKeep_StencilOp
+ GL_REPLACE, // kReplace_StencilOp
+ GL_INCR_WRAP, // kIncWrap_StencilOp
+ GL_INCR, // kIncClamp_StencilOp
+ GL_DECR_WRAP, // kDecWrap_StencilOp
+ GL_DECR, // kDecClamp_StencilOp
+ GL_ZERO, // kZero_StencilOp
+ GL_INVERT, // kInvert_StencilOp
+};
+GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilOp) == kStencilOpCount);
+GR_STATIC_ASSERT(0 == kKeep_StencilOp);
+GR_STATIC_ASSERT(1 == kReplace_StencilOp);
+GR_STATIC_ASSERT(2 == kIncWrap_StencilOp);
+GR_STATIC_ASSERT(3 == kIncClamp_StencilOp);
+GR_STATIC_ASSERT(4 == kDecWrap_StencilOp);
+GR_STATIC_ASSERT(5 == kDecClamp_StencilOp);
+GR_STATIC_ASSERT(6 == kZero_StencilOp);
+GR_STATIC_ASSERT(7 == kInvert_StencilOp);
+
void GrGpuGL::flushStencil() {
+ const GrStencilSettings* settings = &fCurrDrawState.fStencilSettings;
// use stencil for clipping if clipping is enabled and the clip
// has been written into the stencil.
bool stencilClip = fClipState.fClipInStencil &&
(kClip_StateBit & fCurrDrawState.fFlagBits);
- bool stencilChange =
- fDirtyFlags.fWriteMaskChanged ||
- fHWStencilClip != stencilClip ||
- fHWDrawState.fStencilPass != fCurrDrawState.fStencilPass ||
- (kNone_StencilPass != fCurrDrawState.fStencilPass &&
- (StencilPass)kSetClip_StencilPass != fCurrDrawState.fStencilPass &&
- fHWDrawState.fReverseFill != fCurrDrawState.fReverseFill);
+ bool stencilChange = fHWStencilClip != stencilClip ||
+ fHWDrawState.fStencilSettings != *settings ||
+ ((fHWDrawState.fFlagBits & kModifyStencilClip_StateBit) !=
+ (fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit));
if (stencilChange) {
- GLint clipStencilMask;
- GLint pathStencilMask;
- GLint stencilBitCount = ((GrGLRenderTarget*)fCurrDrawState.fRenderTarget)->getStencilBits();
- GrAssert(stencilBitCount > 0 ||
- kNone_StencilPass == fCurrDrawState.fStencilPass);
- clipStencilMask = (1 << (stencilBitCount - 1));
- pathStencilMask = clipStencilMask - 1;
- switch (fCurrDrawState.fStencilPass) {
- case kNone_StencilPass:
- if (stencilClip) {
- GR_GL(Enable(GL_STENCIL_TEST));
- GR_GL(StencilFunc(GL_EQUAL,
- clipStencilMask,
- clipStencilMask));
- GR_GL(StencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
- } else {
- GR_GL(Disable(GL_STENCIL_TEST));
- }
- GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- if (!fSingleStencilPassForWinding) {
- GR_GL(Disable(GL_CULL_FACE));
- }
- break;
- case kEvenOddStencil_StencilPass:
- GR_GL(Enable(GL_STENCIL_TEST));
- if (stencilClip) {
- GR_GL(StencilFunc(GL_EQUAL, clipStencilMask, clipStencilMask));
- } else {
- GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0));
- }
- GR_GL(StencilMask(pathStencilMask));
- GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- GR_GL(StencilOp(GL_KEEP, GL_INVERT, GL_INVERT));
- GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
- if (!fSingleStencilPassForWinding) {
- GR_GL(Disable(GL_CULL_FACE));
- }
- break;
- case kEvenOddColor_StencilPass: {
- GR_GL(Enable(GL_STENCIL_TEST));
- GLint funcRef = 0;
- GLuint funcMask = pathStencilMask;
- if (stencilClip) {
- funcRef |= clipStencilMask;
- funcMask |= clipStencilMask;
- }
- if (!fCurrDrawState.fReverseFill) {
- funcRef |= pathStencilMask;
- }
- GR_GL(StencilFunc(GL_EQUAL, funcRef, funcMask));
- GR_GL(StencilMask(pathStencilMask));
- GR_GL(StencilOp(GL_ZERO, GL_ZERO, GL_ZERO));
- GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- if (!fSingleStencilPassForWinding) {
- GR_GL(Disable(GL_CULL_FACE));
- }
- } break;
- case kWindingStencil1_StencilPass:
- GR_GL(Enable(GL_STENCIL_TEST));
- if (fHasStencilWrap) {
- if (stencilClip) {
- GR_GL(StencilFunc(GL_EQUAL,
- clipStencilMask,
- clipStencilMask));
- } else {
- GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0));
- }
- if (fSingleStencilPassForWinding) {
- GR_GL(StencilOpSeparate(GL_FRONT, GL_KEEP,
- GL_INCR_WRAP, GL_INCR_WRAP));
- GR_GL(StencilOpSeparate(GL_BACK, GL_KEEP,
- GL_DECR_WRAP, GL_DECR_WRAP));
- } else {
- GR_GL(StencilOp(GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP));
- GR_GL(Enable(GL_CULL_FACE));
- GR_GL(CullFace(GL_BACK));
- }
- } else {
- // If we don't have wrap then we use the Func to detect
- // values that would wrap (0 on decr and mask on incr). We
- // make the func fail on these values and use the sfail op
- // to effectively wrap by inverting.
- // This applies whether we are doing a two-pass (front faces
- // followed by back faces) or a single pass (separate func/op)
-
- // Note that in the case where we are also using stencil to
- // clip this means we will write into the path bits in clipped
- // out pixels. We still apply the clip bit in the color pass
- // stencil func so we don't draw color outside the clip.
- // We also will clear the stencil bits in clipped pixels by
- // using zero in the sfail op with write mask set to the
- // path mask.
- GR_GL(Enable(GL_STENCIL_TEST));
- if (fSingleStencilPassForWinding) {
- GR_GL(StencilFuncSeparate(GL_FRONT,
- GL_NOTEQUAL,
- pathStencilMask,
- pathStencilMask));
- GR_GL(StencilFuncSeparate(GL_BACK,
- GL_NOTEQUAL,
- 0x0,
- pathStencilMask));
- GR_GL(StencilOpSeparate(GL_FRONT, GL_INVERT,
- GL_INCR, GL_INCR));
- GR_GL(StencilOpSeparate(GL_BACK, GL_INVERT,
- GL_DECR, GL_DECR));
- } else {
- GR_GL(StencilFunc(GL_NOTEQUAL,
- pathStencilMask,
- pathStencilMask));
- GR_GL(StencilOp(GL_INVERT, GL_INCR, GL_INCR));
- GR_GL(Enable(GL_CULL_FACE));
- GR_GL(CullFace(GL_BACK));
- }
- }
- GR_GL(StencilMask(pathStencilMask));
- GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
- break;
- case kWindingStencil2_StencilPass:
- GrAssert(!fSingleStencilPassForWinding);
- GR_GL(Enable(GL_STENCIL_TEST));
- if (fHasStencilWrap) {
- if (stencilClip) {
- GR_GL(StencilFunc(GL_EQUAL,
+ // we can't simultaneously perform stencil-clipping and modify the stencil clip
+ GrAssert(!stencilClip || !(fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit));
+
+ if (settings->isDisabled()) {
+ if (stencilClip) {
+ settings = &gClipStencilSettings;
+ }
+ }
+
+ if (settings->isDisabled()) {
+ GR_GL(Disable(GL_STENCIL_TEST));
+ } else {
+ GR_GL(Enable(GL_STENCIL_TEST));
+ #if GR_DEBUG
+ if (!fStencilWrapOpsSupport) {
+ GrAssert(settings->fFrontPassOp != kIncWrap_StencilOp);
+ GrAssert(settings->fFrontPassOp != kDecWrap_StencilOp);
+ GrAssert(settings->fFrontFailOp != kIncWrap_StencilOp);
+ GrAssert(settings->fBackFailOp != kDecWrap_StencilOp);
+ GrAssert(settings->fBackPassOp != kIncWrap_StencilOp);
+ GrAssert(settings->fBackPassOp != kDecWrap_StencilOp);
+ GrAssert(settings->fBackFailOp != kIncWrap_StencilOp);
+ GrAssert(settings->fFrontFailOp != kDecWrap_StencilOp);
+ }
+ #endif
+ int stencilBits = fCurrDrawState.fRenderTarget->stencilBits();
+ GrAssert(stencilBits ||
+ (GrStencilSettings::gDisabled ==
+ fCurrDrawState.fStencilSettings));
+ GLuint clipStencilMask = 1 << (stencilBits - 1);
+ GLuint userStencilMask = clipStencilMask - 1;
+
+ unsigned int frontRef = settings->fFrontFuncRef;
+ unsigned int frontMask = settings->fFrontFuncMask;
+ unsigned int frontWriteMask = settings->fFrontWriteMask;
+ GLenum frontFunc;
+
+ if (fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit) {
+
+ GrAssert(settings->fFrontFunc < kBasicStencilFuncCount);
+ frontFunc = grToGLStencilFunc[settings->fFrontFunc];
+ } else {
+ frontFunc = grToGLStencilFunc[ConvertStencilFunc(stencilClip, settings->fFrontFunc)];
+
+ ConvertStencilFuncAndMask(settings->fFrontFunc,
+ stencilClip,
clipStencilMask,
- clipStencilMask));
- } else {
- GR_GL(StencilFunc(GL_ALWAYS, 0x0, 0x0));
- }
- GR_GL(StencilOp(GL_DECR_WRAP, GL_DECR_WRAP, GL_DECR_WRAP));
- } else {
- GR_GL(StencilFunc(GL_NOTEQUAL, 0x0, pathStencilMask));
- GR_GL(StencilOp(GL_INVERT, GL_DECR, GL_DECR));
- }
- GR_GL(StencilMask(pathStencilMask));
- GR_GL(Enable(GL_CULL_FACE));
- GR_GL(CullFace(GL_FRONT));
- GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
- break;
- case kWindingColor_StencilPass: {
- GR_GL(Enable(GL_STENCIL_TEST));
- GLint funcRef = 0;
- GLuint funcMask = pathStencilMask;
- GLenum funcFunc;
-
- if (stencilClip) {
- funcRef |= clipStencilMask;
- funcMask |= clipStencilMask;
- }
- if (fCurrDrawState.fReverseFill) {
- funcFunc = GL_EQUAL;
+ userStencilMask,
+ &frontRef,
+ &frontMask);
+ frontWriteMask &= userStencilMask;
+ }
+ GrAssert(settings->fFrontFailOp >= 0 &&
+ settings->fFrontFailOp < GR_ARRAY_COUNT(grToGLStencilOp));
+ GrAssert(settings->fFrontPassOp >= 0 &&
+ settings->fFrontPassOp < GR_ARRAY_COUNT(grToGLStencilOp));
+ GrAssert(settings->fBackFailOp >= 0 &&
+ settings->fBackFailOp < GR_ARRAY_COUNT(grToGLStencilOp));
+ GrAssert(settings->fBackPassOp >= 0 &&
+ settings->fBackPassOp < GR_ARRAY_COUNT(grToGLStencilOp));
+ if (fTwoSidedStencilSupport) {
+ GLenum backFunc;
+
+ unsigned int backRef = settings->fBackFuncRef;
+ unsigned int backMask = settings->fBackFuncMask;
+ unsigned int backWriteMask = settings->fBackWriteMask;
+
+
+ if (fCurrDrawState.fFlagBits & kModifyStencilClip_StateBit) {
+ GrAssert(settings->fBackFunc < kBasicStencilFuncCount);
+ backFunc = grToGLStencilFunc[settings->fBackFunc];
} else {
- funcFunc = GL_LESS;
+ backFunc = grToGLStencilFunc[ConvertStencilFunc(stencilClip, settings->fBackFunc)];
+ ConvertStencilFuncAndMask(settings->fBackFunc,
+ stencilClip,
+ clipStencilMask,
+ userStencilMask,
+ &backRef,
+ &backMask);
+ backWriteMask &= userStencilMask;
}
- GR_GL(StencilFunc(funcFunc, funcRef, funcMask));
- GR_GL(StencilMask(pathStencilMask));
- // must zero in sfail because winding w/o wrap will write
- // path stencil bits in clipped out pixels
- GR_GL(StencilOp(GL_ZERO, GL_ZERO, GL_ZERO));
- GR_GL(ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- if (!fSingleStencilPassForWinding) {
- GR_GL(Disable(GL_CULL_FACE));
- }
- } break;
- case kSetClip_StencilPass:
- GR_GL(Enable(GL_STENCIL_TEST));
- GR_GL(StencilFunc(GL_ALWAYS, clipStencilMask, clipStencilMask));
- GR_GL(StencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE));
- GR_GL(StencilMask(clipStencilMask));
- GR_GL(ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));
- if (!fSingleStencilPassForWinding) {
- GR_GL(Disable(GL_CULL_FACE));
- }
- break;
- default:
- GrAssert(!"Unexpected stencil pass.");
- break;
+ GR_GL(StencilFuncSeparate(GL_FRONT, frontFunc, frontRef, frontMask));
+ GR_GL(StencilMaskSeparate(GL_FRONT, frontWriteMask));
+ GR_GL(StencilFuncSeparate(GL_BACK, backFunc, backRef, backMask));
+ GR_GL(StencilMaskSeparate(GL_BACK, backWriteMask));
+ GR_GL(StencilOpSeparate(GL_FRONT, grToGLStencilOp[settings->fFrontFailOp],
+ grToGLStencilOp[settings->fFrontPassOp],
+ grToGLStencilOp[settings->fFrontPassOp]));
+
+ GR_GL(StencilOpSeparate(GL_BACK, grToGLStencilOp[settings->fBackFailOp],
+ grToGLStencilOp[settings->fBackPassOp],
+ grToGLStencilOp[settings->fBackPassOp]));
+ } else {
+ GR_GL(StencilFunc(frontFunc, frontRef, frontMask));
+ GR_GL(StencilMask(frontWriteMask));
+ GR_GL(StencilOp(grToGLStencilOp[settings->fFrontFailOp],
+ grToGLStencilOp[settings->fFrontPassOp],
+ grToGLStencilOp[settings->fFrontPassOp]));
+ }
}
- fHWDrawState.fStencilPass = fCurrDrawState.fStencilPass;
- fHWDrawState.fReverseFill = fCurrDrawState.fReverseFill;
+ fHWDrawState.fStencilSettings = fCurrDrawState.fStencilSettings;
fHWStencilClip = stencilClip;
}
}
@@ -1544,6 +1558,17 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
}
}
+ if ((fCurrDrawState.fFlagBits & kNoColorWrites_StateBit) !=
+ (fHWDrawState.fFlagBits & kNoColorWrites_StateBit)) {
+ GLenum mask;
+ if (fCurrDrawState.fFlagBits & kNoColorWrites_StateBit) {
+ mask = GL_FALSE;
+ } else {
+ mask = GL_TRUE;
+ }
+ GR_GL(ColorMask(mask, mask, mask, mask));
+ }
+
#if GR_SUPPORT_GLDESKTOP
// ES doesn't support toggling GL_MULTISAMPLE and doesn't have
// smooth lines.
@@ -1592,6 +1617,25 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
}
}
+ if (fHWDrawState.fDrawFace != fCurrDrawState.fDrawFace) {
+ switch (fCurrDrawState.fDrawFace) {
+ case kCCW_DrawFace:
+ glEnable(GL_CULL_FACE);
+ GR_GL(CullFace(GL_BACK));
+ break;
+ case kCW_DrawFace:
+ GR_GL(Enable(GL_CULL_FACE));
+ GR_GL(CullFace(GL_FRONT));
+ break;
+ case kBoth_DrawFace:
+ GR_GL(Disable(GL_CULL_FACE));
+ break;
+ default:
+ GrCrash("Unknown draw face.");
+ }
+ fHWDrawState.fDrawFace = fCurrDrawState.fDrawFace;
+ }
+
#if GR_DEBUG
// check for circular rendering
for (int s = 0; s < kNumStages; ++s) {
@@ -1605,6 +1649,7 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
flushStencil();
+ // flushStencil may look at the private state bits, so keep it before this.
fHWDrawState.fFlagBits = fCurrDrawState.fFlagBits;
return true;
}
@@ -1654,9 +1699,6 @@ void GrGpuGL::notifyRenderTargetDelete(GrRenderTarget* renderTarget) {
if (fHWDrawState.fRenderTarget == renderTarget) {
fHWDrawState.fRenderTarget = NULL;
}
- if (fClipState.fStencilClipTarget == renderTarget) {
- fClipState.fStencilClipTarget = NULL;
- }
}
void GrGpuGL::notifyTextureDelete(GrGLTexture* texture) {
diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h
index f6216c5c6e..a2905c5d30 100644
--- a/gpu/src/GrGpuGL.h
+++ b/gpu/src/GrGpuGL.h
@@ -80,7 +80,6 @@ protected:
// call resetDirtyFlags after its flush is complete
struct {
bool fRenderTargetChanged : 1;
- bool fWriteMaskChanged : 1;
int fTextureChangedMask;
} fDirtyFlags;
GR_STATIC_ASSERT(8 * sizeof(int) >= kNumStages);
@@ -108,7 +107,7 @@ protected:
uint32_t numVertices);
virtual void flushScissor(const GrIRect* rect);
void eraseStencil(uint32_t value, uint32_t mask);
- virtual void eraseStencilClip();
+ virtual void eraseStencilClip(const GrIRect& rect);
// binds texture unit in GL
void setTextureUnit(int unitIdx);
diff --git a/gpu/src/GrInOrderDrawBuffer.cpp b/gpu/src/GrInOrderDrawBuffer.cpp
index 68590fc8ab..a249364752 100644
--- a/gpu/src/GrInOrderDrawBuffer.cpp
+++ b/gpu/src/GrInOrderDrawBuffer.cpp
@@ -67,17 +67,17 @@ void GrInOrderDrawBuffer::setQuadIndexBuffer(const GrIndexBuffer* indexBuffer) {
fCurrQuad = 0;
fMaxQuads = (NULL == indexBuffer) ? 0 : indexBuffer->maxQuads();
} else {
- GrAssert((NULL == indexBuffer && 0 == fMaxQuads) ||
+ GrAssert((NULL == indexBuffer && 0 == fMaxQuads) ||
(indexBuffer->maxQuads() == fMaxQuads));
}
}
-void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
+void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
const GrMatrix* matrix,
StageBitfield stageEnableBitfield,
const GrRect* srcRects[],
const GrMatrix* srcMatrices[]) {
-
+
GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad));
GrAssert(!(fDraws.empty() && fCurrQuad));
GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer));
@@ -85,7 +85,7 @@ void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
// if we have a quad IB then either append to the previous run of
// rects or start a new run
if (fMaxQuads) {
-
+
bool appendToPreviousDraw = false;
GrVertexLayout layout = GetRectVertexLayout(stageEnableBitfield, srcRects);
AutoReleaseGeometry geo(this, layout, 4, 0);
@@ -103,10 +103,14 @@ void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
// the rect.
bool disabledClip = false;
if (this->isClipState() && fClip.isRect()) {
- GrRect clipRect = GrRect(*fClip.getRects());
+
+ // single rect clip should have bounds
+ GrAssert(fClip.hasBounds());
+
+ GrRect clipRect = GrRect(fClip.getBounds());
// If the clip rect touches the edge of the viewport, extended it
// out (close) to infinity to avoid bogus intersections.
- // We might consider a more exact clip to viewport if this
+ // We might consider a more exact clip to viewport if this
// conservative test fails.
const GrRenderTarget* target = this->getRenderTarget();
if (0 >= clipRect.fLeft) {
@@ -135,11 +139,11 @@ void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
disabledClip = true;
}
}
- if (!needsNewClip() && !needsNewState() && fCurrQuad > 0 &&
+ if (!needsNewClip() && !needsNewState() && fCurrQuad > 0 &&
fCurrQuad < fMaxQuads && layout == fLastRectVertexLayout) {
int vsize = VertexSize(layout);
-
+
Draw& lastDraw = fDraws.back();
GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer);
@@ -500,7 +504,7 @@ void GrInOrderDrawBuffer::pushState() {
}
this->saveCurrentDrawState(&fStates.push_back());
}
-
+
bool GrInOrderDrawBuffer::needsNewClip() const {
if (fCurrDrawState.fFlagBits & kClip_StateBit) {
if (fClips.empty() || (fClipSet && fClips.back() != fClip)) {
@@ -509,12 +513,12 @@ bool GrInOrderDrawBuffer::needsNewClip() const {
}
return false;
}
-
+
void GrInOrderDrawBuffer::pushClip() {
fClips.push_back() = fClip;
fClipSet = false;
}
-
+
void GrInOrderDrawBuffer::clipWillBeSet(const GrClip& newClip) {
fClipSet = true;
}
diff --git a/gpu/src/GrPath.cpp b/gpu/src/GrPath.cpp
index 554b3b915a..ca5c43baa2 100644
--- a/gpu/src/GrPath.cpp
+++ b/gpu/src/GrPath.cpp
@@ -3,6 +3,8 @@
GrPath::GrPath() {}
GrPath::GrPath(const GrPath& src) : INHERITED() {
+ GrPath::Iter iter(src);
+ this->resetFromIter(&iter);
}
GrPath::GrPath(GrPathIter& iter) {
@@ -12,6 +14,26 @@ GrPath::GrPath(GrPathIter& iter) {
GrPath::~GrPath() {
}
+bool GrPath::operator ==(const GrPath& path) const {
+ if (fVerbs.count() != path.fVerbs.count() ||
+ fPts.count() != path.fPts.count()) {
+ return false;
+ }
+
+ for (int v = 0; v < fVerbs.count(); ++v) {
+ if (fVerbs[v] != path.fVerbs[v]) {
+ return false;
+ }
+ }
+
+ for (int p = 0; p < fPts.count(); ++p) {
+ if (fPts[p] != path.fPts[p]) {
+ return false;
+ }
+ }
+ return true;
+}
+
void GrPath::ensureMoveTo() {
if (fVerbs.isEmpty() || this->wasLastVerb(kClose)) {
*fVerbs.append() = kMove;
@@ -90,6 +112,7 @@ void GrPath::resetFromIter(GrPathIter* iter) {
break;
}
}
+ fConvexHint = iter->convexHint();
}
///////////////////////////////////////////////////////////////////////////////
@@ -157,7 +180,7 @@ GrPathIter::Command GrPath::Iter::next(GrPoint points[]) {
return (GrPathIter::Command)cmd;
}
-GrPathIter::ConvexHint GrPath::Iter::hint() const {
+GrPathIter::ConvexHint GrPath::Iter::convexHint() const {
return fPath.getConvexHint();
}
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index c47b6e522c..3e2b4b35c5 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 @@ static inline bool single_pass_path(const GrPathIter& path,
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 @@ static inline bool single_pass_path(const GrPathIter& path,
#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 @@ void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
}
GrScalar tolSqd = GrMul(tol, tol);
+ path->rewind();
+
int subpathCnt;
int maxPts = worst_case_point_count(path,
&subpathCnt,
@@ -211,42 +347,91 @@ void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
// 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 @@ void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
}
}
}
- target->setReverseFill(reverse);
GrPoint pts[4];
@@ -309,11 +493,12 @@ FINISHED:
}
}
- // 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 @@ FINISHED:
}
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);
+ }
diff --git a/gpu/src/GrPathRenderer.h b/gpu/src/GrPathRenderer.h
index e3fd71507a..467b0a04d3 100644
--- a/gpu/src/GrPathRenderer.h
+++ b/gpu/src/GrPathRenderer.h
@@ -34,31 +34,107 @@ public:
* @param stages indicates which stages the are already
* in use. All enabled stages expect positions
* as texture coordinates. The path renderer
- * use the remaining stages for its path
+ * use the remaining stages for its path
* filling algorithm.
* @param path the path to draw.
* @param fill the fill rule to apply.
* @param translate optional additional translation to apply to
- * the path NULL means (0,0).
+ * the path. NULL means (0,0).
*/
virtual void drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
GrPathIter* path,
GrPathFill fill,
const GrPoint* translate) = 0;
+
+ void drawPath(GrDrawTarget* target,
+ GrDrawTarget::StageBitfield stages,
+ const GrPath& path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ GrPath::Iter iter(path);
+ this->drawPath(target, stages, &iter, fill, translate);
+ }
+
+ /**
+ * For complex clips Gr uses the stencil buffer. The path renderer must be
+ * able to render paths into the stencil buffer. However, the path renderer
+ * itself may require the stencil buffer to resolve the path fill rule. This
+ * function queries whether the path render requires its own stencil
+ * pass. If this returns false then drawPath() should not modify the
+ * the target's stencil settings.
+ *
+ * @return false if this path renderer can generate interior-only fragments
+ * without changing the stencil settings on the target. If it
+ * returns true the drawPathToStencil will be used when rendering
+ * clips.
+ */
+ virtual bool requiresStencilPass(GrPathIter*) const { return false; }
+
+ bool requiresStencilPass(const GrPath& path) const {
+ GrPath::Iter iter(path);
+ return requiresStencilPass(&iter);
+ }
+
+ /**
+ * Draws a path to the stencil buffer. Assume the writable bits are zero
+ * prior and write a nonzero value in interior samples. The default
+ * implementation assumes the path filling algorithm doesn't require a
+ * separate stencil pass and so just calls drawPath.
+ *
+ * Fill will never be an inverse fill rule.
+ *
+ * @param target the target to draw into.
+ * @param path the path to draw.
+ * @param fill the fill rule to apply.
+ * @param translate optional additional translation to apply to
+ * the path. NULL means (0,0).
+ */
+ virtual void drawPathToStencil(GrDrawTarget* target,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ GrAssert(kInverseEvenOdd_PathFill != fill);
+ GrAssert(kInverseWinding_PathFill != fill);
+
+ this->drawPath(target, 0, path, fill, translate);
+ }
+
+ void drawPathToStencil(GrDrawTarget* target,
+ const GrPath& path,
+ GrPathFill fill,
+ const GrPoint* translate) {
+ GrPath::Iter iter(path);
+ this->drawPathToStencil(target, &iter, fill, translate);
+ }
};
class GrDefaultPathRenderer : public GrPathRenderer {
public:
- GrDefaultPathRenderer(bool singlePassWindingStencil);
+ GrDefaultPathRenderer(bool separateStencilSupport,
+ bool stencilWrapOpsSupport);
virtual void drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
GrPathIter* path,
GrPathFill fill,
const GrPoint* translate);
+ virtual bool requiresStencilPass(GrPath&) const { return true; }
+ virtual void drawPathToStencil(GrDrawTarget* target,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate);
private:
- bool fSinglePassWindingStencil;
+
+ void drawPathHelper(GrDrawTarget* target,
+ GrDrawTarget::StageBitfield stages,
+ GrPathIter* path,
+ GrPathFill fill,
+ const GrPoint* translate,
+ bool stencilOnly);
+
+ bool fSeparateStencil;
+ bool fStencilWrapOps;
};
#endif \ No newline at end of file
diff --git a/gpu/src/GrStencil.cpp b/gpu/src/GrStencil.cpp
new file mode 100644
index 0000000000..9d68c65ebd
--- /dev/null
+++ b/gpu/src/GrStencil.cpp
@@ -0,0 +1,398 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#include "GrStencil.h"
+
+const GrStencilSettings GrStencilSettings::gDisabled = {};
+GR_STATIC_ASSERT(0 == kKeep_StencilOp);
+GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
+
+////////////////////////////////////////////////////////////////////////////////
+// Stencil Rules for Merging user stencil space into clip
+
+// We can't include the clip bit in the ref or mask values because the division
+// between user and clip bits in the stencil depends on the number of stencil
+// bits in the runtime. Comments below indicate what the code should do to
+// incorporate the clip bit into these settings.
+
+///////
+// Replace
+
+// set the ref to be the clip bit, but mask it out for the test
+static const GrStencilSettings gUserToClipReplace = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gInvUserToClipReplace = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+///////
+// Intersect
+static const GrStencilSettings gUserToClipIsect = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gInvUserToClipIsect = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+///////
+// Difference
+static const GrStencilSettings gUserToClipDiff = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+static const GrStencilSettings gInvUserToClipDiff = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x0, 0x0, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+///////
+// Union
+
+// first pass makes all the passing cases >= just clip bit set.
+static const GrStencilSettings gUserToClipUnionPass0 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kLEqual_StencilFunc, kLEqual_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x00000001, 0x00000001, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+// second pass allows anything greater than just clip bit set to pass
+static const GrStencilSettings gUserToClipUnionPass1 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLEqual_StencilFunc, kLEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+// for inverse first pass finds non-zerp user with clip bit set
+// and converts it to just clip bit set
+static const GrStencilSettings gInvUserToClipUnionPass0 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+// second pass lets anything through with a nonzero user portion
+// and writes a ref value with just the clip bit set to it.
+static const GrStencilSettings gInvUserToClipUnionPass1 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+///////
+// Xor
+static const GrStencilSettings gUserToClipXorPass0 = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x00000000, 0x00000000,
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gUserToClipXorPass1 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kGreater_StencilFunc, kGreater_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gInvUserToClipXorPass0 = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x00000000, 0x00000000,
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gInvUserToClipXorPass1 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+///////
+// Reverse Diff
+static const GrStencilSettings gUserToClipRDiffPass0 = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kLess_StencilFunc, kLess_StencilFunc,
+ 0xffffffff, 0xffffffff, // unset clip bit
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gUserToClipRDiffPass1 = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0x00000000, 0x00000000, // set clip bit
+ 0x00000000, 0x00000000, // set clip bit
+ 0xffffffff, 0xffffffff
+};
+
+static const GrStencilSettings gInvUserToClipRDiff = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kEqual_StencilFunc, kEqual_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 // set clip bit
+};
+///////
+// Direct to Stencil
+
+// We can render a clip element directly without first writing to the client
+// portion of the clip when the fill is not inverse and the set operation will
+// only modify the in/out status of samples covered by the clip element.
+
+// this one only works if used right after stencil clip was cleared.
+// Our GrClip doesn't allow midstream replace ops.
+static const GrStencilSettings gReplaceClip = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kReplace_StencilOp, kReplace_StencilOp,
+ kAlways_StencilFunc, kAlways_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0x00000000, 0x00000000 // set clipBit
+};
+
+static const GrStencilSettings gUnionClip = {
+ kReplace_StencilOp, kReplace_StencilOp,
+ kReplace_StencilOp, kReplace_StencilOp,
+ kAlways_StencilFunc, kAlways_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000, // set clip bit
+ 0x00000000, 0x00000000 // set clip bit
+};
+
+static const GrStencilSettings gXorClip = {
+ kInvert_StencilOp, kInvert_StencilOp,
+ kInvert_StencilOp, kInvert_StencilOp,
+ kAlways_StencilFunc, kAlways_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 // set clip bit
+};
+
+static const GrStencilSettings gDiffClip = {
+ kZero_StencilOp, kZero_StencilOp,
+ kZero_StencilOp, kZero_StencilOp,
+ kAlways_StencilFunc, kAlways_StencilFunc,
+ 0xffffffff, 0xffffffff,
+ 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 // set clip bit
+};
+
+static const GrPathFill gNonInvertedFills[] = {
+ kWinding_PathFill, // kWinding_PathFill
+ kEvenOdd_PathFill, // kEvenOdd_PathFill
+ kWinding_PathFill, // kInverseWinding_PathFill
+ kEvenOdd_PathFill, // kInverseEvenOdd_PathFill
+ kWinding_PathFill, // kHairLine_PathFill
+};
+
+static const bool gIsFillInverted[] = {
+ false, // kWinding_PathFill
+ false, // kEvenOdd_PathFill
+ true, // kInverseWinding_PathFill
+ true, // kInverseEvenOdd_PathFill
+ false, // kHairLine_PathFill
+};
+GR_STATIC_ASSERT(0 == kWinding_PathFill);
+GR_STATIC_ASSERT(1 == kEvenOdd_PathFill);
+GR_STATIC_ASSERT(2 == kInverseWinding_PathFill);
+GR_STATIC_ASSERT(3 == kInverseEvenOdd_PathFill);
+GR_STATIC_ASSERT(4 == kHairLine_PathFill);
+GR_STATIC_ASSERT(5 == kPathFillCount);
+
+bool GrStencilSettings::GetClipPasses(GrSetOp op,
+ bool canBeDirect,
+ unsigned int stencilClipMask,
+ GrPathFill* fill,
+ int* numPasses,
+ GrStencilSettings settings[kMaxStencilClipPasses]) {
+ if (canBeDirect) {
+ if (!gIsFillInverted[*fill]) {
+ *numPasses = 0;
+ switch (op) {
+ case kReplace_SetOp:
+ *numPasses = 1;
+ settings[0] = gReplaceClip;
+ break;
+ case kUnion_SetOp:
+ *numPasses = 1;
+ settings[0] = gUnionClip;
+ break;
+ case kXor_SetOp:
+ *numPasses = 1;
+ settings[0] = gXorClip;
+ break;
+ case kDifference_SetOp:
+ *numPasses = 1;
+ settings[0] = gDiffClip;
+ break;
+ default: // suppress warning
+ break;
+ }
+ if (1 == *numPasses) {
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fFrontWriteMask |= stencilClipMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+ settings[0].fBackWriteMask = settings[0].fFrontWriteMask;
+ return true;
+ }
+ }
+ }
+ switch (op) {
+ case kReplace_SetOp:
+ *numPasses= 1;
+ settings[0] = gIsFillInverted[*fill] ? gInvUserToClipReplace : gUserToClipReplace;
+ settings[0].fFrontFuncMask &= ~stencilClipMask;
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+
+ case kIntersect_SetOp:
+ *numPasses = 1;
+ settings[0] = gIsFillInverted[*fill] ? gInvUserToClipIsect : gUserToClipIsect;
+ settings[0].fFrontFuncRef = stencilClipMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+ break;
+ case kUnion_SetOp:
+ *numPasses = 2;
+ if (gIsFillInverted[*fill]) {
+ settings[0] = gInvUserToClipUnionPass0;
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncMask;
+
+ settings[1] = gInvUserToClipUnionPass1;
+ settings[1].fFrontFuncMask &= ~stencilClipMask;
+ settings[1].fFrontFuncRef |= stencilClipMask;
+ settings[1].fBackFuncMask = settings[1].fFrontFuncMask;
+ settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+
+ } else {
+ settings[0] = gUserToClipUnionPass0;
+ settings[0].fFrontFuncMask &= ~stencilClipMask;
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+
+ settings[1] = gUserToClipUnionPass1;
+ settings[1].fFrontFuncRef |= stencilClipMask;
+ settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+ }
+ break;
+ case kXor_SetOp:
+ *numPasses = 2;
+ if (gIsFillInverted[*fill]) {
+ settings[0] = gInvUserToClipXorPass0;
+ settings[0].fFrontFuncMask &= ~stencilClipMask;
+ settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+
+ settings[1] = gInvUserToClipXorPass1;
+ settings[1].fFrontFuncRef |= stencilClipMask;
+ settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+ } else {
+ settings[0] = gUserToClipXorPass0;
+ settings[0].fFrontFuncMask &= ~stencilClipMask;
+ settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+
+ settings[1] = gUserToClipXorPass1;
+ settings[1].fFrontFuncRef |= stencilClipMask;
+ settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+ }
+ break;
+ case kDifference_SetOp:
+ *numPasses = 1;
+ settings[0] = gIsFillInverted[*fill] ? gInvUserToClipDiff : gUserToClipDiff;
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+ break;
+ case kReverseDifference_SetOp:
+ if (gIsFillInverted[*fill]) {
+ *numPasses = 1;
+ settings[0] = gInvUserToClipRDiff;
+ settings[0].fFrontWriteMask |= stencilClipMask;
+ settings[0].fBackWriteMask = settings[0].fFrontWriteMask;
+ } else {
+ *numPasses = 2;
+ settings[0] = gUserToClipRDiffPass0;
+ settings[0].fFrontFuncMask &= ~stencilClipMask;
+ settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+ settings[0].fFrontFuncRef |= stencilClipMask;
+ settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+
+ settings[1] = gUserToClipRDiffPass1;
+ settings[1].fFrontFuncMask |= stencilClipMask;
+ settings[1].fFrontFuncRef |= stencilClipMask;
+ settings[1].fBackFuncMask = settings[1].fFrontFuncMask;
+ settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+ }
+ break;
+ default:
+ GrCrash("Unknown set op");
+ }
+ *fill = gNonInvertedFills[*fill];
+ return false;
+} \ No newline at end of file
diff --git a/gpu/src/GrTextContext.cpp b/gpu/src/GrTextContext.cpp
index 8ce45e40eb..802e3e3ab7 100644
--- a/gpu/src/GrTextContext.cpp
+++ b/gpu/src/GrTextContext.cpp
@@ -67,21 +67,25 @@ GrTextContext::GrTextContext(GrContext* context,
fCurrTexture = NULL;
fCurrVertex = 0;
- fClipRect = context->getClip().getBounds();
if (NULL != extMatrix) {
fExtMatrix = *extMatrix;
} else {
fExtMatrix = GrMatrix::I();
}
- if (!fExtMatrix.isIdentity()) {
- GrMatrix inverse;
- GrRect r;
- r.set(fClipRect);
- if (fExtMatrix.invert(&inverse)) {
- inverse.mapRect(&r);
- r.roundOut(&fClipRect);
+ if (context->getClip().hasBounds()) {
+ if (!fExtMatrix.isIdentity()) {
+ GrMatrix inverse;
+ GrRect r = context->getClip().getBounds();
+ if (fExtMatrix.invert(&inverse)) {
+ inverse.mapRect(&r);
+ r.roundOut(&fClipRect);
+ }
+ } else {
+ context->getClip().getBounds().roundOut(&fClipRect);
}
+ } else {
+ fClipRect.setLargest();
}
// save the context's original matrix off and restore in destructor
diff --git a/gpu/src/gr_files.mk b/gpu/src/gr_files.mk
index 88aa84b222..d0f41e748f 100644
--- a/gpu/src/gr_files.mk
+++ b/gpu/src/gr_files.mk
@@ -22,4 +22,5 @@ SOURCE := \
GrTextContext.cpp \
GrTextStrike.cpp \
GrBufferAllocPool.cpp\
- GrPathRenderer.cpp
+ GrPathRenderer.cpp \
+ GrStencil.cpp
diff --git a/gpu/src/gr_unittests.cpp b/gpu/src/gr_unittests.cpp
index 8ef61b1605..9caaa1fb2b 100644
--- a/gpu/src/gr_unittests.cpp
+++ b/gpu/src/gr_unittests.cpp
@@ -73,71 +73,9 @@ static void test_bsearch() {
}
}
-static void dump(const GrClip& clip, const char message[]) {
- GrPrintf("--- dump clip %s\n", message);
- GrClipIter iter(clip);
- while (!iter.isDone()) {
- GrIRect r;
- iter.getRect(&r);
- GrPrintf("--- [%d %d %d %d]\n", r.fLeft, r.fTop, r.fRight, r.fBottom);
- iter.next();
- }
-}
-
-static void test_clip() {
- GrClip clip;
- GrAssert(clip.isEmpty());
- GrAssert(!clip.isRect());
- GrAssert(!clip.isComplex());
- GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0));
- GrAssert(0 == clip.countRects());
-
- clip.setRect(GrIRect(10, 10, 10, 10));
- GrAssert(clip.isEmpty());
- GrAssert(!clip.isRect());
- GrAssert(!clip.isComplex());
- GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0));
- GrAssert(0 == clip.countRects());
- dump(clip, "empty");
-
- clip.setRect(GrIRect(10, 10, 20, 20));
- GrAssert(!clip.isEmpty());
- GrAssert(clip.isRect());
- GrAssert(!clip.isComplex());
- GrAssert(clip.getBounds().equalsLTRB(10, 10, 20, 20));
- GrAssert(1 == clip.countRects());
- GrAssert(clip.getRects()[0] == clip.getBounds());
- dump(clip, "rect");
-
- clip.addRect(GrIRect(20, 20, 25, 25));
- GrAssert(!clip.isEmpty());
- GrAssert(!clip.isRect());
- GrAssert(clip.isComplex());
- GrAssert(clip.getBounds().equalsLTRB(10, 10, 25, 25));
- GrAssert(2 == clip.countRects());
- dump(clip, "complex");
-
- GrClip c1(clip);
- GrAssert(c1 == clip);
- GrClip c2;
- GrAssert(c2 != c1);
- c2 = clip;
- GrAssert(c2 == clip);
-
- clip.setEmpty();
- GrAssert(clip.isEmpty());
- GrAssert(!clip.isRect());
- GrAssert(!clip.isComplex());
- GrAssert(clip.getBounds().equalsLTRB(0, 0, 0, 0));
-
- GrAssert(c1 != clip);
- GrAssert(c2 != clip);
-}
-
void gr_run_unittests() {
test_tdarray();
test_bsearch();
- test_clip();
GrMatrix::UnitTest();
GrRedBlackTree<int>::UnitTest();
}