Purge Skia objects from GL caches as needed.

Change-Id: I754c671cf790ad5ae8bf047ad328034217da4ecc
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 95bb24f..880fb6e 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -254,7 +254,9 @@
 

 static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {

 #ifdef USE_OPENGL_RENDERER

-    android::uirenderer::Caches::getInstance().textureCache.remove(bitmap);

+    if (android::uirenderer::Caches::hasInstance()) {

+        android::uirenderer::Caches::getInstance().textureCache.remove(bitmap);

+    }

 #endif

     delete bitmap;

 }

diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index b8b2a10..abe33f4 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -26,12 +26,19 @@
 
 #include "SkPath.h"
 
+#include <Caches.h>
+
 namespace android {
 
 class SkPathGlue {
 public:
 
     static void finalizer(JNIEnv* env, jobject clazz, SkPath* obj) {
+#ifdef USE_OPENGL_RENDERER
+        if (android::uirenderer::Caches::hasInstance()) {
+            android::uirenderer::Caches::getInstance().pathCache.remove(obj);
+        }
+#endif
         delete obj;
     }
 
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index cb1c333..9202429 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -9,6 +9,7 @@
 #include "SkXfermode.h"
 
 #include <SkiaShader.h>
+#include <Caches.h>
 
 using namespace android::uirenderer;
 
@@ -52,6 +53,11 @@
 
 static void Shader_destructor(JNIEnv* env, jobject o, SkShader* shader, SkiaShader* skiaShader)
 {
+#ifdef USE_OPENGL_RENDERER
+    if (android::uirenderer::Caches::hasInstance()) {
+        android::uirenderer::Caches::getInstance().gradientCache.remove(shader);
+    }
+#endif
     delete skiaShader;
     shader->safeUnref();
 }
diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h
index 3b975b4..e1ee8eb 100644
--- a/include/utils/Singleton.h
+++ b/include/utils/Singleton.h
@@ -37,6 +37,11 @@
         }
         return *instance;
     }
+
+    static bool hasInstance() {
+        Mutex::Autolock _l(sLock);
+        return sInstance != 0;
+    }
     
 protected:
     ~Singleton() { };
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index fda57b8..9c67885 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -17,7 +17,9 @@
 #ifndef ANDROID_UI_CACHES_H
 #define ANDROID_UI_CACHES_H
 
-#define LOG_TAG "OpenGLRenderer"
+#ifndef LOG_TAG
+    #define LOG_TAG "OpenGLRenderer"
+#endif
 
 #include <utils/Singleton.h>
 
diff --git a/libs/hwui/GenerationCache.h b/libs/hwui/GenerationCache.h
index c42a5d8..070e33f 100644
--- a/libs/hwui/GenerationCache.h
+++ b/libs/hwui/GenerationCache.h
@@ -61,6 +61,7 @@
 
     bool contains(K key) const;
     V get(K key);
+    K getKeyAt(uint32_t index) const;
     void put(K key, V value);
     V remove(K key);
     V removeOldest();
@@ -122,6 +123,11 @@
 }
 
 template<typename K, typename V>
+K GenerationCache<K, V>::getKeyAt(uint32_t index) const {
+    return mCache.keyAt(index);
+}
+
+template<typename K, typename V>
 V GenerationCache<K, V>::get(K key) {
     ssize_t index = mCache.indexOfKey(key);
     if (index >= 0) {
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 58920bd..9957370 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -21,6 +21,8 @@
 #include <SkCanvas.h>
 #include <SkGradientShader.h>
 
+#include <utils/threads.h>
+
 #include "GradientCache.h"
 #include "Properties.h"
 
@@ -52,6 +54,7 @@
 }
 
 GradientCache::~GradientCache() {
+    Mutex::Autolock _l(mLock);
     mCache.clear();
 }
 
@@ -60,14 +63,17 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 uint32_t GradientCache::getSize() {
+    Mutex::Autolock _l(mLock);
     return mSize;
 }
 
 uint32_t GradientCache::getMaxSize() {
+    Mutex::Autolock _l(mLock);
     return mMaxSize;
 }
 
 void GradientCache::setMaxSize(uint32_t maxSize) {
+    Mutex::Autolock _l(mLock);
     mMaxSize = maxSize;
     while (mSize > mMaxSize) {
         mCache.removeOldest();
@@ -79,6 +85,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void GradientCache::operator()(SkShader*& shader, Texture*& texture) {
+    // Already locked here
     if (shader) {
         const uint32_t size = texture->width * texture->height * 4;
         mSize -= size;
@@ -95,14 +102,17 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 Texture* GradientCache::get(SkShader* shader) {
+    Mutex::Autolock _l(mLock);
     return mCache.get(shader);
 }
 
 void GradientCache::remove(SkShader* shader) {
+    Mutex::Autolock _l(mLock);
     mCache.remove(shader);
 }
 
 void GradientCache::clear() {
+    Mutex::Autolock _l(mLock);
     mCache.clear();
 }
 
@@ -128,17 +138,21 @@
 
     canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 1.0f, p);
 
+    mLock.lock();
     // Asume the cache is always big enough
     const uint32_t size = bitmap.rowBytes() * bitmap.height();
     while (mSize + size > mMaxSize) {
         mCache.removeOldest();
     }
+    mLock.unlock();
 
     Texture* texture = new Texture;
     generateTexture(&bitmap, texture);
 
+    mLock.lock();
     mSize += size;
     mCache.put(shader, texture);
+    mLock.unlock();
 
     return texture;
 }
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 8795920..51a8c01 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -82,6 +82,12 @@
 
     uint32_t mSize;
     uint32_t mMaxSize;
+
+    /**
+     * Used to access mCache and mSize. All methods are accessed from a single
+     * thread except for remove().
+     */
+    mutable Mutex mLock;
 }; // class GradientCache
 
 }; // namespace uirenderer
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 158c0cc..e8afe37 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -21,6 +21,8 @@
 #include <SkCanvas.h>
 #include <SkRect.h>
 
+#include <utils/threads.h>
+
 #include "PathCache.h"
 #include "Properties.h"
 
@@ -51,6 +53,7 @@
 }
 
 PathCache::~PathCache() {
+    Mutex::Autolock _l(mLock);
     mCache.clear();
 }
 
