Implement support for KHR_blend_equation_advanced

Uses KHR(or NV)_blend_equation_advanced to implement custom Xfer modes
in hardware.

BUG=skia:

Review URL: https://codereview.chromium.org/1037123003
This commit is contained in:
cdalton 2015-05-06 13:40:21 -07:00 committed by Commit bot
parent e0cab96599
commit 8917d62ef4
13 changed files with 545 additions and 51 deletions

View File

@ -19,6 +19,43 @@ class GrGLSLCaps;
class GrGLXferProcessor;
class GrProcOptInfo;
/**
* Equations for alpha-blending.
*/
enum GrBlendEquation {
kInvalid_GrBlendEquation = -1,
// Basic blend equations.
kAdd_GrBlendEquation, //<! Cs*S + Cd*D
kSubtract_GrBlendEquation, //<! Cs*S - Cd*D
kReverseSubtract_GrBlendEquation, //<! Cd*D - Cs*S
// Advanced blend equations. These are described in the SVG and PDF specs.
kScreen_GrBlendEquation,
kOverlay_GrBlendEquation,
kDarken_GrBlendEquation,
kLighten_GrBlendEquation,
kColorDodge_GrBlendEquation,
kColorBurn_GrBlendEquation,
kHardLight_GrBlendEquation,
kSoftLight_GrBlendEquation,
kDifference_GrBlendEquation,
kExclusion_GrBlendEquation,
kMultiply_GrBlendEquation,
kHSLHue_GrBlendEquation,
kHSLSaturation_GrBlendEquation,
kHSLColor_GrBlendEquation,
kHSLLuminosity_GrBlendEquation,
kTotalGrBlendEquationCount,
kFirstAdvancedGrBlendEquation = kScreen_GrBlendEquation
};
inline bool GrBlendEquationIsAdvanced(GrBlendEquation equation) {
return equation >= kFirstAdvancedGrBlendEquation;
}
/**
* Coeffecients for alpha-blending.
*/
@ -53,6 +90,7 @@ enum GrBlendCoeff {
*/
enum GrXferBarrierType {
kTexture_GrXferBarrierType, //<! Required when a shader reads and renders to the same texture.
kBlend_GrXferBarrierType, //<! Required by certain blend extensions.
};
/**
@ -142,16 +180,18 @@ public:
struct BlendInfo {
void reset() {
fEquation = kAdd_GrBlendEquation;
fSrcBlend = kOne_GrBlendCoeff;
fDstBlend = kZero_GrBlendCoeff;
fBlendConstant = 0;
fWriteColor = true;
}
GrBlendCoeff fSrcBlend;
GrBlendCoeff fDstBlend;
GrColor fBlendConstant;
bool fWriteColor;
GrBlendEquation fEquation;
GrBlendCoeff fSrcBlend;
GrBlendCoeff fDstBlend;
GrColor fBlendConstant;
bool fWriteColor;
};
void getBlendInfo(BlendInfo* blendInfo) const {
@ -218,6 +258,16 @@ private:
virtual void onGetGLProcessorKey(const GrGLSLCaps& caps,
GrProcessorKeyBuilder* b) const = 0;
/**
* If not using a texture barrier, retrieves whether the subclass will require a different type
* of barrier.
*/
virtual bool onWillNeedXferBarrier(const GrRenderTarget*,
const GrDrawTargetCaps&,
GrXferBarrierType* outBarrierType SK_UNUSED) const {
return false;
}
/**
* 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

View File

@ -638,6 +638,7 @@ void GrDrawTargetCaps::reset() {
fUseDrawInsteadOfClear = false;
fBlendEquationSupport = kBasic_BlendEquationSupport;
fMapBufferFlags = kNone_MapFlags;
fMaxRenderTargetSize = 0;
@ -662,6 +663,7 @@ GrDrawTargetCaps& GrDrawTargetCaps::operator=(const GrDrawTargetCaps& other) {
fUseDrawInsteadOfClear = other.fUseDrawInsteadOfClear;
fBlendEquationSupport = other.fBlendEquationSupport;
fMapBufferFlags = other.fMapBufferFlags;
fMaxRenderTargetSize = other.fMaxRenderTargetSize;
@ -713,6 +715,18 @@ SkString GrDrawTargetCaps::dump() const {
r.appendf("Max Render Target Size : %d\n", fMaxRenderTargetSize);
r.appendf("Max Sample Count : %d\n", fMaxSampleCount);
static const char* kBlendEquationSupportNames[] = {
"Basic",
"Advanced",
"Advanced Coherent",
};
GR_STATIC_ASSERT(0 == kBasic_BlendEquationSupport);
GR_STATIC_ASSERT(1 == kAdvanced_BlendEquationSupport);
GR_STATIC_ASSERT(2 == kAdvancedCoherent_BlendEquationSupport);
GR_STATIC_ASSERT(SK_ARRAY_COUNT(kBlendEquationSupportNames) == kLast_BlendEquationSupport + 1);
r.appendf("Blend Equation Support : %s\n",
kBlendEquationSupportNames[fBlendEquationSupport]);
r.appendf("Map Buffer Support : %s\n",
map_flags_to_string(fMapBufferFlags).c_str());

View File

@ -141,6 +141,30 @@ public:
bool useDrawInsteadOfClear() const { return fUseDrawInsteadOfClear; }
/**
* Indicates the capabilities of the fixed function blend unit.
*/
enum BlendEquationSupport {
kBasic_BlendEquationSupport, //<! Support to select the operator that
// combines src and dst terms.
kAdvanced_BlendEquationSupport, //<! Additional fixed function support for specific
// SVG/PDF blend modes. Requires blend barriers.
kAdvancedCoherent_BlendEquationSupport, //<! Advanced blend equation support that does not
// require blend barriers, and permits overlap.
kLast_BlendEquationSupport = kAdvancedCoherent_BlendEquationSupport
};
BlendEquationSupport blendEquationSupport() const { return fBlendEquationSupport; }
bool advancedBlendEquationSupport() const {
return fBlendEquationSupport >= kAdvanced_BlendEquationSupport;
}
bool advancedCoherentBlendEquationSupport() const {
return kAdvancedCoherent_BlendEquationSupport == fBlendEquationSupport;
}
/**
* Indicates whether GPU->CPU memory mapping for GPU resources such as vertex buffers and
* textures allows partial mappings or full mappings.
@ -192,6 +216,7 @@ protected:
// Driver workaround
bool fUseDrawInsteadOfClear : 1;
BlendEquationSupport fBlendEquationSupport;
uint32_t fMapBufferFlags;
int fMaxRenderTargetSize;

View File

@ -42,7 +42,7 @@ bool GrXferProcessor::willNeedXferBarrier(const GrRenderTarget* rt,
*outBarrierType = kTexture_GrXferBarrierType;
return true;
}
return false;
return this->onWillNeedXferBarrier(rt, caps, outBarrierType);
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -17,6 +17,7 @@
#include "GrTextureAccess.h"
#include "SkXfermode.h"
#include "gl/GrGLCaps.h"
#include "gl/GrGLGpu.h"
#include "gl/GrGLProcessor.h"
#include "gl/GrGLProgramDataManager.h"
#include "gl/builders/GrGLProgramBuilder.h"
@ -29,6 +30,27 @@ bool GrCustomXfermode::IsSupportedMode(SkXfermode::Mode mode) {
// Static helpers
///////////////////////////////////////////////////////////////////////////////
static GrBlendEquation hw_blend_equation(SkXfermode::Mode mode) {
enum { kOffset = kOverlay_GrBlendEquation - SkXfermode::kOverlay_Mode };
return static_cast<GrBlendEquation>(mode + kOffset);
GR_STATIC_ASSERT(kOverlay_GrBlendEquation == SkXfermode::kOverlay_Mode + kOffset);
GR_STATIC_ASSERT(kDarken_GrBlendEquation == SkXfermode::kDarken_Mode + kOffset);
GR_STATIC_ASSERT(kLighten_GrBlendEquation == SkXfermode::kLighten_Mode + kOffset);
GR_STATIC_ASSERT(kColorDodge_GrBlendEquation == SkXfermode::kColorDodge_Mode + kOffset);
GR_STATIC_ASSERT(kColorBurn_GrBlendEquation == SkXfermode::kColorBurn_Mode + kOffset);
GR_STATIC_ASSERT(kHardLight_GrBlendEquation == SkXfermode::kHardLight_Mode + kOffset);
GR_STATIC_ASSERT(kSoftLight_GrBlendEquation == SkXfermode::kSoftLight_Mode + kOffset);
GR_STATIC_ASSERT(kDifference_GrBlendEquation == SkXfermode::kDifference_Mode + kOffset);
GR_STATIC_ASSERT(kExclusion_GrBlendEquation == SkXfermode::kExclusion_Mode + kOffset);
GR_STATIC_ASSERT(kMultiply_GrBlendEquation == SkXfermode::kMultiply_Mode + kOffset);
GR_STATIC_ASSERT(kHSLHue_GrBlendEquation == SkXfermode::kHue_Mode + kOffset);
GR_STATIC_ASSERT(kHSLSaturation_GrBlendEquation == SkXfermode::kSaturation_Mode + kOffset);
GR_STATIC_ASSERT(kHSLColor_GrBlendEquation == SkXfermode::kColor_Mode + kOffset);
GR_STATIC_ASSERT(kHSLLuminosity_GrBlendEquation == SkXfermode::kLuminosity_Mode + kOffset);
GR_STATIC_ASSERT(kTotalGrBlendEquationCount == SkXfermode::kLastMode + 1 + kOffset);
}
static void hard_light(GrGLFragmentBuilder* fsBuilder,
const char* final,
const char* src,
@ -510,15 +532,30 @@ public:
const GrDrawTargetCaps& caps) override;
SkXfermode::Mode mode() const { return fMode; }
bool hasCoverage() const { return fHasCoverage; }
bool hasHWBlendEquation() const { return kInvalid_GrBlendEquation != fHWBlendEquation; }
GrBlendEquation hwBlendEquation() const {
SkASSERT(this->hasHWBlendEquation());
return fHWBlendEquation;
}
private:
CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
bool onWillNeedXferBarrier(const GrRenderTarget* rt,
const GrDrawTargetCaps& caps,
GrXferBarrierType* outBarrierType) const override;
void onGetBlendInfo(BlendInfo*) const override;
bool onIsEqual(const GrXferProcessor& xpBase) const override;
SkXfermode::Mode fMode;
bool fHasCoverage;
GrBlendEquation fHWBlendEquation;
typedef GrXferProcessor INHERITED;
};
@ -540,24 +577,48 @@ public:
GLCustomXP(const GrXferProcessor&) {}
~GLCustomXP() override {}
static void GenKey(const GrXferProcessor& proc, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
uint32_t key = proc.numTextures();
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 |= proc.cast<CustomXP>().mode() << 1;
key |= xp.hasCoverage() << 1;
if (xp.hasHWBlendEquation()) {
SkASSERT(caps.advBlendEqInteraction() > 0); // 0 will mean !xp.hasHWBlendEquation().
key |= caps.advBlendEqInteraction() << 2;
}
if (!xp.hasHWBlendEquation() || caps.mustEnableSpecificAdvBlendEqs()) {
GR_STATIC_ASSERT(GrGLSLCaps::kLast_AdvBlendEqInteraction < 4);
key |= xp.mode() << 4;
}
b->add32(key);
}
private:
void onEmitCode(const EmitArgs& args) override {
SkXfermode::Mode mode = args.fXP.cast<CustomXP>().mode();
const CustomXP& xp = args.fXP.cast<CustomXP>();
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
const char* dstColor = fsBuilder->dstColor();
emit_custom_xfermode_code(mode, fsBuilder, args.fOutputPrimary, args.fInputColor, dstColor);
fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
args.fOutputPrimary, args.fOutputPrimary, args.fInputCoverage,
args.fInputCoverage, dstColor);
if (xp.hasHWBlendEquation()) {
// The blend mode will be implemented in hardware; only output the src color.
fsBuilder->enableAdvancedBlendEquationIfNeeded(xp.hwBlendEquation());
if (xp.hasCoverage()) {
// 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);
}
} else {
const char* dstColor = fsBuilder->dstColor();
emit_custom_xfermode_code(xp.mode(), fsBuilder, args.fOutputPrimary, args.fInputColor,
dstColor);
if (xp.hasCoverage()) {
fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
args.fOutputPrimary, args.fOutputPrimary,
args.fInputCoverage, args.fInputCoverage, dstColor);
}
}
}
void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
@ -569,7 +630,10 @@ private:
CustomXP::CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy,
bool willReadDstColor)
: INHERITED(dstCopy, willReadDstColor), fMode(mode) {
: INHERITED(dstCopy, willReadDstColor),
fMode(mode),
fHasCoverage(true),
fHWBlendEquation(kInvalid_GrBlendEquation) {
this->initClassID<CustomXP>();
}
@ -578,12 +642,15 @@ void CustomXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder
}
GrGLXferProcessor* CustomXP::createGLInstance() const {
SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
return SkNEW_ARGS(GLCustomXP, (*this));
}
bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
const CustomXP& s = other.cast<CustomXP>();
return fMode == s.fMode;
return fMode == s.fMode &&
fHasCoverage == s.fHasCoverage &&
fHWBlendEquation == s.fHWBlendEquation;
}
GrXferProcessor::OptFlags CustomXP::getOptimizations(const GrProcOptInfo& colorPOI,
@ -591,7 +658,131 @@ GrXferProcessor::OptFlags CustomXP::getOptimizations(const GrProcOptInfo& colorP
bool doesStencilWrite,
GrColor* overrideColor,
const GrDrawTargetCaps& caps) {
return GrXferProcessor::kNone_Opt;
/*
Most the optimizations we do here are based on tweaking alpha for coverage.
The general SVG blend equation is defined in the spec as follows:
Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
Da' = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa)
(Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha,
and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied
RGB colors.)
For every blend mode supported by this class, i.e. the "advanced" blend
modes, X=Y=Z=1 and this equation reduces to the PDF blend equation.
It can be shown that when X=Y=Z=1, these equations can modulate alpha for
coverage.
== Color ==
We substitute Y=Z=1 and define a blend() function that calculates Dca' in
terms of premultiplied alpha only:
blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0,
Sca : if Da == 0,
B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if Sa,Da != 0}
And for coverage modulation, we use a post blend src-over model:
Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
(Where f is the fractional coverage.)
Next we show that canTweakAlphaForCoverage() is true by proving the
following relationship:
blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
General case (f,Sa,Da != 0):
f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
= f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca [Sa,Da != 0, definition of blend()]
= B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca
= B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca
= B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca
= B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca
= B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)
= B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) [f!=0]
= blend(f*Sca, Dca, f*Sa, Da) [definition of blend()]
Corner cases (Sa=0, Da=0, and f=0):
Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
= f * Dca + (1-f) * Dca [Sa=0, definition of blend()]
= Dca
= blend(0, Dca, 0, Da) [definition of blend()]
= blend(f*Sca, Dca, f*Sa, Da) [Sa=0]
Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
= f * Sca + (1-f) * Dca [Da=0, definition of blend()]
= f * Sca [Da=0]
= blend(f*Sca, 0, f*Sa, 0) [definition of blend()]
= blend(f*Sca, Dca, f*Sa, Da) [Da=0]
f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
= Dca [f=0]
= blend(0, Dca, 0, Da) [definition of blend()]
= blend(f*Sca, Dca, f*Sa, Da) [f=0]
== Alpha ==
We substitute X=Y=Z=1 and define a blend() function that calculates Da':
blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa)
= Sa * Da + Sa - Sa * Da + Da - Da * Sa
= Sa + Da - Sa * Da
We use the same model for coverage modulation as we did with color:
Da'' = f * blend(Sa, Da) + (1-f) * Da
And show that canTweakAlphaForCoverage() is true by proving the following
relationship:
blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da
f * blend(Sa, Da) + (1-f) * Da
= f * (Sa + Da - Sa * Da) + (1-f) * Da
= f*Sa + f*Da - f*Sa * Da + Da - f*Da
= f*Sa - f*Sa * Da + Da
= f*Sa + Da - f*Sa * Da
= blend(f*Sa, Da)
*/
OptFlags flags = kNone_Opt;
if (colorPOI.allStagesMultiplyInput()) {
flags = flags | kCanTweakAlphaForCoverage_OptFlag;
}
if (coveragePOI.isSolidWhite()) {
flags = flags | kIgnoreCoverage_OptFlag;
fHasCoverage = false;
}
if (caps.advancedBlendEquationSupport() && !coveragePOI.isFourChannelOutput()) {
// This blend mode can be implemented in hardware.
fHWBlendEquation = hw_blend_equation(fMode);
}
return flags;
}
bool CustomXP::onWillNeedXferBarrier(const GrRenderTarget* rt,
const GrDrawTargetCaps& caps,
GrXferBarrierType* outBarrierType) const {
if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
*outBarrierType = kBlend_GrXferBarrierType;
return true;
}
return false;
}
void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
if (this->hasHWBlendEquation()) {
blendInfo->fEquation = this->hwBlendEquation();
}
}
///////////////////////////////////////////////////////////////////////////////
@ -609,6 +800,19 @@ GrCustomXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
return CustomXP::Create(fMode, dstCopy, this->willReadDstColor(caps, colorPOI, coveragePOI));
}
bool GrCustomXPFactory::willReadDstColor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI) const {
if (!caps.advancedBlendEquationSupport()) {
// No hardware support for advanced blend equations; we will need to do it in the shader.
return true;
}
if (coveragePOI.isFourChannelOutput()) {
// Advanced blend equations can't tweak alpha for RGB coverage.
return true;
}
return false;
}
void GrCustomXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,

