Reland r6233 with fix for config conversion texture matrices.

git-svn-id: http://skia.googlecode.com/svn/trunk@6238 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/gl/GrGLEffect.cpp b/src/gpu/gl/GrGLEffect.cpp
index 0bbf1f7..5e0875b 100644
--- a/src/gpu/gl/GrGLEffect.cpp
+++ b/src/gpu/gl/GrGLEffect.cpp
@@ -10,6 +10,8 @@
 
 GrGLEffect::GrGLEffect(const GrBackendEffectFactory& factory)
     : fFactory(factory) {
+
+    fRequiresTextureMatrix = true;
 }
 
 GrGLEffect::~GrGLEffect() {
diff --git a/src/gpu/gl/GrGLEffect.h b/src/gpu/gl/GrGLEffect.h
index 30b8455..0fd5722 100644
--- a/src/gpu/gl/GrGLEffect.h
+++ b/src/gpu/gl/GrGLEffect.h
@@ -51,7 +51,9 @@
 
         @param builder      Interface used to emit code in the shaders.
         @param stage        The effect stage that generated this program stage.
-        @param key          The key that was computed by EffectKey() from the generating GrEffect.
+        @param key          The key that was computed by GenKey() from the generating GrEffect.
+                            Only the bits indicated by GrBackendEffectFactory::kEffectKeyBits are
+                            guaranteed to match the value produced by GenKey();
         @param vertexCoords A vec2 of texture coordinates in the VS, which may be altered. This will
                             be removed soon and stages will be responsible for computing their own
                             coords.
@@ -85,7 +87,14 @@
 
     static EffectKey GenTextureKey(const GrEffect&, const GrGLCaps&);
 
+    bool requiresTextureMatrix() const { return fRequiresTextureMatrix; }
+
+
 protected:
+    // HACK: This is a temporary field that allows GrGLEffect subclasses to opt into the new
+    // shader gen where a texture matrix is not automatically inserted. It defaults to true and is
+    // set to false in a subclass to opt into the new behavior.
+    bool fRequiresTextureMatrix;
 
     const GrBackendEffectFactory& fFactory;
 };