@@ -67,14 +70,17 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 uint32_t PathCache::getSize() {
+    Mutex::Autolock _l(mLock);
     return mSize;
 }
 
 uint32_t PathCache::getMaxSize() {
+    Mutex::Autolock _l(mLock);
     return mMaxSize;
 }
 
 void PathCache::setMaxSize(uint32_t maxSize) {
+    Mutex::Autolock _l(mLock);
     mMaxSize = maxSize;
     while (mSize > mMaxSize) {
         mCache.removeOldest();
@@ -99,14 +105,30 @@
 // Caching
 ///////////////////////////////////////////////////////////////////////////////
 
+void PathCache::remove(SkPath* path) {
+    Mutex::Autolock _l(mLock);
+    // TODO: Linear search...
+    for (uint32_t i = 0; i < mCache.size(); i++) {
+        if (mCache.getKeyAt(i).path == path) {
+            mCache.removeAt(i);
+            return;
+        }
+    }
+}
+
 PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
     PathCacheEntry entry(path, paint);
+
+    mLock.lock();
     PathTexture* texture = mCache.get(entry);
+    mLock.unlock();
 
     if (!texture) {
         texture = addTexture(entry, path, paint);
     } else if (path->getGenerationID() != texture->generation) {
+        mLock.lock();
         mCache.remove(entry);
+        mLock.unlock();
         texture = addTexture(entry, path, paint);
     }
 
@@ -132,9 +154,11 @@
     const uint32_t size = width * height;
     // Don't even try to cache a bitmap that's bigger than the cache
     if (size < mMaxSize) {
+        mLock.lock();
         while (mSize + size > mMaxSize) {
             mCache.removeOldest();
         }
+        mLock.unlock();
     }
 
     PathTexture* texture = new PathTexture;
@@ -157,8 +181,10 @@
     generateTexture(bitmap, texture);
 
     if (size < mMaxSize) {
+        mLock.lock();
         mSize += size;
         mCache.put(entry, texture);
+        mLock.unlock();
     } else {
         texture->cleanup = true;
     }
@@ -167,6 +193,7 @@
 }
 
 void PathCache::clear() {
+    Mutex::Autolock _l(mLock);
     mCache.clear();
 }
 
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 522e5e0..bde0e7d 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -114,6 +114,10 @@
      * Clears the cache. This causes all textures to be deleted.
      */
     void clear();
+    /**
+     * Removes an entry.
+     */
+    void remove(SkPath* path);
 
     /**
      * Sets the maximum size of the cache in bytes.
@@ -143,6 +147,12 @@
     uint32_t mSize;
     uint32_t mMaxSize;
     GLuint mMaxTextureSize;
+
+    /**
+     * Used to access mCache and mSize. All methods are accessed from a single
+     * thread except for remove().
+     */
+    mutable Mutex mLock;
 }; // class PathCache
 
 }; // namespace uirenderer
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index e558870..927070a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -102,9 +102,10 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 Texture* TextureCache::get(SkBitmap* bitmap) {
-    Mutex::Autolock _l(mLock);
-
+    mLock.lock();
     Texture* texture = mCache.get(bitmap);
+    mLock.unlock();
+
     if (!texture) {
         if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
             LOGW("Bitmap too large to be uploaded into a texture");
@@ -114,9 +115,11 @@
         const uint32_t size = bitmap->rowBytes() * bitmap->height();
         // Don't even try to cache a bitmap that's bigger than the cache
         if (size < mMaxSize) {
+            mLock.lock();
             while (mSize + size > mMaxSize) {
                 mCache.removeOldest();
             }
+            mLock.unlock();
         }
 
         texture = new Texture;
@@ -124,8 +127,10 @@
         generateTexture(bitmap, texture, false);
 
         if (size < mMaxSize) {
+            mLock.lock();
             mSize += size;
             mCache.put(bitmap, texture);
+            mLock.unlock();
         } else {
             texture->cleanup = true;
         }
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 847d69c..a63789a 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -87,6 +87,10 @@
     uint32_t mMaxSize;
     GLint mMaxTextureSize;
 
+    /**
+     * Used to access mCache and mSize. All methods are accessed from a single
+     * thread except for remove().
+     */
     mutable Mutex mLock;
 }; // class TextureCache