Allow GrEffects with multiple textures.

It will work as long as the total number of textures sis less than GrDrawState::kNumStages. That will be fixed in a follow up CL.
Review URL: https://codereview.appspot.com/7040052

git-svn-id: http://skia.googlecode.com/svn/trunk@7023 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/SkBlendImageFilter.cpp b/src/effects/SkBlendImageFilter.cpp
index 1394a0a..d2b9e73 100644
--- a/src/effects/SkBlendImageFilter.cpp
+++ b/src/effects/SkBlendImageFilter.cpp
@@ -132,16 +132,18 @@
     virtual void setData(const GrGLUniformManager&, const GrEffectStage&);
 
 private:
+    SkBlendImageFilter::Mode    fMode;
+    GrGLEffectMatrix            fForegroundEffectMatrix;
+    GrGLEffectMatrix            fBackgroundEffectMatrix;
+
     typedef GrGLEffect INHERITED;
-    SkBlendImageFilter::Mode fMode;
-    GrGLEffectMatrix fEffectMatrix;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class GrBlendEffect : public GrSingleTextureEffect {
+class GrBlendEffect : public GrEffect {
 public:
-    GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground, const SkMatrix&);
+    GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground, GrTexture* background);
     virtual ~GrBlendEffect();
 
     virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
@@ -151,9 +153,14 @@
     typedef GrGLBlendEffect GLEffect;
     static const char* Name() { return "Blend"; }
 
+    virtual const GrTextureAccess& textureAccess(int index) const SK_OVERRIDE;
+
 private:
-    typedef GrSingleTextureEffect INHERITED;
-    SkBlendImageFilter::Mode fMode;
+    GrTextureAccess             fForegroundAccess;
+    GrTextureAccess             fBackgroundAccess;
+    SkBlendImageFilter::Mode    fMode;
+
+    typedef GrEffect INHERITED;
 };
 
 // FIXME:  This should be refactored with SkSingleInputImageFilter's version.
@@ -209,14 +216,9 @@
     GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
     GrContext::AutoClip ac(context, rect);
 
-    SkMatrix backgroundTexMatrix, foregroundTexMatrix;
-    backgroundTexMatrix.setIDiv(background->width(), background->height());
-    foregroundTexMatrix.setIDiv(foreground->width(), foreground->height());
     GrPaint paint;
     paint.colorStage(0)->setEffect(
-        SkNEW_ARGS(GrSingleTextureEffect, (background.get(), backgroundTexMatrix)))->unref();
-    paint.colorStage(1)->setEffect(
-        SkNEW_ARGS(GrBlendEffect, (fMode, foreground.get(), foregroundTexMatrix)))->unref();
+        SkNEW_ARGS(GrBlendEffect, (fMode, foreground.get(), background.get())))->unref();
     context->drawRect(paint, rect);
     return dst;
 }
@@ -225,8 +227,11 @@
 
 GrBlendEffect::GrBlendEffect(SkBlendImageFilter::Mode mode,
                              GrTexture* foreground,
-                             const SkMatrix& matrix)
-    : INHERITED(foreground, matrix), fMode(mode) {
+                             GrTexture* background)
+    : INHERITED(2)
+    , fForegroundAccess(foreground)
+    , fBackgroundAccess(background)
+    , fMode(mode) {
 }
 
 GrBlendEffect::~GrBlendEffect() {
@@ -241,10 +246,14 @@
     return GrTBackendEffectFactory<GrBlendEffect>::getInstance();
 }
 
+const GrTextureAccess& GrBlendEffect::textureAccess(int index) const {
+    SkASSERT(index >= 0 && index < 2);
+    return (0 == index) ? fForegroundAccess : fBackgroundAccess;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
-GrGLBlendEffect::GrGLBlendEffect(const GrBackendEffectFactory& factory,
-                                 const GrEffect& effect)
+GrGLBlendEffect::GrGLBlendEffect(const GrBackendEffectFactory& factory, const GrEffect& effect)
     : INHERITED(factory),
       fMode(static_cast<const GrBlendEffect&>(effect).mode()) {
 }
@@ -260,14 +269,21 @@
                                const char* inputColor,
                                const TextureSamplerArray& samplers) {
     const char* coords;
-    GrSLType coordsType =  fEffectMatrix.emitCode(builder, key, vertexCoords, &coords);
+    GrSLType fgCoordsType =  fForegroundEffectMatrix.emitCode(builder, key, vertexCoords, &coords, NULL, "FG");
+    GrSLType bgCoordsType =  fBackgroundEffectMatrix.emitCode(builder, key, vertexCoords, &coords, NULL, "BG");
 
     SkString* code = &builder->fFSCode;
-    const char* bgColor = inputColor;
+    const char* bgColor = "bgColor";
     const char* fgColor = "fgColor";
+
     code->appendf("\t\tvec4 %s = ", fgColor);
-    builder->appendTextureLookup(code, samplers[0], coords, coordsType);
+    builder->appendTextureLookup(code, samplers[0], coords, fgCoordsType);
     code->append(";\n");
+
+    code->appendf("\t\tvec4 %s = ", bgColor);
+    builder->appendTextureLookup(code, samplers[1], coords, bgCoordsType);
+    code->append(";\n");
+
     code->appendf("\t\t%s.a = 1.0 - (1.0 - %s.a) * (1.0 - %s.b);\n", outputColor, bgColor, fgColor);
     switch (fMode) {
       case SkBlendImageFilter::kNormal_Mode:
@@ -290,14 +306,35 @@
 
 void GrGLBlendEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
     const GrBlendEffect& blend = static_cast<const GrBlendEffect&>(*stage.getEffect());
-    fEffectMatrix.setData(uman, blend.getMatrix(), stage.getCoordChangeMatrix(), blend.texture(0));
+    GrTexture* fgTex = blend.texture(0);
+    GrTexture* bgTex = blend.texture(1);
+    fForegroundEffectMatrix.setData(uman,
+                                    GrEffect::MakeDivByTextureWHMatrix(fgTex),
+                                    stage.getCoordChangeMatrix(),
+                                    fgTex);
+    fBackgroundEffectMatrix.setData(uman,
+                                    GrEffect::MakeDivByTextureWHMatrix(bgTex),
+                                    stage.getCoordChangeMatrix(),
+                                    bgTex);
+
 }
 
 GrGLEffect::EffectKey GrGLBlendEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) {
     const GrBlendEffect& blend = static_cast<const GrBlendEffect&>(*stage.getEffect());
-    EffectKey key =
-        GrGLEffectMatrix::GenKey(blend.getMatrix(), stage.getCoordChangeMatrix(), blend.texture(0));
-    key |= (blend.mode() << GrGLEffectMatrix::kKeyBits);
-    return key;
+
+    GrTexture* fgTex = blend.texture(0);
+    GrTexture* bgTex = blend.texture(1);
+
+    EffectKey fgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(fgTex),
+                                               stage.getCoordChangeMatrix(),
+                                               fgTex);
+
+    EffectKey bgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(bgTex),
+                                               stage.getCoordChangeMatrix(),
+                                               bgTex);
+    bgKey <<= GrGLEffectMatrix::kKeyBits;
+    EffectKey modeKey = blend.mode() << (2 * GrGLEffectMatrix::kKeyBits);
+
+    return  modeKey | bgKey | fgKey;
 }
 #endif
