Make GrGLProgramDesc's key variable length by compacting the effect key array

R=robertphillips@google.com

Review URL: https://codereview.chromium.org/15252004

git-svn-id: http://skia.googlecode.com/svn/trunk@9239 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index e8812fa..276f9b5 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -12,6 +12,8 @@
 #include "GrGLShaderBuilder.h"
 #include "GrGpuGL.h"
 
+#include "SkChecksum.h"
+
 void GrGLProgramDesc::Build(const GrDrawState& drawState,
                             bool isPoints,
                             GrDrawState::BlendOptFlags blendOpts,
@@ -19,8 +21,8 @@
                             GrBlendCoeff dstCoeff,
                             const GrGpuGL* gpu,
                             const GrDeviceCoordTexture* dstCopy,
+                            const GrEffectStage* stages[],
                             GrGLProgramDesc* desc) {
-
     // This should already have been caught
     GrAssert(!(GrDrawState::kSkipDraw_BlendOptFlag & blendOpts));
 
@@ -34,131 +36,169 @@
     // bindings in use or other descriptor field settings) it should be set
     // to a canonical value to avoid duplicate programs with different keys.
 
-
-    desc->fEmitsPointSize = isPoints;
-
     bool requiresColorAttrib = !skipColor && drawState.hasColorVertexAttribute();
     bool requiresCoverageAttrib = !skipCoverage && drawState.hasCoverageVertexAttribute();
     // we only need the local coords if we're actually going to generate effect code
     bool requiresLocalCoordAttrib = !(skipCoverage  && skipColor) &&
                                     drawState.hasLocalCoordAttribute();
 
-    // fColorInput/fCoverageInput records how colors are specified for the program so we strip the
-    // bits from the bindings to avoid false negatives when searching for an existing program in the
-    // cache.
-
-    desc->fColorFilterXfermode = skipColor ? SkXfermode::kDst_Mode : drawState.getColorFilterMode();
-
-
     bool colorIsTransBlack = SkToBool(blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag);
     bool colorIsSolidWhite = (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) ||
                              (!requiresColorAttrib && 0xffffffff == drawState.getColor());
-    if (colorIsTransBlack) {
-        desc->fColorInput = kTransBlack_ColorInput;
-    } else if (colorIsSolidWhite) {
-        desc->fColorInput = kSolidWhite_ColorInput;
-    } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresColorAttrib) {
-        desc->fColorInput = kUniform_ColorInput;
-    } else {
-        desc->fColorInput = kAttribute_ColorInput;
+
+    // Do an initial loop over the stages to count them. We count the color and coverage effects
+    // separately here. Later we may decide the distinction doesn't matter and will count all
+    // effects as color in desc. Two things will allow simplication of this mess: GrDrawState will
+    // have tight lists of color and coverage stages rather than a fixed size array with NULLS and
+    // the xfermode-color filter will be removed.
+    int colorEffectCnt = 0;
+    int coverageEffectCnt = 0;
+    if (!skipColor) {
+        for (int s = 0; s < drawState.getFirstCoverageStage(); ++s) {
+            if (drawState.isStageEnabled(s)) {
+                stages[colorEffectCnt] = &drawState.getStage(s);
+                ++colorEffectCnt;
+            }
+        }
+    }
+    if (!skipCoverage) {
+        for (int s = drawState.getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) {
+            if (drawState.isStageEnabled(s)) {
+                stages[colorEffectCnt + coverageEffectCnt] = &drawState.getStage(s);
+                ++coverageEffectCnt;
+            }
+        }
     }
 
-    bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverage();
-
-    if (skipCoverage) {
-        desc->fCoverageInput = kTransBlack_ColorInput;
-    } else if (covIsSolidWhite) {
-        desc->fCoverageInput = kSolidWhite_ColorInput;
-    } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresCoverageAttrib) {
-        desc->fCoverageInput = kUniform_ColorInput;
-    } else {
-        desc->fCoverageInput = kAttribute_ColorInput;
+    size_t newKeyLength = KeyLength(colorEffectCnt + coverageEffectCnt);
+    bool allocChanged;
+    desc->fKey.reset(newKeyLength, SkAutoMalloc::kAlloc_OnShrink, &allocChanged);
+    if (allocChanged || !desc->fInitialized) {
+        // make sure any padding in the header is zero if we we haven't used this allocation before.
+        memset(desc->header(), 0, kHeaderSize);
     }
+    // write the key length
+    *desc->atOffset<uint32_t, kLengthOffset>() = newKeyLength;
 
+    KeyHeader* header = desc->header();
+    EffectKey* effectKeys = desc->effectKeys();
+
+    int currEffectKey = 0;
     bool readsDst = false;
     bool readFragPosition = false;
-    int lastEnabledStage = -1;
-
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-
         bool skip = s < drawState.getFirstCoverageStage() ? skipColor : skipCoverage;
         if (!skip && drawState.isStageEnabled(s)) {
-            lastEnabledStage = s;
             const GrEffectRef& effect = *drawState.getStage(s).getEffect();
             const GrBackendEffectFactory& factory = effect->getFactory();
             GrDrawEffect drawEffect(drawState.getStage(s), requiresLocalCoordAttrib);
-            desc->fEffectKeys[s] = factory.glEffectKey(drawEffect, gpu->glCaps());
+            effectKeys[currEffectKey] = factory.glEffectKey(drawEffect, gpu->glCaps());
+            ++currEffectKey;
             if (effect->willReadDstColor()) {
                 readsDst = true;
             }
             if (effect->willReadFragmentPosition()) {
                 readFragPosition = true;
             }
-        } else {
-            desc->fEffectKeys[s] = 0;
         }
     }
 
