Introduction of set of functions to manage generation of texture fetch shader code.  

A new set of routines have been added to GrGLShaderBuilder to emit texture fetches, taking into consideration the format of the texture to be accessed, and the channel swizzle.  
Review URL: https://codereview.appspot.com/6446072

git-svn-id: http://skia.googlecode.com/svn/trunk@4919 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 18d97d5..62c70e5 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -164,7 +164,7 @@
 
             /** Non-zero if user-supplied code will write the stage's
                 contribution to the fragment shader. */
-            uint16_t fCustomStageKey;
+            GrProgramStageFactory::StageKey fCustomStageKey;
 
             GR_STATIC_ASSERT((InConfigFlags)(uint8_t)kInConfigBitMask ==
                              kInConfigBitMask);
diff --git a/src/gpu/gl/GrGLProgramStage.cpp b/src/gpu/gl/GrGLProgramStage.cpp
index 12c3894..b1a37fe 100644
--- a/src/gpu/gl/GrGLProgramStage.cpp
+++ b/src/gpu/gl/GrGLProgramStage.cpp
@@ -27,3 +27,14 @@
                                int stageNum) {
 }
 
+GrGLProgramStage::StageKey GrGLProgramStage::GenTextureKey(const GrCustomStage& stage,
+                                                           const GrGLCaps& caps) {
+    StageKey key = 0;
+    for (unsigned int index = 0; index < stage.numTextures(); ++index) {
+        if (stage.textureAccess(index)) {
+            key = (key << index) |
+                GrGLShaderBuilder::KeyForTextureAccess(*stage.textureAccess(index), caps);
+        }
+    }
+    return key;
+}
diff --git a/src/gpu/gl/GrGLProgramStage.h b/src/gpu/gl/GrGLProgramStage.h
index 12163f8..8e3a4b1 100644
--- a/src/gpu/gl/GrGLProgramStage.h
+++ b/src/gpu/gl/GrGLProgramStage.h
@@ -78,6 +78,8 @@
 
     const char* name() const { return fFactory.name(); }
 
+    static StageKey GenTextureKey(const GrCustomStage&, const GrGLCaps&);
+
 protected:
 
     const GrProgramStageFactory& fFactory;
diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp
index 2844953..0d2a1ec 100644
--- a/src/gpu/gl/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -8,6 +8,7 @@
 #include "gl/GrGLShaderBuilder.h"
 #include "gl/GrGLProgram.h"
 #include "gl/GrGLUniformHandle.h"
+#include "GrTexture.h"
 
 // number of each input/output type in a single allocation block
 static const int kVarsPerBlock = 8;
@@ -21,6 +22,50 @@
 typedef GrGLUniformManager::UniformHandle UniformHandle;
 ///////////////////////////////////////////////////////////////////////////////
 
+static SkString build_sampler_string(GrGLShaderBuilder::SamplerMode samplerMode) {
+    SkString sampler("texture2D");
+    switch (samplerMode) {
+      case GrGLShaderBuilder::kDefault_SamplerMode:
+          break;
+      case GrGLShaderBuilder::kProj_SamplerMode:
+          sampler.append("Proj");
+          break;
+      case GrGLShaderBuilder::kExplicitDivide_SamplerMode:
+          GrAssert(false);  // Not Implemented
+          break;
+    }
+
+    return sampler;
+}
+
+static bool texture_requires_alpha_to_red_swizzle(const GrGLCaps& caps,
+                                                  const GrTextureAccess& access) {
+    return GrPixelConfigIsAlphaOnly(access.getTexture()->config()) && caps.textureRedSupport() &&
+        access.referencesAlpha();
+}
+
+static SkString build_swizzle_string(const GrTextureAccess& textureAccess,
+                                     const GrGLCaps& caps) {
+    const GrTextureAccess::Swizzle& swizzle = textureAccess.getSwizzle();
+    if (0 == swizzle[0]) {
+        return SkString("");
+    }
+
+    SkString swizzleOut(".");
+    bool alphaIsRed = texture_requires_alpha_to_red_swizzle(caps, textureAccess);
+    for (int offset = 0; offset < 4 && swizzle[offset]; ++offset) {
+        if (alphaIsRed && 'a' == swizzle[offset]) {
+            swizzleOut.appendf("r");
+        } else {
+            swizzleOut.appendf("%c", swizzle[offset]);
+        }
+    }
+
+    return swizzleOut;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 // 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!
@@ -120,6 +165,34 @@
     fFSCode.appendf("%s%s;\n", fSwizzle.c_str(), fModulate.c_str());
 }
 
