Move RenderNode to own file

Change-Id: I9380d161fd3ddd7b569c262dd8e7aa0c96151b1e
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index aa6a035..e72aff9 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -42,13 +42,13 @@
 #include <SkTemplates.h>
 #include <SkXfermode.h>
 
-#include <DisplayList.h>
 #include <DisplayListRenderer.h>
 #include <LayerRenderer.h>
 #include <OpenGLRenderer.h>
 #include <SkiaShader.h>
 #include <Stencil.h>
 #include <Rect.h>
+#include <RenderNode.h>
 
 #include <TextLayout.h>
 #include <TextLayoutCache.h>
diff --git a/core/jni/android_view_GLRenderer.cpp b/core/jni/android_view_GLRenderer.cpp
index 228a92e..00d92fc 100644
--- a/core/jni/android_view_GLRenderer.cpp
+++ b/core/jni/android_view_GLRenderer.cpp
@@ -27,9 +27,9 @@
 #include <utils/Timers.h>
 
 #include <Caches.h>
-#include <DisplayList.h>
 #include <Extensions.h>
 #include <LayerRenderer.h>
+#include <RenderNode.h>
 
 #ifdef USE_OPENGL_RENDERER
     EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index ad2e9ff..4bf5f78 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -32,10 +32,10 @@
 #include <SkXfermode.h>
 
 #include <DeferredLayerUpdater.h>
-#include <DisplayList.h>
 #include <LayerRenderer.h>
 #include <SkiaShader.h>
 #include <Rect.h>