+    header->fEmitsPointSize = isPoints;
+    header->fColorFilterXfermode = skipColor ? SkXfermode::kDst_Mode : drawState.getColorFilterMode();
+
+    // Currently the experimental GS will only work with triangle prims (and it doesn't do anything
+    // other than pass through values from the VS to the FS anyway).
+#if GR_GL_EXPERIMENTAL_GS
+#if 0
+    header->fExperimentalGS = gpu->caps().geometryShaderSupport();
+#else
+    header->fExperimentalGS = false;
+#endif
+#endif
+    if (colorIsTransBlack) {
+        header->fColorInput = kTransBlack_ColorInput;
+    } else if (colorIsSolidWhite) {
+        header->fColorInput = kSolidWhite_ColorInput;
+    } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresColorAttrib) {
+        header->fColorInput = kUniform_ColorInput;
+    } else {
+        header->fColorInput = kAttribute_ColorInput;
+    }
+
+    bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverage();
+
+    if (skipCoverage) {
+        header->fCoverageInput = kTransBlack_ColorInput;
+    } else if (covIsSolidWhite) {
+        header->fCoverageInput = kSolidWhite_ColorInput;
+    } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresCoverageAttrib) {
+        header->fCoverageInput = kUniform_ColorInput;
+    } else {
+        header->fCoverageInput = kAttribute_ColorInput;
+    }
+
     if (readsDst) {
         GrAssert(NULL != dstCopy || gpu->caps()->dstReadInShaderSupport());
         const GrTexture* dstCopyTexture = NULL;
         if (NULL != dstCopy) {
             dstCopyTexture = dstCopy->texture();
         }
-        desc->fDstReadKey = GrGLShaderBuilder::KeyForDstRead(dstCopyTexture, gpu->glCaps());
-        GrAssert(0 != desc->fDstReadKey);
+        header->fDstReadKey = GrGLShaderBuilder::KeyForDstRead(dstCopyTexture, gpu->glCaps());
+        GrAssert(0 != header->fDstReadKey);
     } else {
-        desc->fDstReadKey = 0;
+        header->fDstReadKey = 0;
     }
 
     if (readFragPosition) {
-        desc->fFragPosKey = GrGLShaderBuilder::KeyForFragmentPosition(drawState.getRenderTarget(),
+        header->fFragPosKey = GrGLShaderBuilder::KeyForFragmentPosition(drawState.getRenderTarget(),
                                                                       gpu->glCaps());
     } else {
-        desc->fFragPosKey = 0;
+        header->fFragPosKey = 0;
     }
 
-    desc->fCoverageOutput = kModulate_CoverageOutput;
-
-    // Currently the experimental GS will only work with triangle prims (and it doesn't do anything
-    // other than pass through values from the VS to the FS anyway).
-#if GR_GL_EXPERIMENTAL_GS
-#if 0
-    desc->fExperimentalGS = gpu->caps().geometryShaderSupport();
-#else
-    desc->fExperimentalGS = false;
-#endif
-#endif
-
-    // We leave this set to kNumStages until we discover that the coverage/color distinction is
-    // material to the generated program. We do this to avoid distinct keys that generate equivalent
-    // programs.
-    desc->fFirstCoverageStage = GrDrawState::kNumStages;
-    // This tracks the actual first coverage stage.
-    int firstCoverageStage = GrDrawState::kNumStages;
-    desc->fDiscardIfZeroCoverage = false; // Enabled below if stenciling and there is coverage.
-    bool hasCoverage = false;
-    // If we're rendering coverage-as-color then it's as though there are no coverage stages.
-    if (!drawState.isCoverageDrawing()) {
-        // We can have coverage either through a stage or coverage vertex attributes.
-        if (drawState.getFirstCoverageStage() <= lastEnabledStage) {
-            firstCoverageStage = drawState.getFirstCoverageStage();
-            hasCoverage = true;
-        } else {
-            hasCoverage = requiresCoverageAttrib;
-        }
+    // Record attribute indices
+    header->fPositionAttributeIndex = drawState.positionAttributeIndex();
+    header->fLocalCoordAttributeIndex = drawState.localCoordAttributeIndex();
+    
+    // For constant color and coverage we need an attribute with an index beyond those already set
+    int availableAttributeIndex = drawState.getVertexAttribCount();
+    if (requiresColorAttrib) {
+        header->fColorAttributeIndex = drawState.colorVertexAttributeIndex();
+    } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fColorInput) {
+        GrAssert(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt);
+        header->fColorAttributeIndex = availableAttributeIndex;
+        availableAttributeIndex++;
+    } else {
+        header->fColorAttributeIndex = -1;
+    }
+    
+    if (requiresCoverageAttrib) {
+        header->fCoverageAttributeIndex = drawState.coverageVertexAttributeIndex();
+    } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fCoverageInput) {
+        GrAssert(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt);
+        header->fCoverageAttributeIndex = availableAttributeIndex;
+    } else {
+        header->fCoverageAttributeIndex = -1;
     }
 
-    if (hasCoverage) {
+    // Here we deal with whether/how we handle color and coverage separately.
+    
+    // Set these defaults and then possibly change our mind if there is coverage.
+    header->fDiscardIfZeroCoverage = false;
+    header->fCoverageOutput = kModulate_CoverageOutput;
+
+    // If we do have coverage determine whether it matters.
+    bool separateCoverageFromColor = false;
+    if (!drawState.isCoverageDrawing() && (coverageEffectCnt > 0 || requiresCoverageAttrib)) {
         // color filter is applied between color/coverage computation
-        if (SkXfermode::kDst_Mode != desc->fColorFilterXfermode) {
-            desc->fFirstCoverageStage = firstCoverageStage;
+        if (SkXfermode::kDst_Mode != header->fColorFilterXfermode) {
+            separateCoverageFromColor = true;
         }
 
         // If we're stenciling then we want to discard samples that have zero coverage
         if (drawState.getStencil().doesWrite()) {
-            desc->fDiscardIfZeroCoverage = true;
-            desc->fFirstCoverageStage = firstCoverageStage;
+            header->fDiscardIfZeroCoverage = true;
+            separateCoverageFromColor = true;
         }
 
         if (gpu->caps()->dualSourceBlendingSupport() &&
@@ -166,46 +206,45 @@
                            GrDrawState::kCoverageAsAlpha_BlendOptFlag))) {
             if (kZero_GrBlendCoeff == dstCoeff) {
                 // write the coverage value to second color
-                desc->fCoverageOutput =  kSecondaryCoverage_CoverageOutput;
-                desc->fFirstCoverageStage = firstCoverageStage;
+                header->fCoverageOutput =  kSecondaryCoverage_CoverageOutput;
+                separateCoverageFromColor = true;
             } else if (kSA_GrBlendCoeff == dstCoeff) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
-                desc->fCoverageOutput = kSecondaryCoverageISA_CoverageOutput;
-                desc->fFirstCoverageStage = firstCoverageStage;
+                header->fCoverageOutput = kSecondaryCoverageISA_CoverageOutput;
+                separateCoverageFromColor = true;
             } else if (kSC_GrBlendCoeff == dstCoeff) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
-                desc->fCoverageOutput = kSecondaryCoverageISC_CoverageOutput;
-                desc->fFirstCoverageStage = firstCoverageStage;
+                header->fCoverageOutput = kSecondaryCoverageISC_CoverageOutput;
+                separateCoverageFromColor = true;
             }
         } else if (readsDst &&
                    kOne_GrBlendCoeff == srcCoeff &&
                    kZero_GrBlendCoeff == dstCoeff) {
-            desc->fCoverageOutput = kCombineWithDst_CoverageOutput;
-            desc->fFirstCoverageStage = firstCoverageStage;
+            header->fCoverageOutput = kCombineWithDst_CoverageOutput;
+            separateCoverageFromColor = true;
         }
     }