+void GrGLShaderBuilder::emitCustomTextureLookup(SamplerMode samplerMode,
+                                                const GrTextureAccess& textureAccess,
+                                                const char* samplerName,
+                                                const char* coordName) {
+    GrAssert(samplerName && coordName);
+    SkString sampler = build_sampler_string(samplerMode);
+    SkString swizzle = build_swizzle_string(textureAccess, fContext.caps());
+
+    fFSCode.appendf("%s( %s, %s)%s;\n", sampler.c_str(), samplerName,
+                    coordName, swizzle.c_str());
+}
+
+GrCustomStage::StageKey GrGLShaderBuilder::KeyForTextureAccess(const GrTextureAccess& access,
+                                                               const GrGLCaps& caps) {
+    GrCustomStage::StageKey key = 0;
+    // Assume that swizzle support implies that we never have to modify a shader to adjust
+    // for texture format/swizzle settings.
+    if (caps.textureSwizzleSupport()) {
+        return key;
+    }
+
+    if (texture_requires_alpha_to_red_swizzle(caps, access)) {
+        key = 1;
+    }
+
+    return key;
+}
+
 GrGLUniformManager::UniformHandle GrGLShaderBuilder::addUniformArray(uint32_t visibility,
                                                                      GrSLType type,
                                                                      const char* name,
diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h
index f110628..16a11e2 100644
--- a/src/gpu/gl/GrGLShaderBuilder.h
+++ b/src/gpu/gl/GrGLShaderBuilder.h
@@ -9,6 +9,7 @@
 #define GrGLShaderBuilder_DEFINED
 
 #include "GrAllocator.h"
+#include "GrCustomStage.h"
 #include "gl/GrGLShaderVar.h"
 #include "gl/GrGLSL.h"
 #include "gl/GrGLUniformManager.h"
@@ -55,6 +56,22 @@
     void emitDefaultFetch(const char* outColor,
                           const char* samplerName);
 
+    /** Emits a texture lookup to the shader code with the form:
+          texture2D{Proj}(samplerName, coordName).swizzle
+        The routine selects the type of texturing based on samplerMode.
+        The generated swizzle state is built based on the format of the texture and the requested
+        swizzle access pattern. */
+    void emitCustomTextureLookup(SamplerMode samplerMode,
+                                 const GrTextureAccess& textureAccess,
+                                 const char* samplerName,
+                                 const char* coordName);
+
+    /** Generates a StageKey for the shader code based on the texture access parameters and the
+        capabilities of the GL context.  This is useful for keying the shader programs that may
+        have multiple representations, based on the type/format of textures used. */
+    static GrCustomStage::StageKey KeyForTextureAccess(const GrTextureAccess& access,
+                                                       const GrGLCaps& caps);
+
     /** Add a uniform variable to the current program, that has visibilty in one or more shaders.
         visibility is a bitfield of ShaderType values indicating from which shaders the uniform
         should be accessible. At least one bit must be set. Geometry shader uniforms are not
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index 6d6d916..c959f66 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -596,12 +596,13 @@
 
 void setup_custom_stage(GrGLProgram::Desc::StageDesc* stage,
                         const GrSamplerState& sampler,
+                        const GrGLCaps& caps,
                         const GrCustomStage** customStages,
                         GrGLProgram* program, int index) {
     const GrCustomStage* customStage = sampler.getCustomStage();
     if (customStage) {
         const GrProgramStageFactory& factory = customStage->getFactory();
-        stage->fCustomStageKey = factory.glStageKey(*customStage);
+        stage->fCustomStageKey = factory.glStageKey(*customStage, caps);
         customStages[index] = customStage;
     } else {
         stage->fCustomStageKey = 0;
@@ -746,7 +747,8 @@
                 }
             }
 
-            setup_custom_stage(&stage, sampler, customStages, fCurrentProgram.get(), s);
+            setup_custom_stage(&stage, sampler, this->glCaps(), customStages,
+                               fCurrentProgram.get(), s);
 
         } else {
             stage.fOptFlags         = 0;
diff --git a/src/gpu/gl/GrGpuGL_unittest.cpp b/src/gpu/gl/GrGpuGL_unittest.cpp
index 1a28b98..7fd61d9 100644
--- a/src/gpu/gl/GrGpuGL_unittest.cpp
+++ b/src/gpu/gl/GrGpuGL_unittest.cpp
@@ -413,7 +413,7 @@
                 customStages[s].reset(create_random_effect(&stage, &random, getContext()));
                 if (NULL != customStages[s]) {
                     stage.fCustomStageKey =
-                        customStages[s]->getFactory().glStageKey(*customStages[s]);
+                        customStages[s]->getFactory().glStageKey(*customStages[s], this->glCaps());
                 }
             }
         }