+#include <RenderNode.h>
 
 namespace android {
 
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 823f8d7..fa08a78 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -23,8 +23,8 @@
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 
-#include <DisplayList.h>
 #include <DisplayListRenderer.h>
+#include <RenderNode.h>
 
 namespace android {
 
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index eeff4c0..4de755d 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -38,6 +38,7 @@
 		Program.cpp \
 		ProgramCache.cpp \
 		RenderBufferCache.cpp \
+		RenderNode.cpp \
 		RenderProperties.cpp \
 		ResourceCache.cpp \
 		ShadowTessellator.cpp \
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index d124cde..cf745ee 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -22,10 +22,10 @@
 #include <SkMatrix.h>
 #include <utils/StrongPointer.h>
 
-#include "DisplayList.h"
 #include "Layer.h"
 #include "OpenGLRenderer.h"
 #include "Rect.h"
+#include "RenderNode.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index f4de8ec..9e6a96d 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -29,543 +29,6 @@
 namespace android {
 namespace uirenderer {
 
-void RenderNode::outputLogBuffer(int fd) {
-    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
-    if (logBuffer.isEmpty()) {
-        return;
-    }
-
-    FILE *file = fdopen(fd, "a");
-
-    fprintf(file, "\nRecent DisplayList operations\n");
-    logBuffer.outputCommands(file);
-
-    String8 cachesLog;
-    Caches::getInstance().dumpMemoryUsage(cachesLog);
-    fprintf(file, "\nCaches:\n%s", cachesLog.string());
-    fprintf(file, "\n");
-
-    fflush(file);
-}
-
-RenderNode::RenderNode() : mDestroyed(false), mDisplayListData(0) {
-}
-
-RenderNode::~RenderNode() {
-    LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
-
-    mDestroyed = true;
-    delete mDisplayListData;
-}
-
-void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) {
-    if (displayList) {
-        DISPLAY_LIST_LOGD("Deferring display list destruction");
-        Caches::getInstance().deleteDisplayListDeferred(displayList);
-    }
-}
-
-void RenderNode::setData(DisplayListData* data) {
-    delete mDisplayListData;
-    mDisplayListData = data;
-    if (mDisplayListData) {
-        Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
-    }
-}
-
-/**
- * This function is a simplified version of replay(), where we simply retrieve and log the
- * display list. This function should remain in sync with the replay() function.
- */
-void RenderNode::output(uint32_t level) {
-    ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
-            mName.string(), isRenderable());
-    ALOGD("%*s%s %d", level * 2, "", "Save",
-            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-
-    outputViewProperties(level);
-    int flags = DisplayListOp::kOpLogFlag_Recurse;
-    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
-        mDisplayListData->displayListOps[i]->output(level, flags);
-    }
-
-    ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
-}
-
-void RenderNode::outputViewProperties(const int level) {
-    properties().updateMatrix();
-    if (properties().mLeft != 0 || properties().mTop != 0) {
-        ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", properties().mLeft, properties().mTop);
-    }
-    if (properties().mStaticMatrix) {
-        ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
-                level * 2, "", properties().mStaticMatrix, SK_MATRIX_ARGS(properties().mStaticMatrix));
-    }
-    if (properties().mAnimationMatrix) {
-        ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
-                level * 2, "", properties().mAnimationMatrix, SK_MATRIX_ARGS(properties().mAnimationMatrix));
-    }
-    if (properties().mMatrixFlags != 0) {
-        if (properties().mMatrixFlags == TRANSLATION) {
-            ALOGD("%*sTranslate %.2f, %.2f, %.2f",
-                    level * 2, "", properties().mTranslationX, properties().mTranslationY, properties().mTranslationZ);
-        } else {
-            ALOGD("%*sConcatMatrix %p: " MATRIX_4_STRING,
-                    level * 2, "", properties().mTransformMatrix, MATRIX_4_ARGS(properties().mTransformMatrix));
-        }
-    }
-
-    bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
-    if (properties().mAlpha < 1) {
-        if (properties().mCaching) {
-            ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", properties().mAlpha);
-        } else if (!properties().mHasOverlappingRendering) {
-            ALOGD("%*sScaleAlpha %.2f", level * 2, "", properties().mAlpha);
-        } else {
-            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
-            if (clipToBoundsNeeded) {
-                flags |= SkCanvas::kClipToLayer_SaveFlag;
-                clipToBoundsNeeded = false; // clipping done by save layer
-            }
-            ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
-                    (float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop,
-                    (int)(properties().mAlpha * 255), flags);
-        }
-    }
-    if (clipToBoundsNeeded) {
-        ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
-                (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop);
-    }
-}
-
-/*
- * For property operations, we pass a savecount of 0, since the operations aren't part of the
- * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
- * base saveCount (i.e., how RestoreToCount uses saveCount + properties().mCount)
- */
-#define PROPERTY_SAVECOUNT 0
-
-template <class T>
-void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler,
-        const int level) {
-#if DEBUG_DISPLAY_LIST
-    outputViewProperties(level);
-#endif
-    properties().updateMatrix();
-    if (properties().mLeft != 0 || properties().mTop != 0) {
-        renderer.translate(properties().mLeft, properties().mTop);
-    }
-    if (properties().mStaticMatrix) {
-        renderer.concatMatrix(properties().mStaticMatrix);
-    } else if (properties().mAnimationMatrix) {
-        renderer.concatMatrix(properties().mAnimationMatrix);
-    }
-    if (properties().mMatrixFlags != 0) {
-        if (properties().mMatrixFlags == TRANSLATION) {
-            renderer.translate(properties().mTranslationX, properties().mTranslationY);
-        } else {
-            renderer.concatMatrix(*properties().mTransformMatrix);
-        }
-    }
-    bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
-    if (properties().mAlpha < 1) {
-        if (properties().mCaching) {
-            renderer.setOverrideLayerAlpha(properties().mAlpha);
-        } else if (!properties().mHasOverlappingRendering) {
-            renderer.scaleAlpha(properties().mAlpha);
-        } else {
-            // TODO: should be able to store the size of a DL at record time and not
-            // have to pass it into this call. In fact, this information might be in the
-            // location/size info that we store with the new native transform data.
-            int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
-            if (clipToBoundsNeeded) {
-                saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
-                clipToBoundsNeeded = false; // clipping done by saveLayer
-            }
-
-            SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
-                    0, 0, properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, properties().mAlpha * 255, saveFlags);
-            handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-        }
-    }
-    if (clipToBoundsNeeded) {
-        ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
-                properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op);
-        handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-    }
-    if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) {
-        ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op);
-        handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-    }
-}
-
-/**
- * Apply property-based transformations to input matrix
- *
- * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
- * matrix computation instead of the Skia 3x3 matrix + camera hackery.
- */
-void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
-    if (properties().mLeft != 0 || properties().mTop != 0) {
-        matrix.translate(properties().mLeft, properties().mTop);
-    }
-    if (properties().mStaticMatrix) {
-        mat4 stat(*properties().mStaticMatrix);
-        matrix.multiply(stat);
-    } else if (properties().mAnimationMatrix) {
-        mat4 anim(*properties().mAnimationMatrix);
-        matrix.multiply(anim);
-    }
-    if (properties().mMatrixFlags != 0) {
-        properties().updateMatrix();
-        if (properties().mMatrixFlags == TRANSLATION) {
-            matrix.translate(properties().mTranslationX, properties().mTranslationY,
-                    true3dTransform ? properties().mTranslationZ : 0.0f);
-        } else {
-            if (!true3dTransform) {
-                matrix.multiply(*properties().mTransformMatrix);
-            } else {
-                mat4 true3dMat;
-                true3dMat.loadTranslate(
-                        properties().mPivotX + properties().mTranslationX,
-                        properties().mPivotY + properties().mTranslationY,
-                        properties().mTranslationZ);
-                true3dMat.rotate(properties().mRotationX, 1, 0, 0);
-                true3dMat.rotate(properties().mRotationY, 0, 1, 0);
-                true3dMat.rotate(properties().mRotation, 0, 0, 1);
-                true3dMat.scale(properties().mScaleX, properties().mScaleY, 1);
-                true3dMat.translate(-properties().mPivotX, -properties().mPivotY);
-
-                matrix.multiply(true3dMat);
-            }
-        }
-    }
-}
-
-/**
- * Organizes the DisplayList hierarchy to prepare for background projection reordering.
- *
- * This should be called before a call to defer() or drawDisplayList()
- *
- * Each DisplayList that serves as a 3d root builds its list of composited children,
- * which are flagged to not draw in the standard draw loop.
- */
-void RenderNode::computeOrdering() {
-    ATRACE_CALL();
-    mProjectedNodes.clear();
-
-    // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
-    // transform properties are applied correctly to top level children
-    if (mDisplayListData == NULL) return;
-    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
-        DrawDisplayListOp* childOp = mDisplayListData->children[i];
-        childOp->mDisplayList->computeOrderingImpl(childOp,
-                &mProjectedNodes, &mat4::identity());
-    }
-}
-
-void RenderNode::computeOrderingImpl(
-        DrawDisplayListOp* opState,
-        Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
-        const mat4* transformFromProjectionSurface) {
-    mProjectedNodes.clear();
-    if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
-
-    // TODO: should avoid this calculation in most cases
-    // TODO: just calculate single matrix, down to all leaf composited elements
-    Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
-    localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
-
-    if (properties().mProjectBackwards) {
-        // composited projectee, flag for out of order draw, save matrix, and store in proj surface
-        opState->mSkipInOrderDraw = true;
-        opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
-        compositedChildrenOfProjectionSurface->add(opState);
-    } else {
-        // standard in order draw
-        opState->mSkipInOrderDraw = false;
-    }
-
-    if (mDisplayListData->children.size() > 0) {
-        const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
-        bool haveAppliedPropertiesToProjection = false;
-        for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
-            DrawDisplayListOp* childOp = mDisplayListData->children[i];
-            RenderNode* child = childOp->mDisplayList;
-
-            Vector<DrawDisplayListOp*>* projectionChildren = NULL;
-            const mat4* projectionTransform = NULL;
-            if (isProjectionReceiver && !child->properties().mProjectBackwards) {
-                // if receiving projections, collect projecting descendent
-
-                // Note that if a direct descendent is projecting backwards, we pass it's
-                // grandparent projection collection, since it shouldn't project onto it's
-                // parent, where it will already be drawing.
-                projectionChildren = &mProjectedNodes;
-                projectionTransform = &mat4::identity();
-            } else {
-                if (!haveAppliedPropertiesToProjection) {
-                    applyViewPropertyTransforms(localTransformFromProjectionSurface);
-                    haveAppliedPropertiesToProjection = true;
-                }
-                projectionChildren = compositedChildrenOfProjectionSurface;
-                projectionTransform = &localTransformFromProjectionSurface;
-            }
-            child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
-        }
-    }
-
-}
-
-class DeferOperationHandler {
-public:
-    DeferOperationHandler(DeferStateStruct& deferStruct, int level)
-        : mDeferStruct(deferStruct), mLevel(level) {}
-    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
-        operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
-    }
-    inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
-
-private:
-    DeferStateStruct& mDeferStruct;
-    const int mLevel;
-};
-
-void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
-    DeferOperationHandler handler(deferStruct, level);
-    iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
-}
-
-class ReplayOperationHandler {
-public:
-    ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
-        : mReplayStruct(replayStruct), mLevel(level) {}
-    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
-#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
-        properties().mReplayStruct.mRenderer.eventMark(operation->name());
-#endif
-        operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
-    }
-    inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
-
-private:
-    ReplayStateStruct& mReplayStruct;
-    const int mLevel;
-};
-
-void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
-    ReplayOperationHandler handler(replayStruct, level);
-
-    replayStruct.mRenderer.startMark(mName.string());
-    iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
-    replayStruct.mRenderer.endMark();
-
-    DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
-            replayStruct.mDrawGlStatus);
-}
-
-void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
-    if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
-
-    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
-        DrawDisplayListOp* childOp = mDisplayListData->children[i];
-        RenderNode* child = childOp->mDisplayList;
-        float childZ = child->properties().mTranslationZ;
-
-        if (childZ != 0.0f) {
-            zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
-            childOp->mSkipInOrderDraw = true;
-        } else if (!child->properties().mProjectBackwards) {
-            // regular, in order drawing DisplayList
-            childOp->mSkipInOrderDraw = false;
-        }
-    }
-
-    // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
-    std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
-}
-
-#define SHADOW_DELTA 0.1f
-
-template <class T>
-void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
-        ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
-    const int size = zTranslatedNodes.size();
-    if (size == 0
-            || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
-            || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
-        // no 3d children to draw
-        return;
-    }
-
-    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-    LinearAllocator& alloc = handler.allocator();
-    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
-            SkRegion::kIntersect_Op); // clip to 3d root bounds
-    handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
-    /**
-     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
-     * with very similar Z heights to draw together.
-     *
-     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
-     * underneath both, and neither's shadow is drawn on top of the other.
-     */
-    const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
-    size_t drawIndex, shadowIndex, endIndex;
-    if (mode == kNegativeZChildren) {
-        drawIndex = 0;
-        endIndex = nonNegativeIndex;
-        shadowIndex = endIndex; // draw no shadows
-    } else {
-        drawIndex = nonNegativeIndex;
-        endIndex = size;
-        shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
-    }
-    float lastCasterZ = 0.0f;
-    while (shadowIndex < endIndex || drawIndex < endIndex) {
-        if (shadowIndex < endIndex) {
-            DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
-            RenderNode* caster = casterOp->mDisplayList;
-            const float casterZ = zTranslatedNodes[shadowIndex].key;
-            // attempt to render the shadow if the caster about to be drawn is its caster,
-            // OR if its caster's Z value is similar to the previous potential caster
-            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
-
-                if (caster->properties().mAlpha > 0.0f) {
-                    mat4 shadowMatrixXY(casterOp->mTransformFromParent);
-                    caster->applyViewPropertyTransforms(shadowMatrixXY);
-
-                    // Z matrix needs actual 3d transformation, so mapped z values will be correct
-                    mat4 shadowMatrixZ(casterOp->mTransformFromParent);
-                    caster->applyViewPropertyTransforms(shadowMatrixZ, true);
-
-                    DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(
-                            shadowMatrixXY, shadowMatrixZ,
-                            caster->properties().mAlpha, &(caster->properties().mOutline),
-                            caster->properties().mWidth, caster->properties().mHeight);
-                    handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-                }
-
-                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
-                shadowIndex++;
-                continue;
-            }
-        }
-
-        // only the actual child DL draw needs to be in save/restore,
-        // since it modifies the renderer's matrix
-        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
-
-        DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
-        RenderNode* child = childOp->mDisplayList;
-
-        renderer.concatMatrix(childOp->mTransformFromParent);
-        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
-        handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
-        childOp->mSkipInOrderDraw = true;
-
-        renderer.restoreToCount(restoreTo);
-        drawIndex++;
-    }
-    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
-}
-
-template <class T>
-void RenderNode::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
-    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-    LinearAllocator& alloc = handler.allocator();
-    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
-            SkRegion::kReplace_Op); // clip to projection surface root bounds
-    handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
-    for (size_t i = 0; i < mProjectedNodes.size(); i++) {
-        DrawDisplayListOp* childOp = mProjectedNodes[i];
-
-        // matrix save, concat, and restore can be done safely without allocating operations
-        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
-        renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
-        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
-        handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
-        childOp->mSkipInOrderDraw = true;
-        renderer.restoreToCount(restoreTo);
-    }
-    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
-}
-
-/**
- * This function serves both defer and replay modes, and will organize the displayList's component
- * operations for a single frame:
- *
- * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
- * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
- * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
- * defer vs replay logic, per operation
- */
-template <class T>
-void RenderNode::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
-    if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
-        ALOGW("Error: %s is drawing after destruction", mName.string());
-        CRASH();
-    }
-    if (mDisplayListData->isEmpty() || properties().mAlpha <= 0) {
-        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
-        return;
-    }
-
-#if DEBUG_DISPLAY_LIST
-    Rect* clipRect = renderer.getClipRect();
-    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
-            level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
-            clipRect->right, clipRect->bottom);
-#endif
-
-    LinearAllocator& alloc = handler.allocator();
-    int restoreTo = renderer.getSaveCount();
-    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
-            PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
-    DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
-            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
-
-    setViewProperties<T>(renderer, handler, level + 1);
-
-    bool quickRejected = properties().mClipToBounds && renderer.quickRejectConservative(0, 0, properties().mWidth, properties().mHeight);
-    if (!quickRejected) {
-        Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
-        buildZSortedChildList(zTranslatedNodes);
-
-        // for 3d root, draw children with negative z values
-        iterate3dChildren(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];
-
-#if DEBUG_DISPLAY_LIST
-            op->output(level + 1);
-#endif
-
-            logBuffer.writeCommand(level, op->name());
-            handler(op, saveCountOffset, properties().mClipToBounds);
-
-            if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
-                iterateProjectedChildren(renderer, handler, level);
-            }
-        }
-
-        // for 3d root, draw children with positive z values
-        iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
-    }
-
-    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
-    handler(new (alloc) RestoreToCountOp(restoreTo),
-            PROPERTY_SAVECOUNT, properties().mClipToBounds);
-    renderer.setOverrideLayerAlpha(1.0f);
-}
-
 void DisplayListData::cleanupResources() {
     Caches& caches = Caches::getInstance();
     caches.unregisterFunctors(functorCount);
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index b80c118..df5cba6 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -143,136 +143,6 @@
     void cleanupResources();
 };
 
