Merge "Add display lists caching."
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
new file mode 100644
index 0000000..b1160f0
--- /dev/null
+++ b/core/java/android/view/DisplayList.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+/**
+ * A display lists records a series of graphics related operation and can replay
+ * them later. Display lists are usually built by recording operations on a
+ * {@link android.graphics.Canvas}. Replaying the operations from a display list
+ * avoids executing views drawing code on every frame, and is thus much more
+ * efficient. 
+ */
+abstract class DisplayList {
+    /**
+     * Starts recording the display list. All operations performed on the
+     * returned canvas are recorded and stored in this display list.
+     * 
+     * @return A canvas to record drawing operations.
+     */
+    abstract HardwareCanvas start();
+
+    /**
+     * Ends the recording for this display list. A display list cannot be
+     * replayed if recording is not finished. 
+     */
+    abstract void end();
+
+    /**
+     * Frees resources taken by this display list. This method must be called
+     * before releasing all references.
+     */
+    abstract void destroy();
+
+    /**
+     * Indicates whether this display list can be replayed or not.
+     * 
+     * @return True if the display list can be replayed, false otherwise.
+     * 
+     * @see android.view.HardwareCanvas#drawDisplayList(DisplayList) 
+     */
+    abstract boolean isReady();
+}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index f917001..f0b00dd 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -35,14 +35,10 @@
 import android.text.SpannedString;
 import android.text.TextUtils;
 
-import javax.microedition.khronos.opengles.GL;
-
 /**
  * An implementation of Canvas on top of OpenGL ES 2.0.
  */
