Move clip mask generation into GrReducedClip

This is a temporary solution to facilitate window rectangles and make
clip mask generation more accessible for testing. The eventual goal is
to simplify clips and merge GrReducedClip into GrClipStackClip.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2263343002

Review-Url: https://codereview.chromium.org/2263343002
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 41292fe..5785553 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -430,12 +430,8 @@
     GrAuditTrail                            fAuditTrail;
 
     // TODO: have the GrClipStackClip use drawContexts and rm this friending
-    friend class GrClipStackClip;  // for access to drawingManager
-    friend class GrDrawingManager; // for access to drawingManager for ProgramUnitTest
     friend class GrContextPriv;
 
-    GrDrawingManager* drawingManager() { return fDrawingManager; }
-
     GrContext(); // init must be called after the constructor.
     bool init(GrBackend, GrBackendContext, const GrContextOptions& options);
 
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index 23c2b42..003b4a5 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -8,6 +8,7 @@
 #include "GrClipStackClip.h"
 
 #include "GrAppliedClip.h"
+#include "GrContextPriv.h"
 #include "GrDrawingManager.h"
 #include "GrDrawContextPriv.h"
 #include "GrFixedClip.h"
@@ -150,7 +151,8 @@
         canDrawArgs.fIsStencilBufferMSAA = drawContext->isStencilBufferMultisampled();
 
         // the 'false' parameter disallows use of the SW path renderer
-        GrPathRenderer* pr = context->drawingManager()->getPathRenderer(canDrawArgs, false, type);
+        GrPathRenderer* pr =
+            context->contextPriv().drawingManager()->getPathRenderer(canDrawArgs, false, type);
         if (prOut) {
             *prOut = pr;
         }