View File

@ -75,9 +75,7 @@ private:
bool willReadDstColor(const GrDrawTargetCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI) const override {
return true;
}
const GrProcOptInfo& coveragePOI) const override;
bool onIsEqual(const GrXPFactory& xpfBase) const override {
const GrCustomXPFactory& xpf = xpfBase.cast<GrCustomXPFactory>();

View File

@ -269,6 +269,21 @@ bool GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
fStencilWrapOpsSupport = true;
}
if (kIntel_GrGLVendor != ctxInfo.vendor()) {
if (ctxInfo.hasExtension("GL_KHR_blend_equation_advanced_coherent") ||
ctxInfo.hasExtension("GL_NV_blend_equation_advanced_coherent")) {
fBlendEquationSupport = kAdvancedCoherent_BlendEquationSupport;
} else if (ctxInfo.hasExtension("GL_KHR_blend_equation_advanced") ||
ctxInfo.hasExtension("GL_NV_blend_equation_advanced")) {
fBlendEquationSupport = kAdvanced_BlendEquationSupport;
} else {
fBlendEquationSupport = kBasic_BlendEquationSupport;
}
} else {
// On Intel platforms, KHR_blend_equation_advanced is not conformant.
fBlendEquationSupport = kBasic_BlendEquationSupport;
}
if (kGL_GrGLStandard == standard) {
fMapBufferFlags = kCanMap_MapFlag; // we require VBO support and the desktop VBO
// extension includes glMapBuffer.
@ -352,7 +367,7 @@ bool GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
this->initConfigTexturableTable(ctxInfo, gli);
this->initConfigRenderableTable(ctxInfo);
reinterpret_cast<GrGLSLCaps*>(fShaderCaps.get())->init(ctxInfo, gli);
reinterpret_cast<GrGLSLCaps*>(fShaderCaps.get())->init(ctxInfo, gli, *this);
return true;
}
@ -914,6 +929,7 @@ void GrGLSLCaps::reset() {
fDropsTileOnZeroDivide = false;
fFBFetchSupport = false;
fFBFetchNeedsCustomOutput = false;
fAdvBlendEqInteraction = kNotSupported_AdvBlendEqInteraction;
fFBFetchColorName = NULL;
fFBFetchExtensionString = NULL;
}
@ -927,13 +943,16 @@ GrGLSLCaps& GrGLSLCaps::operator= (const GrGLSLCaps& caps) {
fDropsTileOnZeroDivide = caps.fDropsTileOnZeroDivide;
fFBFetchSupport = caps.fFBFetchSupport;
fFBFetchNeedsCustomOutput = caps.fFBFetchNeedsCustomOutput;
fAdvBlendEqInteraction = caps.fAdvBlendEqInteraction;
fFBFetchColorName = caps.fFBFetchColorName;
fFBFetchExtensionString = caps.fFBFetchExtensionString;
return *this;
}
bool GrGLSLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
bool GrGLSLCaps::init(const GrGLContextInfo& ctxInfo,
const GrGLInterface* gli,
const GrGLCaps& glCaps) {
this->reset();
if (!ctxInfo.isInitialized()) {
return false;
@ -1010,6 +1029,18 @@ bool GrGLSLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli)
ctxInfo.hasExtension("GL_OES_standard_derivatives");
}
if (glCaps.advancedBlendEquationSupport()) {
bool coherent = glCaps.advancedCoherentBlendEquationSupport();
if (ctxInfo.hasExtension(coherent ? "GL_NV_blend_equation_advanced_coherent"
: "GL_NV_blend_equation_advanced")) {
fAdvBlendEqInteraction = kAutomatic_AdvBlendEqInteraction;
} else {
fAdvBlendEqInteraction = kGeneralEnable_AdvBlendEqInteraction;
// TODO: Use the following on any platform where "blend_support_all_equations" is slow.
//fAdvBlendEqInteraction = kSpecificEnables_AdvBlendEqInteraction;
}
}
this->initShaderPrecisionTable(ctxInfo, gli);
return true;
@ -1018,10 +1049,24 @@ bool GrGLSLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli)
SkString GrGLSLCaps::dump() const {
SkString r = INHERITED::dump();
static const char* kAdvBlendEqInteractionStr[] = {
"Not Supported",
"Automatic",
"General Enable",
"Specific Enables",
};
GR_STATIC_ASSERT(0 == kNotSupported_AdvBlendEqInteraction);
GR_STATIC_ASSERT(1 == kAutomatic_AdvBlendEqInteraction);
GR_STATIC_ASSERT(2 == kGeneralEnable_AdvBlendEqInteraction);
GR_STATIC_ASSERT(3 == kSpecificEnables_AdvBlendEqInteraction);
GR_STATIC_ASSERT(SK_ARRAY_COUNT(kAdvBlendEqInteractionStr) == kLast_AdvBlendEqInteraction + 1);
r.appendf("--- GLSL-Specific ---\n");
r.appendf("FB Fetch Support: %s\n", (fFBFetchSupport ? "YES" : "NO"));
r.appendf("Drops tile on zero divide: %s\n", (fDropsTileOnZeroDivide ? "YES" : "NO"));
r.appendf("Advanced blend equation interaction: %s\n",
kAdvBlendEqInteractionStr[fAdvBlendEqInteraction]);
return r;
}

