Add dual source blending support for proper blending with coverage.

Review URL: http://codereview.appspot.com/4535088/



git-svn-id: http://skia.googlecode.com/svn/trunk@1390 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bsalomon@google.com 2011-05-20 14:13:56 +00:00
parent 3c14d0f3d1
commit 271cffc77b
20 changed files with 556 additions and 88 deletions

View File

@ -453,7 +453,7 @@ public:
* @param srcCoef coeffecient applied to the src color.
* @param dstCoef coeffecient applied to the dst color.
*/
void setBlendFunc(GrBlendCoeff srcCoef, GrBlendCoeff dstCoef);
void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff);
/**
* Sets the blending function constant referenced by the following blending

View File

@ -69,6 +69,12 @@
/* GL_DST_ALPHA */
/* GL_ONE_MINUS_DST_ALPHA */
/* ExtendedBlendFactors */
#define GR_GL_SRC1_COLOR 0x88F9
#define GR_GL_ONE_MINUS_SRC1_COLOR 0x88FA
/* GL_SRC1_ALPHA */
#define GR_GL_ONE_MINUS_SRC1_ALPHA 0x88FB
/* BlendEquationSeparate */
#define GR_GL_FUNC_ADD 0x8006
#define GR_GL_BLEND_EQUATION 0x8009

View File

