From 5920ac276877b36624e07baf97c7768e80a07f98 Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Fri, 19 Apr 2013 13:14:45 +0000 Subject: [PATCH] Perform coverage blend with the dst in the shader when using a dst-reading xfermode. 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 | 4 +- src/gpu/gl/GrGLProgram.cpp | 91 ++++++++++++++++++++++++++-------- src/gpu/gl/GrGLProgramDesc.cpp | 13 +++-- src/gpu/gl/GrGLProgramDesc.h | 39 +++++++++++---- tests/GLProgramsTest.cpp | 17 ++++--- 5 files changed, 119 insertions(+), 45 deletions(-) diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 356fcac243..299cf3de66 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -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");) diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp index 0b338f2faa..1b4baa40ea 100644 --- a/src/gpu/gl/GrGLProgram.cpp +++ b/src/gpu/gl/GrGLProgram.cpp @@ -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(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 diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp index 73846b8df3..86771794d9 100644 --- a/src/gpu/gl/GrGLProgramDesc.cpp +++ b/src/gpu/gl/GrGLProgramDesc.cpp @@ -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; } } diff --git a/src/gpu/gl/GrGLProgramDesc.h b/src/gpu/gl/GrGLProgramDesc.h index d2167c758b..8d7ca8d1bc 100644 --- a/src/gpu/gl/GrGLProgramDesc.h +++ b/src/gpu/gl/GrGLProgramDesc.h @@ -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; diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp index 0df29aafbf..d85d8a5f95 100644 --- a/tests/GLProgramsTest.cpp +++ b/tests/GLProgramsTest.cpp @@ -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(random->nextULessThan(kCoverageOutputCnt)); + illegalCoverageOutput = (!gpu->caps()->dualSourceBlendingSupport() && + CoverageOutputUsesSecondaryOutput(coverageOutput)) || + !dstRead && kCombineWithDst_CoverageOutput == coverageOutput; + } while (illegalCoverageOutput); + + fCoverageOutput = coverageOutput; } bool GrGpuGL::programUnitTest(int maxStages) {