View File

@ -379,6 +379,19 @@ class GrGLSLCaps : public GrShaderCaps {
public:
SK_DECLARE_INST_COUNT(GrGLSLCaps)
/**
* Indicates how GLSL must interact with advanced blend equations. The KHR extension requires
* special layout qualifiers in the fragment shader.
*/
enum AdvBlendEqInteraction {
kNotSupported_AdvBlendEqInteraction, //<! No _blend_equation_advanced extension
kAutomatic_AdvBlendEqInteraction, //<! No interaction required
kGeneralEnable_AdvBlendEqInteraction, //<! layout(blend_support_all_equations) out
kSpecificEnables_AdvBlendEqInteraction, //<! Specific layout qualifiers per equation
kLast_AdvBlendEqInteraction = kSpecificEnables_AdvBlendEqInteraction
};
/**
* Creates a GrGLSLCaps that advertises no support for any extensions,
* formats, etc. Call init to initialize from a GrGLContextInfo.
@ -399,7 +412,7 @@ public:
* Initializes the GrGLSLCaps to the set of features supported in the current
* OpenGL context accessible via ctxInfo.
*/
bool init(const GrGLContextInfo& ctxInfo, const GrGLInterface* glInterface);
bool init(const GrGLContextInfo&, const GrGLInterface*, const GrGLCaps&);
/**
* Some helper functions for encapsulating various extensions to read FB Buffer on openglES
@ -416,6 +429,16 @@ public:
bool dropsTileOnZeroDivide() const { return fDropsTileOnZeroDivide; }
AdvBlendEqInteraction advBlendEqInteraction() const { return fAdvBlendEqInteraction; }
bool mustEnableAdvBlendEqs() const {
return fAdvBlendEqInteraction >= kGeneralEnable_AdvBlendEqInteraction;
}
bool mustEnableSpecificAdvBlendEqs() const {
return fAdvBlendEqInteraction == kSpecificEnables_AdvBlendEqInteraction;
}
/**
* Returns a string containing the caps info.
*/
@ -432,6 +455,8 @@ private:
const char* fFBFetchColorName;
const char* fFBFetchExtensionString;
AdvBlendEqInteraction fAdvBlendEqInteraction;
typedef GrShaderCaps INHERITED;
};

