From: bsalomon@google.com Date: Fri, 29 Mar 2013 19:22:36 +0000 (+0000) Subject: Add support for reading the dst pixel value in an effect. Use in a new effect for... X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~12905 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=26e18b593ab65e4d92dfbce92579d8bc180d4c2c;p=platform%2Fupstream%2FlibSkiaSharp.git Add support for reading the dst pixel value in an effect. Use in a new effect for the kDarken xfer mode. The current implementation is to always make a copy of the entire dst before the draw. It will only succeed if the RT is also a texture. Obviously, there is lots of room for improvement. Review URL: https://codereview.chromium.org/13314002 git-svn-id: http://skia.googlecode.com/svn/trunk@8449 2bbb7eff-a529-9590-31e7-b0007b416f81 --- diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h index 0a0099f..92e240b 100644 --- a/include/core/SkXfermode.h +++ b/include/core/SkXfermode.h @@ -188,16 +188,24 @@ public: 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() diff --git a/include/gpu/GrEffect.h b/include/gpu/GrEffect.h index de4b2a1..08f34e0 100644 --- a/include/gpu/GrEffect.h +++ b/include/gpu/GrEffect.h @@ -138,6 +138,8 @@ public: /** 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(); } @@ -145,7 +147,6 @@ public: 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) { @@ -191,7 +192,7 @@ protected: */ 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. */ @@ -234,6 +235,13 @@ protected: return *static_cast(&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()) { @@ -265,6 +273,7 @@ private: SkSTArray<4, const GrTextureAccess*, true> fTextureAccesses; SkSTArray fVertexAttribTypes; + bool fWillReadDst; GrEffectRef* fEffectRef; typedef GrRefCnt INHERITED; diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h index c088bdd..5d8ecaa 100644 --- a/include/gpu/GrTexture.h +++ b/include/gpu/GrTexture.h @@ -10,6 +10,7 @@ #define GrTexture_DEFINED #include "GrSurface.h" +#include "SkPoint.h" class GrRenderTarget; class GrResourceKey; @@ -166,4 +167,42 @@ private: 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 fTexture; + SkIPoint fOffset; +}; + #endif diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp index 09710a3..705faf8 100644 --- a/src/core/SkXfermode.cpp +++ b/src/core/SkXfermode.cpp @@ -680,19 +680,19 @@ bool SkXfermode::asMode(Mode* mode) const { 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); } } @@ -940,6 +940,87 @@ void SkProcXfermode::toString(SkString* str) const { } #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::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 + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -974,9 +1055,23 @@ public: 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) diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index 64576af..4d4ba49 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -9,6 +9,7 @@ #include "GrDrawTarget.h" +#include "GrContext.h" #include "GrDrawTargetCaps.h" #include "GrRenderTarget.h" #include "GrTexture.h" @@ -38,6 +39,9 @@ GrDrawTarget::DrawInfo& GrDrawTarget::DrawInfo::operator =(const DrawInfo& di) { } else { fDevBounds = NULL; } + + fDstCopy = di.fDstCopy; + return *this; } @@ -402,6 +406,62 @@ bool GrDrawTarget::checkDraw(GrPrimitiveType type, int startVertex, 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, @@ -423,6 +483,10 @@ void GrDrawTarget::drawIndexed(GrPrimitiveType type, if (NULL != devBounds) { info.setDevBounds(*devBounds); } + // TODO: We should continue with incorrect blending. + if (!this->setupDstReadIfNecessary(&info)) { + return; + } this->onDraw(info); } } @@ -446,6 +510,10 @@ void GrDrawTarget::drawNonIndexed(GrPrimitiveType type, if (NULL != devBounds) { info.setDevBounds(*devBounds); } + // TODO: We should continue with incorrect blending. + if (!this->setupDstReadIfNecessary(&info)) { + return; + } this->onDraw(info); } } @@ -508,6 +576,10 @@ void GrDrawTarget::drawIndexedInstances(GrPrimitiveType type, 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); diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h index e54a04d..737ce86 100644 --- a/src/gpu/GrDrawTarget.h +++ b/src/gpu/GrDrawTarget.h @@ -655,24 +655,35 @@ protected: } 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: @@ -714,6 +725,10 @@ 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, }; diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp index 73e4d3e..7fc2ab8 100644 --- a/src/gpu/GrGpu.cpp +++ b/src/gpu/GrGpu.cpp @@ -330,13 +330,13 @@ const GrVertexBuffer* GrGpu::getUnitSquareVertexBuffer() const { //////////////////////////////////////////////////////////////////////////////// -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; } @@ -374,7 +374,8 @@ void GrGpu::geometrySourceWillPop(const GeometrySrcState& restoredState) { 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); @@ -387,7 +388,7 @@ void GrGpu::onStencilPath(const GrPath* path, const SkStrokeRec&, SkPath::FillTy GrAutoTRestore asr(this->drawState()->stencil()); this->setStencilPathSettings(*path, fill, this->drawState()->stencil()); - if (!this->setupClipAndFlushState(kStencilPath_DrawType)) { + if (!this->setupClipAndFlushState(kStencilPath_DrawType, NULL)) { return; } diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h index 602197b..d4e4e6b 100644 --- a/src/gpu/GrGpu.h +++ b/src/gpu/GrGpu.h @@ -366,7 +366,7 @@ protected: } // 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. @@ -482,9 +482,9 @@ private: // 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; diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 49d6562..45176cf 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -493,17 +493,27 @@ inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev, 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)); diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp index 52eb970..ed3a87c 100644 --- a/src/gpu/gl/GrGLProgram.cpp +++ b/src/gpu/gl/GrGLProgram.cpp @@ -389,7 +389,9 @@ bool GrGLProgram::compileShaders(const GrGLShaderBuilder& builder) { 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()); @@ -398,9 +400,8 @@ bool GrGLProgram::compileShaders(const GrGLShaderBuilder& builder) { 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) { @@ -417,14 +418,7 @@ bool GrGLProgram::compileShaders(const GrGLShaderBuilder& builder) { 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 @@ -496,7 +490,11 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { } // 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"); } @@ -519,7 +517,7 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { fDesc.fEffectKeys[s], inColor.size() ? inColor.c_str() : NULL, outColor.c_str(), - &fUniformHandles.fSamplerUnis[s]); + &fUniformHandles.fEffectSamplerUnis[s]); builder.setNonStage(); inColor = outColor; } @@ -598,7 +596,7 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { fDesc.fEffectKeys[s], inCoverage.size() ? inCoverage.c_str() : NULL, outCoverage.c_str(), - &fUniformHandles.fSamplerUnis[s]); + &fUniformHandles.fEffectSamplerUnis[s]); builder.setNonStage(); inCoverage = outCoverage; } @@ -674,8 +672,12 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { } 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; } @@ -749,13 +751,18 @@ bool GrGLProgram::bindOutputsAttribsAndLinkProgram(const GrGLShaderBuilder& buil 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; @@ -769,6 +776,7 @@ void GrGLProgram::initSamplerUniforms() { void GrGLProgram::setData(GrGpuGL* gpu, GrColor color, GrColor coverage, + const GrDeviceCoordTexture* dstCopy, SharedGLState* sharedState) { const GrDrawState& drawState = gpu->getDrawState(); @@ -786,6 +794,35 @@ void GrGLProgram::setData(GrGpuGL* gpu, } 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(dstCopy->offset().fX), + static_cast(dstCopy->offset().fY)); + fUniformManager.set2f(fUniformHandles.fDstCopyScaleUni, + 1.f / dstCopy->texture()->width(), + 1.f / dstCopy->texture()->height()); + GrGLTexture* texture = static_cast(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); @@ -795,9 +832,9 @@ void GrGLProgram::setData(GrGpuGL* gpu, (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(access.getTexture()); diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h index f5fd06e..27b6f80 100644 --- a/src/gpu/gl/GrGLProgram.h +++ b/src/gpu/gl/GrGLProgram.h @@ -102,7 +102,11 @@ public: * * 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, @@ -154,11 +158,18 @@ private: 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; @@ -166,6 +177,9 @@ private: fCoverageUni = GrGLUniformManager::kInvalidUniformHandle; fColorFilterUni = GrGLUniformManager::kInvalidUniformHandle; fRTHeightUni = GrGLUniformManager::kInvalidUniformHandle; + fDstCopyTopLeftUni = GrGLUniformManager::kInvalidUniformHandle; + fDstCopyScaleUni = GrGLUniformManager::kInvalidUniformHandle; + fDstCopySamplerUni = GrGLUniformManager::kInvalidUniformHandle; } }; diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp index 98280e2..6ed5473 100644 --- a/src/gpu/gl/GrGLProgramDesc.cpp +++ b/src/gpu/gl/GrGLProgramDesc.cpp @@ -9,6 +9,7 @@ #include "GrBackendEffectFactory.h" #include "GrDrawEffect.h" #include "GrEffect.h" +#include "GrGLShaderBuilder.h" #include "GrGpuGL.h" void GrGLProgramDesc::Build(const GrDrawState& drawState, @@ -17,6 +18,7 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState, GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff, const GrGpuGL* gpu, + const GrDeviceCoordTexture* dstCopy, GrGLProgramDesc* desc) { // This should already have been caught @@ -80,6 +82,7 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState, desc->fCoverageInput = kAttribute_ColorInput; } + bool readsDst = false; int lastEnabledStage = -1; for (int s = 0; s < GrDrawState::kNumStages; ++s) { @@ -93,11 +96,22 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState, 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 diff --git a/src/gpu/gl/GrGLProgramDesc.h b/src/gpu/gl/GrGLProgramDesc.h index 135d46c..b1660d8 100644 --- a/src/gpu/gl/GrGLProgramDesc.h +++ b/src/gpu/gl/GrGLProgramDesc.h @@ -10,6 +10,7 @@ #include "GrGLEffect.h" #include "GrDrawState.h" +#include "GrGLShaderBuilder.h" class GrGpuGL; @@ -18,9 +19,10 @@ 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() { @@ -48,6 +50,7 @@ public: GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff, const GrGpuGL* gpu, + const GrDeviceCoordTexture* dstCopy, GrGLProgramDesc* outDesc); private: @@ -86,6 +89,10 @@ 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 @@ -98,8 +105,10 @@ private: 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 diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp index a5c96c7..e777b31 100644 --- a/src/gpu/gl/GrGLShaderBuilder.cpp +++ b/src/gpu/gl/GrGLShaderBuilder.cpp @@ -35,15 +35,26 @@ inline const char* sample_function_name(GrSLType type, GrGLSLGeneration glslGen) } /** - * 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; } } @@ -76,16 +87,13 @@ void append_swizzle(SkString* outAppend, } -/////////////////////////////////////////////////////////////////////////////// +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) @@ -93,16 +101,22 @@ GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctxInfo, , 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, @@ -110,6 +124,45 @@ GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctxInfo, } 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) { @@ -184,14 +237,26 @@ void GrGLShaderBuilder::appendTextureLookupAndModulate( 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(key) == key); + return static_cast(key); } const GrGLenum* GrGLShaderBuilder::GetTexParamSwizzle(GrPixelConfig config, const GrGLCaps& caps) { diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h index d947771..4e39f91 100644 --- a/src/gpu/gl/GrGLShaderBuilder.h +++ b/src/gpu/gl/GrGLShaderBuilder.h @@ -19,6 +19,7 @@ class GrGLContextInfo; class GrEffectStage; +class GrGLProgramDesc; /** Contains all the incremental state of a shader as it is being built,as well as helpers to @@ -55,6 +56,8 @@ public: 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'. @@ -62,7 +65,8 @@ public: uint32_t configComponentMask, const char* swizzle, int idx) { - GrAssert(0 == fConfigComponentMask); + GrAssert(!this->isInitialized()); + GrAssert(0 != configComponentMask); GrAssert(GrGLUniformManager::kInvalidUniformHandle == fSamplerUniform); GrAssert(NULL != builder); @@ -100,7 +104,7 @@ public: 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. @@ -172,6 +176,13 @@ public: 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); @@ -230,6 +241,10 @@ public: 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. */ @@ -254,7 +269,17 @@ public: const char* fsInColor, // NULL means no incoming color const char* fsOutColor, SkTArray* 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) { @@ -263,7 +288,7 @@ public: int fIndex; SkString fName; }; - const SkSTArray<10, AttributePair, true>& getEffectAttributes() const { + const SkTArray& getEffectAttributes() const { return fEffectAttributes; } const SkString* getEffectAttributeName(int attributeIndex) const; @@ -296,25 +321,38 @@ public: 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; diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h index c95b40a..a71ff39 100644 --- a/src/gpu/gl/GrGpuGL.h +++ b/src/gpu/gl/GrGpuGL.h @@ -135,7 +135,7 @@ private: 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); diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp index 8f527f8..833d811 100644 --- a/src/gpu/gl/GrGpuGL_program.cpp +++ b/src/gpu/gl/GrGpuGL_program.cpp @@ -158,7 +158,7 @@ void GrGpuGL::flushPathStencilMatrix() { } } -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. @@ -187,6 +187,7 @@ bool GrGpuGL::flushGraphicsState(DrawType type) { srcCoeff, dstCoeff, this, + dstCopy, &desc); fCurrentProgram.reset(fProgramCache->getProgram(desc, stages)); @@ -217,7 +218,7 @@ bool GrGpuGL::flushGraphicsState(DrawType type) { color = drawState.getColor(); coverage = drawState.getCoverage(); } - fCurrentProgram->setData(this, color, coverage, &fSharedGLProgramState); + fCurrentProgram->setData(this, color, coverage, dstCopy, &fSharedGLProgramState); } this->flushStencil(type); this->flushScissor();