diff --git a/src/gpu/effects/GrSingleTextureEffect.h b/src/gpu/effects/GrSingleTextureEffect.h
index 709d3dc..a2f8bff 100644
--- a/src/gpu/effects/GrSingleTextureEffect.h
+++ b/src/gpu/effects/GrSingleTextureEffect.h
@@ -10,9 +10,9 @@
 
 #include "GrEffect.h"
 #include "SkMatrix.h"
-#include "GrTexture.h"
 
 class GrGLSingleTextureEffect;
+class GrTexture;
 
 /**
  * An effect that draws a single texture with a texture matrix; commonly used as a base class. The
@@ -48,13 +48,6 @@
         return INHERITED::isEqual(effect) && fMatrix.cheapEqualTo(ste.getMatrix());
     }
 
-    static inline SkMatrix MakeDivByTextureWHMatrix(const GrTexture* texture) {
-        GrAssert(NULL != texture);
-        SkMatrix mat;
-        mat.setIDiv(texture->width(), texture->height());
-        return mat;
-    }
-
 private:
     GR_DECLARE_EFFECT_TEST;
 
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 0558701..b57177b 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -10,6 +10,7 @@
 #include "GrAllocator.h"
 #include "GrEffect.h"
 #include "GrGLEffect.h"
+#include "GrGpuGL.h"
 #include "GrGLShaderVar.h"
 #include "GrBackendEffectFactory.h"
 #include "SkTrace.h"
@@ -32,16 +33,6 @@
     s->appendS32(coordIdx);
 }
 
-inline const char* float_vector_type_str(int count) {
-    return GrGLShaderVar::TypeString(GrSLFloatVectorType(count));
-}
-
-inline const char* vector_all_coords(int count) {
-    static const char* ALL[] = {"ERROR", "", ".xy", ".xyz", ".xyzw"};
-    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ALL));
-    return ALL[count];
-}
-
 inline const char* declared_color_output_name() { return "fsColorOut"; }
 inline const char* dual_source_output_name() { return "dualSourceOut"; }
 
@@ -126,12 +117,14 @@
     }
 }
 
+namespace {
+
 // given two blend coeffecients determine whether the src
 // and/or dst computation can be omitted.
-static inline void needBlendInputs(SkXfermode::Coeff srcCoeff,
-                                   SkXfermode::Coeff dstCoeff,
-                                   bool* needSrcValue,
-                                   bool* needDstValue) {
+inline void need_blend_inputs(SkXfermode::Coeff srcCoeff,
+                              SkXfermode::Coeff dstCoeff,
+                              bool* needSrcValue,
+                              bool* needDstValue) {
     if (SkXfermode::kZero_Coeff == srcCoeff) {
         switch (dstCoeff) {
             // these all read the src
@@ -170,9 +163,9 @@
  * Create a blend_coeff * value string to be used in shader code. Sets empty
  * string if result is trivially zero.
  */
