Merge "Deferred layer updates"
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index ee0fa86..1f75e70 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -157,6 +157,8 @@
     static native void nDestroyLayer(int layerId);
     static native void nDestroyLayerDeferred(int layerId);
     static native void nFlushLayer(int layerId);
+    static native void nUpdateRenderLayer(int layerId, int renderer, int displayList,
+            int left, int top, int right, int bottom);
     static native boolean nCopyLayer(int layerId, int bitmap);
 
     ///////////////////////////////////////////////////////////////////////////
@@ -191,6 +193,13 @@
     private static native int nGetMaximumTextureWidth();
     private static native int nGetMaximumTextureHeight();
 
+    /**
+     * Returns the native OpenGLRenderer object.
+     */
+    int getRenderer() {
+        return mRenderer;
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Setup
     ///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java
index 23a7166..c727a36 100644
--- a/core/java/android/view/GLES20RenderLayer.java
+++ b/core/java/android/view/GLES20RenderLayer.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Rect;
 
 /**
  * An OpenGL ES 2.0 implementation of {@link HardwareLayer}. This
@@ -95,4 +96,11 @@
     @Override
     void setTransform(Matrix matrix) {
     }
+
+    @Override
+    void redraw(DisplayList displayList, Rect dirtyRect) {
+        GLES20Canvas.nUpdateRenderLayer(mLayer, mCanvas.getRenderer(),
+                ((GLES20DisplayList) displayList).getNativeDisplayList(),
+                dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
+    }
 }
diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java
index 6c41023..cbb908b 100644
--- a/core/java/android/view/GLES20TextureLayer.java
+++ b/core/java/android/view/GLES20TextureLayer.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 
 /**
@@ -81,4 +82,8 @@
     void setTransform(Matrix matrix) {
         GLES20Canvas.nSetTextureLayerTransform(mLayer, matrix.native_instance);
     }
+
+    @Override
+    void redraw(DisplayList displayList, Rect dirtyRect) {
+    }
 }
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index d5666f3..49450bd 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -19,6 +19,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Rect;
 
 /**
  * A hardware layer can be used to render graphics operations into a hardware
@@ -163,4 +164,13 @@
      * @param matrix The transform to apply to the layer.
      */
     abstract void setTransform(Matrix matrix);
+
+    /**
+     * Specifies the display list to use to refresh the layer.
+     * 
+     * @param displayList The display list containing the drawing commands to
+     *                    execute in this layer
+     * @param dirtyRect The dirty region of the layer that needs to be redrawn
+     */
+    abstract void redraw(DisplayList displayList, Rect dirtyRect);
 }
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 74916f0..865f9d2 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -315,7 +315,7 @@
     }
 
     @Override