View File

@ -39,6 +39,49 @@
///////////////////////////////////////////////////////////////////////////////
static const GrGLenum gXfermodeEquation2Blend[] = {
// Basic OpenGL blend equations.
GR_GL_FUNC_ADD,
GR_GL_FUNC_SUBTRACT,
GR_GL_FUNC_REVERSE_SUBTRACT,
// GL_KHR_blend_equation_advanced.
GR_GL_SCREEN,
GR_GL_OVERLAY,
GR_GL_DARKEN,
GR_GL_LIGHTEN,
GR_GL_COLORDODGE,
GR_GL_COLORBURN,
GR_GL_HARDLIGHT,
GR_GL_SOFTLIGHT,
GR_GL_DIFFERENCE,
GR_GL_EXCLUSION,
GR_GL_MULTIPLY,
GR_GL_HSL_HUE,
GR_GL_HSL_SATURATION,
GR_GL_HSL_COLOR,
GR_GL_HSL_LUMINOSITY
};
GR_STATIC_ASSERT(0 == kAdd_GrBlendEquation);
GR_STATIC_ASSERT(1 == kSubtract_GrBlendEquation);
GR_STATIC_ASSERT(2 == kReverseSubtract_GrBlendEquation);
GR_STATIC_ASSERT(3 == kScreen_GrBlendEquation);
GR_STATIC_ASSERT(4 == kOverlay_GrBlendEquation);
GR_STATIC_ASSERT(5 == kDarken_GrBlendEquation);
GR_STATIC_ASSERT(6 == kLighten_GrBlendEquation);
GR_STATIC_ASSERT(7 == kColorDodge_GrBlendEquation);
GR_STATIC_ASSERT(8 == kColorBurn_GrBlendEquation);
GR_STATIC_ASSERT(9 == kHardLight_GrBlendEquation);
GR_STATIC_ASSERT(10 == kSoftLight_GrBlendEquation);
GR_STATIC_ASSERT(11 == kDifference_GrBlendEquation);
GR_STATIC_ASSERT(12 == kExclusion_GrBlendEquation);
GR_STATIC_ASSERT(13 == kMultiply_GrBlendEquation);
GR_STATIC_ASSERT(14 == kHSLHue_GrBlendEquation);
GR_STATIC_ASSERT(15 == kHSLSaturation_GrBlendEquation);
GR_STATIC_ASSERT(16 == kHSLColor_GrBlendEquation);
GR_STATIC_ASSERT(17 == kHSLLuminosity_GrBlendEquation);
GR_STATIC_ASSERT(18 == kTotalGrBlendEquationCount);
static const GrGLenum gXfermodeCoeff2Blend[] = {
GR_GL_ZERO,
GR_GL_ONE,
@ -2077,39 +2120,55 @@ void GrGLGpu::flushHWAAState(GrRenderTarget* rt, bool useHWAA) {
void GrGLGpu::flushBlend(const GrXferProcessor::BlendInfo& blendInfo) {
// Any optimization to disable blending should have already been applied and
// tweaked the coeffs to (1, 0).
// tweaked the equation to "add" or "subtract", and the coeffs to (1, 0).
GrBlendEquation equation = blendInfo.fEquation;
GrBlendCoeff srcCoeff = blendInfo.fSrcBlend;
GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
bool blendOff = kOne_GrBlendCoeff == srcCoeff && kZero_GrBlendCoeff == dstCoeff;
bool blendOff = (kAdd_GrBlendEquation == equation || kSubtract_GrBlendEquation == equation) &&
kOne_GrBlendCoeff == srcCoeff && kZero_GrBlendCoeff == dstCoeff;
if (blendOff) {
if (kNo_TriState != fHWBlendState.fEnabled) {
GL_CALL(Disable(GR_GL_BLEND));
fHWBlendState.fEnabled = kNo_TriState;
}
} else {
if (kYes_TriState != fHWBlendState.fEnabled) {
GL_CALL(Enable(GR_GL_BLEND));
fHWBlendState.fEnabled = kYes_TriState;
}
if (fHWBlendState.fSrcCoeff != srcCoeff ||
fHWBlendState.fDstCoeff != dstCoeff) {
GL_CALL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff],
gXfermodeCoeff2Blend[dstCoeff]));
fHWBlendState.fSrcCoeff = srcCoeff;
fHWBlendState.fDstCoeff = dstCoeff;
}
GrColor blendConst = blendInfo.fBlendConstant;
if ((BlendCoeffReferencesConstant(srcCoeff) ||
BlendCoeffReferencesConstant(dstCoeff)) &&
(!fHWBlendState.fConstColorValid ||
fHWBlendState.fConstColor != blendConst)) {
GrGLfloat c[4];
GrColorToRGBAFloat(blendConst, c);
GL_CALL(BlendColor(c[0], c[1], c[2], c[3]));
fHWBlendState.fConstColor = blendConst;
fHWBlendState.fConstColorValid = true;
}
return;
}
if (kYes_TriState != fHWBlendState.fEnabled) {
GL_CALL(Enable(GR_GL_BLEND));
fHWBlendState.fEnabled = kYes_TriState;
}
if (fHWBlendState.fEquation != equation) {
GL_CALL(BlendEquation(gXfermodeEquation2Blend[equation]));
fHWBlendState.fEquation = equation;
}
if (GrBlendEquationIsAdvanced(equation)) {
SkASSERT(this->caps()->advancedBlendEquationSupport());
// Advanced equations have no other blend state.
return;
}
if (fHWBlendState.fSrcCoeff != srcCoeff ||
fHWBlendState.fDstCoeff != dstCoeff) {
GL_CALL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff],
gXfermodeCoeff2Blend[dstCoeff]));
fHWBlendState.fSrcCoeff = srcCoeff;
fHWBlendState.fDstCoeff = dstCoeff;
}
GrColor blendConst = blendInfo.fBlendConstant;
if ((BlendCoeffReferencesConstant(srcCoeff) ||
BlendCoeffReferencesConstant(dstCoeff)) &&
(!fHWBlendState.fConstColorValid ||
fHWBlendState.fConstColor != blendConst)) {
GrGLfloat c[4];
GrColorToRGBAFloat(blendConst, c);
GL_CALL(BlendColor(c[0], c[1], c[2], c[3]));
fHWBlendState.fConstColor = blendConst;
fHWBlendState.fConstColorValid = true;
}
}
@ -2736,6 +2795,11 @@ void GrGLGpu::xferBarrier(GrXferBarrierType type) {
SkASSERT(this->caps()->textureBarrierSupport());
GL_CALL(TextureBarrier());
return;
case kBlend_GrXferBarrierType:
SkASSERT(GrDrawTargetCaps::kAdvanced_BlendEquationSupport ==
this->caps()->blendEquationSupport());
GL_CALL(BlendBarrier());
return;
}
}