-static void blendTermString(SkString* str, SkXfermode::Coeff coeff,
-                             const char* src, const char* dst,
-                             const char* value) {
+inline void blend_term_string(SkString* str, SkXfermode::Coeff coeff,
+                       const char* src, const char* dst,
+                       const char* value) {
     switch (coeff) {
     case SkXfermode::kZero_Coeff:    /** 0 */
         *str = "";
@@ -213,19 +206,20 @@
  * Adds a line to the fragment shader code which modifies the color by
  * the specified color filter.
  */
-static void addColorFilter(SkString* fsCode, const char * outputVar,
-                           SkXfermode::Coeff uniformCoeff,
-                           SkXfermode::Coeff colorCoeff,
-                           const char* filterColor,
-                           const char* inColor) {
+void add_color_filter(SkString* fsCode, const char * outputVar,
+                      SkXfermode::Coeff uniformCoeff,
+                      SkXfermode::Coeff colorCoeff,
+                      const char* filterColor,
+                      const char* inColor) {
     SkString colorStr, constStr;
-    blendTermString(&colorStr, colorCoeff, filterColor, inColor, inColor);
-    blendTermString(&constStr, uniformCoeff, filterColor, inColor, filterColor);
+    blend_term_string(&colorStr, colorCoeff, filterColor, inColor, inColor);
+    blend_term_string(&constStr, uniformCoeff, filterColor, inColor, filterColor);
 
     fsCode->appendf("\t%s = ", outputVar);
     GrGLSLAdd4f(fsCode, colorStr.c_str(), constStr.c_str());
     fsCode->append(";\n");
 }
+}
 
 bool GrGLProgram::genEdgeCoverage(SkString* coverageVar,
                                   GrGLShaderBuilder* builder) const {
@@ -308,8 +302,8 @@
             } break;
         case GrGLProgram::Desc::kUniform_ColorInput: {
             const char* name;
-            fUniforms.fColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
-                                                      kVec4f_GrSLType, "Color", &name);
+            fUniformHandles.fColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                            kVec4f_GrSLType, "Color", &name);
             *inColor = name;
             break;
         }
@@ -326,8 +320,8 @@
 
 void GrGLProgram::genUniformCoverage(GrGLShaderBuilder* builder, SkString* inOutCoverage) {
     const char* covUniName;
-    fUniforms.fCoverageUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
-                                                 kVec4f_GrSLType, "Coverage", &covUniName);
+    fUniformHandles.fCoverageUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                       kVec4f_GrSLType, "Coverage", &covUniName);
     if (inOutCoverage->size()) {
         builder->fFSCode.appendf("\tvec4 uniCoverage = %s * %s;\n",
                                   covUniName, inOutCoverage->c_str());
@@ -542,8 +536,8 @@
 
     bool needColorFilterUniform;
     bool needComputedColor;
-    needBlendInputs(uniformCoeff, colorCoeff,
-                    &needColorFilterUniform, &needComputedColor);
+    need_blend_inputs(uniformCoeff, colorCoeff,
+                      &needColorFilterUniform, &needComputedColor);
 
     // the dual source output has no canonical var name, have to
     // declare an output, which is incompatible with gl_FragColor/gl_FragData.
@@ -560,8 +554,8 @@
     }
 
     const char* viewMName;
-    fUniforms.fViewMatrixUni = builder.addUniform(GrGLShaderBuilder::kVertex_ShaderType,
-                                                  kMat33f_GrSLType, "ViewM", &viewMName);
+    fUniformHandles.fViewMatrixUni = builder.addUniform(GrGLShaderBuilder::kVertex_ShaderType,
+                                                        kMat33f_GrSLType, "ViewM", &viewMName);
 
 
     builder.fVSCode.appendf("\tvec3 pos3 = %s * vec3(%s, 1);\n"
@@ -617,13 +611,12 @@
                 }
 
                 builder.setCurrentStage(s);
-                fEffects[s] = GenStageCode(*stages[s],
-                                           fDesc.fEffectKeys[s],
-                                           &fUniforms.fStages[s],
-                                           inColor.size() ? inColor.c_str() : NULL,
-                                           outColor.c_str(),
-                                           inCoords,
-                                           &builder);
+                fEffects[s] = builder.createAndEmitGLEffect(*stages[s],
+                                                            fDesc.fEffectKeys[s],
+                                                            inColor.size() ? inColor.c_str() : NULL,
+                                                            outColor.c_str(),
+                                                            inCoords,
+                                                            &fUniformHandles.fSamplerUnis[s]);
                 builder.setNonStage();
                 inColor = outColor;
             }
@@ -639,15 +632,16 @@
         if (uniformCoeffIsZero) {
             uniformCoeff = SkXfermode::kZero_Coeff;
             bool bogus;
-            needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff,
-                            &needColorFilterUniform, &bogus);
+            need_blend_inputs(SkXfermode::kZero_Coeff, colorCoeff,
+                              &needColorFilterUniform, &bogus);
         }
     }
     const char* colorFilterColorUniName = NULL;
     if (needColorFilterUniform) {
-        fUniforms.fColorFilterUni = builder.addUniform(GrGLShaderBuilder::kFragment_ShaderType,
-                                                       kVec4f_GrSLType, "FilterColor",
-                                                       &colorFilterColorUniName);
+        fUniformHandles.fColorFilterUni = builder.addUniform(
+                                                        GrGLShaderBuilder::kFragment_ShaderType,
+                                                        kVec4f_GrSLType, "FilterColor",
+                                                        &colorFilterColorUniName);
     }
     bool wroteFragColorZero = false;
     if (SkXfermode::kZero_Coeff == uniformCoeff &&
@@ -659,7 +653,7 @@
     } else if (SkXfermode::kDst_Mode != fDesc.fColorFilterXfermode) {
         builder.fFSCode.append("\tvec4 filteredColor;\n");
         const char* color = adjustInColor(inColor);
-        addColorFilter(&builder.fFSCode, "filteredColor", uniformCoeff,
+        add_color_filter(&builder.fFSCode, "filteredColor", uniformCoeff,
                        colorCoeff, colorFilterColorUniName, color);
         inColor = "filteredColor";
     }
@@ -722,13 +716,13 @@
                         inCoverage.append("4");
                     }
                     builder.setCurrentStage(s);