-    HardwareLayer getHardwareLayer() {
+    HardwareLayer getHardwareLayer(boolean immediateRefresh) {
         if (mLayer == null) {
             if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
                 return null;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f7dc73c..49f6023 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10123,7 +10123,7 @@
      * dynamic.
      */
     boolean hasStaticLayer() {
-        return mLayerType == LAYER_TYPE_NONE;
+        return true;
     }
 
     /**
@@ -10170,7 +10170,7 @@
                 if (mAttachInfo.mHardwareRenderer != null &&
                         mAttachInfo.mHardwareRenderer.isEnabled() &&
                         mAttachInfo.mHardwareRenderer.validate()) {
-                    getHardwareLayer();
+                    getHardwareLayer(true);
                 }
                 break;
             case LAYER_TYPE_SOFTWARE:
@@ -10192,7 +10192,7 @@
      *
      * @return A HardwareLayer ready to render, or null if an error occurred.
      */
-    HardwareLayer getHardwareLayer() {
+    HardwareLayer getHardwareLayer(boolean immediateRefresh) {
         if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null ||
                 !mAttachInfo.mHardwareRenderer.isEnabled()) {
             return null;
@@ -10222,41 +10222,32 @@
                 return null;
             }
 
-            HardwareCanvas currentCanvas = mAttachInfo.mHardwareCanvas;
-            final HardwareCanvas canvas = mHardwareLayer.start(currentCanvas);
-
-            // Make sure all the GPU resources have been properly allocated
-            if (canvas == null) {
-                mHardwareLayer.end(currentCanvas);
-                return null;
-            }
-
-            mAttachInfo.mHardwareCanvas = canvas;
-            try {
-                canvas.setViewport(width, height);
-                canvas.onPreDraw(mLocalDirtyRect);
+            if (!immediateRefresh) {
+                mHardwareLayer.redraw(getDisplayList(), mLocalDirtyRect);
                 mLocalDirtyRect.setEmpty();
-
-                final int restoreCount = canvas.save();
-
-                computeScroll();
-                canvas.translate(-mScrollX, -mScrollY);
-
-                mPrivateFlags |= DRAWN | DRAWING_CACHE_VALID;
-
-                // Fast path for layouts with no backgrounds
-                if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
-                    mPrivateFlags &= ~DIRTY_MASK;
-                    dispatchDraw(canvas);
-                } else {
-                    draw(canvas);
+            } else {
+                HardwareCanvas currentCanvas = mAttachInfo.mHardwareCanvas;
+                final HardwareCanvas canvas = mHardwareLayer.start(currentCanvas);
+    
+                // Make sure all the GPU resources have been properly allocated
+                if (canvas == null) {
+                    mHardwareLayer.end(currentCanvas);
+                    return null;
                 }
-
-                canvas.restoreToCount(restoreCount);
-            } finally {
-                canvas.onPostDraw();
-                mHardwareLayer.end(currentCanvas);
-                mAttachInfo.mHardwareCanvas = currentCanvas;
+    
+                mAttachInfo.mHardwareCanvas = canvas;
+                try {
+                    canvas.setViewport(width, height);
+                    canvas.onPreDraw(mLocalDirtyRect);
+                    mLocalDirtyRect.setEmpty();
+                    
+                    canvas.drawDisplayList(getDisplayList(), mRight - mLeft, mBottom - mTop, null,
+                            DisplayList.FLAG_CLIP_CHILDREN);
+                } finally {
+                    canvas.onPostDraw();
+                    mHardwareLayer.end(currentCanvas);
+                    mAttachInfo.mHardwareCanvas = currentCanvas;
+                }
             }
         }
 
@@ -11224,7 +11215,7 @@
         if (hasNoCache) {
             boolean layerRendered = false;
             if (layerType == LAYER_TYPE_HARDWARE) {
-                final HardwareLayer layer = getHardwareLayer();
+                final HardwareLayer layer = getHardwareLayer(false);
                 if (layer != null && layer.isValid()) {
                     mLayerPaint.setAlpha((int) (alpha * 255));
                     ((HardwareCanvas) canvas).drawHardwareLayer(layer, 0, 0, mLayerPaint);
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index f0560c1..77a6e52 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -770,6 +770,12 @@
     LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, renderTarget, transform);
 }
 
+static void android_view_GLES20Canvas_updateRenderLayer(JNIEnv* env, jobject clazz,
+        Layer* layer, OpenGLRenderer* renderer, DisplayList* displayList,
+        jint left, jint top, jint right, jint bottom) {
+    layer->updateDeferred(renderer, displayList, left, top, right, bottom);
+}
+
 static void android_view_GLES20Canvas_setTextureLayerTransform(JNIEnv* env, jobject clazz,
         Layer* layer, SkMatrix* matrix) {
 
@@ -953,13 +959,15 @@
     { "nCreateTextureLayer",     "(Z[I)I",     (void*) android_view_GLES20Canvas_createTextureLayer },
     { "nUpdateTextureLayer",     "(IIIZLandroid/graphics/SurfaceTexture;)V",
                                                (void*) android_view_GLES20Canvas_updateTextureLayer },
-    { "nSetTextureLayerTransform", "(II)V",    (void*) android_view_GLES20Canvas_setTextureLayerTransform },
+    { "nUpdateRenderLayer",      "(IIIIIII)V", (void*) android_view_GLES20Canvas_updateRenderLayer },
     { "nDestroyLayer",           "(I)V",       (void*) android_view_GLES20Canvas_destroyLayer },
     { "nDestroyLayerDeferred",   "(I)V",       (void*) android_view_GLES20Canvas_destroyLayerDeferred },
     { "nFlushLayer",             "(I)V",       (void*) android_view_GLES20Canvas_flushLayer },
     { "nDrawLayer",              "(IIFFI)V",   (void*) android_view_GLES20Canvas_drawLayer },
     { "nCopyLayer",              "(II)Z",      (void*) android_view_GLES20Canvas_copyLayer },
 
+    { "nSetTextureLayerTransform", "(II)V",    (void*) android_view_GLES20Canvas_setTextureLayerTransform },
+
     { "nGetMaximumTextureWidth",  "()I",       (void*) android_view_GLES20Canvas_getMaxTextureWidth },
     { "nGetMaximumTextureHeight", "()I",       (void*) android_view_GLES20Canvas_getMaxTextureHeight },
 
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index ee6ef1a..f243177 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -37,6 +37,10 @@
 // Layers
 ///////////////////////////////////////////////////////////////////////////////
 
+// Forward declarations
+class OpenGLRenderer;
+class DisplayList;
+
 /**
  * A layer has dimensions and is backed by an OpenGL texture or FBO.
  */
@@ -51,6 +55,9 @@
         texture.width = layerWidth;
         texture.height = layerHeight;
         colorFilter = NULL;
+        deferredUpdateScheduled = false;
+        renderer = NULL;
+        displayList = NULL;
     }
 
     ~Layer() {
@@ -77,6 +84,15 @@
         regionRect.translate(layer.left, layer.top);
     }
 
+    void updateDeferred(OpenGLRenderer* renderer, DisplayList* displayList,
+            int left, int top, int right, int bottom) {
+        this->renderer = renderer;
+        this->displayList = displayList;
+        const Rect r(left, top, right, bottom);
+        dirtyRect.unionWith(r);
+        deferredUpdateScheduled = true;
+    }
+
     inline uint32_t getWidth() {
         return texture.width;
     }
@@ -234,6 +250,14 @@
     uint16_t* meshIndices;
     GLsizei meshElementCount;
 
+    /**
+     * Used for deferred updates.
+     */
+    bool deferredUpdateScheduled;
+    OpenGLRenderer* renderer;
+    DisplayList* displayList;
+    Rect dirtyRect;
+
 private:
     /**
      * Name of the FBO used to render the layer. If the name is 0
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e3148e8..339ae0a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2364,6 +2364,24 @@
         return;
     }
 
+    if (layer->deferredUpdateScheduled && layer->renderer && layer->displayList) {
+        OpenGLRenderer* renderer = layer->renderer;
+        Rect& dirty = layer->dirtyRect;
+
+        interrupt();
+        renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
+        renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, !layer->isBlend());
+        renderer->drawDisplayList(layer->displayList, layer->getWidth(), layer->getHeight(),
+                dirty, DisplayList::kReplayFlag_ClipChildren);
+        renderer->finish();
+        resume();
+
+        dirty.setEmpty();
+        layer->deferredUpdateScheduled = false;
+        layer->renderer = NULL;
+        layer->displayList = NULL;
+    }
+
     mCaches.activeTexture(0);
 
     int alpha;