Move LayerType to RenderNode

Change-Id: Icb79a5015cb0362b1f3a66d09007450730135a97
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index b5b9199..266a6fed 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -19,7 +19,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.Paint;
-import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 
 import com.android.internal.util.VirtualRefBasePtr;
@@ -34,34 +33,18 @@
  * @hide
  */
 final class HardwareLayer {
-    private static final int LAYER_TYPE_TEXTURE = 1;
-    private static final int LAYER_TYPE_DISPLAY_LIST = 2;
-
     private HardwareRenderer mRenderer;
     private VirtualRefBasePtr mFinalizer;
-    private RenderNode mDisplayList;
-    private final int mLayerType;
 
-    private HardwareLayer(HardwareRenderer renderer, long deferredUpdater, int type) {
+    private HardwareLayer(HardwareRenderer renderer, long deferredUpdater) {
         if (renderer == null || deferredUpdater == 0) {
             throw new IllegalArgumentException("Either hardware renderer: " + renderer
                     + " or deferredUpdater: " + deferredUpdater + " is invalid");
         }
         mRenderer = renderer;
-        mLayerType = type;
         mFinalizer = new VirtualRefBasePtr(deferredUpdater);
     }
 
-    private void assertType(int type) {
-        if (mLayerType != type) {
-            throw new IllegalAccessError("Method not appropriate for this layer type! " + mLayerType);
-        }
-    }
-
-    boolean hasDisplayList() {
-        return mDisplayList != null;
-    }
-
     /**
      * Update the paint used when drawing this layer.
      *
@@ -90,11 +73,6 @@
             // Already destroyed
             return;
         }
-
-        if (mDisplayList != null) {
-            mDisplayList.destroyDisplayListData();
-            mDisplayList = null;
-        }
         mRenderer.onLayerDestroyed(this);
         mRenderer = null;
         mFinalizer.release();
@@ -105,21 +83,6 @@
         return mFinalizer.get();
     }
 
-    public RenderNode startRecording() {
-        assertType(LAYER_TYPE_DISPLAY_LIST);
-
-        if (mDisplayList == null) {
-            mDisplayList = RenderNode.create("HardwareLayer");
-        }
-        return mDisplayList;
-    }
-
-    public void endRecording(Rect dirtyRect) {
-        nUpdateRenderLayer(mFinalizer.get(), mDisplayList.getNativeDisplayList(),
-                dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
-        mRenderer.pushLayerUpdate(this);
-    }
-
     /**
      * Copies this layer into the specified bitmap.
      *
@@ -160,7 +123,6 @@
      * Indicates that this layer has lost its texture.
      */
     public void detachSurfaceTexture(final SurfaceTexture surface) {
-        assertType(LAYER_TYPE_TEXTURE);
         mRenderer.safelyRun(new Runnable() {
             @Override
             public void run() {
@@ -177,13 +139,11 @@
     }
 
     public void setSurfaceTexture(SurfaceTexture surface) {
-        assertType(LAYER_TYPE_TEXTURE);
         nSetSurfaceTexture(mFinalizer.get(), surface, false);
         mRenderer.pushLayerUpdate(this);
     }
 
     public void updateSurfaceTexture() {
-        assertType(LAYER_TYPE_TEXTURE);
         nUpdateSurfaceTexture(mFinalizer.get());
         mRenderer.pushLayerUpdate(this);
     }
@@ -192,18 +152,13 @@
      * This should only be used by HardwareRenderer! Do not call directly
      */
     SurfaceTexture createSurfaceTexture() {
-        assertType(LAYER_TYPE_TEXTURE);
         SurfaceTexture st = new SurfaceTexture(nGetTexName(mFinalizer.get()));
         nSetSurfaceTexture(mFinalizer.get(), st, true);
         return st;
     }
 
     static HardwareLayer adoptTextureLayer(HardwareRenderer renderer, long layer) {
-        return new HardwareLayer(renderer, layer, LAYER_TYPE_TEXTURE);
-    }
-
-    static HardwareLayer adoptDisplayListLayer(HardwareRenderer renderer, long layer) {
-        return new HardwareLayer(renderer, layer, LAYER_TYPE_DISPLAY_LIST);
+        return new HardwareLayer(renderer, layer);
     }
 
     private static native void nOnTextureDestroyed(long layerUpdater);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 592dec8..3de8144 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -374,16 +374,6 @@
     abstract HardwareLayer createTextureLayer();
 
     /**
-     * Creates a new hardware layer.
-     *
-     * @param width The minimum width of the layer
-     * @param height The minimum height of the layer
-     *
-     * @return A hardware layer
-     */
-    abstract HardwareLayer createDisplayListLayer(int width, int height);
-
-    /**
      * Creates a new {@link SurfaceTexture} that can be used to render into the
      * specified hardware layer.
      *
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index c165475..d86b699 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.graphics.Matrix;
 import android.graphics.Outline;
+import android.graphics.Paint;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -322,11 +323,13 @@
      * handled in the drawLayer operation directly (and more efficiently).
      *
      * @param caching true if the display list represents a hardware layer, false otherwise.
-     *
-     * @hide
      */
-    public boolean setCaching(boolean caching) {
-        return nSetCaching(mNativeRenderNode, caching);
+    public boolean setLayerType(int layerType) {
+        return nSetLayerType(mNativeRenderNode, layerType);
+    }
+
+    public boolean setLayerPaint(Paint paint) {
+        return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.mNativePaint : 0);
     }
 
     /**
@@ -902,7 +905,8 @@
     private static native boolean nSetCameraDistance(long renderNode, float distance);
     private static native boolean nSetPivotY(long renderNode, float pivotY);
     private static native boolean nSetPivotX(long renderNode, float pivotX);
-    private static native boolean nSetCaching(long renderNode, boolean caching);
+    private static native boolean nSetLayerType(long renderNode, int layerType);
+    private static native boolean nSetLayerPaint(long renderNode, long paint);
     private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds);
     private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject);
     private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve);
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 2a9f7d5..1f7eaa2 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -275,6 +275,11 @@
         }
     }
 
+    @Override
+    public void setLayerPaint(Paint paint) {
+        setLayerType(/* ignored */ 0, paint);
+    }
+
     /**
      * Always returns {@link #LAYER_TYPE_HARDWARE}.
      */
@@ -332,11 +337,6 @@
         }
     }
 
-    @Override
-    boolean destroyLayer(boolean valid) {
-        return false;
-    }
-
     /**
      * @hide
      */
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 7bbe84e..2bfbd65 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -257,12 +257,6 @@
     }
 
     @Override
