Revert "Create GrXferProcessor while doing GrProcessorSet analysis."

This reverts commit 0ae0e23696f2ef08503040f8c02765eb58b26ddf.

Reason for revert: static assert failure on ios build

Original change's description:
> Create GrXferProcessor while doing GrProcessorSet analysis.
> 
> 
> Bug: skia:
> Change-Id: I62a628f9c0536ffb05c8f9d0c9ded5657f93b48e
> Reviewed-on: https://skia-review.googlesource.com/11482
> Reviewed-by: Greg Daniel <egdaniel@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>
> 

TBR=egdaniel@google.com,bsalomon@google.com,reviews@skia.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Change-Id: Ifd57367b0326dd2b53c622705a868e246645d589
Reviewed-on: https://skia-review.googlesource.com/12104
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrPipeline.cpp b/src/gpu/GrPipeline.cpp
index d1f5931..759f97c 100644
--- a/src/gpu/GrPipeline.cpp
+++ b/src/gpu/GrPipeline.cpp
@@ -20,8 +20,6 @@
 
 void GrPipeline::init(const InitArgs& args) {
     SkASSERT(args.fRenderTarget);
-    SkASSERT(args.fProcessors);
-    SkASSERT(args.fProcessors->isFinalized());
 
     fRenderTarget.reset(args.fRenderTarget);
 
@@ -50,8 +48,17 @@
 
     fDrawFace = static_cast<int16_t>(args.fDrawFace);
 
-    fXferProcessor.reset(args.fProcessors->xferProcessor());
+    bool isHWAA = kHWAntialias_Flag & args.fFlags;
 
+    // Create XferProcessor from DS's XPFactory
+    {
+        bool hasMixedSamples =
+                args.fRenderTarget->isMixedSampled() && (isHWAA || this->isStencilEnabled());
+        sk_sp<GrXferProcessor> xferProcessor =
+                GrXPFactory::MakeXferProcessor(args.fProcessors->xpFactory(), args.fXPInputColor,
+                                               args.fXPInputCoverage, hasMixedSamples, *args.fCaps);
+        fXferProcessor.reset(xferProcessor.get());
+    }
     if (args.fDstTexture.texture()) {
         fDstTexture.reset(args.fDstTexture.texture());
         fDstTextureOffset = args.fDstTexture.offset();
diff --git a/src/gpu/GrPipeline.h b/src/gpu/GrPipeline.h
index a2a332f..02a14af 100644
--- a/src/gpu/GrPipeline.h
+++ b/src/gpu/GrPipeline.h
@@ -56,7 +56,9 @@
     struct InitArgs {
         uint32_t fFlags = 0;
         GrDrawFace fDrawFace = GrDrawFace::kBoth;
-        const GrProcessorSet* fProcessors = nullptr;  // Must be finalized
+        const GrProcessorSet* fProcessors = nullptr;
+        GrProcessorAnalysisColor fXPInputColor;
+        GrProcessorAnalysisCoverage fXPInputCoverage = GrProcessorAnalysisCoverage::kNone;
         const GrUserStencilSettings* fUserStencil = &GrUserStencilSettings::kUnused;
         const GrAppliedClip* fAppliedClip = nullptr;
         GrRenderTarget* fRenderTarget = nullptr;
diff --git a/src/gpu/GrPipelineBuilder.h b/src/gpu/GrPipelineBuilder.h
index 78aead0..94e07e6 100644
--- a/src/gpu/GrPipelineBuilder.h
+++ b/src/gpu/GrPipelineBuilder.h
@@ -63,12 +63,12 @@
 
     const GrProcessorSet& processors() const { return fProcessors; }
 
-    GrProcessorSet::Analysis finalizeProcessors(const GrProcessorAnalysisColor& colorInput,
-                                                const GrProcessorAnalysisCoverage coverageInput,
-                                                const GrAppliedClip* clip, bool isMixedSamples,
-                                                const GrCaps& caps, GrColor* overrideColor) {
-        return fProcessors.finalize(colorInput, coverageInput, clip, isMixedSamples, caps,
-                                    overrideColor);
+    void analyzeAndEliminateFragmentProcessors(GrProcessorSet::Analysis* analysis,
+                                               const GrProcessorAnalysisColor& colorInput,
+                                               const GrProcessorAnalysisCoverage coverageInput,
+                                               const GrAppliedClip* clip, const GrCaps& caps) {
+        fProcessors.analyzeAndEliminateFragmentProcessors(analysis, colorInput, coverageInput, clip,
+                                                          caps);
     }
 
     /// @}
diff --git a/src/gpu/GrProcessorSet.cpp b/src/gpu/GrProcessorSet.cpp
index 9361764..8bdf6b6 100644
--- a/src/gpu/GrProcessorSet.cpp
+++ b/src/gpu/GrProcessorSet.cpp
@@ -9,9 +9,9 @@
 #include "GrAppliedClip.h"
 #include "GrCaps.h"
 #include "GrXferProcessor.h"
-#include "effects/GrPorterDuffXferProcessor.h"
 
-GrProcessorSet::GrProcessorSet(GrPaint&& paint) : fXP(paint.getXPFactory()) {
+GrProcessorSet::GrProcessorSet(GrPaint&& paint) {
+    fXPFactory = paint.fXPFactory;
     fFlags = 0;
     if (paint.numColorFragmentProcessors() <= kMaxColorProcessors) {
         fColorFragmentProcessorCnt = paint.numColorFragmentProcessors();
@@ -40,22 +40,27 @@
 
 GrProcessorSet::~GrProcessorSet() {
     for (int i = fFragmentProcessorOffset; i < fFragmentProcessors.count(); ++i) {
-        if (this->isFinalized()) {
+        if (this->isPendingExecution()) {
             fFragmentProcessors[i]->completedExecution();
         } else {
             fFragmentProcessors[i]->unref();
         }
     }
-    if (this->isFinalized() && this->xferProcessor()) {
-        this->xferProcessor()->completedExecution();
+}
+
+void GrProcessorSet::makePendingExecution() {
+    SkASSERT(!(kPendingExecution_Flag & fFlags));
+    fFlags |= kPendingExecution_Flag;
+    for (int i = fFragmentProcessorOffset; i < fFragmentProcessors.count(); ++i) {
+        fFragmentProcessors[i]->addPendingExecution();
+        fFragmentProcessors[i]->unref();
     }
 }
 
 bool GrProcessorSet::operator==(const GrProcessorSet& that) const {
-    SkASSERT(this->isFinalized());
-    SkASSERT(that.isFinalized());
     int fpCount = this->numFragmentProcessors();
-    if (((fFlags ^ that.fFlags) & ~kFinalized_Flag) || fpCount != that.numFragmentProcessors() ||
+    if (((fFlags ^ that.fFlags) & ~kPendingExecution_Flag) ||
+        fpCount != that.numFragmentProcessors() ||
         fColorFragmentProcessorCnt != that.fColorFragmentProcessorCnt) {
         return false;
     }
@@ -67,43 +72,34 @@
             return false;
         }
     }
-    // Most of the time both of these are null
-    if (!this->xferProcessor() && !that.xferProcessor()) {
-        return true;
+    if (fXPFactory != that.fXPFactory) {
+        return false;
     }
-    const GrXferProcessor& thisXP = this->xferProcessor()
-                                            ? *this->xferProcessor()
-                                            : GrPorterDuffXPFactory::SimpleSrcOverXP();
-    const GrXferProcessor& thatXP = that.xferProcessor()
-                                            ? *that.xferProcessor()
-                                            : GrPorterDuffXPFactory::SimpleSrcOverXP();
-    return thisXP.isEqual(thatXP);
+    return true;
 }
 
-GrProcessorSet::Analysis GrProcessorSet::finalize(const GrProcessorAnalysisColor& colorInput,
-                                                  const GrProcessorAnalysisCoverage coverageInput,
-                                                  const GrAppliedClip* clip, bool isMixedSamples,
-                                                  const GrCaps& caps, GrColor* overrideInputColor) {
-    SkASSERT(!this->isFinalized());
-    SkASSERT(!fFragmentProcessorOffset);
+//////////////////////////////////////////////////////////////////////////////
 
-    GrProcessorSet::Analysis analysis;
+void GrProcessorSet::Analysis::internalInit(const GrProcessorAnalysisColor& colorInput,
+                                            const GrProcessorAnalysisCoverage coverageInput,
+                                            const GrProcessorSet& processors,
+                                            const GrFragmentProcessor* clipFP,
+                                            const GrCaps& caps) {
+    GrColorFragmentProcessorAnalysis colorInfo(colorInput);
+    fCompatibleWithCoverageAsAlpha = GrProcessorAnalysisCoverage::kLCD != coverageInput;
+    fValidInputColor = colorInput.isConstant(&fInputColor);
 
-    const GrFragmentProcessor* clipFP = clip ? clip->clipCoverageFragmentProcessor() : nullptr;
-    GrColorFragmentProcessorAnalysis colorAnalysis(colorInput);
-    analysis.fCompatibleWithCoverageAsAlpha = GrProcessorAnalysisCoverage::kLCD != coverageInput;
-
-    const GrFragmentProcessor* const* fps = fFragmentProcessors.get() + fFragmentProcessorOffset;
-    colorAnalysis.analyzeProcessors(fps, fColorFragmentProcessorCnt);
-    analysis.fCompatibleWithCoverageAsAlpha &=
-            colorAnalysis.allProcessorsCompatibleWithCoverageAsAlpha();
-    fps += fColorFragmentProcessorCnt;
-    int n = this->numCoverageFragmentProcessors();
+    const GrFragmentProcessor* const* fps =
+            processors.fFragmentProcessors.get() + processors.fFragmentProcessorOffset;
+    colorInfo.analyzeProcessors(fps, processors.fColorFragmentProcessorCnt);
+    fCompatibleWithCoverageAsAlpha &= colorInfo.allProcessorsCompatibleWithCoverageAsAlpha();
+    fps += processors.fColorFragmentProcessorCnt;
+    int n = processors.numCoverageFragmentProcessors();
     bool hasCoverageFP = n > 0;
     bool coverageUsesLocalCoords = false;
     for (int i = 0; i < n; ++i) {
         if (!fps[i]->compatibleWithCoverageAsAlpha()) {
-            analysis.fCompatibleWithCoverageAsAlpha = false;
+            fCompatibleWithCoverageAsAlpha = false;
             // Other than tests that exercise atypical behavior we expect all coverage FPs to be
             // compatible with the coverage-as-alpha optimization.
             GrCapsDebugf(&caps, "Coverage FP is not compatible with coverage as alpha.\n");
@@ -112,14 +108,21 @@
     }
 
     if (clipFP) {
-        analysis.fCompatibleWithCoverageAsAlpha &= clipFP->compatibleWithCoverageAsAlpha();
+        fCompatibleWithCoverageAsAlpha &= clipFP->compatibleWithCoverageAsAlpha();
         coverageUsesLocalCoords |= clipFP->usesLocalCoords();
         hasCoverageFP = true;
     }
-    int colorFPsToEliminate = colorAnalysis.initialProcessorsToEliminate(overrideInputColor);
-    analysis.fInputColorType = static_cast<Analysis::PackedInputColorType>(
-            colorFPsToEliminate ? Analysis::kOverridden_InputColorType
-                                : Analysis::kOriginal_InputColorType);
+    fInitialColorProcessorsToEliminate = colorInfo.initialProcessorsToEliminate(&fInputColor);
+    fValidInputColor |= SkToBool(fInitialColorProcessorsToEliminate);
+
+    GrProcessorAnalysisColor outputColor = colorInfo.outputColor();
+    if (outputColor.isConstant(&fKnownOutputColor)) {
+        fOutputColorType = static_cast<unsigned>(ColorType::kConstant);
+    } else if (outputColor.isOpaque()) {
+        fOutputColorType = static_cast<unsigned>(ColorType::kOpaque);
+    } else {
+        fOutputColorType = static_cast<unsigned>(ColorType::kUnknown);
+    }
 
     GrProcessorAnalysisCoverage outputCoverage;
     if (GrProcessorAnalysisCoverage::kLCD == coverageInput) {
@@ -129,51 +132,82 @@
     } else {
         outputCoverage = GrProcessorAnalysisCoverage::kNone;
     }
+    fOutputCoverageType = static_cast<unsigned>(outputCoverage);
 
     GrXPFactory::AnalysisProperties props = GrXPFactory::GetAnalysisProperties(
-            this->xpFactory(), colorAnalysis.outputColor(), outputCoverage, caps);
-    if (!this->numCoverageFragmentProcessors() &&
+            processors.fXPFactory, colorInfo.outputColor(), outputCoverage, caps);
+    if (!processors.numCoverageFragmentProcessors() &&
         GrProcessorAnalysisCoverage::kNone == coverageInput) {
-        analysis.fCanCombineOverlappedStencilAndCover = SkToBool(
+        fCanCombineOverlappedStencilAndCover = SkToBool(
                 props & GrXPFactory::AnalysisProperties::kCanCombineOverlappedStencilAndCover);
     } else {
         // If we have non-clipping coverage processors we don't try to merge stencil steps as its
         // unclear whether it will be correct. We don't expect this to happen in practice.
-        analysis.fCanCombineOverlappedStencilAndCover = false;
+        fCanCombineOverlappedStencilAndCover = false;
     }
-    analysis.fRequiresDstTexture =
-            SkToBool(props & GrXPFactory::AnalysisProperties::kRequiresDstTexture);
-    analysis.fCompatibleWithCoverageAsAlpha &=
+    fRequiresDstTexture = SkToBool(props & GrXPFactory::AnalysisProperties::kRequiresDstTexture);
+    fIgnoresInputColor = SkToBool(props & GrXPFactory::AnalysisProperties::kIgnoresInputColor);
+    fCompatibleWithCoverageAsAlpha &=
             SkToBool(props & GrXPFactory::AnalysisProperties::kCompatibleWithAlphaAsCoverage);
-    analysis.fRequiresBarrierBetweenOverlappingDraws = SkToBool(
+    fRequiresBarrierBetweenOverlappingDraws = SkToBool(
             props & GrXPFactory::AnalysisProperties::kRequiresBarrierBetweenOverlappingDraws);
     if (props & GrXPFactory::AnalysisProperties::kIgnoresInputColor) {
-        colorFPsToEliminate = this->numColorFragmentProcessors();
-        analysis.fInputColorType =
-                static_cast<Analysis::PackedInputColorType>(Analysis::kIgnored_InputColorType);
-        analysis.fUsesLocalCoords = coverageUsesLocalCoords;
+        fInitialColorProcessorsToEliminate = processors.numColorFragmentProcessors();
+        // If the output of the last color stage is known then the kIgnoresInputColor optimization
+        // may depend upon it being the input to the xp.
+        if (!outputColor.isConstant(&fInputColor)) {
+            // Otherwise, the only property the XP factory could have relied upon to compute
+            // kIgnoresInputColor is opaqueness.
+            fInputColor = GrColor_WHITE;
+        }
+        fValidInputColor = true;
+        fUsesLocalCoords = coverageUsesLocalCoords;
     } else {
-        analysis.fUsesLocalCoords = coverageUsesLocalCoords | colorAnalysis.usesLocalCoords();
+        fUsesLocalCoords = coverageUsesLocalCoords | colorInfo.usesLocalCoords();
     }
-    for (int i = 0; i < colorFPsToEliminate; ++i) {
-        fFragmentProcessors[i]->unref();
-        fFragmentProcessors[i] = nullptr;
-    }
-    for (int i = colorFPsToEliminate; i < fFragmentProcessors.count(); ++i) {
-        fFragmentProcessors[i]->addPendingExecution();
-        fFragmentProcessors[i]->unref();
-    }
-    fFragmentProcessorOffset = colorFPsToEliminate;
-    fColorFragmentProcessorCnt -= colorFPsToEliminate;
+}
 
-    auto xp = GrXPFactory::MakeXferProcessor(this->xpFactory(), colorAnalysis.outputColor(),
-                                             outputCoverage, isMixedSamples, caps);
-    fXP.fProcessor = xp.get();
-    if (fXP.fProcessor) {
-        fXP.fProcessor->addPendingExecution();
-    }
-    fFlags |= kFinalized_Flag;
+void GrProcessorSet::Analysis::init(const GrProcessorAnalysisColor& colorInput,
+                                    const GrProcessorAnalysisCoverage coverageInput,
+                                    const GrProcessorSet& processors,
+                                    const GrAppliedClip* appliedClip,
+                                    const GrCaps& caps) {
+    const GrFragmentProcessor* clipFP =
+            appliedClip ? appliedClip->clipCoverageFragmentProcessor() : nullptr;
+    this->internalInit(colorInput, coverageInput, processors, clipFP, caps);
+    fIsInitializedWithProcessorSet = true;
+}
 
-    analysis.fIsInitialized = true;
-    return analysis;
+GrProcessorSet::Analysis::Analysis(const GrProcessorAnalysisColor& colorInput,
+                                   const GrProcessorAnalysisCoverage coverageInput,
+                                   const GrXPFactory* factory,
+                                   const GrCaps& caps)
+        : Analysis() {
+    GrPaint paint;
+    paint.setXPFactory(factory);
+    this->internalInit(colorInput, coverageInput, GrProcessorSet(std::move(paint)), nullptr, caps);
+}
+
+void GrProcessorSet::analyzeAndEliminateFragmentProcessors(
+        Analysis* analysis,
+        const GrProcessorAnalysisColor& colorInput,
+        const GrProcessorAnalysisCoverage coverageInput,
+        const GrAppliedClip* clip,
+        const GrCaps& caps) {
+    analysis->init(colorInput, coverageInput, *this, clip, caps);
+    if (analysis->fInitialColorProcessorsToEliminate > 0) {
+        for (unsigned i = 0; i < analysis->fInitialColorProcessorsToEliminate; ++i) {
+            if (this->isPendingExecution()) {
+                fFragmentProcessors[i + fFragmentProcessorOffset]->completedExecution();
+            } else {
+                fFragmentProcessors[i + fFragmentProcessorOffset]->unref();
+            }
+            fFragmentProcessors[i + fFragmentProcessorOffset] = nullptr;
+        }
+        fFragmentProcessorOffset += analysis->fInitialColorProcessorsToEliminate;
+        fColorFragmentProcessorCnt -= analysis->fInitialColorProcessorsToEliminate;
+        SkASSERT(fFragmentProcessorOffset + fColorFragmentProcessorCnt <=
+                 fFragmentProcessors.count());
+        analysis->fInitialColorProcessorsToEliminate = 0;
+    }
 }
diff --git a/src/gpu/GrProcessorSet.h b/src/gpu/GrProcessorSet.h
index 289ca1b..39ba013 100644
--- a/src/gpu/GrProcessorSet.h
+++ b/src/gpu/GrProcessorSet.h
@@ -14,7 +14,6 @@
 #include "SkTemplates.h"
 
 class GrAppliedClip;
-class GrXferProcessor;
 class GrXPFactory;
 
 class GrProcessorSet : private SkNoncopyable {
@@ -23,6 +22,14 @@
 
     ~GrProcessorSet();
 
+    /**
+     * If an op is recorded with this processor set then this must be called to ensure pending
+     * reads and writes are propagated to resources referred to by the processors. Otherwise,
+     * data hazards may occur.
+     */
+    void makePendingExecution();
+    bool isPendingExecution() const { return SkToBool(kPendingExecution_Flag & fFlags); }
+
     int numColorFragmentProcessors() const { return fColorFragmentProcessorCnt; }
     int numCoverageFragmentProcessors() const {
         return this->numFragmentProcessors() - fColorFragmentProcessorCnt;
@@ -39,10 +46,7 @@
         return fFragmentProcessors[idx + fColorFragmentProcessorCnt + fFragmentProcessorOffset];
     }
 
-    const GrXferProcessor* xferProcessor() const {
-        SkASSERT(this->isFinalized());
-        return fXP.fProcessor;
-    }
+    const GrXPFactory* xpFactory() const { return fXPFactory; }
 
     bool usesDistanceVectorField() const { return SkToBool(fFlags & kUseDistanceVectorField_Flag); }
     bool disableOutputConversionToSRGB() const {
@@ -50,20 +54,74 @@
     }
     bool allowSRGBInputs() const { return SkToBool(fFlags & kAllowSRGBInputs_Flag); }
 
-    /** Comparisons are only legal on finalized processor sets. */
     bool operator==(const GrProcessorSet& that) const;
     bool operator!=(const GrProcessorSet& that) const { return !(*this == that); }
 
     /**
-     * This is used to report results of processor analysis when a processor set is finalized (see
-     * below).
+     * This is used to track analysis of color and coverage values through the processors.
      */
     class Analysis {
     public:
-        Analysis(const Analysis&) = default;
-        Analysis() { *reinterpret_cast<uint32_t*>(this) = 0; }
+        /**
+         * This constructor allows an op to record its initial color in an Analysis member and then
+         * then run analysis later when the analysis inputs are available. If the analysis produces
+         * color fragment processor elimination then the input color is replaced by the expected
+         * input to the first non-eliminated processor. Otherwise, the original input color is
+         * preserved. The only reason to use this is to save space on the op by not separately
+         * storing the initial color.
+         */
+        explicit Analysis(GrColor initialColor) : Analysis() {
+            fInputColor = initialColor;
+            fValidInputColor = true;
+        }
 
-        bool isInitialized() const { return fIsInitialized; }
+        Analysis()
+                : fIsInitializedWithProcessorSet(false)
+                , fCompatibleWithCoverageAsAlpha(true)
+                , fValidInputColor(false)
+                , fRequiresDstTexture(false)
+                , fCanCombineOverlappedStencilAndCover(true)
+                , fIgnoresInputColor(false)
+                , fRequiresBarrierBetweenOverlappingDraws(false)
+                , fOutputCoverageType(static_cast<unsigned>(GrProcessorAnalysisCoverage::kNone))
+                , fOutputColorType(static_cast<unsigned>(ColorType::kUnknown))
+                , fInitialColorProcessorsToEliminate(0) {}
+
+        // This version is used by a unit test that assumes no clip and no fragment processors.
+        Analysis(const GrProcessorAnalysisColor&, GrProcessorAnalysisCoverage, const GrXPFactory*,
+                 const GrCaps&);
+
+        void init(const GrProcessorAnalysisColor&, GrProcessorAnalysisCoverage,
+                  const GrProcessorSet&, const GrAppliedClip*, const GrCaps&);
+
+        bool isInitializedWithProcessorSet() const { return fIsInitializedWithProcessorSet; }
+
+        /**
+         * If the return is greater than or equal to zero then 'newInputColor' should be used as the
+         * input color to the GrPipeline derived from this processor set, replacing the GrDrawOp's
+         * initial color. If the return is less than zero then newInputColor has not been
+         * modified and no modification need be made to the pipeline's input color by the op.
+         */
+        int getInputColorOverrideAndColorProcessorEliminationCount(GrColor* newInputColor) const {
+            if (fValidInputColor) {
+                *newInputColor = fInputColor;
+                return fInitialColorProcessorsToEliminate;
+            }
+            SkASSERT(!fInitialColorProcessorsToEliminate);
+            return -1;
+        }
+
+        /**
+         * Valid if initialProcessorsToEliminate returns true or this analysis was initialized with
+         * a known color via constructor or init(). If color fragment processors are eliminated then
+         * this returns the expected input to the first non-eliminated processors. Otherwise it is
+         * the color passed to the constructor or init().
+         */
+        GrColor inputColor() const {
+            SkASSERT(fValidInputColor);
+            return fInputColor;
+        }
+
         bool usesLocalCoords() const { return fUsesLocalCoords; }
         bool requiresDstTexture() const { return fRequiresDstTexture; }
         bool canCombineOverlappedStencilAndCover() const {
@@ -73,54 +131,60 @@
             return fRequiresBarrierBetweenOverlappingDraws;
         }
         bool isCompatibleWithCoverageAsAlpha() const { return fCompatibleWithCoverageAsAlpha; }
-
-        bool inputColorIsIgnored() const { return fInputColorType == kIgnored_InputColorType; }
-        bool inputColorIsOverridden() const {
-            return fInputColorType == kOverridden_InputColorType;
+        bool isInputColorIgnored() const { return fIgnoresInputColor; }
+        GrProcessorAnalysisCoverage outputCoverage() const {
+            return static_cast<GrProcessorAnalysisCoverage>(fOutputCoverageType);
+        }
+        GrProcessorAnalysisColor outputColor() const {
+            switch (this->outputColorType()) {
+                case ColorType::kConstant:
+                    return fKnownOutputColor;
+                case ColorType::kOpaque:
+                    return GrProcessorAnalysisColor::Opaque::kYes;
+                case ColorType::kUnknown:
+                    return GrProcessorAnalysisColor::Opaque::kNo;
+            }
+            SkFAIL("Unexpected color type");
+            return GrProcessorAnalysisColor::Opaque::kNo;
         }
 
     private:
-        enum InputColorType : uint32_t {
-            kOriginal_InputColorType,
-            kOverridden_InputColorType,
-            kIgnored_InputColorType
-        };
+        enum class ColorType : unsigned { kUnknown, kConstant, kOpaque };
 
-        // MSVS 2015 won't pack different underlying types
-        using PackedBool = uint32_t;
-        using PackedInputColorType = uint32_t;
+        ColorType outputColorType() const { return static_cast<ColorType>(fOutputColorType); }
 
+        void internalInit(const GrProcessorAnalysisColor&, const GrProcessorAnalysisCoverage,
+                          const GrProcessorSet&, const GrFragmentProcessor* clipFP, const GrCaps&);
+
+        // MSVS 2015 won't pack a bool with an unsigned.
+        using PackedBool = unsigned;
+
+        PackedBool fIsInitializedWithProcessorSet : 1;
         PackedBool fUsesLocalCoords : 1;
         PackedBool fCompatibleWithCoverageAsAlpha : 1;
+        PackedBool fValidInputColor : 1;
         PackedBool fRequiresDstTexture : 1;
         PackedBool fCanCombineOverlappedStencilAndCover : 1;
+        // These could be removed if we created the XP from the XPFactory when doing analysis.
+        PackedBool fIgnoresInputColor : 1;
         PackedBool fRequiresBarrierBetweenOverlappingDraws : 1;
-        PackedBool fIsInitialized : 1;
-        PackedInputColorType fInputColorType : 2;
+        unsigned fOutputCoverageType : 2;
+        unsigned fOutputColorType : 2;
+
+        unsigned fInitialColorProcessorsToEliminate : 32 - 12;
+
+        GrColor fInputColor;
+        // This could be removed if we created the XP from the XPFactory when doing analysis.
+        GrColor fKnownOutputColor;
 
         friend class GrProcessorSet;
     };
-    GR_STATIC_ASSERT(sizeof(Analysis) == sizeof(uint32_t));
+    GR_STATIC_ASSERT(sizeof(Analysis) == 2 * sizeof(GrColor) + sizeof(uint32_t));
 
-    /**
-     * This analyzes the processors given an op's input color and coverage as well as a clip. The
-     * state of the processor set may change to an equivalent but more optimal set of processors.
-     * This new state requires that the caller respect the returned 'inputColorOverride'. This is
-     * indicated by the returned Analysis's inputColorIsOverriden(). 'inputColorOverride' will not
-     * be written if the analysis does not override the input color.
-     *
-     * This must be called before the processor set is used to construct a GrPipeline and may only
-     * be called once.
-     *
-     * This also puts the processors in "pending execution" state and must be called when an op
-     * that owns a processor set is recorded to ensure pending and writes are propagated to
-     * resources referred to by the processors. Otherwise, data hazards may occur.
-     */
-    Analysis finalize(const GrProcessorAnalysisColor& colorInput,
-                      const GrProcessorAnalysisCoverage coverageInput, const GrAppliedClip*,
-                      bool isMixedSamples, const GrCaps&, GrColor* inputColorOverride);
-
-    bool isFinalized() const { return SkToBool(kFinalized_Flag & fFlags); }
+    void analyzeAndEliminateFragmentProcessors(Analysis*,
+                                               const GrProcessorAnalysisColor& colorInput,
+                                               const GrProcessorAnalysisCoverage coverageInput,
+                                               const GrAppliedClip*, const GrCaps&);
 
 private:
     // This absurdly large limit allows Analysis and this to pack fields together.
@@ -130,22 +194,11 @@
         kUseDistanceVectorField_Flag = 0x1,
         kDisableOutputConversionToSRGB_Flag = 0x2,
         kAllowSRGBInputs_Flag = 0x4,
-        kFinalized_Flag = 0x8
+        kPendingExecution_Flag = 0x8
     };
 
-    union XP {
-        XP(const GrXPFactory* factory) : fFactory(factory) {}
-        const GrXPFactory* fFactory;
-        const GrXferProcessor* fProcessor;
-    };
-
-    const GrXPFactory* xpFactory() const {
-        SkASSERT(!this->isFinalized());
-        return fXP.fFactory;
-    }
-
+    const GrXPFactory* fXPFactory = nullptr;
     SkAutoSTArray<4, const GrFragmentProcessor*> fFragmentProcessors;
-    XP fXP;
     uint8_t fColorFragmentProcessorCnt;
     uint8_t fFragmentProcessorOffset = 0;
     uint8_t fFlags;
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index b0a1b6d..a4b7731 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1688,33 +1688,30 @@
     }
 
     GrResourceProvider* resourceProvider = fContext->resourceProvider();
-    bool usesStencil = pipelineBuilder.hasUserStencilSettings() || appliedClip.hasStencilClip();
-    if (usesStencil) {
+    if (pipelineBuilder.hasUserStencilSettings() || appliedClip.hasStencilClip()) {
         if (!resourceProvider->attachStencilAttachment(this->accessRenderTarget())) {
             SkDebugf("ERROR creating stencil attachment. Draw skipped.\n");
             return SK_InvalidUniqueID;
         }
     }
 
-    bool isMixedSamples = fRenderTargetProxy->isMixedSampled() &&
-                          (pipelineBuilder.isHWAntialias() || usesStencil);
-
-    GrColor overrideColor;
-    GrProcessorSet::Analysis analysis = op->analyzeUpdateAndRecordProcessors(
-            &pipelineBuilder, &appliedClip, isMixedSamples, *this->caps(), &overrideColor);
+    GrProcessorSet::Analysis analysis;
+    op->analyzeProcessors(&analysis, &pipelineBuilder, &appliedClip, *this->caps());
 
     GrPipeline::InitArgs args;
     pipelineBuilder.getPipelineInitArgs(&args);
     args.fAppliedClip = &appliedClip;
     args.fRenderTarget = rt;
     args.fCaps = this->caps();
+    args.fXPInputColor = analysis.outputColor();
+    args.fXPInputCoverage = analysis.outputCoverage();
 
     if (analysis.requiresDstTexture()) {
         if (!this->setupDstTexture(fRenderTargetProxy.get(), clip, bounds, &args.fDstTexture)) {
             return SK_InvalidUniqueID;
         }
     }
-    op->initPipeline(args, analysis, overrideColor);
+    op->initPipeline(args, analysis);
     // TODO: We need to add pipeline dependencies on textures, etc before recording this op.
     op->setClippedBounds(bounds);
     return this->getOpList()->addOp(std::move(op), this);
diff --git a/src/gpu/instanced/InstancedRendering.cpp b/src/gpu/instanced/InstancedRendering.cpp
index 9d17a82..9b6e576 100644
--- a/src/gpu/instanced/InstancedRendering.cpp
+++ b/src/gpu/instanced/InstancedRendering.cpp
@@ -332,19 +332,17 @@
 }
 
 bool InstancedRendering::Op::xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) {
-    SkASSERT(State::kRecordingDraws == fInstancedRendering->fState);
+    GrProcessorSet::Analysis analysis;
     GrProcessorAnalysisCoverage coverageInput;
-    bool isMixedSamples = false;
     if (GrAAType::kCoverage == fInfo.aaType() ||
         (GrAAType::kNone == fInfo.aaType() && !fInfo.isSimpleRects() && fInfo.fCannotDiscard)) {
         coverageInput = GrProcessorAnalysisCoverage::kSingleChannel;
     } else {
         coverageInput = GrProcessorAnalysisCoverage::kNone;
-        isMixedSamples = GrAAType::kMixedSamples == fInfo.aaType();
     }
-    GrProcessorSet::Analysis analysis =
-            fProcessors.finalize(this->getSingleInstance().fColor, coverageInput, clip,
-                                 isMixedSamples, caps, &this->getSingleDraw().fInstance.fColor);
+    fProcessors.analyzeAndEliminateFragmentProcessors(&analysis, this->getSingleInstance().fColor,
+                                                      coverageInput, clip, caps);
+    fAnalysisColor = analysis.outputColor();
 
     Draw& draw = this->getSingleDraw(); // This will assert if we have > 1 command.
     SkASSERT(draw.fGeometry.isEmpty());
@@ -365,6 +363,11 @@
         fInstancedRendering->fParams.push_back_n(fParams.count(), fParams.begin());
     }
 
+    GrColor overrideColor;
+    if (analysis.getInputColorOverrideAndColorProcessorEliminationCount(&overrideColor) >= 0) {
+        SkASSERT(State::kRecordingDraws == fInstancedRendering->fState);
+        this->getSingleDraw().fInstance.fColor = overrideColor;
+    }
     fInfo.fCannotTweakAlphaForCoverage = !analysis.isCompatibleWithCoverageAsAlpha();
 
     fInfo.fUsesLocalCoords = analysis.usesLocalCoords();
@@ -375,6 +378,7 @@
 void InstancedRendering::Op::wasRecorded() {
     SkASSERT(!fIsTracked);
     fInstancedRendering->fTrackedOps.addToTail(this);
+    fProcessors.makePendingExecution();
     fIsTracked = true;
 }
 
@@ -409,6 +413,7 @@
     this->joinBounds(*that);
     fInfo = combinedInfo;
     fPixelLoad += that->fPixelLoad;
+    fAnalysisColor = GrProcessorAnalysisColor::Combine(fAnalysisColor, that->fAnalysisColor);
     // Adopt the other op's draws.
     fNumDraws += that->fNumDraws;
     fNumChangesInGeometry += that->fNumChangesInGeometry;
@@ -465,9 +470,21 @@
 
     state->gpu()->handleDirtyContext();
 
+    const GrAppliedClip* clip = state->drawOpArgs().fAppliedClip;
+    GrProcessorAnalysisCoverage coverage;
+    if (GrAAType::kCoverage == fInfo.aaType() ||
+        (clip && clip->clipCoverageFragmentProcessor()) ||
+        (GrAAType::kNone == fInfo.aaType() && !fInfo.isSimpleRects() && fInfo.fCannotDiscard)) {
+        coverage = GrProcessorAnalysisCoverage::kSingleChannel;
+    } else {
+        coverage = GrProcessorAnalysisCoverage::kNone;
+    }
+
     GrPipeline pipeline;
     GrPipeline::InitArgs args;
-    args.fAppliedClip = state->drawOpArgs().fAppliedClip;
+    args.fXPInputColor = fAnalysisColor;
+    args.fXPInputCoverage = coverage;
+    args.fAppliedClip = clip;
     args.fCaps = &state->caps();
     args.fProcessors = &fProcessors;
     args.fFlags = GrAATypeIsHW(fInfo.aaType()) ? GrPipeline::kHWAntialias_Flag : 0;
diff --git a/src/gpu/instanced/InstancedRendering.h b/src/gpu/instanced/InstancedRendering.h
index a8b9530..3ec8214 100644
--- a/src/gpu/instanced/InstancedRendering.h
+++ b/src/gpu/instanced/InstancedRendering.h
@@ -151,6 +151,7 @@
         OpInfo fInfo;
         SkScalar fPixelLoad;
         GrProcessorSet fProcessors;
+        GrProcessorAnalysisColor fAnalysisColor;
         SkSTArray<5, ParamsTexel, true> fParams;
         bool fIsTracked : 1;
         bool fRequiresBarrierOnOverlap : 1;
diff --git a/src/gpu/ops/GrDrawOp.h b/src/gpu/ops/GrDrawOp.h
index dbdd9d8..0458370 100644
--- a/src/gpu/ops/GrDrawOp.h
+++ b/src/gpu/ops/GrDrawOp.h
@@ -76,8 +76,7 @@
      * This is called after the GrAppliedClip has been computed and just prior to recording the op
      * or combining it with a previously recorded op. It is used to determine whether a copy of the
      * destination (or destination texture itself) needs to be provided to the xp when this op
-     * executes. This is guaranteed to be called before an op is recorded. However, this is also
-     * called on ops that are not recorded because they combine with a previously recorded op.
+     * executes.
      */
     virtual bool xpRequiresDstTexture(const GrCaps&, const GrAppliedClip*) = 0;
 
diff --git a/src/gpu/ops/GrDrawPathOp.cpp b/src/gpu/ops/GrDrawPathOp.cpp
index 3632787..1c68fdf 100644
--- a/src/gpu/ops/GrDrawPathOp.cpp
+++ b/src/gpu/ops/GrDrawPathOp.cpp
@@ -12,15 +12,13 @@
 #include "SkTemplates.h"
 
 GrDrawPathOpBase::GrDrawPathOpBase(uint32_t classID, const SkMatrix& viewMatrix, GrPaint&& paint,
-                                   GrPathRendering::FillType fill, GrAAType aaType)
+                                   GrPathRendering::FillType fill, GrAA aa)
         : INHERITED(classID)
         , fViewMatrix(viewMatrix)
-        , fInputColor(paint.getColor())
         , fProcessorSet(std::move(paint))
+        , fAnalysis(paint.getColor())
         , fFillType(fill)
-        , fAAType(aaType) {
-    SkASSERT(fAAType != GrAAType::kCoverage);
-}
+        , fAA(aa) {}
 
 SkString GrDrawPathOp::dumpInfo() const {
     SkString string;
@@ -40,13 +38,16 @@
                     0xffff>()
     };
     GrPipeline::InitArgs args;
+    auto analysis = this->processorAnalysis();
     args.fProcessors = &this->processors();
-    args.fFlags = GrAATypeIsHW(fAAType) ? GrPipeline::kHWAntialias_Flag : 0;
+    args.fFlags = GrAA::kYes == fAA ? GrPipeline::kHWAntialias_Flag : 0;
     args.fUserStencil = &kCoverPass;
     args.fAppliedClip = state.drawOpArgs().fAppliedClip;
     args.fRenderTarget = state.drawOpArgs().fRenderTarget;
     args.fCaps = &state.caps();
     args.fDstTexture = state.drawOpArgs().fDstTexture;
+    args.fXPInputColor = analysis.outputColor();
+    args.fXPInputCoverage = analysis.outputCoverage();
 
     return pipeline->init(args);
 }
@@ -89,9 +90,9 @@
 
 GrDrawPathRangeOp::GrDrawPathRangeOp(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x,
                                      SkScalar y, GrPaint&& paint, GrPathRendering::FillType fill,
-                                     GrAAType aaType, GrPathRange* range,
-                                     const InstanceData* instanceData, const SkRect& bounds)
-        : INHERITED(ClassID(), viewMatrix, std::move(paint), fill, aaType)
+                                     GrAA aa, GrPathRange* range, const InstanceData* instanceData,
+                                     const SkRect& bounds)
+        : INHERITED(ClassID(), viewMatrix, std::move(paint), fill, aa)
         , fPathRange(range)
         , fTotalPathCount(instanceData->count())
         , fScale(scale) {
diff --git a/src/gpu/ops/GrDrawPathOp.h b/src/gpu/ops/GrDrawPathOp.h
index 8995de4..0631adc 100644
--- a/src/gpu/ops/GrDrawPathOp.h
+++ b/src/gpu/ops/GrDrawPathOp.h
@@ -23,33 +23,31 @@
 
 class GrDrawPathOpBase : public GrDrawOp {
 protected:
-    GrDrawPathOpBase(uint32_t classID, const SkMatrix& viewMatrix, GrPaint&&,
-                     GrPathRendering::FillType, GrAAType);
+    GrDrawPathOpBase(uint32_t classID, const SkMatrix& viewMatrix, GrPaint&& paint,
+                     GrPathRendering::FillType fill, GrAA aa);
     FixedFunctionFlags fixedFunctionFlags() const override {
-        if (GrAATypeIsHW(fAAType)) {
-            return FixedFunctionFlags::kUsesHWAA | FixedFunctionFlags::kUsesStencil;
-        }
-        return FixedFunctionFlags::kUsesStencil;
+        return FixedFunctionFlags::kUsesHWAA | FixedFunctionFlags::kUsesStencil;
     }
     bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
         return this->doProcessorAnalysis(caps, clip).requiresDstTexture();
     }
 
+    void wasRecorded() override { fProcessorSet.makePendingExecution(); }
+
 protected:
     const SkMatrix& viewMatrix() const { return fViewMatrix; }
-    GrColor color() const { return fInputColor; }
+    GrColor color() const { return fAnalysis.inputColor(); }
     GrPathRendering::FillType fillType() const { return fFillType; }
     const GrProcessorSet& processors() const { return fProcessorSet; }
     void initPipeline(const GrOpFlushState&, GrPipeline*);
     const GrProcessorSet::Analysis& doProcessorAnalysis(const GrCaps& caps,
                                                         const GrAppliedClip* clip) {
-        bool isMixedSamples = GrAAType::kMixedSamples == fAAType;
-        fAnalysis = fProcessorSet.finalize(fInputColor, GrProcessorAnalysisCoverage::kNone, clip,
-                                           isMixedSamples, caps, &fInputColor);
+        fProcessorSet.analyzeAndEliminateFragmentProcessors(
+                &fAnalysis, fAnalysis.inputColor(), GrProcessorAnalysisCoverage::kNone, clip, caps);
         return fAnalysis;
     }
     const GrProcessorSet::Analysis& processorAnalysis() const {
-        SkASSERT(fAnalysis.isInitialized());
+        SkASSERT(fAnalysis.isInitializedWithProcessorSet());
         return fAnalysis;
     }
 
@@ -57,11 +55,10 @@
     void onPrepare(GrOpFlushState*) final {}
 
     SkMatrix fViewMatrix;
-    GrColor fInputColor;
     GrProcessorSet fProcessorSet;
     GrProcessorSet::Analysis fAnalysis;
     GrPathRendering::FillType fFillType;
-    GrAAType fAAType;
+    GrAA fAA;
 
     typedef GrDrawOp INHERITED;
 };
@@ -70,10 +67,9 @@
 public:
     DEFINE_OP_CLASS_ID
 
-    static std::unique_ptr<GrDrawOp> Make(const SkMatrix& viewMatrix, GrPaint&& paint,
-                                          GrAAType aaType, GrPath* path) {
-        return std::unique_ptr<GrDrawOp>(
-                new GrDrawPathOp(viewMatrix, std::move(paint), aaType, path));
+    static std::unique_ptr<GrDrawOp> Make(const SkMatrix& viewMatrix, GrPaint&& paint, GrAA aa,
+                                          GrPath* path) {
+        return std::unique_ptr<GrDrawOp>(new GrDrawPathOp(viewMatrix, std::move(paint), aa, path));
     }
 
     const char* name() const override { return "DrawPath"; }
@@ -81,8 +77,8 @@
     SkString dumpInfo() const override;
 
 private:
-    GrDrawPathOp(const SkMatrix& viewMatrix, GrPaint&& paint, GrAAType aaType, const GrPath* path)
-            : GrDrawPathOpBase(ClassID(), viewMatrix, std::move(paint), path->getFillType(), aaType)
+    GrDrawPathOp(const SkMatrix& viewMatrix, GrPaint&& paint, GrAA aa, const GrPath* path)
+            : GrDrawPathOpBase(ClassID(), viewMatrix, std::move(paint), path->getFillType(), aa)
             , fPath(path) {
         this->setTransformedBounds(path->getBounds(), viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
     }
@@ -164,12 +160,11 @@
 
     static std::unique_ptr<GrDrawOp> Make(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x,
                                           SkScalar y, GrPaint&& paint,
-                                          GrPathRendering::FillType fill, GrAAType aaType,
+                                          GrPathRendering::FillType fill, GrAA aa,
                                           GrPathRange* range, const InstanceData* instanceData,
                                           const SkRect& bounds) {
-        return std::unique_ptr<GrDrawOp>(new GrDrawPathRangeOp(viewMatrix, scale, x, y,
-                                                               std::move(paint), fill, aaType,
-                                                               range, instanceData, bounds));
+        return std::unique_ptr<GrDrawOp>(new GrDrawPathRangeOp(
+                viewMatrix, scale, x, y, std::move(paint), fill, aa, range, instanceData, bounds));
     }
 
     const char* name() const override { return "DrawPathRange"; }
@@ -178,8 +173,8 @@
 
 private:
     GrDrawPathRangeOp(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x, SkScalar y,
-                      GrPaint&& paint, GrPathRendering::FillType fill, GrAAType aaType,
-                      GrPathRange* range, const InstanceData* instanceData, const SkRect& bounds);
+                      GrPaint&& paint, GrPathRendering::FillType fill, GrAA aa, GrPathRange* range,
+                      const InstanceData* instanceData, const SkRect& bounds);
 
     TransformType transformType() const { return fDraws.head()->fInstanceData->transformType(); }
 
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index 8fbd30d..41d3848 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -101,22 +101,20 @@
      * Performs analysis of the fragment processors in GrProcessorSet and GrAppliedClip using the
      * initial color and coverage from this op's geometry processor.
      */
-    GrProcessorSet::Analysis analyzeUpdateAndRecordProcessors(GrPipelineBuilder* pipelineBuilder,
-                                                              const GrAppliedClip* appliedClip,
-                                                              bool isMixedSamples,
-                                                              const GrCaps& caps,
-                                                              GrColor* overrideColor) const {
+    void analyzeProcessors(GrProcessorSet::Analysis* analysis,
+                           GrPipelineBuilder* pipelineBuilder,
+                           const GrAppliedClip* appliedClip,
+                           const GrCaps& caps) const {
         GrProcessorAnalysisColor inputColor;
         GrProcessorAnalysisCoverage inputCoverage;
         this->getProcessorAnalysisInputs(&inputColor, &inputCoverage);
-        return pipelineBuilder->finalizeProcessors(inputColor, inputCoverage, appliedClip,
-                                                   isMixedSamples, caps, overrideColor);
+        pipelineBuilder->analyzeAndEliminateFragmentProcessors(analysis, inputColor, inputCoverage,
+                                                               appliedClip, caps);
     }
 
-    void initPipeline(const GrPipeline::InitArgs& args, const GrProcessorSet::Analysis& analysis,
-                      GrColor overrideColor) {
+    void initPipeline(const GrPipeline::InitArgs& args, const GrProcessorSet::Analysis& analysis) {
         fPipeline.init(args);
-        this->applyPipelineOptimizations(PipelineOptimizations(analysis, overrideColor));
+        this->applyPipelineOptimizations(PipelineOptimizations(analysis));
     }
 
     /**
@@ -140,11 +138,11 @@
      */
     class PipelineOptimizations {
     public:
-        PipelineOptimizations(const GrProcessorSet::Analysis& analysis, GrColor overrideColor) {
+        PipelineOptimizations(const GrProcessorSet::Analysis& analysis) {
             fFlags = 0;
-            if (analysis.inputColorIsOverridden()) {
+            if (analysis.getInputColorOverrideAndColorProcessorEliminationCount(&fOverrideColor) >=
+                0) {
                 fFlags |= kUseOverrideColor_Flag;
-                fOverrideColor = overrideColor;
             }
             if (analysis.usesLocalCoords()) {
                 fFlags |= kReadsLocalCoords_Flag;
diff --git a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
index bd5c297..a4e32ac 100644
--- a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
+++ b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
@@ -145,8 +145,9 @@
                                                            std::move(coverOp));
         }
     } else {
+        GrAA aa = GrBoolToAA(GrAATypeIsHW(args.fAAType));
         std::unique_ptr<GrDrawOp> op =
-                GrDrawPathOp::Make(viewMatrix, std::move(args.fPaint), args.fAAType, path.get());
+                GrDrawPathOp::Make(viewMatrix, std::move(args.fPaint), aa, path.get());
         args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
     }
 
diff --git a/src/gpu/text/GrStencilAndCoverTextContext.cpp b/src/gpu/text/GrStencilAndCoverTextContext.cpp
index 0ae4023..e572e5d 100644
--- a/src/gpu/text/GrStencilAndCoverTextContext.cpp
+++ b/src/gpu/text/GrStencilAndCoverTextContext.cpp
@@ -570,7 +570,9 @@
                                                  SkScalar y, const SkIRect& clipBounds,
                                                  GrAtlasTextContext* fallbackTextContext,
                                                  const SkPaint& originalSkPaint) const {
+    GrAA runAA = this->isAntiAlias();
     SkASSERT(fInstanceData);
+    SkASSERT(renderTargetContext->isStencilBufferMultisampled() || GrAA::kNo == runAA);
 
     if (fInstanceData->count()) {
         static constexpr GrUserStencilSettings kCoverPass(
@@ -604,18 +606,9 @@
                                               renderTargetContext->height());
 
         // The run's "font" overrides the anti-aliasing of the passed in SkPaint!
-        GrAAType aaType;
-        if (this->aa() == GrAA::kYes) {
-            SkASSERT(renderTargetContext->isStencilBufferMultisampled());
-            aaType = renderTargetContext->isUnifiedMultisampled() ? GrAAType::kMSAA
-                                                                  : GrAAType::kMixedSamples;
-        } else {
-            aaType = GrAAType::kNone;
-        }
-
         std::unique_ptr<GrDrawOp> op = GrDrawPathRangeOp::Make(
                 viewMatrix, fTextRatio, fTextInverseRatio * x, fTextInverseRatio * y,
-                std::move(grPaint), GrPathRendering::kWinding_FillType, aaType, glyphs.get(),
+                std::move(grPaint), GrPathRendering::kWinding_FillType, runAA, glyphs.get(),
                 fInstanceData.get(), bounds);
 
         renderTargetContext->addDrawOp(clip, std::move(op));
diff --git a/src/gpu/text/GrStencilAndCoverTextContext.h b/src/gpu/text/GrStencilAndCoverTextContext.h
index 54c0a9d..f06442b 100644
--- a/src/gpu/text/GrStencilAndCoverTextContext.h
+++ b/src/gpu/text/GrStencilAndCoverTextContext.h
@@ -83,7 +83,7 @@
 
         size_t computeSizeInCache() const;
 
-        GrAA aa() const { return fFont.isAntiAlias() ? GrAA::kYes : GrAA::kNo; }
+        GrAA isAntiAlias() const { return fFont.isAntiAlias() ? GrAA::kYes : GrAA::kNo; }
 
     private:
         typedef GrDrawPathRangeOp::InstanceData InstanceData;
diff --git a/tests/GpuSampleLocationsTest.cpp b/tests/GpuSampleLocationsTest.cpp
index 26d7e70..75fa6fcf 100644
--- a/tests/GpuSampleLocationsTest.cpp
+++ b/tests/GpuSampleLocationsTest.cpp
@@ -91,8 +91,17 @@
     virtual ~TestSampleLocationsInterface() {}
 };
 
-static sk_sp<GrPipeline> construct_dummy_pipeline(GrRenderTargetContext* dc) {
-    return sk_sp<GrPipeline>(new GrPipeline(dc->accessRenderTarget(), SkBlendMode::kSrcOver));
+static void construct_dummy_pipeline(GrRenderTargetContext* dc, GrPipeline* pipeline) {
+    GrPipelineBuilder dummyBuilder(GrPaint(), GrAAType::kNone);
+    GrScissorState dummyScissor;
+    GrWindowRectsState dummyWindows;
+
+    GrPipeline::InitArgs args;
+    dummyBuilder.getPipelineInitArgs(&args);
+    args.fRenderTarget = dc->accessRenderTarget();
+    args.fCaps = dc->caps();
+    args.fDstTexture = GrXferProcessor::DstTexture();
+    pipeline->init(args);
 }
 
 void assert_equal(skiatest::Reporter* reporter, const SamplePattern& pattern,
@@ -137,10 +146,11 @@
         for (int i = 0; i < numTestPatterns; ++i) {
             testInterface->overrideSamplePattern(kTestPatterns[i]);
             for (GrRenderTargetContext* dc : {bottomUps[i].get(), topDowns[i].get()}) {
-                sk_sp<GrPipeline> dummyPipeline = construct_dummy_pipeline(dc);
+                GrPipeline dummyPipeline;
+                construct_dummy_pipeline(dc, &dummyPipeline);
                 GrRenderTarget* rt = dc->accessRenderTarget();
                 assert_equal(reporter, kTestPatterns[i],
-                             rt->renderTargetPriv().getMultisampleSpecs(*dummyPipeline),
+                             rt->renderTargetPriv().getMultisampleSpecs(dummyPipeline),
                              kBottomLeft_GrSurfaceOrigin == rt->origin());
             }
         }
diff --git a/tests/GrPorterDuffTest.cpp b/tests/GrPorterDuffTest.cpp
index 4066d28..f2bd1e7 100644
--- a/tests/GrPorterDuffTest.cpp
+++ b/tests/GrPorterDuffTest.cpp
@@ -55,30 +55,16 @@
     kISCModulate_OutputType
 };
 
-static GrProcessorSet::Analysis do_analysis(const GrXPFactory* xpf,
-                                            const GrProcessorAnalysisColor& colorInput,
-                                            GrProcessorAnalysisCoverage coverageInput,
-                                            const GrCaps& caps) {
-    GrPaint paint;
-    paint.setXPFactory(xpf);
-    GrProcessorSet procs(std::move(paint));
-    GrColor overrideColor;
-    GrProcessorSet::Analysis analysis =
-            procs.finalize(colorInput, coverageInput, nullptr, false, caps, &overrideColor);
-    return analysis;
-}
-
 class GrPorterDuffTest {
 public:
     struct XPInfo {
         XPInfo(skiatest::Reporter* reporter, SkBlendMode xfermode, const GrCaps& caps,
                GrProcessorAnalysisColor inputColor, GrProcessorAnalysisCoverage inputCoverage) {
             const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(xfermode);
-
-            GrProcessorSet::Analysis analysis = do_analysis(xpf, inputColor, inputCoverage, caps);
+            GrProcessorSet::Analysis analysis(inputColor, inputCoverage, xpf, caps);
             fCompatibleWithCoverageAsAlpha = analysis.isCompatibleWithCoverageAsAlpha();
             fCanCombineOverlappedStencilAndCover = analysis.canCombineOverlappedStencilAndCover();
-            fIgnoresInputColor = analysis.inputColorIsIgnored();
+            fIgnoresInputColor = analysis.isInputColorIgnored();
             sk_sp<GrXferProcessor> xp(
                     GrXPFactory::MakeXferProcessor(xpf, inputColor, inputCoverage, false, caps));
             TEST_ASSERT(!analysis.requiresDstTexture());
@@ -1054,6 +1040,8 @@
             for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
                 SkBlendMode xfermode = static_cast<SkBlendMode>(m);
                 const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(xfermode);
+                GrProcessorSet::Analysis analysis;
+                analysis = GrProcessorSet::Analysis(colorInput, coverageType, xpf, caps);
                 sk_sp<GrXferProcessor> xp(
                         GrXPFactory::MakeXferProcessor(xpf, colorInput, coverageType, false, caps));
                 if (!xp) {