Use only one GL context per process, share chaches.

Change-Id: Ieabaa25338d2f4b8d4fd90e7401ad6e7452eae11
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 4c72e95..b058685 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -44,7 +44,7 @@
     @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
     private final GL mGl;
     private final boolean mOpaque;
-    private final int mRenderer;
+    private int mRenderer;
     
     private int mWidth;
     private int mHeight;
@@ -76,16 +76,25 @@
         mOpaque = !translucent;
 
         mRenderer = nCreateRenderer();
+        if (mRenderer == 0) {
+            throw new IllegalStateException("Could not create GLES20Canvas renderer");
+        }
     }
-
+    
     private native int nCreateRenderer();
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            super.finalize();
-        } finally {
+    /**
+     * This method <strong>must</strong> be called before releasing a
+     * reference to a GLES20Canvas. This method is responsible for freeing
+     * native resources associated with the hardware. Not invoking this
+     * method properly can result in memory leaks.
+     * 
+     * @hide
+     */
+    public synchronized void destroy() {
+        if (mRenderer != 0) {
             nDestroyRenderer(mRenderer);
+            mRenderer = 0;
         }
     }
 
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index ca60a89..32d8123 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -35,9 +35,10 @@
  * @hide
  */
 public abstract class HardwareRenderer {
+    private static final String LOG_TAG = "HardwareRenderer";
+
     private boolean mEnabled;
     private boolean mRequested = true;
-    private static final String LOG_TAG = "HardwareRenderer";
 
     /**
      * Indicates whether hardware acceleration is available under any form for
@@ -70,9 +71,8 @@
      * 
      * @param width Width of the drawing surface.
      * @param height Height of the drawing surface.
-     * @param attachInfo The AttachInfo used to render the ViewRoot. 
      */
-    abstract void setup(int width, int height, View.AttachInfo attachInfo);
+    abstract void setup(int width, int height);
 
     /**
      * Draws the specified view.
@@ -96,12 +96,11 @@
      */
     void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
             SurfaceHolder holder) {
-
         if (isRequested()) {
             // We lost the gl context, so recreate it.
             if (!isEnabled()) {
                 if (initialize(holder)) {
-                    setup(width, height, attachInfo);
+                    setup(width, height);
                 }
             }
         }        
@@ -165,18 +164,23 @@
     static abstract class GlRenderer extends HardwareRenderer {
         private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
 
-        EGL10 mEgl;
-        EGLDisplay mEglDisplay;
-        EGLContext mEglContext;
-        EGLSurface mEglSurface;
-        EGLConfig mEglConfig;
+        static EGLContext sEglContext;
+        static EGL10 sEgl;
+        static EGLDisplay sEglDisplay;
+        static EGLConfig sEglConfig;
 
+        private static Thread sEglThread;        
+
+        EGLSurface mEglSurface;
+        
         GL mGl;
-        Canvas mCanvas;
+        GLES20Canvas mCanvas;
 
         final int mGlVersion;
         final boolean mTranslucent;
 
+        private boolean mDestroyed;
+
         GlRenderer(int glVersion, boolean translucent) {
             mGlVersion = glVersion;
             mTranslucent = translucent;
@@ -189,7 +193,7 @@
          */
         void checkErrors() {
             if (isEnabled()) {
-                int error = mEgl.eglGetError();
+                int error = sEgl.eglGetError();
                 if (error != EGL10.EGL_SUCCESS) {
                     // something bad has happened revert to
                     // normal rendering.
@@ -208,13 +212,17 @@
             if (isRequested() && !isEnabled()) {
                 initializeEgl();
                 mGl = createEglSurface(holder);
+                mDestroyed = false;
 
                 if (mGl != null) {
-                    int err = mEgl.eglGetError();
+                    int err = sEgl.eglGetError();
                     if (err != EGL10.EGL_SUCCESS) {
                         destroy();
                         setRequested(false);
                     } else {
+                        if (mCanvas != null) {
+                            destroyCanvas();
+                        }
                         mCanvas = createCanvas();
                         if (mCanvas != null) {
                             setEnabled(true);
@@ -229,64 +237,75 @@
             return false;
         }
 
-        abstract Canvas createCanvas();
+        private void destroyCanvas() {
+            mCanvas.destroy();
+            mCanvas = null;
+        }
+
+        abstract GLES20Canvas createCanvas();
 
         void initializeEgl() {
-            mEgl = (EGL10) EGLContext.getEGL();
+            if (sEglContext != null) return;
+
+            sEglThread = Thread.currentThread();
+            sEgl = (EGL10) EGLContext.getEGL();
             
             // Get to the default display.
-            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+            sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
             
-            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+            if (sEglDisplay == EGL10.EGL_NO_DISPLAY) {
                 throw new RuntimeException("eglGetDisplay failed");
             }
             
             // We can now initialize EGL for that display
             int[] version = new int[2];
-            if (!mEgl.eglInitialize(mEglDisplay, version)) {
+            if (!sEgl.eglInitialize(sEglDisplay, version)) {
                 throw new RuntimeException("eglInitialize failed");
             }
-            mEglConfig = getConfigChooser(mGlVersion).chooseConfig(mEgl, mEglDisplay);
+            sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
             
             /*
             * Create an EGL context. We want to do this as rarely as we can, because an
             * EGL context is a somewhat heavy object.
             */
-            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
+            sEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
         }
 
         GL createEglSurface(SurfaceHolder holder) {
             // Check preconditions.
-            if (mEgl == null) {
+            if (sEgl == null) {
                 throw new RuntimeException("egl not initialized");
             }
-            if (mEglDisplay == null) {
+            if (sEglDisplay == null) {
                 throw new RuntimeException("eglDisplay not initialized");
             }
-            if (mEglConfig == null) {
+            if (sEglConfig == null) {
                 throw new RuntimeException("mEglConfig not initialized");
             }
+            if (Thread.currentThread() != sEglThread) {
+                throw new IllegalStateException("HardwareRenderer cannot be used " 
+                        + "from multiple threads");
+            }
 
             /*
              *  The window size has changed, so we need to create a new
              *  surface.
              */
             if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
-
                 /*
                  * Unbind and destroy the old EGL surface, if
                  * there is one.
                  */
-                mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+                sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
                         EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
-                mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+                sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
             }
 
             // Create an EGL surface we can render into.
-            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, null);
+            mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null);
 
             if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
-                int error = mEgl.eglGetError();
+                int error = sEgl.eglGetError();
                 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
                     Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
                     return null;
@@ -298,11 +317,11 @@
              * Before we can issue GL commands, we need to make sure
              * the context is current and bound to a surface.
              */
-            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+            if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
                 throw new RuntimeException("eglMakeCurrent failed");
             }
 
-            return mEglContext.getGL();
+            return sEglContext.getGL();
         }
 
         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
@@ -315,7 +334,6 @@
         @Override
         void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
                 SurfaceHolder holder) {
-
             if (isRequested()) {
                 checkErrors();
                 super.initializeIfNeeded(width, height, attachInfo, holder);
@@ -324,28 +342,34 @@
         
         @Override
         void destroy() {
-            if (!isEnabled()) return;
+            if (!isEnabled() || mDestroyed) return;
 
-            mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+            mDestroyed = true;
+
+            checkCurrent();
+            // Destroy the Canvas first in case it needs to use a GL context
+            // to perform its cleanup.
+            destroyCanvas();
+
+            sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
                     EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
-            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
-            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
-            mEgl.eglTerminate(mEglDisplay);
+            sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
 
-            mEglContext = null;
             mEglSurface = null;
-            mEglDisplay = null;
-            mEgl = null;
             mGl = null;
-            mCanvas = null;
+
+            // mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+            // mEglContext = null;            
+            // mEgl.eglTerminate(mEglDisplay);
+            // mEgl = null;
+            // mEglDisplay = null;
 
             setEnabled(false);
-        }        
-        
+        }
+
         @Override
-        void setup(int width, int height, View.AttachInfo attachInfo) {
-            final float scale = attachInfo.mApplicationScale;
-            mCanvas.setViewport((int) (width * scale + 0.5f), (int) (height * scale + 0.5f));
+        void setup(int width, int height) {
+            mCanvas.setViewport(width, height);
         }
         
         boolean canDraw() {
@@ -363,7 +387,8 @@
          * @param glVersion
          */
         EglConfigChooser getConfigChooser(int glVersion) {
-            return new ComponentSizeChooser(glVersion, 8, 8, 8, mTranslucent ? 8 : 0, 0, 0);
+            // TODO: Check for mTranslucent here, which means at least 2 EGL contexts
+            return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0);
         }
 
         @Override
@@ -373,14 +398,7 @@
                 attachInfo.mIgnoreDirtyState = true;
                 view.mPrivateFlags |= View.DRAWN;
 
-                // TODO: Don't check the current context when we have one per UI thread
-                // TODO: Use a threadlocal flag to know whether the surface has changed
-                if (mEgl.eglGetCurrentContext() != mEglContext ||
-                        mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) {
-                    if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
-                        throw new RuntimeException("eglMakeCurrent failed");
-                    }
-                }
+                checkCurrent();
 
                 onPreDraw();
 
@@ -396,11 +414,22 @@
 
                 attachInfo.mIgnoreDirtyState = false;
 
-                mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+                sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
                 checkErrors();
             }
         }
 
+        private void checkCurrent() {
+            // TODO: Don't check the current context when we have one per UI thread
+            // TODO: Use a threadlocal flag to know whether the surface has changed
+            if (sEgl.eglGetCurrentContext() != sEglContext ||
+                    sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) {
+                if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
+                    throw new RuntimeException("eglMakeCurrent failed");
+                }
+            }
+        }
+
         static abstract class EglConfigChooser {
             final int[] mConfigSpec;
             private final int mGlVersion;
@@ -496,7 +525,7 @@
                         int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
                         int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
                         int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
-                        if (r == mRedSize && g == mGreenSize && b == mBlueSize && a >= mAlphaSize) {
+                        if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize) {
                             return config;
                         }
                     }
@@ -514,7 +543,7 @@
             }
         }
     }
-    
+
     /**
      * Hardware renderer using OpenGL ES 2.0.
      */
@@ -526,8 +555,9 @@
         }
 
         @Override
-        Canvas createCanvas() {
-            return mGlCanvas = new GLES20Canvas(mGl, mTranslucent);
+        GLES20Canvas createCanvas() {
+            // TODO: Pass mTranslucent instead of true
+            return mGlCanvas = new GLES20Canvas(mGl, true);
         }
 
         @Override
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index d32ccb1..5999aba 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -251,7 +251,7 @@
         mTempRect = new Rect();
         mVisRect = new Rect();
         mWinFrame = new Rect();
-        mWindow = new W(this, context);
+        mWindow = new W(this);
         mInputMethodCallback = new InputMethodCallback(this);
         mViewVisibility = View.GONE;
         mTransparentRegion = new Region();
@@ -469,6 +469,7 @@
             if (attrs != null &&
                     (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) {
                 final boolean translucent = attrs.format != PixelFormat.OPAQUE;
+                destroyHardwareRenderer();
                 mHwRenderer = HardwareRenderer.createGlRenderer(2, translucent);
             }
         }
@@ -677,9 +678,7 @@
             attachInfo.mWindowVisibility = viewVisibility;
             host.dispatchWindowVisibilityChanged(viewVisibility);
             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
-                if (mHwRenderer != null) {
-                    mHwRenderer.destroy();
-                }
+                destroyHardwareRenderer();
             }
             if (viewVisibility == View.GONE) {
                 // After making a window gone, we will count it as being
@@ -963,7 +962,7 @@
             }
             
             if (hwIntialized) {
-                mHwRenderer.setup(mWidth, mHeight, mAttachInfo);
+                mHwRenderer.setup(mWidth, mHeight);
             }
 
             boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
@@ -1598,9 +1597,9 @@
         mAttachInfo.mRootView = null;
         mAttachInfo.mSurface = null;
 
-        if (mHwRenderer != null) {
-            mHwRenderer.destroy();
-        }
+        destroyHardwareRenderer();
+        mHwRenderer = null;
+        
         mSurface.release();
 
         if (mInputChannel != null) {
@@ -1625,6 +1624,12 @@
         }
     }
 
+    private void destroyHardwareRenderer() {
+        if (mHwRenderer != null) {
+            mHwRenderer.destroy();
+        }
+    }
+
     void updateConfiguration(Configuration config, boolean force) {
         if (DEBUG_CONFIGURATION) Log.v(TAG,
                 "Applying new config to window "
@@ -2734,10 +2739,6 @@
     public void childDrawableStateChanged(View child) {
     }
 
-    protected Rect getWindowFrame() {
-        return mWinFrame;
-    }
-
     void checkThread() {
         if (mThread != Thread.currentThread()) {
             throw new CalledFromWrongThreadException(
@@ -2816,16 +2817,15 @@
     static class W extends IWindow.Stub {
         private final WeakReference<ViewRoot> mViewRoot;
 
-        public W(ViewRoot viewRoot, Context context) {
+        W(ViewRoot viewRoot) {
             mViewRoot = new WeakReference<ViewRoot>(viewRoot);
         }
 
-        public void resized(int w, int h, Rect coveredInsets,
-                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+        public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets,
+                boolean reportDraw, Configuration newConfig) {
             final ViewRoot viewRoot = mViewRoot.get();
             if (viewRoot != null) {
-                viewRoot.dispatchResized(w, h, coveredInsets,
-                        visibleInsets, reportDraw, newConfig);
+                viewRoot.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, newConfig);
             }
         }
 
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 4c6eced..98c03a6 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -34,7 +34,6 @@
 #include <SkiaShader.h>
 #include <SkiaColorFilter.h>
 #include <Rect.h>
-#include <ui/Rect.h>
 
 #include "TextLayout.h"
 
@@ -418,7 +417,7 @@
     { "nDrawTextRun",       "(ILjava/lang/String;IIIIFFII)V",
             (void*) android_view_GLES20Canvas_drawTextRun },
 
-    {   "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
+    { "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
             (void*) android_view_GLES20Canvas_getClipBounds },
 #endif
 };
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
new file mode 100644
index 0000000..0789d89
--- /dev/null
+++ b/libs/hwui/Caches.h
@@ -0,0 +1,75 @@
+/*
+ * 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_CACHES_H
+#define ANDROID_UI_CACHES_H
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/Singleton.h>
+
+#include "TextureCache.h"
+#include "LayerCache.h"
+#include "GradientCache.h"
+#include "PatchCache.h"
+#include "FontRenderer.h"
+#include "ProgramCache.h"
+#include "PathCache.h"
+#include "TextDropShadowCache.h"
+
+namespace android {
+namespace uirenderer {
+
+struct CacheLogger {
+    CacheLogger() {
+        LOGD("Creating caches");
+    }
+}; // struct CacheLogger
+
+class Caches: public Singleton<Caches> {
+    Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO),
+            lastDstMode(GL_ZERO), currentProgram(NULL) {
+        dropShadowCache.setFontRenderer(fontRenderer);
+    }
+
+    friend class Singleton<Caches>;
+
+    CacheLogger logger;
+
+public:
+    bool blend;
+    GLenum lastSrcMode;
+    GLenum lastDstMode;
+    Program* currentProgram;
+
+    TextureCache textureCache;
+    LayerCache layerCache;
+    GradientCache gradientCache;
+    ProgramCache programCache;
+    PathCache pathCache;
+    PatchCache patchCache;
+    TextDropShadowCache dropShadowCache;
+    FontRenderer fontRenderer;
+}; // class Caches
+
+}; // namespace uirenderer
+
+using namespace uirenderer;
+ANDROID_SINGLETON_STATIC_INSTANCE(Caches);
+
+}; // namespace android
+
+#endif // ANDROID_UI_CACHES_H
diff --git a/libs/hwui/GenerationCache.h b/libs/hwui/GenerationCache.h
index 45b3ffa..c358c80 100644
--- a/libs/hwui/GenerationCache.h
+++ b/libs/hwui/GenerationCache.h
@@ -104,12 +104,14 @@
 template<typename K, typename V>
 void GenerationCache<K, V>::clear() {
     if (mListener) {
-        while (mCache.size() > 0) {
-            removeOldest();
+        for (uint32_t i = 0; i < mCache.size(); i++) {
+            sp<Entry<K, V> > entry = mCache.valueAt(i);
+            if (mListener) {
+                (*mListener)(entry->key, entry->value);
+            }
         }
-    } else {
-        mCache.clear();
     }
+    mCache.clear();
     mYoungest.clear();
     mOldest.clear();
 }
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 59fa0a7..58920bd 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -22,6 +22,7 @@
 #include <SkGradientShader.h>
 
 #include "GradientCache.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -30,6 +31,20 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+GradientCache::GradientCache():
+        mCache(GenerationCache<SkShader*, 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) {
+        LOGD("  Setting gradient cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
+    }
+
+    mCache.setOnEntryRemovedListener(this);
+}
+
 GradientCache::GradientCache(uint32_t maxByteSize):
         mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 12c8a23..8795920 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -32,6 +32,7 @@
  */
 class GradientCache: public OnEntryRemoved<SkShader*, Texture*> {
 public:
+    GradientCache();
     GradientCache(uint32_t maxByteSize);
     ~GradientCache();
 
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 3d263a3..a204778 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -21,6 +21,7 @@
 #include <utils/Log.h>
 
 #include "LayerCache.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -29,6 +30,18 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+LayerCache::LayerCache():
+        mCache(GenerationCache<LayerSize, Layer*>::kUnlimitedCapacity),
+        mIdGenerator(1), mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting layer cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE);
+    }
+}
+
 LayerCache::LayerCache(uint32_t maxByteSize):
         mCache(GenerationCache<LayerSize, Layer*>::kUnlimitedCapacity),
         mIdGenerator(1), mSize(0), mMaxSize(maxByteSize) {
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 2580551..9860994 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -43,6 +43,7 @@
 
 class LayerCache: public OnEntryRemoved<LayerSize, Layer*> {
 public:
+    LayerCache();
     LayerCache(uint32_t maxByteSize);
     ~LayerCache();
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 49d49da..033d8e2 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -23,11 +23,9 @@
 #include <SkCanvas.h>
 #include <SkTypeface.h>
 
-#include <cutils/properties.h>
 #include <utils/Log.h>
 
 #include "OpenGLRenderer.h"
-#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -36,18 +34,8 @@
 // Defines
 ///////////////////////////////////////////////////////////////////////////////
 
-#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
-#define DEFAULT_LAYER_CACHE_SIZE 6.0f
-#define DEFAULT_PATH_CACHE_SIZE 6.0f
-#define DEFAULT_PATCH_CACHE_SIZE 100
-#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
-#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
-
 #define REQUIRED_TEXTURE_UNITS_COUNT 3
 
-// Converts a number of mega-bytes into bytes
-#define MB(s) s * 1024 * 1024
-
 // Generates simple and textured vertices
 #define FV(x, y, u, v) { { x, y }, { u, v } }
 
@@ -102,54 +90,9 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-OpenGLRenderer::OpenGLRenderer():
-        mBlend(false), mLastSrcMode(GL_ZERO), mLastDstMode(GL_ZERO),
-        mTextureCache(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
-        mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)),
-        mGradientCache(MB(DEFAULT_GRADIENT_CACHE_SIZE)),
-        mPathCache(MB(DEFAULT_PATH_CACHE_SIZE)),
-        mPatchCache(DEFAULT_PATCH_CACHE_SIZE),
-        mDropShadowCache(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) {
+OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) {
     LOGD("Create OpenGLRenderer");
 
-    char property[PROPERTY_VALUE_MAX];
-    if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
-        LOGD("  Setting texture cache size to %sMB", property);
-        mTextureCache.setMaxSize(MB(atof(property)));
-    } else {
-        LOGD("  Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
-    }
-
-    if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) {
-        LOGD("  Setting layer cache size to %sMB", property);
-        mLayerCache.setMaxSize(MB(atof(property)));
-    } else {
-        LOGD("  Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE);
-    }
-
-    if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
-        LOGD("  Setting gradient cache size to %sMB", property);
-        mGradientCache.setMaxSize(MB(atof(property)));
-    } else {
-        LOGD("  Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
-    }
-
-    if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) {
-        LOGD("  Setting path cache size to %sMB", property);
-        mPathCache.setMaxSize(MB(atof(property)));
-    } else {
-        LOGD("  Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE);
-    }
-
-    if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) {
-        LOGD("  Setting drop shadow cache size to %sMB", property);
-        mDropShadowCache.setMaxSize(MB(atof(property)));
-    } else {
-        LOGD("  Using default drop shadow cache size of %.2fMB", DEFAULT_DROP_SHADOW_CACHE_SIZE);
-    }
-    mDropShadowCache.setFontRenderer(mFontRenderer);
-
-    mCurrentProgram = NULL;
     mShader = NULL;
     mColorFilter = NULL;
     mHasShadow = false;
@@ -167,14 +110,6 @@
 
 OpenGLRenderer::~OpenGLRenderer() {
     LOGD("Destroy OpenGLRenderer");
-
-    mTextureCache.clear();
-    mLayerCache.clear();
-    mGradientCache.clear();
-    mPathCache.clear();
-    mPatchCache.clear();
-    mProgramCache.clear();
-    mDropShadowCache.clear();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -195,6 +130,8 @@
     mSnapshot = new Snapshot(mFirstSnapshot);
     mSaveCount = 1;
 
+    glViewport(0, 0, mWidth, mHeight);
+
     glDisable(GL_SCISSOR_TEST);
 
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@@ -310,12 +247,12 @@
 bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
         float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
     LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top);
-    LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize());
+    LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
 
     GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
     LayerSize size(right - left, bottom - top);
 
-    Layer* layer = mLayerCache.get(size, previousFbo);
+    Layer* layer = mCaches.layerCache.get(size, previousFbo);
     if (!layer) {
         return false;
     }
@@ -380,7 +317,7 @@
     LayerSize size(rect.getWidth(), rect.getHeight());
     // Failing to add the layer to the cache should happen only if the
     // layer is too large
-    if (!mLayerCache.put(size, layer)) {
+    if (!mCaches.layerCache.put(size, layer)) {
         LAYER_LOGD("Deleting layer");
 
         glDeleteFramebuffers(1, &layer->fbo);
@@ -461,7 +398,7 @@
     }
 
     glActiveTexture(GL_TEXTURE0);
-    const Texture* texture = mTextureCache.get(bitmap);
+    const Texture* texture = mCaches.textureCache.get(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
@@ -478,7 +415,7 @@
     }
 
     glActiveTexture(GL_TEXTURE0);
-    const Texture* texture = mTextureCache.get(bitmap);
+    const Texture* texture = mCaches.textureCache.get(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
@@ -494,7 +431,7 @@
     }
 
     glActiveTexture(GL_TEXTURE0);
-    const Texture* texture = mTextureCache.get(bitmap);
+    const Texture* texture = mCaches.textureCache.get(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
@@ -520,7 +457,7 @@
     }
 
     glActiveTexture(GL_TEXTURE0);
-    const Texture* texture = mTextureCache.get(bitmap);
+    const Texture* texture = mCaches.textureCache.get(bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
@@ -528,7 +465,7 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
-    Patch* mesh = mPatchCache.get(patch);
+    Patch* mesh = mCaches.patchCache.get(patch);
     mesh->updateVertices(bitmap, left, top, right, bottom,
             &patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs);
 
@@ -607,10 +544,11 @@
     const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
     const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
 
-    mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
+    mCaches.fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
+            paint->getTextSize());
     if (mHasShadow) {
         glActiveTexture(gTextureUnits[0]);
-        const ShadowTexture* shadow = mDropShadowCache.get(paint, text, bytesCount,
+        const ShadowTexture* shadow = mCaches.dropShadowCache.get(paint, text, bytesCount,
                 count, mShadowRadius);
         const AutoTexture autoCleanup(shadow);
 
@@ -618,20 +556,20 @@
 
         // Draw the mesh
         glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-        glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
+        glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
     }
 
     GLuint textureUnit = 0;
     glActiveTexture(gTextureUnits[textureUnit]);
 
-    setupTextureAlpha8(mFontRenderer.getTexture(), 0, 0, textureUnit, x, y, r, g, b, a,
+    setupTextureAlpha8(mCaches.fontRenderer.getTexture(), 0, 0, textureUnit, x, y, r, g, b, a,
             mode, false, true);
 
     const Rect& clip = mSnapshot->getLocalClip();
-    mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
+    mCaches.fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-    glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
+    glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
 
     drawTextDecorations(text, bytesCount, length, x, y, paint);
 
@@ -646,7 +584,7 @@
     GLuint textureUnit = 0;
     glActiveTexture(gTextureUnits[textureUnit]);
 
-    const PathTexture* texture = mPathCache.get(path, paint);
+    const PathTexture* texture = mCaches.pathCache.get(path, paint);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
@@ -671,7 +609,7 @@
 
     // Draw the mesh
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-    glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
+    glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -685,7 +623,7 @@
 void OpenGLRenderer::setupShader(SkiaShader* shader) {
     mShader = shader;
     if (mShader) {
-        mShader->set(&mTextureCache, &mGradientCache);
+        mShader->set(&mCaches.textureCache, &mCaches.gradientCache);
     }
 }
 
@@ -761,18 +699,18 @@
      }
 
      // Build and use the appropriate shader
-     useProgram(mProgramCache.get(description));
+     useProgram(mCaches.programCache.get(description));
 
      // Setup the blending mode
      chooseBlending(true, mode);
      bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
-     glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
+     glUniform1i(mCaches.currentProgram->getUniform("sampler"), textureUnit);
 
-     int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
+     int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
      glEnableVertexAttribArray(texCoordsSlot);
 
      // Setup attributes
-     glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
+     glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
              gMeshStride, &mMeshVertices[0].position[0]);
      glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE,
              gMeshStride, &mMeshVertices[0].texture[0]);
@@ -784,17 +722,17 @@
      } else {
          mModelView.loadIdentity();
      }
-     mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
-     glUniform4f(mCurrentProgram->color, r, g, b, a);
+     mCaches.currentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+     glUniform4f(mCaches.currentProgram->color, r, g, b, a);
 
      textureUnit++;
      if (applyFilters) {
          // Setup attributes and uniforms required by the shaders
          if (mShader) {
-             mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
+             mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit);
          }
          if (mColorFilter) {
-             mColorFilter->setupProgram(mCurrentProgram);
+             mColorFilter->setupProgram(mCaches.currentProgram);
          }
      }
 }
@@ -879,29 +817,29 @@
     }
 
     // Build and use the appropriate shader
-    useProgram(mProgramCache.get(description));
+    useProgram(mCaches.programCache.get(description));
 
     // Setup attributes
-    glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
+    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
             gMeshStride, &mMeshVertices[0].position[0]);
 
     // Setup uniforms
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
     if (!ignoreTransform) {
-        mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
     } else {
         mat4 identity;
-        mCurrentProgram->set(mOrthoMatrix, mModelView, identity);
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, identity);
     }
