Merge GrClipMaskManager into GrClipStackClip

TBR=bsalomon@google.com
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2196393007

Review-Url: https://codereview.chromium.org/2196393007
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
new file mode 100644
index 0000000..4bd6d55
--- /dev/null
+++ b/src/gpu/GrClipStackClip.cpp
@@ -0,0 +1,823 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrClipStackClip.h"
+
+#include "GrDrawingManager.h"
+#include "GrDrawContextPriv.h"
+#include "GrGpuResourcePriv.h"
+#include "GrStencilAttachment.h"
+#include "GrSWMaskHelper.h"
+#include "effects/GrConvexPolyEffect.h"
+#include "effects/GrRRectEffect.h"
+#include "effects/GrTextureDomain.h"
+
+typedef SkClipStack::Element Element;
+typedef GrReducedClip::InitialState InitialState;
+
+static const int kMaxAnalyticElements = 4;
+
+bool GrClipStackClip::quickContains(const SkRect& rect) const {
+    if (!fStack) {
+        return true;
+    }
+    return fStack->quickContains(rect.makeOffset(SkIntToScalar(fOrigin.x()),
+                                                 SkIntToScalar(fOrigin.y())));
+}
+
+void GrClipStackClip::getConservativeBounds(int width, int height, SkIRect* devResult,
+                                            bool* isIntersectionOfRects) const {
+    if (!fStack) {
+        devResult->setXYWH(0, 0, width, height);
+        if (isIntersectionOfRects) {
+            *isIntersectionOfRects = true;
+        }
+        return;
+    }
+    SkRect devBounds;
+    fStack->getConservativeBounds(-fOrigin.x(), -fOrigin.y(), width, height, &devBounds,
+                                  isIntersectionOfRects);
+    devBounds.roundOut(devResult);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// set up the draw state to enable the aa clipping mask. Besides setting up the
+// stage matrix this also alters the vertex layout
+static sk_sp<GrFragmentProcessor> create_fp_for_mask(GrTexture* result,
+                                                     const SkIRect &devBound) {
+    SkMatrix mat;
+    // We use device coords to compute the texture coordinates. We set our matrix to be a
+    // translation to the devBound, and then a scaling matrix to normalized coords.
+    mat.setIDiv(result->width(), result->height());
+    mat.preTranslate(SkIntToScalar(-devBound.fLeft),
+                     SkIntToScalar(-devBound.fTop));
+
+    SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
+    return sk_sp<GrFragmentProcessor>(GrTextureDomainEffect::Make(
+                                         result,
+                                         nullptr,
+                                         mat,
+                                         GrTextureDomain::MakeTexelDomain(result, domainTexels),
+                                         GrTextureDomain::kDecal_Mode,
+                                         GrTextureParams::kNone_FilterMode,
+                                         kDevice_GrCoordSet));
+}
+
+// Does the path in 'element' require SW rendering? If so, return true (and,
+// optionally, set 'prOut' to NULL. If not, return false (and, optionally, set
+// 'prOut' to the non-SW path renderer that will do the job).
+bool GrClipStackClip::PathNeedsSWRenderer(GrContext* context,
+                                          bool hasUserStencilSettings,
+                                          const GrDrawContext* drawContext,
+                                          const SkMatrix& viewMatrix,
+                                          const Element* element,
+                                          GrPathRenderer** prOut,
+                                          bool needsStencil) {
+    if (Element::kRect_Type == element->getType()) {
+        // rects can always be drawn directly w/o using the software path
+        // TODO: skip rrects once we're drawing them directly.
+        if (prOut) {
+            *prOut = nullptr;
+        }
+        return false;
+    } else {
+        // We shouldn't get here with an empty clip element.
+        SkASSERT(Element::kEmpty_Type != element->getType());
+
+        // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
+        SkPath path;
+        element->asPath(&path);
+        if (path.isInverseFillType()) {
+            path.toggleInverseFillType();
+        }
+
+        GrPathRendererChain::DrawType type;
+
+        if (needsStencil) {
+            type = element->isAA()
+                            ? GrPathRendererChain::kStencilAndColorAntiAlias_DrawType
+                            : GrPathRendererChain::kStencilAndColor_DrawType;
+        } else {
+            type = element->isAA()
+                            ? GrPathRendererChain::kColorAntiAlias_DrawType
+                            : GrPathRendererChain::kColor_DrawType;
+        }
+
+        GrShape shape(path, GrStyle::SimpleFill());
+        GrPathRenderer::CanDrawPathArgs canDrawArgs;
+        canDrawArgs.fShaderCaps = context->caps()->shaderCaps();
+        canDrawArgs.fViewMatrix = &viewMatrix;
+        canDrawArgs.fShape = &shape;
+        canDrawArgs.fAntiAlias = element->isAA();
+        canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings;
+        canDrawArgs.fIsStencilBufferMSAA = drawContext->isStencilBufferMultisampled();
+
+        // the 'false' parameter disallows use of the SW path renderer
+        GrPathRenderer* pr = context->drawingManager()->getPathRenderer(canDrawArgs, false, type);
+        if (prOut) {
+            *prOut = pr;
+        }
+        return SkToBool(!pr);
+    }
+}
+
+/*
+ * This method traverses the clip stack to see if the GrSoftwarePathRenderer
+ * 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 GrClipStackClip::UseSWOnlyPath(GrContext* context,
+                                    bool hasUserStencilSettings,
+                                    const GrDrawContext* drawContext,
+                                    const SkVector& clipToMaskOffset,
+                                    const GrReducedClip::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.
+
+    // Set the matrix so that rendered clip elements are transformed to mask space from clip
+    // space.
+    const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMaskOffset.fY);
+
+    for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
+        const Element* element = iter.get();
+
+        SkRegion::Op op = element->getOp();
+        bool invert = element->isInverseFilled();
+        bool needsStencil = invert ||
+                            SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op;
+
+        if (PathNeedsSWRenderer(context, hasUserStencilSettings,
+                                drawContext, translate, element, nullptr, needsStencil)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static bool get_analytic_clip_processor(const GrReducedClip::ElementList& elements,
+                                        bool abortIfAA,
+                                        const SkVector& clipToRTOffset,
+                                        const SkRect& drawBounds,
+                                        sk_sp<GrFragmentProcessor>* resultFP) {
+    SkRect boundsInClipSpace;
+    boundsInClipSpace = drawBounds.makeOffset(-clipToRTOffset.fX, -clipToRTOffset.fY);
+    SkASSERT(elements.count() <= kMaxAnalyticElements);
+    SkSTArray<kMaxAnalyticElements, sk_sp<GrFragmentProcessor>> fps;
+    GrReducedClip::ElementList::Iter iter(elements);
+    while (iter.get()) {
+        SkRegion::Op op = iter.get()->getOp();
+        bool invert;
+        bool skip = false;
+        switch (op) {
+            case SkRegion::kReplace_Op:
+                SkASSERT(iter.get() == elements.head());
+                // Fallthrough, handled same as intersect.
+            case SkRegion::kIntersect_Op:
+                invert = false;
+                if (iter.get()->contains(boundsInClipSpace)) {
+                    skip = true;
+                }
+                break;
+            case SkRegion::kDifference_Op:
+                invert = true;
+                // We don't currently have a cheap test for whether a rect is fully outside an
+                // element's primitive, so don't attempt to set skip.
+                break;
+            default:
+                return false;
+        }
+        if (!skip) {
+            GrPrimitiveEdgeType edgeType;
+            if (iter.get()->isAA()) {
+                if (abortIfAA) {
+                    return false;
+                }
+                edgeType =
+                    invert ? kInverseFillAA_GrProcessorEdgeType : kFillAA_GrProcessorEdgeType;
+            } else {
+                edgeType =
+                    invert ? kInverseFillBW_GrProcessorEdgeType : kFillBW_GrProcessorEdgeType;
+            }
+
+            switch (iter.get()->getType()) {
+                case SkClipStack::Element::kPath_Type:
+                    fps.emplace_back(GrConvexPolyEffect::Make(edgeType, iter.get()->getPath(),
+                                                              &clipToRTOffset));
+                    break;
+                case SkClipStack::Element::kRRect_Type: {
+                    SkRRect rrect = iter.get()->getRRect();
+                    rrect.offset(clipToRTOffset.fX, clipToRTOffset.fY);
+                    fps.emplace_back(GrRRectEffect::Make(edgeType, rrect));
+                    break;
+                }
+                case SkClipStack::Element::kRect_Type: {
+                    SkRect rect = iter.get()->getRect();
+                    rect.offset(clipToRTOffset.fX, clipToRTOffset.fY);
+                    fps.emplace_back(GrConvexPolyEffect::Make(edgeType, rect));
+                    break;
+                }
+                default:
+                    break;
+            }
+            if (!fps.back()) {
+                return false;
+            }
+        }
+        iter.next();
+    }
+
+    *resultFP = nullptr;
+    if (fps.count()) {
+        *resultFP = GrFragmentProcessor::RunInSeries(fps.begin(), fps.count());
+    }
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// sort out what kind of clip mask needs to be created: alpha, stencil,
+// scissor, or entirely software
+bool GrClipStackClip::apply(GrContext* context,
+                            GrDrawContext* drawContext,
+                            const SkRect* origDevBounds,
+                            bool useHWAA,
+                            bool hasUserStencilSettings,
+                            GrAppliedClip* out) const {
+    if (!fStack || fStack->isWideOpen()) {
+        return true;
+    }
+
+    SkRect devBounds = SkRect::MakeIWH(drawContext->width(), drawContext->height());
+    if (origDevBounds && !devBounds.intersect(*origDevBounds)) {
+        return false;
+    }
+
+    const SkScalar clipX = SkIntToScalar(fOrigin.x()),
+                   clipY = SkIntToScalar(fOrigin.y());
+
+    GrReducedClip::ElementList elements;
+    int32_t genID = 0;
+    SkIRect clipSpaceIBounds;
+    bool requiresAA = false;
+
+    InitialState initialState = GrReducedClip::ReduceClipStack(*fStack,
+                                                               devBounds.makeOffset(clipX, clipY),
+                                                               &elements,
+                                                               &genID,
+                                                               &clipSpaceIBounds,
+                                                               &requiresAA);
+    if (elements.isEmpty()) {
+        if (GrReducedClip::kAllOut_InitialState == initialState || clipSpaceIBounds.isEmpty()) {
+            return false;
+        } else {
+            SkIRect scissorSpaceIBounds(clipSpaceIBounds);
+            scissorSpaceIBounds.offset(-fOrigin);
+            if (!GrClip::IsInsideClip(scissorSpaceIBounds, devBounds)) {
+                out->makeScissored(scissorSpaceIBounds);
+            }
+            return true;
+        }
+    }
+
+    // An element count of 4 was chosen because of the common pattern in Blink of:
+    //   isect RR
+    //   diff  RR
+    //   isect convex_poly
+    //   isect convex_poly
+    // when drawing rounded div borders. This could probably be tuned based on a
+    // configuration's relative costs of switching RTs to generate a mask vs
+    // longer shaders.
+    if (elements.count() <= kMaxAnalyticElements) {
+        // When there are multiple samples we want to do per-sample clipping, not compute a
+        // fractional pixel coverage.
+        bool disallowAnalyticAA = drawContext->isStencilBufferMultisampled();
+        if (disallowAnalyticAA && !drawContext->numColorSamples()) {
+            // With a single color sample, any coverage info is lost from color once it hits the
+            // color buffer anyway, so we may as well use coverage AA if nothing else in the pipe
+            // is multisampled.
+            disallowAnalyticAA = useHWAA || hasUserStencilSettings;
+        }
+        sk_sp<GrFragmentProcessor> clipFP;
+        if (requiresAA &&
+            get_analytic_clip_processor(elements, disallowAnalyticAA, {-clipX, -clipY}, devBounds,
+                                        &clipFP)) {
+            SkIRect scissorSpaceIBounds(clipSpaceIBounds);
+            scissorSpaceIBounds.offset(-fOrigin);
+            if (GrClip::IsInsideClip(scissorSpaceIBounds, devBounds)) {
+                out->makeFPBased(std::move(clipFP), SkRect::Make(scissorSpaceIBounds));
+            } else {
+                out->makeScissoredFPBased(std::move(clipFP), scissorSpaceIBounds);
+            }
+            return true;
+        }
+    }
+
+    // If the stencil buffer is multisampled we can use it to do everything.
+    if (!drawContext->isStencilBufferMultisampled() && requiresAA) {
+        sk_sp<GrTexture> result;
+
+        // The top-left of the mask corresponds to the top-left corner of the bounds.
+        SkVector clipToMaskOffset = {
+            SkIntToScalar(-clipSpaceIBounds.fLeft),
+            SkIntToScalar(-clipSpaceIBounds.fTop)
+        };
+
+        if (UseSWOnlyPath(context, hasUserStencilSettings, drawContext,
+                          clipToMaskOffset, elements)) {
+            // The clip geometry is complex enough that it will be more efficient to create it
+            // entirely in software
+            result = CreateSoftwareClipMask(context->textureProvider(),
+                                            genID,
+                                            initialState,
+                                            elements,
+                                            clipToMaskOffset,
+                                            clipSpaceIBounds);
+        } else {
+            result = CreateAlphaClipMask(context,
+                                         genID,
+                                         initialState,
+                                         elements,
+                                         clipToMaskOffset,
+                                         clipSpaceIBounds);
+            // If createAlphaClipMask fails it means UseSWOnlyPath has a bug
+            SkASSERT(result);
+        }
+
+        if (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(-fOrigin);
+            out->makeFPBased(create_fp_for_mask(result.get(), rtSpaceMaskBounds),
+                             SkRect::Make(rtSpaceMaskBounds));
+            return true;
+        }
+        // if alpha clip mask creation fails fall through to the non-AA code paths
+    }
+
+    // use the stencil clip if we can't represent the clip as a rectangle.
+    SkIPoint clipSpaceToStencilSpaceOffset = -fOrigin;
+    CreateStencilClipMask(context,
+                          drawContext,
+                          genID,
+                          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);
+    if (GrClip::IsInsideClip(scissorSpaceIBounds, devBounds)) {
+        out->makeStencil(true, devBounds);
+    } else {
+        out->makeScissoredStencil(scissorSpaceIBounds);
+    }
+    return true;
+}
+
+static bool stencil_element(GrDrawContext* dc,
+                            const GrFixedClip& clip,
+                            const GrUserStencilSettings* ss,
+                            const SkMatrix& viewMatrix,
+                            const SkClipStack::Element* element) {
+
+    // TODO: Draw rrects directly here.
+    switch (element->getType()) {
+        case Element::kEmpty_Type:
+            SkDEBUGFAIL("Should never get here with an empty element.");
+            break;
+        case Element::kRect_Type:
+            return dc->drawContextPriv().drawAndStencilRect(clip, ss,
+                                                            element->getOp(),
+                                                            element->isInverseFilled(),
+                                                            element->isAA(),
+                                                            viewMatrix, element->getRect());
+            break;
+        default: {
+            SkPath path;
+            element->asPath(&path);
+            if (path.isInverseFillType()) {
+                path.toggleInverseFillType();
+            }
+
+            return dc->drawContextPriv().drawAndStencilPath(clip, ss,
+                                                            element->getOp(),
+                                                            element->isInverseFilled(),
+                                                            element->isAA(), viewMatrix, path);
+            break;
+        }
+    }
+
+    return false;
+}
+
+static void draw_element(GrDrawContext* dc,
+                         const GrClip& clip, // TODO: can this just always be WideOpen?
+                         const GrPaint &paint,
+                         const SkMatrix& viewMatrix,
+                         const SkClipStack::Element* element) {
+
+    // TODO: Draw rrects directly here.
+    switch (element->getType()) {
+        case Element::kEmpty_Type:
+            SkDEBUGFAIL("Should never get here with an empty element.");
+            break;
+        case Element::kRect_Type:
+            dc->drawRect(clip, paint, viewMatrix, element->getRect());
+            break;
+        default: {
+            SkPath path;
+            element->asPath(&path);
+            if (path.isInverseFillType()) {
+                path.toggleInverseFillType();
+            }
+
+            dc->drawPath(clip, paint, viewMatrix, path, GrStyle::SimpleFill());
+            break;
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Create a 8-bit clip mask in alpha
+
+static void GetClipMaskKey(int32_t clipGenID, const SkIRect& bounds, GrUniqueKey* key) {
+    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
+    GrUniqueKey::Builder builder(key, kDomain, 3);
+    builder[0] = clipGenID;
+    builder[1] = SkToU16(bounds.fLeft) | (SkToU16(bounds.fRight) << 16);
+    builder[2] = SkToU16(bounds.fTop) | (SkToU16(bounds.fBottom) << 16);
+}
+
+sk_sp<GrTexture> GrClipStackClip::CreateAlphaClipMask(GrContext* context,
+                                                      int32_t elementsGenID,
+                                                      GrReducedClip::InitialState initialState,
+                                                      const GrReducedClip::ElementList& elements,
+                                                      const SkVector& clipToMaskOffset,
+                                                      const SkIRect& clipSpaceIBounds) {
+    GrResourceProvider* resourceProvider = context->resourceProvider();
+    GrUniqueKey key;
+    GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key);
+    if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) {
+        return sk_sp<GrTexture>(texture);
+    }
+
+    // There's no texture in the cache. Let's try to allocate it then.
+    GrPixelConfig config = kRGBA_8888_GrPixelConfig;
+    if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
+        config = kAlpha_8_GrPixelConfig;
+    }
+
+    sk_sp<GrDrawContext> dc(context->makeDrawContext(SkBackingFit::kApprox,
+                                                     clipSpaceIBounds.width(),
+                                                     clipSpaceIBounds.height(),
+                                                     config, nullptr));
+    if (!dc) {
+        return nullptr;
+    }
+    
+    // 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());
+
+    // The scratch texture that we are drawing into can be substantially larger than the mask. Only
+    // clear the part that we care about.
+    dc->clear(&maskSpaceIBounds,
+              GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
+              true);
+
+    // Set the matrix so that rendered clip elements are transformed to mask space from clip
+    // space.
+    const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMaskOffset.fY);
+
+    // It is important that we use maskSpaceIBounds as the stencil rect in the below loop.
+    // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first
+    // pass must not set values outside of this bounds or stencil values outside the rect won't be
+    // cleared.
+
+    // walk through each clip element and perform its set op
+    for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) {
+        const Element* element = iter.get();
+        SkRegion::Op op = element->getOp();
+        bool invert = element->isInverseFilled();
+        if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
+            GrFixedClip clip(maskSpaceIBounds);
+
+            // draw directly into the result with the stencil set to make the pixels affected
+            // by the clip shape be non-zero.
+            static constexpr GrUserStencilSettings kStencilInElement(
+                 GrUserStencilSettings::StaticInit<
+                     0xffff,
+                     GrUserStencilTest::kAlways,
+                     0xffff,
+                     GrUserStencilOp::kReplace,
+                     GrUserStencilOp::kReplace,
+                     0xffff>()
+            );
+            if (!stencil_element(dc.get(), clip, &kStencilInElement,
+                                 translate, element)) {
+                return nullptr;
+            }
+
+            // Draw to the exterior pixels (those with a zero stencil value).
+            static constexpr GrUserStencilSettings kDrawOutsideElement(
+                 GrUserStencilSettings::StaticInit<
+                     0x0000,
+                     GrUserStencilTest::kEqual,
+                     0xffff,
+                     GrUserStencilOp::kZero,
+                     GrUserStencilOp::kZero,
+                     0xffff>()
+            );
+            if (!dc->drawContextPriv().drawAndStencilRect(clip, &kDrawOutsideElement,
+                                                          op, !invert, false,
+                                                          translate,
+                                                          SkRect::Make(clipSpaceIBounds))) {
+                return nullptr;
+            }
+        } else {
+            // all the remaining ops can just be directly draw into the accumulation buffer
+            GrPaint paint;
+            paint.setAntiAlias(element->isAA());
+            paint.setCoverageSetOpXPFactory(op, false);
+
+            draw_element(dc.get(), GrNoClip(), paint, translate, element);
+        }
+    }
+
+    sk_sp<GrTexture> texture(dc->asTexture());
+    SkASSERT(texture);
+    texture->resourcePriv().setUniqueKey(key);
+    return texture;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
+// (as opposed to canvas) coordinates
+bool GrClipStackClip::CreateStencilClipMask(GrContext* context,
+                                            GrDrawContext* drawContext,
+                                            int32_t elementsGenID,
+                                            GrReducedClip::InitialState initialState,
+                                            const GrReducedClip::ElementList& elements,
+                                            const SkIRect& clipSpaceIBounds,
+                                            const SkIPoint& clipSpaceToStencilOffset) {
+    SkASSERT(drawContext);
+
+    GrStencilAttachment* stencilAttachment = context->resourceProvider()->attachStencilAttachment(
+                                                    drawContext->accessRenderTarget());
+    if (nullptr == stencilAttachment) {
+        return false;
+    }
+
+    // TODO: these need to be swapped over to using a StencilAttachmentProxy
+    if (stencilAttachment->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) {
+        stencilAttachment->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset);
+        // Set the matrix so that rendered clip elements are transformed from clip to stencil space.
+        SkVector translate = {
+            SkIntToScalar(clipSpaceToStencilOffset.fX),
+            SkIntToScalar(clipSpaceToStencilOffset.fY)
+        };
+        SkMatrix viewMatrix;
+        viewMatrix.setTranslate(translate);
+
+        // We set the current clip to the bounds so that our recursive draws are scissored to them.
+        SkIRect stencilSpaceIBounds(clipSpaceIBounds);
+        stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
+        GrFixedClip clip(stencilSpaceIBounds);
+
+        drawContext->drawContextPriv().clearStencilClip(
+                                            stencilSpaceIBounds,
+                                            GrReducedClip::kAllIn_InitialState == initialState);
+
+        // walk through each clip element and perform its set op
+        // with the existing clip.
+        for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
+            const Element* element = iter.get();
+            bool useHWAA = element->isAA() && drawContext->isStencilBufferMultisampled();
+
+            bool fillInverted = false;
+            // enabled at bottom of loop
+            clip.disableStencilClip();
+
+            // This will be used to determine whether the clip shape can be rendered into the
+            // stencil with arbitrary stencil settings.
+            GrPathRenderer::StencilSupport stencilSupport;
+
+            SkRegion::Op op = element->getOp();
+
+            GrPathRenderer* pr = nullptr;
+            SkPath clipPath;
+            if (Element::kRect_Type == element->getType()) {
+                stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
+                fillInverted = false;
+            } else {
+                element->asPath(&clipPath);
+                fillInverted = clipPath.isInverseFillType();
+                if (fillInverted) {
+                    clipPath.toggleInverseFillType();
+                }
+
+                GrShape shape(clipPath, GrStyle::SimpleFill());
+                GrPathRenderer::CanDrawPathArgs canDrawArgs;
+                canDrawArgs.fShaderCaps = context->caps()->shaderCaps();
+                canDrawArgs.fViewMatrix = &viewMatrix;
+                canDrawArgs.fShape = &shape;
+                canDrawArgs.fAntiAlias = false;
+                canDrawArgs.fHasUserStencilSettings = false;
+                canDrawArgs.fIsStencilBufferMSAA = drawContext->isStencilBufferMultisampled();
+
+                GrDrawingManager* dm = context->drawingManager();
+                pr = dm->getPathRenderer(canDrawArgs, false,
+                                         GrPathRendererChain::kStencilOnly_DrawType,
+                                         &stencilSupport);
+                if (!pr) {
+                    return false;
+                }
+            }
+
+            bool canRenderDirectToStencil =
+                GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
+            bool drawDirectToClip; // Given the renderer, the element,
+                                   // fill rule, and set operation should
+                                   // we render the element directly to
+                                   // stencil bit used for clipping.
+            GrUserStencilSettings const* const* stencilPasses =
+                GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, fillInverted,
+                                                 &drawDirectToClip);
+
+            // draw the element to the client stencil bits if necessary
+            if (!drawDirectToClip) {
+                static constexpr GrUserStencilSettings kDrawToStencil(
+                     GrUserStencilSettings::StaticInit<
+                         0x0000,
+                         GrUserStencilTest::kAlways,
+                         0xffff,
+                         GrUserStencilOp::kIncMaybeClamp,
+                         GrUserStencilOp::kIncMaybeClamp,
+                         0xffff>()
+                );
+                if (Element::kRect_Type == element->getType()) {
+                    drawContext->drawContextPriv().stencilRect(clip, &kDrawToStencil, useHWAA,
+                                                               viewMatrix, element->getRect());
+                } else {
+                    if (!clipPath.isEmpty()) {
+                        GrShape shape(clipPath, GrStyle::SimpleFill());
+                        if (canRenderDirectToStencil) {
+                            GrPaint paint;
+                            paint.setXPFactory(GrDisableColorXPFactory::Make());
+                            paint.setAntiAlias(element->isAA());
+
+                            GrPathRenderer::DrawPathArgs args;
+                            args.fResourceProvider = context->resourceProvider();
+                            args.fPaint = &paint;
+                            args.fUserStencilSettings = &kDrawToStencil;
+                            args.fDrawContext = drawContext;
+                            args.fClip = &clip;
+                            args.fViewMatrix = &viewMatrix;
+                            args.fShape = &shape;
+                            args.fAntiAlias = false;
+                            args.fGammaCorrect = false;
+                            pr->drawPath(args);
+                        } else {
+                            GrPathRenderer::StencilPathArgs args;
+                            args.fResourceProvider = context->resourceProvider();
+                            args.fDrawContext = drawContext;
+                            args.fClip = &clip;
+                            args.fViewMatrix = &viewMatrix;
+                            args.fIsAA = element->isAA();
+                            args.fShape = &shape;
+                            pr->stencilPath(args);
+                        }
+                    }
+                }
+            }
+
+            // now we modify the clip bit by rendering either the clip
+            // element directly or a bounding rect of the entire clip.
+            for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {
+
+                if (drawDirectToClip) {
+                    if (Element::kRect_Type == element->getType()) {
+                        clip.enableStencilClip(element->getRect().makeOffset(translate.fX,
+                                                                             translate.fY));
+                        drawContext->drawContextPriv().stencilRect(clip, *pass, useHWAA, viewMatrix,
+                                                                   element->getRect());
+                    } else {
+                        GrShape shape(clipPath, GrStyle::SimpleFill());
+                        GrPaint paint;
+                        SkRect bounds = clipPath.getBounds();
+                        bounds.offset(translate.fX, translate.fY);
+                        clip.enableStencilClip(bounds);
+                        paint.setXPFactory(GrDisableColorXPFactory::Make());
+                        paint.setAntiAlias(element->isAA());
+                        GrPathRenderer::DrawPathArgs args;
+                        args.fResourceProvider = context->resourceProvider();
+                        args.fPaint = &paint;
+                        args.fUserStencilSettings = *pass;
+                        args.fDrawContext = drawContext;
+                        args.fClip = &clip;
+                        args.fViewMatrix = &viewMatrix;
+                        args.fShape = &shape;
+                        args.fAntiAlias = false;
+                        args.fGammaCorrect = false;
+                        pr->drawPath(args);
+                    }
+                } else {
+                    // The view matrix is setup to do clip space -> stencil space translation, so
+                    // draw rect in clip space.
+                    SkRect bounds = SkRect::Make(clipSpaceIBounds);
+                    bounds.offset(translate.fX, translate.fY);
+                    clip.enableStencilClip(bounds);
+                    drawContext->drawContextPriv().stencilRect(clip, *pass, false, viewMatrix,
+                                                               SkRect::Make(clipSpaceIBounds));
+                }
+            }
+        }
+    }
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+sk_sp<GrTexture> GrClipStackClip::CreateSoftwareClipMask(GrTextureProvider* texProvider,
+                                                         int32_t elementsGenID,
+                                                         GrReducedClip::InitialState initialState,
+                                                         const GrReducedClip::ElementList& elements,
+                                                         const SkVector& clipToMaskOffset,
+                                                         const SkIRect& clipSpaceIBounds) {
+    GrUniqueKey key;
+    GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key);
+    if (GrTexture* texture = texProvider->findAndRefTextureByUniqueKey(key)) {
+        return sk_sp<GrTexture>(texture);
+    }
+
+    // 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(texProvider);
+
+    // Set the matrix so that rendered clip elements are transformed to mask space from clip
+    // space.
+    SkMatrix translate;
+    translate.setTranslate(clipToMaskOffset);
+
+    helper.init(maskSpaceIBounds, &translate);
+    helper.clear(GrReducedClip::kAllIn_InitialState == initialState ? 0xFF : 0x00);
+
+    for (GrReducedClip::ElementList::Iter iter(elements.headIter()) ; iter.get(); iter.next()) {
+        const Element* element = iter.get();
+        SkRegion::Op op = element->getOp();
+
+        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 = SkRect::Make(clipSpaceIBounds);
+                // invert the entire scene
+                helper.drawRect(temp, SkRegion::kXOR_Op, false, 0xFF);
+            }
+            SkPath clipPath;
+            element->asPath(&clipPath);
+            clipPath.toggleInverseFillType();
+            GrShape shape(clipPath, GrStyle::SimpleFill());
+            helper.drawShape(shape, SkRegion::kReplace_Op, element->isAA(), 0x00);
+            continue;
+        }
+
+        // The other ops (union, xor, diff) only affect pixels inside
+        // the geometry so they can just be drawn normally
+        if (Element::kRect_Type == element->getType()) {
+            helper.drawRect(element->getRect(), op, element->isAA(), 0xFF);
+        } else {
+            SkPath path;
+            element->asPath(&path);
+            GrShape shape(path, GrStyle::SimpleFill());
+            helper.drawShape(shape, op, element->isAA(), 0xFF);
+        }
+    }
+
+    // Allocate clip mask texture
+    GrSurfaceDesc desc;
+    desc.fWidth = clipSpaceIBounds.width();
+    desc.fHeight = clipSpaceIBounds.height();
+    desc.fConfig = kAlpha_8_GrPixelConfig;
+
+    sk_sp<GrTexture> result(texProvider->createApproxTexture(desc));
+    if (!result) {
+        return nullptr;
+    }
+    result->resourcePriv().setUniqueKey(key);
+
+    helper.toTexture(result.get());
+
+    return result;
+}