Perform coverage blend with the dst in the shader when using a dst-reading xfermode.
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 19 Apr 2013 13:14:45 +0000 (13:14 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 19 Apr 2013 13:14:45 +0000 (13:14 +0000)
Review URL: https://codereview.chromium.org/14233006

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

src/gpu/SkGpuDevice.cpp
src/gpu/gl/GrGLProgram.cpp
src/gpu/gl/GrGLProgramDesc.cpp
src/gpu/gl/GrGLProgramDesc.h
tests/GLProgramsTest.cpp

index 356fcac..299cf3d 100644 (file)
@@ -501,10 +501,8 @@ inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev,
     if (SkXfermode::AsNewEffectOrCoeff(mode, dev->context(), &xferEffect, &sm, &dm)) {
         if (NULL != xferEffect) {
             grPaint->colorStage(kXfermodeEffectIdx)->setEffect(xferEffect)->unref();
-            // This may not be the right place to have this logic but we set the GPU blend to
-            // src-over so that fractional coverage will be accounted for correctly.
             sm = SkXfermode::kOne_Coeff;
-            dm = SkXfermode::kISA_Coeff;
+            dm = SkXfermode::kZero_Coeff;
         }
     } else {
         //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)
index 0b338f2..1b4baa4 100644 (file)
@@ -96,24 +96,28 @@ void GrGLProgram::abandon() {
 
 void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
                                 GrBlendCoeff* dstCoeff) const {
-    switch (fDesc.fDualSrcOutput) {
-        case GrGLProgramDesc::kNone_DualSrcOutput:
+    switch (fDesc.fCoverageOutput) {
+        case GrGLProgramDesc::kModulate_CoverageOutput:
             break;
-        // the prog will write a coverage value to the secondary
+        // The prog will write a coverage value to the secondary
         // output and the dst is blended by one minus that value.
-        case GrGLProgramDesc::kCoverage_DualSrcOutput:
-        case GrGLProgramDesc::kCoverageISA_DualSrcOutput:
-        case GrGLProgramDesc::kCoverageISC_DualSrcOutput:
-        *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
-        break;
+        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)
+            GrAssert(kOne_GrBlendCoeff == *srcCoeff && kZero_GrBlendCoeff == *dstCoeff);
+            break;
         default:
-            GrCrash("Unexpected dual source blend output");
+            GrCrash("Unexpected coverage output");
             break;
     }
 }
 
 namespace {
-// given two blend coeffecients determine whether the src
+// given two blend coefficients determine whether the src
 // and/or dst computation can be omitted.
 inline void need_blend_inputs(SkXfermode::Coeff srcCoeff,
                               SkXfermode::Coeff dstCoeff,
@@ -375,6 +379,20 @@ GrGLuint compile_shader(const GrGLContext& gl, GrGLenum type, const SkString& sh
     return compile_shader(gl, type, 1, &str, &length);
 }
 
+void expand_known_value4f(SkString* string, GrSLConstantVec vec) {
+    GrAssert(string->isEmpty() == (vec != kNone_GrSLConstantVec));
+    switch (vec) {
+        case kNone_GrSLConstantVec:
+            break;
+        case kZeros_GrSLConstantVec:
+            *string = GrGLSLZerosVecf(4);
+            break;
+        case kOnes_GrSLConstantVec:
+            *string = GrGLSLOnesVecf(4);
+            break;
+    }
+}
+
 }
 
 // compiles all the shaders from builder and stores the shader IDs
@@ -564,14 +582,16 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) {
         }
     }
 
-    if (GrGLProgramDesc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
+    GrGLProgramDesc::CoverageOutput coverageOutput =
+        static_cast<GrGLProgramDesc::CoverageOutput>(fDesc.fCoverageOutput);
+    if (GrGLProgramDesc::CoverageOutputUsesSecondaryOutput(coverageOutput)) {
         builder.fFSOutputs.push_back().set(kVec4f_GrSLType,
                                            GrGLShaderVar::kOut_TypeModifier,
                                            dual_source_output_name());
         // default coeff to ones for kCoverage_DualSrcOutput
         SkString coeff;
         GrSLConstantVec knownCoeffValue = kOnes_GrSLConstantVec;
-        if (GrGLProgramDesc::kCoverageISA_DualSrcOutput == fDesc.fDualSrcOutput) {
+        if (GrGLProgramDesc::kSecondaryCoverageISA_CoverageOutput == fDesc.fCoverageOutput) {
             // Get (1-A) into coeff
             SkString inColorAlpha;
             GrGLSLGetComponent4f(&inColorAlpha,
@@ -585,7 +605,7 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) {
                                                  kOnes_GrSLConstantVec,
                                                  knownColorValue,
                                                  true);
-        } else if (GrGLProgramDesc::kCoverageISC_DualSrcOutput == fDesc.fDualSrcOutput) {
+        } else if (GrGLProgramDesc::kSecondaryCoverageISC_CoverageOutput == coverageOutput) {
             // Get (1-RGBA) into coeff
             knownCoeffValue = GrGLSLSubtractf<4>(&coeff,
                                                  NULL,
@@ -609,15 +629,42 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) {
     ///////////////////////////////////////////////////////////////////////////
     // combine color and coverage as frag color
 
-    // Get color * coverage into modulate and write that to frag shader's output.
-    SkString modulate;
-    GrGLSLModulatef<4>(&modulate,
-                       inColor.c_str(),
-                       inCoverage.c_str(),
-                       knownColorValue,
-                       knownCoverageValue,
-                       false);
-    builder.fsCodeAppendf("\t%s = %s;\n", colorOutput.getName().c_str(), modulate.c_str());
+    // Get "color * coverage" into fragColor
+    SkString fragColor;
+    GrSLConstantVec knownFragColorValue = GrGLSLModulatef<4>(&fragColor,
+                                                             inColor.c_str(),
+                                                             inCoverage.c_str(),
+                                                             knownColorValue,
+                                                             knownCoverageValue,
+                                                             true);
+    // Now tack on "+(1-coverage)dst onto the frag color if we were asked to do so.
+    if (GrGLProgramDesc::kCombineWithDst_CoverageOutput == coverageOutput) {
+        SkString dstCoeff;
+        GrSLConstantVec knownDstCoeffValue = GrGLSLSubtractf<4>(&dstCoeff,
+                                                                NULL,
+                                                                inCoverage.c_str(),
+                                                                kOnes_GrSLConstantVec,
+                                                                knownCoverageValue,
+                                                                true);
+        SkString dstContribution;
+        GrSLConstantVec knownDstContributionValue = GrGLSLModulatef<4>(&dstContribution,
+                                                                       dstCoeff.c_str(),
+                                                                       builder.dstColor(),
+                                                                       knownDstCoeffValue,
+                                                                       kNone_GrSLConstantVec,
+                                                                       true);
+        SkString oldFragColor = fragColor;
+        fragColor.reset();
+        GrGLSLAddf<4>(&fragColor,
+                      oldFragColor.c_str(),
+                      dstContribution.c_str(),
+                      knownFragColorValue,
+                      knownDstContributionValue,
+                      false);
+    } else {
+        expand_known_value4f(&fragColor, knownFragColorValue);
+    }
+    builder.fsCodeAppendf("\t%s = %s;\n", colorOutput.getName().c_str(), fragColor.c_str());
 
     ///////////////////////////////////////////////////////////////////////////
     // insert GS
index 73846b8..8677179 100644 (file)
@@ -103,7 +103,7 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState,
         desc->fDstRead = 0;
     }
 
-    desc->fDualSrcOutput = kNone_DualSrcOutput;
+    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).
@@ -151,17 +151,22 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState,
                            GrDrawState::kCoverageAsAlpha_BlendOptFlag))) {
             if (kZero_GrBlendCoeff == dstCoeff) {
                 // write the coverage value to second color
-                desc->fDualSrcOutput =  kCoverage_DualSrcOutput;
+                desc->fCoverageOutput =  kSecondaryCoverage_CoverageOutput;
                 desc->fFirstCoverageStage = firstCoverageStage;
             } else if (kSA_GrBlendCoeff == dstCoeff) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
-                desc->fDualSrcOutput = kCoverageISA_DualSrcOutput;
+                desc->fCoverageOutput = kSecondaryCoverageISA_CoverageOutput;
                 desc->fFirstCoverageStage = firstCoverageStage;
             } else if (kSC_GrBlendCoeff == dstCoeff) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
-                desc->fDualSrcOutput = kCoverageISC_DualSrcOutput;
+                desc->fCoverageOutput = kSecondaryCoverageISC_CoverageOutput;
                 desc->fFirstCoverageStage = firstCoverageStage;
             }