@@ -166,17 +168,17 @@
 bool GrClipStackClip::UseSWOnlyPath(GrContext* context,
                                     bool hasUserStencilSettings,
                                     const GrDrawContext* drawContext,
-                                    const SkVector& clipToMaskOffset,
-                                    const ElementList& elements) {
+                                    const GrReducedClip& reducedClip) {
     // 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);
+    SkMatrix translate;
+    translate.setTranslate(SkIntToScalar(-reducedClip.left()), SkIntToScalar(-reducedClip.top()));
 
-    for (ElementList::Iter iter(elements); iter.get(); iter.next()) {
+    for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) {
         const Element* element = iter.get();
 
         SkRegion::Op op = element->getOp();
@@ -351,21 +353,12 @@
     // If the stencil buffer is multisampled we can use it to do everything.
     if (!drawContext->isStencilBufferMultisampled() && reducedClip.requiresAA()) {
         sk_sp<GrTexture> result;
-
-        // The top-left of the mask corresponds to the top-left corner of the bounds.
-        SkVector clipToMaskOffset = {
-            SkIntToScalar(-reducedClip.left()),
-            SkIntToScalar(-reducedClip.top())
-        };
-
-        if (UseSWOnlyPath(context, hasUserStencilSettings, drawContext,
-                          clipToMaskOffset, reducedClip.elements())) {
+        if (UseSWOnlyPath(context, hasUserStencilSettings, drawContext, reducedClip)) {
             // The clip geometry is complex enough that it will be more efficient to create it
             // entirely in software
-            result = CreateSoftwareClipMask(context->textureProvider(), reducedClip,
-                                            clipToMaskOffset);
+            result = CreateSoftwareClipMask(context->textureProvider(), reducedClip);
         } else {
-            result = CreateAlphaClipMask(context, reducedClip, clipToMaskOffset);
+            result = CreateAlphaClipMask(context, reducedClip);
             // If createAlphaClipMask fails it means UseSWOnlyPath has a bug
             SkASSERT(result);
         }
@@ -382,75 +375,24 @@
     }
 
     // use the stencil clip if we can't represent the clip as a rectangle.
-    SkIPoint clipSpaceToStencilSpaceOffset = -fOrigin;
-    CreateStencilClipMask(context, drawContext, reducedClip, clipSpaceToStencilSpaceOffset);
+    // TODO: these need to be swapped over to using a StencilAttachmentProxy
+    GrStencilAttachment* stencilAttachment =
+        context->resourceProvider()->attachStencilAttachment(drawContext->accessRenderTarget());
+    if (nullptr == stencilAttachment) {
+        SkDebugf("WARNING: failed to attach stencil buffer for clip mask. Clip will be ignored.\n");
+        return true;
+    }
+
+    if (stencilAttachment->mustRenderClip(reducedClip.elementsGenID(), reducedClip.ibounds(),
+                                          fOrigin)) {
+        reducedClip.drawStencilClipMask(context, drawContext, fOrigin);
+        stencilAttachment->setLastClip(reducedClip.elementsGenID(), reducedClip.ibounds(),
+                                       fOrigin);
+    }
     out->addStencilClip();
     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
 
@@ -463,8 +405,7 @@
 }
 
 sk_sp<GrTexture> GrClipStackClip::CreateAlphaClipMask(GrContext* context,
-                                                      const GrReducedClip& reducedClip,
-                                                      const SkVector& clipToMaskOffset) {
+                                                      const GrReducedClip& reducedClip) {
     GrResourceProvider* resourceProvider = context->resourceProvider();
     GrUniqueKey key;
     GetClipMaskKey(reducedClip.elementsGenID(), reducedClip.ibounds(), &key);
@@ -485,72 +426,9 @@
     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(reducedClip.width(), reducedClip.height());
-    GrFixedClip clip(maskSpaceIBounds);
 
-    // The scratch texture that we are drawing into can be substantially larger than the mask. Only
-    // clear the part that we care about.
-    GrColor initialCoverage = InitialState::kAllIn == reducedClip.initialState() ? -1 : 0;
-    dc->drawContextPriv().clear(clip, initialCoverage, 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 (ElementList::Iter iter(reducedClip.elements()); 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) {
-            // 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(reducedClip.ibounds()))) {
-                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(), clip, paint, translate, element);
-        }
+    if (!reducedClip.drawAlphaClipMask(dc.get())) {
+        return nullptr;
     }
 
     sk_sp<GrTexture> texture(dc->asTexture());
@@ -559,214 +437,8 @@
     return texture;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Create a 1-bit clip mask in the stencil buffer.
-
-class StencilClip final : public GrClip {
-public:
-    StencilClip(const SkIRect& scissorRect) : fFixedClip(scissorRect) {}
-    const GrFixedClip& fixedClip() const { return fFixedClip; }
-
-private:
-    bool quickContains(const SkRect&) const final {
-        return false;
-    }
-    void getConservativeBounds(int width, int height, SkIRect* devResult, bool* iior) const final {
-        fFixedClip.getConservativeBounds(width, height, devResult, iior);
-    }
-    bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const final {
-        return false;
-    }
-    bool apply(GrContext* context, GrDrawContext* drawContext, bool useHWAA,
-               bool hasUserStencilSettings, GrAppliedClip* out) const final {
-        if (!fFixedClip.apply(context, drawContext, useHWAA, hasUserStencilSettings, out)) {
-            return false;
-        }
-        out->addStencilClip();
-        return true;
-    }
-
-    GrFixedClip fFixedClip;
-
-    typedef GrClip INHERITED;
-};
-
-bool GrClipStackClip::CreateStencilClipMask(GrContext* context,
-                                            GrDrawContext* drawContext,
-                                            const GrReducedClip& reducedClip,
-                                            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(reducedClip.elementsGenID(), reducedClip.ibounds(),
-                                          clipSpaceToStencilOffset)) {
-        stencilAttachment->setLastClip(reducedClip.elementsGenID(), reducedClip.ibounds(),
-                                       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(reducedClip.ibounds());
-        stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
-        StencilClip stencilClip(stencilSpaceIBounds);
-
-        bool initialState = InitialState::kAllIn == reducedClip.initialState();
-        drawContext->drawContextPriv().clearStencilClip(stencilClip.fixedClip(), initialState);
-
-        // walk through each clip element and perform its set op
-        // with the existing clip.
-        for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) {
-            const Element* element = iter.get();
-            bool useHWAA = element->isAA() && drawContext->isStencilBufferMultisampled();
-
-            bool fillInverted = false;
-
-            // 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(stencilClip.fixedClip(),
-                                                               &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 = &stencilClip.fixedClip();
-                            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 = &stencilClip.fixedClip();
-                            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()) {
-                        drawContext->drawContextPriv().stencilRect(stencilClip, *pass, useHWAA,
-                                                                   viewMatrix, element->getRect());
-                    } else {
-                        GrShape shape(clipPath, GrStyle::SimpleFill());
-                        GrPaint paint;
-                        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 = &stencilClip;
-                        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.
-                    drawContext->drawContextPriv().stencilRect(stencilClip, *pass,
-                                                               false, viewMatrix,
-                                                               SkRect::Make(reducedClip.ibounds()));
-                }
-            }
-        }
-    }
-    return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
 sk_sp<GrTexture> GrClipStackClip::CreateSoftwareClipMask(GrTextureProvider* texProvider,
-                                                         const GrReducedClip& reducedClip,
-                                                         const SkVector& clipToMaskOffset) {
+                                                         const GrReducedClip& reducedClip) {
     GrUniqueKey key;
     GetClipMaskKey(reducedClip.elementsGenID(), reducedClip.ibounds(), &key);
     if (GrTexture* texture = texProvider->findAndRefTextureByUniqueKey(key)) {
@@ -782,7 +454,7 @@
     // Set the matrix so that rendered clip elements are transformed to mask space from clip
     // space.
     SkMatrix translate;
-    translate.setTranslate(clipToMaskOffset);
+    translate.setTranslate(SkIntToScalar(-reducedClip.left()), SkIntToScalar(-reducedClip.top()));
 
     helper.init(maskSpaceIBounds, &translate);
     helper.clear(InitialState::kAllIn == reducedClip.initialState() ? 0xFF : 0x00);
diff --git a/src/gpu/GrClipStackClip.h b/src/gpu/GrClipStackClip.h
index ad143d6..075d1d8 100644
--- a/src/gpu/GrClipStackClip.h
+++ b/src/gpu/GrClipStackClip.h
@@ -49,28 +49,17 @@
                                     GrPathRenderer** prOut,
                                     bool needsStencil);
 
-    // Draws the clip into the stencil buffer
-    static bool CreateStencilClipMask(GrContext*,
-                                      GrDrawContext*,
-                                      const GrReducedClip&,
-                                      const SkIPoint& clipSpaceToStencilOffset);
-
     // Creates an alpha mask of the clip. The mask is a rasterization of elements through the
     // rect specified by clipSpaceIBounds.
-    static sk_sp<GrTexture> CreateAlphaClipMask(GrContext*,
-                                                const GrReducedClip&,
-                                                const SkVector& clipToMaskOffset);
+    static sk_sp<GrTexture> CreateAlphaClipMask(GrContext*, const GrReducedClip&);
 
     // Similar to createAlphaClipMask but it rasterizes in SW and uploads to the result texture.
-    static sk_sp<GrTexture> CreateSoftwareClipMask(GrTextureProvider*,
-                                                   const GrReducedClip&,
-                                                   const SkVector& clipToMaskOffset);
+    static sk_sp<GrTexture> CreateSoftwareClipMask(GrTextureProvider*, const GrReducedClip&);
 
    static bool UseSWOnlyPath(GrContext*,
                              bool hasUserStencilSettings,
                              const GrDrawContext*,
-                             const SkVector& clipToMaskOffset,
-                             const GrReducedClip::ElementList& elements);
+                             const GrReducedClip&);
 
     static GrTexture* CreateCachedMask(int width, int height, const GrUniqueKey& key,
                                        bool renderTarget);
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 6221fea..0157760 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -627,9 +627,9 @@
                                                            sk_sp<SkColorSpace> colorSpace,
                                                            const SkSurfaceProps* surfaceProps) {
     ASSERT_SINGLE_OWNER_PRIV
-    return fContext->drawingManager()->makeDrawContext(std::move(rt),
-                                                       std::move(colorSpace),
-                                                       surfaceProps);
+    return this->drawingManager()->makeDrawContext(std::move(rt),
+                                                   std::move(colorSpace),
+                                                   surfaceProps);
 }
 
 sk_sp<GrDrawContext> GrContextPriv::makeBackendTextureDrawContext(const GrBackendTextureDesc& desc, 
@@ -644,8 +644,8 @@
         return nullptr;
     }
 
