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
This commit is contained in:
bsalomon@google.com 2011-08-29 15:18:41 +00:00
parent 5b86a379e9
commit ab3dee5c8a
6 changed files with 110 additions and 21 deletions

View File

@ -61,7 +61,9 @@ public:
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);

View File

@ -61,6 +61,7 @@ void GrClip::setFromRect(const GrRect& r) {
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 @@ void GrClip::setFromIRect(const GrIRect& r) {
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 @@ void GrClip::setFromIterator(GrClipIterator* iter, GrScalar tx, GrScalar ty,
}
++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);

View File

@ -415,6 +415,87 @@ void GrGpu::ConvertStencilFuncAndMask(GrStencilFunc func,
#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 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
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 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
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 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
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];

View File

@ -487,8 +487,11 @@ protected:
// 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;

View File

@ -1388,15 +1388,15 @@ void GrGpuGL::clearStencil() {
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 @@ void GrGpuGL::clearStencilClip(const GrIRect& rect) {
// 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();
}

View File

@ -105,7 +105,7 @@ protected:
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