Generate shaders to cover all possible cases.

With this change, all the vertex and fragment shaders used by the GL
renderer are now generated based on a program description supplied
by the caller. This allows the renderer to generate a large number
of shaders without having to write all the possible combinations by
hand. The generated shaders are stored in a program cache.

Change-Id: If54d286e77ae021c724d42090da476df12a18ebb
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 172952a..a9714c7 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -10,6 +10,7 @@
 	Patch.cpp \
 	PatchCache.cpp \
 	Program.cpp \
+	ProgramCache.cpp \
 	TextureCache.cpp
 
 LOCAL_C_INCLUDES += \
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2e44e122..0ed6276 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -45,32 +45,22 @@
 #define MB(s) s * 1024 * 1024
 
 // Generates simple and textured vertices
-#define SV(x, y) { { x, y } }
 #define FV(x, y, u, v) { { x, y }, { u, v } }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Globals
 ///////////////////////////////////////////////////////////////////////////////
 
-static const SimpleVertex gDrawColorVertices[] = {
-        SV(0.0f, 0.0f),
-        SV(1.0f, 0.0f),
-        SV(0.0f, 1.0f),
-        SV(1.0f, 1.0f)
-};
-static const GLsizei gDrawColorVertexStride = sizeof(SimpleVertex);
-static const GLsizei gDrawColorVertexCount = 4;
-
 // This array is never used directly but used as a memcpy source in the
 // OpenGLRenderer constructor
-static const TextureVertex gDrawTextureVertices[] = {
+static const TextureVertex gMeshVertices[] = {
         FV(0.0f, 0.0f, 0.0f, 0.0f),
         FV(1.0f, 0.0f, 1.0f, 0.0f),
         FV(0.0f, 1.0f, 0.0f, 1.0f),
         FV(1.0f, 1.0f, 1.0f, 1.0f)
 };
-static const GLsizei gDrawTextureVertexStride = sizeof(TextureVertex);
-static const GLsizei gDrawTextureVertexCount = 4;
+static const GLsizei gMeshStride = sizeof(TextureVertex);
+static const GLsizei gMeshCount = 4;
 
 // In this array, the index of each Blender equals the value of the first
 // entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
@@ -143,7 +133,18 @@
 
     mLastTexture = 0;
 
-    memcpy(mDrawTextureVertices, gDrawTextureVertices, sizeof(gDrawTextureVertices));
+    memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
+
+    ProgramDescription d;
+    mProgramCache.get(d);
+    d.hasTexture = true;
+    mProgramCache.get(d);
+    d.hasAlpha8Texture = true;
+    d.hasGradient = true;
+    d.hasBitmap = true;
+    d.shadersMode = SkXfermode::kDstOut_Mode;
+    d.colorOp = ProgramDescription::kColorMatrix;
+    mProgramCache.get(d);
 }
 
 OpenGLRenderer::~OpenGLRenderer() {
@@ -650,9 +651,13 @@
     mModelView.scale(right - left, bottom - top, 1.0f);
 
     if (!useProgram(mDrawColorProgram)) {
-        const GLvoid* p = &gDrawColorVertices[0].position[0];
+        const GLvoid* vertices = &mMeshVertices[0].position[0];
+        const GLvoid* texCoords = &mMeshVertices[0].texture[0];
+
         glVertexAttribPointer(mDrawColorProgram->position, 2, GL_FLOAT, GL_FALSE,
-                gDrawColorVertexStride, p);
+                gMeshStride, vertices);
+        glVertexAttribPointer(mDrawColorProgram->texCoords, 2, GL_FLOAT, GL_FALSE,
+                gMeshStride, texCoords);
     }
 
     if (!ignoreTransform) {
@@ -664,7 +669,7 @@
 
     glUniform4f(mDrawColorProgram->color, r, g, b, a);
 
-    glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
 }
 
 void OpenGLRenderer::drawLinearGradientShader(float left, float top, float right, float bottom,
@@ -717,9 +722,9 @@
             &screenSpace.data[0]);
 
     glVertexAttribPointer(mDrawLinearGradientProgram->position, 2, GL_FLOAT, GL_FALSE,
-            gDrawTextureVertexStride, &mDrawTextureVertices[0].position[0]);
+            gMeshStride, &mMeshVertices[0].position[0]);
 
-    glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
 }
 
 void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float bottom,
@@ -757,7 +762,7 @@
     resetDrawTextureTexCoords(u1, v1, u2, v2);
 
     drawTextureMesh(left, top, right, bottom, texture->id, alpha, mode, texture->blend,
-            &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+            &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], NULL);
 
     resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
 }
