Use LruCache instead of GenerationCache in libhwui

Change-Id: Ic26ddc7151eb5462bcd243b21daf7187ed6d3bec
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 150caf3..29a36de 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -792,7 +792,9 @@
     static void doTextBounds(JNIEnv* env, const jchar* text, int count,
                              jobject bounds, const SkPaint& paint)
     {
-        SkRect  r{0,0,0,0};
+        SkRect  r;
+        r.set(0,0,0,0);
+
         SkIRect ir;
 
         sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 2e4e349..35a8487 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
+#include <utils/JenkinsHash.h>
 #include <utils/threads.h>
 
 #include "Caches.h"
@@ -43,11 +44,38 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// Cache entry
+///////////////////////////////////////////////////////////////////////////////
+
+hash_t GradientCacheEntry::hash() const {
+    uint32_t hash = JenkinsHashMix(0, count);
+    hash = JenkinsHashMix(hash, tileMode);
+    for (uint32_t i = 0; i < count; i++) {
+        hash = JenkinsHashMix(hash, android::hash_type(colors[i]));
+        hash = JenkinsHashMix(hash, android::hash_type(positions[i]));
+    }
+    return JenkinsHashWhiten(hash);
+}
+
+int GradientCacheEntry::compare(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs) {
+    int deltaInt = int(lhs.count) - int(rhs.count);
+    if (deltaInt != 0) return deltaInt;
+
+    deltaInt = lhs.tileMode - rhs.tileMode;
+    if (deltaInt != 0) return deltaInt;
+
+    deltaInt = memcmp(lhs.colors, rhs.colors, lhs.count * sizeof(uint32_t));
+    if (deltaInt != 0) return deltaInt;
+
+    return memcmp(lhs.positions, rhs.positions, lhs.count * sizeof(float));
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
 GradientCache::GradientCache():
-        mCache(GenerationCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
+        mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) {
     char property[PROPERTY_VALUE_MAX];
     if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
@@ -63,7 +91,7 @@
 }
 
 GradientCache::GradientCache(uint32_t maxByteSize):
-        mCache(GenerationCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
+        mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
     mCache.setOnEntryRemovedListener(this);
 }
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 3b7c1fa..d183a85 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -21,12 +21,11 @@
 
 #include <SkShader.h>
 
+#include <utils/LruCache.h>
 #include <utils/Mutex.h>
 #include <utils/Vector.h>
 
 #include "Texture.h"
-#include "utils/Compare.h"
-#include "utils/GenerationCache.h"
 
 namespace android {
 namespace uirenderer {
@@ -38,7 +37,7 @@
         positions = NULL;
     }
 
-    GradientCacheEntry(uint32_t* colors, float* positions, int count) {
+    GradientCacheEntry(uint32_t* colors, float* positions, uint32_t count) {
         copy(colors, positions, count);
     }
 
@@ -62,27 +61,26 @@
         return *this;
     }
 
-    bool operator<(const GradientCacheEntry& r) const {
-        const GradientCacheEntry& rhs = (const GradientCacheEntry&) r;
-        LTE_INT(count) {
-            int result = memcmp(colors, rhs.colors, count * sizeof(uint32_t));
-            if (result< 0) return true;
-            else if (result == 0) {
-                result = memcmp(positions, rhs.positions, count * sizeof(float));
-                if (result < 0) return true;
-            }
-        }
-        return false;
+    hash_t hash() const;
+
+    static int compare(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs);
+
+    bool operator==(const GradientCacheEntry& other) const {
+        return compare(*this, other) == 0;
+    }
+
+    bool operator!=(const GradientCacheEntry& other) const {
+        return compare(*this, other) != 0;
     }
 
     uint32_t* colors;
     float* positions;
-    int count;
+    uint32_t count;
     SkShader::TileMode tileMode;
 
 private:
 
-    void copy(uint32_t* colors, float* positions, int count) {
+    void copy(uint32_t* colors, float* positions, uint32_t count) {
         this->count = count;
         this->colors = new uint32_t[count];
         this->positions = new float[count];
@@ -93,6 +91,20 @@
 
 }; // GradientCacheEntry
 
+// Caching support
+
+inline int strictly_order_type(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs) {
+    return GradientCacheEntry::compare(lhs, rhs) < 0;
+}
+
+inline int compare_type(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs) {
+    return GradientCacheEntry::compare(lhs, rhs);
+}
+
+inline hash_t hash_type(const GradientCacheEntry& entry) {
+    return entry.hash();
+}
+
 /**
  * A simple LRU gradient cache. The cache has a maximum size expressed in bytes.
  * Any texture added to the cache causing the cache to grow beyond the maximum
@@ -150,7 +162,7 @@
 
     void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info);
 
-    GenerationCache<GradientCacheEntry, Texture*> mCache;
+    LruCache<GradientCacheEntry, Texture*> mCache;
 
     uint32_t mSize;
     uint32_t mMaxSize;
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 71a4ed7..03ddf59 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -55,22 +55,19 @@
 }
 
 void PathCache::remove(SkPath* path) {
-    // TODO: Linear search...
-    Vector<size_t> pathsToRemove;
-    for (size_t i = 0; i < mCache.size(); i++) {
-        if (mCache.getKeyAt(i).path == path) {
-            pathsToRemove.push(i);
-            removeTexture(mCache.getValueAt(i));
+    Vector<PathCacheEntry> pathsToRemove;
+    LruCache<PathCacheEntry, PathTexture*>::Iterator i(mCache);
+
+    while (i.next()) {
+        const PathCacheEntry& key = i.key();
+        if (key.path == path) {
+            pathsToRemove.push(key);
         }
     }
 
-    mCache.setOnEntryRemovedListener(NULL);
     for (size_t i = 0; i < pathsToRemove.size(); i++) {
-        // This will work because pathsToRemove is sorted
-        // and because the cache is a sorted keyed vector
-        mCache.removeAt(pathsToRemove.itemAt(i) - i);
+        mCache.remove(pathsToRemove.itemAt(i));
     }
-    mCache.setOnEntryRemovedListener(this);
 }
 
 void PathCache::removeDeferred(SkPath* path) {
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 4904a58..8a0235b 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -22,8 +22,6 @@
 #include "Debug.h"
 #include "ShapeCache.h"
 
-#include "utils/Compare.h"
-
 namespace android {
 namespace uirenderer {
 
@@ -41,18 +39,28 @@
         path = NULL;
     }
 
-    bool lessThan(const ShapeCacheEntry& r) const {
+    hash_t hash() const {
+        uint32_t hash = ShapeCacheEntry::hash();
+        hash = JenkinsHashMix(hash, android::hash_type(path));
+        return JenkinsHashWhiten(hash);
+    }
+
+    int compare(const ShapeCacheEntry& r) const {
+        int deltaInt = ShapeCacheEntry::compare(r);
+        if (deltaInt != 0) return deltaInt;
+
         const PathCacheEntry& rhs = (const PathCacheEntry&) r;
-        LTE_INT(path) {
-            return false;
-        }
-        return false;
+        return path - rhs.path;
     }
 
     SkPath* path;
 
 }; // PathCacheEntry
 
+inline hash_t hash_type(const PathCacheEntry& entry) {
+    return entry.hash();
+}
+
 /**
  * A simple LRU path cache. The cache has a maximum size expressed in bytes.
  * Any texture added to the cache causing the cache to grow beyond the maximum
diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h
index 3a95b99..47cab83e 100644
--- a/libs/hwui/ShapeCache.h
+++ b/libs/hwui/ShapeCache.h
@@ -25,11 +25,12 @@
 #include <SkPath.h>
 #include <SkRect.h>
 
+#include <utils/JenkinsHash.h>
+#include <utils/LruCache.h>
+
 #include "Debug.h"
 #include "Properties.h"
 #include "Texture.h"
-#include "utils/Compare.h"
-#include "utils/GenerationCache.h"
 
 namespace android {
 namespace uirenderer {
@@ -89,10 +90,8 @@
         join = SkPaint::kDefault_Join;
         cap = SkPaint::kDefault_Cap;
         style = SkPaint::kFill_Style;
-        float v = 4.0f;
-        miter = *(uint32_t*) &v;
-        v = 1.0f;
-        strokeWidth = *(uint32_t*) &v;
+        miter = 4.0f;
+        strokeWidth = 1.0f;
         pathEffect = NULL;
     }
 
@@ -100,10 +99,8 @@
         shapeType = type;
         join = paint->getStrokeJoin();
         cap = paint->getStrokeCap();
-        float v = paint->getStrokeMiter();
-        miter = *(uint32_t*) &v;
-        v = paint->getStrokeWidth();
-        strokeWidth = *(uint32_t*) &v;
+        miter = paint->getStrokeMiter();
+        strokeWidth = paint->getStrokeWidth();
         style = paint->getStyle();
         pathEffect = paint->getPathEffect();
     }
@@ -111,47 +108,80 @@
     virtual ~ShapeCacheEntry() {
     }
 
+    virtual hash_t hash() const {
+        uint32_t hash = JenkinsHashMix(0, shapeType);
+        hash = JenkinsHashMix(hash, join);
+        hash = JenkinsHashMix(hash, cap);
+        hash = JenkinsHashMix(hash, style);
+        hash = JenkinsHashMix(hash, android::hash_type(miter));
+        hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
+        hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
+        return JenkinsHashWhiten(hash);
+    }
+
+    virtual int compare(const ShapeCacheEntry& rhs) const {
+        int deltaInt = shapeType - rhs.shapeType;
+        if (deltaInt != 0) return deltaInt;
+
+        deltaInt = join - rhs.join;
+        if (deltaInt != 0) return deltaInt;
+
+        deltaInt = cap - rhs.cap;
+        if (deltaInt != 0) return deltaInt;
+
+        deltaInt = style - rhs.style;
+        if (deltaInt != 0) return deltaInt;
+
+        if (miter < rhs.miter) return -1;
+        if (miter > rhs.miter) return +1;
+
+        if (strokeWidth < rhs.strokeWidth) return -1;
+        if (strokeWidth > rhs.strokeWidth) return +1;
+
+        if (pathEffect < rhs.pathEffect) return -1;
+        if (pathEffect > rhs.pathEffect) return +1;
+
+        return 0;
+    }
+
+    bool operator==(const ShapeCacheEntry& other) const {
+        return compare(other) == 0;
+    }
+
+    bool operator!=(const ShapeCacheEntry& other) const {
+        return compare(other) != 0;
+    }
+
     ShapeType shapeType;
     SkPaint::Join join;
     SkPaint::Cap cap;
     SkPaint::Style style;
-    uint32_t miter;
-    uint32_t strokeWidth;
+    float miter;
+    float strokeWidth;
     SkPathEffect* pathEffect;
-
-    bool operator<(const ShapeCacheEntry& rhs) const {
-        LTE_INT(shapeType) {
-            LTE_INT(join) {
-                LTE_INT(cap) {
-                    LTE_INT(style) {
-                        LTE_INT(miter) {
-                            LTE_INT(strokeWidth) {
-                                LTE_INT(pathEffect) {
-                                    return lessThan(rhs);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-protected:
-    virtual bool lessThan(const ShapeCacheEntry& rhs) const {
-        return false;
-    }
 }; // struct ShapeCacheEntry
 
+// Cache support
+
+inline int strictly_order_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
+    return lhs.compare(rhs) < 0;
+}
+
+inline int compare_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
+    return lhs.compare(rhs);
+}
+
+inline hash_t hash_type(const ShapeCacheEntry& entry) {
+    return entry.hash();
+}
 
 struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
     RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
             ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
-        mWidth = *(uint32_t*) &width;
-        mHeight = *(uint32_t*) &height;
-        mRx = *(uint32_t*) &rx;
-        mRy = *(uint32_t*) &ry;
+        mWidth = width;
+        mHeight = height;
+        mRx = rx;
+        mRy = ry;
     }
 
     RoundRectShapeCacheEntry(): ShapeCacheEntry() {
@@ -161,109 +191,175 @@
         mRy = 0;
     }
 
-    bool lessThan(const ShapeCacheEntry& r) const {
+    hash_t hash() const {
+        uint32_t hash = ShapeCacheEntry::hash();
+        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
+        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
+        hash = JenkinsHashMix(hash, android::hash_type(mRx));
+        hash = JenkinsHashMix(hash, android::hash_type(mRy));
+        return JenkinsHashWhiten(hash);
+    }
+
+    int compare(const ShapeCacheEntry& r) const {
+        int deltaInt = ShapeCacheEntry::compare(r);
+        if (deltaInt != 0) return deltaInt;
+
         const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
-        LTE_INT(mWidth) {
-            LTE_INT(mHeight) {
-                LTE_INT(mRx) {
-                    LTE_INT(mRy) {
-                        return false;
-                    }
-                }
-            }
-        }
-        return false;
+
+        if (mWidth < rhs.mWidth) return -1;
+        if (mWidth > rhs.mWidth) return +1;
+
+        if (mHeight < rhs.mHeight) return -1;
+        if (mHeight > rhs.mHeight) return +1;
+
+        if (mRx < rhs.mRx) return -1;
+        if (mRx > rhs.mRx) return +1;
+
+        if (mRy < rhs.mRy) return -1;
+        if (mRy > rhs.mRy) return +1;
+
+        return 0;
     }
 
 private:
-    uint32_t mWidth;
-    uint32_t mHeight;
-    uint32_t mRx;
-    uint32_t mRy;
+    float mWidth;
+    float mHeight;
+    float mRx;
+    float mRy;
 }; // RoundRectShapeCacheEntry
 
+inline hash_t hash_type(const RoundRectShapeCacheEntry& entry) {
+    return entry.hash();
+}
+
 struct CircleShapeCacheEntry: public ShapeCacheEntry {
     CircleShapeCacheEntry(float radius, SkPaint* paint):
             ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
-        mRadius = *(uint32_t*) &radius;
+        mRadius = radius;
     }
 
     CircleShapeCacheEntry(): ShapeCacheEntry() {
         mRadius = 0;
     }
 
-    bool lessThan(const ShapeCacheEntry& r) const {
+    hash_t hash() const {
+        uint32_t hash = ShapeCacheEntry::hash();
+        hash = JenkinsHashMix(hash, android::hash_type(mRadius));
+        return JenkinsHashWhiten(hash);
+    }
+
+    int compare(const ShapeCacheEntry& r) const {
+        int deltaInt = ShapeCacheEntry::compare(r);
+        if (deltaInt != 0) return deltaInt;
+
         const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
-        LTE_INT(mRadius) {
-            return false;
-        }
-        return false;
+
+        if (mRadius < rhs.mRadius) return -1;
+        if (mRadius > rhs.mRadius) return +1;
+
+        return 0;
     }
 
 private:
-    uint32_t mRadius;
+    float mRadius;
 }; // CircleShapeCacheEntry
 
+inline hash_t hash_type(const CircleShapeCacheEntry& entry) {
+    return entry.hash();
+}
+
 struct OvalShapeCacheEntry: public ShapeCacheEntry {
     OvalShapeCacheEntry(float width, float height, SkPaint* paint):
             ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) {
-        mWidth = *(uint32_t*) &width;
-        mHeight = *(uint32_t*) &height;
+        mWidth = width;
+        mHeight = height;
     }
 
     OvalShapeCacheEntry(): ShapeCacheEntry() {
         mWidth = mHeight = 0;
     }
 
-    bool lessThan(const ShapeCacheEntry& r) const {
+    hash_t hash() const {
+        uint32_t hash = ShapeCacheEntry::hash();
+        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
+        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
+        return JenkinsHashWhiten(hash);
+    }
+
+    int compare(const ShapeCacheEntry& r) const {
+        int deltaInt = ShapeCacheEntry::compare(r);
+        if (deltaInt != 0) return deltaInt;
+
         const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r;
-        LTE_INT(mWidth) {
-            LTE_INT(mHeight) {
-                return false;
-            }
-        }
-        return false;
+
+        if (mWidth < rhs.mWidth) return -1;
+        if (mWidth > rhs.mWidth) return +1;
+
+        if (mHeight < rhs.mHeight) return -1;
+        if (mHeight > rhs.mHeight) return +1;
+
+        return 0;
     }
 
 private:
-    uint32_t mWidth;
-    uint32_t mHeight;
+    float mWidth;
+    float mHeight;
 }; // OvalShapeCacheEntry
 
+inline hash_t hash_type(const OvalShapeCacheEntry& entry) {
+    return entry.hash();
+}
+
 struct RectShapeCacheEntry: public ShapeCacheEntry {
     RectShapeCacheEntry(float width, float height, SkPaint* paint):
             ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) {
-        mWidth = *(uint32_t*) &width;
-        mHeight = *(uint32_t*) &height;
+        mWidth = width;
+        mHeight = height;
     }
 
     RectShapeCacheEntry(): ShapeCacheEntry() {
         mWidth = mHeight = 0;
     }
 
-    bool lessThan(const ShapeCacheEntry& r) const {
+    hash_t hash() const {
+        uint32_t hash = ShapeCacheEntry::hash();
+        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
+        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
+        return JenkinsHashWhiten(hash);
+    }
+
+    int compare(const ShapeCacheEntry& r) const {
+        int deltaInt = ShapeCacheEntry::compare(r);
+        if (deltaInt != 0) return deltaInt;
+
         const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r;
-        LTE_INT(mWidth) {
-            LTE_INT(mHeight) {
-                return false;
-            }
-        }
-        return false;
+
+        if (mWidth < rhs.mWidth) return -1;
+        if (mWidth > rhs.mWidth) return +1;
+
+        if (mHeight < rhs.mHeight) return -1;
+        if (mHeight > rhs.mHeight) return +1;
+
+        return 0;
     }
 
 private:
-    uint32_t mWidth;
-    uint32_t mHeight;
+    float mWidth;
+    float mHeight;
 }; // RectShapeCacheEntry
 
+inline hash_t hash_type(const RectShapeCacheEntry& entry) {
+    return entry.hash();
+}
+
 struct ArcShapeCacheEntry: public ShapeCacheEntry {
     ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle,
             bool useCenter, SkPaint* paint):
             ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) {
-        mWidth = *(uint32_t*) &width;
-        mHeight = *(uint32_t*) &height;
-        mStartAngle = *(uint32_t*) &startAngle;
-        mSweepAngle = *(uint32_t*) &sweepAngle;
+        mWidth = width;
+        mHeight = height;
+        mStartAngle = startAngle;
+        mSweepAngle = sweepAngle;
         mUseCenter = useCenter ? 1 : 0;
     }
 
@@ -275,30 +371,49 @@
         mUseCenter = 0;
     }
 
-    bool lessThan(const ShapeCacheEntry& r) const {
+    hash_t hash() const {
+        uint32_t hash = ShapeCacheEntry::hash();
+        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
+        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
+        hash = JenkinsHashMix(hash, android::hash_type(mStartAngle));
+        hash = JenkinsHashMix(hash, android::hash_type(mSweepAngle));
+        hash = JenkinsHashMix(hash, mUseCenter);
+        return JenkinsHashWhiten(hash);
+    }
+
+    int compare(const ShapeCacheEntry& r) const {
+        int deltaInt = ShapeCacheEntry::compare(r);
+        if (deltaInt != 0) return deltaInt;
+
         const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r;
-        LTE_INT(mWidth) {
-            LTE_INT(mHeight) {
-                LTE_INT(mStartAngle) {
-                    LTE_INT(mSweepAngle) {
-                        LTE_INT(mUseCenter) {
-                            return false;
-                        }
-                    }
-                }
-            }
-        }
-        return false;
+
+        if (mWidth < rhs.mWidth) return -1;
+        if (mWidth > rhs.mWidth) return +1;
+
+        if (mHeight < rhs.mHeight) return -1;
+        if (mHeight > rhs.mHeight) return +1;
+
+        if (mStartAngle < rhs.mStartAngle) return -1;
+        if (mStartAngle > rhs.mStartAngle) return +1;
+
+        if (mSweepAngle < rhs.mSweepAngle) return -1;
+        if (mSweepAngle > rhs.mSweepAngle) return +1;
+
+        return mUseCenter - rhs.mUseCenter;
     }
 
 private:
-    uint32_t mWidth;
-    uint32_t mHeight;
-    uint32_t mStartAngle;
-    uint32_t mSweepAngle;
+    float mWidth;
+    float mHeight;
+    float mStartAngle;
+    float mSweepAngle;
     uint32_t mUseCenter;
 }; // ArcShapeCacheEntry
 
+inline hash_t hash_type(const ArcShapeCacheEntry& entry) {
+    return entry.hash();
+}
+
 /**
  * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
  * Any texture added to the cache causing the cache to grow beyond the maximum
@@ -356,7 +471,7 @@
 
     void removeTexture(PathTexture* texture);
 
-    GenerationCache<Entry, PathTexture*> mCache;
+    LruCache<Entry, PathTexture*> mCache;
     uint32_t mSize;
     uint32_t mMaxSize;
     GLuint mMaxTextureSize;
@@ -415,7 +530,7 @@
 
 template<class Entry>
 ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
-        mCache(GenerationCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
+        mCache(LruCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(MB(defaultSize)) {
     char property[PROPERTY_VALUE_MAX];
     if (property_get(propertyName, property, NULL) > 0) {
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 8426f58..4d80f73 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
+#include <utils/JenkinsHash.h>
+
 #include "Debug.h"
 #include "TextDropShadowCache.h"
 #include "Properties.h"
@@ -24,11 +26,71 @@
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
+// Cache support
+///////////////////////////////////////////////////////////////////////////////
+
+hash_t ShadowText::hash() const {
+    uint32_t charCount = len * sizeof(char16_t);
+    uint32_t hash = JenkinsHashMix(0, len);
+    hash = JenkinsHashMix(hash, android::hash_type(radius));
+    hash = JenkinsHashMix(hash, android::hash_type(textSize));
+    hash = JenkinsHashMix(hash, android::hash_type(typeface));
+    hash = JenkinsHashMix(hash, flags);
+    hash = JenkinsHashMix(hash, android::hash_type(italicStyle));
+    hash = JenkinsHashMix(hash, android::hash_type(scaleX));
+    hash = JenkinsHashMixShorts(hash, text, charCount);
+    for (uint32_t i = 0; i < charCount * 2; i++) {
+        hash = JenkinsHashMix(hash, android::hash_type(positions[i]));
+    }
+    return JenkinsHashWhiten(hash);
+}
+
+int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) {
+    int deltaInt = int(lhs.len) - int(rhs.len);
+    if (deltaInt != 0) return deltaInt;
+
+    deltaInt = lhs.flags - rhs.flags;
+    if (deltaInt != 0) return deltaInt;
+
+    if (lhs.radius < rhs.radius) return -1;
+    if (lhs.radius > rhs.radius) return +1;
+
+    if (lhs.typeface < rhs.typeface) return -1;
+    if (lhs.typeface > rhs.typeface) return +1;
+
+    if (lhs.textSize < rhs.textSize) return -1;
+    if (lhs.textSize > rhs.textSize) return +1;
+
+    if (lhs.italicStyle < rhs.italicStyle) return -1;
+    if (lhs.italicStyle > rhs.italicStyle) return +1;
+
+    if (lhs.scaleX < rhs.scaleX) return -1;
+    if (lhs.scaleX > rhs.scaleX) return +1;
+
+    if (lhs.text != rhs.text) {
+        if (!lhs.text) return -1;
+        if (!rhs.text) return +1;
+
+        deltaInt = memcmp(lhs.text, rhs.text, lhs.len);
+        if (deltaInt != 0) return deltaInt;
+    }
+
+    if (lhs.positions != rhs.positions) {
+        if (!lhs.positions) return -1;
+        if (!rhs.positions) return +1;
+
+        return memcmp(lhs.positions, rhs.positions, lhs.len << 2);
+    }
+
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
 TextDropShadowCache::TextDropShadowCache():
-        mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity),
+        mCache(LruCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) {
     char property[PROPERTY_VALUE_MAX];
     if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) {
@@ -43,7 +105,7 @@
 }
 
 TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize):
-        mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity),
+        mCache(LruCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
     init();
 }
@@ -102,7 +164,7 @@
 }
 
 ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len,
-        int numGlyphs, uint32_t radius, const float* positions) {
+        int numGlyphs, float radius, const float* positions) {
     ShadowText entry(paint, radius, len, text, positions);
     ShadowTexture* texture = mCache.get(entry);
 
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index bae0c49..38bf05a 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -21,10 +21,9 @@
 
 #include <SkPaint.h>
 
+#include <utils/LruCache.h>
 #include <utils/String16.h>
 
-#include "utils/Compare.h"
-#include "utils/GenerationCache.h"
 #include "FontRenderer.h"
 #include "Texture.h"
 
@@ -32,12 +31,13 @@
 namespace uirenderer {
 
 struct ShadowText {
-    ShadowText(): radius(0), len(0), textSize(0.0f), typeface(NULL) {
+    ShadowText(): len(0), radius(0.0f), textSize(0.0f), typeface(NULL),
+            flags(0), italicStyle(0.0f), scaleX(0), text(NULL), positions(NULL) {
     }
 
-    ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText,
+    ShadowText(SkPaint* paint, float radius, uint32_t len, const char* srcText,
             const float* positions):
-            radius(radius), len(len), positions(positions) {
+            len(len), radius(radius), positions(positions) {
         // TODO: Propagate this through the API, we should not cast here
         text = (const char16_t*) srcText;
 
@@ -49,30 +49,27 @@
             flags |= Font::kFakeBold;
         }
 
-        const float skewX = paint->getTextSkewX();
-        italicStyle = *(uint32_t*) &skewX;
-
-        const float scaleXFloat = paint->getTextScaleX();
-        scaleX = *(uint32_t*) &scaleXFloat;
+        italicStyle = paint->getTextSkewX();
+        scaleX = paint->getTextScaleX();
     }
 
     ~ShadowText() {
     }
 
-    uint32_t radius;
-    uint32_t len;
-    float textSize;
-    SkTypeface* typeface;
-    uint32_t flags;
-    uint32_t italicStyle;
-    uint32_t scaleX;
-    const char16_t* text;
-    const float* positions;
-    String16 str;
-    Vector<float> positionsCopy;
+    hash_t hash() const;
+
+    static int compare(const ShadowText& lhs, const ShadowText& rhs);
+
+    bool operator==(const ShadowText& other) const {
+        return compare(*this, other) == 0;
+    }
+
+    bool operator!=(const ShadowText& other) const {
+        return compare(*this, other) != 0;
+    }
 
     void copyTextLocally() {
-        str.setTo((const char16_t*) text, len >> 1);
+        str.setTo((const char16_t*) text, len * sizeof(char16_t));
         text = str.string();
         if (positions != NULL) {
             positionsCopy.clear();
@@ -81,31 +78,36 @@
         }
     }
 
-    bool operator<(const ShadowText& rhs) const {
-        LTE_INT(len) {
-            LTE_INT(radius) {
-                LTE_FLOAT(textSize) {
-                    LTE_INT(typeface) {
-                        LTE_INT(flags) {
-                            LTE_INT(italicStyle) {
-                                LTE_INT(scaleX) {
-                                    int cmp = memcmp(text, rhs.text, len);
-                                    if (cmp < 0) return true;
-                                    if (cmp == 0 && rhs.positions != NULL) {
-                                        if (positions == NULL) return true;
-                                        return memcmp(positions, rhs.positions, len << 2) < 0;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
+    uint32_t len;
+    float radius;
+    float textSize;
+    SkTypeface* typeface;
+    uint32_t flags;
+    float italicStyle;
+    float scaleX;
+    const char16_t* text;
+    const float* positions;
+
+    // Not directly used to compute the cache key
+    String16 str;
+    Vector<float> positionsCopy;
+
 }; // struct ShadowText
 
+// Caching support
+
+inline int strictly_order_type(const ShadowText& lhs, const ShadowText& rhs) {
+    return ShadowText::compare(lhs, rhs) < 0;
+}
+
+inline int compare_type(const ShadowText& lhs, const ShadowText& rhs) {
+    return ShadowText::compare(lhs, rhs);
+}
+
+inline hash_t hash_type(const ShadowText& entry) {
+    return entry.hash();
+}
+
 /**
  * Alpha texture used to represent a shadow.
  */
@@ -130,7 +132,7 @@
     void operator()(ShadowText& text, ShadowTexture*& texture);
 
     ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len,
-            int numGlyphs, uint32_t radius, const float* positions);
+            int numGlyphs, float radius, const float* positions);
 
     /**
      * Clears the cache. This causes all textures to be deleted.
@@ -157,7 +159,7 @@
 private:
     void init();
 
-    GenerationCache<ShadowText, ShadowTexture*> mCache;
+    LruCache<ShadowText, ShadowTexture*> mCache;
 
     uint32_t mSize;
     uint32_t mMaxSize;
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 10d112a..abf2d98 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -34,7 +34,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 TextureCache::TextureCache():
-        mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
+        mCache(LruCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
         mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) {
     char property[PROPERTY_VALUE_MAX];
@@ -58,7 +58,7 @@
 }
 
 TextureCache::TextureCache(uint32_t maxByteSize):
-        mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
+        mCache(LruCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
     init();
 }
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 31a2e3d..80bb22e 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -19,12 +19,12 @@
 
 #include <SkBitmap.h>
 
+#include <utils/LruCache.h>
 #include <utils/Mutex.h>
 #include <utils/Vector.h>
 
 #include "Debug.h"
 #include "Texture.h"
-#include "utils/GenerationCache.h"
 
 namespace android {
 namespace uirenderer {
@@ -130,7 +130,7 @@
 
     void init();
 
-    GenerationCache<SkBitmap*, Texture*> mCache;
+    LruCache<SkBitmap*, Texture*> mCache;
 
     uint32_t mSize;
     uint32_t mMaxSize;