aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/core/SkTLList.h9
-rw-r--r--src/gpu/GrClipMaskCache.h48
-rw-r--r--src/gpu/GrClipMaskManager.cpp739
-rw-r--r--src/gpu/GrClipMaskManager.h47
-rw-r--r--src/gpu/GrSWMaskHelper.cpp2
-rw-r--r--src/gpu/GrStencilBuffer.h48
-rw-r--r--tests/ClipCacheTest.cpp13
7 files changed, 326 insertions, 580 deletions
diff --git a/src/core/SkTLList.h b/src/core/SkTLList.h
index 43554eac31..bc85daa215 100644
--- a/src/core/SkTLList.h
+++ b/src/core/SkTLList.h
@@ -5,6 +5,9 @@
* found in the LICENSE file.
*/
+#ifndef SkTLList_DEFINED
+#define SkTLList_DEFINED
+
#include "SkTInternalLList.h"
#include "SkTemplates.h"
@@ -95,6 +98,11 @@ public:
Iter headIter() const { return Iter(*this, Iter::kHead_IterStart); }
Iter tailIter() const { return Iter(*this, Iter::kTail_IterStart); }
+ T* head() { return Iter(*this, Iter::kHead_IterStart).get(); }
+ T* tail() { return Iter(*this, Iter::kTail_IterStart).get(); }
+ const T* head() const { return Iter(*this, Iter::kHead_IterStart).get(); }
+ const T* tail() const { return Iter(*this, Iter::kTail_IterStart).get(); }
+
void popHead() {
this->validate();
Node* node = fList.head();
@@ -372,3 +380,4 @@ void operator delete(void*,
#define SkNEW_INSERT_AT_LLIST_TAIL(list, type_name, args) \
SkNEW_INSERT_IN_LLIST_AFTER((list), (list)->tailIter(), type_name, args)
+#endif
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;
};
diff --git a/tests/ClipCacheTest.cpp b/tests/ClipCacheTest.cpp
index ec8169eff4..ce879d1c07 100644
--- a/tests/ClipCacheTest.cpp
+++ b/tests/ClipCacheTest.cpp
@@ -102,9 +102,8 @@ static void check_state(skiatest::Reporter* reporter,
const SkClipStack& clip,
GrTexture* mask,
const GrIRect& bound) {
- SkClipStack cacheClip;
- cache.getLastClip(&cacheClip);
- REPORTER_ASSERT(reporter, clip == cacheClip);
+ SkClipStack cacheClip;
+ REPORTER_ASSERT(reporter, clip.getTopmostGenID() == cache.getLastClipGenID());
REPORTER_ASSERT(reporter, mask == cache.getLastMask());
@@ -146,7 +145,7 @@ static void test_cache(skiatest::Reporter* reporter, GrContext* context) {
desc.fHeight = Y_SIZE;
desc.fConfig = kSkia8888_PM_GrPixelConfig;
- cache.acquireMask(clip1, desc, bound1);
+ cache.acquireMask(clip1.getTopmostGenID(), desc, bound1);
GrTexture* texture1 = cache.getLastMask();
REPORTER_ASSERT(reporter, texture1);
@@ -171,7 +170,7 @@ static void test_cache(skiatest::Reporter* reporter, GrContext* context) {
SkClipStack clip2(bound2);
- cache.acquireMask(clip2, desc, bound2);
+ cache.acquireMask(clip2.getTopmostGenID(), desc, bound2);
GrTexture* texture2 = cache.getLastMask();
REPORTER_ASSERT(reporter, texture2);
@@ -185,8 +184,8 @@ static void test_cache(skiatest::Reporter* reporter, GrContext* context) {
REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());
// check to make sure canReuse works
- REPORTER_ASSERT(reporter, cache.canReuse(clip2, bound2));
- REPORTER_ASSERT(reporter, !cache.canReuse(clip1, bound1));
+ REPORTER_ASSERT(reporter, cache.canReuse(clip2.getTopmostGenID(), bound2));
+ REPORTER_ASSERT(reporter, !cache.canReuse(clip1.getTopmostGenID(), bound1));
// pop the state
cache.pop();