-    HardwareLayer createDisplayListLayer(int width, int height) {
-        long layer = nCreateDisplayListLayer(mNativeProxy, width, height);
-        return HardwareLayer.adoptDisplayListLayer(this, layer);
-    }
-
-    @Override
     HardwareLayer createTextureLayer() {
         long layer = nCreateTextureLayer(mNativeProxy);
         return HardwareLayer.adoptTextureLayer(this, layer);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 434f853..f3996fb 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3465,8 +3465,6 @@
     })
     int mLayerType = LAYER_TYPE_NONE;
     Paint mLayerPaint;
-    Rect mLocalDirtyRect;
-    private HardwareLayer mHardwareLayer;
 
     /**
      * Set to true when drawing cache is enabled and cannot be created.
@@ -12931,7 +12929,6 @@
         stopNestedScroll();
 
         destroyDrawingCache();
-        destroyLayer(false);
 
         cleanupDraw();
         mCurrentAnimation = null;
@@ -13409,30 +13406,32 @@
                     + "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE");
         }
 
-        if (layerType == mLayerType) {
+        boolean typeChanged = mRenderNode.setLayerType(layerType);
+
+        if (!typeChanged) {
             setLayerPaint(paint);
             return;
         }
 
         // Destroy any previous software drawing cache if needed
-        switch (mLayerType) {
-            case LAYER_TYPE_HARDWARE:
-                destroyLayer(false);
-                // fall through - non-accelerated views may use software layer mechanism instead
-            case LAYER_TYPE_SOFTWARE:
-                destroyDrawingCache();
-                break;
-            default:
-                break;
+        if (mLayerType == LAYER_TYPE_SOFTWARE) {
+            destroyDrawingCache();
+            invalidateParentCaches();
+            invalidate(true);
         }
 
         mLayerType = layerType;
-        final boolean layerDisabled = mLayerType == LAYER_TYPE_NONE;
+        final boolean layerDisabled = (mLayerType == LAYER_TYPE_NONE);
         mLayerPaint = layerDisabled ? null : (paint == null ? new Paint() : paint);
-        mLocalDirtyRect = layerDisabled ? null : new Rect();
+        mRenderNode.setLayerPaint(mLayerPaint);
 
-        invalidateParentCaches();
-        invalidate(true);
+        if (mLayerType == LAYER_TYPE_SOFTWARE) {
+            // LAYER_TYPE_SOFTWARE is handled by View:draw(), so need to invalidate()
+            invalidateParentCaches();
+            invalidate(true);
+        } else {
+            invalidateViewProperty(false, false);
+        }
     }
 
     /**
@@ -13465,11 +13464,9 @@
         if (layerType != LAYER_TYPE_NONE) {
             mLayerPaint = paint == null ? new Paint() : paint;
             if (layerType == LAYER_TYPE_HARDWARE) {
-                HardwareLayer layer = getHardwareLayer();
-                if (layer != null) {
-                    layer.setLayerPaint(mLayerPaint);
+                if (mRenderNode.setLayerPaint(mLayerPaint)) {
+                    invalidateViewProperty(false, false);
                 }
-                invalidateViewProperty(false, false);
             } else {
                 invalidate();
             }
@@ -13527,16 +13524,11 @@
 
         switch (mLayerType) {
             case LAYER_TYPE_HARDWARE:
-                getHardwareLayer();
-                // TODO: We need a better way to handle this case
-                // If views have registered pre-draw listeners they need
-                // to be notified before we build the layer. Those listeners
-                // may however rely on other events to happen first so we
-                // cannot just invoke them here until they don't cancel the
-                // current frame
-                if (!attachInfo.mTreeObserver.hasOnPreDrawListeners()) {
-                    attachInfo.mViewRootImpl.dispatchFlushHardwareLayerUpdates();
-                }
+                // The only part of a hardware layer we can build in response to
+                // this call is to ensure the display list is up to date.
+                // The actual rendering of the display list into the layer must
+                // be done at playback time
+                updateDisplayListIfDirty();
                 break;
             case LAYER_TYPE_SOFTWARE:
                 buildDrawingCache(true);
@@ -13545,70 +13537,13 @@
     }
 
     /**
-     * <p>Returns a hardware layer that can be used to draw this view again
-     * without executing its draw method.</p>
+     * If this View draws with a HardwareLayer, returns it.
+     * Otherwise returns null
      *
-     * @return A HardwareLayer ready to render, or null if an error occurred.
+     * TODO: Only TextureView uses this, can we eliminate it?
      */
     HardwareLayer getHardwareLayer() {
-        if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null ||
-                !mAttachInfo.mHardwareRenderer.isEnabled()) {
-            return null;
-        }
-
-        final int width = mRight - mLeft;
-        final int height = mBottom - mTop;
-
-        if (width == 0 || height == 0) {
-            return null;
-        }
-
-        if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || mHardwareLayer == null) {
-            if (mHardwareLayer == null) {
-                mHardwareLayer = mAttachInfo.mHardwareRenderer.createDisplayListLayer(
-                        width, height);
-                mLocalDirtyRect.set(0, 0, width, height);
-            } else if (mHardwareLayer.isValid()) {
-                // This should not be necessary but applications that change
-                // the parameters of their background drawable without calling
-                // this.setBackground(Drawable) can leave the view in a bad state
-                // (for instance isOpaque() returns true, but the background is
-                // not opaque.)
-                computeOpaqueFlags();
-
-                if (mHardwareLayer.prepare(width, height, isOpaque())) {
-                    mLocalDirtyRect.set(0, 0, width, height);
-                }
-            }
-
-            mHardwareLayer.setLayerPaint(mLayerPaint);
-            RenderNode displayList = mHardwareLayer.startRecording();
-            updateDisplayListIfDirty(displayList, true);
-            mHardwareLayer.endRecording(mLocalDirtyRect);
-            mLocalDirtyRect.setEmpty();
-        }
-
-        return mHardwareLayer;
-    }
-
-    /**
-     * Destroys this View's hardware layer if possible.
-     *
-     * @return True if the layer was destroyed, false otherwise.
-     *
-     * @see #setLayerType(int, android.graphics.Paint)
-     * @see #LAYER_TYPE_HARDWARE
-     */
-    boolean destroyLayer(boolean valid) {
-        if (mHardwareLayer != null) {
-            mHardwareLayer.destroy();
-            mHardwareLayer = null;
-
-            invalidate(true);
-            invalidateParentCaches();
-            return true;
-        }
-        return false;
+        return null;
     }
 
     /**
@@ -13624,7 +13559,6 @@
      */
     protected void destroyHardwareResources() {
         resetDisplayList();
-        destroyLayer(true);
     }
 
     /**
@@ -13719,20 +13653,8 @@
         return !(mAttachInfo == null || mAttachInfo.mHardwareRenderer == null);
     }
 
-    /**
-     * Returns a DisplayList. If the incoming displayList is null, one will be created.
-     * Otherwise, the same display list will be returned (after having been rendered into
-     * along the way, depending on the invalidation state of the view).
-     *
-     * @param renderNode The previous version of this displayList, could be null.
-     * @param isLayer Whether the requester of the display list is a layer. If so,
-     * the view will avoid creating a layer inside the resulting display list.
-     * @return A new or reused DisplayList object.
-     */
-    private void updateDisplayListIfDirty(@NonNull RenderNode renderNode, boolean isLayer) {
-        if (renderNode == null) {
-            throw new IllegalArgumentException("RenderNode must not be null");
-        }
+    private void updateDisplayListIfDirty() {
+        final RenderNode renderNode = mRenderNode;
         if (!canHaveDisplayList()) {
             // can't populate RenderNode, don't try
             return;
@@ -13740,11 +13662,10 @@
 
         if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
                 || !renderNode.isValid()
-                || (!isLayer && mRecreateDisplayList)) {
+                || (mRecreateDisplayList)) {
             // Don't need to recreate the display list, just need to tell our
             // children to restore/recreate theirs
             if (renderNode.isValid()
-                    && !isLayer
                     && !mRecreateDisplayList) {
                 mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
                 mPrivateFlags &= ~PFLAG_DIRTY_MASK;
@@ -13753,13 +13674,10 @@
                 return; // no work needed
             }
 
-            if (!isLayer) {
-                // If we got here, we're recreating it. Mark it as such to ensure that
-                // we copy in child display lists into ours in drawChild()
-                mRecreateDisplayList = true;
-            }
+            // If we got here, we're recreating it. Mark it as such to ensure that
+            // we copy in child display lists into ours in drawChild()
+            mRecreateDisplayList = true;
 
-            boolean caching = false;
             int width = mRight - mLeft;
             int height = mBottom - mTop;
             int layerType = getLayerType();
@@ -13767,34 +13685,21 @@
             final HardwareCanvas canvas = renderNode.start(width, height);
 
             try {
-                if (!isLayer && layerType != LAYER_TYPE_NONE) {
-                    if (layerType == LAYER_TYPE_HARDWARE) {
-                        final HardwareLayer layer = getHardwareLayer();
-                        if (layer != null && layer.isValid()) {
-                            canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
-                        } else {
-                            canvas.saveLayer(0, 0, mRight - mLeft, mBottom - mTop, mLayerPaint,
-                                    Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
-                                            Canvas.CLIP_TO_LAYER_SAVE_FLAG);
-                        }
-                        caching = true;
-                    } else {
-                        buildDrawingCache(true);
-                        Bitmap cache = getDrawingCache(true);
-                        if (cache != null) {
-                            canvas.drawBitmap(cache, 0, 0, mLayerPaint);
-                            caching = true;
-                        }
+                final HardwareLayer layer = getHardwareLayer();
+                if (layer != null && layer.isValid()) {
+                    canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
+                } else if (layerType == LAYER_TYPE_SOFTWARE) {
+                    buildDrawingCache(true);
+                    Bitmap cache = getDrawingCache(true);
+                    if (cache != null) {
+                        canvas.drawBitmap(cache, 0, 0, mLayerPaint);
                     }
                 } else {
-
                     computeScroll();
 
                     canvas.translate(-mScrollX, -mScrollY);
-                    if (!isLayer) {
-                        mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
-                        mPrivateFlags &= ~PFLAG_DIRTY_MASK;
-                    }
+                    mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
+                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
 
                     // Fast path for layouts with no backgrounds
                     if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
@@ -13808,14 +13713,9 @@
                 }
             } finally {
                 renderNode.end(canvas);
-                renderNode.setCaching(caching);
-                if (isLayer) {
-                    renderNode.setLeftTopRightBottom(0, 0, width, height);
-                } else {
-                    setDisplayListProperties(renderNode);
-                }
+                setDisplayListProperties(renderNode);
             }
-        } else if (!isLayer) {
+        } else {
             mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
             mPrivateFlags &= ~PFLAG_DIRTY_MASK;
         }
@@ -13830,7 +13730,7 @@
      * @hide
      */
     public RenderNode getDisplayList() {
-        updateDisplayListIfDirty(mRenderNode, false);
+        updateDisplayListIfDirty();
         return mRenderNode;
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 2905851..21b266e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4386,10 +4386,9 @@
             // Make sure we do not set both flags at the same time
             int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
 
-            if (child.mLayerType != LAYER_TYPE_NONE) {
+            if (child.mLayerType == LAYER_TYPE_SOFTWARE) {
                 mPrivateFlags |= PFLAG_INVALIDATED;
                 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
-                child.mLocalDirtyRect.union(dirty);
             }
 
             final int[] location = attachInfo.mInvalidateChildLocation;
@@ -4498,9 +4497,8 @@
                 location[CHILD_LEFT_INDEX] = left;
                 location[CHILD_TOP_INDEX] = top;
 
-                if (mLayerType != LAYER_TYPE_NONE) {
+                if (mLayerType == LAYER_TYPE_SOFTWARE) {
                     mPrivateFlags |= PFLAG_INVALIDATED;
-                    mLocalDirtyRect.union(dirty);
                 }
 
                 return mParent;
@@ -4517,9 +4515,8 @@
                     dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
                 }
 
-                if (mLayerType != LAYER_TYPE_NONE) {
+                if (mLayerType == LAYER_TYPE_SOFTWARE) {
                     mPrivateFlags |= PFLAG_INVALIDATED;
-                    mLocalDirtyRect.union(dirty);
                 }
 
                 return mParent;
@@ -4567,10 +4564,6 @@
 
         final AttachInfo attachInfo = mAttachInfo;
         if (attachInfo != null) {
-            if (child.mLayerType != LAYER_TYPE_NONE) {
-                child.mLocalDirtyRect.union(dirty);
-            }
-
             int left = child.mLeft;
             int top = child.mTop;
             if (!child.getMatrix().isIdentity()) {
@@ -4618,9 +4611,6 @@
             if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0 ||
                     dirty.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
 
-                if (mLayerType != LAYER_TYPE_NONE) {
-                    mLocalDirtyRect.union(dirty);
-                }
                 if (!getMatrix().isIdentity()) {
                     transformRect(dirty);
                 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 76d5038..fc48497 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -631,27 +631,7 @@
     }
 
     void destroyHardwareLayers() {
-        if (mThread != Thread.currentThread()) {
-            if (mAttachInfo.mHardwareRenderer != null &&
-                    mAttachInfo.mHardwareRenderer.isEnabled()) {
-                HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE);
-            }
-        } else {
-            destroyHardwareLayer(mView);
-        }
-    }
-
-    private static void destroyHardwareLayer(View view) {
-        view.destroyLayer(true);
-
-        if (view instanceof ViewGroup) {
-            ViewGroup group = (ViewGroup) view;
-
-            int count = group.getChildCount();
-            for (int i = 0; i < count; i++) {
-                destroyHardwareLayer(group.getChildAt(i));
-            }
-        }
+        // TODO Implement
     }
 
     void flushHardwareLayerUpdates() {
@@ -1520,54 +1500,55 @@
 
                         disposeResizeBuffer();
 
-                        if (mResizeBuffer == null) {
-                            mResizeBuffer = mAttachInfo.mHardwareRenderer.createDisplayListLayer(
-                                    mWidth, mHeight);
-                        }
-                        mResizeBuffer.prepare(mWidth, mHeight, false);
-                        RenderNode layerRenderNode = mResizeBuffer.startRecording();
-                        HardwareCanvas layerCanvas = layerRenderNode.start(mWidth, mHeight);
-                        try {
-                            final int restoreCount = layerCanvas.save();
-
-                            int yoff;
-                            final boolean scrolling = mScroller != null
-                                    && mScroller.computeScrollOffset();
-                            if (scrolling) {
-                                yoff = mScroller.getCurrY();
-                                mScroller.abortAnimation();
-                            } else {
-                                yoff = mScrollY;
-                            }
-
-                            layerCanvas.translate(0, -yoff);
-                            if (mTranslator != null) {
-                                mTranslator.translateCanvas(layerCanvas);
-                            }
-
-                            RenderNode renderNode = mView.mRenderNode;
-                            if (renderNode != null && renderNode.isValid()) {
-                                layerCanvas.drawDisplayList(renderNode, null,
-                                        RenderNode.FLAG_CLIP_CHILDREN);
-                            } else {
-                                mView.draw(layerCanvas);
-                            }
-
-                            drawAccessibilityFocusedDrawableIfNeeded(layerCanvas);
-
-                            mResizeBufferStartTime = SystemClock.uptimeMillis();
-                            mResizeBufferDuration = mView.getResources().getInteger(
-                                    com.android.internal.R.integer.config_mediumAnimTime);
-
-                            layerCanvas.restoreToCount(restoreCount);
-                            layerRenderNode.end(layerCanvas);
-                            layerRenderNode.setCaching(true);
-                            layerRenderNode.setLeftTopRightBottom(0, 0, mWidth, mHeight);
-                            mTempRect.set(0, 0, mWidth, mHeight);
-                        } finally {
-                            mResizeBuffer.endRecording(mTempRect);
-                        }
-                        mAttachInfo.mHardwareRenderer.flushLayerUpdates();
+// TODO: Again....
+//                        if (mResizeBuffer == null) {
+//                            mResizeBuffer = mAttachInfo.mHardwareRenderer.createDisplayListLayer(
+//                                    mWidth, mHeight);
+//                        }
+//                        mResizeBuffer.prepare(mWidth, mHeight, false);
+//                        RenderNode layerRenderNode = mResizeBuffer.startRecording();
+//                        HardwareCanvas layerCanvas = layerRenderNode.start(mWidth, mHeight);
+//                        try {
+//                            final int restoreCount = layerCanvas.save();
+//
+//                            int yoff;
+//                            final boolean scrolling = mScroller != null
+//                                    && mScroller.computeScrollOffset();
+//                            if (scrolling) {
+//                                yoff = mScroller.getCurrY();
+//                                mScroller.abortAnimation();
+//                            } else {
+//                                yoff = mScrollY;
+//                            }
+//
+//                            layerCanvas.translate(0, -yoff);
+//                            if (mTranslator != null) {
+//                                mTranslator.translateCanvas(layerCanvas);
+//                            }
+//
+//                            RenderNode renderNode = mView.mRenderNode;
+//                            if (renderNode != null && renderNode.isValid()) {
+//                                layerCanvas.drawDisplayList(renderNode, null,
+//                                        RenderNode.FLAG_CLIP_CHILDREN);
+//                            } else {
+//                                mView.draw(layerCanvas);
+//                            }
+//
+//                            drawAccessibilityFocusedDrawableIfNeeded(layerCanvas);
+//
+//                            mResizeBufferStartTime = SystemClock.uptimeMillis();
+//                            mResizeBufferDuration = mView.getResources().getInteger(
+//                                    com.android.internal.R.integer.config_mediumAnimTime);
+//
+//                            layerCanvas.restoreToCount(restoreCount);
+//                            layerRenderNode.end(layerCanvas);
+//                            layerRenderNode.setCaching(true);
+//                            layerRenderNode.setLeftTopRightBottom(0, 0, mWidth, mHeight);
+//                            mTempRect.set(0, 0, mWidth, mHeight);
+//                        } finally {
+//                            mResizeBuffer.endRecording(mTempRect);
+//                        }
+//                        mAttachInfo.mHardwareRenderer.flushLayerUpdates();
                     }
                     mAttachInfo.mContentInsets.set(mPendingContentInsets);
                     if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index 64b077b..ace17ec 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -87,14 +87,6 @@
     layer->updateTexImage();
 }
 
-static void android_view_HardwareLayer_updateRenderLayer(JNIEnv* env, jobject clazz,
-        jlong layerUpdaterPtr, jlong displayListPtr,
-        jint left, jint top, jint right, jint bottom) {
-    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
-    RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
-    layer->setDisplayList(displayList, left, top, right, bottom);
-}
-
 static jlong android_view_HardwareLayer_getLayer(JNIEnv* env, jobject clazz,
         jlong layerUpdaterPtr) {
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
@@ -126,7 +118,6 @@
     { "nSetSurfaceTexture",      "(JLandroid/graphics/SurfaceTexture;Z)V",
             (void*) android_view_HardwareLayer_setSurfaceTexture },
     { "nUpdateSurfaceTexture",   "(J)V",       (void*) android_view_HardwareLayer_updateSurfaceTexture },
-    { "nUpdateRenderLayer",      "(JJIIII)V",  (void*) android_view_HardwareLayer_updateRenderLayer },
 
     { "nGetLayer",               "(J)J",       (void*) android_view_HardwareLayer_getLayer },
     { "nGetTexName",             "(J)I",       (void*) android_view_HardwareLayer_getTexName },
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 26f8993..3ffde2d 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -87,9 +87,16 @@
 // RenderProperties - setters
 // ----------------------------------------------------------------------------
 
-static jboolean android_view_RenderNode_setCaching(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jboolean caching) {
-    return SET_AND_DIRTY(setCaching, caching, RenderNode::GENERIC);
+static jboolean android_view_RenderNode_setLayerType(JNIEnv* env,
+        jobject clazz, jlong renderNodePtr, jint jlayerType) {
+    LayerType layerType = static_cast<LayerType>(jlayerType);
+    return SET_AND_DIRTY(mutateLayerProperties().setType, layerType, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setLayerPaint(JNIEnv* env,
+        jobject clazz, jlong renderNodePtr, jlong paintPtr) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+    return SET_AND_DIRTY(mutateLayerProperties().setFromPaint, paint, RenderNode::GENERIC);
 }
 
 static jboolean android_view_RenderNode_setStaticMatrix(JNIEnv* env,
@@ -475,7 +482,8 @@
     { "nOutput",               "(J)V",  (void*) android_view_RenderNode_output },
     { "nGetDebugSize",         "(J)I",  (void*) android_view_RenderNode_getDebugSize },
 
-    { "nSetCaching",           "(JZ)Z",  (void*) android_view_RenderNode_setCaching },
+    { "nSetLayerType",         "(JI)Z",  (void*) android_view_RenderNode_setLayerType },
+    { "nSetLayerPaint",        "(JJ)Z",  (void*) android_view_RenderNode_setLayerPaint },
     { "nSetStaticMatrix",      "(JJ)Z",  (void*) android_view_RenderNode_setStaticMatrix },
     { "nSetAnimationMatrix",   "(JJ)Z",  (void*) android_view_RenderNode_setAnimationMatrix },
     { "nSetClipToBounds",      "(JZ)Z",  (void*) android_view_RenderNode_setClipToBounds },
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 898e81a..1cb87f2 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -35,6 +35,7 @@
 enum TransformType {
     TransformRenderNode,
     TransformMatrix4,
+    TransformNone,
 };
 
 struct DirtyStack {
@@ -80,14 +81,25 @@
     mHead->matrix4 = transform;
 }
 
+void DamageAccumulator::pushNullTransform() {
+    pushCommon();
+    mHead->type = TransformNone;
+}
+
 void DamageAccumulator::popTransform() {
     LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
     DirtyStack* dirtyFrame = mHead;
     mHead = mHead->prev;
-    if (dirtyFrame->type == TransformRenderNode) {
+    switch (dirtyFrame->type) {
+    case TransformRenderNode:
         applyRenderNodeTransform(dirtyFrame);
-    } else {
+        break;
+    case TransformMatrix4:
         applyMatrix4Transform(dirtyFrame);
+        break;
+    case TransformNone:
+        mHead->pendingDirty.join(dirtyFrame->pendingDirty);
+        break;
     }
 }
 
@@ -186,6 +198,10 @@
     mHead->pendingDirty.join(left, top, right, bottom);
 }
 
+void DamageAccumulator::peekAtDirty(SkRect* dest) {
+    *dest = mHead->pendingDirty;
+}
+
 void DamageAccumulator::finish(SkRect* totalDirty) {
     LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
     // Root node never has a transform, so this is the fully mapped dirty rect
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index 2ca30d4..fc9b41b 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -35,8 +35,10 @@
 public:
     virtual void pushTransform(const RenderNode* transform) = 0;
     virtual void pushTransform(const Matrix4* transform) = 0;
+    virtual void pushNullTransform() = 0;
     virtual void popTransform() = 0;
     virtual void dirty(float left, float top, float right, float bottom) = 0;
+    virtual void peekAtDirty(SkRect* dest) = 0;
 protected:
     virtual ~IDamageAccumulator() {}
 };
@@ -52,6 +54,9 @@
     // will be affected by the transform when popTransform() is called.
     virtual void pushTransform(const RenderNode* transform);
     virtual void pushTransform(const Matrix4* transform);
+    // This is used in combination with peekAtDirty to inspect the damage
+    // area of a subtree
+    virtual void pushNullTransform();
 
     // Pops a transform node from the stack, propagating the dirty rect
     // up to the parent node. Returns the IDamageTransform that was just applied
@@ -59,6 +64,9 @@
 
     virtual void dirty(float left, float top, float right, float bottom);
 
+    // Returns the current dirty area, *NOT* transformed by pushed transforms
+    virtual void peekAtDirty(SkRect* dest);
+
     void finish(SkRect* totalDirty);
 
 private:
@@ -75,8 +83,10 @@
 public:
     virtual void pushTransform(const RenderNode* transform) { }
     virtual void pushTransform(const Matrix4* transform) { }
+    virtual void pushNullTransform() { }
     virtual void popTransform() { }
     virtual void dirty(float left, float top, float right, float bottom) { }
+    virtual void peekAtDirty(SkRect* dest) { dest->setEmpty(); }
 
     ANDROID_API static NullDamageAccumulator* instance();
 
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index d494c4c..8e99b9a 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -27,8 +27,7 @@
 }
 
 DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroyer)
-        : mDisplayList(0)
-        , mSurfaceTexture(0)
+        : mSurfaceTexture(0)
         , mTransform(0)
         , mNeedsGLContextAttach(false)
         , mUpdateTexImage(false)
@@ -41,7 +40,6 @@
     mColorFilter = SkSafeRef(mLayer->getColorFilter());
     mAlpha = mLayer->getAlpha();
     mMode = mLayer->getMode();
-    mDirtyRect.setEmpty();
 
     if (!mDestroyer) {
         mDestroyer = defaultLayerDestroyer;
@@ -60,37 +58,13 @@
     SkRefCnt_SafeAssign(mColorFilter, colorFilter);
 }
 
-void DeferredLayerUpdater::setDisplayList(RenderNode* displayList,
-        int left, int top, int right, int bottom) {
-    mDisplayList = displayList;
-    if (mDirtyRect.isEmpty()) {
-        mDirtyRect.set(left, top, right, bottom);
-    } else {
-        mDirtyRect.unionWith(Rect(left, top, right, bottom));
-    }
-}
-
 bool DeferredLayerUpdater::apply(TreeInfo& info) {
     bool success = true;
     // These properties are applied the same to both layer types
     mLayer->setColorFilter(mColorFilter);
     mLayer->setAlpha(mAlpha, mMode);
 
-    if (mDisplayList.get()) {
-        if (mWidth != mLayer->layer.getWidth() || mHeight != mLayer->layer.getHeight()) {
-            success = LayerRenderer::resizeLayer(mLayer, mWidth, mHeight);
-        }
-        mLayer->setBlend(mBlend);
-        // TODO: Use DamageAccumulator to get the damage area for the layer's
-        // subtree to only update that part of the layer. Do this as part of
-        // reworking layers to be a RenderProperty instead of a View-managed object
-        mDirtyRect.set(0, 0, mWidth, mHeight);
-        mDisplayList->prepareTree(info);
-        mLayer->updateDeferred(mDisplayList.get(),
-                mDirtyRect.left, mDirtyRect.top, mDirtyRect.right, mDirtyRect.bottom);
-        mDirtyRect.setEmpty();
-        mDisplayList = 0;
-    } else if (mSurfaceTexture.get()) {
+    if (mSurfaceTexture.get()) {
         if (mNeedsGLContextAttach) {
             mNeedsGLContextAttach = false;
             mSurfaceTexture->attachToContext(mLayer->getTexture());
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index b7cfe80..5082271 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -74,9 +74,6 @@
         mTransform = matrix ? new SkMatrix(*matrix) : 0;
     }
 
-    ANDROID_API void setDisplayList(RenderNode* displayList,
-                int left, int top, int right, int bottom);
-
     ANDROID_API void setPaint(const SkPaint* paint);
 
     ANDROID_API bool apply(TreeInfo& info);
@@ -94,11 +91,6 @@
     int mAlpha;
     SkXfermode::Mode mMode;
 
-    // Layer type specific properties
-    // displayList and surfaceTexture are mutually exclusive, only 1 may be set
-    // dirtyRect is only valid if displayList is set
-    sp<RenderNode> mDisplayList;
-    Rect mDirtyRect;
     sp<GLConsumer> mSurfaceTexture;
     SkMatrix* mTransform;
     bool mNeedsGLContextAttach;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 83ad76f..378183a 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -30,6 +30,8 @@
 #include "Debug.h"
 #include "DisplayListOp.h"
 #include "DisplayListLogBuffer.h"
+#include "LayerRenderer.h"
+#include "OpenGLRenderer.h"
 #include "utils/MathUtils.h"
 
 namespace android {
@@ -59,12 +61,14 @@
         , mNeedsDisplayListDataSync(false)
         , mDisplayListData(0)
         , mStagingDisplayListData(0)
-        , mNeedsAnimatorsSync(false) {
+        , mNeedsAnimatorsSync(false)
+        , mLayer(0) {
 }
 
 RenderNode::~RenderNode() {
     delete mDisplayListData;
     delete mStagingDisplayListData;
+    LayerRenderer::destroyLayerDeferred(mLayer);
 }
 
 void RenderNode::setStagingDisplayList(DisplayListData* data) {
@@ -124,17 +128,72 @@
     }
 }
 
+void RenderNode::prepareLayer(TreeInfo& info) {
+    LayerType layerType = properties().layerProperties().type();
+    if (CC_UNLIKELY(layerType == kLayerTypeRenderLayer)) {
+        // We push a null transform here as we don't care what the existing dirty
+        // area is, only what our display list dirty is as well as our children's
+        // dirty area
+        info.damageAccumulator->pushNullTransform();
+    }
+}
+
+void RenderNode::pushLayerUpdate(TreeInfo& info) {
+    LayerType layerType = properties().layerProperties().type();
+    // If we are not a layer OR we cannot be rendered (eg, view was detached)
+    // we need to destroy any Layers we may have had previously
+    if (CC_LIKELY(layerType != kLayerTypeRenderLayer) || CC_UNLIKELY(!isRenderable())) {
+        if (layerType == kLayerTypeRenderLayer) {
+            info.damageAccumulator->popTransform();
+        }
+        if (CC_UNLIKELY(mLayer)) {
+            LayerRenderer::destroyLayer(mLayer);
+            mLayer = NULL;
+        }
+        return;
+    }
+
+    if (!mLayer) {
+        mLayer = LayerRenderer::createRenderLayer(getWidth(), getHeight());
+        applyLayerPropertiesToLayer(info);
+        damageSelf(info);
+    } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
+        LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight());
+        damageSelf(info);
+    }
+
+    SkRect dirty;
+    info.damageAccumulator->peekAtDirty(&dirty);
+    info.damageAccumulator->popTransform();
+
+    if (!dirty.isEmpty()) {
+        mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
+    }
+    // This is not inside the above if because we may have called
+    // updateDeferred on a previous prepare pass that didn't have a renderer
+    if (info.renderer && mLayer->deferredUpdateScheduled) {
+        info.renderer->pushLayerUpdate(mLayer);
+    }
+}
+
 void RenderNode::prepareTreeImpl(TreeInfo& info) {
     info.damageAccumulator->pushTransform(this);
     if (info.mode == TreeInfo::MODE_FULL) {
-        pushStagingChanges(info);
+        pushStagingPropertiesChanges(info);
         evaluateAnimations(info);
     } else if (info.mode == TreeInfo::MODE_MAYBE_DETACHING) {
-        pushStagingChanges(info);
+        pushStagingPropertiesChanges(info);
     } else if (info.mode == TreeInfo::MODE_RT_ONLY) {
         evaluateAnimations(info);
     }
+
+    prepareLayer(info);
+    if (info.mode == TreeInfo::MODE_FULL) {
+        pushStagingDisplayListChanges(info);
+    }
     prepareSubTree(info, mDisplayListData);
+    pushLayerUpdate(info);
+
     info.damageAccumulator->popTransform();
 }
 
@@ -152,7 +211,7 @@
     TreeInfo& mInfo;
 };
 
-void RenderNode::pushStagingChanges(TreeInfo& info) {
+void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
     // Push the animators first so that setupStartValueIfNecessary() is called
     // before properties() is trampled by stagingProperties(), as they are
     // required by some animators.
@@ -170,6 +229,7 @@
         damageSelf(info);
         info.damageAccumulator->popTransform();
         mProperties = mStagingProperties;
+        applyLayerPropertiesToLayer(info);
         // We could try to be clever and only re-damage if the matrix changed.
         // However, we don't need to worry about that. The cost of over-damaging
         // here is only going to be a single additional map rect of this node
@@ -178,6 +238,18 @@
         info.damageAccumulator->pushTransform(this);
         damageSelf(info);
     }
+}
+
+void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
+    if (CC_LIKELY(!mLayer)) return;
+
+    const LayerProperties& props = properties().layerProperties();
+    mLayer->setAlpha(props.alpha(), props.xferMode());
+    mLayer->setColorFilter(props.colorFilter());
+    mLayer->setBlend(props.needsBlending());
+}
+
+void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
     if (mNeedsDisplayListDataSync) {
         mNeedsDisplayListDataSync = false;
         // Do a push pass on the old tree to handle freeing DisplayListData
@@ -274,9 +346,10 @@
             renderer.concatMatrix(*properties().getTransformMatrix());
         }
     }
-    bool clipToBoundsNeeded = properties().getCaching() ? false : properties().getClipToBounds();
+    const bool isLayer = properties().layerProperties().type() != kLayerTypeNone;
+    bool clipToBoundsNeeded = isLayer ? false : properties().getClipToBounds();
     if (properties().getAlpha() < 1) {
-        if (properties().getCaching()) {
+        if (isLayer) {
             renderer.setOverrideLayerAlpha(properties().getAlpha());
         } else if (!properties().getHasOverlappingRendering()) {
             renderer.scaleAlpha(properties().getAlpha());
@@ -691,8 +764,14 @@
  */
 template <class T>
 void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
+    const bool drawLayer = (mLayer && (&renderer != mLayer->renderer));
+    // If we are updating the contents of mLayer, we don't want to apply any of
+    // the RenderNode's properties to this issueOperations pass. Those will all
+    // be applied when the layer is drawn, aka when this is true.
+    const bool useViewProperties = (!mLayer || drawLayer);
+
     const int level = handler.level();
-    if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) {
+    if (mDisplayListData->isEmpty() || (useViewProperties && properties().getAlpha() <= 0)) {
         DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
         return;
     }
@@ -714,7 +793,9 @@
     DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
 
-    setViewProperties<T>(renderer, handler);
+    if (useViewProperties) {
+        setViewProperties<T>(renderer, handler);
+    }
 
     bool quickRejected = properties().getClipToBounds()
             && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
@@ -723,31 +804,36 @@
             renderer.setClippingOutline(alloc, &(mProperties.getOutline()));
         }
 
-        Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
-        buildZSortedChildList(zTranslatedNodes);
+        if (drawLayer) {
+            handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
+                    renderer.getSaveCount() - 1, properties().getClipToBounds());
+        } else {
+            Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
+            buildZSortedChildList(zTranslatedNodes);
 
-        // for 3d root, draw children with negative z values
-        issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
+            // for 3d root, draw children with negative z values
+            issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
 
-        DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
-        const int saveCountOffset = renderer.getSaveCount() - 1;
-        const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
-        for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
-            DisplayListOp *op = mDisplayListData->displayListOps[i];
+            DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+            const int saveCountOffset = renderer.getSaveCount() - 1;
+            const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
+            for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+                DisplayListOp *op = mDisplayListData->displayListOps[i];
 
-#if DEBUG_DISPLAY_LIST
-            op->output(level + 1);
-#endif
-            logBuffer.writeCommand(level, op->name());
-            handler(op, saveCountOffset, properties().getClipToBounds());
+    #if DEBUG_DISPLAY_LIST
+                op->output(level + 1);
+    #endif
+                logBuffer.writeCommand(level, op->name());
+                handler(op, saveCountOffset, properties().getClipToBounds());
 
-            if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
-                issueOperationsOfProjectedChildren(renderer, handler);
+                if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
+                    issueOperationsOfProjectedChildren(renderer, handler);
+                }
             }
-        }
 
-        // for 3d root, draw children with positive z values
-        issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+            // for 3d root, draw children with positive z values
+            issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+        }
     }
 
     DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index f0f6e7c..b2fe849 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -254,9 +254,13 @@
     };
 
     void prepareTreeImpl(TreeInfo& info);
-    void pushStagingChanges(TreeInfo& info);
+    void pushStagingPropertiesChanges(TreeInfo& info);
+    void pushStagingDisplayListChanges(TreeInfo& info);
     void evaluateAnimations(TreeInfo& info);
     void prepareSubTree(TreeInfo& info, DisplayListData* subtree);
+    void applyLayerPropertiesToLayer(TreeInfo& info);
+    void prepareLayer(TreeInfo& info);
+    void pushLayerUpdate(TreeInfo& info);
 
     String8 mName;
 
@@ -272,6 +276,10 @@
     std::set< sp<BaseRenderNodeAnimator> > mStagingAnimators;
     std::vector< sp<BaseRenderNodeAnimator> > mAnimators;
 
+    // Owned by RT. Lifecycle is managed by prepareTree(), with the exception
+    // being in ~RenderNode() which may happen on any thread.
+    Layer* mLayer;
+
     /**
      * Draw time state - these properties are only set and used during rendering
      */
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 5f7d4e3..8848b2f 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -21,16 +21,59 @@
 #include <utils/Trace.h>
 
 #include <SkCanvas.h>
+#include <SkColorFilter.h>
 #include <SkMatrix.h>
 #include <SkPath.h>
 #include <SkPathOps.h>
 
 #include "Matrix.h"
+#include "OpenGLRenderer.h"
 #include "utils/MathUtils.h"
 
 namespace android {
 namespace uirenderer {
 
+LayerProperties::LayerProperties()
+        : mType(kLayerTypeNone)
+        , mColorFilter(NULL) {
+    reset();
+}
+
+LayerProperties::~LayerProperties() {
+    setType(kLayerTypeNone);
+}
+
+void LayerProperties::reset() {
+    mOpaque = false;
+    setFromPaint(NULL);
+}
+
+bool LayerProperties::setColorFilter(SkColorFilter* filter) {
+   if (mColorFilter == filter) return false;
+   SkRefCnt_SafeAssign(mColorFilter, filter);
+   return true;
+}
+
+bool LayerProperties::setFromPaint(const SkPaint* paint) {
+    bool changed = false;
+    SkXfermode::Mode mode;
+    int alpha;
+    OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+    changed |= setAlpha(static_cast<uint8_t>(alpha));
+    changed |= setXferMode(mode);
+    changed |= setColorFilter(paint ? paint->getColorFilter() : NULL);
+    return changed;
+}
+
+LayerProperties& LayerProperties::operator=(const LayerProperties& other) {
+    setType(other.type());
+    setOpaque(other.opaque());
+    setAlpha(other.alpha());
+    setXferMode(other.xferMode());
+    setColorFilter(other.colorFilter());
+    return *this;
+}
+
 RenderProperties::PrimitiveFields::PrimitiveFields()
         : mClipToBounds(true)
         , mProjectBackwards(false)
@@ -45,8 +88,7 @@
         , mLeft(0), mTop(0), mRight(0), mBottom(0)
         , mWidth(0), mHeight(0)
         , mPivotExplicitlySet(false)
-        , mMatrixOrPivotDirty(false)
-        , mCaching(false) {
+        , mMatrixOrPivotDirty(false) {
 }
 
 RenderProperties::ComputedFields::ComputedFields()
@@ -73,6 +115,7 @@
         setStaticMatrix(other.getStaticMatrix());
         setAnimationMatrix(other.getAnimationMatrix());
         setCameraDistance(other.getCameraDistance());
+        mLayerProperties = other.layerProperties();
 
         // Force recalculation of the matrix, since other's dirty bit may be clear
         mPrimitiveFields.mMatrixOrPivotDirty = true;
@@ -103,9 +146,9 @@
         }
     }
 
-    bool clipToBoundsNeeded = mPrimitiveFields.mCaching ? false : mPrimitiveFields.mClipToBounds;
+    bool clipToBoundsNeeded = layerProperties().type() != kLayerTypeNone ? false : mPrimitiveFields.mClipToBounds;
     if (mPrimitiveFields.mAlpha < 1) {
-        if (mPrimitiveFields.mCaching) {
+        if (layerProperties().type() != kLayerTypeNone) {
             ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha);
         } else if (!mPrimitiveFields.mHasOverlappingRendering) {
             ALOGD("%*sScaleAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha);
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index b012fc5..8c6cc9e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -32,6 +32,7 @@
 #include "Outline.h"
 
 class SkBitmap;
+class SkColorFilter;
 class SkPaint;
 
 namespace android {
@@ -39,15 +40,95 @@
 
 class Matrix4;
 class RenderNode;
+class RenderProperties;
 
 // The __VA_ARGS__ will be executed if a & b are not equal
 #define RP_SET(a, b, ...) (a != b ? (a = b, ##__VA_ARGS__, true) : false)
 #define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true)
 
+// Keep in sync with View.java:LAYER_TYPE_*
+enum LayerType {
+    kLayerTypeNone = 0,
+    // Although we cannot build the software layer directly (must be done at
+    // record time), this information is used when applying alpha.
+    kLayerTypeSoftware = 1,
+    kLayerTypeRenderLayer = 2,
+    // TODO: LayerTypeSurfaceTexture? Maybe?
+};
+
+class ANDROID_API LayerProperties {
+public:
+    bool setType(LayerType type) {
+        if (RP_SET(mType, type)) {
+            reset();
+            return true;
+        }
+        return false;
+    }
+
+    LayerType type() const {
+        return mType;
+    }
+
+    bool setOpaque(bool opaque) {
+        return RP_SET(mOpaque, opaque);
+    }
+
+    bool opaque() const {
+        return mOpaque;
+    }
+
+    bool setAlpha(uint8_t alpha) {
+        return RP_SET(mAlpha, alpha);
+    }
+
+    uint8_t alpha() const {
+        return mAlpha;
+    }
+
+    bool setXferMode(SkXfermode::Mode mode) {
+        return RP_SET(mMode, mode);
+    }
+
+    SkXfermode::Mode xferMode() const {
+        return mMode;
+    }
+
+    bool setColorFilter(SkColorFilter* filter);
+
+    SkColorFilter* colorFilter() const {
+        return mColorFilter;
+    }
+
+    // Sets alpha, xfermode, and colorfilter from an SkPaint
+    // paint may be NULL, in which case defaults will be set
+    bool setFromPaint(const SkPaint* paint);
+
+    bool needsBlending() const {
+        return !opaque() || alpha() < 255;
+    }
+
+    LayerProperties& operator=(const LayerProperties& other);
+
+private:
+    LayerProperties();
+    ~LayerProperties();
+    void reset();
+
+    friend class RenderProperties;
+
+    LayerType mType;
+    // Whether or not that Layer's content is opaque, doesn't include alpha
+    bool mOpaque;
+    uint8_t mAlpha;
+    SkXfermode::Mode mMode;
+    SkColorFilter* mColorFilter;
+};
+
 /*
  * Data structure that holds the properties for a RenderNode
  */
-class RenderProperties {
+class ANDROID_API RenderProperties {
 public:
     RenderProperties();
     virtual ~RenderProperties();
@@ -366,10 +447,6 @@
         return false;
     }
 
-    bool setCaching(bool caching) {
-        return RP_SET(mPrimitiveFields.mCaching, caching);
-    }
-
     int getWidth() const {
         return mPrimitiveFields.mWidth;
     }
@@ -396,10 +473,6 @@
         return mComputedFields.mTransformMatrix;
     }
 
-    bool getCaching() const {
-        return mPrimitiveFields.mCaching;
-    }
-
     bool getClipToBounds() const {
         return mPrimitiveFields.mClipToBounds;
     }
@@ -422,7 +495,7 @@
 
     void debugOutputProperties(const int level) const;
 
-    ANDROID_API void updateMatrix();
+    void updateMatrix();
 
     bool hasClippingPath() const {
         return mPrimitiveFields.mRevealClip.willClip();
@@ -445,6 +518,14 @@
         return mPrimitiveFields.mRevealClip;
     }
 
+    const LayerProperties& layerProperties() const {
+        return mLayerProperties;
+    }
+
+    LayerProperties& mutateLayerProperties() {
+        return mLayerProperties;
+    }
+
 private:
 
     // Rendering properties
@@ -467,11 +548,11 @@
         int mWidth, mHeight;
         bool mPivotExplicitlySet;
         bool mMatrixOrPivotDirty;
-        bool mCaching;
     } mPrimitiveFields;
 
     SkMatrix* mStaticMatrix;
     SkMatrix* mAnimationMatrix;
+    LayerProperties mLayerProperties;
 
     /**
      * These fields are all generated from other properties and are not set directly.
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index fd78f8e..0fc0cef 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -26,6 +26,7 @@
 
 class BaseRenderNodeAnimator;
 class AnimationListener;
+class OpenGLRenderer;
 
 class AnimationHook {
 public:
@@ -63,6 +64,7 @@
         , animationHook(NULL)
         , prepareTextures(mode == MODE_FULL)
         , damageAccumulator(NullDamageAccumulator::instance())
+        , renderer(0)
     {}
 
     const TraversalMode mode;
@@ -73,6 +75,9 @@
     bool prepareTextures;
     // Must not be null
     IDamageAccumulator* damageAccumulator;
+    // The renderer that will be drawing the next frame. Use this to push any
+    // layer updates or similar. May be NULL.
+    OpenGLRenderer* renderer;
 
     struct Out {
         Out()
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 8a5c857..440f965 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -440,6 +440,7 @@
 
     info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
     info.damageAccumulator = &mDamageAccumulator;
+    info.renderer = mCanvas;
     mRootRenderNode->prepareTree(info);
 
     int runningBehind = 0;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index bdfdd21..797566f 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -130,9 +130,6 @@
         mContext->processLayerUpdate(mLayers[i].get(), info);
     }
     mLayers.clear();
-    if (info.out.hasAnimations) {
-        // TODO: Uh... crap?
-    }
     mContext->prepareTree(info);
 
     if (info.out.hasAnimations) {