Use dst copies in porter duffer XP to correctly render certain blends.

BUG=skia:

Committed: https://skia.googlesource.com/skia/+/997c6358d94e188b1a7b89a4f86e24cbe0f5a164

Review URL: https://codereview.chromium.org/914003003
This commit is contained in:
egdaniel 2015-02-17 11:15:47 -08:00 committed by Commit bot
parent d603b22903
commit 3ad6570e94
12 changed files with 192 additions and 89 deletions

View File

@ -293,14 +293,16 @@ protected:
uint32_t fClassID;
private:
virtual GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
virtual GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
const GrDeviceCoordTexture* dstCopy) const = 0;
/**
* Returns true if the XP generated by this factory will explicitly read dst in the fragment
* shader.
*/
virtual bool willReadDstColor(const GrProcOptInfo& colorPOI,
virtual bool willReadDstColor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI) const = 0;
virtual bool onIsEqual(const GrXPFactory&) const = 0;

View File

@ -41,11 +41,13 @@ public:
private:
GrCoverageSetOpXPFactory(SkRegion::Op regionOp, bool invertCoverage);
GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE;
bool willReadDstColor(const GrProcOptInfo& /*colorPOI*/,
bool willReadDstColor(const GrDrawTargetCaps& /*caps*/,
const GrProcOptInfo& /*colorPOI*/,
const GrProcOptInfo& /*coveragePOI*/) const SK_OVERRIDE {
return false;
}

View File

@ -20,8 +20,10 @@ public:
bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const SK_OVERRIDE;
bool canApplyCoverage(const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI) const SK_OVERRIDE;
bool canApplyCoverage(const GrProcOptInfo& /*colorPOI*/,
const GrProcOptInfo& /*coveragePOI*/) const SK_OVERRIDE {
return true;
}
bool canTweakAlphaForCoverage() const SK_OVERRIDE;
@ -31,11 +33,13 @@ public:
private:
GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst);
GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE;
bool willReadDstColor(const GrProcOptInfo& colorPOI,
bool willReadDstColor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI) const SK_OVERRIDE;
bool onIsEqual(const GrXPFactory& xpfBase) const SK_OVERRIDE {

View File

@ -304,11 +304,12 @@ GrArithmeticXPFactory::GrArithmeticXPFactory(float k1, float k2, float k3, float
}
GrXferProcessor*
GrArithmeticXPFactory::onCreateXferProcessor(const GrProcOptInfo& colorPOI,
GrArithmeticXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
const GrDeviceCoordTexture* dstCopy) const {
return ArithmeticXP::Create(fK1, fK2, fK3, fK4, fEnforcePMColor, dstCopy,
this->willReadDstColor(colorPOI, coveragePOI));
this->willReadDstColor(caps, colorPOI, coveragePOI));
}

View File

@ -96,11 +96,13 @@ public:
private:
GrArithmeticXPFactory(float k1, float k2, float k3, float k4, bool enforcePMColor);
GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE;
bool willReadDstColor(const GrProcOptInfo& colorPOI,
bool willReadDstColor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI) const SK_OVERRIDE {
return true;
}

View File

@ -38,7 +38,7 @@ GrXferProcessor* GrXPFactory::createXferProcessor(const GrProcOptInfo& colorPOI,
const GrDeviceCoordTexture* dstCopy,
const GrDrawTargetCaps& caps) const {
#ifdef SK_DEBUG
if (this->willReadDstColor(colorPOI, coveragePOI)) {
if (this->willReadDstColor(caps, colorPOI, coveragePOI)) {
if (!caps.dstReadInShaderSupport()) {
SkASSERT(dstCopy && dstCopy->texture());
} else {
@ -46,14 +46,13 @@ GrXferProcessor* GrXPFactory::createXferProcessor(const GrProcOptInfo& colorPOI,
}
} else {
SkASSERT(!dstCopy || !dstCopy->texture());
}
#endif
return this->onCreateXferProcessor(colorPOI, coveragePOI, dstCopy);
return this->onCreateXferProcessor(caps, colorPOI, coveragePOI, dstCopy);
}
bool GrXPFactory::willNeedDstCopy(const GrDrawTargetCaps& caps, const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI) const {
return (this->willReadDstColor(colorPOI, coveragePOI) && !caps.dstReadInShaderSupport());
return (this->willReadDstColor(caps, colorPOI, coveragePOI) && !caps.dstReadInShaderSupport());
}

View File

@ -223,7 +223,8 @@ GrXPFactory* GrCoverageSetOpXPFactory::Create(SkRegion::Op regionOp, bool invert
}
GrXferProcessor*
GrCoverageSetOpXPFactory::onCreateXferProcessor(const GrProcOptInfo& colorPOI,
GrCoverageSetOpXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& covPOI,
const GrDeviceCoordTexture* dstCopy) const {
return CoverageSetOpXP::Create(fRegionOp, fInvertCoverage);

View File

@ -608,10 +608,11 @@ GrCustomXPFactory::GrCustomXPFactory(SkXfermode::Mode mode)
}
GrXferProcessor*
GrCustomXPFactory::onCreateXferProcessor(const GrProcOptInfo& colorPOI,
GrCustomXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
const GrDeviceCoordTexture* dstCopy) const {
return CustomXP::Create(fMode, dstCopy, this->willReadDstColor(colorPOI, coveragePOI));
return CustomXP::Create(fMode, dstCopy, this->willReadDstColor(caps, colorPOI, coveragePOI));
}

View File

@ -77,11 +77,13 @@ public:
GrXPFactory::InvariantOutput*) const SK_OVERRIDE;
private:
GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE;
bool willReadDstColor(const GrProcOptInfo& colorPOI,
bool willReadDstColor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI) const SK_OVERRIDE {
return true;
}

View File

@ -100,7 +100,8 @@ GrDisableColorXPFactory::GrDisableColorXPFactory() {
}
GrXferProcessor*
GrDisableColorXPFactory::onCreateXferProcessor(const GrProcOptInfo& colorPOI,
GrDisableColorXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& covPOI,
const GrDeviceCoordTexture* dstCopy) const {
return DisableColorXP::Create();

View File

@ -39,11 +39,13 @@ public:
private:
GrDisableColorXPFactory();
GrXferProcessor* onCreateXferProcessor(const GrProcOptInfo& colorPOI,
GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE;
bool willReadDstColor(const GrProcOptInfo& colorPOI,
bool willReadDstColor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI) const SK_OVERRIDE {
return false;
}

View File

@ -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);