Add support for reading the dst pixel value in an effect. Use in a new effect for...
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 29 Mar 2013 19:22:36 +0000 (19:22 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 29 Mar 2013 19:22:36 +0000 (19:22 +0000)
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

17 files changed:
include/core/SkXfermode.h
include/gpu/GrEffect.h
include/gpu/GrTexture.h
src/core/SkXfermode.cpp
src/gpu/GrDrawTarget.cpp
src/gpu/GrDrawTarget.h
src/gpu/GrGpu.cpp
src/gpu/GrGpu.h
src/gpu/SkGpuDevice.cpp
src/gpu/gl/GrGLProgram.cpp
src/gpu/gl/GrGLProgram.h
src/gpu/gl/GrGLProgramDesc.cpp
src/gpu/gl/GrGLProgramDesc.h
src/gpu/gl/GrGLShaderBuilder.cpp
src/gpu/gl/GrGLShaderBuilder.h
src/gpu/gl/GrGpuGL.h
src/gpu/gl/GrGpuGL_program.cpp

index 0a0099f..92e240b 100644 (file)
@@ -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()
index de4b2a1..08f34e0 100644 (file)
@@ -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<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()) {
@@ -265,6 +273,7 @@ private:
 
     SkSTArray<4, const GrTextureAccess*, true>   fTextureAccesses;
     SkSTArray<kMaxVertexAttribs, GrSLType, true> fVertexAttribTypes;
+    bool                                         fWillReadDst;
     GrEffectRef*                                 fEffectRef;
 
     typedef GrRefCnt INHERITED;
index c088bdd..5d8ecaa 100644 (file)
@@ -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<GrTexture> fTexture;
+    SkIPoint                fOffset;
+};
+
 #endif
index 09710a3..705faf8 100644 (file)
@@ -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<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
+
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -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)
index 64576af..4d4ba49 100644 (file)
@@ -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, &copyRect, 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);
index e54a04d..737ce86 100644 (file)
@@ -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,
     };
index 73e4d3e..7fc2ab8 100644 (file)
@@ -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<GrStencilSettings> asr(this->drawState()->stencil());
 
     this->setStencilPathSettings(*path, fill, this->drawState()->stencil());
-    if (!this->setupClipAndFlushState(kStencilPath_DrawType)) {
+    if (!this->setupClipAndFlushState(kStencilPath_DrawType, NULL)) {
         return;
     }
 
index 602197b..d4e4e6b 100644 (file)
@@ -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;
index 49d6562..45176cf 100644 (file)
@@ -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));
index 52eb970..ed3a87c 100644 (file)
@@ -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<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);
@@ -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<GrGLTexture*>(access.getTexture());
index f5fd06e..27b6f80 100644 (file)
@@ -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;
         }
     };
 
index 98280e2..6ed5473 100644 (file)
@@ -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
index 135d46c..b1660d8 100644 (file)
@@ -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
index a5c96c7..e777b31 100644 (file)
@@ -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<DstReadKey>(key) == key);
+    return static_cast<DstReadKey>(key);
 }
 
 const GrGLenum* GrGLShaderBuilder::GetTexParamSwizzle(GrPixelConfig config, const GrGLCaps& caps) {
index d947771..4e39f91 100644 (file)
@@ -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<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) {
@@ -263,7 +288,7 @@ public:
         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;
@@ -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;
 
index c95b40a..a71ff39 100644 (file)
@@ -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);
index 8f527f8..833d811 100644 (file)
@@ -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();