@@ -769,13 +774,13 @@
     getAlphaAndMode(paint, &alpha, &mode);
 
     drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, texture->blend,
-            &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+            &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], NULL);
 }
 
 void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
         GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) {
     drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend,
-            &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+            &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], NULL);
 }
 
 void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
@@ -794,12 +799,12 @@
     glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
 
     glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE,
-            gDrawTextureVertexStride, vertices);
+            gMeshStride, vertices);
     glVertexAttribPointer(mDrawTextureProgram->texCoords, 2, GL_FLOAT, GL_FALSE,
-            gDrawTextureVertexStride, texCoords);
+            gMeshStride, texCoords);
 
     if (!indices) {
-        glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
     } else {
         glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_SHORT, indices);
     }
@@ -842,7 +847,7 @@
 }
 
 void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
-    TextureVertex* v = &mDrawTextureVertices[0];
+    TextureVertex* v = &mMeshVertices[0];
     TextureVertex::setUV(v++, u1, v1);
     TextureVertex::setUV(v++, u2, v1);
     TextureVertex::setUV(v++, u1, v2);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 248c9c3..975be05 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -41,6 +41,7 @@
 #include "PatchCache.h"
 #include "Vertex.h"
 #include "FontRenderer.h"
+#include "ProgramCache.h"
 
 namespace android {
 namespace uirenderer {
@@ -268,7 +269,7 @@
             SkXfermode::Mode mode);
 
     /**
-     * Resets the texture coordinates stored in mDrawTextureVertices. Setting the values
+     * Resets the texture coordinates stored in mMeshVertices. Setting the values
      * back to default is achieved by calling:
      *
      * resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
@@ -337,7 +338,7 @@
     sp<DrawLinearGradientProgram> mDrawLinearGradientProgram;
 
     // Used to draw textured quads
-    TextureVertex mDrawTextureVertices[4];
+    TextureVertex mMeshVertices[4];
 
     // Current texture state
     GLuint mLastTexture;
@@ -372,6 +373,7 @@
     TextureCache mTextureCache;
     LayerCache mLayerCache;
     GradientCache mGradientCache;
+    ProgramCache mProgramCache;
     PatchCache mPatchCache;
 }; // class OpenGLRenderer
 
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 6e60808..86fc154 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -27,7 +27,6 @@
 
 #define SHADER_SOURCE(name, source) const char* name = #source
 
-#include "shaders/drawColor.vert"
 #include "shaders/drawColor.frag"
 
 #include "shaders/drawTexture.vert"
@@ -127,7 +126,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 DrawColorProgram::DrawColorProgram():
-        Program(gDrawColorVertexShader, gDrawColorFragmentShader) {
+        Program(gDrawTextureVertexShader, gDrawColorFragmentShader) {
     getAttribsAndUniforms();
 }
 
@@ -138,6 +137,7 @@
 
 void DrawColorProgram::getAttribsAndUniforms() {
     position = addAttrib("position");
+    texCoords = addAttrib("texCoords");
     color = addUniform("color");
     transform = addUniform("transform");
 }
@@ -154,11 +154,13 @@
 void DrawColorProgram::use() {
     Program::use();
     glEnableVertexAttribArray(position);
+    glEnableVertexAttribArray(texCoords);
 }
 
 void DrawColorProgram::remove() {
     Program::remove();
     glDisableVertexAttribArray(position);
+    glDisableVertexAttribArray(texCoords);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -167,26 +169,21 @@
 
 DrawTextureProgram::DrawTextureProgram():
         DrawColorProgram(gDrawTextureVertexShader, gDrawTextureFragmentShader) {
-    texCoords = addAttrib("texCoords");
     sampler = addUniform("sampler");
 }
 
 DrawTextureProgram::DrawTextureProgram(const char* vertex, const char* fragment):
         DrawColorProgram(vertex, fragment) {
-    texCoords = addAttrib("texCoords");
     sampler = addUniform("sampler");
 }
 
 void DrawTextureProgram::use() {
     DrawColorProgram::use();
-    glActiveTexture(GL_TEXTURE0);
     glUniform1i(sampler, 0);
-    glEnableVertexAttribArray(texCoords);
 }
 
 void DrawTextureProgram::remove() {
     DrawColorProgram::remove();
-    glDisableVertexAttribArray(texCoords);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 824aa05..2cdd905 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -53,6 +53,16 @@
     virtual void remove();
 
     /**
+     * Returns the OpenGL name of the specified attribute.
+     */
+    int getAttrib(const char* name);
+
+    /**
+     * Returns the OpenGL name of the specified uniform.
+     */
+    int getUniform(const char* name);
+
+    /**
      * Indicates whether this program is currently in use with
      * the GL context.
      */
@@ -67,10 +77,6 @@
      * @return The OpenGL name of the attribute.
      */
     int addAttrib(const char* name);
-    /**
-     * Returns the OpenGL name of the specified attribute.
-     */
-    int getAttrib(const char* name);
 
     /**
      * Adds a uniform with the specified name.
@@ -78,10 +84,6 @@
      * @return The OpenGL name of the uniform.
      */
     int addUniform(const char* name);
-    /**
-     * Returns the OpenGL name of the specified uniform.
-     */
-    int getUniform(const char* name);
 
 private:
     /**
@@ -145,6 +147,11 @@
     int position;
 
     /**
+     * Name of the texture coordinates attribute.
+     */
+    int texCoords;
+
+    /**
      * Name of the color uniform.
      */
     int color;
@@ -184,11 +191,6 @@
      * Name of the texture sampler uniform.
      */
     int sampler;
-
-    /**
-     * Name of the texture coordinates attribute.
-     */
-    int texCoords;
 };
 
 class DrawTextProgram: public DrawTextureProgram {
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
new file mode 100644
index 0000000..5a89eb6
--- /dev/null
+++ b/libs/hwui/ProgramCache.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/String8.h>
+
+#include "ProgramCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Vertex shaders snippets
+///////////////////////////////////////////////////////////////////////////////
+
+// TODO: Implement BitmapShader, implement repeat/mirror for npot
+
+const char* gVS_Header_Attributes =
+        "attribute vec4 position;\n";
+const char* gVS_Header_Attributes_TexCoords =
+        "attribute vec2 texCoords;\n";
+const char* gVS_Header_Uniforms =
+        "uniform mat4 transform;\n";
+const char* gVS_Header_Uniforms_HasGradient =
+        "uniform float gradientLength;\n"
+        "uniform vec2 gradient;\n"
+        "uniform vec2 gradientStart;\n"
+        "uniform mat4 screenSpace;\n";
+const char* gVS_Header_Varyings_HasTexture =
+        "varying vec2 outTexCoords;\n";
+const char* gVS_Header_Varyings_HasBitmap =
+        "varying vec2 outBitmapTexCoords;\n";
+const char* gVS_Header_Varyings_HasGradient =
+        "varying float index;\n";
+const char* gVS_Main =
+        "\nvoid main(void) {\n";
+const char* gVS_Main_OutTexCoords =
+        "    outTexCoords = texCoords;\n";
+const char* gVS_Main_OutGradientIndex =
+        "    vec4 location = screenSpace * position;\n"
+        "    index = dot(location.xy - gradientStart, gradient) * gradientLength;\n";
+const char* gVS_Main_Position =
+        "    gl_Position = transform * position;\n";
+const char* gVS_Footer =
+        "}\n\n";
+
+///////////////////////////////////////////////////////////////////////////////
+// Fragment shaders snippets
+///////////////////////////////////////////////////////////////////////////////
+
+const char* gFS_Header =
+        "precision mediump float;\n\n";
+const char* gFS_Uniforms_Color =
+        "uniform vec4 color;\n";
+const char* gFS_Uniforms_TextureSampler =
+        "uniform sampler2D sampler;\n";
+const char* gFS_Uniforms_GradientSampler =
+        "uniform sampler2D gradientSampler;\n";
+const char* gFS_Uniforms_BitmapSampler =
+        "uniform sampler2D bitmapSampler;\n";
+const char* gFS_Uniforms_ColorOp[4] = {
+        // None
+        "",
+        // Matrix
+        "uniform mat4 colorMatrix;\n"
+        "uniform vec4 colorMatrixVector;\n",
+        // Lighting
+        "uniform float lightingMul;\n"
+        "uniform float lightingAdd;\n",
+        // PorterDuff
+        "uniform vec4 colorBLend;\n"
+};
+const char* gFS_Main =
+        "\nvoid main(void) {\n"
+        "    vec4 fragColor;\n";
+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_FetchGradient =
+        "    vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n";
+const char* gFS_Main_FetchBitmap =
+        "    vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
+const char* gFS_Main_BlendShadersBG =
+        "    fragColor = blendShaders(bitmapColor, gradientColor)";
+const char* gFS_Main_BlendShadersGB =
+        "    fragColor = blendShaders(gradientColor, bitmapColor)";
+const char* gFS_Main_BlendShaders_Modulate =
+        " * fragColor.a;\n";
+const char* gFS_Main_FragColor =
+        "    gl_FragColor = fragColor;\n";
+const char* gFS_Main_ApplyColorOp[4] = {
+        // None
+        "",
+        // Matrix
+        "    fragColor *= colorMatrix;\n"
+        "    fragColor += colorMatrixVector;\n",
+        // Lighting
+        "    fragColor *= lightingMul;\n"
+        "    fragColor += lightingAdd;\n",
+        // PorterDuff
+        "    fragColor = blendColors(colorBlend, fragColor);\n"
+};
+const char* gFS_Footer =
+        "}\n\n";
+
+///////////////////////////////////////////////////////////////////////////////
+// PorterDuff snippets
+///////////////////////////////////////////////////////////////////////////////
+
+const char* gPorterDuff[12] = {
+        // Clear
+        "return vec4(0.0, 0.0, 0.0, 0.0);\n",
+        // Src
+        "return src;\n",
+        // Dst
+        "return dst;\n",
+        // SrcOver
+        "return vec4(src.rgb + (1.0 - src.a) * dst.rgb, src.a + dst.a - src.a * dst.a);\n",
+        // DstOver
+        "return vec4(dst.rgb + (1.0 - dst.a) * src.rgb, src.a + dst.a - src.a * dst.a);\n",
+        // SrcIn
+        "return vec4(src.rgb * dst.a, src.a * dst.a);\n",
+        // DstIn
+        "return vec4(dst.rgb * src.a, src.a * dst.a);\n",
+        // SrcOut
+        "return vec4(src.rgb * (1.0 - dst.a), src.a * (1.0 - dst.a));\n",
+        // DstOut
+        "return vec4(dst.rgb * (1.0 - src.a), dst.a * (1.0 - src.a));\n",
+        // SrcAtop
+        "return vec4(src.rgb * dst.a + (1.0 - src.a) * dst.rgb, dst.a);\n",
+        // DstAtop
+        "return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n",
+        // Xor
+        "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, "
+                "src.a + dst.a - 2.0 * src.a * dst.a);\n",
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructors
+///////////////////////////////////////////////////////////////////////////////
+
+ProgramCache::ProgramCache() {
+}
+
+ProgramCache::~ProgramCache() {
+    clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache management
+///////////////////////////////////////////////////////////////////////////////
+
+void ProgramCache::clear() {
+    size_t count = mCache.size();
+    for (size_t i = 0; i < count; i++) {
+        delete mCache.valueAt(i);
+    }
+    mCache.clear();
+}
+
+Program* ProgramCache::get(const ProgramDescription& description) {
+    programid key = description.key();
+    ssize_t index = mCache.indexOfKey(key);
+    Program* program = NULL;
+    if (index < 0) {
+        PROGRAM_LOGD("Could not find program with key 0x%x", key);
+        program = generateProgram(description, key);
+        mCache.add(key, program);
+    } else {
+        program = mCache.valueAt(index);
+    }
+    return program;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Program generation
+///////////////////////////////////////////////////////////////////////////////
+
+Program* ProgramCache::generateProgram(const ProgramDescription& description, programid key) {
+    String8 vertexShader = generateVertexShader(description);
+    String8 fragmentShader = generateFragmentShader(description);
+
+    Program* program = new Program(vertexShader.string(), fragmentShader.string());
+    return program;
+}
+
+String8 ProgramCache::generateVertexShader(const ProgramDescription& description) {
+    // Add attributes
+    String8 shader(gVS_Header_Attributes);
+    if (description.hasTexture || description.hasBitmap) {
+        shader.append(gVS_Header_Attributes_TexCoords);
+    }
+    // Uniforms
+    shader.append(gVS_Header_Uniforms);
+    if (description.hasGradient) {
+        shader.append(gVS_Header_Uniforms_HasGradient);
+    }
+    // Varyings
+    if (description.hasTexture) {
+        shader.append(gVS_Header_Varyings_HasTexture);
+    }
+    if (description.hasGradient) {
+        shader.append(gVS_Header_Varyings_HasGradient);
+    }
+    if (description.hasBitmap) {
+        shader.append(gVS_Header_Varyings_HasBitmap);
+    }
+
+    // Begin the shader
+    shader.append(gVS_Main); {
+        if (description.hasTexture) {
+            shader.append(gVS_Main_OutTexCoords);
+        }
+        if (description.hasGradient) {
+            shader.append(gVS_Main_OutGradientIndex);
+        }
+        // Output transformed position
+        shader.append(gVS_Main_Position);
+    }
+    // End the shader
+    shader.append(gVS_Footer);
+
+    PROGRAM_LOGD("*** Generated vertex shader:\n\n%s", shader.string());
+
+    return shader;
+}
+
+String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
+    // Set the default precision
+    String8 shader(gFS_Header);
+
+    // Varyings
+    if (description.hasTexture) {
+        shader.append(gVS_Header_Varyings_HasTexture);
+    }
+    if (description.hasGradient) {
+        shader.append(gVS_Header_Varyings_HasGradient);
+    }
+    if (description.hasBitmap) {
+        shader.append(gVS_Header_Varyings_HasBitmap);
+    }
+
+
+    // Uniforms
+    shader.append(gFS_Uniforms_Color);
+    if (description.hasTexture) {
+        shader.append(gFS_Uniforms_TextureSampler);
+    }
+    if (description.hasGradient) {
+        shader.append(gFS_Uniforms_GradientSampler);
+    }
+    if (description.hasBitmap) {
+        shader.append(gFS_Uniforms_BitmapSampler);
+    }
+    shader.append(gFS_Uniforms_ColorOp[description.colorOp]);
+
+    // Generate required functions
+    if (description.hasGradient && description.hasBitmap) {
+        generatePorterDuffBlend(shader, "blendShaders", description.shadersMode);
+    }
+    if (description.colorOp == ProgramDescription::kColorBlend) {
+        generatePorterDuffBlend(shader, "blendColors", description.colorMode);
+    }
+
+    // Begin the shader
+    shader.append(gFS_Main); {
+        // Stores the result in fragColor directly
+        if (description.hasTexture) {
+            if (description.hasAlpha8Texture) {
+                shader.append(gFS_Main_FetchA8Texture);
+            } else {
+                shader.append(gFS_Main_FetchTexture);
+            }
+        } else {
+            shader.append(gFS_Main_FetchColor);
+        }
+        if (description.hasGradient) {
+            shader.append(gFS_Main_FetchGradient);
+        }
+        if (description.hasBitmap) {
+            shader.append(gFS_Main_FetchBitmap);
+        }
+        // Case when we have two shaders set
+        if (description.hasGradient && description.hasBitmap) {
+            if (description.isBitmapFirst) {
+                shader.append(gFS_Main_BlendShadersBG);
+            } else {
+                shader.append(gFS_Main_BlendShadersGB);
+            }
+            shader.append(gFS_Main_BlendShaders_Modulate);
+        }
+        // Apply the color op if needed
+        shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
+        // Output the fragment
+        shader.append(gFS_Main_FragColor);
+    }
+    // End the shader
+    shader.append(gFS_Footer);
+
+    PROGRAM_LOGD("*** Generated fragment shader:\n\n%s", shader.string());
+    return shader;
+}
+
+void ProgramCache::generatePorterDuffBlend(String8& shader, const char* name,
+        SkXfermode::Mode mode) {
+    shader.append("\nvec4 ");
+    shader.append(name);
+    shader.append("(vec4 src, vec4 dst) {\n");
+    shader.append("    ");
+    shader.append(gPorterDuff[mode]);
+    shader.append("}\n");
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
new file mode 100644
index 0000000..7f2f5fa
--- /dev/null
+++ b/libs/hwui/ProgramCache.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_PROGRAM_CACHE_H
+#define ANDROID_UI_PROGRAM_CACHE_H
+
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+
+#include <SkXfermode.h>
+
+#include "Program.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_PROGRAM_CACHE 0
+
+// Debug
+#if DEBUG_PROGRAM_CACHE
+    #define PROGRAM_LOGD(...) LOGD(__VA_ARGS__)
+#else
+    #define PROGRAM_LOGD(...)
+#endif
+
+#define PROGRAM_KEY_TEXTURE 0x1
+#define PROGRAM_KEY_A8_TEXTURE 0x2
+#define PROGRAM_KEY_BITMAP 0x4
+#define PROGRAM_KEY_GRADIENT 0x8
+#define PROGRAM_KEY_BITMAP_FIRST 0x10
+#define PROGRAM_KEY_COLOR_MATRIX 0x20
+#define PROGRAM_KEY_COLOR_LIGHTING 0x40
+#define PROGRAM_KEY_COLOR_BLEND 0x80
+
+// Support only the 12 Porter-Duff modes for now
+#define PROGRAM_MAX_XFERMODE 0xC
+#define PROGRAM_XFERMODE_SHADER_SHIFT 24
+#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
+
+///////////////////////////////////////////////////////////////////////////////
+// Types
+///////////////////////////////////////////////////////////////////////////////
+
+typedef uint32_t programid;
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Describe the features required for a given program. The features
+ * determine the generation of both the vertex and fragment shaders.
+ * A ProgramDescription must be used in conjunction with a ProgramCache.
+ */
+struct ProgramDescription {
+    enum ColorModifier {
+        kColorNone,
+        kColorMatrix,
+        kColorLighting,
+        kColorBlend
+    };
+
+    ProgramDescription():
+        hasTexture(false), hasAlpha8Texture(false),
+        hasBitmap(false), hasGradient(false), shadersMode(SkXfermode::kClear_Mode),
+        colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode) {
+    }
+
+    // Texturing
+    bool hasTexture;
+    bool hasAlpha8Texture;
+
+    // Shaders
+    bool hasBitmap;
+    bool hasGradient;
+    SkXfermode::Mode shadersMode;
+    bool isBitmapFirst;
+
+    // Color operations
+    int colorOp;
+    SkXfermode::Mode colorMode;
+
+    programid key() const {
+        programid key = 0;
+        if (hasTexture) key |= PROGRAM_KEY_TEXTURE;
+        if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
+        if (hasBitmap) key |= PROGRAM_KEY_BITMAP;
+        if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
+        if (isBitmapFirst) key  |= PROGRAM_KEY_BITMAP_FIRST;
+        if (hasBitmap && hasGradient) {
+            key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
+        }
+        switch (colorOp) {
+            case kColorMatrix:
+                key |= PROGRAM_KEY_COLOR_MATRIX;
+                break;
+            case kColorLighting:
+                key |= PROGRAM_KEY_COLOR_LIGHTING;
+                break;
+            case kColorBlend:
+                key |= PROGRAM_KEY_COLOR_BLEND;
+                key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
+                break;
+            case kColorNone:
+                break;
+        }
+        return key;
+    }
+}; // struct ProgramDescription
+
+/**
+ * Generates and caches program. Programs are generated based on
+ * ProgramDescriptions.
+ */
+class ProgramCache {
+public:
+    ProgramCache();
+    ~ProgramCache();
+
+    Program* get(const ProgramDescription& description);
+
+    void clear();
+
+private:
+    Program* generateProgram(const ProgramDescription& description, programid key);
+    String8 generateVertexShader(const ProgramDescription& description);
+    String8 generateFragmentShader(const ProgramDescription& description);
+    void generatePorterDuffBlend(String8& shader, const char* name, SkXfermode::Mode mode);
+
+    KeyedVector<programid, Program*> mCache;
+
+}; // class ProgramCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_PROGRAM_CACHE_H
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index ffd0633..1f54086 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -21,14 +21,6 @@
 namespace uirenderer {
 
 /**
- * Simple structure to describe a vertex with a position.
- * This is used to draw filled rectangles without a texture.
- */
-struct SimpleVertex {
-    float position[2];
-}; // struct SimpleVertex
-
-/**
  * Simple structure to describe a vertex with a position and a texture.
  */
 struct TextureVertex {
diff --git a/libs/hwui/shaders/drawColor.vert b/libs/hwui/shaders/drawColor.vert
deleted file mode 100644
index 20e2636..0000000
--- a/libs/hwui/shaders/drawColor.vert
+++ /dev/null
@@ -1,11 +0,0 @@
-SHADER_SOURCE(gDrawColorVertexShader,
-
-attribute vec4 position;
-
-uniform mat4 transform;
-
-void main(void) {
-    gl_Position = transform * position;
-}
-
-);
diff --git a/libs/hwui/shaders/drawLinearGradient.vert b/libs/hwui/shaders/drawLinearGradient.vert
index f5c669b..d2f857d 100644
--- a/libs/hwui/shaders/drawLinearGradient.vert
+++ b/libs/hwui/shaders/drawLinearGradient.vert
@@ -2,10 +2,10 @@
 
 attribute vec4 position;
 
+uniform mat4 transform;
 uniform float gradientLength;
 uniform vec2 gradient;
 uniform vec2 start;
-uniform mat4 transform;
 uniform mat4 screenSpace;
 
 varying float index;