@ -63,6 +63,7 @@ typedef unsigned int GrGLenum;
typedef unsigned char GrGLboolean;
typedef unsigned int GrGLbitfield;
typedef signed char GrGLbyte;
typedef char GrGLchar;
typedef short GrGLshort;
typedef int GrGLint;
typedef int GrGLsizei;
@ -199,6 +200,9 @@ extern "C" {
// Buffer mapping (extension in ES).
typedef GrGLvoid* (GR_GL_FUNCTION_TYPE *GrGLMapBufferProc)(GrGLenum target, GrGLenum access);
typedef GrGLboolean (GR_GL_FUNCTION_TYPE *GrGLUnmapBufferProc)(GrGLenum target);
// Dual source blending
typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBindFragDataLocationIndexedProc)(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name);
} // extern "C"
/*
@ -333,6 +337,9 @@ struct GrGLInterface {
GrGLMapBufferProc fMapBuffer;
GrGLUnmapBufferProc fUnmapBuffer;
// Dual Source Blending
GrGLBindFragDataLocationIndexedProc fBindFragDataLocationIndexed;
// Code that initializes this struct using a static initializer should
// make this the last entry in the static initializer. It can help to guard
// against failing to initialize newly-added members of this struct.

View File

@ -59,6 +59,21 @@ struct GrGpuStats {
class GrGpu : public GrDrawTarget {
public:
/**
* Additional blend coeffecients for dual source blending, not exposed
* through GrPaint/GrContext.
*/
enum ExtendedBlendCoeffs {
// source 2 refers to second output color when
// using dual source blending.
kS2C_BlendCoeff = kPublicBlendCoeffCount,
kIS2C_BlendCoeff,
kS2A_BlendCoeff,
kIS2A_BlendCoeff,
kTotalBlendCoeffCount
};
/**
* Create an instance of GrGpu that matches the specified Engine backend.
* If the requested engine is not supported (at compile-time or run-time)
@ -189,6 +204,15 @@ public:
*/
bool supports4x4DownsampleFilter() const { return f4X4DownsampleFilterSupport; }
/**
* Does this instance support dual-source blending? Required for proper
* blending with partial coverage with certain blend modes (dst coeff is
* not 1, ISA, or ISC)
*/
bool supportsDualSourceBlending() const {
return fDualSourceBlendingSupport;
}
/**
* Gets the minimum width of a render target. If a texture/rt is created
* with a width less than this size the GrGpu object will clamp it to this
@ -371,6 +395,7 @@ protected:
bool fAALineSupport;
bool fFSAASupport;
bool f4X4DownsampleFilterSupport; // supports GrSamplerState::k4x4Downsample_Filter
bool fDualSourceBlendingSupport;
// set by subclass to true if index and vertex buffers can be locked, false
// otherwise.

View File

@ -232,7 +232,7 @@ enum GrBlendCoeff {
kConstA_BlendCoeff, //<! constant color alpha
kIConstA_BlendCoeff, //<! one minus constant color alpha
kBlendCoeffCount
kPublicBlendCoeffCount
};
/**

View File

@ -374,10 +374,34 @@ void GrDrawTarget::disableState(uint32_t bits) {
fCurrDrawState.fFlagBits &= ~(bits);
}
void GrDrawTarget::setBlendFunc(GrBlendCoeff srcCoef,
GrBlendCoeff dstCoef) {
fCurrDrawState.fSrcBlend = srcCoef;
fCurrDrawState.fDstBlend = dstCoef;
void GrDrawTarget::setBlendFunc(GrBlendCoeff srcCoeff,
GrBlendCoeff dstCoeff) {
fCurrDrawState.fSrcBlend = srcCoeff;
fCurrDrawState.fDstBlend = dstCoeff;
#if GR_DEBUG
switch (dstCoeff) {
case kDC_BlendCoeff:
case kIDC_BlendCoeff:
case kDA_BlendCoeff:
case kIDA_BlendCoeff:
GrPrintf("Unexpected dst blend coeff. Won't work correctly with"
"coverage stages.\n");
break;
default:
break;
}
switch (srcCoeff) {
case kSC_BlendCoeff:
case kISC_BlendCoeff:
case kSA_BlendCoeff:
case kISA_BlendCoeff:
GrPrintf("Unexpected src blend coeff. Won't work correctly with"
"coverage stages.\n");
break;
default:
break;
}
#endif
}
void GrDrawTarget::setColor(GrColor c) {
@ -482,10 +506,16 @@ void GrDrawTarget::setIndexSourceToBuffer(const GrIndexBuffer* buffer) {
///////////////////////////////////////////////////////////////////////////////
bool GrDrawTarget::canDisableBlend() const {
// If we're using edge antialiasing, we can't force blend off.
// If we compute a coverage value (using edge AA or a coverage stage) then
// we can't force blending off.
if (fCurrDrawState.fEdgeAANumEdges > 0) {
return false;
}
for (int s = fCurrDrawState.fFirstCoverageStage; s < kNumStages; ++s) {
if (this->isStageEnabled(s)) {
return false;
}
}
if ((kOne_BlendCoeff == fCurrDrawState.fSrcBlend) &&
(kZero_BlendCoeff == fCurrDrawState.fDstBlend)) {
@ -510,8 +540,8 @@ bool GrDrawTarget::canDisableBlend() const {
return false;
}
// ...and there isn't a texture with an alpha channel...
for (int s = 0; s < kNumStages; ++s) {
// ...and there isn't a texture stage with an alpha channel...
for (int s = 0; s < fCurrDrawState.fFirstCoverageStage; ++s) {
if (this->isStageEnabled(s)) {
GrAssert(NULL != fCurrDrawState.fTextures[s]);

View File

@ -333,6 +333,15 @@ bool GrGLInterface::validate(GrEngine engine) const {
}
}
// Dual source blending
if (kDesktop_GrGLBinding == fBindingsExported &&
(has_gl_extension_from_string("GL_ARB_blend_func_extended", ext) ||
(3 < major) || (3 == major && 3 <= minor))) {
if (NULL == fBindFragDataLocationIndexed) {
return false;
}
}
return true;
}

View File

@ -99,6 +99,9 @@ static inline const char* all_zeros_vec(int count) {
return ZEROSVEC[count];
}
static inline const char* declared_color_output_name() { return "fsColorOut"; }
static inline const char* dual_source_output_name() { return "dualSourceOut"; }
static void tex_matrix_name(int stage, GrStringBuilder* s) {
#if GR_GL_ATTRIBUTE_MATRICES
*s = "aTexM";
@ -144,12 +147,32 @@ GrGLProgram::GrGLProgram() {
GrGLProgram::~GrGLProgram() {
}
void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
GrBlendCoeff* dstCoeff) const {
switch (fProgramDesc.fDualSrcOutput) {
case ProgramDesc::kNone_DualSrcOutput:
break;
// the prog will write a coverage value to the secondary
// output and the dst is blended by one minus that value.
case ProgramDesc::kCoverage_DualSrcOutput:
case ProgramDesc::kCoverageISA_DualSrcOutput:
case ProgramDesc::kCoverageISC_DualSrcOutput:
*dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_BlendCoeff;
break;
default:
GrCrash("Unexpected dual source blend output");
break;
}
}
void GrGLProgram::buildKey(GrBinHashKeyBuilder& key) const {
// Add stage configuration to the key
key.keyData(reinterpret_cast<const uint8_t*>(&fProgramDesc), sizeof(ProgramDesc));
}
// assigns modulation of two vars to an output var
// vars can be vec4s or floats (or one of each)
// result is always vec4
// if either var is "" then assign to the other var
// if both are "" then assign all ones
static inline void modulate_helper(const char* outputVar,
@ -167,15 +190,17 @@ static inline void modulate_helper(const char* outputVar,
if (!has0 && !has1) {
code->appendf("\t%s = %s;\n", outputVar, all_ones_vec(4));
} else if (!has0) {
code->appendf("\t%s = %s;\n", outputVar, var1);
code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
} else if (!has1) {
code->appendf("\t%s = %s;\n", outputVar, var0);
code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
} else {
code->appendf("\t%s = %s * %s;\n", outputVar, var0, var1);
code->appendf("\t%s = vec4(%s * %s);\n", outputVar, var0, var1);
}
}
// assigns addition of two vars to an output var
// vars can be vec4s or floats (or one of each)
// result is always vec4
// if either var is "" then assign to the other var
// if both are "" then assign all zeros
static inline void add_helper(const char* outputVar,
@ -193,11 +218,11 @@ static inline void add_helper(const char* outputVar,
if (!has0 && !has1) {
code->appendf("\t%s = %s;\n", outputVar, all_zeros_vec(4));
} else if (!has0) {
code->appendf("\t%s = %s;\n", outputVar, var1);
code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
} else if (!has1) {
code->appendf("\t%s = %s;\n", outputVar, var0);
code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
} else {
code->appendf("\t%s = %s + %s;\n", outputVar, var0, var1);
code->appendf("\t%s = vec4(%s + %s);\n", outputVar, var0, var1);
}
}
@ -325,6 +350,21 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
needBlendInputs(uniformCoeff, colorCoeff,
&needColorFilterUniform, &needComputedColor);
// the dual source output has no canonical var name, have to
// declare an output, which is incompatible with gl_FragColor/gl_FragData.
const char* fsColorOutput;
bool dualSourceOutputWritten = false;
bool usingDeclaredOutputs = ProgramDesc::kNone_DualSrcOutput !=
fProgramDesc.fDualSrcOutput;
if (usingDeclaredOutputs) {
GrAssert(0 == segments.fHeader.size());
segments.fHeader.printf("#version 150\n");
fsColorOutput = declared_color_output_name();
segments.fFSOutputs.appendf("out vec4 %s;\n", fsColorOutput);
} else {
fsColorOutput = "gl_FragColor";
}
#if GR_GL_ATTRIBUTE_MATRICES
segments.fVSAttrs += "attribute mat3 " VIEW_MATRIX_NAME ";\n";
programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
@ -433,7 +473,9 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
bool wroteFragColorZero = false;
if (SkXfermode::kZero_Coeff == uniformCoeff &&
SkXfermode::kZero_Coeff == colorCoeff) {
segments.fFSCode.appendf("\tgl_FragColor = %s;\n", all_zeros_vec(4));
segments.fFSCode.appendf("\t%s = %s;\n",
fsColorOutput,
all_zeros_vec(4));
wroteFragColorZero = true;
} else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) {
segments.fFSCode.appendf("\tvec4 filteredColor;\n");
@ -447,11 +489,11 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
// compute the partial coverage (coverage stages and edge aa)
GrStringBuilder inCoverage;
bool coverageIsScalar = false;
// we will want to compute coverage for some blend when there is no
// color (when dual source blending is enabled). But for now we have this if
if (!wroteFragColorZero) {
// we don't need to compute coverage at all if we know the final shader
// output will be zero and we don't have a dual src blend output.
if (!wroteFragColorZero ||
ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
if (fProgramDesc.fEdgeAANumEdges > 0) {
segments.fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "[");
segments.fFSUnis.appendS32(fProgramDesc.fEdgeAANumEdges);
@ -483,7 +525,6 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
}
segments.fFSCode.append(";\n");
inCoverage = "edgeAlpha";
coverageIsScalar = true;
}
GrStringBuilder outCoverage;
@ -516,26 +557,48 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
&segments,
&programData->fUniLocations.fStages[s]);
inCoverage = outCoverage;
coverageIsScalar = false;
}
}
if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
segments.fFSOutputs.appendf("out vec4 %s;\n",
dual_source_output_name());
bool outputIsZero = false;
GrStringBuilder coeff;
if (ProgramDesc::kCoverage_DualSrcOutput !=
fProgramDesc.fDualSrcOutput && !wroteFragColorZero) {
if (!inColor.size()) {
outputIsZero = true;
} else {
if (fProgramDesc.fDualSrcOutput ==
ProgramDesc::kCoverageISA_DualSrcOutput) {
coeff.printf("(1 - %s.a)", inColor.c_str());
} else {
coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str());
}
}
}
if (outputIsZero) {
segments.fFSCode.appendf("\t%s = %s;\n",
dual_source_output_name(),
all_zeros_vec(4));
} else {
modulate_helper(dual_source_output_name(),
coeff.c_str(),
inCoverage.c_str(),
&segments.fFSCode);
}
dualSourceOutputWritten = true;
}
}
// TODO: ADD dual source blend output based on coverage here
///////////////////////////////////////////////////////////////////////////
// combine color and coverage as frag color
if (!wroteFragColorZero) {
if (coverageIsScalar && !inColor.size()) {
GrStringBuilder oldCoverage = inCoverage;
inCoverage.swap(oldCoverage);
inCoverage.printf("vec4(%s,%s,%s,%s)", oldCoverage.c_str(),
oldCoverage.c_str(), oldCoverage.c_str(),
oldCoverage.c_str());
}
modulate_helper("gl_FragColor", inColor.c_str(),
inCoverage.c_str(), &segments.fFSCode);
modulate_helper(fsColorOutput,
inColor.c_str(),
inCoverage.c_str(),
&segments.fFSCode);
}
segments.fVSCode.append("}\n");
@ -548,7 +611,10 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
return false;
}
if (!this->bindAttribsAndLinkProgram(texCoordAttrs, programData)) {
if (!this->bindOutputsAttribsAndLinkProgram(texCoordAttrs,
usingDeclaredOutputs,
dualSourceOutputWritten,
programData)) {
return false;
}
@ -560,10 +626,16 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
CachedData* programData) {
const char* strings[4];
int lengths[4];
static const int MAX_STRINGS = 6;
const char* strings[MAX_STRINGS];
int lengths[MAX_STRINGS];
int stringCnt = 0;
if (segments.fHeader.size()) {
strings[stringCnt] = segments.fHeader.c_str();
lengths[stringCnt] = segments.fHeader.size();
++stringCnt;
}
if (segments.fVSUnis.size()) {
strings[stringCnt] = segments.fVSUnis.c_str();
lengths[stringCnt] = segments.fVSUnis.size();
@ -586,12 +658,14 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
++stringCnt;
#if PRINT_SHADERS
GrPrintf(segments.fHeader.c_str());
GrPrintf(segments.fVSUnis.c_str());
GrPrintf(segments.fVSAttrs.c_str());
GrPrintf(segments.fVaryings.c_str());
GrPrintf(segments.fVSCode.c_str());
GrPrintf("\n");
#endif
GrAssert(stringCnt <= MAX_STRINGS);
programData->fVShaderID = CompileShader(GR_GL_VERTEX_SHADER,
stringCnt,
strings,
@ -603,6 +677,11 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
stringCnt = 0;
if (segments.fHeader.size()) {
strings[stringCnt] = segments.fHeader.c_str();
lengths[stringCnt] = segments.fHeader.size();
++stringCnt;
}
if (strlen(GrShaderPrecision()) > 1) {
strings[stringCnt] = GrShaderPrecision();
lengths[stringCnt] = strlen(GrShaderPrecision());
@ -618,6 +697,11 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
lengths[stringCnt] = segments.fVaryings.size();
++stringCnt;
}
if (segments.fFSOutputs.size()) {
strings[stringCnt] = segments.fFSOutputs.c_str();
lengths[stringCnt] = segments.fFSOutputs.size();
++stringCnt;
}
GrAssert(segments.fFSCode.size());
strings[stringCnt] = segments.fFSCode.c_str();
@ -625,12 +709,15 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
++stringCnt;
#if PRINT_SHADERS
GrPrintf(segments.fHeader.c_str());
GrPrintf(GrShaderPrecision());
GrPrintf(segments.fFSUnis.c_str());
GrPrintf(segments.fVaryings.c_str());
GrPrintf(segments.fFSOutputs.c_str());
GrPrintf(segments.fFSCode.c_str());
GrPrintf("\n");
#endif
GrAssert(stringCnt <= MAX_STRINGS);
programData->fFShaderID = CompileShader(GR_GL_FRAGMENT_SHADER,
stringCnt,
strings,
@ -639,6 +726,7 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
if (!programData->fFShaderID) {
return false;
}
return true;
}
@ -678,8 +766,11 @@ GrGLuint GrGLProgram::CompileShader(GrGLenum type,
return shader;
}
bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
CachedData* programData) const {
bool GrGLProgram::bindOutputsAttribsAndLinkProgram(
GrStringBuilder texCoordAttrNames[],
bool bindColorOut,
bool bindDualSrcOut,
CachedData* programData) const {
programData->fProgramID = GR_GL(CreateProgram());
if (!programData->fProgramID) {
return false;
@ -689,6 +780,15 @@ bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
GR_GL(AttachShader(progID, programData->fVShaderID));
GR_GL(AttachShader(progID, programData->fFShaderID));
if (bindColorOut) {
GR_GL(BindFragDataLocationIndexed(programData->fProgramID,
0, 0, declared_color_output_name()));
}
if (bindDualSrcOut) {
GR_GL(BindFragDataLocationIndexed(programData->fProgramID,
0, 1, dual_source_output_name()));
}
// Bind the attrib locations to same values for all shaders
GR_GL(BindAttribLocation(progID, PositionAttributeIdx(), POS_ATTR_NAME));
for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
@ -699,7 +799,6 @@ bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
}
}
if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) {
GR_GL(BindAttribLocation(progID,
ViewMatrixAttributeIdx(),
@ -1072,7 +1171,7 @@ void GrGLProgram::genStageCode(int stageNum,
segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear);
segments->fFSCode.appendf("\t%s = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str());
} else {
segments->fFSCode.appendf("\t%s = %s(%s, %s)%s %s;\n", fsOutColor, texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), smear, modulate.c_str());
segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n", fsOutColor, texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), smear, modulate.c_str());
}
}

View File

@ -19,17 +19,19 @@
#include "GrGLInterface.h"
#include "GrStringBuilder.h"
#include "GrDrawTarget.h"
#include "GrGpu.h"
#include "SkXfermode.h"
class GrBinHashKeyBuilder;
struct ShaderCodeSegments {
GrStringBuilder fHeader; // VS+FS, GLSL version, etc
GrStringBuilder fVSUnis;
GrStringBuilder fVSAttrs;
GrStringBuilder fVaryings;
GrStringBuilder fFSUnis;
GrStringBuilder fFSOutputs;
GrStringBuilder fVSCode;
GrStringBuilder fFSCode;
};
@ -65,6 +67,14 @@ public:
*/
bool genProgram(CachedData* programData) const;
/**
* The shader may modify the blend coeffecients. Params are in/out
*/
void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const;
/**
* Attribute indices
*/
static int PositionAttributeIdx() { return 0; }
static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; }
static int ColorAttributeIdx() { return 1 + GrDrawTarget::kMaxTexCoords; }
@ -94,6 +104,17 @@ private:
kUniform_ColorType = 2,
} fColorType;
// Dual-src blending makes use of a secondary output color that can be
// used as a per-pixel blend coeffecient. This controls whether a
// secondary source is output and what value it holds.
enum DualSrcOutput {
kNone_DualSrcOutput,
kCoverage_DualSrcOutput,
kCoverageISA_DualSrcOutput,
kCoverageISC_DualSrcOutput,
kDualSrcOutputCnt
} fDualSrcOutput;
int fFirstCoverageStage;
bool fEmitsPointSize;
int fEdgeAANumEdges;
@ -181,7 +202,6 @@ public:
memcpy(this, &other, sizeof(*this));
}
public:
// IDs
@ -238,8 +258,11 @@ private:
// Creates a GL program ID, binds shader attributes to GL vertex attrs, and
// links the program
bool bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[GrDrawTarget::kMaxTexCoords],
CachedData* programData) const;
bool bindOutputsAttribsAndLinkProgram(
GrStringBuilder texCoordAttrNames[GrDrawTarget::kMaxTexCoords],
bool bindColorOut,
bool bindDualSrcOut,
CachedData* programData) const;
// Gets locations for all uniforms set to kUseUniform and initializes cache
// to invalid values.

