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>
diff --git a/src/gpu/GrPipeline.cpp b/src/gpu/GrPipeline.cpp
index 759f97c..d1f5931 100644
--- a/src/gpu/GrPipeline.cpp
+++ b/src/gpu/GrPipeline.cpp
@@ -20,6 +20,8 @@
 
 void GrPipeline::init(const InitArgs& args) {
     SkASSERT(args.fRenderTarget);
+    SkASSERT(args.fProcessors);
+    SkASSERT(args.fProcessors->isFinalized());
 
     fRenderTarget.reset(args.fRenderTarget);
 
@@ -48,17 +50,8 @@
 
     fDrawFace = static_cast<int16_t>(args.fDrawFace);
 
-    bool isHWAA = kHWAntialias_Flag & args.fFlags;
+    fXferProcessor.reset(args.fProcessors->xferProcessor());
 
-    // 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 02a14af..a2a332f 100644
--- a/src/gpu/GrPipeline.h
+++ b/src/gpu/GrPipeline.h
@@ -56,9 +56,7 @@
     struct InitArgs {
         uint32_t fFlags = 0;
         GrDrawFace fDrawFace = GrDrawFace::kBoth;
-        const GrProcessorSet* fProcessors = nullptr;
-        GrProcessorAnalysisColor fXPInputColor;
-        GrProcessorAnalysisCoverage fXPInputCoverage = GrProcessorAnalysisCoverage::kNone;
+        const GrProcessorSet* fProcessors = nullptr;  // Must be finalized
         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 94e07e6..78aead0 100644
--- a/src/gpu/GrPipelineBuilder.h
+++ b/src/gpu/GrPipelineBuilder.h
@@ -63,12 +63,12 @@
 
     const GrProcessorSet& processors() const { return fProcessors; }
 
-    void analyzeAndEliminateFragmentProcessors(GrProcessorSet::Analysis* analysis,
-                                               const GrProcessorAnalysisColor& colorInput,
-                                               const GrProcessorAnalysisCoverage coverageInput,
-                                               const GrAppliedClip* clip, const GrCaps& caps) {
-        fProcessors.analyzeAndEliminateFragmentProcessors(analysis, colorInput, coverageInput, clip,
-                                                          caps);
+    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);
     }
 
     /// @}
diff --git a/src/gpu/GrProcessorSet.cpp b/src/gpu/GrProcessorSet.cpp
index 8bdf6b6..9361764 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) {
-    fXPFactory = paint.fXPFactory;
+GrProcessorSet::GrProcessorSet(GrPaint&& paint) : fXP(paint.getXPFactory()) {
     fFlags = 0;
     if (paint.numColorFragmentProcessors() <= kMaxColorProcessors) {
         fColorFragmentProcessorCnt = paint.numColorFragmentProcessors();
@@ -40,27 +40,22 @@
 
 GrProcessorSet::~GrProcessorSet() {
     for (int i = fFragmentProcessorOffset; i < fFragmentProcessors.count(); ++i) {
-        if (this->isPendingExecution()) {
+        if (this->isFinalized()) {
             fFragmentProcessors[i]->completedExecution();
         } else {
             fFragmentProcessors[i]->unref();
         }
     }
-}
-
-void GrProcessorSet::makePendingExecution() {
-    SkASSERT(!(kPendingExecution_Flag & fFlags));
-    fFlags |= kPendingExecution_Flag;
-    for (int i = fFragmentProcessorOffset; i < fFragmentProcessors.count(); ++i) {
-        fFragmentProcessors[i]->addPendingExecution();
-        fFragmentProcessors[i]->unref();
+    if (this->isFinalized() && this->xferProcessor()) {
+        this->xferProcessor()->completedExecution();
     }
 }
 
 bool GrProcessorSet::operator==(const GrProcessorSet& that) const {
+    SkASSERT(this->isFinalized());
+    SkASSERT(that.isFinalized());
     int fpCount = this->numFragmentProcessors();
-    if (((fFlags ^ that.fFlags) & ~kPendingExecution_Flag) ||
-        fpCount != that.numFragmentProcessors() ||
+    if (((fFlags ^ that.fFlags) & ~kFinalized_Flag) || fpCount != that.numFragmentProcessors() ||
         fColorFragmentProcessorCnt != that.fColorFragmentProcessorCnt) {
         return false;
     }
@@ -72,34 +67,43 @@
             return false;
         }
     }
-    if (fXPFactory != that.fXPFactory) {
-        return false;
+    // Most of the time both of these are null
+    if (!this->xferProcessor() && !that.xferProcessor()) {
+        return true;
     }
-    return true;
+    const GrXferProcessor& thisXP = this->xferProcessor()
+                                            ? *this->xferProcessor()
+                                            : GrPorterDuffXPFactory::SimpleSrcOverXP();
+    const GrXferProcessor& thatXP = that.xferProcessor()
+                                            ? *that.xferProcessor()
+                                            : GrPorterDuffXPFactory::SimpleSrcOverXP();
+    return thisXP.isEqual(thatXP);
 }
 
-//////////////////////////////////////////////////////////////////////////////
+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);
 
-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);
+    GrProcessorSet::Analysis analysis;
 
-    const GrFragmentProcessor* const* fps =
-            processors.fFragmentProcessors.get() + processors.fFragmentProcessorOffset;
-    colorInfo.analyzeProcessors(fps, processors.fColorFragmentProcessorCnt);
-    fCompatibleWithCoverageAsAlpha &= colorInfo.allProcessorsCompatibleWithCoverageAsAlpha();
-    fps += processors.fColorFragmentProcessorCnt;
-    int n = processors.numCoverageFragmentProcessors();
+    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();
     bool hasCoverageFP = n > 0;
     bool coverageUsesLocalCoords = false;
     for (int i = 0; i < n; ++i) {
         if (!fps[i]->compatibleWithCoverageAsAlpha()) {
-            fCompatibleWithCoverageAsAlpha = false;
+            analysis.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");
@@ -108,21 +112,14 @@
     }
 
     if (clipFP) {
-        fCompatibleWithCoverageAsAlpha &= clipFP->compatibleWithCoverageAsAlpha();
+        analysis.fCompatibleWithCoverageAsAlpha &= clipFP->compatibleWithCoverageAsAlpha();
         coverageUsesLocalCoords |= clipFP->usesLocalCoords();
         hasCoverageFP = true;
     }
-    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);
-    }
+    int colorFPsToEliminate = colorAnalysis.initialProcessorsToEliminate(overrideInputColor);
+    analysis.fInputColorType = static_cast<Analysis::PackedInputColorType>(
+            colorFPsToEliminate ? Analysis::kOverridden_InputColorType
+                                : Analysis::kOriginal_InputColorType);
 
     GrProcessorAnalysisCoverage outputCoverage;
     if (GrProcessorAnalysisCoverage::kLCD == coverageInput) {
@@ -132,82 +129,51 @@
     } else {
         outputCoverage = GrProcessorAnalysisCoverage::kNone;
     }
-    fOutputCoverageType = static_cast<unsigned>(outputCoverage);
 
     GrXPFactory::AnalysisProperties props = GrXPFactory::GetAnalysisProperties(
-            processors.fXPFactory, colorInfo.outputColor(), outputCoverage, caps);
-    if (!processors.numCoverageFragmentProcessors() &&
+            this->xpFactory(), colorAnalysis.outputColor(), outputCoverage, caps);
+    if (!this->numCoverageFragmentProcessors() &&
         GrProcessorAnalysisCoverage::kNone == coverageInput) {
-        fCanCombineOverlappedStencilAndCover = SkToBool(
+        analysis.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.
-        fCanCombineOverlappedStencilAndCover = false;
+        analysis.fCanCombineOverlappedStencilAndCover = false;
     }
-    fRequiresDstTexture = SkToBool(props & GrXPFactory::AnalysisProperties::kRequiresDstTexture);
-    fIgnoresInputColor = SkToBool(props & GrXPFactory::AnalysisProperties::kIgnoresInputColor);
-    fCompatibleWithCoverageAsAlpha &=
+    analysis.fRequiresDstTexture =
+            SkToBool(props & GrXPFactory::AnalysisProperties::kRequiresDstTexture);
+    analysis.fCompatibleWithCoverageAsAlpha &=
             SkToBool(props & GrXPFactory::AnalysisProperties::kCompatibleWithAlphaAsCoverage);
-    fRequiresBarrierBetweenOverlappingDraws = SkToBool(
+    analysis.fRequiresBarrierBetweenOverlappingDraws = SkToBool(
             props & GrXPFactory::AnalysisProperties::kRequiresBarrierBetweenOverlappingDraws);
     if (props & GrXPFactory::AnalysisProperties::kIgnoresInputColor) {
-        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;
+        colorFPsToEliminate = this->numColorFragmentProcessors();
+        analysis.fInputColorType =
+                static_cast<Analysis::PackedInputColorType>(Analysis::kIgnored_InputColorType);
+        analysis.fUsesLocalCoords = coverageUsesLocalCoords;
     } else {
-        fUsesLocalCoords = coverageUsesLocalCoords | colorInfo.usesLocalCoords();
+        analysis.fUsesLocalCoords = coverageUsesLocalCoords | colorAnalysis.usesLocalCoords();
     }
-}
-
-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;
-}
-
-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;
+    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;
+
+    analysis.fIsInitialized = true;
+    return analysis;
 }
diff --git a/src/gpu/GrProcessorSet.h b/src/gpu/GrProcessorSet.h
index 39ba013..289ca1b 100644
--- a/src/gpu/GrProcessorSet.h
+++ b/src/gpu/GrProcessorSet.h
@@ -14,6 +14,7 @@
 #include "SkTemplates.h"
 
 class GrAppliedClip;
+class GrXferProcessor;
 class GrXPFactory;
 
 class GrProcessorSet : private SkNoncopyable {
@@ -22,14 +23,6 @@
 
     ~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;
@@ -46,7 +39,10 @@
         return fFragmentProcessors[idx + fColorFragmentProcessorCnt + fFragmentProcessorOffset];
     }
 
-    const GrXPFactory* xpFactory() const { return fXPFactory; }
+    const GrXferProcessor* xferProcessor() const {
+        SkASSERT(this->isFinalized());
+        return fXP.fProcessor;
+    }
 
     bool usesDistanceVectorField() const { return SkToBool(fFlags & kUseDistanceVectorField_Flag); }
     bool disableOutputConversionToSRGB() const {
@@ -54,74 +50,20 @@
     }
     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 track analysis of color and coverage values through the processors.
+     * This is used to report results of processor analysis when a processor set is finalized (see
+     * below).
      */
     class Analysis {
     public:
-        /**
-         * 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;
-        }
+        Analysis(const Analysis&) = default;
+        Analysis() { *reinterpret_cast<uint32_t*>(this) = 0; }
 
-        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 isInitialized() const { return fIsInitialized; }
         bool usesLocalCoords() const { return fUsesLocalCoords; }
         bool requiresDstTexture() const { return fRequiresDstTexture; }
         bool canCombineOverlappedStencilAndCover() const {
@@ -131,60 +73,54 @@
             return fRequiresBarrierBetweenOverlappingDraws;
         }
         bool isCompatibleWithCoverageAsAlpha() const { return fCompatibleWithCoverageAsAlpha; }
-        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;
+
+        bool inputColorIsIgnored() const { return fInputColorType == kIgnored_InputColorType; }
+        bool inputColorIsOverridden() const {
+            return fInputColorType == kOverridden_InputColorType;
         }
 
     private:
-        enum class ColorType : unsigned { kUnknown, kConstant, kOpaque };
+        enum InputColorType : uint32_t {
+            kOriginal_InputColorType,
+            kOverridden_InputColorType,
+            kIgnored_InputColorType
+        };
 
-        ColorType outputColorType() const { return static_cast<ColorType>(fOutputColorType); }
+        // MSVS 2015 won't pack different underlying types
+        using PackedBool = uint32_t;
+        using PackedInputColorType = uint32_t;
 
-        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;
-        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;
+        PackedBool fIsInitialized : 1;
+        PackedInputColorType fInputColorType : 2;
 
         friend class GrProcessorSet;
     };
-    GR_STATIC_ASSERT(sizeof(Analysis) == 2 * sizeof(GrColor) + sizeof(uint32_t));
+    GR_STATIC_ASSERT(sizeof(Analysis) == sizeof(uint32_t));
 
-    void analyzeAndEliminateFragmentProcessors(Analysis*,
-                                               const GrProcessorAnalysisColor& colorInput,
-                                               const GrProcessorAnalysisCoverage coverageInput,
-                                               const GrAppliedClip*, const GrCaps&);
+    /**
+     * 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); }
 
 private:
     // This absurdly large limit allows Analysis and this to pack fields together.
@@ -194,11 +130,22 @@
         kUseDistanceVectorField_Flag = 0x1,
         kDisableOutputConversionToSRGB_Flag = 0x2,
         kAllowSRGBInputs_Flag = 0x4,
-        kPendingExecution_Flag = 0x8
+        kFinalized_Flag = 0x8
     };
 
-    const GrXPFactory* fXPFactory = nullptr;
+    union XP {
+        XP(const GrXPFactory* factory) : fFactory(factory) {}
+        const GrXPFactory* fFactory;
+        const GrXferProcessor* fProcessor;
+    };
+
+    const GrXPFactory* xpFactory() const {
+        SkASSERT(!this->isFinalized());
+        return fXP.fFactory;
+    }
+
     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 a4b7731..b0a1b6d 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1688,30 +1688,33 @@
     }
 
     GrResourceProvider* resourceProvider = fContext->resourceProvider();
-    if (pipelineBuilder.hasUserStencilSettings() || appliedClip.hasStencilClip()) {
+    bool usesStencil = pipelineBuilder.hasUserStencilSettings() || appliedClip.hasStencilClip();
+    if (usesStencil) {
         if (!resourceProvider->attachStencilAttachment(this->accessRenderTarget())) {
             SkDebugf("ERROR creating stencil attachment. Draw skipped.\n");
             return SK_InvalidUniqueID;
         }
     }
 
-    GrProcessorSet::Analysis analysis;
-    op->analyzeProcessors(&analysis, &pipelineBuilder, &appliedClip, *this->caps());
+    bool isMixedSamples = fRenderTargetProxy->isMixedSampled() &&
+                          (pipelineBuilder.isHWAntialias() || usesStencil);
+
+    GrColor overrideColor;
+    GrProcessorSet::Analysis analysis = op->analyzeUpdateAndRecordProcessors(
+            &pipelineBuilder, &appliedClip, isMixedSamples, *this->caps(), &overrideColor);
 
     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);
+    op->initPipeline(args, analysis, overrideColor);
     // 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 9b6e576..9d17a82 100644
--- a/src/gpu/instanced/InstancedRendering.cpp
+++ b/src/gpu/instanced/InstancedRendering.cpp
@@ -332,17 +332,19 @@
 }
 
 bool InstancedRendering::Op::xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) {
-    GrProcessorSet::Analysis analysis;
+    SkASSERT(State::kRecordingDraws == fInstancedRendering->fState);
     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();
     }
-    fProcessors.analyzeAndEliminateFragmentProcessors(&analysis, this->getSingleInstance().fColor,
-                                                      coverageInput, clip, caps);
-    fAnalysisColor = analysis.outputColor();
+    GrProcessorSet::Analysis analysis =
+            fProcessors.finalize(this->getSingleInstance().fColor, coverageInput, clip,
+                                 isMixedSamples, caps, &this->getSingleDraw().fInstance.fColor);
 
     Draw& draw = this->getSingleDraw(); // This will assert if we have > 1 command.
     SkASSERT(draw.fGeometry.isEmpty());
@@ -363,11 +365,6 @@
         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();
@@ -378,7 +375,6 @@
 void InstancedRendering::Op::wasRecorded() {
     SkASSERT(!fIsTracked);
     fInstancedRendering->fTrackedOps.addToTail(this);
-    fProcessors.makePendingExecution();
     fIsTracked = true;
 }
 
@@ -413,7 +409,6 @@
     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;
@@ -470,21 +465,9 @@
 
     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.fXPInputColor = fAnalysisColor;
-    args.fXPInputCoverage = coverage;
-    args.fAppliedClip = clip;
+    args.fAppliedClip = state->drawOpArgs().fAppliedClip;
     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 3ec8214..a8b9530 100644
--- a/src/gpu/instanced/InstancedRendering.h
+++ b/src/gpu/instanced/InstancedRendering.h
@@ -151,7 +151,6 @@
         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 0458370..dbdd9d8 100644
--- a/src/gpu/ops/GrDrawOp.h
+++ b/src/gpu/ops/GrDrawOp.h
@@ -76,7 +76,8 @@
      * 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.
+     * 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.
      */
     virtual bool xpRequiresDstTexture(const GrCaps&, const GrAppliedClip*) = 0;
 
diff --git a/src/gpu/ops/GrDrawPathOp.cpp b/src/gpu/ops/GrDrawPathOp.cpp
index 1c68fdf..3632787 100644
--- a/src/gpu/ops/GrDrawPathOp.cpp
+++ b/src/gpu/ops/GrDrawPathOp.cpp
@@ -12,13 +12,15 @@
 #include "SkTemplates.h"
 
 GrDrawPathOpBase::GrDrawPathOpBase(uint32_t classID, const SkMatrix& viewMatrix, GrPaint&& paint,
-                                   GrPathRendering::FillType fill, GrAA aa)
+                                   GrPathRendering::FillType fill, GrAAType aaType)
         : INHERITED(classID)
         , fViewMatrix(viewMatrix)
+        , fInputColor(paint.getColor())
         , fProcessorSet(std::move(paint))
-        , fAnalysis(paint.getColor())
         , fFillType(fill)
-        , fAA(aa) {}
+        , fAAType(aaType) {
+    SkASSERT(fAAType != GrAAType::kCoverage);
+}
 
 SkString GrDrawPathOp::dumpInfo() const {
     SkString string;
@@ -38,16 +40,13 @@
                     0xffff>()
     };
     GrPipeline::InitArgs args;
-    auto analysis = this->processorAnalysis();
     args.fProcessors = &this->processors();
-    args.fFlags = GrAA::kYes == fAA ? GrPipeline::kHWAntialias_Flag : 0;
+    args.fFlags = GrAATypeIsHW(fAAType) ? 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);
 }