-    return fContext->drawingManager()->makeDrawContext(sk_ref_sp(surface->asRenderTarget()),
-                                                       std::move(colorSpace), props);
+    return this->drawingManager()->makeDrawContext(sk_ref_sp(surface->asRenderTarget()),
+                                                   std::move(colorSpace), props);
 }
 
 sk_sp<GrDrawContext> GrContextPriv::makeBackendRenderTargetDrawContext(
@@ -659,9 +659,9 @@
         return nullptr;
     }
 
-    return fContext->drawingManager()->makeDrawContext(std::move(rt),
-                                                       std::move(colorSpace),
-                                                       surfaceProps);
+    return this->drawingManager()->makeDrawContext(std::move(rt),
+                                                   std::move(colorSpace),
+                                                   surfaceProps);
 }
 
 sk_sp<GrDrawContext> GrContextPriv::makeBackendTextureAsRenderTargetDrawContext(
@@ -676,9 +676,9 @@
         return nullptr;
     }
 
-    return fContext->drawingManager()->makeDrawContext(sk_ref_sp(surface->asRenderTarget()),
-                                                       std::move(colorSpace),
-                                                       surfaceProps);
+    return this->drawingManager()->makeDrawContext(sk_ref_sp(surface->asRenderTarget()),
+                                                   std::move(colorSpace),
+                                                   surfaceProps);
 }
 
 sk_sp<GrDrawContext> GrContext::makeDrawContext(SkBackingFit fit,
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index 8646e25..29eb151 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -15,6 +15,8 @@
     data members or virtual methods. */
 class GrContextPriv {
 public:
+    GrDrawingManager* drawingManager() { return fContext->fDrawingManager; }
+
     // Create a drawContext that wraps an existing renderTarget
     sk_sp<GrDrawContext> makeWrappedDrawContext(sk_sp<GrRenderTarget> rt,
                                                 sk_sp<SkColorSpace> colorSpace,
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index 237ea22..dfc0a26 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -7,7 +7,17 @@
 
 #include "GrReducedClip.h"
 
+#include "GrAppliedClip.h"
 #include "GrClip.h"
+#include "GrColor.h"
+#include "GrContextPriv.h"
+#include "GrDrawContext.h"
+#include "GrDrawContextPriv.h"
+#include "GrDrawingManager.h"
+#include "GrFixedClip.h"
+#include "GrPathRenderer.h"
+#include "GrStyle.h"
+#include "GrUserStencilSettings.h"
 
 typedef SkClipStack::Element Element;
 
@@ -421,3 +431,316 @@
     }
     return true;
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// Create a 8-bit clip mask in alpha
+
+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;
+        }
+    }
+}
+
+bool GrReducedClip::drawAlphaClipMask(GrDrawContext* dc) const {
+    // The texture may be larger than necessary, this rect represents the part of the texture
+    // we populate with a rasterization of the clip.
+    GrFixedClip clip(SkIRect::MakeWH(fIBounds.width(), fIBounds.height()));
+
+    // The scratch texture that we are drawing into can be substantially larger than the mask. Only
+    // clear the part that we care about.
+    GrColor initialCoverage = InitialState::kAllIn == this->initialState() ? -1 : 0;
+    dc->drawContextPriv().clear(clip, initialCoverage, true);
+
+    // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
+    SkMatrix translate;
+    translate.setTranslate(SkIntToScalar(-fIBounds.left()), SkIntToScalar(-fIBounds.top()));
+
+    // walk through each clip element and perform its set op
+    for (ElementList::Iter iter(fElements); 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) {
+            // 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, clip, &kStencilInElement, translate, element)) {
+                return false;
+            }
+
+            // 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(fIBounds))) {
+                return false;
+            }
+        } 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, clip, paint, translate, element);
+        }
+    }
+
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Create a 1-bit clip mask in the stencil buffer.
+
+class StencilClip final : public GrClip {
+public:
+    StencilClip(const SkIRect& scissorRect) : fFixedClip(scissorRect) {}
+    const GrFixedClip& fixedClip() const { return fFixedClip; }
+
+private:
+    bool quickContains(const SkRect&) const final {
+        return false;
+    }
+    void getConservativeBounds(int width, int height, SkIRect* devResult, bool* iior) const final {
+        fFixedClip.getConservativeBounds(width, height, devResult, iior);
+    }
+    bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const final {
+        return false;
+    }
+    bool apply(GrContext* context, GrDrawContext* drawContext, bool useHWAA,
+               bool hasUserStencilSettings, GrAppliedClip* out) const final {
+        if (!fFixedClip.apply(context, drawContext, useHWAA, hasUserStencilSettings, out)) {
+            return false;
+        }
+        out->addStencilClip();
+        return true;
+    }
+
+    GrFixedClip fFixedClip;
+
+    typedef GrClip INHERITED;
+};
+
+bool GrReducedClip::drawStencilClipMask(GrContext* context,
+                                        GrDrawContext* drawContext,
+                                        const SkIPoint& clipOrigin) const {
+    // We set the current clip to the bounds so that our recursive draws are scissored to them.
+    StencilClip stencilClip(fIBounds.makeOffset(-clipOrigin.x(), -clipOrigin.y()));
+
+    bool initialState = InitialState::kAllIn == this->initialState();
+    drawContext->drawContextPriv().clearStencilClip(stencilClip.fixedClip(), initialState);
+
+    // Set the matrix so that rendered clip elements are transformed from clip to stencil space.
+    SkMatrix viewMatrix;
+    viewMatrix.setTranslate(SkIntToScalar(-clipOrigin.x()), SkIntToScalar(-clipOrigin.y()));
+
+    // walk through each clip element and perform its set op
+    // with the existing clip.
+    for (ElementList::Iter iter(fElements); iter.get(); iter.next()) {
+        const Element* element = iter.get();
+        bool useHWAA = element->isAA() && drawContext->isStencilBufferMultisampled();
+
+        bool fillInverted = false;
+
+        // 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->contextPriv().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(stencilClip.fixedClip(),
+                                                           &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 = &stencilClip.fixedClip();
+                        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 = &stencilClip.fixedClip();
+                        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()) {
+                    drawContext->drawContextPriv().stencilRect(stencilClip, *pass, useHWAA,
+                                                               viewMatrix, element->getRect());
+                } else {
+                    GrShape shape(clipPath, GrStyle::SimpleFill());
+                    GrPaint paint;
+                    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 = &stencilClip;
+                    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.
+                drawContext->drawContextPriv().stencilRect(stencilClip, *pass,
+                                                           false, viewMatrix,
+                                                           SkRect::Make(fIBounds));
+            }
+        }
+    }
+    return true;
+}
diff --git a/src/gpu/GrReducedClip.h b/src/gpu/GrReducedClip.h
index c3f94a0..731d58f 100644
--- a/src/gpu/GrReducedClip.h
+++ b/src/gpu/GrReducedClip.h
@@ -11,6 +11,9 @@
 #include "SkClipStack.h"
 #include "SkTLList.h"
 