-    glUniform4f(mCurrentProgram->color, r, g, b, a);
+    glUniform4f(mCaches.currentProgram->color, r, g, b, a);
 
     // Setup attributes and uniforms required by the shaders
     if (mShader) {
-        mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
+        mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit);
     }
     if (mColorFilter) {
-        mColorFilter->setupProgram(mCurrentProgram);
+        mColorFilter->setupProgram(mCaches.currentProgram);
     }
 
     // Draw the mesh
@@ -936,28 +874,28 @@
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
 
-    useProgram(mProgramCache.get(description));
-    mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+    useProgram(mCaches.programCache.get(description));
+    mCaches.currentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
 
     chooseBlending(blend || alpha < 1.0f, mode);
 
     // Texture
     bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0);
-    glUniform1i(mCurrentProgram->getUniform("sampler"), 0);
+    glUniform1i(mCaches.currentProgram->getUniform("sampler"), 0);
 
     // Always premultiplied
-    glUniform4f(mCurrentProgram->color, alpha, alpha, alpha, alpha);
+    glUniform4f(mCaches.currentProgram->color, alpha, alpha, alpha, alpha);
 
     // Mesh
-    int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
+    int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
     glEnableVertexAttribArray(texCoordsSlot);
-    glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
+    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
             gMeshStride, vertices);
     glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords);
 
     // Color filter
     if (mColorFilter) {
-        mColorFilter->setupProgram(mCurrentProgram);
+        mColorFilter->setupProgram(mCaches.currentProgram);
     }
 
     if (!indices) {
@@ -971,7 +909,7 @@
 void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied) {
     blend = blend || mode != SkXfermode::kSrcOver_Mode;
     if (blend) {
-        if (!mBlend) {
+        if (!mCaches.blend) {
             glEnable(GL_BLEND);
         }
 
@@ -981,22 +919,22 @@
             sourceMode = GL_SRC_ALPHA;
         }
 
-        if (sourceMode != mLastSrcMode || destMode != mLastDstMode) {
+        if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
             glBlendFunc(sourceMode, destMode);
-            mLastSrcMode = sourceMode;
-            mLastDstMode = destMode;
+            mCaches.lastSrcMode = sourceMode;
+            mCaches.lastDstMode = destMode;
         }
-    } else if (mBlend) {
+    } else if (mCaches.blend) {
         glDisable(GL_BLEND);
     }
-    mBlend = blend;
+    mCaches.blend = blend;
 }
 
 bool OpenGLRenderer::useProgram(Program* program) {
     if (!program->isInUse()) {
-        if (mCurrentProgram != NULL) mCurrentProgram->remove();
+        if (mCaches.currentProgram != NULL) mCaches.currentProgram->remove();
         program->use();
-        mCurrentProgram = program;
+        mCaches.currentProgram = program;
         return false;
     }
     return true;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 5c0089f..5748d57 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -35,17 +35,10 @@
 #include "Program.h"
 #include "Rect.h"
 #include "Snapshot.h"
-#include "TextureCache.h"
-#include "LayerCache.h"
-#include "GradientCache.h"
-#include "PatchCache.h"
 #include "Vertex.h"
-#include "FontRenderer.h"
-#include "ProgramCache.h"
 #include "SkiaShader.h"
 #include "SkiaColorFilter.h"
-#include "PathCache.h"
-#include "TextDropShadowCache.h"
+#include "Caches.h"
 
 namespace android {
 namespace uirenderer {
@@ -356,7 +349,6 @@
     sp<Snapshot> mSnapshot;
 
     // Shaders
-    Program* mCurrentProgram;
     SkiaShader* mShader;
 
     // Color filters
@@ -365,17 +357,9 @@
     // Used to draw textured quads
     TextureVertex mMeshVertices[4];
 
-    // Last known blend state
-    bool mBlend;
-    GLenum mLastSrcMode;
-    GLenum mLastDstMode;
-
     // GL extensions
     Extensions mExtensions;
 
-    // Font renderer
-    FontRenderer mFontRenderer;
-
     // Drop shadow
     bool mHasShadow;
     float mShadowRadius;
@@ -384,13 +368,7 @@
     int mShadowColor;
 
     // Various caches
-    TextureCache mTextureCache;
-    LayerCache mLayerCache;
-    GradientCache mGradientCache;
-    ProgramCache mProgramCache;
-    PathCache mPathCache;
-    PatchCache mPatchCache;
-    TextDropShadowCache mDropShadowCache;
+    Caches& mCaches;
 }; // class OpenGLRenderer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 694e3fd..a7c0cce 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -20,6 +20,7 @@
 #include <utils/ResourceTypes.h>
 
 #include "PatchCache.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -28,6 +29,9 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+PatchCache::PatchCache(): mCache(DEFAULT_PATCH_CACHE_SIZE) {
+}
+
 PatchCache::PatchCache(uint32_t maxEntries): mCache(maxEntries) {
 }
 
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index de95087..e874a16 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -43,6 +43,7 @@
 
 class PatchCache: public OnEntryRemoved<PatchDescription, Patch*> {
 public:
+    PatchCache();
     PatchCache(uint32_t maxCapacity);
     ~PatchCache();
 
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 10440ea..158c0cc 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -22,6 +22,7 @@
 #include <SkRect.h>
 
 #include "PathCache.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -30,9 +31,30 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+PathCache::PathCache():
+        mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting path cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE);
+    }
+    init();
+}
+
 PathCache::PathCache(uint32_t maxByteSize):
         mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