-class GLES20Canvas extends Canvas {
-    @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
-    private final GL mGl;
+class GLES20Canvas extends HardwareCanvas {
     private final boolean mOpaque;
     private int mRenderer;
     
@@ -72,27 +68,29 @@
     ///////////////////////////////////////////////////////////////////////////
     // Constructors
     ///////////////////////////////////////////////////////////////////////////
+
+    GLES20Canvas(boolean translucent) {
+        this(false, translucent);
+    }
     
-    GLES20Canvas(GL gl, boolean translucent) {
-        mGl = gl;
+    GLES20Canvas(boolean record, boolean translucent) {
         mOpaque = !translucent;
 
-        mRenderer = nCreateRenderer();
+        if (record) {
+            mRenderer = nCreateDisplayListRenderer();
+        } else {
+            mRenderer = nCreateRenderer();
+        }
+       
         if (mRenderer == 0) {
             throw new IllegalStateException("Could not create GLES20Canvas renderer");
         }
     }
-    
-    private native int nCreateRenderer();
 
-    /**
-     * 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
-     */
+    private native int nCreateRenderer();    
+    private native int nCreateDisplayListRenderer();    
+
+    @Override
     public synchronized void destroy() {
         if (mRenderer != 0) {
             nDestroyRenderer(mRenderer);
@@ -105,16 +103,6 @@
     ///////////////////////////////////////////////////////////////////////////
     // Canvas management
     ///////////////////////////////////////////////////////////////////////////
-    
-    @Override
-    public boolean isHardwareAccelerated() {
-        return true;
-    }
-
-    @Override
-    public void setBitmap(Bitmap bitmap) {
-        throw new UnsupportedOperationException();
-    }
 
     @Override
     public boolean isOpaque() {
@@ -145,12 +133,14 @@
     
     private native void nSetViewport(int renderer, int width, int height);
 
+    @Override
     void onPreDraw() {
         nPrepare(mRenderer);
     }
 
     private native void nPrepare(int renderer);
 
+    @Override
     void onPostDraw() {
         nFinish(mRenderer);
     }
@@ -177,6 +167,29 @@
     }
 
     private native void nReleaseContext(int renderer);
+    
+    ///////////////////////////////////////////////////////////////////////////
+    // Display list
+    ///////////////////////////////////////////////////////////////////////////
+
+    int getDisplayList() {
+        return nCreateDisplayList(mRenderer);
+    }
+
+    private native int nCreateDisplayList(int renderer);
+    
+    void destroyDisplayList(int displayList) {
+        nDestroyDisplayList(displayList);
+    }
+
+    private native void nDestroyDisplayList(int displayList);
+
+    @Override
+    public void drawDisplayList(DisplayList displayList) {
+        nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).mNativeDisplayList);
+    }
+
+    private native void nDrawDisplayList(int renderer, int displayList);
 
     ///////////////////////////////////////////////////////////////////////////
     // Clipping
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
new file mode 100644
index 0000000..2886bf3
--- /dev/null
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+/**
+ * An implementation of display list for OpenGL ES 2.0.
+ */
+class GLES20DisplayList extends DisplayList {
+    private GLES20Canvas mCanvas;
+
+    private boolean mStarted = false;
+    private boolean mRecorded = false;
+
+    int mNativeDisplayList;
+
+    @Override
+    HardwareCanvas start() {
+        if (mStarted) {
+            throw new IllegalStateException("Recording has already started");
+        }
+
+        destroyCanvas();
+
+        mCanvas = new GLES20Canvas(true, true);
+        mStarted = true;
+        mRecorded = false;
+
+        return mCanvas;
+    }
+
+    private void destroyCanvas() {
+        if (mCanvas != null) {
+            mCanvas.destroyDisplayList(mNativeDisplayList);
+            mCanvas.destroy();
+
+            mCanvas = null;
+            mNativeDisplayList = 0;
+        }
+    }
+
+    @Override
+    void end() {
+        if (mCanvas != null) {
+            mStarted = false;
+            mRecorded = true;
+
+            mNativeDisplayList = mCanvas.getDisplayList();
+        }
+    }
+
+    @Override
+    void destroy() {
+        destroyCanvas();
+    }
+
+    @Override
+    boolean isReady() {
+        return !mStarted && mRecorded;
+    }
+}
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
new file mode 100644
index 0000000..22d2fe6
--- /dev/null
+++ b/core/java/android/view/HardwareCanvas.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+/**
+ * Hardware accelerated canvas. 
+ */
+abstract class HardwareCanvas extends Canvas {
+    @Override
+    public boolean isHardwareAccelerated() {
+        return true;
+    }
+
+    @Override
+    public void setBitmap(Bitmap bitmap) {
+        throw new UnsupportedOperationException();
+    }
+    
+    /**
+     * This method <strong>must</strong> be called before releasing a
+     * reference to a hardware canvas. This method is responsible for
+     * freeing native resources associated with the hardware. Not
+     * invoking this method properly can result in memory leaks.
+     */    
+    public abstract void destroy();
+
+    /**
+     * Invoked before any drawing operation is performed in this canvas.
+     */
+    abstract void onPreDraw();
+
+    /**
+     * Invoked after all drawing operation have been performed.
+     */
+    abstract void onPostDraw();
+    
+    /**
+     * Draws the specified display list onto this canvas.
+     * 
+     * @param displayList The display list to replay.
+     */
+    public abstract void drawDisplayList(DisplayList displayList);
+}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index fbb13af..2cc4052 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -103,6 +103,14 @@
     abstract void draw(View view, View.AttachInfo attachInfo, int yOffset);
 
     /**
+     * Creates a new canvas that can be used to record drawing operations
+     * in the specified display list.
+     * 
+     * @return A new recording canvas.
+     */
+    abstract DisplayList createDisplayList();
+
+    /**
      * Initializes the hardware renderer for the specified surface and setup the
      * renderer for drawing, if needed. This is invoked when the ViewRoot has
      * potentially lost the hardware renderer. The hardware renderer should be
@@ -577,7 +585,7 @@
 
         @Override
         GLES20Canvas createCanvas() {
-            return mGlCanvas = new GLES20Canvas(mGl, true);
+            return mGlCanvas = new GLES20Canvas(true);
         }
 
         @Override
@@ -590,6 +598,11 @@
             mGlCanvas.onPostDraw();
         }
 
+        @Override
+        DisplayList createDisplayList() {
+            return new GLES20DisplayList();
+        }
+
         static HardwareRenderer create(boolean translucent) {
             if (GLES20Canvas.isAvailable()) {
                 return new Gl20Renderer(translucent);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 96066b7..3b10437 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1250,12 +1250,12 @@
      */
     private static final int[][] VIEW_STATE_SETS;
 
-    static final int VIEW_STATE_WINDOW_FOCUSED = 1<<0;
-    static final int VIEW_STATE_SELECTED = 1<<1;
-    static final int VIEW_STATE_FOCUSED = 1<<2;
-    static final int VIEW_STATE_ENABLED = 1<<3;
-    static final int VIEW_STATE_PRESSED = 1<<4;
-    static final int VIEW_STATE_ACTIVATED = 1<<5;
+    static final int VIEW_STATE_WINDOW_FOCUSED = 1;
+    static final int VIEW_STATE_SELECTED = 1 << 1;
+    static final int VIEW_STATE_FOCUSED = 1 << 2;
+    static final int VIEW_STATE_ENABLED = 1 << 3;
+    static final int VIEW_STATE_PRESSED = 1 << 4;
+    static final int VIEW_STATE_ACTIVATED = 1 << 5;
 
     static final int[] VIEW_STATE_IDS = new int[] {
         R.attr.state_window_focused,    VIEW_STATE_WINDOW_FOCUSED,
@@ -1268,28 +1268,23 @@
 
     static {
         int[] orderedIds = new int[VIEW_STATE_IDS.length];
-        for (int i=0; i<R.styleable.ViewDrawableStates.length; i++) {
+        for (int i = 0; i < R.styleable.ViewDrawableStates.length; i++) {
             int viewState = R.styleable.ViewDrawableStates[i];
-            for (int j=0; j<VIEW_STATE_IDS.length; j+=2) {
+            for (int j = 0; j<VIEW_STATE_IDS.length; j += 2) {
                 if (VIEW_STATE_IDS[j] == viewState) {
-                    orderedIds[i*2] = viewState;
-                    orderedIds[i*2+1] = VIEW_STATE_IDS[j+1];
+                    orderedIds[i * 2] = viewState;
+                    orderedIds[i * 2 + 1] = VIEW_STATE_IDS[j + 1];
                 }
             }
         }
-        final int NUM_BITS = VIEW_STATE_IDS.length/2;
-        VIEW_STATE_SETS = new int[1<<NUM_BITS][];
-        for (int i=0; i<VIEW_STATE_SETS.length; i++) {
+        final int NUM_BITS = VIEW_STATE_IDS.length / 2;
+        VIEW_STATE_SETS = new int[1 << NUM_BITS][];
+        for (int i = 0; i < VIEW_STATE_SETS.length; i++) {
             int numBits = Integer.bitCount(i);
             int[] set = new int[numBits];
             int pos = 0;
-            for (int j=0; j<orderedIds.length; j+=2) {
-                if ((i&orderedIds[j+1]) != 0) {
-                    if (false) {
-                        Log.i("View", "Index #" + i + " @ ordered #" + j
-                                + " resid=0x" + Integer.toHexString(orderedIds[j])
-                                + " mask " + orderedIds[j+1]);
-                    }
+            for (int j = 0; j < orderedIds.length; j += 2) {
+                if ((i & orderedIds[j+1]) != 0) {
                     set[pos++] = orderedIds[j];
                 }
             }
@@ -1958,6 +1953,7 @@
 
     private Bitmap mDrawingCache;
     private Bitmap mUnscaledDrawingCache;
+    private DisplayList mDisplayList;
 
     /**
      * When this view has focus and the next focus is {@link #FOCUS_LEFT},
@@ -7317,6 +7313,64 @@
     }
 
     /**
+     * <p>Returns a display list that can be used to draw this view again
+     * without executing its draw method.</p>
+     * 
+     * @return A DisplayList ready to replay, or null if caching is not enabled.
+     */
+    DisplayList getDisplayList() {
+        if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) {
+            return null;
+        }
+        
+        if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
+            return null;
+        }
+
+        if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED &&
+                ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDisplayList == null)) {
+
+            if (mDisplayList != null) {
+                mDisplayList.destroy();
+            }
+
+            mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList();
+
+            final HardwareCanvas canvas = mDisplayList.start();
+            try {
+                int width = mRight - mLeft;
+                int height = mBottom - mTop;
+
+                canvas.setViewport(width, height);
+                canvas.onPreDraw();
+
+                final int restoreCount = canvas.save();
+
+                mPrivateFlags |= DRAWN;
+                mPrivateFlags |= DRAWING_CACHE_VALID;
+    
+                // Fast path for layouts with no backgrounds
+                if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
+                    mPrivateFlags &= ~DIRTY_MASK;
+                    dispatchDraw(canvas);
+                } else {
+                    draw(canvas);
+                }
+    
+                canvas.restoreToCount(restoreCount);
+            } finally {
+                canvas.onPostDraw();
+
+                mDisplayList.end();
+
+                canvas.destroy();                
+            }
+        }
+
+        return mDisplayList;
+    }
+
+    /**
      * <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p>
      * 
      * @return A non-scaled bitmap representing this view or null if cache is disabled.
@@ -7383,6 +7437,10 @@
             mUnscaledDrawingCache.recycle();
             mUnscaledDrawingCache = null;
         }
+        if (mDisplayList != null) {
+            mDisplayList.destroy();
+            mDisplayList = null;
+        }
     }
 
     /**
@@ -10167,7 +10225,8 @@
         IBinder mPanelParentWindowToken;
         Surface mSurface;
 
-        boolean mHardwareAccelerated;        
+        boolean mHardwareAccelerated;
+        HardwareRenderer mHardwareRenderer;
         
         /**
          * Scale factor used by the compatibility mode
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 12c49c4..570e288 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1830,8 +1830,7 @@
 
         boolean scalingRequired = false;
         boolean caching = false;
-        if (!canvas.isHardwareAccelerated() &&
-                (flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
+        if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
                 (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
             caching = true;
             if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
@@ -1914,12 +1913,18 @@
         final int sx = child.mScrollX;
         final int sy = child.mScrollY;
 
+        DisplayList displayList = null;
         Bitmap cache = null;
         if (caching) {
-            cache = child.getDrawingCache(true);
+            if (!canvas.isHardwareAccelerated()) {
+                cache = child.getDrawingCache(true);
+            } else {
+                displayList = child.getDisplayList();
+            }
         }
 
-        final boolean hasNoCache = cache == null;
+        final boolean hasDisplayList = displayList != null && displayList.isReady();
+        final boolean hasNoCache = cache == null || hasDisplayList;
 
         final int restoreTo = canvas.save();
         if (hasNoCache) {
@@ -2002,17 +2007,21 @@
         }
 
         if (hasNoCache) {
-            // Fast path for layouts with no backgrounds
-            if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
-                if (ViewDebug.TRACE_HIERARCHY) {
-                    ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
+            if (!hasDisplayList) {
+                // Fast path for layouts with no backgrounds
+                if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
+                    if (ViewDebug.TRACE_HIERARCHY) {
+                        ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
+                    }
+                    child.mPrivateFlags &= ~DIRTY_MASK;
+                    child.dispatchDraw(canvas);
+                } else {
+                    child.draw(canvas);
                 }
-                child.mPrivateFlags &= ~DIRTY_MASK;
-                child.dispatchDraw(canvas);
             } else {
-                child.draw(canvas);
+                ((HardwareCanvas) canvas).drawDisplayList(displayList);
             }
-        } else {
+        } else if (cache != null) {
             final Paint cachePaint = mCachePaint;
             if (alpha < 1.0f) {
                 cachePaint.setAlpha((int) (alpha * 255));
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index f6a06ce..77ba6fe 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -196,8 +196,6 @@
     int mCurScrollY;
     Scroller mScroller;
 
-    HardwareRenderer mHwRenderer;
-
     final ViewConfiguration mViewConfiguration;
 
     /**
@@ -451,10 +449,10 @@
             if (attrs != null &&
                     (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) {
                 final boolean translucent = attrs.format != PixelFormat.OPAQUE;
-                if (mHwRenderer != null) {
-                    mHwRenderer.destroy(true);
+                if (mAttachInfo.mHardwareRenderer != null) {
+                    mAttachInfo.mHardwareRenderer.destroy(true);
                 }                
-                mHwRenderer = HardwareRenderer.createGlRenderer(2, translucent);
+                mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent);
                 mAttachInfo.mHardwareAccelerated = true;
             }
         }
@@ -663,8 +661,8 @@
             attachInfo.mWindowVisibility = viewVisibility;
             host.dispatchWindowVisibilityChanged(viewVisibility);
             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
-                if (mHwRenderer != null) {
-                    mHwRenderer.destroy(false);
+                if (mAttachInfo.mHardwareRenderer != null) {
+                    mAttachInfo.mHardwareRenderer.destroy(false);
                 }                
             }
             if (viewVisibility == View.GONE) {
@@ -869,8 +867,8 @@
                         fullRedrawNeeded = true;
                         mPreviousTransparentRegion.setEmpty();
 
-                        if (mHwRenderer != null) {
-                            hwIntialized = mHwRenderer.initialize(mHolder);
+                        if (mAttachInfo.mHardwareRenderer != null) {
+                            hwIntialized = mAttachInfo.mHardwareRenderer.initialize(mHolder);
                         }
                     }
                 } else if (!mSurface.isValid()) {
@@ -948,8 +946,8 @@
                 }
             }
 
-            if (hwIntialized || (windowShouldResize && mHwRenderer != null)) {
-                mHwRenderer.setup(mWidth, mHeight);
+            if (hwIntialized || (windowShouldResize && mAttachInfo.mHardwareRenderer != null)) {
+                mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight);
             }
 
             boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
@@ -1262,9 +1260,9 @@
             dirty.union(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
         }
         
-        if (mHwRenderer != null && mHwRenderer.isEnabled()) {
+        if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
             if (!dirty.isEmpty()) {
-                mHwRenderer.draw(mView, mAttachInfo, yoff);
+                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, yoff);
             }
 
             if (scrolling) {
@@ -1774,8 +1772,9 @@
                     boolean inTouchMode = msg.arg2 != 0;
                     ensureTouchModeLocally(inTouchMode);
 
-                    if (mHwRenderer != null) {
-                        mHwRenderer.initializeIfNeeded(mWidth, mHeight, mAttachInfo, mHolder);
+                    if (mAttachInfo.mHardwareRenderer != null) {
+                        mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight,
+                                mAttachInfo, mHolder);
                     }
                 }
 
@@ -2582,9 +2581,9 @@
     }
 
     private void destroyHardwareRenderer() {
-        if (mHwRenderer != null) {
-            mHwRenderer.destroy(true);
-            mHwRenderer = null;
+        if (mAttachInfo.mHardwareRenderer != null) {
+            mAttachInfo.mHardwareRenderer.destroy(true);
+            mAttachInfo.mHardwareRenderer = null;
             mAttachInfo.mHardwareAccelerated = false;
         }
     }
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index bbf3509..cb1556b1 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -30,6 +30,7 @@
 #include <SkTemplates.h>
 #include <SkXfermode.h>
 
+#include <DisplayListRenderer.h>
 #include <OpenGLDebugRenderer.h>
 #include <OpenGLRenderer.h>
 #include <SkiaShader.h>
@@ -378,6 +379,30 @@
     env->ReleaseStringChars(text, textArray);
 }
 
+// ----------------------------------------------------------------------------
+// Display lists
+// ----------------------------------------------------------------------------
+
+static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer(
+        JNIEnv* env, jobject canvas) {
+    return new DisplayListRenderer;
+}
+
+static DisplayList* android_view_GLES20Canvas_createDisplayList(JNIEnv* env,
+        jobject canvas, DisplayListRenderer* renderer) {
+    return renderer->getDisplayList();
+}
+
+static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env,
+        jobject canvas, DisplayList* displayList) {
+    delete displayList;
+}
+
+static void android_view_GLES20Canvas_drawDisplayList(JNIEnv* env,
+        jobject canvas, OpenGLRenderer* renderer, DisplayList* displayList) {
+    displayList->replay(*renderer);
+}
+
 #endif // USE_OPENGL_RENDERER
 
 // ----------------------------------------------------------------------------
@@ -455,6 +480,12 @@
 
     { "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
             (void*) android_view_GLES20Canvas_getClipBounds },
+
+    { "nCreateDisplayListRenderer", "()I",     (void*) android_view_GLES20Canvas_createDisplayListRenderer },
+    { "nCreateDisplayList",  "(I)I",           (void*) android_view_GLES20Canvas_createDisplayList },
+    { "nDestroyDisplayList", "(I)V",           (void*) android_view_GLES20Canvas_destroyDisplayList },
+    { "nDrawDisplayList",    "(II)V",          (void*) android_view_GLES20Canvas_drawDisplayList },
+
 #endif
 };
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 16b6b56..ee90702 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -22,6 +22,229 @@
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
+// Display list
+///////////////////////////////////////////////////////////////////////////////
+
+DisplayList::DisplayList(const DisplayListRenderer& recorder) {
+    const SkWriter32& writer = recorder.writeStream();
+    init();
+
+    if (writer.size() == 0) {
+        return;
+    }
+
+    size_t size = writer.size();
+    void* buffer = sk_malloc_throw(size);
+    writer.flatten(buffer);
+    mReader.setMemory(buffer, size);
+
+    mRCPlayback.reset(&recorder.mRCRecorder);
+    mRCPlayback.setupBuffer(mReader);
+
+    mTFPlayback.reset(&recorder.mTFRecorder);
+    mTFPlayback.setupBuffer(mReader);
+
+    const SkTDArray<const SkFlatBitmap*>& bitmaps = recorder.getBitmaps();
+    mBitmapCount = bitmaps.count();
+    if (mBitmapCount > 0) {
+        mBitmaps = new SkBitmap[mBitmapCount];
+        for (const SkFlatBitmap** flatBitmapPtr = bitmaps.begin();
+                flatBitmapPtr != bitmaps.end(); flatBitmapPtr++) {
+            const SkFlatBitmap* flatBitmap = *flatBitmapPtr;
+            int index = flatBitmap->index() - 1;
+            flatBitmap->unflatten(&mBitmaps[index], &mRCPlayback);
+        }
+    }
+
+    const SkTDArray<const SkFlatMatrix*>& matrices = recorder.getMatrices();
+    mMatrixCount = matrices.count();
+    if (mMatrixCount > 0) {
+        mMatrices = new SkMatrix[mMatrixCount];
+        for (const SkFlatMatrix** matrixPtr = matrices.begin();
+                matrixPtr != matrices.end(); matrixPtr++) {
+            const SkFlatMatrix* flatMatrix = *matrixPtr;
+            flatMatrix->unflatten(&mMatrices[flatMatrix->index() - 1]);
+        }
+    }
+
+    const SkTDArray<const SkFlatPaint*>& paints = recorder.getPaints();
+    mPaintCount = paints.count();
+    if (mPaintCount > 0) {
+        mPaints = new SkPaint[mPaintCount];
+        for (const SkFlatPaint** flatPaintPtr = paints.begin();
+                flatPaintPtr != paints.end(); flatPaintPtr++) {
+            const SkFlatPaint* flatPaint = *flatPaintPtr;
+            int index = flatPaint->index() - 1;
+            flatPaint->unflatten(&mPaints[index], &mRCPlayback, &mTFPlayback);
+        }
+    }
+
+    mPathHeap = recorder.mPathHeap;
+    mPathHeap->safeRef();
+}
+
+DisplayList::~DisplayList() {
+    sk_free((void*) mReader.base());
+
+    Caches& caches = Caches::getInstance();
+    for (int i = 0; i < mBitmapCount; i++) {
+        caches.textureCache.remove(&mBitmaps[i]);
+    }
+
+    delete[] mBitmaps;
+    delete[] mMatrices;
+    delete[] mPaints;
+
+    mPathHeap->safeUnref();
+}
+
+void DisplayList::init() {
+    mBitmaps = NULL;
+    mMatrices = NULL;
+    mPaints = NULL;
+    mPathHeap = NULL;
+    mBitmapCount = mMatrixCount = mPaintCount = 0;
+}
+
+void DisplayList::replay(OpenGLRenderer& renderer) {
+    TextContainer text;
+    mReader.rewind();
+
+    int saveCount = renderer.getSaveCount() - 1;
+
+    while (!mReader.eof()) {
+        switch (mReader.readInt()) {
+            case AcquireContext: {
+                renderer.acquireContext();
+            }
+            break;
+            case ReleaseContext: {
+                renderer.releaseContext();
+            }
+            break;
+            case Save: {
+                renderer.save(getInt());
+            }
+            break;
+            case Restore: {
+                renderer.restore();
+            }
+            break;
+            case RestoreToCount: {
+                renderer.restoreToCount(saveCount + getInt());
+            }
+            break;
+            case SaveLayer: {
+                renderer.saveLayer(getFloat(), getFloat(), getFloat(), getFloat(),
+                        getPaint(), getInt());
+            }
+            break;
+            case Translate: {
+                renderer.translate(getFloat(), getFloat());
+            }
+            break;
+            case Rotate: {
+                renderer.rotate(getFloat());
+            }
+            break;
+            case Scale: {
+                renderer.scale(getFloat(), getFloat());
+            }
+            break;
+            case SetMatrix: {
+                renderer.setMatrix(getMatrix());
+            }
+            break;
+            case ConcatMatrix: {
+                renderer.concatMatrix(getMatrix());
+            }
+            break;
+            case ClipRect: {
+                renderer.clipRect(getFloat(), getFloat(), getFloat(), getFloat(),
+                        (SkRegion::Op) getInt());
+            }
+            break;
+            case DrawBitmap: {
+                renderer.drawBitmap(getBitmap(), getFloat(), getFloat(), getPaint());
+            }
+            break;
+            case DrawBitmapMatrix: {
+                renderer.drawBitmap(getBitmap(), getMatrix(), getPaint());
+            }
+            break;
+            case DrawBitmapRect: {
+                renderer.drawBitmap(getBitmap(), getFloat(), getFloat(), getFloat(), getFloat(),
+                        getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
+            }
+            break;
+            case DrawPatch: {
+                int32_t* xDivs = NULL;
+                int32_t* yDivs = NULL;
+                uint32_t xDivsCount = 0;
+                uint32_t yDivsCount = 0;
+
+                SkBitmap* bitmap = getBitmap();
+
+                xDivs = getInts(xDivsCount);
+                yDivs = getInts(yDivsCount);
+
+                renderer.drawPatch(bitmap, xDivs, yDivs, xDivsCount, yDivsCount,
+                        getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
+            }
+            break;
+            case DrawColor: {
+                renderer.drawColor(getInt(), (SkXfermode::Mode) getInt());
+            }
+            break;
+            case DrawRect: {
+                renderer.drawRect(getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
+            }
+            break;
+            case DrawPath: {
+                renderer.drawPath(getPath(), getPaint());
+            }
+            break;
+            case DrawLines: {
+                int count = 0;
+                float* points = getFloats(count);
+                renderer.drawLines(points, count, getPaint());
+            }
+            break;
+            case DrawText: {
+                getText(&text);
+                renderer.drawText(text.text(), text.length(), getInt(),
+                        getFloat(), getFloat(), getPaint());
+            }
+            break;
+            case ResetShader: {
+                renderer.resetShader();
+            }
+            break;
+            case SetupShader: {
+                // TODO: Implement
+            }
+            break;
+            case ResetColorFilter: {
+                renderer.resetColorFilter();
+            }
+            break;
+            case SetupColorFilter: {
+                // TODO: Implement
+            }
+            break;
+            case ResetShadow: {
+                renderer.resetShadow();
+            }
+            break;
+            case SetupShadow: {
+                renderer.setupShadow(getFloat(), getFloat(), getFloat(), getInt());
+            }
+            break;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Base structure
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -56,75 +279,89 @@
 // Operations
 ///////////////////////////////////////////////////////////////////////////////
 
+void DisplayListRenderer::setViewport(int width, int height) {
+    mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
+
+    mWidth = width;
+    mHeight = height;
+}
+
+void DisplayListRenderer::prepare() {
+    mSnapshot = new Snapshot(mFirstSnapshot,
+            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    mSaveCount = 1;
+    mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight);
+}
+
 void DisplayListRenderer::acquireContext() {
-    addOp(AcquireContext);
+    addOp(DisplayList::AcquireContext);
     OpenGLRenderer::acquireContext();
 }
 
 void DisplayListRenderer::releaseContext() {
-    addOp(ReleaseContext);
+    addOp(DisplayList::ReleaseContext);
     OpenGLRenderer::releaseContext();
 }
 
 int DisplayListRenderer::save(int flags) {
-    addOp(Save);
+    addOp(DisplayList::Save);
     addInt(flags);
     return OpenGLRenderer::save(flags);
 }
 
 void DisplayListRenderer::restore() {
-    addOp(Restore);
+    addOp(DisplayList::Restore);
     OpenGLRenderer::restore();
 }
 
 void DisplayListRenderer::restoreToCount(int saveCount) {
-    addOp(RestoreToCount);
+    addOp(DisplayList::RestoreToCount);
     addInt(saveCount);
     OpenGLRenderer::restoreToCount(saveCount);
 }
 
 int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom,
         const SkPaint* p, int flags) {
-    addOp(SaveLayer);
+    addOp(DisplayList::SaveLayer);
     addBounds(left, top, right, bottom);
     addPaint(p);
     addInt(flags);
-    return OpenGLRenderer::saveLayer(left, top, right, bottom, p, flags);
+    return OpenGLRenderer::save(flags);
 }
 
 void DisplayListRenderer::translate(float dx, float dy) {
-    addOp(Translate);
+    addOp(DisplayList::Translate);
     addPoint(dx, dy);
     OpenGLRenderer::translate(dx, dy);
 }
 
 void DisplayListRenderer::rotate(float degrees) {
-    addOp(Rotate);
+    addOp(DisplayList::Rotate);
     addFloat(degrees);
     OpenGLRenderer::rotate(degrees);
 }
 
 void DisplayListRenderer::scale(float sx, float sy) {
-    addOp(Scale);
+    addOp(DisplayList::Scale);
     addPoint(sx, sy);
     OpenGLRenderer::scale(sx, sy);
 }
 
 void DisplayListRenderer::setMatrix(SkMatrix* matrix) {
-    addOp(SetMatrix);
+    addOp(DisplayList::SetMatrix);
     addMatrix(matrix);
     OpenGLRenderer::setMatrix(matrix);
 }
 
 void DisplayListRenderer::concatMatrix(SkMatrix* matrix) {
-    addOp(ConcatMatrix);
+    addOp(DisplayList::ConcatMatrix);
     addMatrix(matrix);
     OpenGLRenderer::concatMatrix(matrix);
 }
 
 bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom,
         SkRegion::Op op) {
-    addOp(ClipRect);
+    addOp(DisplayList::ClipRect);
     addBounds(left, top, right, bottom);
     addInt(op);
     return OpenGLRenderer::clipRect(left, top, right, bottom, op);
@@ -132,88 +369,77 @@
 
 void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top,
         const SkPaint* paint) {
-    addOp(DrawBitmap);
+    addOp(DisplayList::DrawBitmap);
     addBitmap(bitmap);
     addPoint(left, top);
     addPaint(paint);
-    OpenGLRenderer::drawBitmap(bitmap, left, top, paint);
 }
 
 void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix,
         const SkPaint* paint) {
-    addOp(DrawBitmapMatrix);
+    addOp(DisplayList::DrawBitmapMatrix);
     addBitmap(bitmap);
     addMatrix(matrix);
     addPaint(paint);
-    OpenGLRenderer::drawBitmap(bitmap, matrix, paint);
 }
 
 void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
         float srcRight, float srcBottom, float dstLeft, float dstTop,
         float dstRight, float dstBottom, const SkPaint* paint) {
-    addOp(DrawBitmapRect);
+    addOp(DisplayList::DrawBitmapRect);
     addBitmap(bitmap);
     addBounds(srcLeft, srcTop, srcRight, srcBottom);
     addBounds(dstLeft, dstTop, dstRight, dstBottom);
     addPaint(paint);
-    OpenGLRenderer::drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
-            dstLeft, dstTop, dstRight, dstBottom, paint);
 }
 
 void DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
         uint32_t width, uint32_t height, float left, float top, float right, float bottom,
         const SkPaint* paint) {
-    addOp(DrawPatch);
+    addOp(DisplayList::DrawPatch);
     addBitmap(bitmap);
     addInts(xDivs, width);
     addInts(yDivs, height);
     addBounds(left, top, right, bottom);
     addPaint(paint);
-    OpenGLRenderer::drawPatch(bitmap, xDivs, yDivs, width, height,
-            left, top, right, bottom, paint);
 }
 
 void DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) {
-    addOp(DrawColor);
+    addOp(DisplayList::DrawColor);
     addInt(color);
     addInt(mode);
-    OpenGLRenderer::drawColor(color, mode);
 }
 
 void DisplayListRenderer::drawRect(float left, float top, float right, float bottom,
         const SkPaint* paint) {
-    addOp(DrawRect);
+    addOp(DisplayList::DrawRect);
     addBounds(left, top, right, bottom);
     addPaint(paint);
-    OpenGLRenderer::drawRect(left, top, right, bottom, paint);
 }
 
 void DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) {
-    addOp(DrawPath);
+    addOp(DisplayList::DrawPath);
     addPath(path);
     addPaint(paint);
-    OpenGLRenderer::drawPath(path, paint);
 }
 
 void DisplayListRenderer::drawLines(float* points, int count, const SkPaint* paint) {
-    addOp(DrawLines);
+    addOp(DisplayList::DrawLines);
     addFloats(points, count);
     addPaint(paint);
-    OpenGLRenderer::drawLines(points, count, paint);
 }
 
 void DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
         float x, float y, SkPaint* paint) {
-    addOp(DrawText);
+    addOp(DisplayList::DrawText);
     addText(text, bytesCount);
     addInt(count);
     addPoint(x, y);
     addPaint(paint);
-    OpenGLRenderer::drawText(text, bytesCount, count, x, y, paint);
 }
 
 void DisplayListRenderer::resetShader() {
-    addOp(ResetShader);
+    addOp(DisplayList::ResetShader);
     OpenGLRenderer::resetShader();
 }
 
@@ -223,7 +449,7 @@
 }
 
 void DisplayListRenderer::resetColorFilter() {
-    addOp(ResetColorFilter);
+    addOp(DisplayList::ResetColorFilter);
     OpenGLRenderer::resetColorFilter();
 }
 
@@ -233,12 +459,12 @@
 }
 
 void DisplayListRenderer::resetShadow() {
-    addOp(ResetShadow);
+    addOp(DisplayList::ResetShadow);
     OpenGLRenderer::resetShadow();
 }
 
 void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int color) {
-    addOp(SetupShadow);
+    addOp(DisplayList::SetupShadow);
     addFloat(radius);
     addPoint(dx, dy);
     addInt(color);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 7a20b59..735f0e7 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -81,7 +81,7 @@
 
     int count() const { return mPaths.count(); }
 
-    const SkPath& operator[](int index) const {
+    SkPath& operator[](int index) const {
         return *mPaths[index];
     }
 
@@ -103,16 +103,18 @@
 };
 
 ///////////////////////////////////////////////////////////////////////////////
-// Renderer
+// Display list
 ///////////////////////////////////////////////////////////////////////////////
 
+class DisplayListRenderer;
+
 /**
- * Records drawing commands in a display list for latter playback.
+ * Replays recorded drawing commands.
  */
-class DisplayListRenderer: public OpenGLRenderer {
+class DisplayList {
 public:
-    DisplayListRenderer();
-    ~DisplayListRenderer();
+    DisplayList(const DisplayListRenderer& recorder);
+    ~DisplayList();
 
     enum Op {
         AcquireContext,
@@ -121,7 +123,6 @@
         Restore,
         RestoreToCount,
         SaveLayer,
-        SaveLayerAlpha,
         Translate,
         Rotate,
         Scale,
@@ -145,6 +146,109 @@
         SetupShadow
     };
 
+    void replay(OpenGLRenderer& renderer);
+
+private:
+    void init();
+
+    class TextContainer {
+    public:
+        size_t length() const {
+            return mByteLength;
+        }
+
+        const char* text() const {
+            return (const char*) mText;
+        }
+
+        size_t mByteLength;
+        const char* mText;
+    };
+
+    SkBitmap* getBitmap() {
+        int index = getInt();
+        return &mBitmaps[index - 1];
+    }
+
+    inline int getIndex() {
+        return mReader.readInt();
+    }
+
+    inline int getInt() {
+        return mReader.readInt();
+    }
+
+    SkMatrix* getMatrix() {
+        int index = getInt();
+        if (index == 0) {
+            return NULL;
+        }
+        return &mMatrices[index - 1];
+    }
+
+    SkPath* getPath() {
+        return &(*mPathHeap)[getInt() - 1];
+    }
+
+    SkPaint* getPaint() {
+        int index = getInt();
+        if (index == 0) {
+            return NULL;
+        }
+        return &mPaints[index - 1];
+    }
+
+    inline float getFloat() {
+        return mReader.readScalar();
+    }
+
+    int32_t* getInts(uint32_t& count) {
+        count = getInt();
+        return (int32_t*) mReader.skip(count * sizeof(int32_t));
+    }
+
+    float* getFloats(int& count) {
+        count = getInt();
+        return (float*) mReader.skip(count * sizeof(float));
+    }
+
+    void getText(TextContainer* text) {
+        size_t length = text->mByteLength = getInt();
+        text->mText = (const char*) mReader.skip(length);
+    }
+
+    PathHeap* mPathHeap;
+
+    SkBitmap* mBitmaps;
+    int mBitmapCount;
+
+    SkMatrix* mMatrices;
+    int mMatrixCount;
+
+    SkPaint* mPaints;
+    int mPaintCount;
+
+    mutable SkFlattenableReadBuffer mReader;
+
+    SkRefCntPlayback mRCPlayback;
+    SkTypefacePlayback mTFPlayback;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Renderer
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Records drawing commands in a display list for latter playback.
+ */
+class DisplayListRenderer: public OpenGLRenderer {
+public:
+    DisplayListRenderer();
+    ~DisplayListRenderer();
+
+    void setViewport(int width, int height);
+    void prepare();
+
     void acquireContext();
     void releaseContext();
 
@@ -189,8 +293,28 @@
 
     void reset();
 
+    DisplayList* getDisplayList() const {
+        return new DisplayList(*this);
+    }
+
+    const SkWriter32& writeStream() const {
+        return mWriter;
+    }
+
+    const SkTDArray<const SkFlatBitmap*>& getBitmaps() const {
+        return mBitmaps;
+    }
+
+    const SkTDArray<const SkFlatMatrix*>& getMatrices() const {
+        return mMatrices;
+    }
+
+    const SkTDArray<const SkFlatPaint*>& getPaints() const {
+        return mPaints;
+    }
+
 private:
-    inline void addOp(Op drawOp) {
+    inline void addOp(DisplayList::Op drawOp) {
         mWriter.writeInt(drawOp);
     }
 
@@ -199,6 +323,7 @@
     }
 
     void addInts(const int32_t* values, uint32_t count) {
+        mWriter.writeInt(count);
         for (uint32_t i = 0; i < count; i++) {
             mWriter.writeInt(values[i]);
         }
@@ -209,6 +334,7 @@
     }
 
     void addFloats(const float* values, int count) {
+        mWriter.writeInt(count);
         for (int i = 0; i < count; i++) {
             mWriter.writeScalar(values[i]);
         }
@@ -273,6 +399,8 @@
     SkRefCntRecorder mRCRecorder;
     SkRefCntRecorder mTFRecorder;
 
+    friend class DisplayList;
+
 }; // class DisplayListRenderer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index d505d80..1974cf0 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -54,6 +54,8 @@
 // Renderer
 ///////////////////////////////////////////////////////////////////////////////
 
+class DisplayListRenderer;
+
 /**
  * OpenGL renderer used to draw accelerated 2D graphics. The API is a
  * simplified version of Skia's Canvas API.
@@ -63,7 +65,7 @@
     OpenGLRenderer();
     virtual ~OpenGLRenderer();
 
-    void setViewport(int width, int height);
+    virtual void setViewport(int width, int height);
 
     virtual void prepare();
     virtual void finish();
@@ -428,6 +430,8 @@
     // Misc
     GLint mMaxTextureSize;
 
+    friend class DisplayListRenderer;
+
 }; // class OpenGLRenderer
 
 }; // namespace uirenderer