Fix gpu lcd blending to semi-correctly handle alpha coverage

Bug: skia:6606
Change-Id: I16ccd97f5d047eb7fddfed5310bf669e7435ccdd
Reviewed-on: https://skia-review.googlesource.com/17370
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Greg Daniel 2017-05-19 10:56:46 -04:00 committed by Skia Commit-Bot
parent 09e9f68625
commit 6ebe4b9dbe
6 changed files with 111 additions and 34 deletions

View File

@ -9,11 +9,16 @@
#include "GrPipeline.h"
#include "gl/GrGLCaps.h"
GrXferProcessor::GrXferProcessor() : fWillReadDstColor(false), fDstReadUsesMixedSamples(false) {}
GrXferProcessor::GrXferProcessor()
: fWillReadDstColor(false)
, fDstReadUsesMixedSamples(false)
, fIsLCD(false) {}
GrXferProcessor::GrXferProcessor(bool willReadDstColor, bool hasMixedSamples)
GrXferProcessor::GrXferProcessor(bool willReadDstColor, bool hasMixedSamples,
GrProcessorAnalysisCoverage coverage)
: fWillReadDstColor(willReadDstColor)
, fDstReadUsesMixedSamples(willReadDstColor && hasMixedSamples) {}
, fDstReadUsesMixedSamples(willReadDstColor && hasMixedSamples)
, fIsLCD(GrProcessorAnalysisCoverage::kLCD == coverage) {}
bool GrXferProcessor::hasSecondaryOutput() const {
if (!this->willReadDstColor()) {
@ -45,6 +50,9 @@ void GrXferProcessor::getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorK
key |= 0x8;
}
}
if (fIsLCD) {
key |= 0x10;
}
b->add32(key);
this->onGetGLSLProcessorKey(caps, b);
}

View File