diff --git a/src/gpu/gl/GrGLEffectMatrix.cpp b/src/gpu/gl/GrGLEffectMatrix.cpp
new file mode 100644
index 0000000..0db87f9
--- /dev/null
+++ b/src/gpu/gl/GrGLEffectMatrix.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLEffectMatrix.h"
+#include "GrTexture.h"
+
+GrGLEffect::EffectKey GrGLEffectMatrix::GenKey(const SkMatrix& effectMatrix,
+                                               const SkMatrix& coordChangeMatrix,
+                                               const GrTexture* texture) {
+    SkMatrix::TypeMask type0 = effectMatrix.getType();
+    SkMatrix::TypeMask type1 = coordChangeMatrix.getType();
+
+    static const int kNonTransMask = SkMatrix::kAffine_Mask |
+                                     SkMatrix::kScale_Mask  |
+                                     SkMatrix::kPerspective_Mask;
+    int combinedTypes = type0 | type1;
+
+    bool reverseY = (NULL != texture) && GrSurface::kBottomLeft_Origin == texture->origin();
+
+    if (SkMatrix::kPerspective_Mask & combinedTypes) {
+        return kGeneral_Key;
+    } else if ((kNonTransMask & combinedTypes) || reverseY) {
+        return kNoPersp_Key;
+    } else if (kTrans_Key & combinedTypes) {
+        return kTrans_Key;
+    } else {
+        GrAssert(effectMatrix.isIdentity() && coordChangeMatrix.isIdentity());
+        return kIdentity_Key;
+    }
+}
+
+GrSLType GrGLEffectMatrix::emitCode(GrGLShaderBuilder* builder,
+                                    EffectKey key,
+                                    const char* vertexCoords,
+                                    const char** fsCoordName,
+                                    const char** vsCoordName,
+                                    const char* suffix) {
+    GrSLType varyingType;
+    const char* uniName;
+    key &= kKeyMask;
+    switch (key) {
+        case kIdentity_Key:
+            fUniType = kVoid_GrSLType;
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kTrans_Key:
+            fUniType = kVec2f_GrSLType;
+            uniName = "StageTranslate";
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kNoPersp_Key:
+            fUniType = kMat33f_GrSLType;
+            uniName = "StageMatrix";
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kGeneral_Key:
+            fUniType = kMat33f_GrSLType;
+            uniName = "StageMatrix";
+            varyingType = kVec3f_GrSLType;
+            break;
+        default:
+            GrCrash("Unexpected key.");
+    }
+    SkString suffixedUniName;
+    if (NULL != suffix) {
+        suffixedUniName.append(uniName);
+        suffixedUniName.append(suffix);
+        uniName = suffixedUniName.c_str();
+    }
+    if (kVoid_GrSLType != fUniType) {
+        fUni = builder->addUniform(GrGLShaderBuilder::kVertex_ShaderType,
+                                   fUniType,
+                                   uniName,
+                                   &uniName);
+    }
+
+    const char* varyingName = "StageCoord";
+    SkString suffixedVaryingName;
+    if (NULL != suffix) {
+        suffixedVaryingName.append(varyingName);
+        suffixedVaryingName.append(suffix);
+        varyingName = suffixedVaryingName.c_str();
+    }
+    const char* vsVaryingName;
+    const char* fsVaryingName;
+    builder->addVarying(varyingType, varyingName, &vsVaryingName, &fsVaryingName);
+
+    // varying = matrix * vertex-coords (logically)
+    switch (fUniType) {
+        case kVoid_GrSLType:
+            GrAssert(kVec2f_GrSLType == varyingType);
+            builder->fVSCode.appendf("\t%s = %s;\n", vsVaryingName, vertexCoords);
+            break;
+        case kVec2f_GrSLType:
+            GrAssert(kVec2f_GrSLType == varyingType);
+            builder->fVSCode.appendf("\t%s = %s + %s;\n", vsVaryingName, uniName, vertexCoords);
+            break;
+        case kMat33f_GrSLType: {
+            GrAssert(kVec2f_GrSLType == varyingType || kVec3f_GrSLType == varyingType);
+            if (kVec2f_GrSLType == varyingType) {
+                builder->fVSCode.appendf("\t%s = (%s * vec3(%s, 1)).xy;\n",
+                                         vsVaryingName, uniName, vertexCoords);
+            } else {
+                builder->fVSCode.appendf("\t%s = %s * vec3(%s, 1);\n",
+                                         vsVaryingName, uniName, vertexCoords);
+            }
+            break;
+        }
+        default:
+            GrCrash("Unexpected uniform type.");
+    }
+    if (NULL != vsCoordName) {
+        *vsCoordName = vsVaryingName;
+    }
+    if (NULL != fsCoordName) {
+        *fsCoordName = fsVaryingName;
+    }
+    return varyingType;
+}
+
+/**
+    * This is similar to emitCode except that it performs perspective division in the FS if the
+    * texture coordinates have a w coordinate. The fsCoordName always refers to a vec2f.
+    */
+void GrGLEffectMatrix::emitCodeMakeFSCoords2D(GrGLShaderBuilder* builder,
+                                              EffectKey key,
+                                              const char* vertexCoords,
+                                              const char** fsCoordName,
+                                              const char** vsVaryingName,
+                                              GrSLType* vsVaryingType,
+                                              const char* suffix) {
+    const char* fsVaryingName;
+        
+    GrSLType varyingType = this->emitCode(builder,
+                                          key,
+                                          vertexCoords,
+                                          &fsVaryingName,
+                                          vsVaryingName,
+                                          suffix);
+    if (kVec3f_GrSLType == varyingType) {
+
+        const char* coordName = "coords2D";
+        SkString suffixedCoordName;
+        if (NULL != suffix) {
+            suffixedCoordName.append(coordName);
+            suffixedCoordName.append(suffix);
+            coordName = suffixedCoordName.c_str();
+        }
+        builder->fFSCode.appendf("\tvec2 %s = %s.xy / %s.z;",
+                                    coordName, fsVaryingName, fsVaryingName);
+        if (NULL != fsCoordName) {
+            *fsCoordName = coordName;
+        }
+    } else if(NULL != fsCoordName) {
+        *fsCoordName = fsVaryingName;
+    }
+    if (NULL != vsVaryingType) {
+        *vsVaryingType = varyingType;
+    }
+}
+
+void GrGLEffectMatrix::setData(const GrGLUniformManager& uniformManager,
+                              const SkMatrix& matrix,
+                              const SkMatrix& coordChangeMatrix,
+                              const GrTexture* texture) {
+    GrAssert((GrGLUniformManager::kInvalidUniformHandle == fUni) ==
+                (kVoid_GrSLType == fUniType));
+    switch (fUniType) {
+        case kVoid_GrSLType:
+            GrAssert(matrix.isIdentity());
+            GrAssert(coordChangeMatrix.isIdentity());
+            GrAssert(NULL == texture || GrSurface::kTopLeft_Origin == texture->origin());
+            return;
+        case kVec2f_GrSLType: {
+            GrAssert(SkMatrix::kTranslate_Mask == (matrix.getType() | coordChangeMatrix.getType()));
+            GrAssert(NULL == texture || GrSurface::kTopLeft_Origin == texture->origin());
+            SkScalar tx = matrix[SkMatrix::kMTransX] + coordChangeMatrix[SkMatrix::kMTransX];
+            SkScalar ty = matrix[SkMatrix::kMTransY] + coordChangeMatrix[SkMatrix::kMTransY];
+            if (fPrevMatrix.get(SkMatrix::kMTransX) != tx ||
+                fPrevMatrix.get(SkMatrix::kMTransY) != ty) {
+                uniformManager.set2f(fUni, tx, ty);
+                fPrevMatrix.set(SkMatrix::kMTransX, tx);
+                fPrevMatrix.set(SkMatrix::kMTransY, ty);
+            }
+            break;
+        }
+        case kMat33f_GrSLType: {
+            SkMatrix combined;
+            combined.setConcat(matrix, coordChangeMatrix);
+            if (NULL != texture && GrSurface::kBottomLeft_Origin == texture->origin()) {
+                // combined.postScale(1,-1);
+                // combined.postTranslate(0,1);
+                combined.set(SkMatrix::kMSkewY,
+                    combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
+                combined.set(SkMatrix::kMScaleY,
+                    combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
+                combined.set(SkMatrix::kMTransY,
+                    combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
+            }
+            if (!fPrevMatrix.cheapEqualTo(combined)) {
+                uniformManager.setSkMatrix(fUni, combined);
+                fPrevMatrix = combined;
+            }
+            break;
+        }
+        default:
+            GrCrash("Unexpected uniform type.");
+    }
+}
diff --git a/src/gpu/gl/GrGLEffectMatrix.h b/src/gpu/gl/GrGLEffectMatrix.h
new file mode 100644
index 0000000..9e45f3e
--- /dev/null
+++ b/src/gpu/gl/GrGLEffectMatrix.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGLEffectMatrix_DEFINED
+#define GrGLEffectMatrix_DEFINED
+
+#include "GrGLEffect.h"
+#include "SkMatrix.h"
+
+class GrTexture;
+class SkRandom;
+
+/**
+ * This is a helper to implement a texture matrix in a GrGLEffect.
+ */
+class GrGLEffectMatrix {
+public:
+    typedef GrGLEffect::EffectKey EffectKey;
+    /**
+     * The matrix uses kKeyBits of the effect's EffectKey. A GrGLEffect may place these bits at an
+     * arbitrary shift in its final key. However, when GrGLEffectMatrix::emitCode*() code is called
+     * the relevant bits must be in the lower kKeyBits of the key parameter.
+     */
+    enum {
+        kKeyBits = 2,
+        kKeyMask = (1 << kKeyBits) - 1,
+    };
+
+    GrGLEffectMatrix() : fUni(GrGLUniformManager::kInvalidUniformHandle) {
+        fPrevMatrix = SkMatrix::InvalidMatrix();
+    }
+
+    /**
+     * Generates the key for the portion of the code emitted by this class's emitCode() function.
+     * Pass a texture to make GrGLEffectMatrix automatically adjust for the texture's origin. Pass
+     * NULL when not using the EffectMatrix for a texture lookups, or if the GrGLEffect subclass
+     * wants to handle origin adjustments in some other manner. coordChangeMatrix is the matrix
+     * from GrEffectStage.
+     */
+    static EffectKey GenKey(const SkMatrix& effectMatrix,
+                            const SkMatrix& coordChangeMatrix,
+                            const GrTexture*);
+
+    /**
+     * Emits code to implement the matrix in the VS. A varying is added as an output of the VS and
+     * input to the FS. The varying may be either a vec2f or vec3f depending upon whether
+     * perspective interpolation is required or not. The names of the varying in the VS and FS are
+     * are returned as output parameters and the type of the varying is the return value. The suffix
+     * is an optional parameter that can be used to make all variables emitted by the object
+     * unique within a stage. It is only necessary if multiple GrGLEffectMatrix objects are used by
+     * a GrGLEffect.
+     */
+    GrSLType emitCode(GrGLShaderBuilder*,
+                      EffectKey,
+                      const char* vertexCoords,
+                      const char** fsCoordName, /* optional */
+                      const char** vsCoordName = NULL,
+                      const char* suffix = NULL);
+
+    /**
+     * This is similar to emitCode except that it performs perspective division in the FS if the
+     * texture coordinates have a w coordinate. The fsCoordName always refers to a vec2f.
+     */
+    void emitCodeMakeFSCoords2D(GrGLShaderBuilder*,
+                                EffectKey,
+                                const char* vertexCoords,
+                                const char** fsCoordName, /* optional */
+                                const char** vsVaryingName = NULL,
+                                GrSLType* vsVaryingType = NULL,
+                                const char* suffix = NULL);
+    /**
+     * Call from a GrGLEffect's subclass to update the texture matrix. The matrix,
+     * coordChangeMatrix, and texture params should match those used with GenKey.
+     */
+    void setData(const GrGLUniformManager& uniformManager,
+                 const SkMatrix& effectMatrix,
+                 const SkMatrix& coordChangeMatrix,
+                 const GrTexture*);
+
+private:
+    enum {
+        kIdentity_Key   = 0,
+        kTrans_Key      = 1,
+        kNoPersp_Key    = 2,
+        kGeneral_Key    = 3,
+    };
+
+    GrGLUniformManager::UniformHandle fUni;
+    GrSLType                          fUniType;
+    SkMatrix                          fPrevMatrix;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index cd16d9e..120bd1e 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -909,57 +909,64 @@
 
     /// Vertex Shader Stuff
 
-    // decide whether we need a matrix to transform texture coords and whether the varying needs a
-    // perspective coord.
-    const char* matName = NULL;
-    GrSLType texCoordVaryingType;
-    if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
-        texCoordVaryingType = kVec2f_GrSLType;
-    } else {
-        uniforms->fTextureMatrixUni = builder->addUniform(GrGLShaderBuilder::kVertex_ShaderType,
-                                                         kMat33f_GrSLType, "TexM", &matName);
-        builder->getUniformVariable(uniforms->fTextureMatrixUni);
+    const char* vertexCoords;
 
-        if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
+    // Has the effect not yet been updated to insert its own texture matrix if necessary.
+    if (glEffect->requiresTextureMatrix()) {
+        // Decide whether we need a matrix to transform texture coords and whether the varying needs
+        // a perspective coord.
+        const char* matName = NULL;
+        GrSLType texCoordVaryingType;
+        if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
             texCoordVaryingType = kVec2f_GrSLType;
         } else {
-            texCoordVaryingType = kVec3f_GrSLType;
-        }
-    }
-    const char *varyingVSName, *varyingFSName;
-    builder->addVarying(texCoordVaryingType,
-                        "Stage",
-                        &varyingVSName,
-                        &varyingFSName);
-    builder->setupTextureAccess(varyingFSName, texCoordVaryingType);
+            uniforms->fTextureMatrixUni = builder->addUniform(GrGLShaderBuilder::kVertex_ShaderType,
+                                                              kMat33f_GrSLType, "TexM", &matName);
+            builder->getUniformVariable(uniforms->fTextureMatrixUni);
 
+            if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
+                texCoordVaryingType = kVec2f_GrSLType;
+            } else {
+                texCoordVaryingType = kVec3f_GrSLType;
+            }
+        }
+        const char *varyingVSName, *varyingFSName;
+        builder->addVarying(texCoordVaryingType,
+                            "Stage",
+                            &varyingVSName,
+                            &varyingFSName);
+        builder->setupTextureAccess(varyingFSName, texCoordVaryingType);
+
+        if (!matName) {
+            GrAssert(kVec2f_GrSLType == texCoordVaryingType);
+            builder->fVSCode.appendf("\t%s = %s;\n", varyingVSName, vsInCoord);
+        } else {
+            // varying = texMatrix * texCoord
+            builder->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n",
+                                     varyingVSName, matName, vsInCoord,
+                                     vector_all_coords(GrSLTypeToVecLength(texCoordVaryingType)));
+        }
+        vertexCoords = varyingVSName;
+    } else {
+        vertexCoords = vsInCoord;
+    }
+
+    // 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);
     }
 