View File

@ -42,9 +42,15 @@ static const GrGLenum gXfermodeCoeff2Blend[] = {
GR_GL_ONE_MINUS_CONSTANT_COLOR,
GR_GL_CONSTANT_ALPHA,
GR_GL_ONE_MINUS_CONSTANT_ALPHA,
// extended blend coeffs
GR_GL_SRC1_COLOR,
GR_GL_ONE_MINUS_SRC1_COLOR,
GR_GL_SRC1_ALPHA,
GR_GL_ONE_MINUS_SRC1_ALPHA,
};
bool GrGpuGL::BlendCoefReferencesConstant(GrBlendCoeff coeff) {
bool GrGpuGL::BlendCoeffReferencesConstant(GrBlendCoeff coeff) {
static const bool gCoeffReferencesBlendConst[] = {
false,
false,
@ -60,28 +66,40 @@ bool GrGpuGL::BlendCoefReferencesConstant(GrBlendCoeff coeff) {
true,
true,
true,
// extended blend coeffs
false,
false,
false,
false,
};
return gCoeffReferencesBlendConst[coeff];
GR_STATIC_ASSERT(kBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
GR_STATIC_ASSERT(0 == kZero_BlendCoeff);
GR_STATIC_ASSERT(1 == kOne_BlendCoeff);
GR_STATIC_ASSERT(2 == kSC_BlendCoeff);
GR_STATIC_ASSERT(3 == kISC_BlendCoeff);
GR_STATIC_ASSERT(4 == kDC_BlendCoeff);
GR_STATIC_ASSERT(5 == kIDC_BlendCoeff);
GR_STATIC_ASSERT(6 == kSA_BlendCoeff);
GR_STATIC_ASSERT(7 == kISA_BlendCoeff);
GR_STATIC_ASSERT(8 == kDA_BlendCoeff);
GR_STATIC_ASSERT(9 == kIDA_BlendCoeff);
GR_STATIC_ASSERT(10 == kConstC_BlendCoeff);
GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff);
GR_STATIC_ASSERT(12 == kConstA_BlendCoeff);
GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff);
GR_STATIC_ASSERT(14 == kS2C_BlendCoeff);
GR_STATIC_ASSERT(15 == kIS2C_BlendCoeff);
GR_STATIC_ASSERT(16 == kS2A_BlendCoeff);
GR_STATIC_ASSERT(17 == kIS2A_BlendCoeff);
// assertion for gXfermodeCoeff2Blend have to be in GrGpu scope
GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
}
GR_STATIC_ASSERT(0 == kZero_BlendCoeff);
GR_STATIC_ASSERT(1 == kOne_BlendCoeff);
GR_STATIC_ASSERT(2 == kSC_BlendCoeff);
GR_STATIC_ASSERT(3 == kISC_BlendCoeff);
GR_STATIC_ASSERT(4 == kDC_BlendCoeff);
GR_STATIC_ASSERT(5 == kIDC_BlendCoeff);
GR_STATIC_ASSERT(6 == kSA_BlendCoeff);
GR_STATIC_ASSERT(7 == kISA_BlendCoeff);
GR_STATIC_ASSERT(8 == kDA_BlendCoeff);
GR_STATIC_ASSERT(9 == kIDA_BlendCoeff);
GR_STATIC_ASSERT(10 == kConstC_BlendCoeff);
GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff);
GR_STATIC_ASSERT(12 == kConstA_BlendCoeff);
GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff);
GR_STATIC_ASSERT(kBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
///////////////////////////////////////////////////////////////////////////////
void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
@ -1652,7 +1670,9 @@ void GrGpuGL::flushAAState(GrPrimitiveType type) {
}
}
void GrGpuGL::flushBlend(GrPrimitiveType type) {
void GrGpuGL::flushBlend(GrPrimitiveType type,
GrBlendCoeff srcCoeff,
GrBlendCoeff dstCoeff) {
if (GrIsPrimTypeLines(type) && useSmoothLines()) {
if (fHWBlendDisabled) {
GR_GL(Enable(GR_GL_BLEND));
@ -1676,15 +1696,15 @@ void GrGpuGL::flushBlend(GrPrimitiveType type) {
fHWBlendDisabled = blendOff;
}
if (!blendOff) {
if (fHWDrawState.fSrcBlend != fCurrDrawState.fSrcBlend ||
fHWDrawState.fDstBlend != fCurrDrawState.fDstBlend) {
GR_GL(BlendFunc(gXfermodeCoeff2Blend[fCurrDrawState.fSrcBlend],
gXfermodeCoeff2Blend[fCurrDrawState.fDstBlend]));
fHWDrawState.fSrcBlend = fCurrDrawState.fSrcBlend;
fHWDrawState.fDstBlend = fCurrDrawState.fDstBlend;
if (fHWDrawState.fSrcBlend != srcCoeff ||
fHWDrawState.fDstBlend != dstCoeff) {
GR_GL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff],
gXfermodeCoeff2Blend[dstCoeff]));
fHWDrawState.fSrcBlend = srcCoeff;
fHWDrawState.fDstBlend = dstCoeff;
}
if ((BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) ||
BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) &&
if ((BlendCoeffReferencesConstant(srcCoeff) ||
BlendCoeffReferencesConstant(dstCoeff)) &&
fHWDrawState.fBlendConstant != fCurrDrawState.fBlendConstant) {
float c[] = {
@ -1787,7 +1807,6 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
}
this->flushRenderTarget(rect);
this->flushAAState(type);
this->flushBlend(type);
if ((fCurrDrawState.fFlagBits & kDither_StateBit) !=
(fHWDrawState.fFlagBits & kDither_StateBit)) {

View File

@ -116,13 +116,17 @@ protected:
// flushes state that is common to fixed and programmable GL
// dither
// line smoothing
// blend func
// texture binding
// sampler state (filtering, tiling)
// FBO binding
// line width
bool flushGLStateCommon(GrPrimitiveType type);
// subclass should call this to flush the blend state
void flushBlend(GrPrimitiveType type,
GrBlendCoeff srcCoeff,
GrBlendCoeff dstCoeff);
// adjusts texture matrix to account for orientation, size, and npotness
static void AdjustTextureMatrix(const GrGLTexture* texture,
GrSamplerState::SampleMode mode,
@ -134,7 +138,7 @@ protected:
static bool TextureMatrixIsIdentity(const GrGLTexture* texture,
const GrSamplerState& sampler);
static bool BlendCoefReferencesConstant(GrBlendCoeff coeff);
static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
private:
@ -156,7 +160,6 @@ private:
void flushRenderTarget(const GrIRect* bound);
void flushStencil();
void flushAAState(GrPrimitiveType type);
void flushBlend(GrPrimitiveType type);
void resolveRenderTarget(GrGLRenderTarget* texture);

View File

@ -57,6 +57,7 @@ static const GrGLenum gMatrixMode2Enum[] = {
GrGpuGLFixed::GrGpuGLFixed() {
f4X4DownsampleFilterSupport = false;
fDualSourceBlendingSupport = false;
}
GrGpuGLFixed::~GrGpuGLFixed() {
@ -136,8 +137,8 @@ bool GrGpuGLFixed::flushGraphicsState(GrPrimitiveType type) {
}
if (GR_GL_SUPPORT_ES1) {
if (BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) ||
BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) {
if (BlendCoeffReferencesConstant(fCurrDrawState.fSrcBlend) ||
BlendCoeffReferencesConstant(fCurrDrawState.fDstBlend)) {
unimpl("ES1 doesn't support blend constant");
return false;
}
@ -147,6 +148,8 @@ bool GrGpuGLFixed::flushGraphicsState(GrPrimitiveType type) {
return false;
}
this->flushBlend(type, fCurrDrawState.fSrcBlend, fCurrDrawState.fDstBlend);
if (fDirtyFlags.fRenderTargetChanged) {
flushProjectionMatrix();
}

View File

@ -195,6 +195,15 @@ void GrGpuGLShaders::ProgramUnitTest() {
pdesc.fEdgeAANumEdges = (random.nextF() * (getMaxEdges() + 1));
if (fDualSourceBlendingSupport) {
pdesc.fDualSrcOutput =
(GrGLProgram::ProgramDesc::DualSrcOutput)
(int)(random.nextF() * GrGLProgram::ProgramDesc::kDualSrcOutputCnt);
} else {
pdesc.fDualSrcOutput =
GrGLProgram::ProgramDesc::kNone_DualSrcOutput;
}
for (int s = 0; s < kNumStages; ++s) {
// enable the stage?
if (random.nextF() > .5f) {
@ -234,7 +243,17 @@ void GrGpuGLShaders::ProgramUnitTest() {
GrGpuGLShaders::GrGpuGLShaders() {
resetContext();
int major, minor;
gl_version(&major, &minor);
f4X4DownsampleFilterSupport = true;
if (GR_GL_SUPPORT_DESKTOP) {
fDualSourceBlendingSupport =
major > 3 ||(3 == major && 3 <= minor) ||
has_gl_extension("GL_ARB_blend_func_extended");
} else {
fDualSourceBlendingSupport = false;
}
fProgramData = NULL;
fProgramCache = new ProgramCache();
@ -533,6 +552,11 @@ bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
GR_GL(UseProgram(fProgramData->fProgramID));
fHWProgramID = fProgramData->fProgramID;
}
GrBlendCoeff srcCoeff = fCurrDrawState.fSrcBlend;
GrBlendCoeff dstCoeff = fCurrDrawState.fDstBlend;
fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
this->flushBlend(type, srcCoeff, dstCoeff);
this->flushColor();
@ -677,16 +701,6 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode;
// coverage vs. color only applies when there is a color filter
// (currently)
if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
} else {
// use canonical value when this won't affect generated
// code to prevent duplicate programs.
desc.fFirstCoverageStage = kNumStages;
}
#if GR_AGGRESSIVE_SHADER_OPTS
if (!requiresAttributeColors && (0xffffffff == fCurrDrawState.fColor)) {
desc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType;
@ -704,12 +718,15 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
desc.fEdgeAANumEdges = fCurrDrawState.fEdgeAANumEdges;
int lastEnabledStage = -1;
for (int s = 0; s < kNumStages; ++s) {
GrGLProgram::ProgramDesc::StageDesc& stage = desc.fStages[s];
stage.fEnabled = this->isStageEnabled(s);
if (stage.fEnabled) {
lastEnabledStage = s;
GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
GrAssert(NULL != texture);
// we matrix to invert when orientation is TopDown, so make sure
@ -775,6 +792,43 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
stage.fModulation = (GrGLProgram::ProgramDesc::StageDesc::Modulation)0;
}
}
desc.fDualSrcOutput = GrGLProgram::ProgramDesc::kNone_DualSrcOutput;
// use canonical value when coverage/color distinction won't affect
// generated code to prevent duplicate programs.
desc.fFirstCoverageStage = kNumStages;
if (fCurrDrawState.fFirstCoverageStage <= lastEnabledStage) {
// color filter is applied between color/coverage computation
if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
}
// We could consider cases where the final color is solid (0xff alpha)
// and the dst coeff can correctly be set to a non-dualsrc gl value.
// (e.g. solid draw, and dst coeff is kZero. It's correct to make
// the dst coeff be kISA. Or solid draw with kSA can be tweaked to be
// kOne).
if (fDualSourceBlendingSupport) {
if (kZero_BlendCoeff == fCurrDrawState.fDstBlend) {
// write the coverage value to second color
desc.fDualSrcOutput =
GrGLProgram::ProgramDesc::kCoverage_DualSrcOutput;
desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
} else if (kSA_BlendCoeff == fCurrDrawState.fDstBlend) {
// SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
// cover
desc.fDualSrcOutput =
GrGLProgram::ProgramDesc::kCoverageISA_DualSrcOutput;
desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
} else if (kSC_BlendCoeff == fCurrDrawState.fDstBlend) {
// SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
// cover
desc.fDualSrcOutput =
GrGLProgram::ProgramDesc::kCoverageISC_DualSrcOutput;
desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
}
}
}
}

View File

@ -155,6 +155,8 @@ void GrGLSetDefaultGLInterface() {
gDefaultInterface.fBlitFramebuffer = glBlitFramebufferEXT;
#endif
#endif
gDefaultInterface.fBindFragDataLocationIndexed = NULL;
gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding;
gDefaultInterfaceInit = true;

View File

@ -174,6 +174,7 @@ void GrGLSetDefaultGLInterface() {
// we must have FBOs
return;
}
GR_GL_GET_PROC(BindFragDataLocationIndexed);
gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding;
gDefaultInterfaceInit = true;

View File

@ -133,6 +133,7 @@ void GrGLSetDefaultGLInterface() {
GR_GL_GET_PROC(VertexAttribPointer);
gDefaultInterface.fVertexPointer = glVertexPointer;
gDefaultInterface.fViewport = glViewport;
GR_GL_GET_PROC(BindFragDataLocationIndexed);
// First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
// GL_ARB_framebuffer_object doesn't use ARB suffix.)

View File

@ -139,6 +139,7 @@ void GrGLSetDefaultGLInterface() {
GR_GL_GET_PROC(UseProgram);
GR_GL_GET_PROC(VertexAttrib4fv);
GR_GL_GET_PROC(VertexAttribPointer);
GR_GL_GET_PROC(BindFragDataLocationIndexed);
// First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
// GL_ARB_framebuffer_object doesn't use ARB suffix.)

View File

@ -1504,6 +1504,7 @@
'../samplecode/SampleUnitMapper.cpp',
'../samplecode/SampleVertices.cpp',
'../samplecode/SampleXfermodes.cpp',
'../samplecode/SampleXfermodesBlur.cpp',
],
'sources!': [
'../samplecode/SampleSkLayer.cpp', #relies on SkMatrix44 which doesn't compile

View File

@ -0,0 +1,182 @@
#include "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "Sk64.h"
#include "SkCornerPathEffect.h"
#include "SkGradientShader.h"
#include "SkGraphics.h"
#include "SkImageDecoder.h"
#include "SkKernel33MaskFilter.h"
#include "SkPath.h"
#include "SkRandom.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkUtils.h"
#include "SkColorPriv.h"
#include "SkColorFilter.h"
#include "SkTime.h"
#include "SkTypeface.h"
#include "SkXfermode.h"
#include "SkStream.h"
#include "SkXMLParser.h"
#include "SkColorPriv.h"
#include "SkImageDecoder.h"
#include "SkBlurMaskFilter.h"
static void setNamedTypeface(SkPaint* paint, const char name[]) {
SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
paint->setTypeface(face);
SkSafeUnref(face);
}
static uint16_t gBG[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
class XfermodesBlurView : public SampleView {
SkBitmap fBG;
SkBitmap fSrcB, fDstB;
void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
SkScalar x, SkScalar y) {
SkPaint p;
SkMaskFilter* mf = SkBlurMaskFilter::Create(5, SkBlurMaskFilter::kNormal_BlurStyle, 0);
p.setMaskFilter(mf)->unref();
SkScalar ww = SkIntToScalar(W);
SkScalar hh = SkIntToScalar(H);
// draw a circle covering the upper
// left three quarters of the canvas
p.setColor(0xFFCC44FF);
SkRect r;
r.set(0, 0, ww*3/4, hh*3/4);
r.offset(x, y);
canvas->drawOval(r, p);
p.setXfermode(mode);
// draw a square overlapping the circle
// in the lower right of the canvas
p.setColor(0x00AA6633 | alpha << 24);
r.set(ww/3, hh/3, ww*19/20, hh*19/20);
r.offset(x, y);
canvas->drawRect(r, p);
}
public:
const static int W = 64;
const static int H = 64;
XfermodesBlurView() {
const int W = 64;
const int H = 64;
fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
fBG.setPixels(gBG);
fBG.setIsOpaque(true);
}
protected:
// overrides from SkEventSink
virtual bool onQuery(SkEvent* evt) {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "XfermodesBlur");
return true;
}
return this->INHERITED::onQuery(evt);
}
virtual void onDrawContent(SkCanvas* canvas) {
canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
const struct {
SkXfermode::Mode fMode;
const char* fLabel;
} gModes[] = {
{ SkXfermode::kClear_Mode, "Clear" },
{ SkXfermode::kSrc_Mode, "Src" },
{ SkXfermode::kDst_Mode, "Dst" },
{ SkXfermode::kSrcOver_Mode, "SrcOver" },
{ SkXfermode::kDstOver_Mode, "DstOver" },
{ SkXfermode::kSrcIn_Mode, "SrcIn" },
{ SkXfermode::kDstIn_Mode, "DstIn" },
{ SkXfermode::kSrcOut_Mode, "SrcOut" },
{ SkXfermode::kDstOut_Mode, "DstOut" },
{ SkXfermode::kSrcATop_Mode, "SrcATop" },
{ SkXfermode::kDstATop_Mode, "DstATop" },
{ SkXfermode::kXor_Mode, "Xor" },
{ SkXfermode::kPlus_Mode, "Plus" },
/*{ SkXfermode::kMultiply_Mode, "Multiply" },
{ SkXfermode::kScreen_Mode, "Screen" },
{ SkXfermode::kOverlay_Mode, "Overlay" },
{ SkXfermode::kDarken_Mode, "Darken" },
{ SkXfermode::kLighten_Mode, "Lighten" },
{ SkXfermode::kColorDodge_Mode, "ColorDodge" },
{ SkXfermode::kColorBurn_Mode, "ColorBurn" },
{ SkXfermode::kHardLight_Mode, "HardLight" },
{ SkXfermode::kSoftLight_Mode, "SoftLight" },
{ SkXfermode::kDifference_Mode, "Difference" },
{ SkXfermode::kExclusion_Mode, "Exclusion" },*/
};
const SkScalar w = SkIntToScalar(W);
const SkScalar h = SkIntToScalar(H);
SkShader* s = SkShader::CreateBitmapShader(fBG,
SkShader::kRepeat_TileMode,
SkShader::kRepeat_TileMode);
SkMatrix m;
m.setScale(SkIntToScalar(6), SkIntToScalar(6));
s->setLocalMatrix(m);
SkPaint labelP;
labelP.setAntiAlias(true);
labelP.setLCDRenderText(true);
labelP.setTextAlign(SkPaint::kCenter_Align);
setNamedTypeface(&labelP, "Menlo Regular");
const int W = 5;
SkScalar x0 = 0;
for (int twice = 0; twice < 2; twice++) {
SkScalar x = x0, y = 0;
for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
SkAutoUnref aur(mode);
SkRect r;
r.set(x, y, x+w, y+h);
SkPaint p;
p.setStyle(SkPaint::kFill_Style);
p.setShader(s);
canvas->drawRect(r, p);
canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop);
canvas->restore();
r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
p.setStyle(SkPaint::kStroke_Style);
p.setShader(NULL);
canvas->drawRect(r, p);
canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel),
x + w/2, y - labelP.getTextSize()/2, labelP);
x += w + SkIntToScalar(10);
if ((i % W) == W - 1) {
x = x0;
y += h + SkIntToScalar(30);
}
}
x0 += SkIntToScalar(400);
}
s->unref();
}
private:
typedef SampleView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new XfermodesBlurView; }
static SkViewRegister reg(MyFactory);

View File

@ -345,6 +345,7 @@ void kill_dummy(HWND dummy) {
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_ALPHA_BITS_ARB 0x201B
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_FULL_ACCELERATION_ARB 0x2027
@ -419,6 +420,7 @@ HGLRC create_gl(HWND hwnd) {
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_SUPPORT_OPENGL_ARB, TRUE,
WGL_COLOR_BITS_ARB, 24,
WGL_ALPHA_BITS_ARB, 8,
WGL_STENCIL_BITS_ARB, 8,
#if USE_MSAA
WGL_SAMPLE_BUFFERS_ARB, TRUE,