Insert clip fragment processor outside GrCMM

Review URL: https://codereview.chromium.org/1393553002
diff --git a/gm/texturedomaineffect.cpp b/gm/texturedomaineffect.cpp
index 77b51da..9952690 100644
--- a/gm/texturedomaineffect.cpp
+++ b/gm/texturedomaineffect.cpp
@@ -116,7 +116,7 @@
                 for (int m = 0; m < GrTextureDomain::kModeCount; ++m) {
                     GrTextureDomain::Mode mode = (GrTextureDomain::Mode) m;
                     GrPipelineBuilder pipelineBuilder;
-                    SkAutoTUnref<GrFragmentProcessor> fp(
+                    SkAutoTUnref<const GrFragmentProcessor> fp(
                         GrTextureDomainEffect::Create(texture, textureMatrices[tm],
                                                 GrTextureDomain::MakeTexelDomain(texture,
                                                                                 texelDomains[d]),
diff --git a/src/effects/SkGpuBlurUtils.cpp b/src/effects/SkGpuBlurUtils.cpp
index 0ebbfe4..276fb6c 100644
--- a/src/effects/SkGpuBlurUtils.cpp
+++ b/src/effects/SkGpuBlurUtils.cpp
@@ -208,7 +208,7 @@
             matrix.mapRect(&domain, rect);
             domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f,
                          i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f);
-            SkAutoTUnref<GrFragmentProcessor> fp(GrTextureDomainEffect::Create(
+            SkAutoTUnref<const GrFragmentProcessor> fp(GrTextureDomainEffect::Create(
                 srcTexture,
                 matrix,
                 domain,
diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp
index af74c22..2311bb7 100644
--- a/src/effects/SkXfermodeImageFilter.cpp
+++ b/src/effects/SkXfermodeImageFilter.cpp
@@ -185,7 +185,7 @@
     SkRect srcRect;
     src.getBounds(&srcRect);
 
-    SkAutoTUnref<GrFragmentProcessor> foregroundDomain(GrTextureDomainEffect::Create(
+    SkAutoTUnref<const GrFragmentProcessor> foregroundDomain(GrTextureDomainEffect::Create(
         foregroundTex, foregroundMatrix,
         GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()),
         GrTextureDomain::kDecal_Mode,
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index dadd2ea..4af96ac 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -27,16 +27,9 @@
 typedef SkClipStack::Element Element;
 
 ////////////////////////////////////////////////////////////////////////////////
-namespace {
 // set up the draw state to enable the aa clipping mask. Besides setting up the
 // stage matrix this also alters the vertex layout
-void setup_drawstate_aaclip(const GrPipelineBuilder& pipelineBuilder,
-                            GrTexture* result,
-                            GrPipelineBuilder::AutoRestoreFragmentProcessorState* arfps,
-                            const SkIRect &devBound) {
-    SkASSERT(arfps);
-    arfps->set(&pipelineBuilder);
-
+static const 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.
@@ -45,23 +38,21 @@
                      SkIntToScalar(-devBound.fTop));
 
     SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
-    // This could be a long-lived effect that is cached with the alpha-mask.
-    arfps->addCoverageFragmentProcessor(
-        GrTextureDomainEffect::Create(result,
-                                      mat,
-                                      GrTextureDomain::MakeTexelDomain(result, domainTexels),
-                                      GrTextureDomain::kDecal_Mode,
-                                      GrTextureParams::kNone_FilterMode,
-                                      kDevice_GrCoordSet))->unref();
+    return GrTextureDomainEffect::Create(result,
+                                         mat,
+                                         GrTextureDomain::MakeTexelDomain(result, domainTexels),
+                                         GrTextureDomain::kDecal_Mode,
+                                         GrTextureParams::kNone_FilterMode,
+                                         kDevice_GrCoordSet);
 }
 
-bool path_needs_SW_renderer(GrContext* context,
-                            const GrDrawTarget* gpu,
-                            const GrPipelineBuilder& pipelineBuilder,
-                            const SkMatrix& viewMatrix,
-                            const SkPath& origPath,
-                            const GrStrokeInfo& stroke,
-                            bool doAA) {
+static bool path_needs_SW_renderer(GrContext* context,
+                                   const GrDrawTarget* gpu,
+                                   const GrPipelineBuilder& pipelineBuilder,
+                                   const SkMatrix& viewMatrix,
+                                   const SkPath& origPath,
+                                   const GrStrokeInfo& stroke,
+                                   bool doAA) {
     // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
     SkTCopyOnFirstWrite<SkPath> path(origPath);
     if (path->isInverseFillType()) {
@@ -75,7 +66,6 @@
     return nullptr == context->getPathRenderer(gpu, &pipelineBuilder, viewMatrix, *path, stroke,
                                             false, type);
 }
-}
 
 GrClipMaskManager::GrClipMaskManager(GrDrawTarget* drawTarget)
     : fCurrClipMaskType(kNone_ClipMaskType)
@@ -119,20 +109,21 @@
     return false;
 }
 
-bool GrClipMaskManager::installClipEffects(
-        const GrPipelineBuilder& pipelineBuilder,
-        GrPipelineBuilder::AutoRestoreFragmentProcessorState* arfps,
-        const GrReducedClip::ElementList& elements,
-        const SkVector& clipToRTOffset,
-        const SkRect* drawBounds) {
+const GrFragmentProcessor* GrClipMaskManager::getAnalyticClipProcessor(
+                                                        const GrReducedClip::ElementList& elements,
+                                                        const SkVector& clipToRTOffset,
+                                                        const SkRect* drawBounds) {
     SkRect boundsInClipSpace;
     if (drawBounds) {
         boundsInClipSpace = *drawBounds;
         boundsInClipSpace.offset(-clipToRTOffset.fX, -clipToRTOffset.fY);
     }
-
-    arfps->set(&pipelineBuilder);
-    GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
+    SkASSERT(elements.count() <= kMaxAnalyticElements);
+    const GrFragmentProcessor* fps[kMaxAnalyticElements];
+    for (int i = 0; i < kMaxAnalyticElements; ++i) {
+        fps[i] = nullptr;
+    }
+    int fpCnt = 0;
     GrReducedClip::ElementList::Iter iter(elements);
     bool failed = false;
     while (iter.get()) {
@@ -165,62 +156,59 @@
         if (!skip) {
             GrPrimitiveEdgeType edgeType;
             if (iter.get()->isAA()) {
-                if (rt->isUnifiedMultisampled()) {
-                    // Coverage based AA clips don't place nicely with MSAA.
-                    failed = true;
-                    break;
-                }
                 edgeType =
-                        invert ? kInverseFillAA_GrProcessorEdgeType : kFillAA_GrProcessorEdgeType;
+                    invert ? kInverseFillAA_GrProcessorEdgeType : kFillAA_GrProcessorEdgeType;
             } else {
-                edgeType = invert ? kInverseFillBW_GrProcessorEdgeType :
-                                    kFillBW_GrProcessorEdgeType;
+                edgeType =
+                    invert ? kInverseFillBW_GrProcessorEdgeType : kFillBW_GrProcessorEdgeType;
             }
-            SkAutoTUnref<GrFragmentProcessor> fp;
             switch (iter.get()->getType()) {
                 case SkClipStack::Element::kPath_Type:
-                    fp.reset(GrConvexPolyEffect::Create(edgeType, iter.get()->getPath(),
-                                                        &clipToRTOffset));
+                    fps[fpCnt] = GrConvexPolyEffect::Create(edgeType, iter.get()->getPath(),
+                                                            &clipToRTOffset);
                     break;
                 case SkClipStack::Element::kRRect_Type: {
                     SkRRect rrect = iter.get()->getRRect();
                     rrect.offset(clipToRTOffset.fX, clipToRTOffset.fY);
-                    fp.reset(GrRRectEffect::Create(edgeType, rrect));
+                    fps[fpCnt] = GrRRectEffect::Create(edgeType, rrect);
                     break;
                 }
                 case SkClipStack::Element::kRect_Type: {
                     SkRect rect = iter.get()->getRect();
                     rect.offset(clipToRTOffset.fX, clipToRTOffset.fY);
-                    fp.reset(GrConvexPolyEffect::Create(edgeType, rect));
+                    fps[fpCnt] = GrConvexPolyEffect::Create(edgeType, rect);
                     break;
                 }
                 default:
                     break;
             }
-            if (fp) {
-                arfps->addCoverageFragmentProcessor(fp);
-            } else {
+            if (!fps[fpCnt]) {
                 failed = true;
                 break;
             }
+            fpCnt++;
         }
         iter.next();
     }
 
-    if (failed) {
-        arfps->set(nullptr);
+    const GrFragmentProcessor* resultFP = nullptr;
+    if (!failed) {
+        resultFP = GrFragmentProcessor::RunInSeries(fps, fpCnt);
     }
-    return !failed;
+    for (int i = 0; i < fpCnt; ++i) {
+        fps[i]->unref();
+    }
+    return resultFP;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // sort out what kind of clip mask needs to be created: alpha, stencil,
 // scissor, or entirely software
 bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder,
-                                      GrPipelineBuilder::AutoRestoreFragmentProcessorState* arfps,
                                       GrPipelineBuilder::AutoRestoreStencil* ars,
                                       GrScissorState* scissorState,
-                                      const SkRect* devBounds) {
+                                      const SkRect* devBounds,
+                                      GrAppliedClip* out) {
     fCurrClipMaskType = kNone_ClipMaskType;
     if (kRespectClip_StencilClipMode == fClipMode) {
         fClipMode = kIgnoreClip_StencilClipMode;
@@ -288,12 +276,16 @@
     // 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() <= 4) {
+    if (elements.count() <= kMaxAnalyticElements) {
         SkVector clipToRTOffset = { SkIntToScalar(-clip.origin().fX),
                                     SkIntToScalar(-clip.origin().fY) };
+        // When there are multiple color samples we want to do per-sample clipping, not compute
+        // a fractional pixel coverage.
+        bool disallowAnalyticAA = pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
+        const GrFragmentProcessor* clipFP = nullptr;
         if (elements.isEmpty() ||
-            (requiresAA && this->installClipEffects(pipelineBuilder, arfps, elements, clipToRTOffset,
-                                                    devBounds))) {
+            (requiresAA && !disallowAnalyticAA &&
+             SkToBool(clipFP = this->getAnalyticClipProcessor(elements, clipToRTOffset, devBounds)))) {
             SkIRect scissorSpaceIBounds(clipSpaceIBounds);
             scissorSpaceIBounds.offset(-clip.origin());
             if (nullptr == devBounds ||
@@ -301,6 +293,7 @@
                 scissorState->set(scissorSpaceIBounds);
             }
             this->setPipelineBuilderStencil(pipelineBuilder, ars);
+            out->fClipCoverageFP.reset(clipFP);
             return true;
         }
     }
@@ -332,12 +325,11 @@
         }
 
         if (result) {
-            arfps->set(&pipelineBuilder);
             // 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(-clip.origin());
-            setup_drawstate_aaclip(pipelineBuilder, result, arfps, rtSpaceMaskBounds);
+            out->fClipCoverageFP.reset(create_fp_for_mask(result, rtSpaceMaskBounds));
             this->setPipelineBuilderStencil(pipelineBuilder, ars);
             return true;
         }
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index 0c0c10c..5c336e0 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -23,6 +23,25 @@
 class GrPathRendererChain;
 class GrTexture;
 class SkPath;
+
+/**
+ * Produced by GrClipMaskManager. It provides a set of modifications to the drawing state that
+ * are used to create the final GrPipeline for a GrBatch. This is a work in progress. It will
+ * eventually encapsulate all mechanisms for modifying the scissor, shaders, and stencil state
+ * to implement clipping.
+ */
+class GrAppliedClip : public SkNoncopyable {
+public:
+    GrAppliedClip() {}
+    const GrFragmentProcessor* clipCoverageFragmentProcessor() const { return fClipCoverageFP; }
+
+private:
+    SkAutoTUnref<const GrFragmentProcessor> fClipCoverageFP;
+    friend class GrClipMaskManager;
+
+    typedef SkNoncopyable INHERITED;
+};
+
 /**
  * The clip mask creator handles the generation of the clip mask. If anti
  * aliasing is requested it will (in the future) generate a single channel
@@ -43,10 +62,10 @@
      * clip. devBounds is optional but can help optimize clipping.
      */
     bool setupClipping(const GrPipelineBuilder&,
-                       GrPipelineBuilder::AutoRestoreFragmentProcessorState*,
                        GrPipelineBuilder::AutoRestoreStencil*,
                        GrScissorState*,
-                       const SkRect* devBounds);
+                       const SkRect* devBounds,
+                       GrAppliedClip*);
 
     bool isClipInStencil() const {
         return kStencil_ClipMaskType == fCurrClipMaskType;
@@ -77,11 +96,9 @@
 
     // Attempts to install a series of coverage effects to implement the clip. Return indicates
     // whether the element list was successfully converted to effects.
-    bool installClipEffects(const GrPipelineBuilder&,
-                            GrPipelineBuilder::AutoRestoreFragmentProcessorState*,
-                            const GrReducedClip::ElementList&,
-                            const SkVector& clipOffset,
-                            const SkRect* devBounds);
+    const GrFragmentProcessor* getAnalyticClipProcessor(const GrReducedClip::ElementList&,
+                                                        const SkVector& clipOffset,
+                                                        const SkRect* devBounds);
 
     // Draws the clip into the stencil buffer
     bool createStencilClipMask(GrRenderTarget*,
@@ -164,6 +181,8 @@
         kAlpha_ClipMaskType,
     } fCurrClipMaskType;
 
+    static const int kMaxAnalyticElements = 4;
+
     GrDrawTarget*   fDrawTarget;    // This is our owning draw target.
     StencilClipMode fClipMode;
 
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 1edb48c..ba0142e 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -146,12 +146,17 @@
 void GrDrawTarget::drawBatch(const GrPipelineBuilder& pipelineBuilder, GrDrawBatch* batch) {
     // Setup clip
     GrScissorState scissorState;
-    GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
     GrPipelineBuilder::AutoRestoreStencil ars;
-    if (!fClipMaskManager->setupClipping(pipelineBuilder, &arfps, &ars, &scissorState,
-                                         &batch->bounds())) {
+    GrAppliedClip clip;
+    if (!fClipMaskManager->setupClipping(pipelineBuilder, &ars, &scissorState, &batch->bounds(),
+                                         &clip)) {
         return;
     }
+    GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
+    if (clip.clipCoverageFragmentProcessor()) {
+        arfps.set(&pipelineBuilder);
+        arfps.addCoverageFragmentProcessor(clip.clipCoverageFragmentProcessor());
+    }
 
     GrPipeline::CreateArgs args;
     if (!this->installPipelineInDrawBatch(&pipelineBuilder, &scissorState, batch)) {
@@ -206,13 +211,18 @@
 
     // Setup clip
     GrScissorState scissorState;
-    GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
     GrPipelineBuilder::AutoRestoreStencil ars;
-
-    if (!fClipMaskManager->setupClipping(pipelineBuilder, &arfps, &ars, &scissorState, nullptr)) {
+    GrAppliedClip clip;
+    if (!fClipMaskManager->setupClipping(pipelineBuilder, &ars, &scissorState, nullptr, &clip)) {
         return;
     }
 
+    GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
+    if (clip.clipCoverageFragmentProcessor()) {
+        arfps.set(&pipelineBuilder);
+        arfps.addCoverageFragmentProcessor(clip.clipCoverageFragmentProcessor());
+    }
+
     // set stencil settings for path
     GrStencilSettings stencilSettings;
     GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
@@ -261,13 +271,19 @@
     // batches.
 
     GrScissorState scissorState;
-    GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
     GrPipelineBuilder::AutoRestoreStencil ars;
-    if (!fClipMaskManager->setupClipping(pipelineBuilder, &arfps, &ars, &scissorState,
-                                         &batch->bounds())) {
+    GrAppliedClip clip;
+    if (!fClipMaskManager->setupClipping(pipelineBuilder, &ars, &scissorState, &batch->bounds(),
+                                         &clip)) {
         return;
     }
 
+    GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
+    if (clip.clipCoverageFragmentProcessor()) {
+        arfps.set(&pipelineBuilder);
+        arfps.addCoverageFragmentProcessor(clip.clipCoverageFragmentProcessor());
+    }
+
     // Ensure the render target has a stencil buffer and get the stencil settings.
     GrStencilSettings stencilSettings;
     GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
diff --git a/src/gpu/effects/GrTextureDomain.cpp b/src/gpu/effects/GrTextureDomain.cpp
index bf8bb59..ae0a614 100644
--- a/src/gpu/effects/GrTextureDomain.cpp
+++ b/src/gpu/effects/GrTextureDomain.cpp
@@ -215,12 +215,12 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrFragmentProcessor* GrTextureDomainEffect::Create(GrTexture* texture,
-                                                   const SkMatrix& matrix,
-                                                   const SkRect& domain,
-                                                   GrTextureDomain::Mode mode,
-                                                   GrTextureParams::FilterMode filterMode,
-                                                   GrCoordSet coordSet) {
+const GrFragmentProcessor* GrTextureDomainEffect::Create(GrTexture* texture,
+                                                         const SkMatrix& matrix,
+                                                         const SkRect& domain,
+                                                         GrTextureDomain::Mode mode,
+                                                         GrTextureParams::FilterMode filterMode,
+                                                         GrCoordSet coordSet) {
     static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
     if (GrTextureDomain::kIgnore_Mode == mode ||
         (GrTextureDomain::kClamp_Mode == mode && domain.contains(kFullRect))) {
@@ -243,12 +243,10 @@
     this->initClassID<GrTextureDomainEffect>();
 }
 
-GrTextureDomainEffect::~GrTextureDomainEffect() {
-
-}
+GrTextureDomainEffect::~GrTextureDomainEffect() {}
 
 void GrTextureDomainEffect::onGetGLProcessorKey(const GrGLSLCaps& caps,
-                                              GrProcessorKeyBuilder* b) const {
+                                                GrProcessorKeyBuilder* b) const {
     GrGLTextureDomainEffect::GenKey(*this, caps, b);
 }
 
@@ -290,10 +288,11 @@
     const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
     bool bilerp = mode != GrTextureDomain::kRepeat_Mode ? d->fRandom->nextBool() : false;
     GrCoordSet coords = d->fRandom->nextBool() ? kLocal_GrCoordSet : kDevice_GrCoordSet;
-    return GrTextureDomainEffect::Create(d->fTextures[texIdx],
-                                         matrix,
-                                         domain,
-                                         mode,
-                                         bilerp ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode,
-                                         coords);
+    return GrTextureDomainEffect::Create(
+        d->fTextures[texIdx],
+        matrix,
+        domain,
+        mode,
+        bilerp ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode,
+        coords);
 }
diff --git a/src/gpu/effects/GrTextureDomain.h b/src/gpu/effects/GrTextureDomain.h
index f0d180d..978a68c 100644
--- a/src/gpu/effects/GrTextureDomain.h
+++ b/src/gpu/effects/GrTextureDomain.h
@@ -160,12 +160,12 @@
 class GrTextureDomainEffect : public GrSingleTextureEffect {
 
 public:
-    static GrFragmentProcessor* Create(GrTexture*,
-                                       const SkMatrix&,
-                                       const SkRect& domain,
-                                       GrTextureDomain::Mode,
-                                       GrTextureParams::FilterMode filterMode,
-                                       GrCoordSet = kLocal_GrCoordSet);
+    static const GrFragmentProcessor* Create(GrTexture*,
+                                             const SkMatrix&,
+                                             const SkRect& domain,
+                                             GrTextureDomain::Mode,
+                                             GrTextureParams::FilterMode filterMode,
+                                             GrCoordSet = kLocal_GrCoordSet);
 
     virtual ~GrTextureDomainEffect();