Changes to GrProcessorSet::FragmentProcessorAnalysis to prepare for deferred pipeline creation.

This compacts the object so that it is more efficient for ops to store it.

It also adds a new constructor and query that will allow ops to use the analysis to also store the GrPaint's color.

This has the side effect of limiting the number of color processors on a GrProcessorSet to 64K which is just under 64K more than should ever be needed.

Change-Id: I4e6bc8e3f81bb2ff6a73af685beb6fb928a3de67
Reviewed-on: https://skia-review.googlesource.com/8972
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrProcessorSet.cpp b/src/gpu/GrProcessorSet.cpp
index e5beca0..c4b8262 100644
--- a/src/gpu/GrProcessorSet.cpp
+++ b/src/gpu/GrProcessorSet.cpp
@@ -12,18 +12,23 @@
 
 GrProcessorSet::GrProcessorSet(GrPaint&& paint) {
     fXPFactory = paint.fXPFactory;
-    fColorFragmentProcessorCnt = paint.numColorFragmentProcessors();
-    fFragmentProcessors.reset(paint.numTotalFragmentProcessors());
-    int i = 0;
-    for (auto& fp : paint.fColorFragmentProcessors) {
-        fFragmentProcessors[i++] = fp.release();
-    }
-    for (auto& fp : paint.fCoverageFragmentProcessors) {
-        fFragmentProcessors[i++] = fp.release();
-    }
     fFlags = 0;
-    if (paint.usesDistanceVectorField()) {
-        fFlags |= kUseDistanceVectorField_Flag;
+    if (paint.numColorFragmentProcessors() <= kMaxColorProcessors) {
+        fColorFragmentProcessorCnt = paint.numColorFragmentProcessors();
+        fFragmentProcessors.reset(paint.numTotalFragmentProcessors());
+        int i = 0;
+        for (auto& fp : paint.fColorFragmentProcessors) {
+            fFragmentProcessors[i++] = fp.release();
+        }
+        for (auto& fp : paint.fCoverageFragmentProcessors) {
+            fFragmentProcessors[i++] = fp.release();
+        }
+        if (paint.usesDistanceVectorField()) {
+            fFlags |= kUseDistanceVectorField_Flag;
+        }
+    } else {
+        SkDebugf("Insane number of color fragment processors in paint. Dropping all processors.");
+        fColorFragmentProcessorCnt = 0;
     }
     if (paint.getDisableOutputConversionToSRGB()) {
         fFlags |= kDisableOutputConversionToSRGB_Flag;
@@ -35,13 +40,14 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-void GrProcessorSet::FragmentProcessorAnalysis::internalReset(const GrPipelineInput& colorInput,
-                                                              const GrPipelineInput coverageInput,
-                                                              const GrProcessorSet& processors,
-                                                              const GrFragmentProcessor* clipFP,
-                                                              const GrCaps& caps) {
+void GrProcessorSet::FragmentProcessorAnalysis::internalInit(const GrPipelineInput& colorInput,
+                                                             const GrPipelineInput coverageInput,
+                                                             const GrProcessorSet& processors,
+                                                             const GrFragmentProcessor* clipFP,
+                                                             const GrCaps& caps) {
     GrProcOptInfo colorInfo(colorInput);
     fCompatibleWithCoverageAsAlpha = !coverageInput.isLCDCoverage();
+    fValidInputColor = colorInput.isConstant(&fInputColor);
 
     const GrFragmentProcessor* const* fps = processors.fFragmentProcessors.get();
     colorInfo.analyzeProcessors(fps, processors.fColorFragmentProcessorCnt);
@@ -65,38 +71,41 @@
         fUsesLocalCoords |= clipFP->usesLocalCoords();
         hasCoverageFP = true;
     }
-    fInitialColorProcessorsToEliminate =
-            colorInfo.initialProcessorsToEliminate(&fOverrideInputColor);
+    fInitialColorProcessorsToEliminate = colorInfo.initialProcessorsToEliminate(&fInputColor);
+    fValidInputColor |= SkToBool(fInitialColorProcessorsToEliminate);
 
     bool opaque = colorInfo.isOpaque();
     if (colorInfo.hasKnownOutputColor(&fKnownOutputColor)) {
-        fColorType = opaque ? ColorType::kOpaqueConstant : ColorType::kConstant;
+        fOutputColorType = static_cast<unsigned>(opaque ? ColorType::kOpaqueConstant
+                                                        : ColorType::kConstant);
     } else if (opaque) {
-        fColorType = ColorType::kOpaque;
+        fOutputColorType = static_cast<unsigned>(ColorType::kOpaque);
     } else {
-        fColorType = ColorType::kUnknown;
+        fOutputColorType = static_cast<unsigned>(ColorType::kUnknown);
     }
 
     if (coverageInput.isLCDCoverage()) {
-        fCoverageType = CoverageType::kLCD;
+        fOutputCoverageType = static_cast<unsigned>(CoverageType::kLCD);
     } else {
-        fCoverageType = hasCoverageFP || !coverageInput.isSolidWhite()
-                                ? CoverageType::kSingleChannel
-                                : CoverageType::kNone;
+        fOutputCoverageType = hasCoverageFP || !coverageInput.isSolidWhite()
+                                      ? static_cast<unsigned>(CoverageType::kSingleChannel)
+                                      : static_cast<unsigned>(CoverageType::kNone);
     }
 }
 
-void GrProcessorSet::FragmentProcessorAnalysis::reset(const GrPipelineInput& colorInput,
-                                                      const GrPipelineInput coverageInput,
-                                                      const GrProcessorSet& processors,
-                                                      const GrAppliedClip& appliedClip,
-                                                      const GrCaps& caps) {
-    this->internalReset(colorInput, coverageInput, processors,
-                        appliedClip.clipCoverageFragmentProcessor(), caps);
+void GrProcessorSet::FragmentProcessorAnalysis::init(const GrPipelineInput& colorInput,
+                                                     const GrPipelineInput 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::FragmentProcessorAnalysis::FragmentProcessorAnalysis(
         const GrPipelineInput& colorInput, const GrPipelineInput coverageInput, const GrCaps& caps)
         : FragmentProcessorAnalysis() {
-    this->internalReset(colorInput, coverageInput, GrProcessorSet(GrPaint()), nullptr, caps);
+    this->internalInit(colorInput, coverageInput, GrProcessorSet(GrPaint()), nullptr, caps);
 }
diff --git a/src/gpu/GrProcessorSet.h b/src/gpu/GrProcessorSet.h
index de8bd69..8203491 100644
--- a/src/gpu/GrProcessorSet.h
+++ b/src/gpu/GrProcessorSet.h
@@ -55,63 +55,114 @@
      */
     class FragmentProcessorAnalysis {
     public:
-        FragmentProcessorAnalysis() = default;
-        // This version is used by a unit test that assumes no clip and no fragment processors.
+        /**
+         * This constructor allows an op to record its initial color in a FragmentProcessorAnalysis
+         * member and 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 FragmentProcessorAnalysis(GrColor initialColor) : FragmentProcessorAnalysis() {
+            fInputColor = initialColor;
+            fValidInputColor = true;
+        }
+
+        FragmentProcessorAnalysis()
+                : fIsInitializedWithProcessorSet(false)
+                , fCompatibleWithCoverageAsAlpha(true)
+                , fValidInputColor(false)
+                , fOutputCoverageType(static_cast<unsigned>(CoverageType::kNone))
+                , fOutputColorType(static_cast<unsigned>(ColorType::kUnknown))
+                , fInitialColorProcessorsToEliminate(0) {}
+
+        // This version is used by a unit test that assumes no clip, no processors, and no PLS.
         FragmentProcessorAnalysis(const GrPipelineInput& colorInput,
                                   const GrPipelineInput coverageInput, const GrCaps&);
 
-        void reset(const GrPipelineInput& colorInput, const GrPipelineInput coverageInput,
-                   const GrProcessorSet&, const GrAppliedClip&, const GrCaps&);
+        void init(const GrPipelineInput& colorInput, const GrPipelineInput coverageInput,
+                  const GrProcessorSet&, const GrAppliedClip*, const GrCaps&);
+
+        bool isInitializedWithProcessorSet() const { return fIsInitializedWithProcessorSet; }
 
         int initialColorProcessorsToEliminate(GrColor* newInputColor) const {
             if (fInitialColorProcessorsToEliminate > 0) {
-                *newInputColor = fOverrideInputColor;
+                SkASSERT(fValidInputColor);
+                *newInputColor = fInputColor;
             }
             return fInitialColorProcessorsToEliminate;
         }
 
+        /**
+         * 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 isCompatibleWithCoverageAsAlpha() const { return fCompatibleWithCoverageAsAlpha; }
         bool isOutputColorOpaque() const {
-            return ColorType::kOpaque == fColorType || ColorType::kOpaqueConstant == fColorType;
+            return ColorType::kOpaque == this->outputColorType() ||
+                   ColorType::kOpaqueConstant == this->outputColorType();
         }
         bool hasKnownOutputColor(GrColor* color = nullptr) const {
-            bool constant =
-                    ColorType::kConstant == fColorType || ColorType::kOpaqueConstant == fColorType;
+            bool constant = ColorType::kConstant == this->outputColorType() ||
+                            ColorType::kOpaqueConstant == this->outputColorType();
             if (constant && color) {
                 *color = fKnownOutputColor;
             }
             return constant;
         }
-        bool hasCoverage() const { return CoverageType::kNone != fCoverageType; }
-        bool hasLCDCoverage() const { return CoverageType::kLCD == fCoverageType; }
+        bool hasCoverage() const { return CoverageType::kNone != this->outputCoverageType(); }
+        bool hasLCDCoverage() const { return CoverageType::kLCD == this->outputCoverageType(); }
 
     private:
-        void internalReset(const GrPipelineInput& colorInput, const GrPipelineInput coverageInput,
-                           const GrProcessorSet&, const GrFragmentProcessor* clipFP, const GrCaps&);
+        enum class ColorType : unsigned { kUnknown, kOpaqueConstant, kConstant, kOpaque };
+        enum class CoverageType : unsigned { kNone, kSingleChannel, kLCD };
 
-        enum class ColorType { kUnknown, kOpaqueConstant, kConstant, kOpaque };
-        enum class CoverageType { kNone, kSingleChannel, kLCD };
+        CoverageType outputCoverageType() const {
+            return static_cast<CoverageType>(fOutputCoverageType);
+        }
+        ColorType outputColorType() const { return static_cast<ColorType>(fOutputColorType); }
 
-        bool fCompatibleWithCoverageAsAlpha = true;
-        bool fUsesLocalCoords = false;
-        CoverageType fCoverageType = CoverageType::kNone;
-        ColorType fColorType = ColorType::kUnknown;
-        int fInitialColorProcessorsToEliminate = 0;
-        GrColor fOverrideInputColor;
+        void internalInit(const GrPipelineInput& colorInput, const GrPipelineInput coverageInput,
+                          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;
+        unsigned fOutputCoverageType : 2;
+        unsigned fOutputColorType : 2;
+        unsigned fInitialColorProcessorsToEliminate : 32 - 8;
+
+        GrColor fInputColor;
         GrColor fKnownOutputColor;
     };
+    GR_STATIC_ASSERT(sizeof(FragmentProcessorAnalysis) == 2 * sizeof(GrColor) + sizeof(uint32_t));
 
 private:
-    const GrXPFactory* fXPFactory = nullptr;
-    SkAutoSTArray<4, const GrFragmentProcessor*> fFragmentProcessors;
-    int fColorFragmentProcessorCnt;
-    enum Flags : uint32_t {
+    // This absurdly large limit allows FragmentProcessorAnalysis and this to pack fields together.
+    static constexpr int kMaxColorProcessors = SK_MaxU16;
+
+    enum Flags : uint16_t {
         kUseDistanceVectorField_Flag = 0x1,
         kDisableOutputConversionToSRGB_Flag = 0x2,
         kAllowSRGBInputs_Flag = 0x4
     };
-    uint32_t fFlags;
+
+    const GrXPFactory* fXPFactory = nullptr;
+    SkAutoSTArray<4, const GrFragmentProcessor*> fFragmentProcessors;
+    uint16_t fColorFragmentProcessorCnt;
+    uint16_t fFlags;
 };
 
 #endif
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 051af82..7a6abe0 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1714,7 +1714,7 @@
     }
 
     GrProcessorSet::FragmentProcessorAnalysis analysis;
-    op->analyzeProcessors(&analysis, pipelineBuilder.processors(), appliedClip, *this->caps());
+    op->analyzeProcessors(&analysis, pipelineBuilder.processors(), &appliedClip, *this->caps());
 
     GrPipeline::InitArgs args;
     pipelineBuilder.getPipelineInitArgs(&args);
diff --git a/src/gpu/ops/GrDrawOp.h b/src/gpu/ops/GrDrawOp.h
index 18c218f..62a1688 100644
--- a/src/gpu/ops/GrDrawOp.h
+++ b/src/gpu/ops/GrDrawOp.h
@@ -66,11 +66,11 @@
      */
     void analyzeProcessors(GrProcessorSet::FragmentProcessorAnalysis* analysis,
                            const GrProcessorSet& processors,
-                           const GrAppliedClip& appliedClip,
+                           const GrAppliedClip* appliedClip,
                            const GrCaps& caps) const {
         FragmentProcessorAnalysisInputs input;
         this->getFragmentProcessorAnalysisInputs(&input);
-        analysis->reset(*input.colorInput(), *input.coverageInput(), processors, appliedClip, caps);
+        analysis->init(*input.colorInput(), *input.coverageInput(), processors, appliedClip, caps);
     }
 
 protected: