separate coverage stages from color stages.
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 16 May 2011 20:56:06 +0000 (20:56 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 16 May 2011 20:56:06 +0000 (20:56 +0000)
Review URL: http://codereview.appspot.com/4538064/

git-svn-id: http://skia.googlecode.com/svn/trunk@1339 2bbb7eff-a529-9590-31e7-b0007b416f81

gpu/include/GrDrawTarget.h
gpu/src/GrGLProgram.cpp
gpu/src/GrGLProgram.h
gpu/src/GrGpuGLShaders.cpp
include/core/SkXfermode.h

index 93b381d..5020e16 100644 (file)
@@ -137,10 +137,16 @@ protected:
             // all DrState members should default to something
             // valid by the memset
             memset(this, 0, sizeof(DrState));
-            // This is an exception to our memset, since it will
-            // result in no change.
+            
+            // memset exceptions
             fColorFilterXfermode = SkXfermode::kDstIn_Mode;
+            fFirstCoverageStage = kNumStages;
+
+            // pedantic assertion that our ptrs will
+            // be NULL (0 ptr is mem addr 0)
             GrAssert((intptr_t)(void*)NULL == 0LL);
+
+            // default stencil setting should be disabled
             GrAssert(fStencilSettings.isDisabled());
         }
         uint32_t                fFlagBits;
@@ -150,6 +156,7 @@ protected:
         GrTexture*              fTextures[kNumStages];
         GrEffect*               fEffects[kNumStages];
         GrSamplerState          fSamplerStates[kNumStages];
+        int                     fFirstCoverageStage;
         GrRenderTarget*         fRenderTarget;
         GrColor                 fColor;
         DrawFace                fDrawFace;
@@ -343,6 +350,26 @@ public:
     void setDrawFace(DrawFace face) { fCurrDrawState.fDrawFace = face; }
 
     /**
+     * A common pattern is to compute a color with the initial stages and then
+     * modulate that color by a coverage value in later stage(s) (AA, mask-
+     * filters, glyph mask, etc). Color-filters, xfermodes, etc should be 
+     * computed based on the pre-coverage-modulated color. The division of 
+     * stages between color-computing and coverage-computing is specified by 
+     * this method. Initially this is kNumStages (all stages are color-
+     * computing).
+     */
+    void setFirstCoverageStage(int firstCoverageStage) { 
+        fCurrDrawState.fFirstCoverageStage = firstCoverageStage; 
+    }
+
+    /**
+     * Gets the index of the first coverage-computing stage.
+     */
+    int getFirstCoverageStage() const { 
+        return fCurrDrawState.fFirstCoverageStage; 
+    }
+
+    /**
      * Gets whether the target is drawing clockwise, counterclockwise,
      * or both faces.
      * @return the current draw face(s).
index ab327d0..bd95f3c 100644 (file)
@@ -86,6 +86,20 @@ static inline const char* vector_all_coords(int count) {
     return ALL[count];
 }
 
+static inline const char* all_ones_vec(int count) {
+    static const char* ONESVEC[] = {"ERROR", "1.0", "vec2(1,1)", 
+                                    "vec3(1,1,1)", "vec4(1,1,1,1)"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ONESVEC));
+    return ONESVEC[count];
+}
+
+static inline const char* all_zeros_vec(int count) {
+    static const char* ZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)", 
+                                    "vec3(0,0,0)", "vec4(0,0,0,0)"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ZEROSVEC));
+    return ZEROSVEC[count];
+}
+
 static void tex_matrix_name(int stage, GrStringBuilder* s) {
 #if GR_GL_ATTRIBUTE_MATRICES
     *s = "aTexM";
@@ -183,55 +197,157 @@ void GrGLProgram::doGLPost() const {
     }
 }
 
+// assigns modulation of two vars to an output var
+// if either var is "" then assign to the other var
+// if both are "" then assign all ones
+static inline void modulate_helper(const char* outputVar,
+                                   const char* var0,
+                                   const char* var1,
+                                   GrStringBuilder* code) {
+    GrAssert(NULL != outputVar);
+    GrAssert(NULL != var0);
+    GrAssert(NULL != var1);
+    GrAssert(NULL != code);
+
+    bool has0 = '\0' != *var0;
+    bool has1 = '\0' != *var1;
+
+    if (!has0 && !has1) {
+        code->appendf("\t%s = %s;\n", outputVar, all_ones_vec(4));
+    } else if (!has0) {
+        code->appendf("\t%s = %s;\n", outputVar, var1);
+    } else if (!has1) {
+        code->appendf("\t%s = %s;\n", outputVar, var0);
+    } else {
+        code->appendf("\t%s = %s * %s;\n", outputVar, var0, var1);
+    }
+}
+
+// assigns addition of two vars to an output var
+// if either var is "" then assign to the other var
+// if both are "" then assign all zeros
+static inline void add_helper(const char* outputVar,
+                              const char* var0,
+                              const char* var1,
+                              GrStringBuilder* code) {
+    GrAssert(NULL != outputVar);
+    GrAssert(NULL != var0);
+    GrAssert(NULL != var1);
+    GrAssert(NULL != code);
+
+    bool has0 = '\0' != *var0;
+    bool has1 = '\0' != *var1;
+
+    if (!has0 && !has1) {
+        code->appendf("\t%s = %s;\n", outputVar, all_zeros_vec(4));
+    } else if (!has0) {
+        code->appendf("\t%s = %s;\n", outputVar, var1);
+    } else if (!has1) {
+        code->appendf("\t%s = %s;\n", outputVar, var0);
+    } else {
+        code->appendf("\t%s = %s + %s;\n", outputVar, var0, var1);
+    }
+}
+
+// given two blend coeffecients determine whether the src
+// and/or dst computation can be omitted.
+static inline void needBlendInputs(SkXfermode::Coeff srcCoeff,
+                                   SkXfermode::Coeff dstCoeff,
+                                   bool* needSrcValue,
+                                   bool* needDstValue) {
+    if (SkXfermode::kZero_Coeff == srcCoeff) {
+        switch (dstCoeff) {
+            // these all read the src
+            case SkXfermode::kSC_Coeff:
+            case SkXfermode::kISC_Coeff:
+            case SkXfermode::kSA_Coeff:
+            case SkXfermode::kISA_Coeff:
+                *needSrcValue = true;
+                break;
+            default:
+                *needSrcValue = false;
+                break;
+        }
+    } else {
+        *needSrcValue = true;
+    }
+    if (SkXfermode::kZero_Coeff == dstCoeff) {
+        switch (srcCoeff) {
+            // these all read the dst
+            case SkXfermode::kDC_Coeff:
+            case SkXfermode::kIDC_Coeff:
+            case SkXfermode::kDA_Coeff:
+            case SkXfermode::kIDA_Coeff:
+                *needDstValue = true;
+                break;
+            default:
+                *needDstValue = false;
+                break;
+        }
+    } else {
+        *needDstValue = true;
+    }
+}
+
 /**
- * Create a text coefficient to be used in fragment shader code.
+ * Create a blend_coeff * value string to be used in shader code. Sets empty
+ * string if result is trivially zero.
  */
-static void coefficientString(GrStringBuilder* str, SkXfermode::Coeff coeff,
-            const char* src, const char* dst) {
+static void blendTermString(GrStringBuilder* str, SkXfermode::Coeff coeff,
+                             const char* src, const char* dst,
+                             const char* value) {
+                       
     switch (coeff) {
     case SkXfermode::kZero_Coeff:    /** 0 */
-        *str = "0.0";
+        *str = "";
         break;
     case SkXfermode::kOne_Coeff:     /** 1 */
-        *str = "1.0";
+        *str = value;
+        break;
+    case SkXfermode::kSC_Coeff:
+        str->printf("(%s * %s)", src, value);
+        break;
+    case SkXfermode::kISC_Coeff:
+        str->printf("((%s - %s) * %s)", all_ones_vec(4), src, value);
+        break;
+    case SkXfermode::kDC_Coeff:
+        str->printf("(%s * %s)", dst, value);
+        break;
+    case SkXfermode::kIDC_Coeff:
+        str->printf("((%s - %s) * %s)", all_ones_vec(4), dst, value);
         break;
     case SkXfermode::kSA_Coeff:      /** src alpha */
-        str->appendf("%s.a", src);
+        str->printf("(%s.a * %s)", src, value);
         break;
     case SkXfermode::kISA_Coeff:     /** inverse src alpha (i.e. 1 - sa) */
-        str->appendf("(1.0 - %s.a)", src);
+        str->printf("((1.0 - %s.a) * %s)", src, value);
         break;
     case SkXfermode::kDA_Coeff:      /** dst alpha */
-        str->appendf("%s.a", dst);
+        str->printf("(%s.a * %s)", dst, value);
         break;
     case SkXfermode::kIDA_Coeff:     /** inverse dst alpha (i.e. 1 - da) */
-        str->appendf("(1.0 - %s.a)", dst);
-        break;
-    case SkXfermode::kSC_Coeff:
-        str->append(src);
+        str->printf("((1.0 - %s.a) * %s)", dst, value);
         break;
     default:
+        GrCrash("Unexpected xfer coeff.");
         break;
     }
 }
-
 /**
  * Adds a line to the fragment shader code which modifies the color by
  * the specified color filter.
  */
-static void addColorFilter(GrStringBuilder* FSCode, const char * outputVar,
-            SkXfermode::Mode colorFilterXfermode, const char* dstColor) {
-    SkXfermode::Coeff srcCoeff, dstCoeff;
-    SkDEBUGCODE(bool success =)
-    SkXfermode::ModeAsCoeff(colorFilterXfermode, &srcCoeff, &dstCoeff);
-    // We currently do not handle modes that cannot be represented as
-    // coefficients.
-    GrAssert(success);
-    GrStringBuilder srcCoeffStr, dstCoeffStr;
-    coefficientString(&srcCoeffStr, srcCoeff, COL_FILTER_UNI_NAME, dstColor);
-    coefficientString(&dstCoeffStr, dstCoeff, COL_FILTER_UNI_NAME, dstColor);
-    FSCode->appendf("\t%s = %s*%s + %s*%s;\n", outputVar, srcCoeffStr.c_str(),
-            COL_FILTER_UNI_NAME, dstCoeffStr.c_str(), dstColor);
+static void addColorFilter(GrStringBuilder* fsCode, const char * outputVar,
+                           SkXfermode::Coeff uniformCoeff, 
+                           SkXfermode::Coeff colorCoeff,
+                           const char* inColor) {
+    GrStringBuilder colorStr, constStr;
+    blendTermString(&colorStr, colorCoeff, COL_FILTER_UNI_NAME,
+                    inColor, inColor);
+    blendTermString(&constStr, uniformCoeff, COL_FILTER_UNI_NAME,
+                    inColor, COL_FILTER_UNI_NAME);
+
+    add_helper(outputVar, colorStr.c_str(), constStr.c_str(), fsCode);
 }
 
 bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
@@ -241,6 +357,22 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
 
     programData->fUniLocations.reset();
 
+    SkXfermode::Coeff colorCoeff, uniformCoeff;
+    // The rest of transfer mode color filters have not been implemented
+    if (fProgramDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
+        GR_DEBUGCODE(bool success =)
+            SkXfermode::ModeAsCoeff(fProgramDesc.fColorFilterXfermode, &uniformCoeff, &colorCoeff);
+        GR_DEBUGASSERT(success);
+    } else {
+        colorCoeff = SkXfermode::kOne_Coeff;
+        uniformCoeff = SkXfermode::kZero_Coeff;
+    }
+
+    bool needColorFilterUniform;
+    bool needComputedColor;
+    needBlendInputs(uniformCoeff, colorCoeff,
+                    &needColorFilterUniform, &needComputedColor);
+
 #if GR_GL_ATTRIBUTE_MATRICES
     segments.fVSAttrs += "attribute mat3 " VIEW_MATRIX_NAME ";\n";
     programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
@@ -258,21 +390,23 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
     // incoming color to current stage being processed.
     GrStringBuilder inColor;
 
-    switch (fProgramDesc.fColorType) {
-    case ProgramDesc::kAttribute_ColorType:
-        segments.fVSAttrs.append( "attribute vec4 " COL_ATTR_NAME ";\n");
-        segments.fVaryings.append("varying vec4 vColor;\n");
-        segments.fVSCode.append(    "\tvColor = " COL_ATTR_NAME ";\n");
-        inColor = "vColor";
-        break;
-    case ProgramDesc::kUniform_ColorType:
-        segments.fFSUnis.append(  "uniform vec4 " COL_UNI_NAME ";\n");
-        programData->fUniLocations.fColorUni = kUseUniform;
-        inColor = COL_UNI_NAME;
-        break;
-    case ProgramDesc::kNone_ColorType:
-        inColor = "";
-        break;
+    if (needComputedColor) {
+        switch (fProgramDesc.fColorType) {
+            case ProgramDesc::kAttribute_ColorType:
+                segments.fVSAttrs.append( "attribute vec4 " COL_ATTR_NAME ";\n");
+                segments.fVaryings.append("varying vec4 vColor;\n");
+                segments.fVSCode.append(    "\tvColor = " COL_ATTR_NAME ";\n");
+                inColor = "vColor";
+                break;
+            case ProgramDesc::kUniform_ColorType:
+                segments.fFSUnis.append(  "uniform vec4 " COL_UNI_NAME ";\n");
+                programData->fUniLocations.fColorUni = kUseUniform;
+                inColor = COL_UNI_NAME;
+                break;
+            default:
+                GrAssert(ProgramDesc::kNone_ColorType == fProgramDesc.fColorType);
+                break;
+        }
     }
 
     if (fProgramDesc.fUsesEdgeAA) {
@@ -294,74 +428,82 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
         }
     }
 
-    bool useColorFilter = 
-            // The rest of transfer mode color filters have not been implemented
-            fProgramDesc.fColorFilterXfermode <= SkXfermode::kMultiply_Mode
-            // This mode has no effect.
-            && fProgramDesc.fColorFilterXfermode != SkXfermode::kDst_Mode;
-    bool onlyUseColorFilter = useColorFilter
-            && (fProgramDesc.fColorFilterXfermode == SkXfermode::kClear_Mode
-            || fProgramDesc.fColorFilterXfermode == SkXfermode::kSrc_Mode);
-    if (useColorFilter) {
-        // Set up a uniform for the color
-        segments.fFSUnis.append(  "uniform vec4 " COL_FILTER_UNI_NAME ";\n");
-        programData->fUniLocations.fColorFilterUni = kUseUniform;
-    }
-
-    // for each enabled stage figure out what the input coordinates are
-    // and count the number of stages in use.
-    const char* stageInCoords[GrDrawTarget::kNumStages];
-    int numActiveStages = 0;
+    ///////////////////////////////////////////////////////////////////////////
+    // compute the final color
 
-    if (!onlyUseColorFilter) {
-        for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
+    // if we have color stages string them together, feeding the output color
+    // of each to the next and generating code for each stage.
+    if (needComputedColor) {
+        GrStringBuilder outColor;
+        for (int s = 0; s < fProgramDesc.fFirstCoverageStage; ++s) {
             if (fProgramDesc.fStages[s].fEnabled) {
-                if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) {
-                    stageInCoords[s] = POS_ATTR_NAME;
+                // create var to hold stage result
+                outColor = "color";
+                outColor.appendS32(s);
+                segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
+
+                const char* inCoords;
+                // figure out what our input coords are
+                if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) &
+                    layout) {
+                    inCoords = POS_ATTR_NAME;
                 } else {
                     int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
                      // we better have input tex coordinates if stage is enabled.
                     GrAssert(tcIdx >= 0);
                     GrAssert(texCoordAttrs[tcIdx].size());
-                    stageInCoords[s] = texCoordAttrs[tcIdx].c_str();
-                }
-                ++numActiveStages;
-            }
-        }
-    }
-
-    // if we have active stages string them together, feeding the output color
-    // of each to the next and generating code for each stage.
-    if (numActiveStages) {
-        int currActiveStage = 0;
-        GrStringBuilder outColor;
-        for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
-            if (fProgramDesc.fStages[s].fEnabled) {
-                if (currActiveStage < (numActiveStages - 1) || useColorFilter) {
-                    outColor = "color";
-                    outColor.appendS32(currActiveStage);
-                    segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
-                } else {
-                    outColor = "gl_FragColor";
+                    inCoords = texCoordAttrs[tcIdx].c_str();
                 }
 
                 genStageCode(s,
                              fProgramDesc.fStages[s],
                              inColor.size() ? inColor.c_str() : NULL,
                              outColor.c_str(),
-                             stageInCoords[s],
+                             inCoords,
                              &segments,
                              &programData->fUniLocations.fStages[s]);
-                ++currActiveStage;
                 inColor = outColor;
             }
         }
-        if (useColorFilter) {
-            addColorFilter(&segments.fFSCode, "gl_FragColor",
-                    fProgramDesc.fColorFilterXfermode, outColor.c_str());
-        }
+    }
 
-    } else {
+    // if have all ones for the "dst" input to the color filter then we can make
+    // additional optimizations.
+    if (needColorFilterUniform && !inColor.size() && 
+        (SkXfermode::kIDC_Coeff == uniformCoeff ||
+         SkXfermode::kIDA_Coeff == uniformCoeff)) {
+          uniformCoeff = SkXfermode::kZero_Coeff;
+          bool bogus;
+          needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff, 
+                          &needColorFilterUniform, &bogus);
+    }
+    if (needColorFilterUniform) {
+        segments.fFSUnis.append(  "uniform vec4 " COL_FILTER_UNI_NAME ";\n");
+        programData->fUniLocations.fColorFilterUni = kUseUniform;
+    }
+
+    bool wroteFragColorZero = false;
+    if (SkXfermode::kZero_Coeff == uniformCoeff && 
+        SkXfermode::kZero_Coeff == colorCoeff) {
+        segments.fFSCode.appendf("\tgl_FragColor = %s;\n", all_zeros_vec(4));
+        wroteFragColorZero = true;
+    } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) {
+        segments.fFSCode.appendf("\tvec4 filteredColor;\n");
+        const char* color = inColor.size() ? inColor.c_str() : all_ones_vec(4);
+        addColorFilter(&segments.fFSCode, "filteredColor", uniformCoeff, 
+                       colorCoeff, color);
+        inColor = "filteredColor";
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // compute the partial coverage (coverage stages and edge aa)
+
+    GrStringBuilder inCoverage;
+    bool coverageIsScalar = false;
+
+    // we will want to compute coverage for some blend when there is no
+    // color (when dual source blending is enabled). But for now we have this if
+    if (!wroteFragColorZero) {
         if (fProgramDesc.fUsesEdgeAA) {
             // FIXME:  put the a's in a loop
             segments.fFSCode.append(
@@ -373,25 +515,68 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
                 "\tfloat a4 = clamp(dot(uEdges[4], pos), 0.0, 1.0);\n"
                 "\tfloat a5 = clamp(dot(uEdges[5], pos), 0.0, 1.0);\n"
                 "\tfloat edgeAlpha = min(min(a0 * a1, a2 * a3), a4 * a5);\n");
-            if (inColor.size()) {
-                inColor.append(" * edgeAlpha");
-            } else {
-                inColor = "vec4(edgeAlpha)";
+            inCoverage = "edgeAlpha";
+            coverageIsScalar = true;
+        }
+
+        GrStringBuilder outCoverage;
+        const int& startStage = fProgramDesc.fFirstCoverageStage;
+        for (int s = startStage; s < GrDrawTarget::kNumStages; ++s) {
+            if (fProgramDesc.fStages[s].fEnabled) {
+
+                // create var to hold stage output
+                outCoverage = "coverage";
+                outCoverage.appendS32(s);
+                segments.fFSCode.appendf("\tvec4 %s;\n", outCoverage.c_str());
+
+                const char* inCoords;
+                // figure out what our input coords are
+                if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) {
+                    inCoords = POS_ATTR_NAME;
+                } else {
+                    int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
+                        // we better have input tex coordinates if stage is enabled.
+                    GrAssert(tcIdx >= 0);
+                    GrAssert(texCoordAttrs[tcIdx].size());
+                    inCoords = texCoordAttrs[tcIdx].c_str();
+                }
+
+                genStageCode(s,
+                             fProgramDesc.fStages[s],
+                             inCoverage.size() ? inCoverage.c_str() : NULL,
+                             outCoverage.c_str(),
+                             inCoords,
+                             &segments,
+                             &programData->fUniLocations.fStages[s]);
+                inCoverage = outCoverage;
+                coverageIsScalar = false;
             }
         }
-        // we may not have any incoming color
-        const char * incomingColor = (inColor.size() ? inColor.c_str()
-                : "vec4(1,1,1,1)");
-        if (useColorFilter) {
-            addColorFilter(&segments.fFSCode, "gl_FragColor",
-                    fProgramDesc.fColorFilterXfermode, incomingColor);
-        } else {
-            segments.fFSCode.appendf("\tgl_FragColor = %s;\n", incomingColor);
+    }
+
+    // TODO: ADD dual source blend output based on coverage here
+
+    ///////////////////////////////////////////////////////////////////////////
+    // combine color and coverage as frag color
+
+    if (!wroteFragColorZero) {
+        if (coverageIsScalar && !inColor.size()) {
+            GrStringBuilder oldCoverage = inCoverage;
+            inCoverage.swap(oldCoverage);
+            inCoverage.printf("vec4(%s,%s,%s,%s)", oldCoverage.c_str(), 
+                              oldCoverage.c_str(), oldCoverage.c_str(), 
+                              oldCoverage.c_str());
         }
+        modulate_helper("gl_FragColor", inColor.c_str(),
+                        inCoverage.c_str(), &segments.fFSCode);
     }
+
     segments.fVSCode.append("}\n");
     segments.fFSCode.append("}\n");
 
+    ///////////////////////////////////////////////////////////////////////////
+    // compile and setup attribs and unis
+
     if (!CompileFSAndVS(segments, programData)) {
         return false;
     }
@@ -565,7 +750,6 @@ bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
         }
     }
 
-
     GR_GL(BindAttribLocation(progID, ColorAttributeIdx(), COL_ATTR_NAME));
 
     GR_GL(LinkProgram(progID));
@@ -610,7 +794,7 @@ void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const
         GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
     }
 
-    if (fProgramDesc.fUsesEdgeAA) {
+    if (kUseUniform == programData->fUniLocations.fEdgesUni) {
         programData->fUniLocations.fEdgesUni = 
             GR_GL(GetUniformLocation(progID, EDGES_UNI_NAME));
         GrAssert(kUnusedUniform != programData->fUniLocations.fEdgesUni);
index 74078e3..64d7088 100644 (file)
@@ -99,6 +99,7 @@ private:
             kUniform_ColorType      = 2,
         } fColorType;
 
+        int  fFirstCoverageStage;
         bool fEmitsPointSize;
         bool fUsesEdgeAA;
 
index 125b820..bcf29ae 100644 (file)
@@ -169,6 +169,14 @@ void GrGpuGLShaders::ProgramUnitTest() {
     GrRandom random;
     for (int t = 0; t < NUM_TESTS; ++t) {
 
+#if 0
+        GrPrintf("\nTest Program %d\n-------------\n", t);
+        static const int stop = -1;
+        if (t == stop) {
+            int breakpointhere = 9;
+        }
+#endif
+
         pdesc.fVertexLayout = 0;
         pdesc.fEmitsPointSize = random.nextF() > .5f;
         float colorType = random.nextF();
@@ -179,6 +187,15 @@ void GrGpuGLShaders::ProgramUnitTest() {
         } else {
             pdesc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType;
         }
+
+        int idx = (int)(random.nextF() * (SkXfermode::kCoeffModesCnt));
+        pdesc.fColorFilterXfermode = (SkXfermode::Mode)idx;
+
+        idx = (int)(random.nextF() * (kNumStages+1));
+        pdesc.fFirstCoverageStage = idx;
+
+        pdesc.fUsesEdgeAA = (random.nextF() > .5f);
+
         for (int s = 0; s < kNumStages; ++s) {
             // enable the stage?
             if (random.nextF() > .5f) {
@@ -194,19 +211,15 @@ void GrGpuGLShaders::ProgramUnitTest() {
             if (random.nextF() > .5f) {
                 pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit;
             }
-        }
-
-        for (int s = 0; s < kNumStages; ++s) {
-            int x;
             pdesc.fStages[s].fEnabled = VertexUsesStage(s, pdesc.fVertexLayout);
-            x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS));
-            pdesc.fStages[s].fOptFlags = STAGE_OPTS[x];
-            x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_MODULATES));
-            pdesc.fStages[s].fModulation = STAGE_MODULATES[x];
-            x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_COORD_MAPPINGS));
-            pdesc.fStages[s].fCoordMapping = STAGE_COORD_MAPPINGS[x];
-            x = (int)(random.nextF() * GR_ARRAY_COUNT(FETCH_MODES));
-            pdesc.fStages[s].fFetchMode = FETCH_MODES[x];
+            idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS));
+            pdesc.fStages[s].fOptFlags = STAGE_OPTS[idx];
+            idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_MODULATES));
+            pdesc.fStages[s].fModulation = STAGE_MODULATES[idx];
+            idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_COORD_MAPPINGS));
+            pdesc.fStages[s].fCoordMapping = STAGE_COORD_MAPPINGS[idx];
+            idx = (int)(random.nextF() * GR_ARRAY_COUNT(FETCH_MODES));
+            pdesc.fStages[s].fFetchMode = FETCH_MODES[idx];
         }
         GrGLProgram::CachedData cachedData;
         program.genProgram(&cachedData);
