src, and dst must all be NULL or all non-NULL. If effect is non-NULL then the xfermode may
optionally allocate an effect to return and the caller as *effect. The caller will install
it and own a ref to it. Since the xfermode may or may not assign *effect, the caller should
- set *effect to NULL beforehand. If the function returns true then the src and dst coeffs
- will be applied to the draw regardless of whether an effect was returned.
+ set *effect to NULL beforehand. If the function returns true and *effect is NULL then the
+ src and dst coeffs will be applied to the draw. When *effect is non-NULL the coeffs are
+ ignored.
*/
- virtual bool asNewEffect(GrContext*, GrEffectRef** effect, Coeff* src, Coeff* dst) const;
+ virtual bool asNewEffectOrCoeff(GrContext*,
+ GrEffectRef** effect,
+ Coeff* src,
+ Coeff* dst) const;
/**
* The same as calling xfermode->asNewEffect(...), except that this also checks if the xfermode
* is NULL, and if so, treats it as kSrcOver_Mode.
*/
- static bool AsNewEffect(SkXfermode*, GrContext*, GrEffectRef** effect, Coeff* src, Coeff* dst);
+ static bool AsNewEffectOrCoeff(SkXfermode*,
+ GrContext*,
+ GrEffectRef** effect,
+ Coeff* src,
+ Coeff* dst);
SkDEVCODE(virtual void toString(SkString* str) const = 0;)
SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
/** Shortcut for textureAccess(index).texture(); */
GrTexture* texture(int index) const { return this->textureAccess(index).getTexture(); }
+ /** Will this effect read the destination pixel value? */
+ bool willReadDst() const { return fWillReadDst; }
int numVertexAttribs() const { return fVertexAttribTypes.count(); }
static const int kMaxVertexAttribs = 2;
-
/** Useful for effects that want to insert a texture matrix that is implied by the texture
dimensions */
static inline SkMatrix MakeDivByTextureWHMatrix(const GrTexture* texture) {
*/
void addVertexAttrib(GrSLType type);
- GrEffect() : fEffectRef(NULL) {};
+ GrEffect() : fWillReadDst(false), fEffectRef(NULL) {}
/** This should be called by GrEffect subclass factories. See the comment on AutoEffectUnref for
an example factory function. */
return *static_cast<const T*>(&effectRef);
}
+ /**
+ * If the effect subclass will read the destination pixel value then it must call this function
+ * from its constructor. Otherwise, when its generated backend-specific effect class attempts
+ * to generate code that reads the destination pixel it will fail.
+ */
+ void setWillReadDst() { fWillReadDst = true; }
+
private:
bool isEqual(const GrEffect& other) const {
if (&this->getFactory() != &other.getFactory()) {
SkSTArray<4, const GrTextureAccess*, true> fTextureAccesses;
SkSTArray<kMaxVertexAttribs, GrSLType, true> fVertexAttribTypes;
+ bool fWillReadDst;
GrEffectRef* fEffectRef;
typedef GrRefCnt INHERITED;
#define GrTexture_DEFINED
#include "GrSurface.h"
+#include "SkPoint.h"
class GrRenderTarget;
class GrResourceKey;
typedef GrSurface INHERITED;
};
+/**
+ * Represents a texture that is intended to be accessed in device coords with an offset.
+ */
+class GrDeviceCoordTexture {
+public:
+ GrDeviceCoordTexture() { fOffset.set(0, 0); }
+
+ GrDeviceCoordTexture(const GrDeviceCoordTexture& other) {
+ *this = other;
+ }
+
+ GrDeviceCoordTexture(GrTexture* texture, const SkIPoint& offset)
+ : fTexture(SkSafeRef(texture))
+ , fOffset(offset) {
+ }
+
+ GrDeviceCoordTexture& operator=(const GrDeviceCoordTexture& other) {
+ fTexture.reset(SkSafeRef(other.fTexture.get()));
+ fOffset = other.fOffset;
+ return *this;
+ }
+
+ const SkIPoint& offset() const { return fOffset; }
+
+ void setOffset(const SkIPoint& offset) { fOffset = offset; }
+ void setOffset(int ox, int oy) { fOffset.set(ox, oy); }
+
+ GrTexture* texture() const { return fTexture.get(); }
+
+ GrTexture* setTexture(GrTexture* texture) {
+ fTexture.reset(SkSafeRef(texture));
+ return texture;
+ }
+private:
+ SkAutoTUnref<GrTexture> fTexture;
+ SkIPoint fOffset;
+};
+
#endif
return false;
}
-bool SkXfermode::asNewEffect(GrContext*, GrEffectRef**, Coeff*, Coeff*) const {
- return false;
+bool SkXfermode::asNewEffectOrCoeff(GrContext*, GrEffectRef**, Coeff* src, Coeff* dst) const {
+ return this->asCoeff(src, dst);
}
-bool SkXfermode::AsNewEffect(SkXfermode* xfermode,
- GrContext* context,
- GrEffectRef** effect,
- Coeff* src,
- Coeff* dst) {
+bool SkXfermode::AsNewEffectOrCoeff(SkXfermode* xfermode,
+ GrContext* context,
+ GrEffectRef** effect,
+ Coeff* src,
+ Coeff* dst) {
if (NULL == xfermode) {
return ModeAsCoeff(kSrcOver_Mode, src, dst);
} else {
- return xfermode->asNewEffect(context, effect, src, dst);
+ return xfermode->asNewEffectOrCoeff(context, effect, src, dst);
}
}
}
#endif
+//////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "GrEffect.h"
+#include "GrEffectUnitTest.h"
+#include "GrTBackendEffectFactory.h"
+#include "gl/GrGLEffect.h"
+
+/**
+ * GrEffect that implements the kDarken_Mode Xfermode. It requires access to the dst pixel color
+ * in the shader. TODO: Make this work for all non-Coeff SkXfermode::Mode values.
+ */
+class DarkenEffect : public GrEffect {
+public:
+ static GrEffectRef* Create() {
+ static AutoEffectUnref gEffect(SkNEW(DarkenEffect));
+ return CreateEffectRef(gEffect);
+ }
+
+ virtual void getConstantColorComponents(GrColor* color,
+ uint32_t* validFlags) const SK_OVERRIDE {
+ *validFlags = 0;
+ }
+
+ virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
+ return GrTBackendEffectFactory<DarkenEffect>::getInstance();
+ }
+
+ static const char* Name() { return "XfermodeDarken"; }
+
+ class GLEffect : public GrGLEffect {
+ public:
+ GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
+ : GrGLEffect(factory ) {
+ }
+ virtual void emitCode(GrGLShaderBuilder* builder,
+ const GrDrawEffect& drawEffect,
+ EffectKey key,
+ const char* outputColor,
+ const char* inputColor,
+ const TextureSamplerArray& samplers) SK_OVERRIDE {
+ const char* dstColorName = builder->dstColor();
+ GrAssert(NULL != dstColorName);
+ builder->fsCodeAppendf("\t\t%s.a = 1.0 - (1.0 - %s.a) * (1.0 - %s.a);\n",
+ outputColor, dstColorName, inputColor);
+ builder->fsCodeAppendf("\t\t%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb,"
+ " (1.0 - %s.a) * %s.rgb + %s.rgb);\n",
+ outputColor,
+ inputColor, dstColorName, inputColor,
+ dstColorName, inputColor, dstColorName);
+ }
+
+ static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) { return 0; }
+
+ virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
+
+ private:
+ typedef GrGLEffect INHERITED;
+ };
+
+ GR_DECLARE_EFFECT_TEST;
+
+private:
+ DarkenEffect() { this->setWillReadDst(); }
+ virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { return true; }
+
+ typedef GrEffect INHERITED;
+};
+
+GR_DEFINE_EFFECT_TEST(DarkenEffect);
+GrEffectRef* DarkenEffect::TestCreate(SkMWCRandom*,
+ GrContext*,
+ const GrDrawTargetCaps&,
+ GrTexture*[]) {
+ static AutoEffectUnref gEffect(SkNEW(DarkenEffect));
+ return CreateEffectRef(gEffect);
+}
+
+#endif
+
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
return true;
}
- virtual bool asNewEffect(GrContext*, GrEffectRef**, Coeff* src, Coeff* dst) const SK_OVERRIDE {
- return this->asCoeff(src, dst);
+#if SK_SUPPORT_GPU
+ virtual bool asNewEffectOrCoeff(GrContext*,
+ GrEffectRef** effect,
+ Coeff* src,
+ Coeff* dst) const SK_OVERRIDE {
+ if (this->asCoeff(src, dst)) {
+ return true;
+ }
+ if (kDarken_Mode == fMode) {
+ if (NULL != effect) {
+ *effect = DarkenEffect::Create();
+ }
+ return true;
+ }
+ return false;
}
+#endif
SK_DEVELOPER_TO_STRING()
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkProcCoeffXfermode)
#include "GrDrawTarget.h"
+#include "GrContext.h"
#include "GrDrawTargetCaps.h"
#include "GrRenderTarget.h"
#include "GrTexture.h"
} else {
fDevBounds = NULL;
}
+
+ fDstCopy = di.fDstCopy;
+
return *this;
}
return true;
}
+bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) {
+ bool willReadDst = false;
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ const GrEffectRef* effect = this->drawState()->getStage(s).getEffect();
+ if (NULL != effect && (*effect)->willReadDst()) {
+ willReadDst = true;
+ break;
+ }
+ }
+ if (!willReadDst) {
+ return true;
+ }
+ GrRenderTarget* rt = this->drawState()->getRenderTarget();
+ // If the dst is not a texture then we don't currently have a way of copying the
+ // texture. TODO: make copying RT->Tex (or Surface->Surface) a GrDrawTarget operation that can
+ // be built on top of GL/D3D APIs.
+ if (NULL == rt->asTexture()) {
+ GrPrintf("Reading Dst of non-texture render target is not currently supported.\n");
+ return false;
+ }
+ // TODO: Consider bounds of draw and bounds of clip
+
+ GrDrawTarget::AutoGeometryAndStatePush agasp(this, kReset_ASRInit);
+
+ // The draw will resolve dst if it has MSAA. Two things to consider in the future:
+ // 1) to make the dst values be pre-resolve we'd need to be able to copy to MSAA
+ // texture and sample it correctly in the shader. 2) If 1 isn't available then we
+ // should just resolve and use the resolved texture directly rather than making a
+ // copy of it.
+ GrTextureDesc desc;
+ desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+ desc.fWidth = rt->width();
+ desc.fHeight = rt->height();
+ desc.fSampleCnt = 0;
+ desc.fConfig = rt->config();
+
+ GrAutoScratchTexture ast(fContext, desc, GrContext::kApprox_ScratchTexMatch);
+
+ if (NULL == ast.texture()) {
+ GrPrintf("Failed to create temporary copy of destination texture.\n");
+ return false;
+ }
+ this->drawState()->disableState(GrDrawState::kClip_StateBit);
+ this->drawState()->setRenderTarget(ast.texture()->asRenderTarget());
+ static const int kTextureStage = 0;
+ SkMatrix matrix;
+ matrix.setIDiv(rt->width(), rt->height());
+ this->drawState()->createTextureEffect(kTextureStage, rt->asTexture(), matrix);
+ SkRect copyRect = SkRect::MakeWH(SkIntToScalar(desc.fWidth),
+ SkIntToScalar(desc.fHeight));
+ this->drawRect(copyRect, NULL, ©Rect, NULL);
+ info->fDstCopy.setTexture(ast.texture());
+ info->fDstCopy.setOffset(0, 0);
+ return true;
+}
+
void GrDrawTarget::drawIndexed(GrPrimitiveType type,
int startVertex,
int startIndex,
if (NULL != devBounds) {
info.setDevBounds(*devBounds);
}
+ // TODO: We should continue with incorrect blending.
+ if (!this->setupDstReadIfNecessary(&info)) {
+ return;
+ }
this->onDraw(info);
}
}
if (NULL != devBounds) {
info.setDevBounds(*devBounds);
}
+ // TODO: We should continue with incorrect blending.
+ if (!this->setupDstReadIfNecessary(&info)) {
+ return;
+ }
this->onDraw(info);
}
}
if (NULL != devBounds) {
info.setDevBounds(*devBounds);
}
+ // TODO: We should continue with incorrect blending.
+ if (!this->setupDstReadIfNecessary(&info)) {
+ return;
+ }
while (instanceCount) {
info.fInstanceCount = GrMin(instanceCount, maxInstancesPerDraw);
}
const SkRect* getDevBounds() const { return fDevBounds; }
+ // NULL if no copy of the dst is needed for the draw.
+ const GrDeviceCoordTexture* getDstCopy() const {
+ if (NULL != fDstCopy.texture()) {
+ return &fDstCopy;
+ } else {
+ return NULL;
+ }
+ }
+
private:
DrawInfo() { fDevBounds = NULL; }
friend class GrDrawTarget;
- GrPrimitiveType fPrimitiveType;
+ GrPrimitiveType fPrimitiveType;
- int fStartVertex;
- int fStartIndex;
- int fVertexCount;
- int fIndexCount;
+ int fStartVertex;
+ int fStartIndex;
+ int fVertexCount;
+ int fIndexCount;
- int fInstanceCount;
- int fVerticesPerInstance;
- int fIndicesPerInstance;
+ int fInstanceCount;
+ int fVerticesPerInstance;
+ int fIndicesPerInstance;
- SkRect fDevBoundsStorage;
- SkRect* fDevBounds;
+ SkRect fDevBoundsStorage;
+ SkRect* fDevBounds;
+
+ GrDeviceCoordTexture fDstCopy;
};
private:
void releasePreviousVertexSource();
void releasePreviousIndexSource();
+ // Makes a copy of the dst if it is necessary for the draw. Returns false if a copy is required
+ // but couldn't be made. Otherwise, returns true.
+ bool setupDstReadIfNecessary(DrawInfo* info);
+
enum {
kPreallocGeoSrcStateStackCnt = 4,
};
////////////////////////////////////////////////////////////////////////////////
-bool GrGpu::setupClipAndFlushState(DrawType type) {
+bool GrGpu::setupClipAndFlushState(DrawType type, const GrDeviceCoordTexture* dstCopy) {
if (!fClipMaskManager.setupClipping(this->getClip())) {
return false;
}
- if (!this->flushGraphicsState(type)) {
+ if (!this->flushGraphicsState(type, dstCopy)) {
return false;
}
void GrGpu::onDraw(const DrawInfo& info) {
this->handleDirtyContext();
- if (!this->setupClipAndFlushState(PrimTypeToDrawType(info.primitiveType()))) {
+ if (!this->setupClipAndFlushState(PrimTypeToDrawType(info.primitiveType()),
+ info.getDstCopy())) {
return;
}
this->onGpuDraw(info);
GrAutoTRestore<GrStencilSettings> asr(this->drawState()->stencil());
this->setStencilPathSettings(*path, fill, this->drawState()->stencil());
- if (!this->setupClipAndFlushState(kStencilPath_DrawType)) {
+ if (!this->setupClipAndFlushState(kStencilPath_DrawType, NULL)) {
return;
}
}
// prepares clip flushes gpu state before a draw
- bool setupClipAndFlushState(DrawType);
+ bool setupClipAndFlushState(DrawType, const GrDeviceCoordTexture* dstCopy);
// Functions used to map clip-respecting stencil tests into normal
// stencil funcs supported by GPUs.
// The GrGpu typically records the clients requested state and then flushes
// deltas from previous state at draw time. This function does the
- // backend-specific flush of the state
+ // backend-specific flush of the state.
// returns false if current state is unsupported.
- virtual bool flushGraphicsState(DrawType) = 0;
+ virtual bool flushGraphicsState(DrawType, const GrDeviceCoordTexture* dstCopy) = 0;
// clears the entire stencil buffer to 0
virtual void clearStencil() = 0;
grPaint->setDither(skPaint.isDither());
grPaint->setAntiAlias(skPaint.isAntiAlias());
- SkXfermode::Coeff sm = SkXfermode::kOne_Coeff;
- SkXfermode::Coeff dm = SkXfermode::kISA_Coeff;
+ SkXfermode::Coeff sm;
+ SkXfermode::Coeff dm;
SkXfermode* mode = skPaint.getXfermode();
GrEffectRef* xferEffect = NULL;
- if (SkXfermode::AsNewEffect(mode, dev->context(), &xferEffect, &sm, &dm)) {
- SkSafeUnref(grPaint->colorStage(kXfermodeEffectIdx)->setEffect(xferEffect));
+ if (SkXfermode::AsNewEffectOrCoeff(mode, dev->context(), &xferEffect, &sm, &dm)) {
+ if (NULL != xferEffect) {
+ grPaint->colorStage(kXfermodeEffectIdx)->setEffect(xferEffect)->unref();
+ // This may not be the right place to have this logic but we set the GPU blend to
+ // src-over so that fractional coverage will be accounted for correctly.
+ sm = SkXfermode::kOne_Coeff;
+ dm = SkXfermode::kISA_Coeff;
+ }
} else {
//SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)
#if 0
return false;
+#else
+ // Fall back to src-over
+ sm = SkXfermode::kOne_Coeff;
+ dm = SkXfermode::kISA_Coeff;
#endif
}
grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
return false;
}
- if (builder.fUsesGS) {
+ fGShaderID = 0;
+#if GR_GL_EXPERIMENTAL_GS
+ if (fDesc.fExperimentalGS) {
builder.getShader(GrGLShaderBuilder::kGeometry_ShaderType, &shader);
if (c_PrintShaders) {
GrPrintf(shader.c_str());
if (!(fGShaderID = compile_shader(fContext, GR_GL_GEOMETRY_SHADER, shader))) {
return false;
}
- } else {
- fGShaderID = 0;
}
+#endif
builder.getShader(GrGLShaderBuilder::kFragment_ShaderType, &shader);
if (c_PrintShaders) {
bool GrGLProgram::genProgram(const GrEffectStage* stages[]) {
GrAssert(0 == fProgramID);
- const GrAttribBindings& attribBindings = fDesc.fAttribBindings;
- bool hasExplicitLocalCoords =
- SkToBool(attribBindings & GrDrawState::kLocalCoords_AttribBindingsBit);
- GrGLShaderBuilder builder(fContext.info(), fUniformManager, hasExplicitLocalCoords);
-
-#if GR_GL_EXPERIMENTAL_GS
- builder.fUsesGS = fDesc.fExperimentalGS;
-#endif
+ GrGLShaderBuilder builder(fContext.info(), fUniformManager, fDesc);
SkXfermode::Coeff colorCoeff, uniformCoeff;
// The rest of transfer mode color filters have not been implemented
}
// we output point size in the GS if present
- if (fDesc.fEmitsPointSize && !builder.fUsesGS){
+ if (fDesc.fEmitsPointSize
+#if GR_GL_EXPERIMENTAL_GS
+ && !fDesc.fExperimentalGS
+#endif
+ ) {
builder.vsCodeAppend("\tgl_PointSize = 1.0;\n");
}
fDesc.fEffectKeys[s],
inColor.size() ? inColor.c_str() : NULL,
outColor.c_str(),
- &fUniformHandles.fSamplerUnis[s]);
+ &fUniformHandles.fEffectSamplerUnis[s]);
builder.setNonStage();
inColor = outColor;
}
fDesc.fEffectKeys[s],
inCoverage.size() ? inCoverage.c_str() : NULL,
outCoverage.c_str(),
- &fUniformHandles.fSamplerUnis[s]);
+ &fUniformHandles.fEffectSamplerUnis[s]);
builder.setNonStage();
inCoverage = outCoverage;
}
}
builder.finished(fProgramID);
- this->initSamplerUniforms();
fUniformHandles.fRTHeightUni = builder.getRTHeightUniform();
+ fUniformHandles.fDstCopyTopLeftUni = builder.getDstCopyTopLeftUniform();
+ fUniformHandles.fDstCopyScaleUni = builder.getDstCopyScaleUniform();
+ fUniformHandles.fDstCopySamplerUni = builder.getDstCopySamplerUniform();
+ // This must be called after we set fDstCopySamplerUni above.
+ this->initSamplerUniforms();
return true;
}
void GrGLProgram::initSamplerUniforms() {
GL_CALL(UseProgram(fProgramID));
- // We simply bind the uniforms to successive texture units beginning at 0. setData() assumes this
- // behavior.
+ // We simply bind the uniforms to successive texture units beginning at 0. setData() assumes
+ // this behavior.
GrGLint texUnitIdx = 0;
+ if (GrGLUniformManager::kInvalidUniformHandle != fUniformHandles.fDstCopySamplerUni) {
+ fUniformManager.setSampler(fUniformHandles.fDstCopySamplerUni, texUnitIdx);
+ ++texUnitIdx;
+ }
+
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
- int numSamplers = fUniformHandles.fSamplerUnis[s].count();
+ int numSamplers = fUniformHandles.fEffectSamplerUnis[s].count();
for (int u = 0; u < numSamplers; ++u) {
- UniformHandle handle = fUniformHandles.fSamplerUnis[s][u];
+ UniformHandle handle = fUniformHandles.fEffectSamplerUnis[s][u];
if (GrGLUniformManager::kInvalidUniformHandle != handle) {
fUniformManager.setSampler(handle, texUnitIdx);
++texUnitIdx;
void GrGLProgram::setData(GrGpuGL* gpu,
GrColor color,
GrColor coverage,
+ const GrDeviceCoordTexture* dstCopy,
SharedGLState* sharedState) {
const GrDrawState& drawState = gpu->getDrawState();
}
GrGLint texUnitIdx = 0;
+ if (NULL != dstCopy) {
+ if (GrGLUniformManager::kInvalidUniformHandle != fUniformHandles.fDstCopyTopLeftUni) {
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle != fUniformHandles.fDstCopyScaleUni);
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle !=
+ fUniformHandles.fDstCopySamplerUni);
+ fUniformManager.set2f(fUniformHandles.fDstCopyTopLeftUni,
+ static_cast<GrGLfloat>(dstCopy->offset().fX),
+ static_cast<GrGLfloat>(dstCopy->offset().fY));
+ fUniformManager.set2f(fUniformHandles.fDstCopyScaleUni,
+ 1.f / dstCopy->texture()->width(),
+ 1.f / dstCopy->texture()->height());
+ GrGLTexture* texture = static_cast<GrGLTexture*>(dstCopy->texture());
+ static GrTextureParams kParams; // the default is clamp, nearest filtering.
+ gpu->bindTexture(texUnitIdx, kParams, texture);
+ ++texUnitIdx;
+ } else {
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle ==
+ fUniformHandles.fDstCopyScaleUni);
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle ==
+ fUniformHandles.fDstCopySamplerUni);
+ }
+ } else {
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle ==
+ fUniformHandles.fDstCopyTopLeftUni);
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle ==
+ fUniformHandles.fDstCopyScaleUni);
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle ==
+ fUniformHandles.fDstCopySamplerUni);
+ }
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
if (NULL != fEffects[s]) {
const GrEffectStage& stage = drawState.getStage(s);
(fDesc.fAttribBindings & GrDrawState::kLocalCoords_AttribBindingsBit);
GrDrawEffect drawEffect(stage, explicitLocalCoords);
fEffects[s]->setData(fUniformManager, drawEffect);
- int numSamplers = fUniformHandles.fSamplerUnis[s].count();
+ int numSamplers = fUniformHandles.fEffectSamplerUnis[s].count();
for (int u = 0; u < numSamplers; ++u) {
- UniformHandle handle = fUniformHandles.fSamplerUnis[s][u];
+ UniformHandle handle = fUniformHandles.fEffectSamplerUnis[s][u];
if (GrGLUniformManager::kInvalidUniformHandle != handle) {
const GrTextureAccess& access = (*stage.getEffect())->textureAccess(u);
GrGLTexture* texture = static_cast<GrGLTexture*>(access.getTexture());
*
* The color and coverage params override the GrDrawState's getColor() and getCoverage() values.
*/
- void setData(GrGpuGL*, GrColor color, GrColor coverage, SharedGLState*);
+ void setData(GrGpuGL*,
+ GrColor color,
+ GrColor coverage,
+ const GrDeviceCoordTexture* dstCopy, // can be NULL
+ SharedGLState*);
private:
GrGLProgram(const GrGLContext& gl,
UniformHandle fColorUni;
UniformHandle fCoverageUni;
UniformHandle fColorFilterUni;
+
// We use the render target height to provide a y-down frag coord when specifying
// origin_upper_left is not supported.
UniformHandle fRTHeightUni;
+
+ // Uniforms for computing texture coords to do the dst-copy lookup
+ UniformHandle fDstCopyTopLeftUni;
+ UniformHandle fDstCopyScaleUni;
+ UniformHandle fDstCopySamplerUni;
+
// An array of sampler uniform handles for each effect.
- SamplerUniSArray fSamplerUnis[GrDrawState::kNumStages];
+ SamplerUniSArray fEffectSamplerUnis[GrDrawState::kNumStages];
UniformHandles() {
fViewMatrixUni = GrGLUniformManager::kInvalidUniformHandle;
fCoverageUni = GrGLUniformManager::kInvalidUniformHandle;
fColorFilterUni = GrGLUniformManager::kInvalidUniformHandle;
fRTHeightUni = GrGLUniformManager::kInvalidUniformHandle;
+ fDstCopyTopLeftUni = GrGLUniformManager::kInvalidUniformHandle;
+ fDstCopyScaleUni = GrGLUniformManager::kInvalidUniformHandle;
+ fDstCopySamplerUni = GrGLUniformManager::kInvalidUniformHandle;
}
};
#include "GrBackendEffectFactory.h"
#include "GrDrawEffect.h"
#include "GrEffect.h"
+#include "GrGLShaderBuilder.h"
#include "GrGpuGL.h"
void GrGLProgramDesc::Build(const GrDrawState& drawState,
GrBlendCoeff srcCoeff,
GrBlendCoeff dstCoeff,
const GrGpuGL* gpu,
+ const GrDeviceCoordTexture* dstCopy,
GrGLProgramDesc* desc) {
// This should already have been caught
desc->fCoverageInput = kAttribute_ColorInput;
}
+ bool readsDst = false;
int lastEnabledStage = -1;
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
GrDrawState::kLocalCoords_AttribBindingsBit);
GrDrawEffect drawEffect(drawState.getStage(s), explicitLocalCoords);
desc->fEffectKeys[s] = factory.glEffectKey(drawEffect, gpu->glCaps());
+ if (effect->willReadDst()) {
+ readsDst = true;
+ }
} else {
desc->fEffectKeys[s] = 0;
}
}
+ if (readsDst) {
+ GrAssert(NULL != dstCopy);
+ desc->fDstRead = GrGLShaderBuilder::KeyForDstRead(dstCopy->texture(), gpu->glCaps());
+ GrAssert(0 != desc->fDstRead);
+ } else {
+ desc->fDstRead = 0;
+ }
+
desc->fDualSrcOutput = kNone_DualSrcOutput;
// Currently the experimental GS will only work with triangle prims (and it doesn't do anything
#include "GrGLEffect.h"
#include "GrDrawState.h"
+#include "GrGLShaderBuilder.h"
class GrGpuGL;
#define GR_GL_EXPERIMENTAL_GS GR_DEBUG
-/** This class describes a program to generate. It also serves as a program cache key. The only
- thing GL-specific about this is the generation of GrGLEffect::EffectKeys. With some refactoring
- it could be made backend-neutral. */
+/** This class describes a program to generate. It also serves as a program cache key. Very little
+ of this is GL-specific. There is the generation of GrGLEffect::EffectKeys and the dst-read part
+ of the key set by GrGLShaderBuilder. If the interfaces that set those portions were abstracted
+ to be API-neutral then so could this class. */
class GrGLProgramDesc {
public:
GrGLProgramDesc() {
GrBlendCoeff srcCoeff,
GrBlendCoeff dstCoeff,
const GrGpuGL* gpu,
+ const GrDeviceCoordTexture* dstCopy,
GrGLProgramDesc* outDesc);
private:
#if GR_GL_EXPERIMENTAL_GS
bool fExperimentalGS;
#endif
+ GrGLShaderBuilder::DstReadKey fDstRead; // set by GrGLShaderBuilder if there
+ // are effects that must read the dst.
+ // Otherwise, 0.
+
uint8_t fColorInput; // casts to enum ColorInput
uint8_t fCoverageInput; // casts to enum ColorInput
uint8_t fDualSrcOutput; // casts to enum DualSrcOutput
int8_t fCoverageAttributeIndex;
int8_t fLocalCoordsAttributeIndex;
- // GrGLProgram reads the private fields to generate code.
+ // GrGLProgram and GrGLShaderBuilder read the private fields to generate code. TODO: Move all
+ // code generation to GrGLShaderBuilder (and maybe add getters rather than friending).
friend class GrGLProgram;
+ friend class GrGLShaderBuilder;
};
#endif
}
/**
- * Do we need to either map r,g,b->a or a->r.
+ * Do we need to either map r,g,b->a or a->r. configComponentMask indicates which channels are
+ * present in the texture's config. swizzleComponentMask indicates the channels present in the
+ * shader swizzle.
*/
inline bool swizzle_requires_alpha_remapping(const GrGLCaps& caps,
- const GrTextureAccess& access) {
- if (GrPixelConfigIsAlphaOnly(access.getTexture()->config())) {
- if (caps.textureRedSupport() && (kA_GrColorComponentFlag & access.swizzleMask())) {
+ uint32_t configComponentMask,
+ uint32_t swizzleComponentMask) {
+ if (caps.textureSwizzleSupport()) {
+ // Any remapping is handled using texture swizzling not shader modifications.
+ return false;
+ }
+ // check if the texture is alpha-only
+ if (kA_GrColorComponentFlag == configComponentMask) {
+ if (caps.textureRedSupport() && (kA_GrColorComponentFlag & swizzleComponentMask)) {
+ // we must map the swizzle 'a's to 'r'.
return true;
}
- if (kRGB_GrColorComponentFlags & access.swizzleMask()) {
+ if (kRGB_GrColorComponentFlags & swizzleComponentMask) {
+ // The 'r', 'g', and/or 'b's must be mapped to 'a' according to our semantics that
+ // alpha-only textures smear alpha across all four channels when read.
return true;
}
}
}
-///////////////////////////////////////////////////////////////////////////////
+static const char kDstColorName[] = "_dstColor";
-// Architectural assumption: always 2-d input coords.
-// Likely to become non-constant and non-static, perhaps even
-// varying by stage, if we use 1D textures for gradients!
-//const int GrGLShaderBuilder::fCoordDims = 2;
+///////////////////////////////////////////////////////////////////////////////
GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctxInfo,
GrGLUniformManager& uniformManager,
- bool explicitLocalCoords)
+ const GrGLProgramDesc& desc)
: fUniforms(kVarsPerBlock)
, fVSAttrs(kVarsPerBlock)
, fVSOutputs(kVarsPerBlock)
, fGSOutputs(kVarsPerBlock)
, fFSInputs(kVarsPerBlock)
, fFSOutputs(kMaxFSOutputs)
- , fUsesGS(false)
, fCtxInfo(ctxInfo)
, fUniformManager(uniformManager)
, fCurrentStageIdx(kNonStageIdx)
+#if GR_GL_EXPERIMENTAL_GS
+ , fUsesGS(desc.fExperimentalGS)
+#else
+ , fUsesGS(false)
+#endif
, fSetupFragPosition(false)
- , fRTHeightUniform(GrGLUniformManager::kInvalidUniformHandle) {
+ , fRTHeightUniform(GrGLUniformManager::kInvalidUniformHandle)
+ , fDstCopyTopLeftUniform (GrGLUniformManager::kInvalidUniformHandle)
+ , fDstCopyScaleUniform (GrGLUniformManager::kInvalidUniformHandle) {
fPositionVar = &fVSAttrs.push_back();
fPositionVar->set(kVec2f_GrSLType, GrGLShaderVar::kAttribute_TypeModifier, "aPosition");
- if (explicitLocalCoords) {
+ if (desc.fAttribBindings & GrDrawState::kLocalCoords_AttribBindingsBit) {
fLocalCoordsVar = &fVSAttrs.push_back();
fLocalCoordsVar->set(kVec2f_GrSLType,
GrGLShaderVar::kAttribute_TypeModifier,
} else {
fLocalCoordsVar = fPositionVar;
}
+ if (kNoDstRead_DstReadKey != desc.fDstRead) {
+ bool topDown = SkToBool(kTopLeftOrigin_DstReadKeyBit & desc.fDstRead);
+ const char* dstCopyTopLeftName;
+ const char* dstCopyCoordScaleName;
+ uint32_t configMask;
+ if (SkToBool(kUseAlphaConfig_DstReadKeyBit & desc.fDstRead)) {
+ configMask = kA_GrColorComponentFlag;
+ } else {
+ configMask = kRGBA_GrColorComponentFlags;
+ }
+ fDstCopySampler.init(this, configMask, "rgba", 0);
+
+ fDstCopyTopLeftUniform = this->addUniform(kFragment_ShaderType,
+ kVec2f_GrSLType,
+ "DstCopyUpperLeft",
+ &dstCopyTopLeftName);
+ fDstCopyScaleUniform = this->addUniform(kFragment_ShaderType,
+ kVec2f_GrSLType,
+ "DstCopyCoordScale",
+ &dstCopyCoordScaleName);
+ const char* fragPos = this->fragmentPosition();
+ this->fsCodeAppend("\t// Read color from copy of the destination.\n");
+ this->fsCodeAppendf("\tvec2 _dstTexCoord = (%s.xy - %s) * %s;\n",
+ fragPos, dstCopyTopLeftName, dstCopyCoordScaleName);
+ if (!topDown) {
+ this->fsCodeAppend("\t_dstTexCoord.y = 1.0 - _dstTexCoord.y;\n");
+ }
+ this->fsCodeAppendf("\tvec4 %s = ", kDstColorName);
+ this->appendTextureLookup(kFragment_ShaderType, fDstCopySampler, "_dstTexCoord");
+ this->fsCodeAppend(";\n\n");
+ }
+}
+
+const char* GrGLShaderBuilder::dstColor() const {
+ if (fDstCopySampler.isInitialized()) {
+ return kDstColorName;
+ } else {
+ return NULL;
+ }
}
void GrGLShaderBuilder::codeAppendf(ShaderType type, const char format[], va_list args) {
GrBackendEffectFactory::EffectKey GrGLShaderBuilder::KeyForTextureAccess(
const GrTextureAccess& access,
const GrGLCaps& caps) {
- GrBackendEffectFactory::EffectKey key = 0;
+ uint32_t configComponentMask = GrPixelConfigComponentMask(access.getTexture()->config());
+ if (swizzle_requires_alpha_remapping(caps, configComponentMask, access.swizzleMask())) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
- // Assume that swizzle support implies that we never have to modify a shader to adjust
- // for texture format/swizzle settings.
- if (!caps.textureSwizzleSupport() && swizzle_requires_alpha_remapping(caps, access)) {
- key = 1;
+GrGLShaderBuilder::DstReadKey GrGLShaderBuilder::KeyForDstRead(const GrTexture* dstCopy,
+ const GrGLCaps& caps) {
+ uint32_t key = kYesDstRead_DstReadKeyBit;
+ if (!caps.textureSwizzleSupport() && GrPixelConfigIsAlphaOnly(dstCopy->config())) {
+ // The fact that the config is alpha-only must be considered when generating code.
+ key |= kUseAlphaConfig_DstReadKeyBit;
+ }
+ if (kTopLeft_GrSurfaceOrigin == dstCopy->origin()) {
+ key |= kTopLeftOrigin_DstReadKeyBit;
}
- return key;
+ GrAssert(static_cast<DstReadKey>(key) == key);
+ return static_cast<DstReadKey>(key);
}
const GrGLenum* GrGLShaderBuilder::GetTexParamSwizzle(GrPixelConfig config, const GrGLCaps& caps) {
class GrGLContextInfo;
class GrEffectStage;
+class GrGLProgramDesc;
/**
Contains all the incremental state of a shader as it is being built,as well as helpers to
const char* swizzle() const { return fSwizzle; }
+ bool isInitialized() const { return 0 != fConfigComponentMask; }
+
private:
// The idx param is used to ensure multiple samplers within a single effect have unique
// uniform names. swizzle is a four char max string made up of chars 'r', 'g', 'b', and 'a'.
uint32_t configComponentMask,
const char* swizzle,
int idx) {
- GrAssert(0 == fConfigComponentMask);
+ GrAssert(!this->isInitialized());
+ GrAssert(0 != configComponentMask);
GrAssert(GrGLUniformManager::kInvalidUniformHandle == fSamplerUniform);
GrAssert(NULL != builder);
kFragment_ShaderType = 0x4,
};
- GrGLShaderBuilder(const GrGLContextInfo&, GrGLUniformManager&, bool explicitLocalCoords);
+ GrGLShaderBuilder(const GrGLContextInfo&, GrGLUniformManager&, const GrGLProgramDesc&);
/**
* Called by GrGLEffects to add code to one of the shaders.
static GrBackendEffectFactory::EffectKey KeyForTextureAccess(const GrTextureAccess&,
const GrGLCaps&);
+ typedef uint8_t DstReadKey;
+
+ /** Returns a key for adding code to read the copy-of-dst color in service of effects that
+ require reading the dst. It must not return 0 because 0 indicates that there is no dst
+ copy read at all. */
+ static DstReadKey KeyForDstRead(const GrTexture* dstCopy, const GrGLCaps&);
+
/** If texture swizzling is available using tex parameters then it is preferred over mangling
the generated shader code. This potentially allows greater reuse of cached shaders. */
static const GrGLenum* GetTexParamSwizzle(GrPixelConfig config, const GrGLCaps& caps);
specified explicit local coords or not in the GrDrawState. */
const GrGLShaderVar& localCoordsAttribute() const { return *fLocalCoordsVar; }
+ /** Returns the color of the destination pixel. This may be NULL if no effect advertised
+ that it will read the destination. */
+ const char* dstColor() const;
+
/**
* Are explicit local coordinates provided as input to the vertex shader.
*/
const char* fsInColor, // NULL means no incoming color
const char* fsOutColor,
SkTArray<GrGLUniformManager::UniformHandle, true>* samplerHandles);
+
GrGLUniformManager::UniformHandle getRTHeightUniform() const { return fRTHeightUniform; }
+ GrGLUniformManager::UniformHandle getDstCopyTopLeftUniform() const {
+ return fDstCopyTopLeftUniform;
+ }
+ GrGLUniformManager::UniformHandle getDstCopyScaleUniform() const {
+ return fDstCopyScaleUniform;
+ }
+ GrGLUniformManager::UniformHandle getDstCopySamplerUniform() const {
+ return fDstCopySampler.fSamplerUniform;
+ }
struct AttributePair {
void set(int index, const SkString& name) {
int fIndex;
SkString fName;
};
- const SkSTArray<10, AttributePair, true>& getEffectAttributes() const {
+ const SkTArray<AttributePair, true>& getEffectAttributes() const {
return fEffectAttributes;
}
const SkString* getEffectAttributeName(int attributeIndex) const;
VarArray fFSInputs;
SkString fGSHeader; // layout qualifiers specific to GS
VarArray fFSOutputs;
- bool fUsesGS;
private:
enum {
kNonStageIdx = -1,
};
+ // Interpretation of DstReadKey when generating code
+ enum {
+ kNoDstRead_DstReadKey = 0,
+ kYesDstRead_DstReadKeyBit = 0x1, // Set if we do a dst-copy-read.
+ kUseAlphaConfig_DstReadKeyBit = 0x2, // Set if dst-copy config is alpha only.
+ kTopLeftOrigin_DstReadKeyBit = 0x4, // Set if dst-copy origin is top-left.
+ };
+
const GrGLContextInfo& fCtxInfo;
GrGLUniformManager& fUniformManager;
int fCurrentStageIdx;
SkString fFSFunctions;
SkString fFSHeader;
+ bool fUsesGS;
+
SkString fFSCode;
SkString fVSCode;
SkString fGSCode;
bool fSetupFragPosition;
+ TextureSampler fDstCopySampler;
+
GrGLUniformManager::UniformHandle fRTHeightUniform;
+ GrGLUniformManager::UniformHandle fDstCopyTopLeftUniform;
+ GrGLUniformManager::UniformHandle fDstCopyScaleUniform;
SkSTArray<10, AttributePair, true> fEffectAttributes;
virtual void clearStencil() SK_OVERRIDE;
virtual void clearStencilClip(const GrIRect& rect,
bool insideClip) SK_OVERRIDE;
- virtual bool flushGraphicsState(DrawType) SK_OVERRIDE;
+ virtual bool flushGraphicsState(DrawType, const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
// binds texture unit in GL
void setTextureUnit(int unitIdx);
}
}
-bool GrGpuGL::flushGraphicsState(DrawType type) {
+bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstCopy) {
const GrDrawState& drawState = this->getDrawState();
// GrGpu::setupClipAndFlushState should have already checked this and bailed if not true.
srcCoeff,
dstCoeff,
this,
+ dstCopy,
&desc);
fCurrentProgram.reset(fProgramCache->getProgram(desc, stages));
color = drawState.getColor();
coverage = drawState.getCoverage();
}
- fCurrentProgram->setData(this, color, coverage, &fSharedGLProgramState);
+ fCurrentProgram->setData(this, color, coverage, dstCopy, &fSharedGLProgramState);
}
this->flushStencil(type);
this->flushScissor();