Initial refactoring to enable the addition of the SkiaOpenGLPipeline.

Test: existing and new HWUI unit tests all pass.
Change-Id: I4f5c1dc839a2ed15d8b0f6245fe030684501b083
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 81a1831..a13cdde 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -95,6 +95,7 @@
     ShadowTessellator.cpp \
     SkiaCanvas.cpp \
     SkiaCanvasProxy.cpp \
+    SkiaDisplayList.cpp \
     SkiaShader.cpp \
     Snapshot.cpp \
     SpotShadow.cpp \
@@ -280,6 +281,7 @@
     tests/unit/RenderNodeTests.cpp \
     tests/unit/RenderPropertiesTests.cpp \
     tests/unit/SkiaBehaviorTests.cpp \
+    tests/unit/SkiaDisplayListTests.cpp \
     tests/unit/SkiaCanvasTests.cpp \
     tests/unit/SnapshotTests.cpp \
     tests/unit/StringUtilsTests.cpp \
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index ca9e2bd..6e7d11f 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -19,10 +19,12 @@
 
 #include <utils/Trace.h>
 
+#include "DamageAccumulator.h"
 #include "Debug.h"
 #include "DisplayList.h"
 #include "RecordedOp.h"
 #include "RenderNode.h"
+#include "VectorDrawable.h"
 
 namespace android {
 namespace uirenderer {
@@ -86,5 +88,46 @@
     return index;
 }
 
+void DisplayList::syncContents() {
+    for (auto& iter : functors) {
+        (*iter.functor)(DrawGlInfo::kModeSync, nullptr);
+    }
+    for (auto& vectorDrawable : vectorDrawables) {
+        vectorDrawable->syncProperties();
+    }
+}
+
+void DisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) {
+    for (auto&& child : children) {
+        updateFn(child->renderNode);
+    }
+}
+
+bool DisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
+        std::function<void(RenderNode*, TreeInfo&, bool)> childFn) {
+    TextureCache& cache = Caches::getInstance().textureCache;
+    for (auto&& bitmapResource : bitmapResources) {
+        void* ownerToken = &info.canvasContext;
+        info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource);
+    }
+    for (auto&& op : children) {
+        RenderNode* childNode = op->renderNode;
+        info.damageAccumulator->pushTransform(&op->localMatrix);
+        bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip;
+        childFn(childNode, info, childFunctorsNeedLayer);
+        info.damageAccumulator->popTransform();
+    }
+
+    bool isDirty = false;
+    for (auto& vectorDrawable : vectorDrawables) {
+        // If any vector drawable in the display list needs update, damage the node.
+        if (vectorDrawable->isDirty()) {
+            isDirty = true;
+        }
+        vectorDrawable->setPropertyChangeWillBeConsumed(true);
+    }
+    return isDirty;
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index c5d8767..06b0891 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <SkCamera.h>
+#include <SkDrawable.h>
 #include <SkMatrix.h>
 
 #include <private/hwui/DrawGlInfo.h>
@@ -36,6 +37,7 @@
 #include "GlFunctorLifecycleListener.h"
 #include "Matrix.h"
 #include "RenderProperties.h"
+#include "TreeInfo.h"
 
 #include <vector>
 
@@ -89,7 +91,7 @@
     };
 
     DisplayList();
-    ~DisplayList();
+    virtual ~DisplayList();
 
     // index of DisplayListOp restore, after which projected descendants should be drawn
     int projectionReceiveIndex;
@@ -100,8 +102,6 @@
     const LsaVector<NodeOpType*>& getChildren() const { return children; }
 
     const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
-    const LsaVector<FunctorContainer>& getFunctors() const { return functors; }
-    const LsaVector<VectorDrawableRoot*>& getVectorDrawables() const { return vectorDrawables; }
 
     size_t addChild(NodeOpType* childOp);
 
@@ -113,15 +113,26 @@
     size_t getUsedSize() {
         return allocator.usedSize();
     }
-    bool isEmpty() {
-        return ops.empty();
+
+    virtual bool isEmpty() const { return ops.empty(); }
+    virtual bool hasFunctor() const { return !functors.empty(); }
+    virtual bool hasVectorDrawables() const { return !vectorDrawables.empty(); }
+    virtual bool isSkiaDL() const { return false; }
+    virtual bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) {
+        return false;
     }
 
-private:
+    virtual void syncContents();
+    virtual void updateChildren(std::function<void(RenderNode*)> updateFn);
+    virtual bool prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeInfo&, bool)> childFn);
+
+protected:
     // allocator into which all ops and LsaVector arrays allocated
     LinearAllocator allocator;
     LinearStdAllocator<void*> stdAllocator;
 