@@ -219,7 +232,6 @@ void GrGpuGLShaders::ProgramUnitTest() {
     }
 }
 
-
 GrGpuGLShaders::GrGpuGLShaders() {
 
     resetContext();
@@ -668,6 +680,18 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
     // existing program in the cache.
     desc.fVertexLayout &= ~(kColor_VertexLayoutBit);
 
+    desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode;
+
+    // coverage vs. color only applies when there is a color filter
+    // (currently)
+    if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
+        desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+    } else {
+        // use canonical value when this won't affect generated
+        // code to prevent duplicate programs.
+        desc.fFirstCoverageStage = kNumStages;
+    }
+
 #if GR_AGGRESSIVE_SHADER_OPTS
     if (!requiresAttributeColors && (0xffffffff == fCurrDrawState.fColor)) {
         desc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType;
@@ -764,7 +788,6 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
             fCurrentProgram.fStageEffects[s] = NULL;
         }
     }
-    desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode;
 }
 
 
index 4d46bb9..6ab9d6d 100644 (file)
@@ -103,11 +103,15 @@ public:
         kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
         kXor_Mode,      //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
 
-        // these modes are defined in the SVG Compositing standard
+        // all remaining modes are defined in the SVG Compositing standard
         // http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/
         kPlus_Mode,
-        kMultiply_Mode,
-        kScreen_Mode,
+        kMultiply_Mode, 
+        
+        // all above modes can be expressed as pair of src/dst Coeffs
+        kCoeffModesCnt, 
+        
+        kScreen_Mode = kCoeffModesCnt,
         kOverlay_Mode,
         kDarken_Mode,
         kLighten_Mode,