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:
parent
c31af44336
commit
edbb31f7dd
@ -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 {}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user