-                    fEffects[s] = GenStageCode(*stages[s],
-                                               fDesc.fEffectKeys[s],
-                                               &fUniforms.fStages[s],
-                                               inCoverage.size() ? inCoverage.c_str() : NULL,
-                                               outCoverage.c_str(),
-                                               inCoords,
-                                               &builder);
+                    fEffects[s] = builder.createAndEmitGLEffect(
+                                                    *stages[s],
+                                                    fDesc.fEffectKeys[s],
+                                                    inCoverage.size() ? inCoverage.c_str() : NULL,
+                                                    outCoverage.c_str(),
+                                                    inCoords,
+                                                    &fUniformHandles.fSamplerUnis[s]);
                     builder.setNonStage();
                     inCoverage = outCoverage;
                 }
@@ -803,7 +797,7 @@
 
     builder.finished(fProgramID);
     this->initSamplerUniforms();
-    fUniforms.fRTHeight = builder.getRTHeightUniform();
+    fUniformHandles.fRTHeightUni = builder.getRTHeightUniform();
 
     return true;
 }
@@ -874,72 +868,48 @@
 
 void GrGLProgram::initSamplerUniforms() {
     GL_CALL(UseProgram(fProgramID));
+    // We simply bind the uniforms to successive texture units beginning at 0. setData() assumes this
+    // behavior.
+    GrGLint texUnitIdx = 0;
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        int count = fUniforms.fStages[s].fSamplerUniforms.count();
-        // FIXME: We're still always reserving one texture per stage. After GrTextureParams are
-        // expressed by the effect rather than the GrEffectStage we can move texture binding
-        // into GrGLProgram and it should be easier to fix this.
-        GrAssert(count <= 1);
-        for (int t = 0; t < count; ++t) {
-            UniformHandle uh = fUniforms.fStages[s].fSamplerUniforms[t];
-            if (GrGLUniformManager::kInvalidUniformHandle != uh) {
-                fUniformManager.setSampler(uh, s);
+        int numSamplers = fUniformHandles.fSamplerUnis[s].count();
+        for (int u = 0; u < numSamplers; ++u) {
+            UniformHandle handle = fUniformHandles.fSamplerUnis[s][u];
+            if (GrGLUniformManager::kInvalidUniformHandle != handle) {
+                fUniformManager.setSampler(handle, texUnitIdx);
+                ++texUnitIdx;
             }
         }
     }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// Stage code generation
 
-// TODO: Move this function to GrGLShaderBuilder
-GrGLEffect* GrGLProgram::GenStageCode(const GrEffectStage& stage,
-                                      GrGLEffect::EffectKey key,
-                                      StageUniforms* uniforms,
-                                      const char* fsInColor, // NULL means no incoming color
-                                      const char* fsOutColor,
-                                      const char* vsInCoord,
-                                      GrGLShaderBuilder* builder) {
+void GrGLProgram::setData(GrGpuGL* gpu) {
+    const GrDrawState& drawState = gpu->getDrawState();
 
-    const GrEffect* effect = stage.getEffect();
-    GrGLEffect* glEffect = effect->getFactory().createGLInstance(*effect);
-
-    // setup texture samplers for GL effect
-    int numTextures = effect->numTextures();
-    SkSTArray<8, GrGLShaderBuilder::TextureSampler> textureSamplers;
-    textureSamplers.push_back_n(numTextures);
-    for (int i = 0; i < numTextures; ++i) {
-        textureSamplers[i].init(builder, &effect->textureAccess(i));
-        uniforms->fSamplerUniforms.push_back(textureSamplers[i].fSamplerUniform);
-    }
-
-    // Enclose custom code in a block to avoid namespace conflicts
-    builder->fVSCode.appendf("\t{ // %s\n", glEffect->name());
-    builder->fFSCode.appendf("\t{ // %s \n", glEffect->name());
-    glEffect->emitCode(builder,
-                       stage,
-                       key,
-                       vsInCoord,
-                       fsOutColor,
-                       fsInColor,
-                       textureSamplers);
-    builder->fVSCode.appendf("\t}\n");
-    builder->fFSCode.appendf("\t}\n");
-
-    return glEffect;
-}
-
-void GrGLProgram::setData(const GrDrawState& drawState) {
     int rtHeight = drawState.getRenderTarget()->height();
-    if (GrGLUniformManager::kInvalidUniformHandle != fUniforms.fRTHeight && fRTHeight != rtHeight) {
-        fUniformManager.set1f(fUniforms.fRTHeight, SkIntToScalar(rtHeight));
+    if (GrGLUniformManager::kInvalidUniformHandle != fUniformHandles.fRTHeightUni &&
+        fRTHeight != rtHeight) {
+        fUniformManager.set1f(fUniformHandles.fRTHeightUni, SkIntToScalar(rtHeight));
         fRTHeight = rtHeight;
     }
+    GrGLint texUnitIdx = 0;
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
         if (NULL != fEffects[s]) {
             const GrEffectStage& stage = drawState.getStage(s);
             GrAssert(NULL != stage.getEffect());
             fEffects[s]->setData(fUniformManager, stage);
+            int numSamplers = fUniformHandles.fSamplerUnis[s].count();
+            for (int u = 0; u < numSamplers; ++u) {
+                UniformHandle handle = fUniformHandles.fSamplerUnis[s][u];
+                if (GrGLUniformManager::kInvalidUniformHandle != handle) {
+                    const GrTextureAccess& access = stage.getEffect()->textureAccess(u);
+                    GrGLTexture* texture = static_cast<GrGLTexture*>(access.getTexture());
+                    gpu->bindTexture(texUnitIdx, access.getParams(), texture);
+                    ++texUnitIdx;
+                }
+            }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 3902a8c..e334391 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -48,7 +48,9 @@
 
     virtual ~GrGLProgram();
 
-    /** Call to abandon GL objects owned by this program */
+    /**
+     * Call to abandon GL objects owned by this program.
+     */
     void abandon();
 
     /**
@@ -68,10 +70,11 @@
     static int TexCoordAttributeIdx(int tcIdx) { return 4 + tcIdx; }
 
     /**
-     * This function uploads uniforms and calls each GrEffect's setData. It is called before a draw
-     * occurs using the program after the program has already been bound.
+     * This function uploads uniforms and calls each GrGLEffect's setData. It is called before a
+     * draw occurs using the program after the program has already been bound. It also uses the
+     * GrGpuGL object to bind the textures required by the GrGLEffects.
      */
-    void setData(const GrDrawState& drawState);
+    void setData(GrGpuGL*);
 
     // Parameters that affect code generation
     // This structs should be kept compact; it is input to an expensive hash key generator.
@@ -114,30 +117,24 @@
         bool                        fDiscardIfOutsideEdge;
 
         // stripped of bits that don't affect program generation
-        GrVertexLayout fVertexLayout;
+        GrVertexLayout              fVertexLayout;
 
         /** Non-zero if this stage has an effect */
-        GrGLEffect::EffectKey fEffectKeys[GrDrawState::kNumStages];
+        GrGLEffect::EffectKey       fEffectKeys[GrDrawState::kNumStages];
 
         // To enable experimental geometry shader code (not for use in
         // production)
 #if GR_GL_EXPERIMENTAL_GS
-        bool fExperimentalGS;
+        bool                        fExperimentalGS;
 #endif
-
-        uint8_t fColorInput;        // casts to enum ColorInput
-        uint8_t fCoverageInput;     // casts to enum CoverageInput
-        uint8_t fDualSrcOutput;     // casts to enum DualSrcOutput
-        int8_t fFirstCoverageStage;
-        SkBool8 fEmitsPointSize;
-
-        uint8_t fColorFilterXfermode;  // casts to enum SkXfermode::Mode
+        uint8_t                     fColorInput;            // casts to enum ColorInput
+        uint8_t                     fCoverageInput;         // casts to enum ColorInput
+        uint8_t                     fDualSrcOutput;         // casts to enum DualSrcOutput
+        int8_t                      fFirstCoverageStage;
+        SkBool8                     fEmitsPointSize;
+        uint8_t                     fColorFilterXfermode;   // casts to enum SkXfermode::Mode
     };
-    GR_STATIC_ASSERT(!(sizeof(Desc) % 4));
-
 private:
-    struct StageUniforms;
-
     GrGLProgram(const GrGLContextInfo& gl,
                 const Desc& desc,
                 const GrEffectStage* stages[]);
@@ -151,14 +148,6 @@
 
     void genInputColor(GrGLShaderBuilder* builder, SkString* inColor);
 
-    static GrGLEffect* GenStageCode(const GrEffectStage& stage,
-                                    GrGLEffect::EffectKey key,
-                                    StageUniforms* stageUniforms, // TODO: Eliminate this
-                                    const char* fsInColor, // NULL means no incoming color
-                                    const char* fsOutColor,
-                                    const char* vsInCoord,
-                                    GrGLShaderBuilder* builder);
-
     void genGeometryShader(GrGLShaderBuilder* segments) const;
 
     typedef GrGLUniformManager::UniformHandle UniformHandle;
@@ -183,41 +172,39 @@
 
     const char* adjustInColor(const SkString& inColor) const;
 
-    struct StageUniforms {
-        SkTArray<UniformHandle, true> fSamplerUniforms;
-    };
-
-    struct Uniforms {
-        UniformHandle fViewMatrixUni;
-        UniformHandle fColorUni;
-        UniformHandle fCoverageUni;
-        UniformHandle fColorFilterUni;
+    typedef SkSTArray<4, UniformHandle, true> SamplerUniSArray;
+    
+    struct UniformHandles {
+        UniformHandle       fViewMatrixUni;
+        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 fRTHeight;
-        StageUniforms fStages[GrDrawState::kNumStages];
-        Uniforms() {
+        UniformHandle       fRTHeightUni;
+        // An array of sampler uniform handles for each effect.
+        SamplerUniSArray    fSamplerUnis[GrDrawState::kNumStages];
+        
+        UniformHandles() {
             fViewMatrixUni = GrGLUniformManager::kInvalidUniformHandle;
             fColorUni = GrGLUniformManager::kInvalidUniformHandle;
             fCoverageUni = GrGLUniformManager::kInvalidUniformHandle;
             fColorFilterUni = GrGLUniformManager::kInvalidUniformHandle;
-            fRTHeight = GrGLUniformManager::kInvalidUniformHandle;
+            fRTHeightUni = GrGLUniformManager::kInvalidUniformHandle;
         }
     };
 
-    // IDs
-    GrGLuint    fVShaderID;
-    GrGLuint    fGShaderID;
-    GrGLuint    fFShaderID;
-    GrGLuint    fProgramID;
-
+    // GL IDs
+    GrGLuint                    fVShaderID;
+    GrGLuint                    fGShaderID;
+    GrGLuint                    fFShaderID;
+    GrGLuint                    fProgramID;
     // The matrix sent to GL is determined by both the client's matrix and
     // the size of the viewport.
-    SkMatrix  fViewMatrix;
-    SkISize   fViewportSize;
+    SkMatrix                    fViewMatrix;
+    SkISize                     fViewportSize;
 
-    // these reflect the current values of uniforms
-    // (GL uniform values travel with program)
+    // these reflect the current values of uniforms (GL uniform values travel with program)
     GrColor                     fColor;
     GrColor                     fCoverage;
     GrColor                     fColorFilterColor;
@@ -225,11 +212,11 @@
 
     GrGLEffect*                 fEffects[GrDrawState::kNumStages];
 
-    Desc fDesc;
+    Desc                        fDesc;
     const GrGLContextInfo&      fContextInfo;
 
     GrGLUniformManager          fUniformManager;
-    Uniforms                    fUniforms;
+    UniformHandles              fUniformHandles;
 
     friend class GrGpuGL; // TODO: remove this by adding getters and moving functionality.
 
diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp
index 8239740..c00cf91 100644
--- a/src/gpu/gl/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -412,3 +412,39 @@
 void GrGLShaderBuilder::finished(GrGLuint programID) {
     fUniformManager.getUniformLocations(programID, fUniforms);
 }
+
+GrGLEffect* GrGLShaderBuilder::createAndEmitGLEffect(
+                                const GrEffectStage& stage,
+                                GrGLEffect::EffectKey key,
+                                const char* fsInColor,
+                                const char* fsOutColor,
+                                const char* vsInCoord,
+                                SkTArray<GrGLUniformManager::UniformHandle, true>* samplerHandles) {
+    GrAssert(NULL != stage.getEffect());
+
+    const GrEffect& effect = *stage.getEffect();
+    int numTextures = effect.numTextures();
+    SkSTArray<8, GrGLShaderBuilder::TextureSampler> textureSamplers;
+    textureSamplers.push_back_n(numTextures);
+    for (int i = 0; i < numTextures; ++i) {
+        textureSamplers[i].init(this, &effect.textureAccess(i), i);
+        samplerHandles->push_back(textureSamplers[i].fSamplerUniform);
+    }
+
+    GrGLEffect* glEffect = effect.getFactory().createGLInstance(effect);
+
+    // Enclose custom code in a block to avoid namespace conflicts
+    this->fVSCode.appendf("\t{ // %s\n", glEffect->name());
+    this->fFSCode.appendf("\t{ // %s \n", glEffect->name());
+    glEffect->emitCode(this,
+                       stage,
+                       key,
+                       vsInCoord,
+                       fsOutColor,
+                       fsInColor,
+                       textureSamplers);
+    this->fVSCode.appendf("\t}\n");
+    this->fFSCode.appendf("\t}\n");
+
+    return glEffect;
+}
diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h
index 852079a..8934280 100644
--- a/src/gpu/gl/GrGLShaderBuilder.h
+++ b/src/gpu/gl/GrGLShaderBuilder.h
@@ -11,7 +11,6 @@
 #include "GrAllocator.h"
 #include "GrBackendEffectFactory.h"
 #include "GrEffect.h"
-#include "gl/GrGLShaderVar.h"
 #include "gl/GrGLSL.h"
 #include "gl/GrGLUniformManager.h"
 
@@ -24,7 +23,7 @@
 class GrGLShaderBuilder {
 public:
     /**
-     * Used by GrGLEffects to add texture reads to their shader code.
+     * Passed to GrGLEffects to add texture reads to their shader code.
      */
     class TextureSampler {
     public:
@@ -46,15 +45,19 @@
         const GrTextureAccess* textureAccess() const { return fTextureAccess; }
 
     private:
-        void init(GrGLShaderBuilder* builder, const GrTextureAccess* access) {
+        // The idx param is used to ensure multiple samplers within a single effect have unique
+        // uniform names.
+        void init(GrGLShaderBuilder* builder, const GrTextureAccess* access, int idx) {
             GrAssert(NULL == fTextureAccess);
             GrAssert(GrGLUniformManager::kInvalidUniformHandle == fSamplerUniform);
 
             GrAssert(NULL != builder);
             GrAssert(NULL != access);
+            SkString name;
+            name.printf("Sampler%d_", idx);
             fSamplerUniform = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
                                                   kSampler2D_GrSLType,
-                                                  "Sampler");
+                                                  name.c_str());
             GrAssert(GrGLUniformManager::kInvalidUniformHandle != fSamplerUniform);
 
             fTextureAccess = access;
@@ -160,25 +163,31 @@
       */
     const GrGLShaderVar& positionAttribute() const { return *fPositionVar; }
 
+    /**
+     * Interfaces used by GrGLProgram.
+     * TODO: Hide these from the GrEffects using friend or splitting this into two related classes.
+     * Also, GrGLProgram's shader string construction should be moved to this class.
+     */
+
     /** Called after building is complete to get the final shader string. */
     void getShader(ShaderType, SkString*) const;
 
-    /**
-     * TODO: Make this do all the compiling, linking, etc. Hide from the GrEffects
-     */
-    void finished(GrGLuint programID);
-
-    /**
-     * Sets the current stage (used to make variable names unique).
-     * TODO: Hide from the GrEffects
-     */
     void setCurrentStage(int stageIdx) { fCurrentStageIdx = stageIdx; }
     void setNonStage() { fCurrentStageIdx = kNonStageIdx; }
-
+    // TODO: move remainder of shader code generation to this class and call this privately
+    // Handles of sampler uniforms generated for the effect are appended to samplerHandles.
+    GrGLEffect* createAndEmitGLEffect(
+                                const GrEffectStage& stage,
+                                GrBackendEffectFactory::EffectKey key,
+                                const char* fsInColor, // NULL means no incoming color
+                                const char* fsOutColor,
+                                const char* vsInCoord,                                      
+                                SkTArray<GrGLUniformManager::UniformHandle, true>* samplerHandles);
     GrGLUniformManager::UniformHandle getRTHeightUniform() const { return fRTHeightUniform; }
+    // TODO: Make this do all the compiling, linking, etc.
+    void finished(GrGLuint programID);
 
 private:
-
     typedef GrTAllocator<GrGLShaderVar> VarArray;
 
     void appendDecls(const VarArray&, SkString*) const;
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index f01fd3f..6e39637 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -2024,43 +2024,25 @@
 
 }
 
-void GrGpuGL::flushBoundTextureAndParams(int stageIdx) {
-    GrDrawState* drawState = this->drawState();
-    // FIXME: Assuming at most one texture per effect
-    const GrEffect* effect = drawState->stage(stageIdx)->getEffect();
-    if (effect->numTextures() > 0) {
-        GrGLTexture* nextTexture =  static_cast<GrGLTexture*>(effect->texture(0));
-        if (NULL != nextTexture) {
-            const GrTextureParams& texParams = effect->textureAccess(0).getParams();
-            this->flushBoundTextureAndParams(stageIdx, texParams, nextTexture);
-        }
-    }
-}
+void GrGpuGL::bindTexture(int unitIdx, const GrTextureParams& params, GrGLTexture* texture) {
+    GrAssert(NULL != texture);
 
-void GrGpuGL::flushBoundTextureAndParams(int stageIdx,
-                                         const GrTextureParams& params,
-                                         GrGLTexture* nextTexture) {
-
-    // true for now, but maybe not with GrEffect.
-    GrAssert(NULL != nextTexture);
     // If we created a rt/tex and rendered to it without using a texture and now we're texturing
     // from the rt it will still be the last bound texture, but it needs resolving. So keep this
     // out of the "last != next" check.
-    GrGLRenderTarget* texRT =  static_cast<GrGLRenderTarget*>(nextTexture->asRenderTarget());
+    GrGLRenderTarget* texRT =  static_cast<GrGLRenderTarget*>(texture->asRenderTarget());
     if (NULL != texRT) {
         this->onResolveRenderTarget(texRT);
     }
 
-    if (fHWBoundTextures[stageIdx] != nextTexture) {
-        this->setTextureUnit(stageIdx);
-        GL_CALL(BindTexture(GR_GL_TEXTURE_2D, nextTexture->textureID()));
-        //GrPrintf("---- bindtexture %d\n", nextTexture->textureID());
-        fHWBoundTextures[stageIdx] = nextTexture;
+    if (fHWBoundTextures[unitIdx] != texture) {
+        this->setTextureUnit(unitIdx);
+        GL_CALL(BindTexture(GR_GL_TEXTURE_2D, texture->textureID()));
+        fHWBoundTextures[unitIdx] = texture;
     }
 
     ResetTimestamp timestamp;
-    const GrGLTexture::TexParams& oldTexParams =
-                            nextTexture->getCachedTexParams(&timestamp);
+    const GrGLTexture::TexParams& oldTexParams = texture->getCachedTexParams(&timestamp);
     bool setAll = timestamp < this->getResetTimestamp();
     GrGLTexture::TexParams newTexParams;
 
@@ -2069,10 +2051,10 @@
     newTexParams.fWrapS = tile_to_gl_wrap(params.getTileModeX());
     newTexParams.fWrapT = tile_to_gl_wrap(params.getTileModeY());
     memcpy(newTexParams.fSwizzleRGBA,
-           GrGLShaderBuilder::GetTexParamSwizzle(nextTexture->config(), this->glCaps()),
+           GrGLShaderBuilder::GetTexParamSwizzle(texture->config(), this->glCaps()),
            sizeof(newTexParams.fSwizzleRGBA));
     if (setAll || newTexParams.fFilter != oldTexParams.fFilter) {
-        this->setTextureUnit(stageIdx);
+        this->setTextureUnit(unitIdx);
         GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
                               GR_GL_TEXTURE_MAG_FILTER,
                               newTexParams.fFilter));
@@ -2081,13 +2063,13 @@
                               newTexParams.fFilter));
     }
     if (setAll || newTexParams.fWrapS != oldTexParams.fWrapS) {
-        this->setTextureUnit(stageIdx);
+        this->setTextureUnit(unitIdx);
         GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
                               GR_GL_TEXTURE_WRAP_S,
                               newTexParams.fWrapS));
     }
     if (setAll || newTexParams.fWrapT != oldTexParams.fWrapT) {
-        this->setTextureUnit(stageIdx);
+        this->setTextureUnit(unitIdx);
         GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
                               GR_GL_TEXTURE_WRAP_T,
                               newTexParams.fWrapT));
@@ -2096,12 +2078,11 @@
         (setAll || memcmp(newTexParams.fSwizzleRGBA,
                           oldTexParams.fSwizzleRGBA,
                           sizeof(newTexParams.fSwizzleRGBA)))) {
-        this->setTextureUnit(stageIdx);
+        this->setTextureUnit(unitIdx);
         set_tex_swizzle(newTexParams.fSwizzleRGBA,
                         this->glInterface());
     }
-    nextTexture->setCachedTexParams(newTexParams,
-                                    this->getResetTimestamp());
+    texture->setCachedTexParams(newTexParams, this->getResetTimestamp());
 }
 
 void GrGpuGL::flushMiscFixedFunctionState() {
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index 33834cf..97b95d0 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -36,6 +36,11 @@
         return fGLContextInfo.glslGeneration();
     }
 
+    // Used by GrGLProgram to bind necessary textures for GrGLEffects.
+    void bindTexture(int unitIdx, const GrTextureParams& params, GrGLTexture* texture);
+
+    bool programUnitTest();
+
     // GrGpu overrides
     virtual GrPixelConfig preferredReadPixelsConfig(GrPixelConfig config)
                                                             const SK_OVERRIDE;
@@ -51,9 +56,6 @@
 
     virtual void abandonResources() SK_OVERRIDE;
 
-    bool programUnitTest();
-
-
 protected:
     // GrGpu overrides
     virtual void onResetContext() SK_OVERRIDE;
@@ -196,16 +198,6 @@
         const GrGLContextInfo&      fGL;
     };
 
-    // binds the texture and sets its texture params
-    // This may also perform a downsample on the src texture which may or may
-    // not modify the scissor test and rect. So in flushGraphicsState a
-    // call to flushScissor must occur after all textures have been flushed via
-    // this function.
-    void flushBoundTextureAndParams(int stage);
-    void flushBoundTextureAndParams(int stage,
-                                    const GrTextureParams& params,
-                                    GrGLTexture* nextTexture);
-
     // sets the color specified by GrDrawState::setColor()
     void flushColor(GrColor color);
 
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index 5850fc4..3d9f0fe 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -151,7 +151,9 @@
             SkScalarToFloat(m[SkMatrix::kMTransY]),
             SkScalarToFloat(m[SkMatrix::kMPersp2])
         };
