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
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 5dfc6fa..d076ced 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 @@
     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 @@
     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 @@
  * 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 @@
                                    element->getPath(),
                                    stroke,
                                    element->isAA())) {
-            useSW = true;
+            return true;
         }
     }
-
-    return useSW;
+    return false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -129,102 +103,111 @@
 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()) {
+
+    const GrRenderTarget* rt = drawState->getRenderTarget();
+    // GrDrawTarget should have filtered this for us
+    GrAssert(NULL != rt);
+
+    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;
+            }
+        }
+    }
+
+    if (ignoreClip) {
         fGpu->disableScissor();
         this->setGpuStencil();
         return true;
     }
 
-    GrRenderTarget* rt = drawState->getRenderTarget();
-    // GrDrawTarget should have filtered this for us
-    GrAssert(NULL != rt);
-
-    GrIRect devClipBounds;
-    bool isIntersectionOfRects = false;
-
-    clipDataIn->getConservativeBounds(rt, &devClipBounds, &isIntersectionOfRects);
-    if (devClipBounds.isEmpty()) {
-        return false;
-    }
-
-#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
-        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 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)
+        int32_t genID = clipDataIn->fClipStack->getTopmostGenID();
         GrTexture* result = NULL;
-        GrIRect devBound;
-        if (this->createAlphaClipMask(*clipDataIn, &result, &devBound)) {
-            setup_drawstate_aaclip(fGpu, result, devBound);
+
+        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 (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();
+    SkIPoint clipSpaceToStencilSpaceOffset = -clipDataIn->fOrigin;
+    this->createStencilClipMask(initialState,
+                                elements,
+                                clipSpaceIBounds,
+                                clipSpaceToStencilSpaceOffset);
 
-    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);
+    // 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 @@
 #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 @@
     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 @@
 
     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 @@
 
 // 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 @@
 
     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());
+// 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) {
 
-    GrRenderTarget* rt = origDrawState->getRenderTarget();
-    GrAssert(NULL != rt);
+        // 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();
 
-    // 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);
+        GrTextureDesc desc;
+        desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
+        desc.fWidth = clipSpaceIBounds.width();
+        desc.fHeight = clipSpaceIBounds.height();
+        desc.fConfig = kAlpha_8_GrPixelConfig;
 
-    // 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);
-
-    // TODO: make sure we don't outset if bounds are still 0,0 @ min
-
-    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 @@
 
     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)
     };
+    // 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());
+
+    // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
     drawState->viewMatrix()->setTranslate(clipToMaskOffset);
 
-    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);
     // 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 @@
     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 @@
         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 @@
             stroke.setDoFill(true);
 
             SkRegion::Op op = element->getOp();
-            if (first) {
-                first = false;
-                op = firstOp;
-            }
 
             GrPathRenderer* pr = NULL;
             SkPath clipPath;
@@ -805,12 +584,6 @@
                 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 @@
                     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 @@
                     }
                 } 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 @@
         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 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-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
+    for (ElementList::Iter iter(elements.headIter()) ; NULL != iter.get(); iter.next()) {
 
-    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()) {
-
+        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 @@
         }
     }
 
-    // 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;
 }
 
 ////////////////////////////////////////////////////////////////////////////////