@@ -90,9 +89,9 @@
 
 GrDrawPathRangeOp::GrDrawPathRangeOp(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x,
                                      SkScalar y, GrPaint&& paint, GrPathRendering::FillType fill,
-                                     GrAA aa, GrPathRange* range, const InstanceData* instanceData,
-                                     const SkRect& bounds)
-        : INHERITED(ClassID(), viewMatrix, std::move(paint), fill, aa)
+                                     GrAAType aaType, GrPathRange* range,
+                                     const InstanceData* instanceData, const SkRect& bounds)
+        : INHERITED(ClassID(), viewMatrix, std::move(paint), fill, aaType)
         , fPathRange(range)
         , fTotalPathCount(instanceData->count())
         , fScale(scale) {
diff --git a/src/gpu/ops/GrDrawPathOp.h b/src/gpu/ops/GrDrawPathOp.h
index 0631adc..8995de4 100644
--- a/src/gpu/ops/GrDrawPathOp.h
+++ b/src/gpu/ops/GrDrawPathOp.h
@@ -23,31 +23,33 @@
 
 class GrDrawPathOpBase : public GrDrawOp {
 protected:
-    GrDrawPathOpBase(uint32_t classID, const SkMatrix& viewMatrix, GrPaint&& paint,
-                     GrPathRendering::FillType fill, GrAA aa);
+    GrDrawPathOpBase(uint32_t classID, const SkMatrix& viewMatrix, GrPaint&&,
+                     GrPathRendering::FillType, GrAAType);
     FixedFunctionFlags fixedFunctionFlags() const override {
-        return FixedFunctionFlags::kUsesHWAA | FixedFunctionFlags::kUsesStencil;
+        if (GrAATypeIsHW(fAAType)) {
+            return FixedFunctionFlags::kUsesHWAA | FixedFunctionFlags::kUsesStencil;
+        }
+        return 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 fAnalysis.inputColor(); }
+    GrColor color() const { return fInputColor; }
     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) {
-        fProcessorSet.analyzeAndEliminateFragmentProcessors(
-                &fAnalysis, fAnalysis.inputColor(), GrProcessorAnalysisCoverage::kNone, clip, caps);
+        bool isMixedSamples = GrAAType::kMixedSamples == fAAType;
+        fAnalysis = fProcessorSet.finalize(fInputColor, GrProcessorAnalysisCoverage::kNone, clip,
+                                           isMixedSamples, caps, &fInputColor);
         return fAnalysis;
     }
     const GrProcessorSet::Analysis& processorAnalysis() const {
-        SkASSERT(fAnalysis.isInitializedWithProcessorSet());
+        SkASSERT(fAnalysis.isInitialized());
         return fAnalysis;
     }
 
@@ -55,10 +57,11 @@
     void onPrepare(GrOpFlushState*) final {}
 
     SkMatrix fViewMatrix;
+    GrColor fInputColor;
     GrProcessorSet fProcessorSet;
     GrProcessorSet::Analysis fAnalysis;
     GrPathRendering::FillType fFillType;
-    GrAA fAA;
+    GrAAType fAAType;
 
     typedef GrDrawOp INHERITED;
 };