-        fCurrentProgram->fUniformManager.setMatrix3f(fCurrentProgram->fUniforms.fViewMatrixUni, mt);
+        fCurrentProgram->fUniformManager.setMatrix3f(
+                                            fCurrentProgram->fUniformHandles.fViewMatrixUni,
+                                            mt);
         fCurrentProgram->fViewMatrix = vm;
         fCurrentProgram->fViewportSize = viewportSize;
     }
@@ -183,9 +185,10 @@
                     // OpenGL ES doesn't support unsigned byte varieties of glUniform
                     GrGLfloat c[4];
                     GrColorToRGBAFloat(color, c);
-                    GrAssert(kInvalidUniformHandle !=  fCurrentProgram->fUniforms.fColorUni);
-                    fCurrentProgram->fUniformManager.set4fv(fCurrentProgram->fUniforms.fColorUni,
-                                                            0, 1, c);
+                    GrAssert(kInvalidUniformHandle !=  fCurrentProgram->fUniformHandles.fColorUni);
+                    fCurrentProgram->fUniformManager.set4fv(
+                                                        fCurrentProgram->fUniformHandles.fColorUni,
+                                                        0, 1, c);
                     fCurrentProgram->fColor = color;
                 }
                 break;
@@ -196,7 +199,7 @@
                 GrCrash("Unknown color type.");
         }
     }