View File

@ -438,6 +438,7 @@ private:
} fHWGeometryState;
struct {
GrBlendEquation fEquation;
GrBlendCoeff fSrcCoeff;
GrBlendCoeff fDstCoeff;
GrColor fConstColor;
@ -445,6 +446,7 @@ private:
TriState fEnabled;
void invalidate() {
fEquation = kInvalid_GrBlendEquation;
fSrcCoeff = kInvalid_GrBlendCoeff;
fDstCoeff = kInvalid_GrBlendCoeff;
fConstColorValid = false;

View File

@ -36,6 +36,47 @@ static void append_default_precision_qualifier(GrSLPrecision p,
}
}
static const char* specific_layout_qualifier_name(GrBlendEquation equation) {
SkASSERT(GrBlendEquationIsAdvanced(equation));
static const char* kLayoutQualifierNames[] = {
"blend_support_screen",
"blend_support_overlay",
"blend_support_darken",
"blend_support_lighten",
"blend_support_colordodge",
"blend_support_colorburn",
"blend_support_hardlight",
"blend_support_softlight",
"blend_support_difference",
"blend_support_exclusion",
"blend_support_multiply",
"blend_support_hsl_hue",
"blend_support_hsl_saturation",
"blend_support_hsl_color",
"blend_support_hsl_luminosity"
};
return kLayoutQualifierNames[equation - kFirstAdvancedGrBlendEquation];
GR_STATIC_ASSERT(0 == kScreen_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(1 == kOverlay_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(2 == kDarken_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(3 == kLighten_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(4 == kColorDodge_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(5 == kColorBurn_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(6 == kHardLight_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(7 == kSoftLight_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(8 == kDifference_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(9 == kExclusion_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(10 == kMultiply_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(11 == kHSLHue_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(12 == kHSLSaturation_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(13 == kHSLColor_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(14 == kHSLLuminosity_GrBlendEquation - kFirstAdvancedGrBlendEquation);
GR_STATIC_ASSERT(SK_ARRAY_COUNT(kLayoutQualifierNames) ==
kTotalGrBlendEquationCount - kFirstAdvancedGrBlendEquation);
}
GrGLFragmentShaderBuilder::DstReadKey
GrGLFragmentShaderBuilder::KeyForDstRead(const GrTexture* dstCopy, const GrGLCaps& caps) {
uint32_t key = kYesDstRead_DstReadKeyBit;
@ -183,6 +224,23 @@ const char* GrGLFragmentShaderBuilder::dstColor() {
}
}
void GrGLFragmentShaderBuilder::enableAdvancedBlendEquationIfNeeded(GrBlendEquation equation) {
SkASSERT(GrBlendEquationIsAdvanced(equation));
const GrGLSLCaps& caps = *fProgramBuilder->gpu()->glCaps().glslCaps();
if (!caps.mustEnableAdvBlendEqs()) {
return;
}
this->addFeature(1 << kBlendEquationAdvanced_GLSLPrivateFeature,
"GL_KHR_blend_equation_advanced");
if (caps.mustEnableSpecificAdvBlendEqs()) {
this->addLayoutQualifier(specific_layout_qualifier_name(equation), kOut_InterfaceQualifier);
} else {
this->addLayoutQualifier("blend_support_all_equations", kOut_InterfaceQualifier);
}
}
void GrGLFragmentShaderBuilder::enableCustomOutput() {
if (!fHasCustomColorOutput) {
fHasCustomColorOutput = true;

View File

@ -66,6 +66,11 @@ public:
no effect advertised that it will read the destination. */
virtual const char* dstColor() = 0;
/** Adds any necessary layout qualifiers in order to legalize the supplied blend equation with
this shader. It is only legal to call this method with an advanced blend equation, and only
if these equations are supported. */
virtual void enableAdvancedBlendEquationIfNeeded(GrBlendEquation) = 0;
private:
typedef GrGLFragmentBuilder INHERITED;
};
@ -95,6 +100,8 @@ public:
const char* fragmentPosition() override;
const char* dstColor() override;
void enableAdvancedBlendEquationIfNeeded(GrBlendEquation) override;
private:
// Private public interface, used by GrGLProgramBuilder to build a fragment shader
void enableCustomOutput();
@ -123,7 +130,8 @@ private:
*/
enum GLSLPrivateFeature {
kFragCoordConventions_GLSLPrivateFeature = kLastGLSLFeature + 1,
kLastGLSLPrivateFeature = kFragCoordConventions_GLSLPrivateFeature
kBlendEquationAdvanced_GLSLPrivateFeature,
kLastGLSLPrivateFeature = kBlendEquationAdvanced_GLSLPrivateFeature
};
// Interpretation of DstReadKey when generating code

View File

@ -173,7 +173,8 @@ void GrGLShaderBuilder::appendTextureLookup(const char* samplerName,
}
void GrGLShaderBuilder::addLayoutQualifier(const char* param, InterfaceQualifier interface) {
SkASSERT(fProgramBuilder->gpu()->glslGeneration() >= k330_GrGLSLGeneration);
SkASSERT(fProgramBuilder->gpu()->glslGeneration() >= k330_GrGLSLGeneration ||
fProgramBuilder->gpu()->glCaps().glslCaps()->mustEnableAdvBlendEqs());
fLayoutParams[interface].push_back() = param;
}