+private:
     LsaVector<Chunk> chunks;
     LsaVector<BaseOpType*> ops;
 
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 93b2e15..848161e 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -214,7 +214,7 @@
     property_get(PROPERTY_DEFAULT_RENDERER, prop, "opengl");
     if (!strcmp(prop, "skiagl") ) {
         sRenderPipelineType = RenderPipelineType::SkiaGL;
-    } else if (!strcmp(prop, "skiavulkan") ) {
+    } else if (!strcmp(prop, "skiavk") ) {
         sRenderPipelineType = RenderPipelineType::SkiaVulkan;
     } else { //"opengl"
         sRenderPipelineType = RenderPipelineType::OpenGL;
@@ -222,5 +222,11 @@
     return sRenderPipelineType;
 }
 
+bool Properties::isSkiaEnabled() {
+    auto renderType = getRenderPipelineType();
+    return RenderPipelineType::SkiaGL == renderType
+            || RenderPipelineType::SkiaVulkan == renderType;
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index eedc9e7..c4486a4 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -308,6 +308,7 @@
 
     static ProfileType getProfileType();
     static RenderPipelineType getRenderPipelineType();
+    static bool isSkiaEnabled();
 
     // Should be used only by test apps
     static bool waitForGpuCompletion;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 8c36ab5..a03ded6 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -51,7 +51,7 @@
 RenderNode::~RenderNode() {
     deleteDisplayList(nullptr);
     delete mStagingDisplayList;
-    LOG_ALWAYS_FATAL_IF(mLayer, "layer missed detachment!");
+    LOG_ALWAYS_FATAL_IF(hasLayer(), "layer missed detachment!");
 }
 
 void RenderNode::setStagingDisplayList(DisplayList* displayList, TreeObserver* observer) {
@@ -81,7 +81,7 @@
             << (properties().hasShadow() ? ", casting shadow" : "")
             << (isRenderable() ? "" : ", empty")
             << (properties().getProjectBackwards() ? ", projected" : "")
-            << (mLayer != nullptr ? ", on HW Layer" : "")
+            << (hasLayer() ? ", on HW Layer" : "")
             << ")" << std::endl;
 
     properties().debugOutputProperties(output, level + 1);
@@ -237,7 +237,7 @@
             || CC_UNLIKELY(!isRenderable())
             || CC_UNLIKELY(properties().getWidth() == 0)
             || CC_UNLIKELY(properties().getHeight() == 0)) {
-        if (CC_UNLIKELY(mLayer)) {
+        if (CC_UNLIKELY(hasLayer())) {
             renderthread::CanvasContext::destroyLayer(this);
         }
         return;
@@ -247,7 +247,7 @@
         damageSelf(info);
     }
 
-    if (!mLayer) {
+    if (!hasLayer()) {
         Caches::getInstance().dumpMemoryUsage();
         if (info.errorHandler) {
             std::ostringstream err;
@@ -295,9 +295,9 @@
 
     bool willHaveFunctor = false;
     if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) {
-        willHaveFunctor = !mStagingDisplayList->getFunctors().empty();
+        willHaveFunctor = mStagingDisplayList->hasFunctor();
     } else if (mDisplayList) {
-        willHaveFunctor = !mDisplayList->getFunctors().empty();
+        willHaveFunctor = mDisplayList->hasFunctor();
     }
     bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
             willHaveFunctor, functorsNeedLayer);
@@ -310,15 +310,15 @@
     if (info.mode == TreeInfo::MODE_FULL) {
         pushStagingDisplayListChanges(info);
     }
-    prepareSubTree(info, childFunctorsNeedLayer, mDisplayList);
 
     if (mDisplayList) {
-        for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) {
-            // If any vector drawable in the display list needs update, damage the node.
-            if (vectorDrawable->isDirty()) {
-                damageSelf(info);
-            }
-            vectorDrawable->setPropertyChangeWillBeConsumed(true);
+        info.out.hasFunctors |= mDisplayList->hasFunctor();
+        bool isDirty = mDisplayList->prepareListAndChildren(info, childFunctorsNeedLayer,
+                [](RenderNode* child, TreeInfo& info, bool functorsNeedLayer) {
+            child->prepareTreeImpl(info, functorsNeedLayer);
+        });
+        if (isDirty) {
+            damageSelf(info);
         }
     }
     pushLayerUpdate(info);
@@ -356,20 +356,15 @@
     // Make sure we inc first so that we don't fluctuate between 0 and 1,
     // which would thrash the layer cache
     if (mStagingDisplayList) {
-        for (auto&& child : mStagingDisplayList->getChildren()) {
-            child->renderNode->incParentRefCount();
-        }
+        mStagingDisplayList->updateChildren([](RenderNode* child) {
+            child->incParentRefCount();
+        });
     }
     deleteDisplayList(info ? info->observer : nullptr, info);
     mDisplayList = mStagingDisplayList;
     mStagingDisplayList = nullptr;
     if (mDisplayList) {
-        for (auto& iter : mDisplayList->getFunctors()) {
-            (*iter.functor)(DrawGlInfo::kModeSync, nullptr);
-        }
-        for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) {
-            vectorDrawable->syncProperties();
-        }
+        mDisplayList->syncContents();
     }
 }
 
@@ -386,40 +381,24 @@
 
 void RenderNode::deleteDisplayList(TreeObserver* observer, TreeInfo* info) {
     if (mDisplayList) {
-        for (auto&& child : mDisplayList->getChildren()) {
-            child->renderNode->decParentRefCount(observer, info);
+        mDisplayList->updateChildren([observer, info](RenderNode* child) {
+            child->decParentRefCount(observer, info);
+        });
+        if (!mDisplayList->reuseDisplayList(this, info ? &info->canvasContext : nullptr)) {
+            delete mDisplayList;
         }
     }
-    delete mDisplayList;
     mDisplayList = nullptr;
 }
 
-void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree) {
-    if (subtree) {
-        TextureCache& cache = Caches::getInstance().textureCache;
-        info.out.hasFunctors |= subtree->getFunctors().size();
-        for (auto&& bitmapResource : subtree->getBitmapResources()) {
-            void* ownerToken = &info.canvasContext;
-            info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource);
-        }
-        for (auto&& op : subtree->getChildren()) {
-            RenderNode* childNode = op->renderNode;
-            info.damageAccumulator->pushTransform(&op->localMatrix);
-            bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip;
-            childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
-            info.damageAccumulator->popTransform();
-        }
-    }
-}
-
 void RenderNode::destroyHardwareResources(TreeObserver* observer, TreeInfo* info) {
-    if (mLayer) {
+    if (hasLayer()) {
         renderthread::CanvasContext::destroyLayer(this);
     }
     if (mDisplayList) {
-        for (auto&& child : mDisplayList->getChildren()) {
-            child->renderNode->destroyHardwareResources(observer, info);
-        }
+        mDisplayList->updateChildren([observer, info](RenderNode* child) {
+            child->destroyHardwareResources(observer, info);
+        });
         if (mNeedsDisplayListSync) {
             // Next prepare tree we are going to push a new display list, so we can
             // drop our current one now
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index da93c13..a05c744 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -32,6 +32,7 @@
 #include "DisplayList.h"
 #include "Matrix.h"
 #include "RenderProperties.h"
+#include "SkiaDisplayList.h"
 
 #include <vector>
 
@@ -39,6 +40,7 @@
 class SkPaint;
 class SkPath;
 class SkRegion;
+class SkSurface;
 
 namespace android {
 namespace uirenderer {
@@ -48,6 +50,7 @@
 class FrameBuilder;
 class OffscreenBuffer;
 class Rect;
+class SkiaDisplayList;
 class SkiaShader;
 struct RenderNodeOp;
 
@@ -240,7 +243,6 @@
     void prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer);
     void pushStagingPropertiesChanges(TreeInfo& info);
     void pushStagingDisplayListChanges(TreeInfo& info);
-    void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree);
     void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
     void pushLayerUpdate(TreeInfo& info);
     void deleteDisplayList(TreeObserver* observer, TreeInfo* info = nullptr);
@@ -285,6 +287,63 @@
     uint32_t mParentCount;
 
     sp<PositionListener> mPositionListener;
+
+// METHODS & FIELDS ONLY USED BY THE SKIA RENDERER
+public:
+    /**
+     * Detach and transfer ownership of an already allocated displayList for use
+     * in recording updated content for this renderNode
+     */
+    std::unique_ptr<SkiaDisplayList> detachAvailableList() {
+        return std::move(mAvailableDisplayList);
+    }
+
+    /**
+     * Attach unused displayList to this node for potential future reuse.
+     */
+    void attachAvailableList(SkiaDisplayList* skiaDisplayList) {
+        mAvailableDisplayList.reset(skiaDisplayList);
+    }
+
+    /**
+     * Returns true if an offscreen layer from any renderPipeline is attached
+     * to this node.
+     */
+    bool hasLayer() const { return mLayer || mLayerSurface.get(); }
+
+    /**
+     * Used by the RenderPipeline to attach an offscreen surface to the RenderNode.
+     * The surface is then will be used to store the contents of a layer.
+     */
+    void setLayerSurface(sk_sp<SkSurface> layer) { mLayerSurface = layer; }
+
+
+    /**
+     * If the RenderNode is of type LayerType::RenderLayer then this method will
+     * return the an offscreen rendering surface that is used to both render into
+     * the layer and composite the layer into its parent.  If the type is not
+     * LayerType::RenderLayer then it will return a nullptr.
+     *
+     * NOTE: this function is only guaranteed to return accurate results after
+     *       prepareTree has been run for this RenderNode
+     */
+    SkSurface* getLayerSurface() const { return mLayerSurface.get(); }
+
+private:
+    /**
+     * If this RenderNode has been used in a previous frame then the SkiaDisplayList
+     * from that frame is cached here until one of the following conditions is met:
+     *  1) The RenderNode is deleted (causing this to be deleted)
+     *  2) It is replaced with the displayList from the next completed frame
+     *  3) It is detached and used to to record a new displayList for a later frame
+     */
+    std::unique_ptr<SkiaDisplayList> mAvailableDisplayList;
+
+    /**
+     * An offscreen rendering target used to contain the contents this RenderNode
+     * when it has been set to draw as a LayerType::RenderLayer.
+     */
+    sk_sp<SkSurface> mLayerSurface;
 }; // class RenderNode
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/SkiaDisplayList.cpp b/libs/hwui/SkiaDisplayList.cpp
new file mode 100644
index 0000000..d10f306
--- /dev/null
+++ b/libs/hwui/SkiaDisplayList.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "SkiaDisplayList.h"
+
+#include "renderthread/CanvasContext.h"
+#include "VectorDrawable.h"
+
+#include <SkImagePriv.h>
+#include <SkMutex.h>
+
+namespace android {
+namespace uirenderer {
+
+SkiaDisplayList::SkiaDisplayList(SkRect bounds) : mDrawable(SkLiteDL::New(bounds)) {
+    SkASSERT(projectionReceiveIndex == -1);
+}
+
+void SkiaDisplayList::syncContents() {
+    for (auto& functor : mChildFunctors) {
+        functor.syncFunctor();
+    }
+    for (auto& vectorDrawable : mVectorDrawables) {
+        vectorDrawable->syncProperties();
+    }
+}
+
+bool SkiaDisplayList::reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) {
+    reset(context ? context->getGrContext() : nullptr, SkRect::MakeEmpty());
+    node->attachAvailableList(this);
+    return true;
+}
+
+void SkiaDisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) {
+    for (auto& child : mChildNodes) {
+        updateFn(child.getRenderNode());
+    }
+}
+
+bool SkiaDisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
+        std::function<void(RenderNode*, TreeInfo&, bool)> childFn) {
+    // force all mutable images to be pinned in the GPU cache for the duration
+    // of this frame
+    pinImages(info.canvasContext.getGrContext());
+
+    for (auto& child : mChildNodes) {
+        RenderNode* childNode = child.getRenderNode();
+        Matrix4 mat4(child.getRecordedMatrix());
+        info.damageAccumulator->pushTransform(&mat4);
+        // TODO: a layer is needed if the canvas is rotated or has a non-rect clip
+        bool childFunctorsNeedLayer = functorsNeedLayer;
+        childFn(childNode, info, childFunctorsNeedLayer);
+        info.damageAccumulator->popTransform();
+    }
+
+    bool isDirty = false;
+    for (auto& vectorDrawable : mVectorDrawables) {
+        // If any vector drawable in the display list needs update, damage the node.
+        if (vectorDrawable->isDirty()) {
+            isDirty = true;
+        }
+        vectorDrawable->setPropertyChangeWillBeConsumed(true);
+    }
+    return isDirty;
+}
+
+static std::vector<sk_sp<SkImage>> gPinnedImages;
+static SkBaseMutex gLock;
+
+void SkiaDisplayList::pinImages(GrContext* context) {
+    if (mPinnedImages) return;
+    for (SkImage* image : mMutableImages) {
+        SkImage_pinAsTexture(image, context);
+    }
+    mPinnedImages = true;
+}
+
+void SkiaDisplayList::unpinImages(GrContext* context) {
+    if (!mPinnedImages) return;
+    if (context) {
+        for (SkImage* image : mMutableImages) {
+            SkImage_unpinAsTexture(image, context);
+        }
+    } else {
+        gLock.acquire();
+        for (SkImage* image : mMutableImages) {
+            gPinnedImages.emplace_back(sk_ref_sp(image));
+        }
+        gLock.release();
+    }
+    mPinnedImages = false;
+}
+
+void SkiaDisplayList::cleanupImages(GrContext* context) {
+    gLock.acquire();
+    for (auto& image : gPinnedImages) {
+        SkImage_unpinAsTexture(image.get(), context);
+    }
+    gPinnedImages.clear();
+    gLock.release();
+}
+
+void SkiaDisplayList::reset(GrContext* context, SkRect bounds) {
+    unpinImages(context);
+    SkASSERT(!mPinnedImages);
+    mIsProjectionReceiver = false;
+
+    mDrawable->reset(bounds);
+
+    mMutableImages.clear();
+    mVectorDrawables.clear();
+    mChildFunctors.clear();
+    mChildNodes.clear();
+
+    projectionReceiveIndex = -1;
+    allocator.~LinearAllocator();
+    new (&allocator) LinearAllocator();
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaDisplayList.h b/libs/hwui/SkiaDisplayList.h
new file mode 100644
index 0000000..c8a82bd
--- /dev/null
+++ b/libs/hwui/SkiaDisplayList.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "DisplayList.h"
+#include "SkiaDrawables.h"
+
+#include <deque>
+#include <SkLiteDL.h>
+#include <SkPictureRecorder.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * This class is intended to be self contained, but still subclasses from
+ * DisplayList to make it easier to support switching between the two at
+ * runtime.  The downside of this inheritance is that we pay for the overhead
+ * of the parent class construction/destruction without any real benefit.
+ */
+class SkiaDisplayList : public DisplayList {
+public:
+    SkiaDisplayList(SkRect bounds);
+    virtual ~SkiaDisplayList() {
+        /* Given that we are using a LinearStdAllocator to store some of the
+         * SkDrawable contents we must ensure that any other object that is
+         * holding a reference to those drawables is destroyed prior to their
+         * deletion.
+         */
+        mDrawable.reset();
+    }
+
+    /**
+     * This resets the DisplayList so that it behaves as if the object were newly
+     * constructed with the provided bounds.  The reuse avoids any overhead
+     * associated with destroying the SkLiteDL as well as the deques and vectors.
+     */
+    void reset(GrContext* context, SkRect bounds);
+
+    /**
+     * Use the linear allocator to create any SkDrawables needed by the display
+     * list. This could be dangerous as these objects are ref-counted, so we
+     * need to monitor that they don't extend beyond the lifetime of the class
+     * that creates them.
+     */
+    template<class T, typename... Params>
+    SkDrawable* allocateDrawable(Params&&... params) {
+        return allocator.create<T>(std::forward<Params>(params)...);
+    }
+
+    bool isSkiaDL() const override { return true; }
+
+    /**
+     * Returns true if the DisplayList does not have any recorded content
+     */
+    bool isEmpty() const override { return mDrawable->empty(); }
+
+    /**
+     * Returns true if this list directly contains a GLFunctor drawing command.
+     */
+    bool hasFunctor() const override { return !mChildFunctors.empty(); }
+
+    /**
+     * Returns true if this list directly contains a VectorDrawable drawing command.
+     */
+    bool hasVectorDrawables() const override { return !mVectorDrawables.empty(); }
+
+    /**
+     * Attempts to reset and reuse this DisplayList.
+     *
+     * @return true if the displayList will be reused and therefore should not be deleted
+     */
+    bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) override;
+
+    /**
+     * ONLY to be called by RenderNode::syncDisplayList so that we can notify any
+     * contained VectorDrawables or GLFunctors to sync their state.
+     *
+     * NOTE: This function can be folded into RenderNode when we no longer need
+     *       to subclass from DisplayList
+     */
+    void syncContents() override;
+
+    /**
+     * ONLY to be called by RenderNode::prepareTree in order to prepare this
+     * list while the UI thread is blocked.  Here we can upload mutable bitmaps
+     * and notify our parent if any of our content has been invalidated and in
+     * need of a redraw.  If the renderNode has any children then they are also
+     * call in order to prepare them.
+     *
+     * @return true if any content change requires the node to be invalidated
+     *
+     * NOTE: This function can be folded into RenderNode when we no longer need
+     *       to subclass from DisplayList
+     */
+
+    bool prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeInfo&, bool)> childFn) override;
+
+    /**
+     *  Calls the provided function once for each child of this DisplayList
+     */
+    void updateChildren(std::function<void(RenderNode*)> updateFn) override;
+
+    /**
+     * Pin/Unpin any mutable images to the GPU cache. A pinned images is
+     * guaranteed to be remain in the cache until it has been unpinned which
+     * we leverage to avoid making a CPU copy of the pixels.
+     */
+    void pinImages(GrContext* context);
+    void unpinImages(GrContext* context);
+
+    /**
+     * If a SkiaDisplayList is deleted on the UI thread we cache a list of any
+     * images that need unpinned from the GPU cache and call this function on
+     * a subsequent frame to perform that cleanup.
+     */
+    static void cleanupImages(GrContext* context);
+
+    /**
+     * We use std::deque here because (1) we need to iterate through these
+     * elements and (2) mDrawable holds pointers to the elements, so they cannot
+     * relocate.
+     */
+    std::deque<RenderNodeDrawable> mChildNodes;
+    std::deque<GLFunctorDrawable> mChildFunctors;
+    std::vector<SkImage*> mMutableImages;
+    std::vector<VectorDrawableRoot*> mVectorDrawables;
+    sk_sp<SkLiteDL> mDrawable;
+
+    bool mIsProjectionReceiver = false;
+
+private:
+    bool mPinnedImages = false;
+};
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaDrawables.h b/libs/hwui/SkiaDrawables.h
new file mode 100644
index 0000000..a1ceeaa
--- /dev/null
+++ b/libs/hwui/SkiaDrawables.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "Layer.h"
+#include "RenderNode.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+#include <SkMatrix.h>
+
+#include <utils/RefBase.h>
+#include <utils/FatVector.h>
+#include <utils/Functor.h>
+
+namespace android {
+
+class Functor;
+
+namespace uirenderer {
+
+
+class RenderProperties;
+class OffscreenBuffer;
+class GlFunctorLifecycleListener;
+class SkiaDisplayList;
+
+/**
+ * This drawable wraps a RenderNode and enables it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class RenderNodeDrawable : public SkDrawable {
+public:
+    explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas)
+            : mRenderNode(node)
+            , mRecordedTransform(canvas->getTotalMatrix()) {}
+
+    /**
+     * The renderNode (and its properties) that is to be drawn
+     */
+    RenderNode* getRenderNode() const { return mRenderNode.get(); }
+
+    /**
+     * Returns the transform on the canvas at time of recording and is used for
+     * computing total transform without rerunning DL contents.
+     */
+    const SkMatrix& getRecordedMatrix() const { return mRecordedTransform; }
+
+protected:
+    virtual SkRect onGetBounds() override {
+        // We don't want to enable a record time quick reject because the properties
+        // of the RenderNode may be updated on subsequent frames.
+        return SkRect::MakeLargest();
+    }
+    virtual void onDraw(SkCanvas* canvas) override { /* TODO */ }
+
+private:
+    sp<RenderNode> mRenderNode;
+    const SkMatrix mRecordedTransform;
+};
+
+/**
+ * This drawable wraps a OpenGL functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class GLFunctorDrawable : public SkDrawable {
+public:
+    GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+            : mFunctor(functor)
+            , mListener(listener) {
+        canvas->getClipBounds(&mBounds);
+    }
+    virtual ~GLFunctorDrawable() {}
+
+    void syncFunctor() const { (*mFunctor)(DrawGlInfo::kModeSync, nullptr); }
+
+ protected:
+    virtual SkRect onGetBounds() override { return mBounds; }
+    virtual void onDraw(SkCanvas* canvas) override { /* TODO */ }
+
+ private:
+     Functor* mFunctor;
+     sp<GlFunctorLifecycleListener> mListener;
+     SkRect mBounds;
+};
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 652cddd..44228f0 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -85,6 +85,12 @@
      */
     static void destroyLayer(RenderNode* node);
 