+        } else if (readsDst &&
+                   kOne_GrBlendCoeff == drawState.getSrcBlendCoeff() &&
+                   kZero_GrBlendCoeff == drawState.getDstBlendCoeff()) {
+            desc->fCoverageOutput = kCombineWithDst_CoverageOutput;
+            desc->fFirstCoverageStage = firstCoverageStage;
         }
     }
 
index d2167c7..8d7ca8d 100644 (file)
@@ -65,18 +65,37 @@ private:
 
         kColorInputCnt
     };
-    // Dual-src blending makes use of a secondary output color that can be
-    // used as a per-pixel blend coefficient. This controls whether a
-    // secondary source is output and what value it holds.
-    enum DualSrcOutput {
-        kNone_DualSrcOutput,
-        kCoverage_DualSrcOutput,
-        kCoverageISA_DualSrcOutput,
-        kCoverageISC_DualSrcOutput,
-
-        kDualSrcOutputCnt
+
+    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 fDstRead is set.
+        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;
+        }
+    }
+
     /** Non-zero if this stage has an effect */
     GrGLEffect::EffectKey       fEffectKeys[GrDrawState::kNumStages];
 
@@ -95,7 +114,7 @@ private:
 
     uint8_t                     fColorInput;            // casts to enum ColorInput
     uint8_t                     fCoverageInput;         // casts to enum ColorInput