-/**
- * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
- *
- * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
- * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData
- * (which holds the actual data), and DisplayList (which holds properties and performs playback onto
- * a renderer).
- *
- * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's
- * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
- * attached.
- */
-class RenderNode {
-public:
-    ANDROID_API RenderNode();
-    ANDROID_API ~RenderNode();
-
-    // See flags defined in DisplayList.java
-    enum ReplayFlag {
-        kReplayFlag_ClipChildren = 0x1
-    };
-
-    ANDROID_API static void destroyDisplayListDeferred(RenderNode* displayList);
-    ANDROID_API static void outputLogBuffer(int fd);
-
-    ANDROID_API void setData(DisplayListData* newData);
-
-    void computeOrdering();
-    void defer(DeferStateStruct& deferStruct, const int level);
-    void replay(ReplayStateStruct& replayStruct, const int level);
-
-    ANDROID_API void output(uint32_t level = 1);
-
-    bool isRenderable() const {
-        return mDisplayListData && mDisplayListData->hasDrawOps;
-    }
-
-    void setName(const char* name) {
-        if (name) {
-            char* lastPeriod = strrchr(name, '.');
-            if (lastPeriod) {
-                mName.setTo(lastPeriod + 1);
-            } else {
-                mName.setTo(name);
-            }
-        }
-    }
-
-    RenderProperties& properties() {
-        return mProperties;
-    }
-
-    bool isProjectionReceiver() {
-        return properties().isProjectionReceiver();
-    }
-
-    int getWidth() {
-        return properties().getWidth();
-    }
-
-    int getHeight() {
-        return properties().getHeight();
-    }
-
-private:
-    typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
-
-    static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) {
-        for (size_t i = 0; i < nodes.size(); i++) {
-            if (nodes[i].key >= 0.0f) return i;
-        }
-        return nodes.size();
-    }
-
-    enum ChildrenSelectMode {
-        kNegativeZChildren,
-        kPositiveZChildren
-    };
-
-    void outputViewProperties(const int level);
-
-    void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
-
-    void computeOrderingImpl(DrawDisplayListOp* opState,
-            Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
-            const mat4* transformFromProjectionSurface);
-
-    template <class T>
-    inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
-
-    void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
-
-    template <class T>
-    inline void iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
-            ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
-
-    template <class T>
-    inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level);
-
-    template <class T>
-    inline void iterate(OpenGLRenderer& renderer, T& handler, const int level);
-
-    class TextContainer {
-    public:
-        size_t length() const {
-            return mByteLength;
-        }
-
-        const char* text() const {
-            return (const char*) mText;
-        }
-
-        size_t mByteLength;
-        const char* mText;
-    };
-
-    String8 mName;
-    bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed
-
-    RenderProperties mProperties;
-    DisplayListData* mDisplayListData;
-
-    /**
-     * Draw time state - these properties are only set and used during rendering
-     */
-
-    // for projection surfaces, contains a list of all children items
-    Vector<DrawDisplayListOp*> mProjectedNodes;
-}; // class DisplayList
-
 }; // namespace uirenderer
 }; // namespace android
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index e69e08e..78c97e1 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -21,12 +21,12 @@
 
 #include <private/hwui/DrawGlInfo.h>
 
