| Index: src/gpu/effects/GrPorterDuffXferProcessor.cpp
|
| diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
|
| index a08776a20ff0668ebe11dc92d646f534dcdf3393..4453c8935d84ec84c190b7be33f0737c31539371 100644
|
| --- a/src/gpu/effects/GrPorterDuffXferProcessor.cpp
|
| +++ b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
|
| @@ -59,6 +59,10 @@ public:
|
| kCoverage_PrimaryOutputType,
|
| // Modulate color and coverage, write result as the color output.
|
| kModulate_PrimaryOutputType,
|
| + // Custom Porter-Duff output, used for when we explictly are reading the dst and blending
|
| + // in the shader. Secondary Output must be none if you use this. The custom blend uses the
|
| + // equation: cov * (coeffS * S + coeffD * D) + (1 - cov) * D
|
| + kCustom_PrimaryOutputType
|
| };
|
|
|
| enum SecondaryOutputType {
|
| @@ -87,11 +91,19 @@ public:
|
| const GrDrawTargetCaps& caps) SK_OVERRIDE;
|
|
|
| void getBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const SK_OVERRIDE {
|
| - blendInfo->fSrcBlend = fSrcBlend;
|
| - blendInfo->fDstBlend = fDstBlend;
|
| + if (!this->willReadDstColor()) {
|
| + blendInfo->fSrcBlend = fSrcBlend;
|
| + blendInfo->fDstBlend = fDstBlend;
|
| + } else {
|
| + blendInfo->fSrcBlend = kOne_GrBlendCoeff;
|
| + blendInfo->fDstBlend = kZero_GrBlendCoeff;
|
| + }
|
| blendInfo->fBlendConstant = fBlendConstant;
|
| }
|
|
|
| + GrBlendCoeff getSrcBlend() const { return fSrcBlend; }
|
| + GrBlendCoeff getDstBlend() const { return fDstBlend; }
|
| +
|
| private:
|
| PorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, GrColor constant,
|
| const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
|
| @@ -128,6 +140,50 @@ private:
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| +bool append_porterduff_term(GrGLFPFragmentBuilder* fsBuilder, GrBlendCoeff coeff,
|
| + const char* colorName, const char* srcColorName,
|
| + const char* dstColorName, bool hasPrevious) {
|
| + if (kZero_GrBlendCoeff == coeff) {
|
| + return hasPrevious;
|
| + } else {
|
| + if (hasPrevious) {
|
| + fsBuilder->codeAppend(" + ");
|
| + }
|
| + fsBuilder->codeAppendf("%s", colorName);
|
| + switch (coeff) {
|
| + case kOne_GrBlendCoeff:
|
| + break;
|
| + case kSC_GrBlendCoeff:
|
| + fsBuilder->codeAppendf(" * %s", srcColorName);
|
| + break;
|
| + case kISC_GrBlendCoeff:
|
| + fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName);
|
| + break;
|
| + case kDC_GrBlendCoeff:
|
| + fsBuilder->codeAppendf(" * %s", dstColorName);
|
| + break;
|
| + case kIDC_GrBlendCoeff:
|
| + fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName);
|
| + break;
|
| + case kSA_GrBlendCoeff:
|
| + fsBuilder->codeAppendf(" * %s.a", srcColorName);
|
| + break;
|
| + case kISA_GrBlendCoeff:
|
| + fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName);
|
| + break;
|
| + case kDA_GrBlendCoeff:
|
| + fsBuilder->codeAppendf(" * %s.a", dstColorName);
|
| + break;
|
| + case kIDA_GrBlendCoeff:
|
| + fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName);
|
| + break;
|
| + default:
|
| + SkFAIL("Unsupported Blend Coeff");
|
| + }
|
| + return true;
|
| + }
|
| +}
|
| +
|
| class GLPorterDuffXferProcessor : public GrGLXferProcessor {
|
| public:
|
| GLPorterDuffXferProcessor(const GrProcessor&) {}
|
| @@ -139,16 +195,24 @@ public:
|
| const PorterDuffXferProcessor& xp = processor.cast<PorterDuffXferProcessor>();
|
| b->add32(xp.primaryOutputType());
|
| b->add32(xp.secondaryOutputType());
|
| + if (xp.willReadDstColor()) {
|
| + b->add32(xp.getSrcBlend());
|
| + b->add32(xp.getDstBlend());
|
| + }
|
| };
|
|
|
| private:
|
| void onEmitCode(const EmitArgs& args) SK_OVERRIDE {
|
| const PorterDuffXferProcessor& xp = args.fXP.cast<PorterDuffXferProcessor>();
|
| GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
|
| - if (xp.hasSecondaryOutput()) {
|
| + if (PorterDuffXferProcessor::kCustom_PrimaryOutputType != xp.primaryOutputType()) {
|
| + SkASSERT(!xp.willReadDstColor());
|
| switch(xp.secondaryOutputType()) {
|
| + case PorterDuffXferProcessor::kNone_SecondaryOutputType:
|
| + break;
|
| case PorterDuffXferProcessor::kCoverage_SecondaryOutputType:
|
| - fsBuilder->codeAppendf("%s = %s;", args.fOutputSecondary, args.fInputCoverage);
|
| + fsBuilder->codeAppendf("%s = %s;", args.fOutputSecondary,
|
| + args.fInputCoverage);
|
| break;
|
| case PorterDuffXferProcessor::kCoverageISA_SecondaryOutputType:
|
| fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;",
|
| @@ -163,24 +227,43 @@ private:
|
| default:
|
| SkFAIL("Unexpected Secondary Output");
|
| }
|
| - }
|
| -
|
| - switch (xp.primaryOutputType()) {
|
| - case PorterDuffXferProcessor::kNone_PrimaryOutputType:
|
| - fsBuilder->codeAppendf("%s = vec4(0);", args.fOutputPrimary);
|
| - break;
|
| - case PorterDuffXferProcessor::kColor_PrimaryOutputType:
|
| - fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor);
|
| - break;
|
| - case PorterDuffXferProcessor::kCoverage_PrimaryOutputType:
|
| - fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputCoverage);
|
| - break;
|
| - case PorterDuffXferProcessor::kModulate_PrimaryOutputType:
|
| - fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor,
|
| - args.fInputCoverage);
|
| - break;
|
| - default:
|
| - SkFAIL("Unexpected Primary Output");
|
| +
|
| + switch (xp.primaryOutputType()) {
|
| + case PorterDuffXferProcessor::kNone_PrimaryOutputType:
|
| + fsBuilder->codeAppendf("%s = vec4(0);", args.fOutputPrimary);
|
| + break;
|
| + case PorterDuffXferProcessor::kColor_PrimaryOutputType:
|
| + fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor);
|
| + break;
|
| + case PorterDuffXferProcessor::kCoverage_PrimaryOutputType:
|
| + fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputCoverage);
|
| + break;
|
| + case PorterDuffXferProcessor::kModulate_PrimaryOutputType:
|
| + fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor,
|
| + args.fInputCoverage);
|
| + break;
|
| + default:
|
| + SkFAIL("Unexpected Primary Output");
|
| + }
|
| + } else {
|
| + SkASSERT(xp.willReadDstColor());
|
| +
|
| + const char* dstColor = fsBuilder->dstColor();
|
| +
|
| + fsBuilder->codeAppend("vec4 colorBlend =");
|
| + // append src blend
|
| + bool didAppend = append_porterduff_term(fsBuilder, xp.getSrcBlend(),
|
| + args.fInputColor, args.fInputColor,
|
| + dstColor, false);
|
| + // append dst blend
|
| + SkAssertResult(append_porterduff_term(fsBuilder, xp.getDstBlend(),
|
| + dstColor, args.fInputColor,
|
| + dstColor, didAppend));
|
| + fsBuilder->codeAppend(";");
|
| +
|
| + fsBuilder->codeAppendf("%s = %s * colorBlend + (vec4(1.0) - %s) * %s;",
|
| + args.fOutputPrimary, args.fInputCoverage, args.fInputCoverage,
|
| + dstColor);
|
| }
|
| }
|
|
|
| @@ -196,7 +279,8 @@ PorterDuffXferProcessor::PorterDuffXferProcessor(GrBlendCoeff srcBlend,
|
| GrColor constant,
|
| const GrDeviceCoordTexture* dstCopy,
|
| bool willReadDstColor)
|
| - : fSrcBlend(srcBlend)
|
| + : INHERITED(dstCopy, willReadDstColor)
|
| + , fSrcBlend(srcBlend)
|
| , fDstBlend(dstBlend)
|
| , fBlendConstant(constant)
|
| , fPrimaryOutputType(kModulate_PrimaryOutputType)
|
| @@ -244,6 +328,11 @@ PorterDuffXferProcessor::getOptimizations(const GrProcOptInfo& colorPOI,
|
| void PorterDuffXferProcessor::calcOutputTypes(GrXferProcessor::OptFlags optFlags,
|
| const GrDrawTargetCaps& caps,
|
| bool hasSolidCoverage) {
|
| + if (this->willReadDstColor()) {
|
| + fPrimaryOutputType = kCustom_PrimaryOutputType;
|
| + return;
|
| + }
|
| +
|
| if (optFlags & kIgnoreColor_OptFlag) {
|
| if (optFlags & kIgnoreCoverage_OptFlag) {
|
| fPrimaryOutputType = kNone_PrimaryOutputType;
|
| @@ -284,11 +373,12 @@ GrXferProcessor::OptFlags
|
| PorterDuffXferProcessor::internalGetOptimizations(const GrProcOptInfo& colorPOI,
|
| const GrProcOptInfo& coveragePOI,
|
| bool doesStencilWrite) {
|
| - bool srcAIsOne;
|
| - bool hasCoverage;
|
| + if (this->willReadDstColor()) {
|
| + return GrXferProcessor::kNone_Opt;
|
| + }
|
|
|
| - srcAIsOne = colorPOI.isOpaque();
|
| - hasCoverage = !coveragePOI.isSolidWhite();
|
| + bool srcAIsOne = colorPOI.isOpaque();
|
| + bool hasCoverage = !coveragePOI.isSolidWhite();
|
|
|
| bool dstCoeffIsOne = kOne_GrBlendCoeff == fDstBlend ||
|
| (kSA_GrBlendCoeff == fDstBlend && srcAIsOne);
|
| @@ -472,19 +562,20 @@ GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode mode) {
|
| }
|
|
|
| GrXferProcessor*
|
| -GrPorterDuffXPFactory::onCreateXferProcessor(const GrProcOptInfo& colorPOI,
|
| +GrPorterDuffXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
|
| + const GrProcOptInfo& colorPOI,
|
| const GrProcOptInfo& covPOI,
|
| const GrDeviceCoordTexture* dstCopy) const {
|
| if (!covPOI.isFourChannelOutput()) {
|
| return PorterDuffXferProcessor::Create(fSrcCoeff, fDstCoeff, 0, dstCopy,
|
| - this->willReadDstColor(colorPOI, covPOI));
|
| + this->willReadDstColor(caps, colorPOI, covPOI));
|
| } else {
|
| if (this->supportsRGBCoverage(colorPOI.color(), colorPOI.validFlags())) {
|
| SkASSERT(kRGBA_GrColorComponentFlags == colorPOI.validFlags());
|
| GrColor blendConstant = GrUnPreMulColor(colorPOI.color());
|
| return PorterDuffXferProcessor::Create(kConstC_GrBlendCoeff, kISC_GrBlendCoeff,
|
| blendConstant, dstCopy,
|
| - this->willReadDstColor(colorPOI, covPOI));
|
| + this->willReadDstColor(caps, colorPOI, covPOI));
|
| } else {
|
| return NULL;
|
| }
|
| @@ -500,39 +591,6 @@ bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/,
|
| return false;
|
| }
|
|
|
| -bool GrPorterDuffXPFactory::canApplyCoverage(const GrProcOptInfo& colorPOI,
|
| - const GrProcOptInfo& coveragePOI) const {
|
| - bool srcAIsOne = colorPOI.isOpaque();
|
| -
|
| - bool dstCoeffIsOne = kOne_GrBlendCoeff == fDstCoeff ||
|
| - (kSA_GrBlendCoeff == fDstCoeff && srcAIsOne);
|
| - bool dstCoeffIsZero = kZero_GrBlendCoeff == fDstCoeff ||
|
| - (kISA_GrBlendCoeff == fDstCoeff && srcAIsOne);
|
| -
|
| - if ((kZero_GrBlendCoeff == fSrcCoeff && dstCoeffIsOne)) {
|
| - return true;
|
| - }
|
| -
|
| - // if we don't have coverage we can check whether the dst
|
| - // has to read at all.
|
| - // check whether coverage can be safely rolled into alpha
|
| - // of if we can skip color computation and just emit coverage
|
| - if (this->canTweakAlphaForCoverage()) {
|
| - return true;
|
| - }
|
| - if (dstCoeffIsZero) {
|
| - if (kZero_GrBlendCoeff == fSrcCoeff) {
|
| - return true;
|
| - } else if (srcAIsOne) {
|
| - return true;
|
| - }
|
| - } else if (dstCoeffIsOne) {
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| bool GrPorterDuffXPFactory::canTweakAlphaForCoverage() const {
|
| return can_tweak_alpha_for_coverage(fDstCoeff);
|
| }
|
| @@ -606,9 +664,37 @@ void GrPorterDuffXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI,
|
| output->fWillBlendWithDst = false;
|
| }
|
|
|
| -bool GrPorterDuffXPFactory::willReadDstColor(const GrProcOptInfo& colorPOI,
|
| +bool GrPorterDuffXPFactory::willReadDstColor(const GrDrawTargetCaps& caps,
|
| + const GrProcOptInfo& colorPOI,
|
| const GrProcOptInfo& coveragePOI) const {
|
| - return false;
|
| + // We can always blend correctly if we have dual source blending.
|
| + if (caps.dualSourceBlendingSupport()) {
|
| + return false;
|
| + }
|
| +
|
| + if (this->canTweakAlphaForCoverage()) {
|
| + return false;
|
| + }
|
| +
|
| + bool srcAIsOne = colorPOI.isOpaque();
|
| +
|
| + if (kZero_GrBlendCoeff == fDstCoeff) {
|
| + if (kZero_GrBlendCoeff == fSrcCoeff || srcAIsOne) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + // Reduces to: coeffS * (Cov*S) + D
|
| + if (kSA_GrBlendCoeff == fDstCoeff && srcAIsOne) {
|
| + return false;
|
| + }
|
| +
|
| + // We can always blend correctly if we have solid coverage.
|
| + if (coveragePOI.isSolidWhite()) {
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| }
|
|
|
| GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory);
|
|
|