-    uint8_t                     fDualSrcOutput;         // casts to enum DualSrcOutput
+    uint8_t                     fCoverageOutput;        // casts to enum CoverageOutput
 
     int8_t                      fFirstCoverageStage;
     SkBool8                     fEmitsPointSize;
index 0df29aa..d85d8a5 100644 (file)
@@ -54,12 +54,6 @@ void GrGLProgramDesc::setRandom(SkMWCRandom* random,
 
     fDiscardIfZeroCoverage = random->nextBool();
 
-    if (gpu->caps()->dualSourceBlendingSupport()) {
-        fDualSrcOutput = random->nextULessThan(kDualSrcOutputCnt);
-    } else {
-        fDualSrcOutput = kNone_DualSrcOutput;
-    }
-
     bool useLocalCoords = random->nextBool() && currAttribIndex < GrDrawState::kMaxVertexAttribCnt;
     fLocalCoordAttributeIndex = useLocalCoords ? currAttribIndex++ : -1;
 
@@ -78,6 +72,17 @@ void GrGLProgramDesc::setRandom(SkMWCRandom* random,
     if (dstRead) {
         this->fDstRead = GrGLShaderBuilder::KeyForDstRead(dstTexture, gpu->glCaps());
     }
+
+    CoverageOutput coverageOutput;
+    bool illegalCoverageOutput;
+    do {
+        coverageOutput = static_cast<CoverageOutput>(random->nextULessThan(kCoverageOutputCnt));
+        illegalCoverageOutput = (!gpu->caps()->dualSourceBlendingSupport() &&
+                                 CoverageOutputUsesSecondaryOutput(coverageOutput)) ||
+                                !dstRead && kCombineWithDst_CoverageOutput == coverageOutput;
+    } while (illegalCoverageOutput);
+
+    fCoverageOutput = coverageOutput;
 }
 
 bool GrGpuGL::programUnitTest(int maxStages) {