-    if (!matName) {
-        GrAssert(kVec2f_GrSLType == texCoordVaryingType);
-        builder->fVSCode.appendf("\t%s = %s;\n", varyingVSName, vsInCoord);
-    } else {
-        // varying = texMatrix * texCoord
-        builder->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n",
-                                  varyingVSName, matName, vsInCoord,
-                                  vector_all_coords(GrSLTypeToVecLength(texCoordVaryingType)));
-    }
-
     // 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,
                        desc.fEffectKey,
-                       varyingVSName,
+                       vertexCoords,
                        fsOutColor,
                        fsInColor,
                        textureSamplers);
diff --git a/src/gpu/gl/GrGLUniformManager.cpp b/src/gpu/gl/GrGLUniformManager.cpp
index 684ef8c..7a92d25 100644
--- a/src/gpu/gl/GrGLUniformManager.cpp
+++ b/src/gpu/gl/GrGLUniformManager.cpp
@@ -8,6 +8,7 @@
 #include "gl/GrGLShaderBuilder.h"
 #include "gl/GrGLProgram.h"
 #include "gl/GrGLUniformHandle.h"
+#include "SkMatrix.h"
 
 #define ASSERT_ARRAY_UPLOAD_IN_BOUNDS(UNI, OFFSET, COUNT) \
          GrAssert(offset + arrayCount <= uni.fArrayCount || \
@@ -231,6 +232,23 @@
     }
 }
 
