Optimize GLSL shaders.

Change-Id: I9a5e01bced63d8da0c61330a543a2b805388a59d
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 7c4897a..b5e103f 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -26,7 +26,6 @@
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.SparseBooleanArray;
@@ -2980,7 +2979,6 @@
             if (!mStackFromBottom) {
                 int bottom;
                 
-                final int scrollY = mScrollY;
                 for (int i = 0; i < count; i++) {
                     if ((headerDividers || first + i >= headerCount) &&
                             (footerDividers || first + i < footerLimit)) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5b226b4..4878063 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -985,6 +985,7 @@
      ProgramDescription description;
      description.hasTexture = true;
      description.hasAlpha8Texture = true;
+     const bool setColor = description.setAlpha8Color(r, g, b, a);
 
      if (applyFilters) {
          if (mShader) {
@@ -1021,7 +1022,9 @@
          mModelView.loadIdentity();
      }
      mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
-     glUniform4f(mCaches.currentProgram->color, r, g, b, a);
+     if (setColor) {
+         mCaches.currentProgram->setColor(r, g, b, a);
+     }
 
      textureUnit++;
      if (applyFilters) {
@@ -1126,6 +1129,8 @@
 
     // Describe the required shaders
     ProgramDescription description;
+    const bool setColor = description.setColor(r, g, b, a);
+
     if (mShader) {
         mShader->describe(description, mExtensions);
     }
@@ -1152,7 +1157,7 @@
         mat4 identity;
         mCaches.currentProgram->set(mOrthoMatrix, mModelView, identity);
     }
-    glUniform4f(mCaches.currentProgram->color, r, g, b, a);
+    mCaches.currentProgram->setColor(r, g, b, a);
 
     // Setup attributes and uniforms required by the shaders
     if (mShader) {
@@ -1189,6 +1194,7 @@
 
     ProgramDescription description;
     description.hasTexture = true;
+    const bool setColor = description.setColor(alpha, alpha, alpha, alpha);
     if (mColorFilter) {
         mColorFilter->describe(description, mExtensions);
     }
@@ -1211,7 +1217,9 @@
     glUniform1i(mCaches.currentProgram->getUniform("sampler"), 0);
 
     // Always premultiplied
-    glUniform4f(mCaches.currentProgram->color, alpha, alpha, alpha, alpha);
+    if (setColor) {
+        mCaches.currentProgram->setColor(alpha, alpha, alpha, alpha);
+    }
 
     // Mesh
     int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 2e1b9a0..25674c6 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -58,7 +58,6 @@
     mUse = false;
 
     position = addAttrib("position");
-    color = addUniform("color");
     transform = addUniform("transform");
 }
 
@@ -124,6 +123,10 @@
     glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
 }
 
+void Program::setColor(const float r, const float g, const float b, const float a) {
+    glUniform4f(getUniform("color"), r, g, b, a);
+}
+
 void Program::use() {
     glUseProgram(id);
     mUse = true;
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 6531c74..026097c 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -77,16 +77,16 @@
              const mat4& transformMatrix);
 
     /**
+     * Sets the color associated with this shader.
+     */
+    void setColor(const float r, const float g, const float b, const float a);
+
+    /**
      * Name of the position attribute.
      */
     int position;
 
     /**
-     * Name of the color uniform.
-     */
-    int color;
-
-    /**
      * Name of the transform uniform.
      */
     int transform;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 63e92a51..1282b710 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -24,6 +24,14 @@
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define MODULATE_OP_NO_MODULATE 0
+#define MODULATE_OP_MODULATE 1
+#define MODULATE_OP_MODULATE_A8 2
+
+///////////////////////////////////////////////////////////////////////////////
 // Vertex shaders snippets
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -69,8 +77,7 @@
         "    sweep = (screenSpace * position).xy;\n"
 };
 const char* gVS_Main_OutBitmapTexCoords =
-        "    vec4 bitmapCoords = textureTransform * position;\n"
-        "    outBitmapTexCoords = bitmapCoords.xy * textureDimension;\n";
+        "    outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
 const char* gVS_Main_Position =
         "    gl_Position = transform * position;\n";
 const char* gVS_Footer =
@@ -113,12 +120,52 @@
 const char* gFS_Main =
         "\nvoid main(void) {\n"
         "    lowp vec4 fragColor;\n";
+
+// Fast cases
+const char* gFS_Fast_SingleColor =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = color;\n"
+        "}\n\n";
+const char* gFS_Fast_SingleTexture =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = texture2D(sampler, outTexCoords);\n"
+        "}\n\n";
+const char* gFS_Fast_SingleModulateTexture =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = color.a * texture2D(sampler, outTexCoords);\n"
+        "}\n\n";
+const char* gFS_Fast_SingleA8Texture =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = vec4(0.0, 0.0, 0.0, texture2D(sampler, outTexCoords).a);\n"
+        "}\n\n";
+const char* gFS_Fast_SingleModulateA8Texture =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = color * texture2D(sampler, outTexCoords).a;\n"
+        "}\n\n";
+const char* gFS_Fast_SingleGradient =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = texture2D(gradientSampler, linear);\n"
+        "}\n\n";
+const char* gFS_Fast_SingleModulateGradient =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = color.a * texture2D(gradientSampler, linear);\n"
+        "}\n\n";
+
+// General case
 const char* gFS_Main_FetchColor =
         "    fragColor = color;\n";
-const char* gFS_Main_FetchTexture =
-        "    fragColor = color * texture2D(sampler, outTexCoords);\n";
-const char* gFS_Main_FetchA8Texture =
-        "    fragColor = color * texture2D(sampler, outTexCoords).a;\n";
+const char* gFS_Main_FetchTexture[2] = {
+        // Don't modulate
+        "    fragColor = texture2D(sampler, outTexCoords);\n",
+        // Modulate
+        "    fragColor = color * texture2D(sampler, outTexCoords);\n"
+};
+const char* gFS_Main_FetchA8Texture[2] = {
+        // Don't modulate
+        "    fragColor = vec4(0.0, 0.0, 0.0, texture2D(sampler, outTexCoords).a);\n",
+        // Modulate
+        "    fragColor = color * texture2D(sampler, outTexCoords).a;\n"
+};
 const char* gFS_Main_FetchGradient[3] = {
         // Linear
         "    vec4 gradientColor = texture2D(gradientSampler, linear);\n",
@@ -137,12 +184,30 @@
         "    fragColor = blendShaders(gradientColor, bitmapColor)";
 const char* gFS_Main_BlendShadersGB =
         "    fragColor = blendShaders(bitmapColor, gradientColor)";
-const char* gFS_Main_BlendShaders_Modulate =
-        " * fragColor.a;\n";
-const char* gFS_Main_GradientShader_Modulate =
-        "    fragColor = gradientColor * fragColor.a;\n";
-const char* gFS_Main_BitmapShader_Modulate =
-        "    fragColor = bitmapColor * fragColor.a;\n";
+const char* gFS_Main_BlendShaders_Modulate[3] = {
+        // Don't modulate
+        ";\n",
+        // Modulate
+        " * fragColor.a;\n",
+        // Modulate with alpha 8 texture
+        " * texture2D(sampler, outTexCoords).a;\n"
+};
+const char* gFS_Main_GradientShader_Modulate[3] = {
+        // Don't modulate
+        "    fragColor = gradientColor;\n",
+        // Modulate
+        "    fragColor = gradientColor * fragColor.a;\n",
+        // Modulate with alpha 8 texture
+        "    fragColor = gradientColor * texture2D(sampler, outTexCoords).a;\n"
+    };
+const char* gFS_Main_BitmapShader_Modulate[3] = {
+        // Don't modulate
+        "    fragColor = bitmapColor;\n",
+        // Modulate
+        "    fragColor = bitmapColor * fragColor.a;\n",
+        // Modulate with alpha 8 texture
+        "    fragColor = bitmapColor * texture2D(sampler, outTexCoords).a;\n"
+    };
 const char* gFS_Main_FragColor =
         "    gl_FragColor = fragColor;\n";
 const char* gFS_Main_FragColor_Blend =
@@ -317,7 +382,7 @@
     // Set the default precision
     String8 shader;
 
-    bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode;
+    const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode;
     if (blendFramebuffer) {
         shader.append(gFS_Header_Extension_FramebufferFetch);
     }
@@ -335,15 +400,72 @@
         shader.append(gVS_Header_Varyings_HasBitmap);
     }
 
-
     // Uniforms
-    shader.append(gFS_Uniforms_Color);
+    int modulateOp = MODULATE_OP_NO_MODULATE;
+    const bool singleColor = !description.hasTexture &&
+            !description.hasGradient && !description.hasBitmap;
+
+    if (description.modulate || singleColor) {
+        shader.append(gFS_Uniforms_Color);
+        if (!singleColor) modulateOp = MODULATE_OP_MODULATE;
+    }
     if (description.hasTexture) {
         shader.append(gFS_Uniforms_TextureSampler);
     }
     if (description.hasGradient) {
         shader.append(gFS_Uniforms_GradientSampler[description.gradientType]);
     }
+
+    // Optimization for common cases
+    if (!blendFramebuffer) {
+        bool fast = false;
+
+        const bool noShader = !description.hasGradient && !description.hasBitmap;
+        const bool singleTexture = description.hasTexture &&
+                !description.hasAlpha8Texture && noShader;
+        const bool singleA8Texture = description.hasTexture &&
+                description.hasAlpha8Texture && noShader;
+        const bool singleGradient = !description.hasTexture &&
+                description.hasGradient && !description.hasBitmap &&
+                description.gradientType == ProgramDescription::kGradientLinear;
+
+        if (singleColor) {
+            shader.append(gFS_Fast_SingleColor);
+            fast = true;
+        } else if (singleTexture) {
+            if (!description.modulate) {
+                shader.append(gFS_Fast_SingleTexture);
+            } else {
+                shader.append(gFS_Fast_SingleModulateTexture);
+            }
+            fast = true;
+        } else if (singleA8Texture) {
+            if (!description.modulate) {
+                shader.append(gFS_Fast_SingleA8Texture);
+            } else {
+                shader.append(gFS_Fast_SingleModulateA8Texture);
+            }
+            fast = true;
+        } else if (singleGradient) {
+            if (!description.modulate) {
+                shader.append(gFS_Fast_SingleGradient);
+            } else {
+                shader.append(gFS_Fast_SingleModulateGradient);
+            }
+            fast = true;
+        }
+
+        if (fast) {
+            if (DEBUG_PROGRAM_CACHE) {
+                PROGRAM_LOGD("*** Fast case:\n");
+                PROGRAM_LOGD("*** Generated fragment shader:\n\n");
+                printLongString(shader);
+            }
+
+            return shader;
+        }
+    }
+
     if (description.hasBitmap) {
         shader.append(gFS_Uniforms_BitmapSampler);
     }
@@ -368,12 +490,16 @@
         // Stores the result in fragColor directly
         if (description.hasTexture) {
             if (description.hasAlpha8Texture) {
-                shader.append(gFS_Main_FetchA8Texture);
+                if (!description.hasGradient && !description.hasBitmap) {
+                    shader.append(gFS_Main_FetchA8Texture[modulateOp]);
+                }
             } else {
-                shader.append(gFS_Main_FetchTexture);
+                shader.append(gFS_Main_FetchTexture[modulateOp]);
             }
         } else {
-            shader.append(gFS_Main_FetchColor);
+            if ((!description.hasGradient && !description.hasBitmap) || description.modulate) {
+                shader.append(gFS_Main_FetchColor);
+            }
         }
         if (description.hasGradient) {
             shader.append(gFS_Main_FetchGradient[description.gradientType]);
@@ -387,17 +513,20 @@
         }
         // Case when we have two shaders set
         if (description.hasGradient && description.hasBitmap) {
+            int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
             if (description.isBitmapFirst) {
                 shader.append(gFS_Main_BlendShadersBG);
             } else {
                 shader.append(gFS_Main_BlendShadersGB);
             }
-            shader.append(gFS_Main_BlendShaders_Modulate);
+            shader.append(gFS_Main_BlendShaders_Modulate[op]);
         } else {
             if (description.hasGradient) {
-                shader.append(gFS_Main_GradientShader_Modulate);
+                int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
+                shader.append(gFS_Main_GradientShader_Modulate[op]);
             } else if (description.hasBitmap) {
-                shader.append(gFS_Main_BitmapShader_Modulate);
+                int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
+                shader.append(gFS_Main_BitmapShader_Modulate[op]);
             }
         }
         // Apply the color op if needed
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 4fa8011..964d842 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -44,6 +44,11 @@
     #define PROGRAM_LOGD(...)
 #endif
 
+// TODO: This should be set in properties
+#define PANEL_BIT_DEPTH 18
+#define COLOR_COMPONENT_THRESHOLD (1.0f - (0.5f / PANEL_BIT_DEPTH))
+#define COLOR_COMPONENT_INV_THRESHOLD (0.5f / PANEL_BIT_DEPTH)
+
 #define PROGRAM_KEY_TEXTURE 0x1
 #define PROGRAM_KEY_A8_TEXTURE 0x2
 #define PROGRAM_KEY_BITMAP 0x4
@@ -68,6 +73,7 @@
 #define PROGRAM_BITMAP_WRAPT_SHIFT 11
 
 #define PROGRAM_GRADIENT_TYPE_SHIFT 33
+#define PROGRAM_MODULATE 35
 
 ///////////////////////////////////////////////////////////////////////////////
 // Types
@@ -99,7 +105,7 @@
     };
 
     ProgramDescription():
