Fix GPU assumption that clipstack begins with intersect or replace.
Review URL: http://codereview.appspot.com/4977043/
git-svn-id: http://skia.googlecode.com/svn/trunk@2183 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrClip.h b/gpu/include/GrClip.h
index 7de6c10..ba6eb44 100644
--- a/gpu/include/GrClip.h
+++ b/gpu/include/GrClip.h
@@ -61,7 +61,9 @@
GrSetOp getOp(int i) const { return fList[i].fOp; }
bool isRect() const {
- if (1 == fList.count() && kRect_ClipType == fList[0].fType) {
+ if (1 == fList.count() && kRect_ClipType == fList[0].fType &&
+ (kIntersect_SetOp == fList[0].fOp ||
+ kReplace_SetOp == fList[0].fOp)) {
// if we determined that the clip is a single rect
// we ought to have also used that rect as the bounds.
GrAssert(fConservativeBoundsValid);
diff --git a/gpu/src/GrClip.cpp b/gpu/src/GrClip.cpp
index ade8c2b..afa143a 100644
--- a/gpu/src/GrClip.cpp
+++ b/gpu/src/GrClip.cpp
@@ -61,6 +61,7 @@
fList.push_back();
fList.back().fRect = r;
fList.back().fType = kRect_ClipType;
+ fList.back().fOp = kReplace_SetOp;
fConservativeBounds = r;
fConservativeBoundsValid = true;
}
@@ -75,6 +76,7 @@
fList.push_back();
fList.back().fRect.set(r);
fList.back().fType = kRect_ClipType;
+ fList.back().fOp = kReplace_SetOp;
fConservativeBounds.set(r);
fConservativeBoundsValid = true;
}
@@ -110,7 +112,7 @@
}
++rectCount;
if (isectRectValid) {
- if (1 == rectCount || kIntersect_SetOp == e.fOp) {
+ if (kIntersect_SetOp == e.fOp) {
GrAssert(fList.count() <= 2);
if (fList.count() > 1) {
GrAssert(2 == rectCount);
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index b492952..46ce46c 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -415,6 +415,87 @@
#define SET_RANDOM_COLOR
#endif
+namespace {
+// determines how many elements at the head of the clip can be skipped and
+// whether the initial clear should be to the inside- or outside-the-clip value,
+// and what op should be used to draw the first element that isn't skipped.
+int process_initial_clip_elements(const GrClip& clip,
+ bool* clearToInside,
+ GrSetOp* startOp) {
+
+ // logically before the first element of the clip stack is
+ // processed the clip is entirely open. However, depending on the
+ // first set op we may prefer to clear to 0 for performance. We may
+ // also be able to skip the initial clip paths/rects. We loop until
+ // we cannot skip an element.
+ int curr;
+ bool done = false;
+ *clearToInside = true;
+ int count = clip.getElementCount();
+
+ for (curr = 0; curr < count && !done; ++curr) {
+ switch (clip.getOp(curr)) {
+ case kReplace_SetOp:
+ // replace ignores everything previous
+ *startOp = kReplace_SetOp;
+ *clearToInside = false;
+ done = true;
+ break;
+ case kIntersect_SetOp:
+ // if everything is initially clearToInside then intersect is
+ // same as clear to 0 and treat as a replace. Otherwise,
+ // set stays empty.
+ if (*clearToInside) {
+ *startOp = kReplace_SetOp;
+ *clearToInside = false;
+ done = true;
+ }
+ break;
+ // we can skip a leading union.
+ case kUnion_SetOp:
+ // if everything is initially outside then union is
+ // same as replace. Otherwise, every pixel is still
+ // clearToInside
+ if (!*clearToInside) {
+ *startOp = kReplace_SetOp;
+ done = true;
+ }
+ break;
+ case kXor_SetOp:
+ // xor is same as difference or replace both of which
+ // can be 1-pass instead of 2 for xor.
+ if (*clearToInside) {
+ *startOp = kDifference_SetOp;
+ } else {
+ *startOp = kReplace_SetOp;
+ }
+ done = true;
+ break;
+ case kDifference_SetOp:
+ // if all pixels are clearToInside then we have to process the
+ // difference, otherwise it has no effect and all pixels
+ // remain outside.
+ if (*clearToInside) {
+ *startOp = kDifference_SetOp;
+ done = true;
+ }
+ break;
+ case kReverseDifference_SetOp:
+ // if all pixels are clearToInside then reverse difference
+ // produces empty set. Otherise it is same as replace
+ if (*clearToInside) {
+ *clearToInside = false;
+ } else {
+ *startOp = kReplace_SetOp;
+ done = true;
+ }
+ break;
+ }
+ }
+ return done ? curr-1 : count;
+}
+}
+
bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
const GrIRect* r = NULL;
GrIRect clipRect;
@@ -475,7 +556,6 @@
AutoGeometryPush agp(this);
this->setViewMatrix(GrMatrix::I());
- this->clearStencilClip(clipRect);
this->flushScissor(NULL);
#if !VISUALIZE_COMPLEX_CLIP
this->enableState(kNoColorWrites_StateBit);
@@ -485,21 +565,17 @@
int count = clip.getElementCount();
int clipBit = stencilBuffer->bits();
clipBit = (1 << (clipBit-1));
+
+ bool clearToInside;
+ GrSetOp startOp;
+ int start = process_initial_clip_elements(clip, &clearToInside,
+ &startOp);
- // often we'll see the first two elements of the clip are
- // the full rt size and another element intersected with it.
- // We can skip the first full-size rect and save a big rect draw.
- int firstElement = 0;
- if (clip.getElementCount() > 1 &&
- kRect_ClipType == clip.getElementType(0) &&
- kIntersect_SetOp == clip.getOp(1)&&
- clip.getRect(0).contains(bounds)) {
- firstElement = 1;
- }
+ this->clearStencilClip(clipRect, clearToInside);
// walk through each clip element and perform its set op
// with the existing clip.
- for (int c = firstElement; c < count; ++c) {
+ for (int c = start; c < count; ++c) {
GrPathFill fill;
bool fillInverted;
// enabled at bottom of loop
@@ -534,7 +610,7 @@
arp.set(pr, this, clipPath, fill, NULL);
}
- GrSetOp op = firstElement == c ? kReplace_SetOp : clip.getOp(c);
+ GrSetOp op = (c == start) ? startOp : clip.getOp(c);
int passes;
GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
diff --git a/gpu/src/GrGpu.h b/gpu/src/GrGpu.h
index 1c947ec..88b50b1 100644
--- a/gpu/src/GrGpu.h
+++ b/gpu/src/GrGpu.h
@@ -487,8 +487,11 @@
// Sets the scissor rect, or disables if rect is NULL.
virtual void flushScissor(const GrIRect* rect) = 0;
- // GrGpu subclass removes the clip from the stencil buffer
- virtual void clearStencilClip(const GrIRect& rect) = 0;
+ // GrGpu subclass sets clip bit in the stencil buffer. The subclass is
+ // free to clear the remaining bits to zero if masked clears are more
+ // expensive than clearing all bits.
+ virtual void clearStencilClip(const GrIRect& rect, bool insideClip) = 0;
+
// clears the entire stencil buffer to 0
virtual void clearStencil() = 0;
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index 936ada3..5054229 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -1388,15 +1388,15 @@
fHWDrawState.fStencilSettings.invalidate();
}
-void GrGpuGL::clearStencilClip(const GrIRect& rect) {
+void GrGpuGL::clearStencilClip(const GrIRect& rect, bool insideClip) {
GrAssert(NULL != fCurrDrawState.fRenderTarget);
// this should only be called internally when we know we have a
// stencil buffer.
GrAssert(NULL != fCurrDrawState.fRenderTarget->getStencilBuffer());
-#if 0
GrGLint stencilBitCount =
fCurrDrawState.fRenderTarget->getStencilBuffer()->bits();
+#if 0
GrAssert(stencilBitCount > 0);
GrGLint clipStencilMask = (1 << (stencilBitCount - 1));
#else
@@ -1407,10 +1407,16 @@
// zero the client's clip bits. So we just clear the whole thing.
static const GrGLint clipStencilMask = ~0;
#endif
+ GrGLint value;
+ if (insideClip) {
+ value = (1 << (stencilBitCount - 1));
+ } else {
+ value = 0;
+ }
this->flushRenderTarget(&GrIRect::EmptyIRect());
this->flushScissor(&rect);
GL_CALL(StencilMask(clipStencilMask));
- GL_CALL(ClearStencil(0));
+ GL_CALL(ClearStencil(value));
GL_CALL(Clear(GR_GL_STENCIL_BUFFER_BIT));
fHWDrawState.fStencilSettings.invalidate();
}
diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h
index 30a50e1..47aca3c 100644
--- a/gpu/src/GrGpuGL.h
+++ b/gpu/src/GrGpuGL.h
@@ -105,7 +105,7 @@
uint32_t numVertices);
virtual void flushScissor(const GrIRect* rect);
virtual void clearStencil();
- virtual void clearStencilClip(const GrIRect& rect);
+ virtual void clearStencilClip(const GrIRect& rect, bool insideClip);
virtual int getMaxEdges() const;
// binds texture unit in GL