-#include "DisplayList.h"
+#include "Caches.h"
 #include "DeferredDisplayList.h"
 #include "DisplayListLogBuffer.h"
 #include "DisplayListOp.h"
 #include "DisplayListRenderer.h"
-#include "Caches.h"
+#include "RenderNode.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 65498a5..04c5a73 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -22,9 +22,9 @@
 #include <SkPath.h>
 #include <cutils/compiler.h>
 
-#include "DisplayList.h"
 #include "DisplayListLogBuffer.h"
 #include "OpenGLRenderer.h"
+#include "RenderNode.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 52176d4..bd9bfe9 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -18,12 +18,12 @@
 
 #include <utils/Log.h>
 
-#include "DisplayList.h"
+#include "Caches.h"
 #include "DeferredDisplayList.h"
 #include "Layer.h"
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
-#include "Caches.h"
+#include "RenderNode.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
new file mode 100644
index 0000000..e371590
--- /dev/null
+++ b/libs/hwui/RenderNode.cpp
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include "RenderNode.h"
+
+#include <SkCanvas.h>
+#include <algorithm>
+
+#include <utils/Trace.h>
+
+#include "Debug.h"
+#include "DisplayListOp.h"
+#include "DisplayListLogBuffer.h"
+
+namespace android {
+namespace uirenderer {
+
+void RenderNode::outputLogBuffer(int fd) {
+    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+    if (logBuffer.isEmpty()) {
+        return;
+    }
+
+    FILE *file = fdopen(fd, "a");
+
+    fprintf(file, "\nRecent DisplayList operations\n");
+    logBuffer.outputCommands(file);
+
+    String8 cachesLog;
+    Caches::getInstance().dumpMemoryUsage(cachesLog);
+    fprintf(file, "\nCaches:\n%s", cachesLog.string());
+    fprintf(file, "\n");
+
+    fflush(file);
+}
+
+RenderNode::RenderNode() : mDestroyed(false), mDisplayListData(0) {
+}
+
+RenderNode::~RenderNode() {
+    LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
+
+    mDestroyed = true;
+    delete mDisplayListData;
+}
+
+void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) {
+    if (displayList) {
+        DISPLAY_LIST_LOGD("Deferring display list destruction");
+        Caches::getInstance().deleteDisplayListDeferred(displayList);
+    }
+}
+
+void RenderNode::setData(DisplayListData* data) {
+    delete mDisplayListData;
+    mDisplayListData = data;
+    if (mDisplayListData) {
+        Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
+    }
+}
+
+/**
+ * This function is a simplified version of replay(), where we simply retrieve and log the
+ * display list. This function should remain in sync with the replay() function.
+ */
+void RenderNode::output(uint32_t level) {
+    ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
+            mName.string(), isRenderable());
+    ALOGD("%*s%s %d", level * 2, "", "Save",
+            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+
+    outputViewProperties(level);
+    int flags = DisplayListOp::kOpLogFlag_Recurse;
+    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+        mDisplayListData->displayListOps[i]->output(level, flags);
+    }
+
+    ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
+}
+
+void RenderNode::outputViewProperties(const int level) {
+    properties().updateMatrix();
+    if (properties().mLeft != 0 || properties().mTop != 0) {
+        ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", properties().mLeft, properties().mTop);
+    }
+    if (properties().mStaticMatrix) {
+        ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
+                level * 2, "", properties().mStaticMatrix, SK_MATRIX_ARGS(properties().mStaticMatrix));
+    }
+    if (properties().mAnimationMatrix) {
+        ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
+                level * 2, "", properties().mAnimationMatrix, SK_MATRIX_ARGS(properties().mAnimationMatrix));
+    }
+    if (properties().mMatrixFlags != 0) {
+        if (properties().mMatrixFlags == TRANSLATION) {
+            ALOGD("%*sTranslate %.2f, %.2f, %.2f",
+                    level * 2, "", properties().mTranslationX, properties().mTranslationY, properties().mTranslationZ);
+        } else {
+            ALOGD("%*sConcatMatrix %p: " MATRIX_4_STRING,
+                    level * 2, "", properties().mTransformMatrix, MATRIX_4_ARGS(properties().mTransformMatrix));
+        }
+    }
+
+    bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
+    if (properties().mAlpha < 1) {
+        if (properties().mCaching) {
+            ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", properties().mAlpha);
+        } else if (!properties().mHasOverlappingRendering) {
+            ALOGD("%*sScaleAlpha %.2f", level * 2, "", properties().mAlpha);
+        } else {
+            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
+            if (clipToBoundsNeeded) {
+                flags |= SkCanvas::kClipToLayer_SaveFlag;
+                clipToBoundsNeeded = false; // clipping done by save layer
+            }
+            ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
+                    (float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop,
+                    (int)(properties().mAlpha * 255), flags);
+        }
+    }
+    if (clipToBoundsNeeded) {
+        ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
+                (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop);
+    }
+}
+
+/*
+ * For property operations, we pass a savecount of 0, since the operations aren't part of the
+ * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
+ * base saveCount (i.e., how RestoreToCount uses saveCount + properties().mCount)
+ */
+#define PROPERTY_SAVECOUNT 0
+
+template <class T>
+void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler,
+        const int level) {
+#if DEBUG_DISPLAY_LIST
+    outputViewProperties(level);
+#endif
+    properties().updateMatrix();
+    if (properties().mLeft != 0 || properties().mTop != 0) {
+        renderer.translate(properties().mLeft, properties().mTop);
+    }
+    if (properties().mStaticMatrix) {
+        renderer.concatMatrix(properties().mStaticMatrix);
+    } else if (properties().mAnimationMatrix) {
+        renderer.concatMatrix(properties().mAnimationMatrix);
+    }
+    if (properties().mMatrixFlags != 0) {
+        if (properties().mMatrixFlags == TRANSLATION) {
+            renderer.translate(properties().mTranslationX, properties().mTranslationY);
+        } else {
+            renderer.concatMatrix(*properties().mTransformMatrix);
+        }
+    }
+    bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
+    if (properties().mAlpha < 1) {
+        if (properties().mCaching) {
+            renderer.setOverrideLayerAlpha(properties().mAlpha);
+        } else if (!properties().mHasOverlappingRendering) {
+            renderer.scaleAlpha(properties().mAlpha);
+        } else {
+            // TODO: should be able to store the size of a DL at record time and not
+            // have to pass it into this call. In fact, this information might be in the
+            // location/size info that we store with the new native transform data.
+            int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
+            if (clipToBoundsNeeded) {
+                saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
+                clipToBoundsNeeded = false; // clipping done by saveLayer
+            }
+
+            SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
+                    0, 0, properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, properties().mAlpha * 255, saveFlags);
+            handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+        }
+    }
+    if (clipToBoundsNeeded) {
+        ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
+                properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op);
+        handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+    }
+    if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) {
+        ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op);
+        handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+    }
+}
+
+/**
+ * Apply property-based transformations to input matrix
+ *
+ * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
+ * matrix computation instead of the Skia 3x3 matrix + camera hackery.
+ */
+void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
+    if (properties().mLeft != 0 || properties().mTop != 0) {
+        matrix.translate(properties().mLeft, properties().mTop);
+    }
+    if (properties().mStaticMatrix) {
+        mat4 stat(*properties().mStaticMatrix);
+        matrix.multiply(stat);
+    } else if (properties().mAnimationMatrix) {
+        mat4 anim(*properties().mAnimationMatrix);
+        matrix.multiply(anim);
+    }
+    if (properties().mMatrixFlags != 0) {
+        properties().updateMatrix();
+        if (properties().mMatrixFlags == TRANSLATION) {
+            matrix.translate(properties().mTranslationX, properties().mTranslationY,
+                    true3dTransform ? properties().mTranslationZ : 0.0f);
+        } else {
+            if (!true3dTransform) {
+                matrix.multiply(*properties().mTransformMatrix);
+            } else {
+                mat4 true3dMat;
+                true3dMat.loadTranslate(
+                        properties().mPivotX + properties().mTranslationX,
+                        properties().mPivotY + properties().mTranslationY,
+                        properties().mTranslationZ);
+                true3dMat.rotate(properties().mRotationX, 1, 0, 0);
+                true3dMat.rotate(properties().mRotationY, 0, 1, 0);
+                true3dMat.rotate(properties().mRotation, 0, 0, 1);
+                true3dMat.scale(properties().mScaleX, properties().mScaleY, 1);
+                true3dMat.translate(-properties().mPivotX, -properties().mPivotY);
+
+                matrix.multiply(true3dMat);
+            }
+        }
+    }
+}
+
+/**
+ * Organizes the DisplayList hierarchy to prepare for background projection reordering.
+ *
+ * This should be called before a call to defer() or drawDisplayList()
+ *
+ * Each DisplayList that serves as a 3d root builds its list of composited children,
+ * which are flagged to not draw in the standard draw loop.
+ */
+void RenderNode::computeOrdering() {
+    ATRACE_CALL();
+    mProjectedNodes.clear();
+
+    // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
+    // transform properties are applied correctly to top level children
+    if (mDisplayListData == NULL) return;
+    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+        DrawDisplayListOp* childOp = mDisplayListData->children[i];
+        childOp->mDisplayList->computeOrderingImpl(childOp,
+                &mProjectedNodes, &mat4::identity());
+    }
+}
+
+void RenderNode::computeOrderingImpl(
+        DrawDisplayListOp* opState,
+        Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+        const mat4* transformFromProjectionSurface) {
+    mProjectedNodes.clear();
+    if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
+
+    // TODO: should avoid this calculation in most cases
+    // TODO: just calculate single matrix, down to all leaf composited elements
+    Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
+    localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
+
+    if (properties().mProjectBackwards) {
+        // composited projectee, flag for out of order draw, save matrix, and store in proj surface
+        opState->mSkipInOrderDraw = true;
+        opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
+        compositedChildrenOfProjectionSurface->add(opState);
+    } else {
+        // standard in order draw
+        opState->mSkipInOrderDraw = false;
+    }
+
+    if (mDisplayListData->children.size() > 0) {
+        const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
+        bool haveAppliedPropertiesToProjection = false;
+        for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+            DrawDisplayListOp* childOp = mDisplayListData->children[i];
+            RenderNode* child = childOp->mDisplayList;
+
+            Vector<DrawDisplayListOp*>* projectionChildren = NULL;
+            const mat4* projectionTransform = NULL;
+            if (isProjectionReceiver && !child->properties().mProjectBackwards) {
+                // if receiving projections, collect projecting descendent
+
+                // Note that if a direct descendent is projecting backwards, we pass it's
+                // grandparent projection collection, since it shouldn't project onto it's
+                // parent, where it will already be drawing.
+                projectionChildren = &mProjectedNodes;
+                projectionTransform = &mat4::identity();
+            } else {
+                if (!haveAppliedPropertiesToProjection) {
+                    applyViewPropertyTransforms(localTransformFromProjectionSurface);
+                    haveAppliedPropertiesToProjection = true;
+                }
+                projectionChildren = compositedChildrenOfProjectionSurface;
+                projectionTransform = &localTransformFromProjectionSurface;
+            }
+            child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
+        }
+    }
+
+}
+
+class DeferOperationHandler {
+public:
+    DeferOperationHandler(DeferStateStruct& deferStruct, int level)
+        : mDeferStruct(deferStruct), mLevel(level) {}
+    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
+        operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
+    }
+    inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
+
+private:
+    DeferStateStruct& mDeferStruct;
+    const int mLevel;
+};
+
+void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
+    DeferOperationHandler handler(deferStruct, level);
+    iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
+}
+
+class ReplayOperationHandler {
+public:
+    ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
+        : mReplayStruct(replayStruct), mLevel(level) {}
+    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
+#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
+        properties().mReplayStruct.mRenderer.eventMark(operation->name());
+#endif
+        operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
+    }
+    inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
+
+private:
+    ReplayStateStruct& mReplayStruct;
+    const int mLevel;
+};
+
+void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
+    ReplayOperationHandler handler(replayStruct, level);
+
+    replayStruct.mRenderer.startMark(mName.string());
+    iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
+    replayStruct.mRenderer.endMark();
+
+    DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
+            replayStruct.mDrawGlStatus);
+}
+
+void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
+    if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
+
+    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+        DrawDisplayListOp* childOp = mDisplayListData->children[i];
+        RenderNode* child = childOp->mDisplayList;
+        float childZ = child->properties().mTranslationZ;
+
+        if (childZ != 0.0f) {
+            zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
+            childOp->mSkipInOrderDraw = true;
+        } else if (!child->properties().mProjectBackwards) {
+            // regular, in order drawing DisplayList
+            childOp->mSkipInOrderDraw = false;
+        }
+    }
+
+    // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
+    std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
+}
+
+#define SHADOW_DELTA 0.1f
+
+template <class T>
+void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+        ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
+    const int size = zTranslatedNodes.size();
+    if (size == 0
+            || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
+            || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
+        // no 3d children to draw
+        return;
+    }
+
+    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    LinearAllocator& alloc = handler.allocator();
+    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
+            SkRegion::kIntersect_Op); // clip to 3d root bounds
+    handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+    /**
+     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+     * with very similar Z heights to draw together.
+     *
+     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
+     * underneath both, and neither's shadow is drawn on top of the other.
+     */
+    const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
+    size_t drawIndex, shadowIndex, endIndex;
+    if (mode == kNegativeZChildren) {
+        drawIndex = 0;
+        endIndex = nonNegativeIndex;
+        shadowIndex = endIndex; // draw no shadows
+    } else {
+        drawIndex = nonNegativeIndex;
+        endIndex = size;
+        shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
+    }
+    float lastCasterZ = 0.0f;
+    while (shadowIndex < endIndex || drawIndex < endIndex) {
+        if (shadowIndex < endIndex) {
+            DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
+            RenderNode* caster = casterOp->mDisplayList;
+            const float casterZ = zTranslatedNodes[shadowIndex].key;
+            // attempt to render the shadow if the caster about to be drawn is its caster,
+            // OR if its caster's Z value is similar to the previous potential caster
+            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
+
+                if (caster->properties().mAlpha > 0.0f) {
+                    mat4 shadowMatrixXY(casterOp->mTransformFromParent);
+                    caster->applyViewPropertyTransforms(shadowMatrixXY);
+
+                    // Z matrix needs actual 3d transformation, so mapped z values will be correct
+                    mat4 shadowMatrixZ(casterOp->mTransformFromParent);
+                    caster->applyViewPropertyTransforms(shadowMatrixZ, true);
+
+                    DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(
+                            shadowMatrixXY, shadowMatrixZ,
+                            caster->properties().mAlpha, &(caster->properties().mOutline),
+                            caster->properties().mWidth, caster->properties().mHeight);
+                    handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+                }
+
+                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+                shadowIndex++;
+                continue;
+            }
+        }
+
+        // only the actual child DL draw needs to be in save/restore,
+        // since it modifies the renderer's matrix
+        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+
+        DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
+        RenderNode* child = childOp->mDisplayList;
+
+        renderer.concatMatrix(childOp->mTransformFromParent);
+        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
+        handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
+        childOp->mSkipInOrderDraw = true;
+
+        renderer.restoreToCount(restoreTo);
+        drawIndex++;
+    }
+    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
+}
+
+template <class T>
+void RenderNode::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
+    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    LinearAllocator& alloc = handler.allocator();
+    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
+            SkRegion::kReplace_Op); // clip to projection surface root bounds
+    handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+    for (size_t i = 0; i < mProjectedNodes.size(); i++) {
+        DrawDisplayListOp* childOp = mProjectedNodes[i];
+
+        // matrix save, concat, and restore can be done safely without allocating operations
+        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+        renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
+        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
+        handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
+        childOp->mSkipInOrderDraw = true;
+        renderer.restoreToCount(restoreTo);
+    }
+    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
+}
+
+/**
+ * This function serves both defer and replay modes, and will organize the displayList's component
+ * operations for a single frame:
+ *
+ * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
+ * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
+ * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
+ * defer vs replay logic, per operation
+ */
+template <class T>
+void RenderNode::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
+    if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
+        ALOGW("Error: %s is drawing after destruction", mName.string());
+        CRASH();
+    }
+    if (mDisplayListData->isEmpty() || properties().mAlpha <= 0) {
+        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
+        return;
+    }
+
+#if DEBUG_DISPLAY_LIST
+    Rect* clipRect = renderer.getClipRect();
+    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
+            level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
+            clipRect->right, clipRect->bottom);
+#endif
+
+    LinearAllocator& alloc = handler.allocator();
+    int restoreTo = renderer.getSaveCount();
+    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+            PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+    DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
+            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
+
+    setViewProperties<T>(renderer, handler, level + 1);
+
+    bool quickRejected = properties().mClipToBounds && renderer.quickRejectConservative(0, 0, properties().mWidth, properties().mHeight);
+    if (!quickRejected) {
+        Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
+        buildZSortedChildList(zTranslatedNodes);
+
+        // for 3d root, draw children with negative z values
+        iterate3dChildren(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];
+
+#if DEBUG_DISPLAY_LIST
+            op->output(level + 1);
+#endif
+
+            logBuffer.writeCommand(level, op->name());
+            handler(op, saveCountOffset, properties().mClipToBounds);
+
+            if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
+                iterateProjectedChildren(renderer, handler, level);
+            }
+        }
+
+        // for 3d root, draw children with positive z values
+        iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+    }
+
+    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
+    handler(new (alloc) RestoreToCountOp(restoreTo),
+            PROPERTY_SAVECOUNT, properties().mClipToBounds);
+    renderer.setOverrideLayerAlpha(1.0f);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
new file mode 100644
index 0000000..177f33e
--- /dev/null
+++ b/libs/hwui/RenderNode.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef RENDERNODE_H
+#define RENDERNODE_H
+
+#ifndef LOG_TAG
+    #define LOG_TAG "OpenGLRenderer"
+#endif
+
+#include <SkCamera.h>
+#include <SkMatrix.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
+#include <utils/KeyedVector.h>
+#include <utils/LinearAllocator.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <cutils/compiler.h>
+
+#include <androidfw/ResourceTypes.h>
+
+#include "Debug.h"
+#include "Matrix.h"
+#include "DeferredDisplayList.h"
+#include "DisplayList.h"
+#include "RenderProperties.h"
+
+class SkBitmap;
+class SkPaint;
+class SkPath;
+class SkRegion;
+
+namespace android {
+namespace uirenderer {
+
+class DeferredDisplayList;
+class DisplayListOp;
+class DisplayListRenderer;
+class OpenGLRenderer;
+class Rect;
+class Layer;
+class SkiaShader;
+
+class ClipRectOp;
+class SaveLayerOp;
+class SaveOp;
+class RestoreToCountOp;
+class DrawDisplayListOp;
+
+/**
+ * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
+ *
+ * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
+ * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData
+ * (which holds the actual data), and DisplayList (which holds properties and performs playback onto
+ * a renderer).
+ *
+ * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's
+ * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
+ * attached.
+ */
+class RenderNode {
+public:
+    ANDROID_API RenderNode();
+    ANDROID_API ~RenderNode();
+
+    // See flags defined in DisplayList.java
+    enum ReplayFlag {
+        kReplayFlag_ClipChildren = 0x1
+    };
+
+    ANDROID_API static void destroyDisplayListDeferred(RenderNode* displayList);
+    ANDROID_API static void outputLogBuffer(int fd);
+
+    ANDROID_API void setData(DisplayListData* newData);
+
+    void computeOrdering();
+    void defer(DeferStateStruct& deferStruct, const int level);
+    void replay(ReplayStateStruct& replayStruct, const int level);
+
+    ANDROID_API void output(uint32_t level = 1);
+
+    bool isRenderable() const {
+        return mDisplayListData && mDisplayListData->hasDrawOps;
+    }
+
+    void setName(const char* name) {
+        if (name) {
+            char* lastPeriod = strrchr(name, '.');
+            if (lastPeriod) {
+                mName.setTo(lastPeriod + 1);
+            } else {
+                mName.setTo(name);
+            }
+        }
+    }
+
+    RenderProperties& properties() {
+        return mProperties;
+    }
+
+    bool isProjectionReceiver() {
+        return properties().isProjectionReceiver();
+    }
+
+    int getWidth() {
+        return properties().getWidth();
+    }
+
+    int getHeight() {
+        return properties().getHeight();
+    }
+
+private:
+    typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
+
+    static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) {
+        for (size_t i = 0; i < nodes.size(); i++) {
+            if (nodes[i].key >= 0.0f) return i;
+        }
+        return nodes.size();
+    }
+
+    enum ChildrenSelectMode {
+        kNegativeZChildren,
+        kPositiveZChildren
+    };
+
+    void outputViewProperties(const int level);
+
+    void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
+
+    void computeOrderingImpl(DrawDisplayListOp* opState,
+            Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+            const mat4* transformFromProjectionSurface);
+
+    template <class T>
+    inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
+
+    void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
+
+    template <class T>
+    inline void iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+            ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
+
+    template <class T>
+    inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level);
+
+    template <class T>
+    inline void iterate(OpenGLRenderer& renderer, T& handler, const int level);
+
+    class TextContainer {
+    public:
+        size_t length() const {
+            return mByteLength;
+        }
+
+        const char* text() const {
+            return (const char*) mText;
+        }
+
+        size_t mByteLength;
+        const char* mText;
+    };
+
+    String8 mName;
+    bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed
+
+    RenderProperties mProperties;
+    DisplayListData* mDisplayListData;
+
+    /**
+     * Draw time state - these properties are only set and used during rendering
+     */
+
+    // for projection surfaces, contains a list of all children items
+    Vector<DrawDisplayListOp*> mProjectedNodes;
+}; // class RenderNode
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* RENDERNODE_H */