-    UniformHandle filterColorUni = fCurrentProgram->fUniforms.fColorFilterUni;
+    UniformHandle filterColorUni = fCurrentProgram->fUniformHandles.fColorFilterUni;
     if (kInvalidUniformHandle != filterColorUni &&
         fCurrentProgram->fColorFilterColor != drawState.getColorFilterColor()) {
         GrGLfloat c[4];
@@ -234,9 +237,11 @@
                     // glUniform
                     GrGLfloat c[4];
                     GrColorToRGBAFloat(coverage, c);
-                    GrAssert(kInvalidUniformHandle !=  fCurrentProgram->fUniforms.fCoverageUni);
-                    fCurrentProgram->fUniformManager.set4fv(fCurrentProgram->fUniforms.fCoverageUni,
-                                                            0, 1, c);
+                    GrAssert(kInvalidUniformHandle !=
+                             fCurrentProgram->fUniformHandles.fCoverageUni);
+                    fCurrentProgram->fUniformManager.set4fv(
+                                                    fCurrentProgram->fUniformHandles.fCoverageUni,
+                                                    0, 1, c);
                     fCurrentProgram->fCoverage = coverage;
                 }
                 break;
@@ -302,13 +307,7 @@
         this->flushColor(color);
         this->flushCoverage(coverage);
 
-        fCurrentProgram->setData(drawState);
-
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-            if (this->isStageEnabled(s)) {
-                this->flushBoundTextureAndParams(s);
-            }
-        }
+        fCurrentProgram->setData(this);
     }
     this->flushStencil(type);
     this->flushViewMatrix(type);
@@ -318,8 +317,7 @@
     GrIRect* devRect = NULL;
     GrIRect devClipBounds;
     if (drawState.isClipState()) {
-        fClip->getConservativeBounds(drawState.getRenderTarget(),
-                                     &devClipBounds);
+        fClip->getConservativeBounds(drawState.getRenderTarget(), &devClipBounds);
         devRect = &devClipBounds;
     }
     // This must come after textures are flushed because a texture may need