+class GrContext;
+class GrDrawContext;
+
 /**
  * This class takes a clip stack and produces a reduced set of elements that are equivalent to
  * applying that full stack within a specified query rectangle.
@@ -60,6 +63,9 @@
 
     InitialState initialState() const { return fInitialState; }
 
+    bool drawAlphaClipMask(GrDrawContext*) const;
+    bool drawStencilClipMask(GrContext*, GrDrawContext*, const SkIPoint& clipOrigin) const;
+
 private:
     void walkStack(const SkClipStack&, const SkRect& queryBounds);
     bool intersectIBounds(const SkIRect&);
diff --git a/src/gpu/GrStencilAttachment.h b/src/gpu/GrStencilAttachment.h
index 8f41b57..0ed3c8b 100644
--- a/src/gpu/GrStencilAttachment.h
+++ b/src/gpu/GrStencilAttachment.h
@@ -31,18 +31,18 @@
     // called to note the last clip drawn to this buffer.
     void setLastClip(int32_t clipStackGenID,
                      const SkIRect& clipSpaceRect,
-                     const SkIPoint clipSpaceToStencilOffset) {
+                     const SkIPoint clipOrigin) {
         fLastClipStackGenID = clipStackGenID;
         fLastClipStackRect = clipSpaceRect;
-        fLastClipSpaceOffset = clipSpaceToStencilOffset;
+        fLastClipOrigin = clipOrigin;
     }
 
     // called to determine if we have to render the clip into SB.
     bool mustRenderClip(int32_t clipStackGenID,
                         const SkIRect& clipSpaceRect,
-                        const SkIPoint clipSpaceToStencilOffset) const {
+                        const SkIPoint& clipOrigin) const {
         return fLastClipStackGenID != clipStackGenID ||
-               fLastClipSpaceOffset != clipSpaceToStencilOffset ||
+               fLastClipOrigin != clipOrigin ||
                !fLastClipStackRect.contains(clipSpaceRect);
     }
 
@@ -71,7 +71,7 @@
 
     int32_t     fLastClipStackGenID;
     SkIRect     fLastClipStackRect;
-    SkIPoint    fLastClipSpaceOffset;
+    SkIPoint    fLastClipOrigin;
 
     typedef GrGpuResource INHERITED;
 };
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
index 2d62a5b..96b0bcb 100644
--- a/tests/GLProgramsTest.cpp
+++ b/tests/GLProgramsTest.cpp
@@ -14,6 +14,7 @@
 #include "GrAutoLocaleSetter.h"
 #include "GrBatchTest.h"
 #include "GrContextFactory.h"
+#include "GrContextPriv.h"
 #include "GrDrawContextPriv.h"
 #include "GrDrawingManager.h"
 #include "GrInvariantOutput.h"
@@ -288,7 +289,7 @@
 }
 
 bool GrDrawingManager::ProgramUnitTest(GrContext* context, int maxStages) {
-    GrDrawingManager* drawingManager = context->drawingManager();
+    GrDrawingManager* drawingManager = context->contextPriv().drawingManager();
 
     // setup dummy textures
     GrSurfaceDesc dummyDesc;