@ -153,6 +153,8 @@ public:
*/
bool hasSecondaryOutput() const;
bool isLCD() const { return fIsLCD; }
/** Returns true if this and other processor conservatively draw identically. It can only return
true when the two processor are of the same subclass (i.e. they return the same object from
from getFactory()).
@ -160,7 +162,7 @@ public:
A return value of true from isEqual() should not be used to test whether the processor would
generate the same shader code. To test for identical code generation use getGLSLProcessorKey
*/
bool isEqual(const GrXferProcessor& that) const {
if (this->classID() != that.classID()) {
return false;
@ -171,12 +173,15 @@ public:
if (this->fDstReadUsesMixedSamples != that.fDstReadUsesMixedSamples) {
return false;
}
if (fIsLCD != that.fIsLCD) {
return false;
}
return this->onIsEqual(that);
}
protected:
GrXferProcessor();
GrXferProcessor(bool willReadDstColor, bool hasMixedSamples);
GrXferProcessor(bool willReadDstColor, bool hasMixedSamples, GrProcessorAnalysisCoverage);
private:
/**
@ -201,8 +206,9 @@ private:
virtual bool onIsEqual(const GrXferProcessor&) const = 0;
bool fWillReadDstColor;
bool fDstReadUsesMixedSamples;
bool fWillReadDstColor;
bool fDstReadUsesMixedSamples;
bool fIsLCD;
typedef GrFragmentProcessor INHERITED;
};

View File

@ -73,13 +73,13 @@ static bool can_use_hw_blend_equation(GrBlendEquation equation,
class CustomXP : public GrXferProcessor {
public:
CustomXP(SkBlendMode mode, GrBlendEquation hwBlendEquation)
: fMode(mode),
fHWBlendEquation(hwBlendEquation) {
: fMode(mode)
, fHWBlendEquation(hwBlendEquation) {
this->initClassID<CustomXP>();
}
CustomXP(bool hasMixedSamples, SkBlendMode mode)
: INHERITED(true, hasMixedSamples)
CustomXP(bool hasMixedSamples, SkBlendMode mode, GrProcessorAnalysisCoverage coverage)
: INHERITED(true, hasMixedSamples, coverage)
, fMode(mode)
, fHWBlendEquation(static_cast<GrBlendEquation>(-1)) {
this->initClassID<CustomXP>();
@ -242,7 +242,7 @@ sk_sp<const GrXferProcessor> CustomXPFactory::makeXferProcessor(
if (can_use_hw_blend_equation(fHWBlendEquation, coverage, caps)) {
return sk_sp<GrXferProcessor>(new CustomXP(fMode, fHWBlendEquation));
}
return sk_sp<GrXferProcessor>(new CustomXP(hasMixedSamples, fMode));
return sk_sp<GrXferProcessor>(new CustomXP(hasMixedSamples, fMode, coverage));
}
GrXPFactory::AnalysisProperties CustomXPFactory::analysisProperties(

View File

@ -392,7 +392,9 @@ static BlendFormula get_lcd_blend_formula(SkBlendMode xfermode) {
class PorterDuffXferProcessor : public GrXferProcessor {
public:
PorterDuffXferProcessor(BlendFormula blendFormula) : fBlendFormula(blendFormula) {
PorterDuffXferProcessor(BlendFormula blendFormula, GrProcessorAnalysisCoverage coverage)
: INHERITED(false, false, coverage)
, fBlendFormula(blendFormula) {
this->initClassID<PorterDuffXferProcessor>();
}
@ -501,8 +503,9 @@ GrGLSLXferProcessor* PorterDuffXferProcessor::createGLSLInstance() const {
class ShaderPDXferProcessor : public GrXferProcessor {
public:
ShaderPDXferProcessor(bool hasMixedSamples, SkBlendMode xfermode)
: INHERITED(true, hasMixedSamples), fXfermode(xfermode) {
ShaderPDXferProcessor(bool hasMixedSamples, SkBlendMode xfermode,
GrProcessorAnalysisCoverage coverage)
: INHERITED(true, hasMixedSamples, coverage), fXfermode(xfermode) {
this->initClassID<ShaderPDXferProcessor>();
}
@ -649,7 +652,8 @@ private:
///////////////////////////////////////////////////////////////////////////////
PDLCDXferProcessor::PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha)
: fBlendConstant(blendConstant)
: INHERITED(false, false, GrProcessorAnalysisCoverage::kLCD)
, fBlendConstant(blendConstant)
, fAlpha(alpha) {
this->initClassID<PDLCDXferProcessor>();
}
@ -754,8 +758,9 @@ sk_sp<const GrXferProcessor> GrPorterDuffXPFactory::makeXferProcessor(
const GrProcessorAnalysisColor& color, GrProcessorAnalysisCoverage coverage,
bool hasMixedSamples, const GrCaps& caps) const {
BlendFormula blendFormula;
if (coverage == GrProcessorAnalysisCoverage::kLCD) {
if (SkBlendMode::kSrcOver == fBlendMode && color.isConstant() &&
bool isLCD = coverage == GrProcessorAnalysisCoverage::kLCD;
if (isLCD) {
if (SkBlendMode::kSrcOver == fBlendMode && color.isConstant() && color.isOpaque() &&
!caps.shaderCaps()->dualSourceBlendingSupport() &&
!caps.shaderCaps()->dstReadInShaderSupport()) {
// If we don't have dual source blending or in shader dst reads, we fall back to this
@ -769,10 +774,12 @@ sk_sp<const GrXferProcessor> GrPorterDuffXPFactory::makeXferProcessor(
hasMixedSamples, fBlendMode);
}
if (blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) {
return sk_sp<const GrXferProcessor>(new ShaderPDXferProcessor(hasMixedSamples, fBlendMode));
if ((blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) ||
(isLCD && !color.isOpaque())) {
return sk_sp<const GrXferProcessor>(new ShaderPDXferProcessor(hasMixedSamples, fBlendMode,
coverage));
}
return sk_sp<const GrXferProcessor>(new PorterDuffXferProcessor(blendFormula));
return sk_sp<const GrXferProcessor>(new PorterDuffXferProcessor(blendFormula, coverage));
}
static inline GrXPFactory::AnalysisProperties analysis_properties(
@ -781,7 +788,13 @@ static inline GrXPFactory::AnalysisProperties analysis_properties(
using AnalysisProperties = GrXPFactory::AnalysisProperties;
AnalysisProperties props = AnalysisProperties::kNone;
bool hasCoverage = GrProcessorAnalysisCoverage::kNone != coverage;
auto formula = gBlendTable[color.isOpaque()][hasCoverage][(int)mode];
bool isLCD = GrProcessorAnalysisCoverage::kLCD == coverage;
BlendFormula formula;
if (isLCD) {
formula = gLCDBlendTable[(int)mode];
} else {
formula = gBlendTable[color.isOpaque()][hasCoverage][(int)mode];
}
if (formula.canTweakAlphaForCoverage()) {
props |= AnalysisProperties::kCompatibleWithAlphaAsCoverage;
}
@ -794,7 +807,7 @@ static inline GrXPFactory::AnalysisProperties analysis_properties(
if (GrProcessorAnalysisCoverage::kLCD == coverage) {
// Check for special case of srcover with a known color which can be done using the
// blend constant.
if (SkBlendMode::kSrcOver == mode && color.isConstant()) {
if (SkBlendMode::kSrcOver == mode && color.isConstant() && color.isOpaque()) {
props |= AnalysisProperties::kIgnoresInputColor;
} else {
if (get_lcd_blend_formula(mode).hasSecondaryOutput()) {
@ -804,6 +817,12 @@ static inline GrXPFactory::AnalysisProperties analysis_properties(
} else if (formula.hasSecondaryOutput()) {
props |= AnalysisProperties::kReadsDstInShader;
}
} else {
// For LCD blending, if the color is not opaque we must read the dst in shader even if we
// have dual source blending.
if (isLCD && !color.isOpaque()) {
props |= AnalysisProperties::kReadsDstInShader;
}
}
if (!formula.modifiesDst() || !formula.usesInputColor()) {
props |= AnalysisProperties::kIgnoresInputColor;
@ -851,7 +870,8 @@ void GrPorterDuffXPFactory::TestGetXPOutputTypes(const GrXferProcessor* xp,
const GrXferProcessor& GrPorterDuffXPFactory::SimpleSrcOverXP() {
static BlendFormula gSrcOverBlendFormula =
MakeCoeffFormula(kOne_GrBlendCoeff, kISA_GrBlendCoeff);
static PorterDuffXferProcessor gSrcOverXP(gSrcOverBlendFormula);
static PorterDuffXferProcessor gSrcOverXP(gSrcOverBlendFormula,
GrProcessorAnalysisCoverage::kSingleChannel);
return gSrcOverXP;
}
@ -870,7 +890,7 @@ sk_sp<const GrXferProcessor> GrPorterDuffXPFactory::MakeSrcOverXferProcessor(
return nullptr;
}
if (color.isConstant() && !caps.shaderCaps()->dualSourceBlendingSupport() &&
if (color.isConstant() && color.isOpaque() && !caps.shaderCaps()->dualSourceBlendingSupport() &&
!caps.shaderCaps()->dstReadInShaderSupport()) {
// If we don't have dual source blending or in shader dst reads, we fall
// back to this trick for rendering SrcOver LCD text instead of doing a
@ -880,16 +900,17 @@ sk_sp<const GrXferProcessor> GrPorterDuffXPFactory::MakeSrcOverXferProcessor(
BlendFormula blendFormula;
blendFormula = get_lcd_blend_formula(SkBlendMode::kSrcOver);
if (blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) {
if (!color.isOpaque() ||
(blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport())) {
return sk_sp<GrXferProcessor>(
new ShaderPDXferProcessor(hasMixedSamples, SkBlendMode::kSrcOver));
new ShaderPDXferProcessor(hasMixedSamples, SkBlendMode::kSrcOver, coverage));
}
return sk_sp<GrXferProcessor>(new PorterDuffXferProcessor(blendFormula));
return sk_sp<GrXferProcessor>(new PorterDuffXferProcessor(blendFormula, coverage));
}
sk_sp<const GrXferProcessor> GrPorterDuffXPFactory::MakeNoCoverageXP(SkBlendMode blendmode) {
BlendFormula formula = get_blend_formula(false, false, false, blendmode);
return sk_make_sp<PorterDuffXferProcessor>(formula);
return sk_make_sp<PorterDuffXferProcessor>(formula, GrProcessorAnalysisCoverage::kNone);
}
GrXPFactory::AnalysisProperties GrPorterDuffXPFactory::SrcOverAnalysisProperties(

View File

@ -13,8 +13,22 @@
#include "glsl/GrGLSLProgramDataManager.h"
#include "glsl/GrGLSLUniformHandler.h"
// This is only called for cases where we are doing LCD coverage and not using in shader blending.
// For these cases we assume the the src alpha is 1, thus we can just use the max for the alpha
// coverage since src alpha will always be greater than or equal to dst alpha.
static void adjust_for_lcd_coverage(GrGLSLXPFragmentBuilder* fragBuilder,
const char* srcCoverage,
const GrXferProcessor& proc) {
if (srcCoverage && proc.isLCD()) {
fragBuilder->codeAppendf("%s.a = max(max(%s.r, %s.g), %s.b);",
srcCoverage, srcCoverage, srcCoverage, srcCoverage);
}
}
void GrGLSLXferProcessor::emitCode(const EmitArgs& args) {
if (!args.fXP.willReadDstColor()) {
adjust_for_lcd_coverage(args.fXPFragBuilder, args.fInputCoverage, args.fXP);
this->emitOutputsForBlendState(args);
return;
}
@ -111,13 +125,26 @@ void GrGLSLXferProcessor::DefaultCoverageModulation(GrGLSLXPFragmentBuilder* fra
const GrXferProcessor& proc) {
if (proc.dstReadUsesMixedSamples()) {
if (srcCoverage) {
SkASSERT(!proc.isLCD());
fragBuilder->codeAppendf("%s *= %s;", outColor, srcCoverage);
fragBuilder->codeAppendf("%s = %s;", outColorSecondary, srcCoverage);
} else {
fragBuilder->codeAppendf("%s = vec4(1.0);", outColorSecondary);
}
} else if (srcCoverage) {
if (proc.isLCD()) {
fragBuilder->codeAppendf("float lerpRed = mix(%s.a, %s.a, %s.r);",
dstColor, outColor, srcCoverage);
fragBuilder->codeAppendf("float lerpBlue = mix(%s.a, %s.a, %s.g);",
dstColor, outColor, srcCoverage);
fragBuilder->codeAppendf("float lerpGreen = mix(%s.a, %s.a, %s.b);",
dstColor, outColor, srcCoverage);
}
fragBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
outColor, srcCoverage, outColor, srcCoverage, dstColor);
if (proc.isLCD()) {
fragBuilder->codeAppendf("%s.a = max(max(lerpRed, lerpBlue), lerpGreen);", outColor);
}
}
}

View File

@ -104,7 +104,7 @@ public:
};
static void test_lcd_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kNo;
GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kYes;
GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kLCD;
for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
@ -1004,10 +1004,26 @@ static void test_color_opaque_no_coverage(skiatest::Reporter* reporter, const Gr
static void test_lcd_coverage_fallback_case(skiatest::Reporter* reporter, const GrCaps& caps) {
const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(SkBlendMode::kSrcOver);
GrProcessorAnalysisColor color = GrColorPackRGBA(123, 45, 67, 221);
GrProcessorAnalysisColor color = GrColorPackRGBA(123, 45, 67, 255);
GrProcessorAnalysisCoverage coverage = GrProcessorAnalysisCoverage::kLCD;
SkASSERT(!(GrXPFactory::GetAnalysisProperties(xpf, color, coverage, caps) &
GrXPFactory::AnalysisProperties::kRequiresDstTexture));
TEST_ASSERT(!(GrXPFactory::GetAnalysisProperties(xpf, color, coverage, caps) &
GrXPFactory::AnalysisProperties::kRequiresDstTexture));
sk_sp<const GrXferProcessor> xp_opaque(
GrXPFactory::MakeXferProcessor(xpf, color, coverage, false, caps));
if (!xp_opaque) {
ERRORF(reporter, "Failed to create an XP with LCD coverage.");
return;
}
GrXferProcessor::BlendInfo blendInfo;
xp_opaque->getBlendInfo(&blendInfo);
TEST_ASSERT(blendInfo.fWriteColor);
// Test with non-opaque alpha
color = GrColorPackRGBA(123, 45, 67, 221);
coverage = GrProcessorAnalysisCoverage::kLCD;
TEST_ASSERT(GrXPFactory::GetAnalysisProperties(xpf, color, coverage, caps) &
GrXPFactory::AnalysisProperties::kRequiresDstTexture);
sk_sp<const GrXferProcessor> xp(
GrXPFactory::MakeXferProcessor(xpf, color, coverage, false, caps));
if (!xp) {
@ -1015,7 +1031,6 @@ static void test_lcd_coverage_fallback_case(skiatest::Reporter* reporter, const
return;
}
GrXferProcessor::BlendInfo blendInfo;
xp->getBlendInfo(&blendInfo);
TEST_ASSERT(blendInfo.fWriteColor);
}