-
-    desc->fPositionAttributeIndex = drawState.positionAttributeIndex();
-    desc->fLocalCoordAttributeIndex = drawState.localCoordAttributeIndex();
-
-    // For constant color and coverage we need an attribute with an index beyond those already set
-    int availableAttributeIndex = drawState.getVertexAttribCount();
-    if (requiresColorAttrib) {
-        desc->fColorAttributeIndex = drawState.colorVertexAttributeIndex();
-    } else if (GrGLProgramDesc::kAttribute_ColorInput == desc->fColorInput) {
-        GrAssert(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt);
-        desc->fColorAttributeIndex = availableAttributeIndex;
-        availableAttributeIndex++;
+    if (separateCoverageFromColor) {
+        header->fColorEffectCnt = colorEffectCnt;
+        header->fCoverageEffectCnt = coverageEffectCnt;
     } else {
-        desc->fColorAttributeIndex = -1;
+        header->fColorEffectCnt = colorEffectCnt + coverageEffectCnt;
+        header->fCoverageEffectCnt = 0;
     }
 
-    if (requiresCoverageAttrib) {
-        desc->fCoverageAttributeIndex = drawState.coverageVertexAttributeIndex();
-    } else if (GrGLProgramDesc::kAttribute_ColorInput == desc->fCoverageInput) {
-        GrAssert(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt);
-        desc->fCoverageAttributeIndex = availableAttributeIndex;
-    } else {
-        desc->fCoverageAttributeIndex = -1;
-    }
+    *desc->checksum() = 0;
+    *desc->checksum() = SkChecksum::Compute(reinterpret_cast<uint32_t*>(desc->fKey.get()),
+                                            newKeyLength);
+    desc->fInitialized = true;
 }
+
+GrGLProgramDesc& GrGLProgramDesc::operator= (const GrGLProgramDesc& other) {
+    fInitialized = other.fInitialized;
+    if (fInitialized) {
+        size_t keyLength = other.keyLength();
+        fKey.reset(keyLength);
+        memcpy(fKey.get(), other.fKey.get(), keyLength);
+    }
+    return *this;
+}
+