@@ -67,9 +70,10 @@
 public:
     DEFINE_OP_CLASS_ID
 
-    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));
+    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));
     }
 
     const char* name() const override { return "DrawPath"; }
@@ -77,8 +81,8 @@
     SkString dumpInfo() const override;
 
 private:
-    GrDrawPathOp(const SkMatrix& viewMatrix, GrPaint&& paint, GrAA aa, const GrPath* path)
-            : GrDrawPathOpBase(ClassID(), viewMatrix, std::move(paint), path->getFillType(), aa)
+    GrDrawPathOp(const SkMatrix& viewMatrix, GrPaint&& paint, GrAAType aaType, const GrPath* path)
+            : GrDrawPathOpBase(ClassID(), viewMatrix, std::move(paint), path->getFillType(), aaType)
             , fPath(path) {
         this->setTransformedBounds(path->getBounds(), viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
     }
@@ -160,11 +164,12 @@
 
     static std::unique_ptr<GrDrawOp> Make(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x,
                                           SkScalar y, GrPaint&& paint,
-                                          GrPathRendering::FillType fill, GrAA aa,
+                                          GrPathRendering::FillType fill, GrAAType aaType,
                                           GrPathRange* range, const InstanceData* instanceData,
                                           const SkRect& bounds) {
-        return std::unique_ptr<GrDrawOp>(new GrDrawPathRangeOp(
-                viewMatrix, scale, x, y, std::move(paint), fill, aa, range, instanceData, bounds));
+        return std::unique_ptr<GrDrawOp>(new GrDrawPathRangeOp(viewMatrix, scale, x, y,
+                                                               std::move(paint), fill, aaType,
+                                                               range, instanceData, bounds));
     }
 
     const char* name() const override { return "DrawPathRange"; }
@@ -173,8 +178,8 @@
 
 private:
     GrDrawPathRangeOp(const SkMatrix& viewMatrix, SkScalar scale, SkScalar x, SkScalar y,
-                      GrPaint&& paint, GrPathRendering::FillType fill, GrAA aa, GrPathRange* range,
-                      const InstanceData* instanceData, const SkRect& bounds);
+                      GrPaint&& paint, GrPathRendering::FillType fill, GrAAType aaType,
+                      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 41d3848..8fbd30d 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -101,20 +101,22 @@
      * Performs analysis of the fragment processors in GrProcessorSet and GrAppliedClip using the
      * initial color and coverage from this op's geometry processor.
      */
-    void analyzeProcessors(GrProcessorSet::Analysis* analysis,
-                           GrPipelineBuilder* pipelineBuilder,
-                           const GrAppliedClip* appliedClip,
-                           const GrCaps& caps) const {
+    GrProcessorSet::Analysis analyzeUpdateAndRecordProcessors(GrPipelineBuilder* pipelineBuilder,
+                                                              const GrAppliedClip* appliedClip,
+                                                              bool isMixedSamples,
+                                                              const GrCaps& caps,
+                                                              GrColor* overrideColor) const {
         GrProcessorAnalysisColor inputColor;
         GrProcessorAnalysisCoverage inputCoverage;
         this->getProcessorAnalysisInputs(&inputColor, &inputCoverage);
-        pipelineBuilder->analyzeAndEliminateFragmentProcessors(analysis, inputColor, inputCoverage,
-                                                               appliedClip, caps);
+        return pipelineBuilder->finalizeProcessors(inputColor, inputCoverage, appliedClip,
+                                                   isMixedSamples, caps, overrideColor);
     }
 
-    void initPipeline(const GrPipeline::InitArgs& args, const GrProcessorSet::Analysis& analysis) {
+    void initPipeline(const GrPipeline::InitArgs& args, const GrProcessorSet::Analysis& analysis,
+                      GrColor overrideColor) {
         fPipeline.init(args);
-        this->applyPipelineOptimizations(PipelineOptimizations(analysis));
+        this->applyPipelineOptimizations(PipelineOptimizations(analysis, overrideColor));
     }
 
     /**
@@ -138,11 +140,11 @@
      */
     class PipelineOptimizations {
     public:
-        PipelineOptimizations(const GrProcessorSet::Analysis& analysis) {
+        PipelineOptimizations(const GrProcessorSet::Analysis& analysis, GrColor overrideColor) {
             fFlags = 0;
-            if (analysis.getInputColorOverrideAndColorProcessorEliminationCount(&fOverrideColor) >=
-                0) {
+            if (analysis.inputColorIsOverridden()) {
                 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 a4e32ac..bd5c297 100644
--- a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
+++ b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
@@ -145,9 +145,8 @@
                                                            std::move(coverOp));
         }
     } else {
-        GrAA aa = GrBoolToAA(GrAATypeIsHW(args.fAAType));
         std::unique_ptr<GrDrawOp> op =
-                GrDrawPathOp::Make(viewMatrix, std::move(args.fPaint), aa, path.get());
+                GrDrawPathOp::Make(viewMatrix, std::move(args.fPaint), args.fAAType, 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 e572e5d..0ae4023 100644
--- a/src/gpu/text/GrStencilAndCoverTextContext.cpp
+++ b/src/gpu/text/GrStencilAndCoverTextContext.cpp
@@ -570,9 +570,7 @@
                                                  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(
@@ -606,9 +604,18 @@
                                               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, runAA, glyphs.get(),
+                std::move(grPaint), GrPathRendering::kWinding_FillType, aaType, 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 f06442b..54c0a9d 100644
--- a/src/gpu/text/GrStencilAndCoverTextContext.h
+++ b/src/gpu/text/GrStencilAndCoverTextContext.h
@@ -83,7 +83,7 @@
 
         size_t computeSizeInCache() const;
 
-        GrAA isAntiAlias() const { return fFont.isAntiAlias() ? GrAA::kYes : GrAA::kNo; }
+        GrAA aa() const { return fFont.isAntiAlias() ? GrAA::kYes : GrAA::kNo; }
 
     private:
         typedef GrDrawPathRangeOp::InstanceData InstanceData;
diff --git a/tests/GpuSampleLocationsTest.cpp b/tests/GpuSampleLocationsTest.cpp
index 75fa6fcf..26d7e70 100644
--- a/tests/GpuSampleLocationsTest.cpp
+++ b/tests/GpuSampleLocationsTest.cpp
@@ -91,17 +91,8 @@
     virtual ~TestSampleLocationsInterface() {}
 };
 
-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);
+static sk_sp<GrPipeline> construct_dummy_pipeline(GrRenderTargetContext* dc) {
+    return sk_sp<GrPipeline>(new GrPipeline(dc->accessRenderTarget(), SkBlendMode::kSrcOver));
 }
 
 void assert_equal(skiatest::Reporter* reporter, const SamplePattern& pattern,
@@ -146,11 +137,10 @@
         for (int i = 0; i < numTestPatterns; ++i) {
             testInterface->overrideSamplePattern(kTestPatterns[i]);
             for (GrRenderTargetContext* dc : {bottomUps[i].get(), topDowns[i].get()}) {
-                GrPipeline dummyPipeline;
-                construct_dummy_pipeline(dc, &dummyPipeline);
+                sk_sp<GrPipeline> dummyPipeline = construct_dummy_pipeline(dc);
                 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 f2bd1e7..4066d28 100644
--- a/tests/GrPorterDuffTest.cpp
+++ b/tests/GrPorterDuffTest.cpp
@@ -55,16 +55,30 @@
     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(inputColor, inputCoverage, xpf, caps);
+
+            GrProcessorSet::Analysis analysis = do_analysis(xpf, inputColor, inputCoverage, caps);
             fCompatibleWithCoverageAsAlpha = analysis.isCompatibleWithCoverageAsAlpha();
             fCanCombineOverlappedStencilAndCover = analysis.canCombineOverlappedStencilAndCover();
-            fIgnoresInputColor = analysis.isInputColorIgnored();
+            fIgnoresInputColor = analysis.inputColorIsIgnored();
             sk_sp<GrXferProcessor> xp(
                     GrXPFactory::MakeXferProcessor(xpf, inputColor, inputCoverage, false, caps));
             TEST_ASSERT(!analysis.requiresDstTexture());
@@ -1040,8 +1054,6 @@
             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) {