Calculate Primary and Secondary output types in the GrOptDrawState

Follow up CL to https://codereview.chromium.org/545693004/

BUG=skia:
R=bsalomon@google.com

Author: egdaniel@google.com

Review URL: https://codereview.chromium.org/554833002
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index f639744..8726965 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -7,24 +7,24 @@
 
 #include "GrDrawState.h"
 
-#include "GrDrawTargetCaps.h"
 #include "GrOptDrawState.h"
 #include "GrPaint.h"
 
 //////////////////////////////////////////////////////////////////////////////s
 
-GrOptDrawState* GrDrawState::createOptState() const {
-    if (NULL == fCachedOptState) {
+GrOptDrawState* GrDrawState::createOptState(const GrDrawTargetCaps& caps) const {
+    if (NULL == fCachedOptState || caps.getUniqueID() != fCachedCapsID) {
         GrBlendCoeff srcCoeff;
         GrBlendCoeff dstCoeff;
         BlendOptFlags blendFlags = this->getBlendOpts(false, &srcCoeff, &dstCoeff);
-        fCachedOptState = SkNEW_ARGS(GrOptDrawState, (*this, blendFlags, srcCoeff, dstCoeff));
+        fCachedOptState = SkNEW_ARGS(GrOptDrawState, (*this, blendFlags, srcCoeff, dstCoeff, caps));
+        fCachedCapsID = caps.getUniqueID();
     } else {
 #ifdef SK_DEBUG
         GrBlendCoeff srcCoeff;
         GrBlendCoeff dstCoeff;
         BlendOptFlags blendFlags = this->getBlendOpts(false, &srcCoeff, &dstCoeff);
-        SkASSERT(GrOptDrawState(*this, blendFlags, srcCoeff, dstCoeff) == *fCachedOptState);
+        SkASSERT(GrOptDrawState(*this, blendFlags, srcCoeff, dstCoeff, caps) == *fCachedOptState);
 #endif
     }
     fCachedOptState->ref();
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 458fb95..5e4ed9b 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -9,6 +9,7 @@
 #define GrDrawState_DEFINED
 
 #include "GrBlend.h"
+#include "GrDrawTargetCaps.h"
 #include "GrGpuResourceRef.h"
 #include "GrRODrawState.h"
 #include "effects/GrSimpleTextureEffect.h"
@@ -547,7 +548,7 @@
      * GrOptDrawState. In all cases the GrOptDrawState is reffed and ownership is given to the
      * caller.
      */
-    GrOptDrawState* createOptState() const;
+    GrOptDrawState* createOptState(const GrDrawTargetCaps&) const;
 
 private:
     void invalidateOptState() const;
@@ -561,6 +562,7 @@
     void internalSetVertexAttribs(const GrVertexAttrib attribs[], int count, size_t stride);
 
     mutable GrOptDrawState* fCachedOptState;
+    mutable uint32_t fCachedCapsID;
 
     typedef GrRODrawState INHERITED;
 };
diff --git a/src/gpu/GrOptDrawState.cpp b/src/gpu/GrOptDrawState.cpp
index cb03d2d..ac30311 100644
--- a/src/gpu/GrOptDrawState.cpp
+++ b/src/gpu/GrOptDrawState.cpp
@@ -8,11 +8,14 @@
 #include "GrOptDrawState.h"
 
 #include "GrDrawState.h"
+#include "GrDrawTargetCaps.h"
+#include "GrGpu.h"
 
 GrOptDrawState::GrOptDrawState(const GrDrawState& drawState,
                                BlendOptFlags blendOptFlags,
                                GrBlendCoeff optSrcCoeff,
-                               GrBlendCoeff optDstCoeff) : INHERITED(drawState) {
+                               GrBlendCoeff optDstCoeff,
+                               const GrDrawTargetCaps& caps) : INHERITED(drawState) {
     fColor = drawState.getColor();
     fCoverage = drawState.getCoverage();
     fViewMatrix = drawState.getViewMatrix();
@@ -45,8 +48,56 @@
     this->copyEffectiveCoverageStages(drawState);
     this->adjustFromBlendOpts();
     this->getStageStats();
+    this->setOutputStateInfo(caps);
 };
 
+void GrOptDrawState::setOutputStateInfo(const GrDrawTargetCaps& caps) {
+    // Set this default and then possibly change our mind if there is coverage.
+    fPrimaryOutputType = kModulate_PrimaryOutputType;
+    fSecondaryOutputType = kNone_SecondaryOutputType;
+
+    // If we do have coverage determine whether it matters.
+    bool separateCoverageFromColor = this->hasGeometryProcessor();
+    if (!this->isCoverageDrawing() &&
+        (this->numCoverageStages() > 0 ||
+         this->hasGeometryProcessor() ||
+         this->hasCoverageVertexAttribute())) {
+
+        if (caps.dualSourceBlendingSupport()) {
+            if (kZero_GrBlendCoeff == fDstBlend) {
+                // write the coverage value to second color
+                fSecondaryOutputType =  kCoverage_SecondaryOutputType;
+                separateCoverageFromColor = true;
+                fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
+            } else if (kSA_GrBlendCoeff == fDstBlend) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
+                fSecondaryOutputType = kCoverageISA_SecondaryOutputType;
+                separateCoverageFromColor = true;
+                fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
+            } else if (kSC_GrBlendCoeff == fDstBlend) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
+                fSecondaryOutputType = kCoverageISC_SecondaryOutputType;
+                separateCoverageFromColor = true;
+                fDstBlend = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
+            }
+        } else if (fReadsDst &&
+                   kOne_GrBlendCoeff == fSrcBlend &&
+                   kZero_GrBlendCoeff == fDstBlend) {
+            fPrimaryOutputType = kCombineWithDst_PrimaryOutputType;
+            separateCoverageFromColor = true;
+        }
+    }
+
+    // TODO: Once we have flag to know if we only multiply on stages, only push coverage into color
+    // stages if everything is multipy
+    if (!separateCoverageFromColor) {
+        for (int s = 0; s < this->numCoverageStages(); ++s) {
+            fColorStages.push_back(this->getCoverageStage(s));
+        }
+        fCoverageStages.reset();
+    }
+}
+
 void GrOptDrawState::adjustFromBlendOpts() {
 
     switch (fBlendOptFlags) {
diff --git a/src/gpu/GrOptDrawState.h b/src/gpu/GrOptDrawState.h
index 9c9bf94..9445d33 100644
--- a/src/gpu/GrOptDrawState.h
+++ b/src/gpu/GrOptDrawState.h
@@ -28,12 +28,48 @@
     bool requiresVertexShader() const { return fRequiresVertexShader; }
     bool requiresLocalCoordAttrib() const { return fRequiresLocalCoordAttrib; }
 
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name Stage Output Types
+    ////
+
+    enum PrimaryOutputType {
+        // Modulate color and coverage, write result as the color output.
+        kModulate_PrimaryOutputType,
+        // Combines the coverage, dst, and color as coverage * color + (1 - coverage) * dst. This
+        // can only be set if fDstReadKey is non-zero.
+        kCombineWithDst_PrimaryOutputType,
+
+        kPrimaryOutputTypeCnt,
+    };
+
+    enum SecondaryOutputType {
+        // There is no secondary output
+        kNone_SecondaryOutputType,
+        // Writes coverage as the secondary output. Only set if dual source blending is supported
+        // and primary output is kModulate.
+        kCoverage_SecondaryOutputType,
+        // Writes coverage * (1 - colorA) as the secondary output. Only set if dual source blending
+        // is supported and primary output is kModulate.
+        kCoverageISA_SecondaryOutputType,
+        // Writes coverage * (1 - colorRGBA) as the secondary output. Only set if dual source
+        // blending is supported and primary output is kModulate.
+        kCoverageISC_SecondaryOutputType,
+
+        kSecondaryOutputTypeCnt,
+    };
+
+    PrimaryOutputType getPrimaryOutputType() const { return fPrimaryOutputType; }
+    SecondaryOutputType getSecondaryOutputType() const { return fSecondaryOutputType; }
+
+    /// @}
+
 private:
     /**
      * Constructs and optimized drawState out of a GrRODrawState.
      */
     GrOptDrawState(const GrDrawState& drawState, BlendOptFlags blendOptFlags,
-                   GrBlendCoeff optSrcCoeff, GrBlendCoeff optDstCoeff);
+                   GrBlendCoeff optSrcCoeff, GrBlendCoeff optDstCoeff,
+                   const GrDrawTargetCaps& caps);
 
     /**
      * Loops through all the color stage effects to check if the stage will ignore color input or
@@ -72,6 +108,13 @@
      */
     void getStageStats();
 
+    /**
+     * Calculates the primary and secondary output types of the shader. For certain output types
+     * the function may adjust the blend coefficients. After this function is called the src and dst
+     * blend coeffs will represent those used by backend API.
+     */
+    void setOutputStateInfo(const GrDrawTargetCaps&);
+
     // These flags are needed to protect the code from creating an unused uniform color/coverage
     // which will cause shader compiler errors.
     bool            fInputColorIsUsed;
@@ -87,7 +130,11 @@
 
     BlendOptFlags   fBlendOptFlags;
 
-    friend GrOptDrawState* GrDrawState::createOptState() const;
+    // Fragment shader color outputs
+    PrimaryOutputType  fPrimaryOutputType : 8;
+    SecondaryOutputType  fSecondaryOutputType : 8;
+
+    friend GrOptDrawState* GrDrawState::createOptState(const GrDrawTargetCaps&) const;
     typedef GrRODrawState INHERITED;
 };
 
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index a695173..1639a84 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -74,28 +74,6 @@
     fProgramID = 0;
 }
 
