Apply coverage in XP base class when using dst reads

Moves the coverage logic into GrGLXferProcessor for XPs that perform
dst reads. XPs that don't use a dst read are still responsible to
handle coverage on their own.

BUG=skia:

Review URL: https://codereview.chromium.org/1170553002
This commit is contained in:
cdalton 2015-06-08 12:14:44 -07:00 committed by Commit bot
parent c31af44336
commit edbb31f7dd
9 changed files with 140 additions and 99 deletions

View File

@ -90,13 +90,16 @@ enum GrXferBarrierType {
/**
* GrXferProcessor is responsible for implementing the xfer mode that blends the src color and dst
* color. It does this by emitting fragment shader code and controlling the fixed-function blend
* state. The inputs to its shader code are the final computed src color and fractional pixel
* coverage. The GrXferProcessor's shader code writes the fragment shader output color that goes
* into the fixed-function blend. When dual-source blending is available, it may also write a
* seconday fragment shader output color. When allowed by the backend API, the GrXferProcessor may
* read the destination color. The GrXferProcessor is responsible for setting the blend coefficients
* and blend constant color.
* color, and for applying any coverage. It does this by emitting fragment shader code and
* controlling the fixed-function blend state. When dual-source blending is available, it may also
* write a seconday fragment shader output color. GrXferProcessor has two modes of operation:
*
* Dst read: When allowed by the backend API, or when supplied a texture of the destination, the
* GrXferProcessor may read the destination color. While operating in this mode, the subclass only
* provides shader code that blends the src and dst colors, and the base class applies coverage.
*
* No dst read: When not performing a dst read, the subclass is given full control of the fixed-
* function blend state and/or secondary output, and is responsible to apply coverage on its own.
*
* A GrXferProcessor is never installed directly into our draw state, but instead is created from a
* GrXPFactory once we have finalized the state of our draw.
@ -228,10 +231,7 @@ public:
bool fWriteColor;
};
void getBlendInfo(BlendInfo* blendInfo) const {
blendInfo->reset();
this->onGetBlendInfo(blendInfo);
}
void getBlendInfo(BlendInfo* blendInfo) const;
bool willReadDstColor() const { return fWillReadDstColor; }
@ -256,11 +256,11 @@ public:
*/
bool readsCoverage() const { return fReadsCoverage; }
/**
/**
* Returns whether or not this xferProcossor will set a secondary output to be used with dual
* source blending.
*/
virtual bool hasSecondaryOutput() const { return false; }
bool hasSecondaryOutput() const;
/** 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
@ -317,9 +317,16 @@ private:
}
/**
* Retrieves the hardware blend state required by this Xfer processor. The BlendInfo struct
* comes initialized to default values, so the Xfer processor only needs to set the state it
* needs. It may not even need to override this method at all.
* If we are not performing a dst read, returns whether the subclass will set a secondary
* output. When using dst reads, the base class disables the secondary output and this method
* will not be called.
*/
virtual bool onHasSecondaryOutput() const { return false; }
/**
* If we are not performing a dst read, retrieves the fixed-function blend state required by the
* subclass. When using dst reads, the base class disables fixed-function blending and this
* method will not be called. The BlendInfo struct comes initialized to "no blending".
*/
virtual void onGetBlendInfo(BlendInfo*) const {}

View File

@ -172,8 +172,6 @@ public:
GrGLXferProcessor* createGLInstance() const override;
bool hasSecondaryOutput() const override { return false; }
float k1() const { return fK1; }
float k2() const { return fK2; }
float k3() const { return fK3; }
@ -228,22 +226,16 @@ public:
}
private:
void onEmitCode(const EmitArgs& args) override {
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
void emitBlendCodeForDstRead(GrGLXPBuilder* pb, const char* srcColor, const char* dstColor,
const char* outColor, const GrXferProcessor& proc) override {
GrGLXPFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder();
const char* dstColor = fsBuilder->dstColor();
fKUni = pb->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kVec4f_GrSLType, kDefault_GrSLPrecision,
"k");
const char* kUni = pb->getUniformCStr(fKUni);
fKUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kVec4f_GrSLType, kDefault_GrSLPrecision,
"k");
const char* kUni = args.fPB->getUniformCStr(fKUni);
add_arithmetic_code(fsBuilder, args.fInputColor, dstColor, args.fOutputPrimary, kUni,
fEnforcePMColor);
fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
args.fOutputPrimary, args.fOutputPrimary, args.fInputCoverage,
args.fInputCoverage, dstColor);
add_arithmetic_code(fsBuilder, srcColor, dstColor, outColor, kUni, fEnforcePMColor);
}
void onSetData(const GrGLProgramDataManager& pdman,

View File

@ -6,6 +6,7 @@
*/
#include "GrXferProcessor.h"
#include "GrProcOptInfo.h"
#include "gl/GrGLCaps.h"
GrXferProcessor::GrXferProcessor()
@ -17,6 +18,7 @@ GrXferProcessor::GrXferProcessor(const DstTexture* dstTexture, bool willReadDstC
, fReadsCoverage(true)
, fDstTextureOffset() {
if (dstTexture && dstTexture->texture()) {
SkASSERT(willReadDstColor);
fDstTexture.reset(dstTexture->texture());
fDstTextureOffset = dstTexture->offset();
this->addTextureAccess(&fDstTexture);
@ -35,17 +37,45 @@ GrXferProcessor::OptFlags GrXferProcessor::getOptimizations(const GrProcOptInfo&
overrideColor,
caps);
if (this->willReadDstColor()) {
// When performing a dst read we handle coverage in the base class.
SkASSERT(!(flags & GrXferProcessor::kIgnoreCoverage_OptFlag));
if (coveragePOI.isSolidWhite()) {
flags |= GrXferProcessor::kIgnoreCoverage_OptFlag;
}
}
if (flags & GrXferProcessor::kIgnoreCoverage_OptFlag) {
fReadsCoverage = false;
}
return flags;
}
bool GrXferProcessor::hasSecondaryOutput() const {
if (!this->willReadDstColor()) {
return this->onHasSecondaryOutput();
}
return false;
}
void GrXferProcessor::getBlendInfo(BlendInfo* blendInfo) const {
blendInfo->reset();
if (!this->willReadDstColor()) {
this->onGetBlendInfo(blendInfo);
}
}
void GrXferProcessor::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
uint32_t key = this->willReadDstColor() ? 0x1 : 0x0;
if (this->getDstTexture() &&
kTopLeft_GrSurfaceOrigin == this->getDstTexture()->origin()) {
key |= 0x2;
if (key) {
if (this->getDstTexture()) {
key |= 0x2;
}
if (kTopLeft_GrSurfaceOrigin == this->getDstTexture()->origin()) {
key |= 0x4;
}
if (this->readsCoverage()) {
key |= 0x8;
}
}
b->add32(key);
this->onGetGLProcessorKey(caps, b);

View File

@ -27,8 +27,6 @@ public:
GrGLXferProcessor* createGLInstance() const override;
bool hasSecondaryOutput() const override { return false; }
bool invertCoverage() const { return fInvertCoverage; }
private:
@ -72,7 +70,7 @@ public:
};
private:
void onEmitCode(const EmitArgs& args) override {
void emitOutputsForBlendState(const EmitArgs& args) override {
const CoverageSetOpXP& xp = args.fXP.cast<CoverageSetOpXP>();
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();

View File

@ -535,8 +535,6 @@ public:
GrGLXferProcessor* createGLInstance() const override;
bool hasSecondaryOutput() const override { return false; }
SkXfermode::Mode mode() const { return fMode; }
bool hasHWBlendEquation() const { return -1 != static_cast<int>(fHWBlendEquation); }
@ -587,48 +585,46 @@ public:
static void GenKey(const GrXferProcessor& p, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {
const CustomXP& xp = p.cast<CustomXP>();
uint32_t key = xp.numTextures();
SkASSERT(key <= 1);
key |= xp.readsCoverage() << 1;
uint32_t key = 0;
if (xp.hasHWBlendEquation()) {
SkASSERT(caps.advBlendEqInteraction() > 0); // 0 will mean !xp.hasHWBlendEquation().
key |= caps.advBlendEqInteraction() << 2;
key |= caps.advBlendEqInteraction();
key |= xp.readsCoverage() << 2;
GR_STATIC_ASSERT(GrGLSLCaps::kLast_AdvBlendEqInteraction < 4);
}
if (!xp.hasHWBlendEquation() || caps.mustEnableSpecificAdvBlendEqs()) {
GR_STATIC_ASSERT(GrGLSLCaps::kLast_AdvBlendEqInteraction < 4);
key |= xp.mode() << 4;
key |= xp.mode() << 3;
}
b->add32(key);
}
private:
void onEmitCode(const EmitArgs& args) override {
void emitOutputsForBlendState(const EmitArgs& args) override {
const CustomXP& xp = args.fXP.cast<CustomXP>();
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
SkASSERT(xp.hasHWBlendEquation());
if (xp.hasHWBlendEquation()) {
// The blend mode will be implemented in hardware; only output the src color.
fsBuilder->enableAdvancedBlendEquationIfNeeded(xp.hwBlendEquation());
if (xp.readsCoverage()) {
// Do coverage modulation by multiplying it into the src color before blending.
// (See getOptimizations())
fsBuilder->codeAppendf("%s = %s * %s;",
args.fOutputPrimary, args.fInputCoverage, args.fInputColor);
} else {
fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor);
}
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
fsBuilder->enableAdvancedBlendEquationIfNeeded(xp.hwBlendEquation());
// Apply coverage by multiplying it into the src color before blending.
// (See onGetOptimizations())
if (xp.readsCoverage()) {
fsBuilder->codeAppendf("%s = %s * %s;",
args.fOutputPrimary, args.fInputCoverage, args.fInputColor);
} else {
const char* dstColor = fsBuilder->dstColor();
emit_custom_xfermode_code(xp.mode(), fsBuilder, args.fOutputPrimary, args.fInputColor,
dstColor);
if (xp.readsCoverage()) {
fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
args.fOutputPrimary, args.fOutputPrimary,
args.fInputCoverage, args.fInputCoverage, dstColor);
}
fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor);
}
}
void emitBlendCodeForDstRead(GrGLXPBuilder* pb, const char* srcColor, const char* dstColor,
const char* outColor, const GrXferProcessor& proc) override {
const CustomXP& xp = proc.cast<CustomXP>();
SkASSERT(!xp.hasHWBlendEquation());
GrGLXPFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder();
emit_custom_xfermode_code(xp.mode(), fsBuilder, outColor, srcColor, dstColor);
}
void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
typedef GrGLFragmentProcessor INHERITED;
@ -755,7 +751,7 @@ GrXferProcessor::OptFlags CustomXP::onGetOptimizations(const GrProcOptInfo& colo
if (colorPOI.allStagesMultiplyInput()) {
flags |= kCanTweakAlphaForCoverage_OptFlag;
}
if (coveragePOI.isSolidWhite()) {
if (this->hasHWBlendEquation() && coveragePOI.isSolidWhite()) {
flags |= kIgnoreCoverage_OptFlag;
}
return flags;

View File

@ -27,8 +27,6 @@ public:
GrGLXferProcessor* createGLInstance() const override;
bool hasSecondaryOutput() const override { return false; }
private:
DisableColorXP();
@ -62,7 +60,7 @@ public:
static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) {}
private:
void onEmitCode(const EmitArgs& args) override {
void emitOutputsForBlendState(const EmitArgs& args) override {
// This emit code should be empty. However, on the nexus 6 there is a driver bug where if
// you do not give gl_FragColor a value, the gl context is lost and we end up drawing
// nothing. So this fix just sets the gl_FragColor arbitrarily to 0.

View File

@ -306,7 +306,6 @@ public:
}
const char* name() const override { return "Porter Duff"; }
bool hasSecondaryOutput() const override { return fBlendFormula.hasSecondaryOutput(); }
GrGLXferProcessor* createGLInstance() const override;
@ -325,6 +324,8 @@ private:
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;
@ -393,7 +394,7 @@ public:
};
private:
void onEmitCode(const EmitArgs& args) override {
void emitOutputsForBlendState(const EmitArgs& args) override {
const PorterDuffXferProcessor& xp = args.fXP.cast<PorterDuffXferProcessor>();
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
@ -459,7 +460,6 @@ public:
}
const char* name() const override { return "Porter Duff Shader"; }
bool hasSecondaryOutput() const override { return false; }
GrGLXferProcessor* createGLInstance() const override;
@ -543,29 +543,22 @@ public:
}
private:
void onEmitCode(const EmitArgs& args) override {
const ShaderPDXferProcessor& xp = args.fXP.cast<ShaderPDXferProcessor>();
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
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);
const char* dstColor = fsBuilder->dstColor();
fsBuilder->codeAppend("vec4 colorBlend =");
fsBuilder->codeAppendf("%s =", outColor);
// append src blend
bool didAppend = append_porterduff_term(fsBuilder, srcCoeff,
args.fInputColor, args.fInputColor,
dstColor, false);
bool didAppend = append_porterduff_term(fsBuilder, srcCoeff, srcColor, srcColor, dstColor,
false);
// append dst blend
SkAssertResult(append_porterduff_term(fsBuilder, dstCoeff,
dstColor, args.fInputColor,
dstColor, didAppend));
SkAssertResult(append_porterduff_term(fsBuilder, dstCoeff, dstColor, srcColor, dstColor,
didAppend));
fsBuilder->codeAppend(";");
fsBuilder->codeAppendf("%s = %s * colorBlend + (vec4(1.0) - %s) * %s;",
args.fOutputPrimary, args.fInputCoverage, args.fInputCoverage,
dstColor);
}
void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
@ -596,8 +589,6 @@ public:
GrGLXferProcessor* createGLInstance() const override;
bool hasSecondaryOutput() const override { return false; }
private:
PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha);
@ -642,7 +633,7 @@ public:
GrProcessorKeyBuilder* b) {}
private:
void onEmitCode(const EmitArgs& args) override {
void emitOutputsForBlendState(const EmitArgs& args) override {
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor,

View File

@ -12,11 +12,17 @@
#include "gl/builders/GrGLProgramBuilder.h"
void GrGLXferProcessor::emitCode(const EmitArgs& args) {
if (!args.fXP.willReadDstColor()) {
this->emitOutputsForBlendState(args);
return;
}
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
const char* dstColor = fsBuilder->dstColor();
if (args.fXP.getDstTexture()) {
bool topDown = kTopLeft_GrSurfaceOrigin == args.fXP.getDstTexture()->origin();
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
if (args.fXP.readsCoverage()) {
// We don't think any shaders actually output negative coverage, but just as a safety
// check for floating point precision errors we compare with <= here
@ -25,8 +31,6 @@ void GrGLXferProcessor::emitCode(const EmitArgs& args) {
"}", args.fInputCoverage);
}
const char* dstColor = fsBuilder->dstColor();
const char* dstTopLeftName;
const char* dstCoordScaleName;
@ -55,7 +59,15 @@ void GrGLXferProcessor::emitCode(const EmitArgs& args) {
fsBuilder->codeAppend(";");
}
this->onEmitCode(args);
this->emitBlendCodeForDstRead(args.fPB, args.fInputColor, dstColor, args.fOutputPrimary,
args.fXP);
// Apply coverage.
if (args.fXP.readsCoverage()) {
fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
args.fOutputPrimary, args.fInputCoverage,
args.fOutputPrimary, args.fInputCoverage, dstColor);
}
}
void GrGLXferProcessor::setData(const GrGLProgramDataManager& pdm, const GrXferProcessor& xp) {

View File

@ -59,7 +59,24 @@ public:
void setData(const GrGLProgramDataManager& pdm, const GrXferProcessor& xp);
private:
virtual void onEmitCode(const EmitArgs&) = 0;
/**
* Called by emitCode() when the XP will not be performing a dst read. This method is
* responsible for both blending and coverage. A subclass only needs to implement this method if
* it can construct a GrXferProcessor that will not read the dst color.
*/
virtual void emitOutputsForBlendState(const EmitArgs&) {
SkFAIL("emitOutputsForBlendState not implemented.");
}
/**
* Called by emitCode() when the XP will perform a dst read. This method only needs to supply
* the blending logic. The base class applies coverage. A subclass only needs to implement this
* method if it can construct a GrXferProcessor that reads the dst color.
*/
virtual void emitBlendCodeForDstRead(GrGLXPBuilder*, const char* srcColor, const char* dstColor,
const char* outColor, const GrXferProcessor&) {
SkFAIL("emitBlendCodeForDstRead not implemented.");
}
virtual void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) = 0;