+    init();
+}
+
+PathCache::~PathCache() {
+    mCache.clear();
+}
+
+void PathCache::init() {
     mCache.setOnEntryRemovedListener(this);
 
     GLint maxTextureSize;
@@ -40,10 +62,6 @@
     mMaxTextureSize = maxTextureSize;
 }
 
-PathCache::~PathCache() {
-    mCache.clear();
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // Size management
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index d09789f..522e5e0 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -95,6 +95,7 @@
  */
 class PathCache: public OnEntryRemoved<PathCacheEntry, PathTexture*> {
 public:
+    PathCache();
     PathCache(uint32_t maxByteSize);
     ~PathCache();
 
@@ -135,6 +136,8 @@
 
     PathTexture* addTexture(const PathCacheEntry& entry, const SkPath *path, const SkPaint* paint);
 
+    void init();
+
     GenerationCache<PathCacheEntry, PathTexture*> mCache;
 
     uint32_t mSize;
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index fa4b8c4..f211ab6 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -183,7 +183,6 @@
     void printLongString(const String8& shader) const;
 
     KeyedVector<programid, Program*> mCache;
-
 }; // class ProgramCache
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 7514b6f..dfe022a 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_UI_PROPERTIES_H
 #define ANDROID_UI_PROPERTIES_H
 
+#include <cutils/properties.h>
+
 /**
  * This file contains the list of system properties used to configure
  * the OpenGLRenderer.
@@ -33,4 +35,14 @@
 #define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width"
 #define PROPERTY_TEXT_CACHE_HEIGHT "ro.hwui.text_cache_height"
 
+// Converts a number of mega-bytes into bytes
+#define MB(s) s * 1024 * 1024
+
+#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
+#define DEFAULT_LAYER_CACHE_SIZE 6.0f
+#define DEFAULT_PATH_CACHE_SIZE 6.0f
+#define DEFAULT_PATCH_CACHE_SIZE 100
+#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
+#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
+
 #endif // ANDROID_UI_PROPERTIES_H
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index aab5bd4..f95d2be 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -25,6 +25,20 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+TextDropShadowCache::TextDropShadowCache():
+        mCache(GenerationCache<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) {
+        LOGD("  Setting drop shadow cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default drop shadow cache size of %.2fMB", DEFAULT_DROP_SHADOW_CACHE_SIZE);
+    }
+
+    mCache.setOnEntryRemovedListener(this);
+}
+
 TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize):
         mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index c3be483..b65d62a 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -69,16 +69,16 @@
     char *text;
 
     bool operator<(const ShadowText& rhs) const {
-        if (len < rhs.len) return true;
-        else if (len == rhs.len) {
-            if (radius < rhs.radius) return true;
-            else if (radius == rhs.radius) {
-                if (textSize < rhs.textSize) return true;
-                else if (textSize == rhs.textSize) {
-                    if (typeface < rhs.typeface) return true;
-                    else if (typeface == rhs.typeface) {
-                        if (hash < rhs.hash) return true;
-                        if (hash == rhs.hash) {
+        if (hash < rhs.hash) return true;
+        else if (hash == rhs.hash) {
+            if (len < rhs.len) return true;
+            else if (len == rhs.len) {
+                if (radius < rhs.radius) return true;
+                else if (radius == rhs.radius) {
+                    if (textSize < rhs.textSize) return true;
+                    else if (textSize == rhs.textSize) {
+                        if (typeface < rhs.typeface) return true;
+                        else if (typeface == rhs.typeface) {
                             return strncmp(text, rhs.text, len) < 0;
                         }
                     }
@@ -102,6 +102,7 @@
 
 class TextDropShadowCache: public OnEntryRemoved<ShadowText, ShadowTexture*> {
 public:
+    TextDropShadowCache();
     TextDropShadowCache(uint32_t maxByteSize);
     ~TextDropShadowCache();
 
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 2e8a8be..753c544 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -19,6 +19,7 @@
 #include <GLES2/gl2.h>
 
 #include "TextureCache.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -27,19 +28,37 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
+TextureCache::TextureCache():
+        mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting texture cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
+    }
+
+    init();
+}
+
 TextureCache::TextureCache(uint32_t maxByteSize):
         mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
-    mCache.setOnEntryRemovedListener(this);
-
-    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
-    LOGD("Maximum texture dimension is %d pixels", mMaxTextureSize);
+    init();
 }
 
 TextureCache::~TextureCache() {
     mCache.clear();
 }
 
+void TextureCache::init() {
+    mCache.setOnEntryRemovedListener(this);
+
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+    LOGD("    Maximum texture dimension is %d pixels", mMaxTextureSize);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Size management
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 452716c..b5e4c7c 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -32,6 +32,7 @@
  */
 class TextureCache: public OnEntryRemoved<SkBitmap*, Texture*> {
 public:
+    TextureCache();
     TextureCache(uint32_t maxByteSize);
     ~TextureCache();
 
@@ -78,6 +79,8 @@
      */
     void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false);
 
+    void init();
+
     GenerationCache<SkBitmap*, Texture*> mCache;
 
     uint32_t mSize;
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java
index c638958..94b936b 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java
@@ -21,6 +21,7 @@
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.util.DisplayMetrics;
+import android.view.ContextMenu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
@@ -83,6 +84,17 @@
 
         ListView list = (ListView) findViewById(R.id.list);
         list.setAdapter(adapter);
+        
+        registerForContextMenu(list);
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+        super.onCreateContextMenu(menu, v, menuInfo);
+        menu.setHeaderTitle("Context menu");
+        menu.add("List item 1");
+        menu.add("List item 2");
+        menu.add("List item 3");
     }
 
     private static class SimpleListAdapter extends ArrayAdapter<String> {