diff options
author | bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-12-06 20:58:57 +0000 |
---|---|---|
committer | bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-12-06 20:58:57 +0000 |
commit | 4c2443e36fdc6c095b17e90baa4a2f26a6f00b08 (patch) | |
tree | 48a14bb302faa65074efad65e015281f24285aec /src/gpu | |
parent | 12b4e27ae1a29460e91a59f38122483e1faec697 (diff) |
Use ReduceClipStack in ClipMaskManager.
R=robertphillips@google.com
This will require some gpu rebaselining (complexclip_rect_aa, complexclip_aa, aaclip, simpleaaclip, complexclip_aa_layer)xy
Review URL: https://codereview.appspot.com/6884051
git-svn-id: http://skia.googlecode.com/svn/trunk@6694 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/gpu')
-rw-r--r-- | src/gpu/GrClipMaskCache.h | 48 | ||||
-rw-r--r-- | src/gpu/GrClipMaskManager.cpp | 739 | ||||
-rw-r--r-- | src/gpu/GrClipMaskManager.h | 47 | ||||
-rw-r--r-- | src/gpu/GrSWMaskHelper.cpp | 2 | ||||
-rw-r--r-- | src/gpu/GrStencilBuffer.h | 48 |
5 files changed, 311 insertions, 573 deletions
diff --git a/src/gpu/GrClipMaskCache.h b/src/gpu/GrClipMaskCache.h index 6722d70362..9a091c4b3a 100644 --- a/src/gpu/GrClipMaskCache.h +++ b/src/gpu/GrClipMaskCache.h @@ -31,18 +31,22 @@ public: } } - bool canReuse(const SkClipStack& clip, const GrIRect& devBounds) { + bool canReuse(int32_t clipGenID, const SkIRect& bounds) { - if (fStack.empty()) { - GrAssert(false); + SkASSERT(clipGenID != SkClipStack::kWideOpenGenID); + SkASSERT(clipGenID != SkClipStack::kEmptyGenID); + + if (SkClipStack::kInvalidGenID == clipGenID) { return false; } GrClipStackFrame* back = (GrClipStackFrame*) fStack.back(); + // We could reuse the mask if bounds is a subset of last bounds. We'd have to communicate + // an offset to the caller. if (back->fLastMask.texture() && - back->fLastBound == devBounds && - clip == back->fLastClip) { + back->fLastBound == bounds && + back->fLastClipGenID == clipGenID) { return true; } @@ -79,17 +83,13 @@ public: } } - void getLastClip(SkClipStack* clip) const { + int32_t getLastClipGenID() const { if (fStack.empty()) { - GrAssert(false); - clip->reset(); - return; + return SkClipStack::kInvalidGenID; } - GrClipStackFrame* back = (GrClipStackFrame*) fStack.back(); - - *clip = back->fLastClip; + return ((GrClipStackFrame*) fStack.back())->fLastClipGenID; } GrTexture* getLastMask() { @@ -116,7 +116,7 @@ public: return back->fLastMask.texture(); } - void acquireMask(const SkClipStack& clip, + void acquireMask(int32_t clipGenID, const GrTextureDesc& desc, const GrIRect& bound) { @@ -127,7 +127,7 @@ public: GrClipStackFrame* back = (GrClipStackFrame*) fStack.back(); - back->acquireMask(fContext, clip, desc, bound); + back->acquireMask(fContext, clipGenID, desc, bound); } int getLastMaskWidth() const { @@ -193,20 +193,19 @@ public: } } -protected: private: struct GrClipStackFrame { GrClipStackFrame() { - reset(); + this->reset(); } void acquireMask(GrContext* context, - const SkClipStack& clip, + int32_t clipGenID, const GrTextureDesc& desc, const GrIRect& bound) { - fLastClip = clip; + fLastClipGenID = clipGenID; fLastMask.set(context, desc); @@ -214,7 +213,7 @@ private: } void reset () { - fLastClip.reset(); + fLastClipGenID = SkClipStack::kInvalidGenID; GrTextureDesc desc; @@ -222,13 +221,12 @@ private: fLastBound.setEmpty(); } - SkClipStack fLastClip; - // The mask's width & height values are used in setupDrawStateAAClip to - // correctly scale the uvs for geometry drawn with this mask + int32_t fLastClipGenID; + // The mask's width & height values are used by GrClipMaskManager to correctly scale the + // texture coords for the geometry drawn with this mask. GrAutoScratchTexture fLastMask; - // fLastBound stores the bounding box of the clip mask in canvas - // space. The left and top fields are used to offset the uvs for - // geometry drawn with this mask (in setupDrawStateAAClip) + // fLastBound stores the bounding box of the clip mask in clip-stack space. This rect is + // used by GrClipMaskManager to position a rect and compute texture coords for the mask. GrIRect fLastBound; }; diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp index 5dfc6faa35..d076ced281 100644 --- a/src/gpu/GrClipMaskManager.cpp +++ b/src/gpu/GrClipMaskManager.cpp @@ -23,10 +23,11 @@ GR_DEFINE_RESOURCE_CACHE_DOMAIN(GrClipMaskManager, GetAlphaMaskDomain) #define GR_AA_CLIP 1 -#define GR_SW_CLIP 1 typedef SkClipStack::Element Element; +using namespace GrReducedClip; + //////////////////////////////////////////////////////////////////////////////// namespace { // set up the draw state to enable the aa clipping mask. Besides setting up the @@ -48,6 +49,7 @@ void setup_drawstate_aaclip(GrGpu* gpu, drawState->stage(kMaskStage)->reset(); SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height()); + // This could be a long-lived effect that is cached with the alpha-mask. drawState->stage(kMaskStage)->setEffect( GrTextureDomainEffect::Create(result, mat, @@ -64,27 +66,6 @@ bool path_needs_SW_renderer(GrContext* context, return NULL == context->getPathRenderer(path, stroke, gpu, doAA, false); } -/** - * Does any individual clip in 'clipIn' use anti-aliasing? - */ -bool requires_AA(const SkClipStack& clipIn) { - - SkClipStack::Iter iter; - iter.reset(clipIn, SkClipStack::Iter::kBottom_IterStart); - - const Element* element = NULL; - for (element = iter.skipToTopmost(SkRegion::kReplace_Op); - NULL != element; - element = iter.next()) { - - if (element->isAA()) { - return true; - } - } - - return false; -} - } /* @@ -92,23 +73,17 @@ bool requires_AA(const SkClipStack& clipIn) { * will be used on any element. If so, it returns true to indicate that the * entire clip should be rendered in SW and then uploaded en masse to the gpu. */ -bool GrClipMaskManager::useSWOnlyPath(const SkClipStack& clipIn) { +bool GrClipMaskManager::useSWOnlyPath(const ElementList& elements) { // TODO: generalize this function so that when // a clip gets complex enough it can just be done in SW regardless // of whether it would invoke the GrSoftwarePathRenderer. bool useSW = false; - - SkClipStack::Iter iter(clipIn, SkClipStack::Iter::kBottom_IterStart); - const Element* element = NULL; - SkStroke stroke; stroke.setDoFill(true); - - for (element = iter.skipToTopmost(SkRegion::kReplace_Op); - NULL != element; - element = iter.next()) { - + + for (ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) { + const Element* element = iter.get(); // rects can always be drawn directly w/o using the software path // so only paths need to be checked if (Element::kPath_Type == element->getType() && @@ -116,11 +91,10 @@ bool GrClipMaskManager::useSWOnlyPath(const SkClipStack& clipIn) { element->getPath(), stroke, element->isAA())) { - useSW = true; + return true; } } - - return useSW; + return false; } //////////////////////////////////////////////////////////////////////////////// @@ -129,102 +103,111 @@ bool GrClipMaskManager::useSWOnlyPath(const SkClipStack& clipIn) { bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn) { fCurrClipMaskType = kNone_ClipMaskType; + ElementList elements(16); + InitialState initialState; + SkIRect clipSpaceIBounds; + bool requiresAA; + bool isRect = false; + GrDrawState* drawState = fGpu->drawState(); - if (!drawState->isClipState() || clipDataIn->fClipStack->isWideOpen()) { - fGpu->disableScissor(); - this->setGpuStencil(); - return true; - } - GrRenderTarget* rt = drawState->getRenderTarget(); + const GrRenderTarget* rt = drawState->getRenderTarget(); // GrDrawTarget should have filtered this for us GrAssert(NULL != rt); - GrIRect devClipBounds; - bool isIntersectionOfRects = false; + bool ignoreClip = !drawState->isClipState() || clipDataIn->fClipStack->isWideOpen(); + + if (!ignoreClip) { + SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height()); + clipSpaceRTIBounds.offset(clipDataIn->fOrigin); + ReduceClipStack(*clipDataIn->fClipStack, + clipSpaceRTIBounds, + &elements, + &initialState, + &clipSpaceIBounds, + &requiresAA); + if (elements.isEmpty()) { + if (kAllIn_InitialState == initialState) { + ignoreClip = clipSpaceIBounds == clipSpaceRTIBounds; + isRect = true; + } else { + return false; + } + } + } - clipDataIn->getConservativeBounds(rt, &devClipBounds, &isIntersectionOfRects); - if (devClipBounds.isEmpty()) { - return false; + if (ignoreClip) { + fGpu->disableScissor(); + this->setGpuStencil(); + return true; } -#if GR_SW_CLIP - bool requiresAA = requires_AA(*clipDataIn->fClipStack); +#if GR_AA_CLIP + // TODO: catch isRect && requiresAA and use clip planes if available rather than a mask. // If MSAA is enabled we can do everything in the stencil buffer. - // Otherwise check if we should just create the entire clip mask - // in software (this will only happen if the clip mask is anti-aliased - // and too complex for the gpu to handle in its entirety) - if (0 == rt->numSamples() && - requiresAA && - this->useSWOnlyPath(*clipDataIn->fClipStack)) { - // The clip geometry is complex enough that it will be more - // efficient to create it entirely in software + if (0 == rt->numSamples() && requiresAA) { + int32_t genID = clipDataIn->fClipStack->getTopmostGenID(); GrTexture* result = NULL; - GrIRect devBound; - if (this->createSoftwareClipMask(*clipDataIn, &result, &devBound)) { - setup_drawstate_aaclip(fGpu, result, devBound); - fGpu->disableScissor(); - this->setGpuStencil(); - return true; - } - // if SW clip mask creation fails fall through to the other - // two possible methods (bottoming out at stencil clipping) - } -#endif // GR_SW_CLIP + if (this->useSWOnlyPath(elements)) { + // The clip geometry is complex enough that it will be more efficient to create it + // entirely in software + result = this->createSoftwareClipMask(genID, + initialState, + elements, + clipSpaceIBounds); + } else { + result = this->createAlphaClipMask(genID, + initialState, + elements, + clipSpaceIBounds); + } -#if GR_AA_CLIP - // If MSAA is enabled use the (faster) stencil path for AA clipping - // otherwise the alpha clip mask is our only option - if (0 == rt->numSamples() && requiresAA) { - // Since we are going to create a destination texture of the correct - // size for the mask (rather than being bound by the size of the - // render target) we aren't going to use scissoring like the stencil - // path does (see scissorSettings below) - GrTexture* result = NULL; - GrIRect devBound; - if (this->createAlphaClipMask(*clipDataIn, &result, &devBound)) { - setup_drawstate_aaclip(fGpu, result, devBound); + if (NULL != result) { + // The mask's top left coord should be pinned to the rounded-out top left corner of + // clipSpace bounds. We determine the mask's position WRT to the render target here. + SkIRect rtSpaceMaskBounds = clipSpaceIBounds; + rtSpaceMaskBounds.offset(-clipDataIn->fOrigin); + setup_drawstate_aaclip(fGpu, result, rtSpaceMaskBounds); fGpu->disableScissor(); this->setGpuStencil(); return true; } - - // if alpha clip mask creation fails fall through to the stencil - // buffer method + // if alpha clip mask creation fails fall through to the non-AA code paths } #endif // GR_AA_CLIP - // Either a hard (stencil buffer) clip was explicitly requested or - // an antialiased clip couldn't be created. In either case, free up - // the texture in the antialiased mask cache. - // TODO: this may require more investigation. Ganesh performs a lot of - // utility draws (e.g., clears, InOrderDrawBuffer playbacks) that hit - // the stencil buffer path. These may be "incorrectly" clearing the - // AA cache. + // Either a hard (stencil buffer) clip was explicitly requested or an anti-aliased clip couldn't + // be created. In either case, free up the texture in the anti-aliased mask cache. + // TODO: this may require more investigation. Ganesh performs a lot of utility draws (e.g., + // clears, InOrderDrawBuffer playbacks) that hit the stencil buffer path. These may be + // "incorrectly" clearing the AA cache. fAACache.reset(); // If the clip is a rectangle then just set the scissor. Otherwise, create // a stencil mask. - if (isIntersectionOfRects) { - fGpu->enableScissor(devClipBounds); + if (isRect) { + SkIRect clipRect = clipSpaceIBounds; + clipRect.offset(-clipDataIn->fOrigin); + fGpu->enableScissor(clipRect); this->setGpuStencil(); return true; } // use the stencil clip if we can't represent the clip as a rectangle. - bool useStencil = !clipDataIn->fClipStack->isWideOpen() && - !devClipBounds.isEmpty(); - - if (useStencil) { - this->createStencilClipMask(*clipDataIn, devClipBounds); - } - // This must occur after createStencilClipMask. That function may change - // the scissor. Also, it only guarantees that the stencil mask is correct - // within the bounds it was passed, so we must use both stencil and scissor - // test to the bounds for the final draw. - fGpu->enableScissor(devClipBounds); + SkIPoint clipSpaceToStencilSpaceOffset = -clipDataIn->fOrigin; + this->createStencilClipMask(initialState, + elements, + clipSpaceIBounds, + clipSpaceToStencilSpaceOffset); + + // This must occur after createStencilClipMask. That function may change the scissor. Also, it + // only guarantees that the stencil mask is correct within the bounds it was passed, so we must + // use both stencil and scissor test to the bounds for the final draw. + SkIRect scissorSpaceIBounds(clipSpaceIBounds); + scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset); + fGpu->enableScissor(scissorSpaceIBounds); this->setGpuStencil(); return true; } @@ -240,126 +223,6 @@ bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn) { #endif namespace { -/** - * Does "canvContainer" contain "devContainee"? If either is empty then - * no containment is possible. "canvContainer" is in canvas coordinates while - * "devContainee" is in device coordiates. "origin" provides the mapping between - * the two. - */ -bool contains(const SkRect& canvContainer, - const SkIRect& devContainee, - const SkIPoint& origin) { - return !devContainee.isEmpty() && !canvContainer.isEmpty() && - canvContainer.fLeft <= SkIntToScalar(devContainee.fLeft+origin.fX) && - canvContainer.fTop <= SkIntToScalar(devContainee.fTop+origin.fY) && - canvContainer.fRight >= SkIntToScalar(devContainee.fRight+origin.fX) && - canvContainer.fBottom >= SkIntToScalar(devContainee.fBottom+origin.fY); -} - -//////////////////////////////////////////////////////////////////////////////// -// 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. -const SkClipStack::Element* process_initial_clip_elements( - SkClipStack::Iter* iter, - const GrIRect& devBounds, - bool* clearToInside, - SkRegion::Op* firstOp, - const GrClipData& clipData) { - - GrAssert(NULL != iter && NULL != clearToInside && NULL != firstOp); - - // 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. - bool done = false; - *clearToInside = true; - - const SkClipStack::Element* element = NULL; - - for (element = iter->skipToTopmost(SkRegion::kReplace_Op); - NULL != element && !done; - element = iter->next()) { - switch (element->getOp()) { - case SkRegion::kReplace_Op: - // replace ignores everything previous - *firstOp = SkRegion::kReplace_Op; - *clearToInside = false; - done = true; - break; - case SkRegion::kIntersect_Op: - // if this element contains the entire bounds then we - // can skip it. - if (Element::kRect_Type == element->getType() && - contains(element->getRect(), devBounds, clipData.fOrigin)) { - break; - } - // if everything is initially clearToInside then intersect is - // same as clear to 0 and treat as a replace. Otherwise, - // set stays empty. - if (*clearToInside) { - *firstOp = SkRegion::kReplace_Op; - *clearToInside = false; - done = true; - } - break; - // we can skip a leading union. - case SkRegion::kUnion_Op: - // if everything is initially outside then union is - // same as replace. Otherwise, every pixel is still - // clearToInside - if (!*clearToInside) { - *firstOp = SkRegion::kReplace_Op; - done = true; - } - break; - case SkRegion::kXOR_Op: - // xor is same as difference or replace both of which - // can be 1-pass instead of 2 for xor. - if (*clearToInside) { - *firstOp = SkRegion::kDifference_Op; - } else { - *firstOp = SkRegion::kReplace_Op; - } - done = true; - break; - case SkRegion::kDifference_Op: - // if all pixels are clearToInside then we have to process the - // difference, otherwise it has no effect and all pixels - // remain outside. - if (*clearToInside) { - *firstOp = SkRegion::kDifference_Op; - done = true; - } - break; - case SkRegion::kReverseDifference_Op: - // if all pixels are clearToInside then reverse difference - // produces empty set. Otherwise it is same as replace - if (*clearToInside) { - *clearToInside = false; - } else { - *firstOp = SkRegion::kReplace_Op; - done = true; - } - break; - default: - GrCrash("Unknown set op."); - } - - if (done) { - // we need to break out here (rather than letting the test in - // the loop do it) since backing up the iterator is very expensive - break; - } - } - return element; -} - -} - -namespace { //////////////////////////////////////////////////////////////////////////////// // set up the OpenGL blend function to perform the specified @@ -417,42 +280,10 @@ bool draw_path_in_software(GrContext* context, GrAssert(!path.isInverseFillType()); return true; } - - -//////////////////////////////////////////////////////////////////////////////// -bool draw_path(GrContext* context, - GrGpu* gpu, - const SkPath& path, - bool doAA, - const GrIRect& resultBounds) { - SkStroke stroke; - stroke.setDoFill(true); - - GrPathRenderer* pr = context->getPathRenderer(path, stroke, gpu, doAA, false); - if (NULL == pr) { - return draw_path_in_software(context, gpu, path, doAA, resultBounds); - } - - pr->drawPath(path, stroke, gpu, doAA); - return true; -} - -// 'rect' enters in device coordinates and leaves in canvas coordinates -void device_to_canvas(SkRect* rect, const SkIPoint& origin) { - GrAssert(NULL != rect); - - rect->fLeft += SkIntToScalar(origin.fX); - rect->fTop += SkIntToScalar(origin.fY); - rect->fRight += SkIntToScalar(origin.fX); - rect->fBottom += SkIntToScalar(origin.fY); -} - } //////////////////////////////////////////////////////////////////////////////// -bool GrClipMaskManager::drawClipShape(GrTexture* target, - const SkClipStack::Element* element, - const GrIRect& resultBounds) { +bool GrClipMaskManager::drawClipShape(GrTexture* target, const SkClipStack::Element* element) { GrDrawState* drawState = fGpu->drawState(); GrAssert(NULL != drawState); @@ -460,17 +291,27 @@ bool GrClipMaskManager::drawClipShape(GrTexture* target, switch (element->getType()) { case Element::kRect_Type: + // TODO: Do rects directly to the accumulator using a aa-rect GrEffect that covers the + // entire mask bounds and writes 0 outside the rect. if (element->isAA()) { getContext()->getAARectRenderer()->fillAARect(fGpu, fGpu, element->getRect(), true); } else { fGpu->drawSimpleRect(element->getRect(), NULL); } return true; - case Element::kPath_Type: - return draw_path(this->getContext(), fGpu, - element->getPath(), - element->isAA(), - resultBounds); + case Element::kPath_Type: { + SkStroke stroke; + stroke.setDoFill(true); + GrPathRenderer* pr = this->getContext()->getPathRenderer(element->getPath(), + stroke, + fGpu, + element->isAA(), false); + if (NULL == pr) { + return false; + } + pr->drawPath(element->getPath(), stroke, fGpu, element->isAA()); + break; + } default: // something is wrong if we're trying to draw an empty element. GrCrash("Unexpected element type"); @@ -508,8 +349,7 @@ void GrClipMaskManager::mergeMask(GrTexture* dstMask, // get a texture to act as a temporary buffer for AA clip boolean operations // TODO: given the expense of createTexture we may want to just cache this too -void GrClipMaskManager::getTemp(const GrIRect& bounds, - GrAutoScratchTexture* temp) { +void GrClipMaskManager::getTemp(int width, int height, GrAutoScratchTexture* temp) { if (NULL != temp->texture()) { // we've already allocated the temp texture return; @@ -517,83 +357,58 @@ void GrClipMaskManager::getTemp(const GrIRect& bounds, GrTextureDesc desc; desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit; - desc.fWidth = bounds.width(); - desc.fHeight = bounds.height(); + desc.fWidth = width; + desc.fHeight = height; desc.fConfig = kAlpha_8_GrPixelConfig; temp->set(this->getContext(), desc); } - -void GrClipMaskManager::setupCache(const SkClipStack& clipIn, - const GrIRect& bounds) { - // Since we are setting up the cache we know the last lookup was a miss - // Free up the currently cached mask so it can be reused - fAACache.reset(); - - GrTextureDesc desc; - desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit; - desc.fWidth = bounds.width(); - desc.fHeight = bounds.height(); - desc.fConfig = kAlpha_8_GrPixelConfig; - - fAACache.acquireMask(clipIn, desc, bounds); -} - //////////////////////////////////////////////////////////////////////////////// -// Shared preamble between gpu and SW-only AA clip mask creation paths. -// Handles caching, determination of clip mask bound & allocation (if needed) -// of the result texture -// Returns true if there is no more work to be done (i.e., we got a cache hit) -bool GrClipMaskManager::clipMaskPreamble(const GrClipData& clipDataIn, - GrTexture** result, - GrIRect* devResultBounds) { - GrDrawState* origDrawState = fGpu->drawState(); - GrAssert(origDrawState->isClipState()); - - GrRenderTarget* rt = origDrawState->getRenderTarget(); - GrAssert(NULL != rt); - - // unlike the stencil path the alpha path is not bound to the size of the - // render target - determine the minimum size required for the mask - // Note: intBounds is in device (as opposed to canvas) coordinates - clipDataIn.getConservativeBounds(rt, devResultBounds); - - // need to outset a pixel since the standard bounding box computation - // path doesn't leave any room for antialiasing (esp. w.r.t. rects) - devResultBounds->outset(1, 1); +// Handles caching & allocation (if needed) of a clip alpha-mask texture for both the sw-upload +// or gpu-rendered cases. Returns true if there is no more work to be done (i.e., we got a cache +// hit) +bool GrClipMaskManager::getMaskTexture(int32_t clipStackGenID, + const SkIRect& clipSpaceIBounds, + GrTexture** result) { + bool cached = fAACache.canReuse(clipStackGenID, clipSpaceIBounds); + if (!cached) { + + // There isn't a suitable entry in the cache so we create a new texture to store the mask. + // Since we are setting up the cache we know the last lookup was a miss. Free up the + // currently cached mask so it can be reused. + fAACache.reset(); - // TODO: make sure we don't outset if bounds are still 0,0 @ min + GrTextureDesc desc; + desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit; + desc.fWidth = clipSpaceIBounds.width(); + desc.fHeight = clipSpaceIBounds.height(); + desc.fConfig = kAlpha_8_GrPixelConfig; - if (fAACache.canReuse(*clipDataIn.fClipStack, *devResultBounds)) { - *result = fAACache.getLastMask(); - fAACache.getLastBound(devResultBounds); - return true; + fAACache.acquireMask(clipStackGenID, desc, clipSpaceIBounds); } - this->setupCache(*clipDataIn.fClipStack, *devResultBounds); - return false; + *result = fAACache.getLastMask(); + return cached; } //////////////////////////////////////////////////////////////////////////////// // Create a 8-bit clip mask in alpha -bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn, - GrTexture** result, - GrIRect *devResultBounds) { - GrAssert(NULL != devResultBounds); +GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t clipStackGenID, + InitialState initialState, + const ElementList& elements, + const SkIRect& clipSpaceIBounds) { GrAssert(kNone_ClipMaskType == fCurrClipMaskType); - if (this->clipMaskPreamble(clipDataIn, result, devResultBounds)) { + GrTexture* result; + if (this->getMaskTexture(clipStackGenID, clipSpaceIBounds, &result)) { fCurrClipMaskType = kAlpha_ClipMaskType; - return true; + return result; } - // Note: 'resultBounds' is in device (as opposed to canvas) coordinates - - GrTexture* accum = fAACache.getLastMask(); - if (NULL == accum) { + if (NULL == result) { fAACache.reset(); - return false; + return NULL; } GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); @@ -601,107 +416,87 @@ bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn, GrDrawTarget::AutoGeometryPush agp(fGpu); - // The mask we generate is translated so that its upper-left corner is at devResultBounds - // upper-left corner in device space. - GrIRect maskResultBounds = GrIRect::MakeWH(devResultBounds->width(), devResultBounds->height()); - - // Set the matrix so that rendered clip elements are transformed from the space of the clip - // stack to the alpha-mask. This accounts for both translation due to the clip-origin and the - // placement of the mask within the device. + // The top-left of the mask corresponds to the top-left corner of the bounds. SkVector clipToMaskOffset = { - SkIntToScalar(-devResultBounds->fLeft - clipDataIn.fOrigin.fX), - SkIntToScalar(-devResultBounds->fTop - clipDataIn.fOrigin.fY) + SkIntToScalar(-clipSpaceIBounds.fLeft), + SkIntToScalar(-clipSpaceIBounds.fTop) }; - drawState->viewMatrix()->setTranslate(clipToMaskOffset); + // The texture may be larger than necessary, this rect represents the part of the texture + // we populate with a rasterization of the clip. + SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); - bool clearToInside; - SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning + // Set the matrix so that rendered clip elements are transformed to mask space from clip space. + drawState->viewMatrix()->setTranslate(clipToMaskOffset); - SkClipStack::Iter iter(*clipDataIn.fClipStack, - SkClipStack::Iter::kBottom_IterStart); - const Element* element = process_initial_clip_elements(&iter, - *devResultBounds, - &clearToInside, - &firstOp, - clipDataIn); // The scratch texture that we are drawing into can be substantially larger than the mask. Only // clear the part that we care about. - fGpu->clear(&maskResultBounds, - clearToInside ? 0xffffffff : 0x00000000, - accum->asRenderTarget()); - bool accumClearedToZero = !clearToInside; + fGpu->clear(&maskSpaceIBounds, + kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, + result->asRenderTarget()); GrAutoScratchTexture temp; - bool first = true; // walk through each clip element and perform its set op - for ( ; NULL != element; element = iter.next()) { + for (ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) { + const Element* element = iter.get(); SkRegion::Op op = element->getOp(); - if (first) { - first = false; - op = firstOp; - } if (SkRegion::kReplace_Op == op) { - // clear the accumulator and draw the new object directly into it - if (!accumClearedToZero) { - fGpu->clear(&maskResultBounds, 0x00000000, accum->asRenderTarget()); - } - setup_boolean_blendcoeffs(drawState, op); - this->drawClipShape(accum, element, *devResultBounds); + this->drawClipShape(result, element); } else if (SkRegion::kReverseDifference_Op == op || SkRegion::kIntersect_Op == op) { - // there is no point in intersecting a screen filling rectangle. - if (SkRegion::kIntersect_Op == op && Element::kRect_Type == element->getType() && - contains(element->getRect(), *devResultBounds, clipDataIn.fOrigin)) { - continue; - } - - getTemp(*devResultBounds, &temp); + this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp); if (NULL == temp.texture()) { fAACache.reset(); - return false; + return NULL; } - // this is the bounds of the clip element in the space of the alpha-mask. The temporary + // This is the bounds of the clip element in the space of the alpha-mask. The temporary // mask buffer can be substantially larger than the actually clip stack element. We // touch the minimum number of pixels necessary and use decal mode to combine it with // the accumulator - GrRect elementMaskBounds = element->getBounds(); - elementMaskBounds.offset(clipToMaskOffset); - GrIRect elementMaskIBounds; - elementMaskBounds.roundOut(&elementMaskIBounds); + GrIRect maskSpaceElementIBounds; + if (element->isInverseFilled()) { + maskSpaceElementIBounds = maskSpaceIBounds; + } else { + GrRect elementBounds = element->getBounds(); + elementBounds.offset(clipToMaskOffset); + elementBounds.roundOut(&maskSpaceElementIBounds); + } // clear the temp target & draw into it - fGpu->clear(&elementMaskIBounds, 0x00000000, temp.texture()->asRenderTarget()); + fGpu->clear(&maskSpaceElementIBounds, 0x00000000, temp.texture()->asRenderTarget()); setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op); - this->drawClipShape(temp.texture(), element, elementMaskIBounds); + if (!this->drawClipShape(temp.texture(), element)) { + fAACache.reset(); + return NULL; + } // Now draw into the accumulator using the real operation // and the temp buffer as a texture - this->mergeMask(accum, temp.texture(), op, maskResultBounds, elementMaskIBounds); + this->mergeMask(result, temp.texture(), op, maskSpaceIBounds, maskSpaceElementIBounds); } else { // all the remaining ops can just be directly draw into // the accumulation buffer setup_boolean_blendcoeffs(drawState, op); - this->drawClipShape(accum, element, *devResultBounds); + this->drawClipShape(result, element); } - accumClearedToZero = false; } - *result = accum; fCurrClipMaskType = kAlpha_ClipMaskType; - return true; + return result; } //////////////////////////////////////////////////////////////////////////////// // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device // (as opposed to canvas) coordinates -bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, - const GrIRect& devClipBounds) { +bool GrClipMaskManager::createStencilClipMask(InitialState initialState, + const ElementList& elements, + const SkIRect& clipSpaceIBounds, + const SkIPoint& clipSpaceToStencilOffset) { GrAssert(kNone_ClipMaskType == fCurrClipMaskType); @@ -716,21 +511,22 @@ bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, if (NULL == stencilBuffer) { return false; } + int32_t genID = elements.tail()->getGenID(); - if (stencilBuffer->mustRenderClip(clipDataIn, rt->width(), rt->height())) { + if (stencilBuffer->mustRenderClip(genID, clipSpaceIBounds, clipSpaceToStencilOffset)) { - stencilBuffer->setLastClip(clipDataIn, rt->width(), rt->height()); + stencilBuffer->setLastClip(genID, clipSpaceIBounds, clipSpaceToStencilOffset); - // 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 - // we just stashed on the SB to render from. We set it back after - // we finish drawing it into the stencil. + // we set the current clip to the bounds so that our recursive draws are scissored to them. + // We use the copy of the GrClipData we just stashed on the SB to render from. We set it + // back after we finish drawing it into the stencil. const GrClipData* oldClipData = fGpu->getClip(); - // The origin of 'newClipData' is (0, 0) so it is okay to place - // a device-coordinate bound in 'newClipStack' - SkClipStack newClipStack(devClipBounds); - GrClipData newClipData; + SkIRect stencilSpaceIBounds(clipSpaceIBounds); + stencilSpaceIBounds.offset(clipSpaceToStencilOffset); + + SkClipStack newClipStack(stencilSpaceIBounds); + GrClipData newClipData; // origin defaults to (0,0) newClipData.fClipStack = &newClipStack; fGpu->setClip(&newClipData); @@ -740,42 +536,29 @@ bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, drawState->setRenderTarget(rt); GrDrawTarget::AutoGeometryPush agp(fGpu); - if (0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { - // Add the saveLayer's offset to the view matrix rather than - // offset each individual draw - drawState->viewMatrix()->setTranslate( - SkIntToScalar(-clipDataIn.fOrigin.fX), - SkIntToScalar(-clipDataIn.fOrigin.fY)); - } + // Set the matrix so that rendered clip elements are transformed from clip to stencil space. + SkVector translate = { + SkIntToScalar(clipSpaceToStencilOffset.fX), + SkIntToScalar(clipSpaceToStencilOffset.fY) + }; + drawState->viewMatrix()->setTranslate(translate); #if !VISUALIZE_COMPLEX_CLIP drawState->enableState(GrDrawState::kNoColorWrites_StateBit); #endif int clipBit = stencilBuffer->bits(); - SkASSERT((clipBit <= 16) && - "Ganesh only handles 16b or smaller stencil buffers"); + SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); clipBit = (1 << (clipBit-1)); GrIRect devRTRect = GrIRect::MakeWH(rt->width(), rt->height()); - bool clearToInside; - SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning - - SkClipStack::Iter iter(*oldClipData->fClipStack, - SkClipStack::Iter::kBottom_IterStart); - const Element* element = process_initial_clip_elements(&iter, - devRTRect, - &clearToInside, - &firstOp, - clipDataIn); - - fGpu->clearStencilClip(devClipBounds, clearToInside); - bool first = true; + fGpu->clearStencilClip(stencilSpaceIBounds, kAllIn_InitialState == initialState); // walk through each clip element and perform its set op // with the existing clip. - for ( ; NULL != element; element = iter.next()) { + for (ElementList::Iter iter(elements.headIter()); NULL != iter.get(); iter.next()) { + const Element* element = iter.get(); SkPath::FillType fill; bool fillInverted = false; // enabled at bottom of loop @@ -794,10 +577,6 @@ bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, stroke.setDoFill(true); SkRegion::Op op = element->getOp(); - if (first) { - first = false; - op = firstOp; - } GrPathRenderer* pr = NULL; SkPath clipPath; @@ -805,12 +584,6 @@ bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, canRenderDirectToStencil = true; fill = SkPath::kEvenOdd_FillType; fillInverted = false; - // there is no point in intersecting a screen filling - // rectangle. - if (SkRegion::kIntersect_Op == op && - contains(element->getRect(), devRTRect, oldClipData->fOrigin)) { - continue; - } } else { GrAssert(Element::kPath_Type == element->getType()); clipPath = element->getPath(); @@ -823,34 +596,32 @@ bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, fGpu->setClip(oldClipData); return false; } - canRenderDirectToStencil = - !pr->requiresStencilPass(clipPath, stroke, fGpu); + canRenderDirectToStencil = !pr->requiresStencilPass(clipPath, stroke, fGpu); } int passes; GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; bool canDrawDirectToClip; // Given the renderer, the element, - // fill rule, and set operation can - // we render the element directly to - // stencil bit used for clipping. - canDrawDirectToClip = - GrStencilSettings::GetClipPasses(op, - canRenderDirectToStencil, - clipBit, - fillInverted, - &passes, - stencilSettings); + // fill rule, and set operation can + // we render the element directly to + // stencil bit used for clipping. + canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, + canRenderDirectToStencil, + clipBit, + fillInverted, + &passes, + stencilSettings); // draw the element to the client stencil bits if necessary if (!canDrawDirectToClip) { GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil, - kIncClamp_StencilOp, - kIncClamp_StencilOp, - kAlways_StencilFunc, - 0xffff, - 0x0000, - 0xffff); + kIncClamp_StencilOp, + kIncClamp_StencilOp, + kAlways_StencilFunc, + 0xffff, + 0x0000, + 0xffff); SET_RANDOM_COLOR if (Element::kRect_Type == element->getType()) { *drawState->stencil() = gDrawToStencil; @@ -882,14 +653,9 @@ bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, } } else { SET_RANDOM_COLOR - // 'devClipBounds' is already in device coordinates so the - // translation in the view matrix is inappropriate. - // Convert it to canvas space so the drawn rect will - // be in the correct location - GrRect canvClipBounds; - canvClipBounds.set(devClipBounds); - device_to_canvas(&canvClipBounds, clipDataIn.fOrigin); - fGpu->drawSimpleRect(canvClipBounds, NULL); + // The view matrix is setup to do clip space -> stencil space translation, so + // draw rect in clip space. + fGpu->drawSimpleRect(SkRect::MakeFromIRect(clipSpaceIBounds), NULL); } } } @@ -1000,8 +766,7 @@ void GrClipMaskManager::setGpuStencil() { stencilBits = stencilBuffer->bits(); } - GrAssert(fGpu->getCaps().stencilWrapOpsSupport() || - !settings.usesWrapOp()); + GrAssert(fGpu->getCaps().stencilWrapOpsSupport() || !settings.usesWrapOp()); GrAssert(fGpu->getCaps().twoSidedStencilSupport() || !settings.isTwoSided()); this->adjustStencilParams(&settings, clipMode, stencilBits); fGpu->setStencilSettings(settings); @@ -1089,67 +854,50 @@ void GrClipMaskManager::adjustStencilParams(GrStencilSettings* settings, } //////////////////////////////////////////////////////////////////////////////// -bool GrClipMaskManager::createSoftwareClipMask(const GrClipData& clipDataIn, - GrTexture** result, - GrIRect* devResultBounds) { +GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t clipStackGenID, + GrReducedClip::InitialState initialState, + const GrReducedClip::ElementList& elements, + const SkIRect& clipSpaceIBounds) { GrAssert(kNone_ClipMaskType == fCurrClipMaskType); - if (this->clipMaskPreamble(clipDataIn, result, devResultBounds)) { - return true; + GrTexture* result; + if (this->getMaskTexture(clipStackGenID, clipSpaceIBounds, &result)) { + return result; } - GrTexture* accum = fAACache.getLastMask(); - if (NULL == accum) { + if (NULL == result) { fAACache.reset(); - return false; + return NULL; } + // The mask texture may be larger than necessary. We round out the clip space bounds and pin + // the top left corner of the resulting rect to the top left of the texture. + SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); + GrSWMaskHelper helper(this->getContext()); SkMatrix matrix; - matrix.setTranslate(SkIntToScalar(-clipDataIn.fOrigin.fX), - SkIntToScalar(-clipDataIn.fOrigin.fY)); - helper.init(*devResultBounds, &matrix); + matrix.setTranslate(SkIntToScalar(-clipSpaceIBounds.fLeft), + SkIntToScalar(-clipSpaceIBounds.fTop)); + helper.init(maskSpaceIBounds, &matrix); + + helper.clear(kAllIn_InitialState == initialState ? 0xFF : 0x00); SkStroke stroke; stroke.setDoFill(true); - bool clearToInside; - SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning - - SkClipStack::Iter iter(*clipDataIn.fClipStack, - SkClipStack::Iter::kBottom_IterStart); - const Element* element = process_initial_clip_elements(&iter, - *devResultBounds, - &clearToInside, - &firstOp, - clipDataIn); - - helper.clear(clearToInside ? 0xFF : 0x00); - - bool first = true; - for ( ; NULL != element; element = iter.next()) { + for (ElementList::Iter iter(elements.headIter()) ; NULL != iter.get(); iter.next()) { + const Element* element = iter.get(); SkRegion::Op op = element->getOp(); - if (first) { - first = false; - op = firstOp; - } - if (SkRegion::kIntersect_Op == op || - SkRegion::kReverseDifference_Op == op) { - // Intersect and reverse difference require modifying pixels - // outside of the geometry that is being "drawn". In both cases - // we erase all the pixels outside of the geometry but - // leave the pixels inside the geometry alone. For reverse - // difference we invert all the pixels before clearing the ones - // outside the geometry. + if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { + // Intersect and reverse difference require modifying pixels outside of the geometry + // that is being "drawn". In both cases we erase all the pixels outside of the geometry + // but leave the pixels inside the geometry alone. For reverse difference we invert all + // the pixels before clearing the ones outside the geometry. if (SkRegion::kReverseDifference_Op == op) { - SkRect temp; - temp.set(*devResultBounds); - temp.offset(SkIntToScalar(clipDataIn.fOrigin.fX), - SkIntToScalar(clipDataIn.fOrigin.fX)); - + SkRect temp = SkRect::MakeFromIRect(clipSpaceIBounds); // invert the entire scene helper.draw(temp, SkRegion::kXOR_Op, false, 0xFF); } @@ -1186,17 +934,10 @@ bool GrClipMaskManager::createSoftwareClipMask(const GrClipData& clipDataIn, } } - // Because we are using the scratch texture cache, "accum" may be - // larger than expected and have some cruft in the areas we aren't using. - // Clear it out. - fGpu->clear(NULL, 0x00000000, accum->asRenderTarget()); - - helper.toTexture(accum, clearToInside ? 0xFF : 0x00); - - *result = accum; + helper.toTexture(result, kAllIn_InitialState == initialState ? 0xFF : 0x00); fCurrClipMaskType = kAlpha_ClipMaskType; - return true; + return result; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h index f4554b67c4..de16221d58 100644 --- a/src/gpu/GrClipMaskManager.h +++ b/src/gpu/GrClipMaskManager.h @@ -12,6 +12,7 @@ #include "GrContext.h" #include "GrNoncopyable.h" #include "GrRect.h" +#include "GrReducedClip.h" #include "GrStencil.h" #include "GrTexture.h" @@ -111,23 +112,33 @@ private: GrClipMaskCache fAACache; // cache for the AA path - bool createStencilClipMask(const GrClipData& clipDataIn, - const GrIRect& devClipBounds); - bool createAlphaClipMask(const GrClipData& clipDataIn, - GrTexture** result, - GrIRect *devResultBounds); - bool createSoftwareClipMask(const GrClipData& clipDataIn, - GrTexture** result, - GrIRect *devResultBounds); - bool clipMaskPreamble(const GrClipData& clipDataIn, - GrTexture** result, - GrIRect *devResultBounds); - - bool useSWOnlyPath(const SkClipStack& clipIn); - - bool drawClipShape(GrTexture* target, - const SkClipStack::Element* element, - const GrIRect& resultBounds); + // Draws the clip into the stencil buffer + bool createStencilClipMask(GrReducedClip::InitialState initialState, + const GrReducedClip::ElementList& elements, + const SkIRect& clipSpaceIBounds, + const SkIPoint& clipSpaceToStencilOffset); + // Creates an alpha mask of the clip. The mask is a rasterization of elements through the + // rect specified by clipSpaceIBounds. + GrTexture* createAlphaClipMask(int32_t clipStackGenID, + GrReducedClip::InitialState initialState, + const GrReducedClip::ElementList& elements, + const SkIRect& clipSpaceIBounds); + // Similar to createAlphaClipMask but it rasterizes in SW and uploads to the result texture. + GrTexture* createSoftwareClipMask(int32_t clipStackGenID, + GrReducedClip::InitialState initialState, + const GrReducedClip::ElementList& elements, + const SkIRect& clipSpaceIBounds); + + // Gets a texture to use for the clip mask. If true is returned then a cached mask was found + // that already contains the rasterization of the clip stack, otherwise an uninitialized texture + // is returned. + bool getMaskTexture(int32_t clipStackGenID, + const SkIRect& clipSpaceIBounds, + GrTexture** result); + + bool useSWOnlyPath(const GrReducedClip::ElementList& elements); + + bool drawClipShape(GrTexture* target, const SkClipStack::Element* element); void mergeMask(GrTexture* dstMask, GrTexture* srcMask, @@ -135,7 +146,7 @@ private: const GrIRect& dstBound, const GrIRect& srcBound); - void getTemp(const GrIRect& bounds, GrAutoScratchTexture* temp); + void getTemp(int width, int height, GrAutoScratchTexture* temp); void setupCache(const SkClipStack& clip, const GrIRect& bounds); diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp index 02868ef9b7..e776caa3db 100644 --- a/src/gpu/GrSWMaskHelper.cpp +++ b/src/gpu/GrSWMaskHelper.cpp @@ -144,7 +144,7 @@ void GrSWMaskHelper::toTexture(GrTexture *texture, uint8_t alpha) { GrDrawState::AutoRenderTargetRestore artr(fContext->getGpu()->drawState(), texture->asRenderTarget()); - fContext->getGpu()->clear(NULL, SkColorSetARGB(alpha, alpha, alpha, alpha)); + fContext->getGpu()->clear(NULL, GrColorPackRGBA(alpha, alpha, alpha, alpha)); texture->writePixels(0, 0, fBM.width(), fBM.height(), kAlpha_8_GrPixelConfig, diff --git a/src/gpu/GrStencilBuffer.h b/src/gpu/GrStencilBuffer.h index 7439c761bc..e4e5190598 100644 --- a/src/gpu/GrStencilBuffer.h +++ b/src/gpu/GrStencilBuffer.h @@ -33,31 +33,22 @@ public: int numSamples() const { return fSampleCnt; } // called to note the last clip drawn to this buffer. - void setLastClip(const GrClipData& clipData, int width, int height) { - // the clip stack needs to be copied separately (and deeply) since - // it could change beneath the stencil buffer - fLastClipStack = *clipData.fClipStack; - fLastClipData.fClipStack = &fLastClipStack; - fLastClipData.fOrigin = clipData.fOrigin; - fLastClipWidth = width; - fLastClipHeight = height; - GrAssert(width <= fWidth); - GrAssert(height <= fHeight); + void setLastClip(int32_t clipStackGenID, + const SkIRect& clipSpaceRect, + const SkIPoint clipSpaceToStencilOffset) { + fLastClipStackGenID = clipStackGenID; + fLastClipStackRect = clipSpaceRect; + fLastClipSpaceOffset = clipSpaceToStencilOffset; } // called to determine if we have to render the clip into SB. - bool mustRenderClip(const GrClipData& clipData, int width, int height) const { - // The clip is in device space. That is it doesn't scale to fit a - // smaller RT. It is just truncated on the right / bottom edges. - // Note that this assumes that the viewport origin never moves within - // the stencil buffer. This is valid today. - return width > fLastClipWidth || - height > fLastClipHeight || - clipData != fLastClipData; - } - - const GrClipData& getLastClip() const { - return fLastClipData; + bool mustRenderClip(int32_t clipStackGenID, + const SkIRect& clipSpaceRect, + const SkIPoint clipSpaceToStencilOffset) const { + return SkClipStack::kInvalidGenID == clipStackGenID || + fLastClipStackGenID != clipStackGenID || + fLastClipSpaceOffset != clipSpaceToStencilOffset || + !fLastClipStackRect.contains(clipSpaceRect); } // Places the sb in the cache. The cache takes a ref of the stencil buffer. @@ -72,10 +63,8 @@ protected: , fHeight(height) , fBits(bits) , fSampleCnt(sampleCnt) - , fLastClipStack() - , fLastClipData() - , fLastClipWidth(-1) - , fLastClipHeight(-1) { + , fLastClipStackGenID(SkClipStack::kInvalidGenID) { + fLastClipStackRect.setEmpty(); } private: @@ -85,10 +74,9 @@ private: int fBits; int fSampleCnt; - SkClipStack fLastClipStack; - GrClipData fLastClipData; - int fLastClipWidth; - int fLastClipHeight; + int32_t fLastClipStackGenID; + SkIRect fLastClipStackRect; + SkIPoint fLastClipSpaceOffset; typedef GrResource INHERITED; }; |