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:
parent
5b86a379e9
commit
ab3dee5c8a
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user