-        hasTexture(false), hasAlpha8Texture(false),
+        hasTexture(false), hasAlpha8Texture(false), modulate(false),
         hasBitmap(false), isBitmapNpot(false), hasGradient(false),
         gradientType(kGradientLinear),
         shadersMode(SkXfermode::kClear_Mode), isBitmapFirst(false),
@@ -112,6 +118,9 @@
     bool hasTexture;
     bool hasAlpha8Texture;
 
+    // Modulate, this should only be set when setColor() return true
+    bool modulate;
+
     // Shaders
     bool hasBitmap;
     bool isBitmapNpot;
@@ -134,18 +143,31 @@
     SkXfermode::Mode framebufferMode;
     bool swapSrcDst;
 
-    inline uint32_t getEnumForWrap(GLenum wrap) const {
-        switch (wrap) {
-            case GL_CLAMP_TO_EDGE:
-                return 0;
-            case GL_REPEAT:
-                return 1;
-            case GL_MIRRORED_REPEAT:
-                return 2;
-        }
-        return 0;
+    /**
+     * Indicates, for a given color, whether color modulation is required in
+     * the fragment shader. When this method returns true, the program should
+     * be provided with a modulation color.
+     */
+    bool setColor(const float r, const float g, const float b, const float a) {
+        modulate = a < COLOR_COMPONENT_THRESHOLD || r < COLOR_COMPONENT_THRESHOLD ||
+                g < COLOR_COMPONENT_THRESHOLD || b < COLOR_COMPONENT_THRESHOLD;
+        return modulate;
     }
 
+    /**
+     * Indicates, for a given color, whether color modulation is required in
+     * the fragment shader. When this method returns true, the program should
+     * be provided with a modulation color.
+     */
+    bool setAlpha8Color(const float r, const float g, const float b, const float a) {
+        modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD ||
+                g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD;
+        return modulate;
+    }
+
+    /**
+     * Computes the unique key identifying this program.
+     */
     programid key() const {
         programid key = 0;
         if (hasTexture) key |= PROGRAM_KEY_TEXTURE;
@@ -180,14 +202,32 @@
         }
         key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
         if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
+        if (modulate) key |= programid(0x1) << PROGRAM_MODULATE;
         return key;
     }
 
+    /**
+     * Logs the specified message followed by the key identifying this program.
+     */
     void log(const char* message) const {
         programid k = key();
         PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32),
                 uint32_t(k & 0xffffffff));
     }
+
+private:
+    inline uint32_t getEnumForWrap(GLenum wrap) const {
+        switch (wrap) {
+            case GL_CLAMP_TO_EDGE:
+                return 0;
+            case GL_REPEAT:
+                return 1;
+            case GL_MIRRORED_REPEAT:
+                return 2;
+        }
+        return 0;
+    }
+
 }; // struct ProgramDescription
 
 /**