+    /*
+     * If Properties::isSkiaEnabled() is true then this will return the Skia
+     * grContext associated with the current RenderPipeline.
+     */
+    GrContext* getGrContext() const { return mRenderPipeline->getGrContext(); }
+
     // Won't take effect until next EGLSurface creation
     void setSwapBehavior(SwapBehavior swapBehavior);
 
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 97cdf7f..f96c2fd 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -22,6 +22,8 @@
 #include <SkRect.h>
 #include <utils/RefBase.h>
 
+class GrContext;
+
 namespace android {
 
 class Surface;
@@ -43,6 +45,8 @@
     Succeeded
 };
 
+class Frame;
+
 class IRenderPipeline {
 public:
     virtual MakeCurrentResult makeCurrent() = 0;
@@ -69,6 +73,7 @@
     virtual TaskManager* getTaskManager() = 0;
     virtual bool createOrUpdateLayer(RenderNode* node,
             const DamageAccumulator& damageAccumulator) = 0;
+    virtual GrContext* getGrContext() = 0;
 
     virtual ~IRenderPipeline() {}
 };
diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h
index 34d9bc0..d024aec 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.h
+++ b/libs/hwui/renderthread/OpenGLPipeline.h
@@ -26,9 +26,6 @@
 namespace uirenderer {
 namespace renderthread {
 
-class Frame;
-
-
 class OpenGLPipeline : public IRenderPipeline {
 public:
     OpenGLPipeline(RenderThread& thread);
@@ -59,6 +56,7 @@
     bool createOrUpdateLayer(RenderNode* node,
             const DamageAccumulator& damageAccumulator) override;
     static void destroyLayer(RenderNode* node);
+    GrContext* getGrContext() override { return nullptr; }
 
 private:
     EglManager& mEglManager;
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 0d90afa..39c7f32 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -160,7 +160,7 @@
 
     // Check that the VD is in the dislay list, and the layer update queue contains the correct
     // damage rect.
-    EXPECT_FALSE(rootNode->getDisplayList()->getVectorDrawables().empty());
+    EXPECT_TRUE(rootNode->getDisplayList()->hasVectorDrawables());
     EXPECT_FALSE(info.layerUpdateQueue->entries().empty());
     EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode);
     EXPECT_EQ(uirenderer::Rect(0, 0, 200, 400), info.layerUpdateQueue->entries().at(0).damage);
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
new file mode 100644
index 0000000..7f622eb
--- /dev/null
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <gtest/gtest.h>
+#include <VectorDrawable.h>
+
+#include "AnimationContext.h"
+#include "DamageAccumulator.h"
+#include "IContextFactory.h"
+#include "SkiaDisplayList.h"
+#include "renderthread/CanvasContext.h"
+#include "tests/common/TestUtils.h"
+
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+
+TEST(SkiaDisplayList, create) {
+    SkRect bounds = SkRect::MakeWH(200, 200);
+    SkiaDisplayList skiaDL(bounds);
+    ASSERT_TRUE(skiaDL.isEmpty());
+    ASSERT_FALSE(skiaDL.mIsProjectionReceiver);
+    ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
+}
+
+TEST(SkiaDisplayList, reset) {
+    SkRect bounds = SkRect::MakeWH(200, 200);
+    SkiaDisplayList skiaDL(bounds);
+
+    SkCanvas dummyCanvas;
+    skiaDL.mChildNodes.emplace_back(nullptr, &dummyCanvas);
+    skiaDL.mChildFunctors.emplace_back(nullptr, nullptr, &dummyCanvas);
+    skiaDL.mMutableImages.push_back(nullptr);
+    skiaDL.mVectorDrawables.push_back(nullptr);
+    skiaDL.mDrawable->drawAnnotation(bounds, "testAnnotation", nullptr);
+    skiaDL.mIsProjectionReceiver = true;
+
+    ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
+    ASSERT_FALSE(skiaDL.mChildNodes.empty());
+    ASSERT_FALSE(skiaDL.mChildFunctors.empty());
+    ASSERT_FALSE(skiaDL.mMutableImages.empty());
+    ASSERT_FALSE(skiaDL.mVectorDrawables.empty());
+    ASSERT_FALSE(skiaDL.isEmpty());
+    ASSERT_TRUE(skiaDL.mIsProjectionReceiver);
+
+    bounds = SkRect::MakeWH(100, 100);
+    skiaDL.reset(nullptr, bounds);
+
+    ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
+    ASSERT_TRUE(skiaDL.mChildNodes.empty());
+    ASSERT_TRUE(skiaDL.mChildFunctors.empty());
+    ASSERT_TRUE(skiaDL.mMutableImages.empty());
+    ASSERT_TRUE(skiaDL.mVectorDrawables.empty());
+    ASSERT_TRUE(skiaDL.isEmpty());
+    ASSERT_FALSE(skiaDL.mIsProjectionReceiver);
+}
+
+TEST(SkiaDisplayList, reuseDisplayList) {
+    sp<RenderNode> renderNode = new RenderNode();
+    std::unique_ptr<SkiaDisplayList> availableList;
+
+    // no list has been attached so it should return a nullptr
+    availableList = renderNode->detachAvailableList();
+    ASSERT_EQ(availableList.get(), nullptr);
+
+    // attach a displayList for reuse
+    SkiaDisplayList skiaDL(SkRect::MakeWH(200, 200));
+    ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get(), nullptr));
+
+    // detach the list that you just attempted to reuse
+    availableList = renderNode->detachAvailableList();
+    ASSERT_EQ(availableList.get(), &skiaDL);
+    availableList.release(); // prevents an invalid free since our DL is stack allocated
+
+    // after detaching there should return no available list
+    availableList = renderNode->detachAvailableList();
+    ASSERT_EQ(availableList.get(), nullptr);
+}
+
+class TestFunctor : public Functor {
+public:
+    bool didSync = false;
+
+    virtual status_t operator ()(int what, void* data) {
+        if (what == DrawGlInfo::kModeSync) { didSync = true; }
+        return DrawGlInfo::kStatusDone;
+    }
+};
+
+TEST(SkiaDisplayList, syncContexts) {
+    SkRect bounds = SkRect::MakeWH(200, 200);
+    SkiaDisplayList skiaDL(bounds);
+
+    SkCanvas dummyCanvas;
+    TestFunctor functor;
+    skiaDL.mChildFunctors.emplace_back(&functor, nullptr, &dummyCanvas);
+
+    VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
+    vectorDrawable.mutateStagingProperties()->setBounds(bounds);
+    skiaDL.mVectorDrawables.push_back(&vectorDrawable);
+
+    // ensure that the functor and vectorDrawable are properly synced
+    skiaDL.syncContents();
+
+    ASSERT_TRUE(functor.didSync);
+    ASSERT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
+}
+
+class ContextFactory : public IContextFactory {
+public:
+    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+        return new AnimationContext(clock);
+    }
+};
+
+RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren) {
+    auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
+    ContextFactory contextFactory;
+    std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
+            renderThread, false, rootNode.get(), &contextFactory));
+    TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
+    DamageAccumulator damageAccumulator;
+    info.damageAccumulator = &damageAccumulator;
+    info.observer = nullptr;
+
+    SkiaDisplayList skiaDL(SkRect::MakeWH(200, 200));
+
+    // prepare with a clean VD
+    VectorDrawableRoot cleanVD(new VectorDrawable::Group());
+    skiaDL.mVectorDrawables.push_back(&cleanVD);
+    cleanVD.getBitmapUpdateIfDirty(); // this clears the dirty bit
+
+    ASSERT_FALSE(cleanVD.isDirty());
+    ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
+    ASSERT_FALSE(skiaDL.prepareListAndChildren(info, false, [](RenderNode*, TreeInfo&, bool) {}));
+    ASSERT_TRUE(cleanVD.getPropertyChangeWillBeConsumed());
+
+    // prepare again this time adding a dirty VD
+    VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
+    skiaDL.mVectorDrawables.push_back(&dirtyVD);
+
+    ASSERT_TRUE(dirtyVD.isDirty());
+    ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+    ASSERT_TRUE(skiaDL.prepareListAndChildren(info, false, [](RenderNode*, TreeInfo&, bool) {}));
+    ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
+
+    // prepare again this time adding a RenderNode and a callback
+    sp<RenderNode> renderNode = new RenderNode();
+    TreeInfo* infoPtr = &info;
+    SkCanvas dummyCanvas;
+    skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
+    bool hasRun = false;
+    ASSERT_TRUE(skiaDL.prepareListAndChildren(info, false,
+            [&hasRun, renderNode, infoPtr](RenderNode* n, TreeInfo& i, bool r) {
+        hasRun = true;
+        ASSERT_EQ(renderNode.get(), n);
+        ASSERT_EQ(infoPtr, &i);
+        ASSERT_FALSE(r);
+    }));
+    ASSERT_TRUE(hasRun);
+
+    canvasContext->destroy(nullptr);
+}
+
+TEST(SkiaDisplayList, updateChildren) {
+    SkRect bounds = SkRect::MakeWH(200, 200);
+    SkiaDisplayList skiaDL(bounds);
+
+    sp<RenderNode> renderNode = new RenderNode();
+    SkCanvas dummyCanvas;
+    skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
+    skiaDL.updateChildren([renderNode](RenderNode* n) {
+        ASSERT_EQ(renderNode.get(), n);
+    });
+}