Move LayerType to RenderNode

Change-Id: Icb79a5015cb0362b1f3a66d09007450730135a97
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) {