blob: b6b57aefead08b0ccbcd477c73a6e28d53ad1f2c [file] [log] [blame]
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "effects/GrPorterDuffXferProcessor.h"
#include "GrBlend.h"
#include "GrCaps.h"
#include "GrProcessor.h"
#include "GrProcOptInfo.h"
#include "GrTypes.h"
#include "GrXferProcessor.h"
#include "gl/GrGLXferProcessor.h"
#include "gl/builders/GrGLFragmentShaderBuilder.h"
#include "gl/builders/GrGLProgramBuilder.h"
/**
* Wraps the shader outputs and HW blend state that comprise a Porter Duff blend mode with coverage.
*/
struct BlendFormula {
public:
/**
* Values the shader can write to primary and secondary outputs. These must all be modulated by
* coverage to support mixed samples. The XP will ignore the multiplies when not using coverage.
*/
enum OutputType {
kNone_OutputType, //<! 0
kCoverage_OutputType, //<! inputCoverage
kModulate_OutputType, //<! inputColor * inputCoverage
kISAModulate_OutputType, //<! (1 - inputColor.a) * inputCoverage
kISCModulate_OutputType, //<! (1 - inputColor) * inputCoverage
kLast_OutputType = kISCModulate_OutputType
};
enum Properties {
kModifiesDst_Property = 1,
kUsesDstColor_Property = 1 << 1,
kUsesInputColor_Property = 1 << 2,
kCanTweakAlphaForCoverage_Property = 1 << 3,
kLast_Property = kCanTweakAlphaForCoverage_Property
};
BlendFormula& operator =(const BlendFormula& other) {
fData = other.fData;
return *this;
}
bool operator ==(const BlendFormula& other) const {
return fData == other.fData;
}
bool hasSecondaryOutput() const { return kNone_OutputType != fSecondaryOutputType; }
bool modifiesDst() const { return SkToBool(fProps & kModifiesDst_Property); }
bool usesDstColor() const { return SkToBool(fProps & kUsesDstColor_Property); }
bool usesInputColor() const { return SkToBool(fProps & kUsesInputColor_Property); }
bool canTweakAlphaForCoverage() const {
return SkToBool(fProps & kCanTweakAlphaForCoverage_Property);
}
/**
* Deduce the properties of a compile-time constant BlendFormula.
*/
template<OutputType PrimaryOut, OutputType SecondaryOut,
GrBlendEquation BlendEquation, GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff>
struct get_properties : SkTIntegralConstant<Properties, static_cast<Properties>(
(GR_BLEND_MODIFIES_DST(BlendEquation, SrcCoeff, DstCoeff) ?
kModifiesDst_Property : 0) |
(GR_BLEND_COEFFS_USE_DST_COLOR(SrcCoeff, DstCoeff) ?
kUsesDstColor_Property : 0) |
((PrimaryOut >= kModulate_OutputType && GR_BLEND_COEFFS_USE_SRC_COLOR(SrcCoeff,DstCoeff)) ||
(SecondaryOut >= kModulate_OutputType && GR_BLEND_COEFF_REFS_SRC2(DstCoeff)) ?
kUsesInputColor_Property : 0) | // We assert later that SrcCoeff doesn't ref src2.
(kModulate_OutputType == PrimaryOut &&
kNone_OutputType == SecondaryOut &&
GR_BLEND_CAN_TWEAK_ALPHA_FOR_COVERAGE(BlendEquation, SrcCoeff, DstCoeff) ?
kCanTweakAlphaForCoverage_Property : 0))> {
// The provided formula should already be optimized.
GR_STATIC_ASSERT((kNone_OutputType == PrimaryOut) ==
!GR_BLEND_COEFFS_USE_SRC_COLOR(SrcCoeff, DstCoeff));
GR_STATIC_ASSERT(!GR_BLEND_COEFF_REFS_SRC2(SrcCoeff));
GR_STATIC_ASSERT((kNone_OutputType == SecondaryOut) ==
!GR_BLEND_COEFF_REFS_SRC2(DstCoeff));
GR_STATIC_ASSERT(PrimaryOut != SecondaryOut || kNone_OutputType == PrimaryOut);
GR_STATIC_ASSERT(kNone_OutputType != PrimaryOut || kNone_OutputType == SecondaryOut);
};
union {
struct {
// We allot the enums one more bit than they require because MSVC seems to sign-extend
// them when the top bit is set. (This is in violation of the C++03 standard 9.6/4)
OutputType fPrimaryOutputType : 4;
OutputType fSecondaryOutputType : 4;
GrBlendEquation fBlendEquation : 6;
GrBlendCoeff fSrcCoeff : 6;
GrBlendCoeff fDstCoeff : 6;
Properties fProps : 32 - (4 + 4 + 6 + 6 + 6);
};
uint32_t fData;
};
GR_STATIC_ASSERT(kLast_OutputType < (1 << 3));
GR_STATIC_ASSERT(kLast_GrBlendEquation < (1 << 5));
GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << 5));
GR_STATIC_ASSERT(kLast_Property < (1 << 6));
};
GR_STATIC_ASSERT(4 == sizeof(BlendFormula));
GR_MAKE_BITFIELD_OPS(BlendFormula::Properties);
/**
* Initialize a compile-time constant BlendFormula and automatically deduce fProps.
*/
#define INIT_BLEND_FORMULA(PRIMARY_OUT, SECONDARY_OUT, BLEND_EQUATION, SRC_COEFF, DST_COEFF) \
{{{PRIMARY_OUT, \
SECONDARY_OUT, \
BLEND_EQUATION, SRC_COEFF, DST_COEFF, \
BlendFormula::get_properties<PRIMARY_OUT, SECONDARY_OUT, \
BLEND_EQUATION, SRC_COEFF, DST_COEFF>::value}}}
/**
* When there is no coverage, or the blend mode can tweak alpha for coverage, we use the standard
* Porter Duff formula.
*/
#define COEFF_FORMULA(SRC_COEFF, DST_COEFF) \
INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \
BlendFormula::kNone_OutputType, \
kAdd_GrBlendEquation, SRC_COEFF, DST_COEFF)
/**
* When the coeffs are (Zero, Zero), we clear the dst. This formula has its own macro so we can set
* the primary output type to none.
*/
#define DST_CLEAR_FORMULA \
INIT_BLEND_FORMULA(BlendFormula::kNone_OutputType, \
BlendFormula::kNone_OutputType, \
kAdd_GrBlendEquation, kZero_GrBlendCoeff, kZero_GrBlendCoeff)
/**
* When the coeffs are (Zero, One), we don't write to the dst at all. This formula has its own macro
* so we can set the primary output type to none.
*/
#define NO_DST_WRITE_FORMULA \
INIT_BLEND_FORMULA(BlendFormula::kNone_OutputType, \
BlendFormula::kNone_OutputType, \
kAdd_GrBlendEquation, kZero_GrBlendCoeff, kOne_GrBlendCoeff)
/**
* When there is coverage, the equation with f=coverage is:
*
* D' = f * (S * srcCoeff + D * dstCoeff) + (1-f) * D
*
* This can be rewritten as:
*
* D' = f * S * srcCoeff + D * (1 - [f * (1 - dstCoeff)])
*
* To implement this formula, we output [f * (1 - dstCoeff)] for the secondary color and replace the
* HW dst coeff with IS2C.
*
* Xfer modes: dst-atop (Sa!=1)
*/
#define COVERAGE_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, SRC_COEFF) \
INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \
ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, \
kAdd_GrBlendEquation, SRC_COEFF, kIS2C_GrBlendCoeff)
/**
* When there is coverage and the src coeff is Zero, the equation with f=coverage becomes:
*
* D' = f * D * dstCoeff + (1-f) * D
*
* This can be rewritten as:
*
* D' = D - D * [f * (1 - dstCoeff)]
*
* To implement this formula, we output [f * (1 - dstCoeff)] for the primary color and use a reverse
* subtract HW blend equation with coeffs of (DC, One).
*
* Xfer modes: clear, dst-out (Sa=1), dst-in (Sa!=1), modulate (Sc!=1)
*/
#define COVERAGE_SRC_COEFF_ZERO_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT) \
INIT_BLEND_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, \
BlendFormula::kNone_OutputType, \
kReverseSubtract_GrBlendEquation, kDC_GrBlendCoeff, kOne_GrBlendCoeff)
/**
* When there is coverage and the dst coeff is Zero, the equation with f=coverage becomes:
*
* D' = f * S * srcCoeff + (1-f) * D
*
* To implement this formula, we output [f] for the secondary color and replace the HW dst coeff
* with IS2A. (Note that we can avoid dual source blending when Sa=1 by using ISA.)
*
* Xfer modes (Sa!=1): src, src-in, src-out
*/
#define COVERAGE_DST_COEFF_ZERO_FORMULA(SRC_COEFF) \
INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \
BlendFormula::kCoverage_OutputType, \
kAdd_GrBlendEquation, SRC_COEFF, kIS2A_GrBlendCoeff)
/**
* This table outlines the blend formulas we will use with each xfermode, with and without coverage,
* with and without an opaque input color. Optimization properties are deduced at compile time so we
* can make runtime decisions quickly. RGB coverage is not supported.
*/
static const BlendFormula gBlendTable[2][2][SkXfermode::kLastCoeffMode + 1] = {
/*>> No coverage, input color unknown <<*/ {{
/* clear */ DST_CLEAR_FORMULA,
/* src */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff),
/* dst */ NO_DST_WRITE_FORMULA,
/* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff),
/* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff),
/* src-in */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff),
/* dst-in */ COEFF_FORMULA( kZero_GrBlendCoeff, kSA_GrBlendCoeff),
/* src-out */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff),
/* dst-out */ COEFF_FORMULA( kZero_GrBlendCoeff, kISA_GrBlendCoeff),
/* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff),
/* dst-atop */ COEFF_FORMULA( kIDA_GrBlendCoeff, kSA_GrBlendCoeff),
/* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff),
/* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff),
/* modulate */ COEFF_FORMULA( kZero_GrBlendCoeff, kSC_GrBlendCoeff),
/* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff),
}, /*>> Has coverage, input color unknown <<*/ {
/* clear */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType),
/* src */ COVERAGE_DST_COEFF_ZERO_FORMULA(kOne_GrBlendCoeff),
/* dst */ NO_DST_WRITE_FORMULA,
/* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff),
/* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff),
/* src-in */ COVERAGE_DST_COEFF_ZERO_FORMULA(kDA_GrBlendCoeff),
/* dst-in */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISAModulate_OutputType),
/* src-out */ COVERAGE_DST_COEFF_ZERO_FORMULA(kIDA_GrBlendCoeff),
/* dst-out */ COEFF_FORMULA( kZero_GrBlendCoeff, kISA_GrBlendCoeff),
/* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff),
/* dst-atop */ COVERAGE_FORMULA(BlendFormula::kISAModulate_OutputType, kIDA_GrBlendCoeff),
/* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff),
/* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff),
/* modulate */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISCModulate_OutputType),
/* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff),
}}, /*>> No coverage, input color opaque <<*/ {{
/* clear */ DST_CLEAR_FORMULA,
/* src */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff),
/* dst */ NO_DST_WRITE_FORMULA,
/* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff),
/* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff),
/* src-in */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff),
/* dst-in */ NO_DST_WRITE_FORMULA,
/* src-out */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff),
/* dst-out */ DST_CLEAR_FORMULA,
/* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff),
/* dst-atop */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff),
/* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff),
/* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff),
/* modulate */ COEFF_FORMULA( kZero_GrBlendCoeff, kSC_GrBlendCoeff),
/* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff),
}, /*>> Has coverage, input color opaque <<*/ {
/* clear */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType),
/* src */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff),
/* dst */ NO_DST_WRITE_FORMULA,
/* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff),
/* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff),
/* src-in */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff),
/* dst-in */ NO_DST_WRITE_FORMULA,
/* src-out */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff),
/* dst-out */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType),
/* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff),
/* dst-atop */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff),
/* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff),
/* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff),
/* modulate */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISCModulate_OutputType),
/* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff),
}}};
static BlendFormula get_blend_formula(const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
bool hasMixedSamples,
SkXfermode::Mode xfermode) {
SkASSERT(xfermode >= 0 && xfermode <= SkXfermode::kLastCoeffMode);
SkASSERT(!coveragePOI.isFourChannelOutput());
bool conflatesCoverage = !coveragePOI.isSolidWhite() || hasMixedSamples;
return gBlendTable[colorPOI.isOpaque()][conflatesCoverage][xfermode];
}
///////////////////////////////////////////////////////////////////////////////
class PorterDuffXferProcessor : public GrXferProcessor {
public:
PorterDuffXferProcessor(BlendFormula blendFormula) : fBlendFormula(blendFormula) {
this->initClassID<PorterDuffXferProcessor>();
}
const char* name() const override { return "Porter Duff"; }
GrGLXferProcessor* createGLInstance() const override;
BlendFormula getBlendFormula() const { return fBlendFormula; }
private:
GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
bool doesStencilWrite,
GrColor* overrideColor,
const GrCaps& caps) override;
void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
bool onHasSecondaryOutput() const override { return fBlendFormula.hasSecondaryOutput(); }
void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override {
blendInfo->fEquation = fBlendFormula.fBlendEquation;
blendInfo->fSrcBlend = fBlendFormula.fSrcCoeff;
blendInfo->fDstBlend = fBlendFormula.fDstCoeff;
blendInfo->fWriteColor = fBlendFormula.modifiesDst();
}
bool onIsEqual(const GrXferProcessor& xpBase) const override {
const PorterDuffXferProcessor& xp = xpBase.cast<PorterDuffXferProcessor>();
return fBlendFormula == xp.fBlendFormula;
}
const BlendFormula fBlendFormula;
typedef GrXferProcessor INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
static void append_color_output(const PorterDuffXferProcessor& xp, GrGLXPFragmentBuilder* fsBuilder,
BlendFormula::OutputType outputType, const char* output,
const char* inColor, const char* inCoverage) {
switch (outputType) {
case BlendFormula::kNone_OutputType:
fsBuilder->codeAppendf("%s = vec4(0.0);", output);
break;
case BlendFormula::kCoverage_OutputType:
// We can have a coverage formula while not reading coverage if there are mixed samples.
fsBuilder->codeAppendf("%s = %s;",
output, xp.readsCoverage() ? inCoverage : "vec4(1.0)");
break;
case BlendFormula::kModulate_OutputType:
if (xp.readsCoverage()) {
fsBuilder->codeAppendf("%s = %s * %s;", output, inColor, inCoverage);
} else {
fsBuilder->codeAppendf("%s = %s;", output, inColor);
}
break;
case BlendFormula::kISAModulate_OutputType:
if (xp.readsCoverage()) {
fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;", output, inColor, inCoverage);
} else {
fsBuilder->codeAppendf("%s = vec4(1.0 - %s.a);", output, inColor);
}
break;
case BlendFormula::kISCModulate_OutputType:
if (xp.readsCoverage()) {
fsBuilder->codeAppendf("%s = (vec4(1.0) - %s) * %s;", output, inColor, inCoverage);
} else {
fsBuilder->codeAppendf("%s = vec4(1.0) - %s;", output, inColor);
}
break;
default:
SkFAIL("Unsupported output type.");
break;
}
}
class GLPorterDuffXferProcessor : public GrGLXferProcessor {
public:
static void GenKey(const GrProcessor& processor, GrProcessorKeyBuilder* b) {
const PorterDuffXferProcessor& xp = processor.cast<PorterDuffXferProcessor>();
b->add32(SkToInt(xp.readsCoverage()) |
(xp.getBlendFormula().fPrimaryOutputType << 1) |
(xp.getBlendFormula().fSecondaryOutputType << 4));
GR_STATIC_ASSERT(BlendFormula::kLast_OutputType < 8);
};
private:
void emitOutputsForBlendState(const EmitArgs& args) override {
const PorterDuffXferProcessor& xp = args.fXP.cast<PorterDuffXferProcessor>();
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
BlendFormula blendFormula = xp.getBlendFormula();
if (blendFormula.hasSecondaryOutput()) {
append_color_output(xp, fsBuilder, blendFormula.fSecondaryOutputType,
args.fOutputSecondary, args.fInputColor, args.fInputCoverage);
}
append_color_output(xp, fsBuilder, blendFormula.fPrimaryOutputType,
args.fOutputPrimary, args.fInputColor, args.fInputCoverage);
}
void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
typedef GrGLXferProcessor INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLSLCaps&,
GrProcessorKeyBuilder* b) const {
GLPorterDuffXferProcessor::GenKey(*this, b);
}
GrGLXferProcessor* PorterDuffXferProcessor::createGLInstance() const {
return SkNEW(GLPorterDuffXferProcessor);
}
GrXferProcessor::OptFlags
PorterDuffXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
bool doesStencilWrite,
GrColor* overrideColor,
const GrCaps& caps) {
GrXferProcessor::OptFlags optFlags = GrXferProcessor::kNone_OptFlags;
if (!fBlendFormula.modifiesDst()) {
if (!doesStencilWrite) {
optFlags |= GrXferProcessor::kSkipDraw_OptFlag;
}
optFlags |= (GrXferProcessor::kIgnoreColor_OptFlag |
GrXferProcessor::kIgnoreCoverage_OptFlag |
GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag);
} else {
if (!fBlendFormula.usesInputColor()) {
optFlags |= GrXferProcessor::kIgnoreColor_OptFlag;
}
if (coveragePOI.isSolidWhite()) {
optFlags |= GrXferProcessor::kIgnoreCoverage_OptFlag;
}
if (colorPOI.allStagesMultiplyInput() && fBlendFormula.canTweakAlphaForCoverage()) {
optFlags |= GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag;
}
}
return optFlags;
}
///////////////////////////////////////////////////////////////////////////////
class ShaderPDXferProcessor : public GrXferProcessor {
public:
ShaderPDXferProcessor(const DstTexture* dstTexture,
bool hasMixedSamples,
SkXfermode::Mode xfermode)
: INHERITED(dstTexture, true, hasMixedSamples)
, fXfermode(xfermode) {
this->initClassID<ShaderPDXferProcessor>();
}
const char* name() const override { return "Porter Duff Shader"; }
GrGLXferProcessor* createGLInstance() const override;
SkXfermode::Mode getXfermode() const { return fXfermode; }
private:
GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo&, const GrProcOptInfo&,
bool, GrColor*, const GrCaps&) override {
return kNone_OptFlags;
}
void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
bool onIsEqual(const GrXferProcessor& xpBase) const override {
const ShaderPDXferProcessor& xp = xpBase.cast<ShaderPDXferProcessor>();
return fXfermode == xp.fXfermode;
}
const SkXfermode::Mode fXfermode;
typedef GrXferProcessor INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
static bool append_porterduff_term(GrGLXPFragmentBuilder* fsBuilder, SkXfermode::Coeff coeff,
const char* colorName, const char* srcColorName,
const char* dstColorName, bool hasPrevious) {
if (SkXfermode::kZero_Coeff == coeff) {
return hasPrevious;
} else {
if (hasPrevious) {
fsBuilder->codeAppend(" + ");
}
fsBuilder->codeAppendf("%s", colorName);
switch (coeff) {
case SkXfermode::kOne_Coeff:
break;
case SkXfermode::kSC_Coeff:
fsBuilder->codeAppendf(" * %s", srcColorName);
break;
case SkXfermode::kISC_Coeff:
fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName);
break;
case SkXfermode::kDC_Coeff:
fsBuilder->codeAppendf(" * %s", dstColorName);
break;
case SkXfermode::kIDC_Coeff:
fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName);
break;
case SkXfermode::kSA_Coeff:
fsBuilder->codeAppendf(" * %s.a", srcColorName);
break;
case SkXfermode::kISA_Coeff:
fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName);
break;
case SkXfermode::kDA_Coeff:
fsBuilder->codeAppendf(" * %s.a", dstColorName);
break;
case SkXfermode::kIDA_Coeff:
fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName);
break;
default:
SkFAIL("Unsupported Blend Coeff");
}
return true;
}
}
class GLShaderPDXferProcessor : public GrGLXferProcessor {
public:
static void GenKey(const GrProcessor& processor, GrProcessorKeyBuilder* b) {
const ShaderPDXferProcessor& xp = processor.cast<ShaderPDXferProcessor>();
b->add32(xp.getXfermode());
}
private:
void emitBlendCodeForDstRead(GrGLXPBuilder* pb, const char* srcColor, const char* dstColor,
const char* outColor, const GrXferProcessor& proc) override {
const ShaderPDXferProcessor& xp = proc.cast<ShaderPDXferProcessor>();
GrGLXPFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder();
SkXfermode::Coeff srcCoeff, dstCoeff;
SkXfermode::ModeAsCoeff(xp.getXfermode(), &srcCoeff, &dstCoeff);
fsBuilder->codeAppendf("%s =", outColor);
// append src blend
bool didAppend = append_porterduff_term(fsBuilder, srcCoeff, srcColor, srcColor, dstColor,
false);
// append dst blend
SkAssertResult(append_porterduff_term(fsBuilder, dstCoeff, dstColor, srcColor, dstColor,
didAppend));
fsBuilder->codeAppend(";");
}
void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
typedef GrGLXferProcessor INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
void ShaderPDXferProcessor::onGetGLProcessorKey(const GrGLSLCaps&,
GrProcessorKeyBuilder* b) const {
GLShaderPDXferProcessor::GenKey(*this, b);
}
GrGLXferProcessor* ShaderPDXferProcessor::createGLInstance() const {
return SkNEW(GLShaderPDXferProcessor);
}
///////////////////////////////////////////////////////////////////////////////
class PDLCDXferProcessor : public GrXferProcessor {
public:
static GrXferProcessor* Create(SkXfermode::Mode xfermode, const GrProcOptInfo& colorPOI);
~PDLCDXferProcessor() override;
const char* name() const override { return "Porter Duff LCD"; }
GrGLXferProcessor* createGLInstance() const override;
private:
PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha);
GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
bool doesStencilWrite,
GrColor* overrideColor,
const GrCaps& caps) override;
void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override {
blendInfo->fSrcBlend = kConstC_GrBlendCoeff;
blendInfo->fDstBlend = kISC_GrBlendCoeff;
blendInfo->fBlendConstant = fBlendConstant;
}
bool onIsEqual(const GrXferProcessor& xpBase) const override {
const PDLCDXferProcessor& xp = xpBase.cast<PDLCDXferProcessor>();
if (fBlendConstant != xp.fBlendConstant ||
fAlpha != xp.fAlpha) {
return false;
}
return true;
}
GrColor fBlendConstant;
uint8_t fAlpha;
typedef GrXferProcessor INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
class GLPDLCDXferProcessor : public GrGLXferProcessor {
public:
GLPDLCDXferProcessor(const GrProcessor&) {}
virtual ~GLPDLCDXferProcessor() {}
static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps,
GrProcessorKeyBuilder* b) {}
private:
void emitOutputsForBlendState(const EmitArgs& args) override {
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor,
args.fInputCoverage);
}
void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {};
typedef GrGLXferProcessor INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
PDLCDXferProcessor::PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha)
: fBlendConstant(blendConstant)
, fAlpha(alpha) {
this->initClassID<PDLCDXferProcessor>();
}
GrXferProcessor* PDLCDXferProcessor::Create(SkXfermode::Mode xfermode,
const GrProcOptInfo& colorPOI) {
if (SkXfermode::kSrcOver_Mode != xfermode) {
return NULL;
}
if (kRGBA_GrColorComponentFlags != colorPOI.validFlags()) {
return NULL;
}
GrColor blendConstant = GrUnPreMulColor(colorPOI.color());
uint8_t alpha = GrColorUnpackA(blendConstant);
blendConstant |= (0xff << GrColor_SHIFT_A);
return SkNEW_ARGS(PDLCDXferProcessor, (blendConstant, alpha));
}
PDLCDXferProcessor::~PDLCDXferProcessor() {
}
void PDLCDXferProcessor::onGetGLProcessorKey(const GrGLSLCaps& caps,
GrProcessorKeyBuilder* b) const {
GLPDLCDXferProcessor::GenKey(*this, caps, b);
}
GrGLXferProcessor* PDLCDXferProcessor::createGLInstance() const {
return SkNEW_ARGS(GLPDLCDXferProcessor, (*this));
}
GrXferProcessor::OptFlags
PDLCDXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
bool doesStencilWrite,
GrColor* overrideColor,
const GrCaps& caps) {
// We want to force our primary output to be alpha * Coverage, where alpha is the alpha
// value of the blend the constant. We should already have valid blend coeff's if we are at
// a point where we have RGB coverage. We don't need any color stages since the known color
// output is already baked into the blendConstant.
*overrideColor = GrColorPackRGBA(fAlpha, fAlpha, fAlpha, fAlpha);
return GrXferProcessor::kOverrideColor_OptFlag;
}
///////////////////////////////////////////////////////////////////////////////
GrPorterDuffXPFactory::GrPorterDuffXPFactory(SkXfermode::Mode xfermode)
: fXfermode(xfermode) {
SkASSERT(fXfermode <= SkXfermode::kLastCoeffMode);
this->initClassID<GrPorterDuffXPFactory>();
}
GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode xfermode) {
static GrPorterDuffXPFactory gClearPDXPF(SkXfermode::kClear_Mode);
static GrPorterDuffXPFactory gSrcPDXPF(SkXfermode::kSrc_Mode);
static GrPorterDuffXPFactory gDstPDXPF(SkXfermode::kDst_Mode);
static GrPorterDuffXPFactory gSrcOverPDXPF(SkXfermode::kSrcOver_Mode);
static GrPorterDuffXPFactory gDstOverPDXPF(SkXfermode::kDstOver_Mode);
static GrPorterDuffXPFactory gSrcInPDXPF(SkXfermode::kSrcIn_Mode);
static GrPorterDuffXPFactory gDstInPDXPF(SkXfermode::kDstIn_Mode);
static GrPorterDuffXPFactory gSrcOutPDXPF(SkXfermode::kSrcOut_Mode);
static GrPorterDuffXPFactory gDstOutPDXPF(SkXfermode::kDstOut_Mode);
static GrPorterDuffXPFactory gSrcATopPDXPF(SkXfermode::kSrcATop_Mode);
static GrPorterDuffXPFactory gDstATopPDXPF(SkXfermode::kDstATop_Mode);
static GrPorterDuffXPFactory gXorPDXPF(SkXfermode::kXor_Mode);
static GrPorterDuffXPFactory gPlusPDXPF(SkXfermode::kPlus_Mode);
static GrPorterDuffXPFactory gModulatePDXPF(SkXfermode::kModulate_Mode);
static GrPorterDuffXPFactory gScreenPDXPF(SkXfermode::kScreen_Mode);
static GrPorterDuffXPFactory* gFactories[] = {
&gClearPDXPF, &gSrcPDXPF, &gDstPDXPF, &gSrcOverPDXPF, &gDstOverPDXPF, &gSrcInPDXPF,
&gDstInPDXPF, &gSrcOutPDXPF, &gDstOutPDXPF, &gSrcATopPDXPF, &gDstATopPDXPF, &gXorPDXPF,
&gPlusPDXPF, &gModulatePDXPF, &gScreenPDXPF
};
GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFactories) == SkXfermode::kLastCoeffMode + 1);
if (xfermode < 0 || xfermode > SkXfermode::kLastCoeffMode) {
return NULL;
}
return SkRef(gFactories[xfermode]);
}
GrXferProcessor*
GrPorterDuffXPFactory::onCreateXferProcessor(const GrCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& covPOI,
bool hasMixedSamples,
const DstTexture* dstTexture) const {
if (covPOI.isFourChannelOutput()) {
SkASSERT(!dstTexture || !dstTexture->texture());
return PDLCDXferProcessor::Create(fXfermode, colorPOI);
}
BlendFormula blendFormula = get_blend_formula(colorPOI, covPOI, hasMixedSamples, fXfermode);
if (blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) {
return SkNEW_ARGS(ShaderPDXferProcessor, (dstTexture, hasMixedSamples, fXfermode));
}
SkASSERT(!dstTexture || !dstTexture->texture());
return SkNEW_ARGS(PorterDuffXferProcessor, (blendFormula));
}
bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/,
uint32_t knownColorFlags) const {
if (SkXfermode::kSrcOver_Mode == fXfermode &&
kRGBA_GrColorComponentFlags == knownColorFlags) {
return true;
}
return false;
}
void GrPorterDuffXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI,
InvariantBlendedColor* blendedColor) const {
// Find the blended color info based on the formula that does not have coverage.
BlendFormula colorFormula = gBlendTable[colorPOI.isOpaque()][0][fXfermode];
if (colorFormula.usesDstColor()) {
blendedColor->fWillBlendWithDst = true;
blendedColor->fKnownColorFlags = kNone_GrColorComponentFlags;
return;
}
blendedColor->fWillBlendWithDst = false;
SkASSERT(kAdd_GrBlendEquation == colorFormula.fBlendEquation);
switch (colorFormula.fSrcCoeff) {
case kZero_GrBlendCoeff:
blendedColor->fKnownColor = 0;
blendedColor->fKnownColorFlags = kRGBA_GrColorComponentFlags;
return;
case kOne_GrBlendCoeff:
blendedColor->fKnownColor = colorPOI.color();
blendedColor->fKnownColorFlags = colorPOI.validFlags();
return;
default:
blendedColor->fKnownColorFlags = kNone_GrColorComponentFlags;
return;
}
}
bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& covPOI,
bool hasMixedSamples) const {
if (caps.shaderCaps()->dualSourceBlendingSupport()) {
return false;
}
if (covPOI.isFourChannelOutput()) {
return false; // The LCD XP will abort rather than doing a dst read.
}
// We fallback on the shader XP when the blend formula would use dual source blending but we
// don't have support for it.
return get_blend_formula(colorPOI, covPOI, hasMixedSamples, fXfermode).hasSecondaryOutput();
}
GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory);
GrXPFactory* GrPorterDuffXPFactory::TestCreate(GrProcessorTestData* d) {
SkXfermode::Mode mode = SkXfermode::Mode(d->fRandom->nextULessThan(SkXfermode::kLastCoeffMode));
return GrPorterDuffXPFactory::Create(mode);
}
void GrPorterDuffXPFactory::TestGetXPOutputTypes(const GrXferProcessor* xp,
int* outPrimary,
int* outSecondary) {
if (!!strcmp(xp->name(), "Porter Duff")) {
*outPrimary = *outSecondary = -1;
return;
}
BlendFormula blendFormula = static_cast<const PorterDuffXferProcessor*>(xp)->getBlendFormula();
*outPrimary = blendFormula.fPrimaryOutputType;
*outSecondary = blendFormula.fSecondaryOutputType;
}