+void GrGLUniformManager::setSkMatrix(UniformHandle u, const SkMatrix& matrix) const {
+    GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
+    GrGLfloat mt[] = {
+        matrix.get(SkMatrix::kMScaleX),
+        matrix.get(SkMatrix::kMSkewY),
+        matrix.get(SkMatrix::kMPersp0),
+        matrix.get(SkMatrix::kMSkewX),
+        matrix.get(SkMatrix::kMScaleY),
+        matrix.get(SkMatrix::kMPersp1),
+        matrix.get(SkMatrix::kMTransX),
+        matrix.get(SkMatrix::kMTransY),
+        matrix.get(SkMatrix::kMPersp2),
+    };
+    this->setMatrix3f(u, mt);
+}
+
+
 void GrGLUniformManager::getUniformLocations(GrGLuint programID, const BuilderUniformArray& uniforms) {
     GrAssert(uniforms.count() == fUniforms.count());
     int count = fUniforms.count();
diff --git a/src/gpu/gl/GrGLUniformManager.h b/src/gpu/gl/GrGLUniformManager.h
index e9856c6..8f435d3 100644
--- a/src/gpu/gl/GrGLUniformManager.h
+++ b/src/gpu/gl/GrGLUniformManager.h
@@ -15,6 +15,7 @@
 #include "SkTArray.h"
 
 class GrGLContextInfo;
+class SkMatrix;
 
 /** Manages a program's uniforms.
 */
@@ -47,6 +48,9 @@
     void setMatrix3fv(UniformHandle, int offset, int arrayCount, const GrGLfloat matrices[]) const;
     void setMatrix4fv(UniformHandle, int offset, int arrayCount, const GrGLfloat matrices[]) const;
 
+    // convenience method for uploading a SkMatrix to a 3x3 matrix uniform
+    void setSkMatrix(UniformHandle, const SkMatrix&) const;
+
     struct BuilderUniform {
         GrGLShaderVar fVariable;
         uint32_t      fVisibility;