-void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
-                                GrBlendCoeff* dstCoeff) const {
-    switch (fDesc.getHeader().fCoverageOutput) {
-        case GrGLProgramDesc::kModulate_CoverageOutput:
-            break;
-        // The prog will write a coverage value to the secondary
-        // output and the dst is blended by one minus that value.
-        case GrGLProgramDesc::kSecondaryCoverage_CoverageOutput:
-        case GrGLProgramDesc::kSecondaryCoverageISA_CoverageOutput:
-        case GrGLProgramDesc::kSecondaryCoverageISC_CoverageOutput:
-            *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
-            break;
-        case GrGLProgramDesc::kCombineWithDst_CoverageOutput:
-            // We should only have set this if the blend was specified as (1, 0)
-            SkASSERT(kOne_GrBlendCoeff == *srcCoeff && kZero_GrBlendCoeff == *dstCoeff);
-            break;
-        default:
-            SkFAIL("Unexpected coverage output");
-            break;
-    }
-}
-
 void GrGLProgram::initSamplerUniforms() {
     GL_CALL(UseProgram(fProgramID));
     GrGLint texUnitIdx = 0;
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index a520bc2..3238a75 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -52,11 +52,6 @@
      */
     void abandon();
 
-    /**
-     * The shader may modify the blend coefficients. Params are in/out.
-     */
-    void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const;
-
     const GrGLProgramDesc& getDesc() { return fDesc; }
 
     /**
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index c0a9e13..5339937 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -343,51 +343,14 @@
         header->fCoverageAttributeIndex = -1;
     }
 
-    // Here we deal with whether/how we handle color and coverage separately.
-
-    // Set this default and then possibly change our mind if there is coverage.
-    header->fCoverageOutput = kModulate_CoverageOutput;
-
-    // If we do have coverage determine whether it matters.
-    bool separateCoverageFromColor = optState.hasGeometryProcessor();
-    if (!optState.isCoverageDrawing() &&
-        (optState.numCoverageStages() > 0 ||
-         optState.hasGeometryProcessor() ||
-         requiresCoverageAttrib)) {
-
-        if (gpu->caps()->dualSourceBlendingSupport()) {
-            if (kZero_GrBlendCoeff == dstCoeff) {
-                // write the coverage value to second color
-                header->fCoverageOutput =  kSecondaryCoverage_CoverageOutput;
-                separateCoverageFromColor = true;
-            } else if (kSA_GrBlendCoeff == dstCoeff) {
-                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
-                header->fCoverageOutput = kSecondaryCoverageISA_CoverageOutput;
-                separateCoverageFromColor = true;
-            } else if (kSC_GrBlendCoeff == dstCoeff) {
-                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
-                header->fCoverageOutput = kSecondaryCoverageISC_CoverageOutput;
-                separateCoverageFromColor = true;
-            }
-        } else if (optState.readsDst() &&
-                   kOne_GrBlendCoeff == srcCoeff &&
-                   kZero_GrBlendCoeff == dstCoeff) {
-            header->fCoverageOutput = kCombineWithDst_CoverageOutput;
-            separateCoverageFromColor = true;
-        }
-    }
+    header->fPrimaryOutputType = optState.getPrimaryOutputType();
+    header->fSecondaryOutputType = optState.getSecondaryOutputType();
 
     for (int s = 0; s < optState.numColorStages(); ++s) {
         colorStages->push_back(&optState.getColorStage(s));
     }
-    SkTArray<const GrEffectStage*, true>* array;
-    if (separateCoverageFromColor) {
-        array = coverageStages;
-    } else {
-        array = colorStages;
-    }
     for (int s = 0; s < optState.numCoverageStages(); ++s) {
-        array->push_back(&optState.getCoverageStage(s));
+        coverageStages->push_back(&optState.getCoverageStage(s));
     }
 
     header->fColorEffectCnt = colorStages->count();
diff --git a/src/gpu/gl/GrGLProgramDesc.h b/src/gpu/gl/GrGLProgramDesc.h
index c9bdac5..172a202 100644
--- a/src/gpu/gl/GrGLProgramDesc.h
+++ b/src/gpu/gl/GrGLProgramDesc.h
@@ -11,6 +11,7 @@
 #include "GrGLEffect.h"
 #include "GrDrawState.h"
 #include "GrGpu.h"
+#include "GrOptDrawState.h"
 
 class GrGpuGL;
 
@@ -111,50 +112,24 @@
         kColorInputCnt
     };
 
-    enum CoverageOutput {
-        // modulate color and coverage, write result as the color output.
-        kModulate_CoverageOutput,
-        // Writes color*coverage as the primary color output and also writes coverage as the
-        // secondary output. Only set if dual source blending is supported.
-        kSecondaryCoverage_CoverageOutput,
-        // Writes color*coverage as the primary color output and also writes coverage * (1 - colorA)
-        // as the secondary output. Only set if dual source blending is supported.
-        kSecondaryCoverageISA_CoverageOutput,
-        // Writes color*coverage as the primary color output and also writes coverage *
-        // (1 - colorRGB) as the secondary output. Only set if dual source blending is supported.
-        kSecondaryCoverageISC_CoverageOutput,
-        // Combines the coverage, dst, and color as coverage * color + (1 - coverage) * dst. This
-        // can only be set if fDstReadKey is non-zero.
-        kCombineWithDst_CoverageOutput,
-
-        kCoverageOutputCnt
-    };
-
-    static bool CoverageOutputUsesSecondaryOutput(CoverageOutput co) {
-        switch (co) {
-            case kSecondaryCoverage_CoverageOutput: //  fallthru
-            case kSecondaryCoverageISA_CoverageOutput:
-            case kSecondaryCoverageISC_CoverageOutput:
-                return true;
-            default:
-                return false;
-        }
-    }
-
     struct KeyHeader {
-        uint8_t                     fDstReadKey;        // set by GrGLShaderBuilder if there
+        uint8_t                          fDstReadKey;   // set by GrGLShaderBuilder if there
                                                         // are effects that must read the dst.
                                                         // Otherwise, 0.
-        uint8_t                     fFragPosKey;        // set by GrGLShaderBuilder if there are
+        uint8_t                          fFragPosKey;   // set by GrGLShaderBuilder if there are
                                                         // effects that read the fragment position.
                                                         // Otherwise, 0.
-        ColorInput                  fColorInput : 8;
-        ColorInput                  fCoverageInput : 8;
-        CoverageOutput              fCoverageOutput : 8;
 
         SkBool8                     fUseFragShaderOnly;
         SkBool8                     fEmitsPointSize;
 
+        ColorInput                       fColorInput : 8;
+        ColorInput                       fCoverageInput : 8;
+
+        GrOptDrawState::PrimaryOutputType    fPrimaryOutputType : 8;
+        GrOptDrawState::SecondaryOutputType  fSecondaryOutputType : 8;
+
+
         // To enable experimental geometry shader code (not for use in
         // production)
 #if GR_GL_EXPERIMENTAL_GS
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index 476f174..387741b 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -205,7 +205,7 @@
 #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
 
 bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstCopy) {
-    SkAutoTUnref<GrOptDrawState> optState(this->getDrawState().createOptState());
+    SkAutoTUnref<GrOptDrawState> optState(this->getDrawState().createOptState(*this->caps()));
 
     // GrGpu::setupClipAndFlushState should have already checked this and bailed if not true.
     SkASSERT(optState->getRenderTarget());
@@ -262,7 +262,6 @@
             fHWProgramID = programID;
         }
 
-        fCurrentProgram->overrideBlend(&srcCoeff, &dstCoeff);
         this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff);
 
         fCurrentProgram->setData(*optState.get(),
@@ -293,7 +292,7 @@
 }
 
 void GrGpuGL::setupGeometry(const DrawInfo& info, size_t* indexOffsetInBytes) {
-    SkAutoTUnref<GrOptDrawState> optState(this->getDrawState().createOptState());
+    SkAutoTUnref<GrOptDrawState> optState(this->getDrawState().createOptState(*this->caps()));
 
     GrGLsizei stride = static_cast<GrGLsizei>(optState->getVertexStride());
 
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
index 730ce04..7279f1c 100644
--- a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
@@ -304,18 +304,22 @@
 
     ///////////////////////////////////////////////////////////////////////////
     // write the secondary color output if necessary
-    if (GrGLProgramDesc::CoverageOutputUsesSecondaryOutput(header.fCoverageOutput)) {
+    if (GrOptDrawState::kNone_SecondaryOutputType != header.fSecondaryOutputType) {
         const char* secondaryOutputName = this->enableSecondaryOutput();
-
-        // default coeff to ones for kCoverage_DualSrcOutput
         GrGLSLExpr4 coeff(1);
-        if (GrGLProgramDesc::kSecondaryCoverageISA_CoverageOutput == header.fCoverageOutput) {
-            // Get (1-A) into coeff
-            coeff = GrGLSLExpr4::VectorCast(GrGLSLExpr1(1) - inputColor.a());
-        } else if (GrGLProgramDesc::kSecondaryCoverageISC_CoverageOutput ==
-                   header.fCoverageOutput){
-            // Get (1-RGBA) into coeff
-            coeff = GrGLSLExpr4(1) - inputColor;
+        switch (header.fSecondaryOutputType) {
+            case GrOptDrawState::kCoverage_SecondaryOutputType:
+                break;
+            case GrOptDrawState::kCoverageISA_SecondaryOutputType:
+                // Get (1-A) into coeff
+                coeff = GrGLSLExpr4::VectorCast(GrGLSLExpr1(1) - inputColor.a());
+                break;
+            case GrOptDrawState::kCoverageISC_SecondaryOutputType:
+                // Get (1-RGBA) into coeff
+                coeff = GrGLSLExpr4(1) - inputColor;
+                break;
+            default:
+                SkFAIL("Unexpected Secondary Output");
         }
         // Get coeff * coverage into modulate and then write that to the dual source output.
         codeAppendf("\t%s = %s;\n", secondaryOutputName, (coeff * inputCoverage).c_str());
@@ -326,13 +330,19 @@
 
     // Get "color * coverage" into fragColor
     GrGLSLExpr4 fragColor = inputColor * inputCoverage;
-    // Now tack on "+(1-coverage)dst onto the frag color if we were asked to do so.
-    if (GrGLProgramDesc::kCombineWithDst_CoverageOutput == header.fCoverageOutput) {
-        GrGLSLExpr4 dstCoeff = GrGLSLExpr4(1) - inputCoverage;
-
-        GrGLSLExpr4 dstContribution = dstCoeff * GrGLSLExpr4(dstColor());
-
-        fragColor = fragColor + dstContribution;
+    switch (header.fPrimaryOutputType) {
+        case GrOptDrawState::kModulate_PrimaryOutputType:
+            break;
+        case GrOptDrawState::kCombineWithDst_PrimaryOutputType:
+            {
+                // Tack on "+(1-coverage)dst onto the frag color.
+                GrGLSLExpr4 dstCoeff = GrGLSLExpr4(1) - inputCoverage;
+                GrGLSLExpr4 dstContribution = dstCoeff * GrGLSLExpr4(dstColor());
+                fragColor = fragColor + dstContribution;
+            }
+            break;
+        default:
+            SkFAIL("Unknown Primary Output");
     }
     codeAppendf("\t%s = %s;\n", this->getColorOutputName(), fragColor.c_str());
 }
diff --git a/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp b/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
index ad61465..f99791e 100644
--- a/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
@@ -87,7 +87,9 @@
     // We pull the current state of attributes off of drawstate's optimized state and bind them in
     // order. This assumes that the drawState has not changed since we called flushGraphicsState()
     // higher up in the stack.
-    SkAutoTUnref<GrOptDrawState> optState(fProgramBuilder->gpu()->drawState()->createOptState());
+    const GrDrawTargetCaps* caps = fProgramBuilder->gpu()->caps();
+    const GrDrawState& drawState = *fProgramBuilder->gpu()->drawState();
+    SkAutoTUnref<GrOptDrawState> optState(drawState.createOptState(*caps));
     const GrVertexAttrib* vaPtr = optState->getVertexAttribs();
     const int vaCount = optState->getVertexAttribCount();