* @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
/* 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
typedef unsigned char GrGLboolean;
typedef unsigned int GrGLbitfield;
typedef signed char GrGLbyte;
+typedef char GrGLchar;
typedef short GrGLshort;
typedef int GrGLint;
typedef int GrGLsizei;
// 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"
/*
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.
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)
* this returns NULL.
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
* value.
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.
kConstA_BlendCoeff, //<! constant color alpha
kIConstA_BlendCoeff, //<! one minus constant color alpha
- kBlendCoeffCount
+ kPublicBlendCoeffCount
};
/**
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) {
///////////////////////////////////////////////////////////////////////////////
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)) {
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]);
}
}
+ // 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;
}
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";
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,
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,
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);
}
}
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;
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");
// 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);
}
segments.fFSCode.append(";\n");
inCoverage = "edgeAlpha";
- coverageIsScalar = true;
}
GrStringBuilder outCoverage;
&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");
return false;
}
- if (!this->bindAttribsAndLinkProgram(texCoordAttrs, programData)) {
+ if (!this->bindOutputsAttribsAndLinkProgram(texCoordAttrs,
+ usingDeclaredOutputs,
+ dualSourceOutputWritten,
+ programData)) {
return false;
}
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();
++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,
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());
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();
++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,
if (!programData->fFShaderID) {
return false;
}
+
return true;
}
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;
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) {
}
}
-
if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) {
GR_GL(BindAttribLocation(progID,
ViewMatrixAttributeIdx(),
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());
}
}
#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;
};
*/
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; }
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;
memcpy(this, &other, sizeof(*this));
}
-
public:
// IDs
// 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.
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,
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,
}
}
-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));
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[] = {
}
this->flushRenderTarget(rect);
this->flushAAState(type);
- this->flushBlend(type);
if ((fCurrDrawState.fFlagBits & kDither_StateBit) !=
(fHWDrawState.fFlagBits & kDither_StateBit)) {
// 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,
static bool TextureMatrixIsIdentity(const GrGLTexture* texture,
const GrSamplerState& sampler);
- static bool BlendCoefReferencesConstant(GrBlendCoeff coeff);
+ static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
private:
void flushRenderTarget(const GrIRect* bound);
void flushStencil();
void flushAAState(GrPrimitiveType type);
- void flushBlend(GrPrimitiveType type);
void resolveRenderTarget(GrGLRenderTarget* texture);
GrGpuGLFixed::GrGpuGLFixed() {
f4X4DownsampleFilterSupport = false;
+ fDualSourceBlendingSupport = false;
}
GrGpuGLFixed::~GrGpuGLFixed() {
}
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;
}
return false;
}
+ this->flushBlend(type, fCurrDrawState.fSrcBlend, fCurrDrawState.fDstBlend);
+
if (fDirtyFlags.fRenderTargetChanged) {
flushProjectionMatrix();
}
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) {
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();
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();
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;
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
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;
+ }
+ }
+ }
}
gDefaultInterface.fBlitFramebuffer = glBlitFramebufferEXT;
#endif
#endif
+ gDefaultInterface.fBindFragDataLocationIndexed = NULL;
+
gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding;
gDefaultInterfaceInit = true;
// we must have FBOs
return;
}
+ GR_GL_GET_PROC(BindFragDataLocationIndexed);
gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding;
gDefaultInterfaceInit = true;
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.)
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.)
'../samplecode/SampleUnitMapper.cpp',
'../samplecode/SampleVertices.cpp',
'../samplecode/SampleXfermodes.cpp',
+ '../samplecode/SampleXfermodesBlur.cpp',
],
'sources!': [
'../samplecode/SampleSkLayer.cpp', #relies on SkMatrix44 which doesn't compile
--- /dev/null
+#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);
#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
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,