Optimize state changes

Change-Id: Iae59bc8dfd6427d0967472462cc1994987092827
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 9e59621..559c717 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -17,16 +17,264 @@
 #ifndef ANDROID_HWUI_PROGRAM_H
 #define ANDROID_HWUI_PROGRAM_H
 
+#include <utils/KeyedVector.h>
+
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
-#include <utils/KeyedVector.h>
+#include <SkXfermode.h>
 
 #include "Matrix.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
 
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#if DEBUG_PROGRAMS
+    #define PROGRAM_LOGD(...) LOGD(__VA_ARGS__)
+#else
+    #define PROGRAM_LOGD(...)
+#endif
+
+#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
+#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
+#define PROGRAM_KEY_BITMAP_NPOT 0x100
+#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
+
+#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
+#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
+
+// Encode the xfermodes on 6 bits
+#define PROGRAM_MAX_XFERMODE 0x1f
+#define PROGRAM_XFERMODE_SHADER_SHIFT 26
+#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
+#define PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT 14
+
+#define PROGRAM_BITMAP_WRAPS_SHIFT 9
+#define PROGRAM_BITMAP_WRAPT_SHIFT 11
+
+#define PROGRAM_GRADIENT_TYPE_SHIFT 33
+#define PROGRAM_MODULATE_SHIFT 35
+
+#define PROGRAM_IS_POINT_SHIFT 36
+
+#define PROGRAM_HAS_AA_SHIFT 37
+
+#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
+#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
+
+///////////////////////////////////////////////////////////////////////////////
+// Types
+///////////////////////////////////////////////////////////////////////////////
+
+typedef uint64_t programid;
+
+///////////////////////////////////////////////////////////////////////////////
+// Program description
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * 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
+    };
+
+    enum Gradient {
+        kGradientLinear,
+        kGradientCircular,
+        kGradientSweep
+    };
+
+    ProgramDescription() {
+        reset();
+    }
+
+    // Texturing
+    bool hasTexture;
+    bool hasAlpha8Texture;
+    bool hasExternalTexture;
+    bool hasTextureTransform;
+
+    // Modulate, this should only be set when setColor() return true
+    bool modulate;
+
+    // Shaders
+    bool hasBitmap;
+    bool isBitmapNpot;
+
+    bool isAA;
+
+    bool hasGradient;
+    Gradient gradientType;
+
+    SkXfermode::Mode shadersMode;
+
+    bool isBitmapFirst;
+    GLenum bitmapWrapS;
+    GLenum bitmapWrapT;
+
+    // Color operations
+    ColorModifier colorOp;
+    SkXfermode::Mode colorMode;
+
+    // Framebuffer blending (requires Extensions.hasFramebufferFetch())
+    // Ignored for all values < SkXfermode::kPlus_Mode
+    SkXfermode::Mode framebufferMode;
+    bool swapSrcDst;
+
+    bool isPoint;
+    float pointSize;
+
+    /**
+     * Resets this description. All fields are reset back to the default
+     * values they hold after building a new instance.
+     */
+    void reset() {
+        hasTexture = false;
+        hasAlpha8Texture = false;
+        hasExternalTexture = false;
+        hasTextureTransform = false;
+
+        isAA = false;
+
+        modulate = false;
+
+        hasBitmap = false;
+        isBitmapNpot = false;
+
+        hasGradient = false;
+        gradientType = kGradientLinear;
+
+        shadersMode = SkXfermode::kClear_Mode;
+
+        isBitmapFirst = false;
+        bitmapWrapS = GL_CLAMP_TO_EDGE;
+        bitmapWrapT = GL_CLAMP_TO_EDGE;
+
+        colorOp = kColorNone;
+        colorMode = SkXfermode::kClear_Mode;
+
+        framebufferMode = SkXfermode::kClear_Mode;
+        swapSrcDst = false;
+
+        isPoint = false;
+        pointSize = 0.0f;
+    }
+
+    /**
+     * 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;
+        if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
+        if (hasBitmap) {
+            key |= PROGRAM_KEY_BITMAP;
+            if (isBitmapNpot) {
+                key |= PROGRAM_KEY_BITMAP_NPOT;
+                key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT;
+                key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT;
+            }
+        }
+        if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
+        key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT;
+        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;
+        }
+        key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
+        if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
+        if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
+        if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
+        if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
+        if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
+        if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
+        return key;
+    }
+
+    /**
+     * Logs the specified message followed by the key identifying this program.
+     */
+    void log(const char* message) const {
+#if DEBUG_PROGRAMS
+        programid k = key();
+        PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32),
+                uint32_t(k & 0xffffffff));
+#endif
+    }
+
+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
+
 /**
  * A program holds a vertex and a fragment shader. It offers several utility
  * methods to query attributes and uniforms.
@@ -42,7 +290,7 @@
      * Creates a new program with the specified vertex and fragment
      * shaders sources.
      */
-    Program(const char* vertex, const char* fragment);
+    Program(const ProgramDescription& description, const char* vertex, const char* fragment);
     virtual ~Program();
 
     /**
@@ -99,6 +347,11 @@
     int position;
 
     /**
+     * Name of the texCoords attribute if it exists, -1 otherwise.
+     */
+    int texCoords;
+
+    /**
      * Name of the transform uniform.
      */
     int transform;