Implement SkiaRecordingCanvas, RenderNodeDrawable and other drawables.

Implement SkiaRecordingCanvas, RenderNodeDrawable, GLFunctorDrawable,
LayerDrawable, StartReorderBarrierDrawable, EndReorderBarrierDrawable.
Move AnimatedRoundRect and AnimatedCircle in a separate file.
All Skia pipeline files are moved in hwui/pipeline/skia folder.
Add unit tests for RenderNodeDrawable, StartReorderBarrierDrawable,
EndReorderBarrierDrawable and SkiaRecordingCanvas.

Test: I tested manually on 6P devices and did run the unit tests.
Change-Id: If2a347bd1fc4689953822294ce5bf98c7f3f57c7
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
new file mode 100644
index 0000000..44c494f
--- /dev/null
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -0,0 +1,90 @@
+/*
+ * 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 "CanvasProperty.h"
+#include <utils/RefBase.h>
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class AnimatedRoundRect : public SkDrawable {
+public:
+    AnimatedRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+            uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+            uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+            uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* p)
+            : mLeft(left)
+            , mTop(top)
+            , mRight(right)
+            , mBottom(bottom)
+            , mRx(rx)
+            , mRy(ry)
+            , mPaint(p) {}
+
+protected:
+    virtual SkRect onGetBounds() override {
+        return SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
+    }
+    virtual void onDraw(SkCanvas* canvas) override {
+        SkRect rect = SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
+        canvas->drawRoundRect(rect, mRx->value, mRy->value, mPaint->value);
+    }
+
+private:
+    sp<uirenderer::CanvasPropertyPrimitive> mLeft;
+    sp<uirenderer::CanvasPropertyPrimitive> mTop;
+    sp<uirenderer::CanvasPropertyPrimitive> mRight;
+    sp<uirenderer::CanvasPropertyPrimitive> mBottom;
+    sp<uirenderer::CanvasPropertyPrimitive> mRx;
+    sp<uirenderer::CanvasPropertyPrimitive> mRy;
+    sp<uirenderer::CanvasPropertyPaint> mPaint;
+};
+
+class AnimatedCircle : public SkDrawable {
+public:
+    AnimatedCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
+            uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint)
+            : mX(x)
+            , mY(y)
+            , mRadius(radius)
+            , mPaint(paint) {}
+
+protected:
+    virtual SkRect onGetBounds() override {
+        const float x = mX->value;
+        const float y = mY->value;
+        const float radius = mRadius->value;
+        return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius);
+    }
+    virtual void onDraw(SkCanvas* canvas) override {
+        canvas->drawCircle(mX->value, mY->value, mRadius->value, mPaint->value);
+    }
+
+private:
+    sp<uirenderer::CanvasPropertyPrimitive> mX;
+    sp<uirenderer::CanvasPropertyPrimitive> mY;
+    sp<uirenderer::CanvasPropertyPrimitive> mRadius;
+    sp<uirenderer::CanvasPropertyPaint> mPaint;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
new file mode 100644
index 0000000..fb2134c
--- /dev/null
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "GLFunctorDrawable.h"
+#include "GlFunctorLifecycleListener.h"
+#include "RenderNode.h"
+#include "SkClipStack.h"
+#include <private/hwui/DrawGlInfo.h>
+#include <SkPath.h>
+#include <GrContext.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+GLFunctorDrawable::~GLFunctorDrawable() {
+    if(mListener.get() != nullptr) {
+        mListener->onGlFunctorReleased(mFunctor);
+    }
+}
+
+void GLFunctorDrawable::syncFunctor() const {
+    (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
+}
+
+static void setScissor(int viewportHeight, const SkIRect& clip) {
+    SkASSERT(!clip.isEmpty());
+    // transform to Y-flipped GL space, and prevent negatives
+    GLint y = viewportHeight - clip.fBottom;
+    GLint height = (viewportHeight - clip.fTop) - y;
+    glScissor(clip.fLeft, y, clip.width(), height);
+}
+
+void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
+    if (canvas->getGrContext() == nullptr) {
+        SkDEBUGF(("Attempting to draw GLFunctor into an unsupported surface"));
+        return;
+    }
+
+    canvas->flush();
+
+    SkImageInfo canvasInfo = canvas->imageInfo();
+    SkMatrix44 mat4(canvas->getTotalMatrix());
+
+    SkIRect ibounds;
+    canvas->getClipDeviceBounds(&ibounds);
+
+    DrawGlInfo info;
+    info.clipLeft = ibounds.fLeft;
+    info.clipTop = ibounds.fTop;
+    info.clipRight = ibounds.fRight;
+    info.clipBottom = ibounds.fBottom;
+    //   info.isLayer = hasLayer();
+    info.isLayer = false;
+    info.width = canvasInfo.width();
+    info.height = canvasInfo.height();
+    mat4.asColMajorf(&info.transform[0]);
+
+    //apply a simple clip with a scissor or a complex clip with a stencil
+    SkRegion clipRegion;
+    SkPath path;
+    canvas->getClipStack()->asPath(&path);
+    clipRegion.setPath(path, SkRegion(ibounds));
+    if (CC_UNLIKELY(clipRegion.isComplex())) {
+        //It is only a temporary solution to use a scissor to draw the stencil.
+        //There is a bug 31489986 to implement efficiently non-rectangular clips.
+        glDisable(GL_SCISSOR_TEST);
+        glDisable(GL_STENCIL_TEST);
+        glStencilMask(0xff);
+        glClearStencil(0);
+        glClear(GL_STENCIL_BUFFER_BIT);
+        glEnable(GL_SCISSOR_TEST);
+        SkRegion::Cliperator it(clipRegion, ibounds);
+        while (!it.done()) {
+            setScissor(info.height, it.rect());
+            glClearStencil(0x1);
+            glClear(GL_STENCIL_BUFFER_BIT);
+            it.next();
+        }
+        glDisable(GL_SCISSOR_TEST);
+        glStencilFunc(GL_EQUAL, 0x1, 0xff);
+        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+        glEnable(GL_STENCIL_TEST);
+    } else if (clipRegion.isEmpty()) {
+        glDisable(GL_STENCIL_TEST);
+        glDisable(GL_SCISSOR_TEST);
+    } else {
+        glDisable(GL_STENCIL_TEST);
+        glEnable(GL_SCISSOR_TEST);
+        setScissor(info.height, clipRegion.getBounds());
+    }
+
+    (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+
+    canvas->getGrContext()->resetContext();
+ }
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
new file mode 100644
index 0000000..bf39dad
--- /dev/null
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -0,0 +1,59 @@
+/*
+ * 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 <SkCanvas.h>
+#include <SkDrawable.h>
+
+#include <utils/RefBase.h>
+#include <utils/Functor.h>
+
+namespace android {
+namespace uirenderer {
+
+class GlFunctorLifecycleListener;
+
+namespace skiapipeline {
+
+/**
+ * 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;
+
+ protected:
+    virtual SkRect onGetBounds() override { return mBounds; }
+    virtual void onDraw(SkCanvas* canvas) override;
+
+ private:
+     Functor* mFunctor;
+     sp<GlFunctorLifecycleListener> mListener;
+     SkRect mBounds;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
new file mode 100644
index 0000000..f8a181f
--- /dev/null
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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 "LayerDrawable.h"
+#include "gl/GrGLTypes.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+void LayerDrawable::onDraw(SkCanvas* canvas) {
+    // transform the matrix based on the layer
+    int saveCount = -1;
+    if (!mLayer->getTransform().isIdentity()) {
+        saveCount = canvas->save();
+        SkMatrix transform;
+        mLayer->getTransform().copyTo(transform);
+        canvas->concat(transform);
+    }
+    GrGLTextureInfo externalTexture;
+    externalTexture.fTarget = mLayer->getRenderTarget();
+    externalTexture.fID = mLayer->getTextureId();
+    GrContext* context = canvas->getGrContext();
+    GrBackendTextureDesc textureDescription;
+    textureDescription.fWidth = mLayer->getWidth();
+    textureDescription.fHeight = mLayer->getHeight();
+    textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
+    textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
+    textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
+    sk_sp<SkImage> layerImage(SkImage::NewFromTexture(context, textureDescription));
+    if (layerImage) {
+        SkPaint paint;
+        paint.setAlpha(mLayer->getAlpha());
+        paint.setBlendMode(mLayer->getMode());
+        paint.setColorFilter(mLayer->getColorFilter());
+        canvas->drawImage(layerImage, 0, 0, &paint);
+    }
+    // restore the original matrix
+    if (saveCount >= 0) {
+        canvas->restoreToCount(saveCount);
+    }
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
new file mode 100644
index 0000000..91e2744
--- /dev/null
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -0,0 +1,48 @@
+/*
+ * 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 <SkCanvas.h>
+#include <SkDrawable.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/*
+ * Draws a layer backed by an OpenGL texture into a SkCanvas.
+ */
+class LayerDrawable : public SkDrawable {
+ public:
+    explicit LayerDrawable(Layer* layer)
+            : mLayer(layer) {}
+
+ protected:
+     virtual SkRect onGetBounds() override {
+         return SkRect::MakeWH(mLayer->getWidth(), mLayer->getHeight());
+     }
+     virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+    sp<Layer> mLayer;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
new file mode 100644
index 0000000..cefa893
--- /dev/null
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -0,0 +1,252 @@
+/*
+ * 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 "RenderNodeDrawable.h"
+#include "RenderNode.h"
+#include "SkiaDisplayList.h"
+#include "SkiaFrameRenderer.h"
+#include "utils/TraceUtils.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) {
+    SkASSERT(outline.willClip());
+    Rect possibleRect;
+    float radius;
+    LOG_ALWAYS_FATAL_IF(!outline.getAsRoundRect(&possibleRect, &radius),
+            "clipping outlines should be at most roundedRects");
+    SkRect rect = possibleRect.toSkRect();
+    if (radius != 0.0f) {
+        if (pendingClip && !pendingClip->contains(rect)) {
+            canvas->clipRect(*pendingClip);
+        }
+        canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkRegion::kIntersect_Op, true);
+    } else {
+        if (pendingClip) {
+            (void)rect.intersect(*pendingClip);
+        }
+        canvas->clipRect(rect);
+    }
+}
+
+const RenderProperties& RenderNodeDrawable::getNodeProperties() const {
+    return mRenderNode->properties();
+}
+
+void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
+    //negative and positive Z order are drawn out of order
+    if (MathUtils::isZero(mRenderNode->properties().getZ())) {
+        this->forceDraw(canvas);
+    }
+}
+
+void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
+    RenderNode* renderNode = mRenderNode.get();
+    if (SkiaFrameRenderer::skpCaptureEnabled()) {
+        SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
+        canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
+    }
+
+    // We only respect the nothingToDraw check when we are composing a layer. This
+    // ensures that we paint the layer even if it is not currently visible in the
+    // event that the properties change and it becomes visible.
+    if (!renderNode->isRenderable() || (renderNode->nothingToDraw() && mComposeLayer)) {
+        return;
+    }
+
+    SkASSERT(renderNode->getDisplayList()->isSkiaDL());
+    SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    const RenderProperties& properties = this->getNodeProperties();
+    if (displayList->mIsProjectionReceiver) {
+        // this node is a projection receiver. We will gather the projected nodes as we draw our
+        // children, and then draw them on top of this node's content.
+        std::vector<ProjectedChild> newList;
+        for (auto& child : displayList->mChildNodes) {
+            // our direct children are not supposed to project into us (nodes project to, at the
+            // nearest, their grandparents). So we "delay" the list's activation one level by
+            // passing it into mNextProjectedChildrenTarget rather than mProjectedChildrenTarget.
+            child.mProjectedChildrenTarget = mNextProjectedChildrenTarget;
+            child.mNextProjectedChildrenTarget = &newList;
+        }
+        // draw ourselves and our children. As a side effect, this will add projected nodes to
+        // newList.
+        this->drawContent(canvas);
+        bool willClip = properties.getOutline().willClip();
+        if (willClip) {
+            canvas->save();
+            clipOutline(properties.getOutline(), canvas, nullptr);
+        }
+        // draw the collected projected nodes
+        for (auto& projectedChild : newList) {
+            canvas->setMatrix(projectedChild.matrix);
+            projectedChild.node->drawContent(canvas);
+        }
+        if (willClip) {
+            canvas->restore();
+        }
+    } else {
+        if (properties.getProjectBackwards() && mProjectedChildrenTarget) {
+            // We are supposed to project this node, so add it to the list and do not actually draw
+            // yet. It will be drawn by its projection receiver.
+            mProjectedChildrenTarget->push_back({ this, canvas->getTotalMatrix() });
+            return;
+        }
+        for (auto& child : displayList->mChildNodes) {
+            // storing these values in the nodes themselves is a bit ugly; they should "really" be
+            // function parameters, but we have to go through the preexisting draw() method and
+            // therefore cannot add additional parameters to it
+            child.mProjectedChildrenTarget = mNextProjectedChildrenTarget;
+            child.mNextProjectedChildrenTarget = mNextProjectedChildrenTarget;
+        }
+        this->drawContent(canvas);
+    }
+    mProjectedChildrenTarget = nullptr;
+    mNextProjectedChildrenTarget = nullptr;
+}
+
+static bool layerNeedsPaint(const LayerProperties& properties,
+                            float alphaMultiplier, SkPaint* paint) {
+    if (alphaMultiplier < 1.0f
+            || properties.alpha() < 255
+            || properties.xferMode() != SkBlendMode::kSrcOver
+            || properties.colorFilter() != nullptr) {
+        paint->setAlpha(properties.alpha() * alphaMultiplier);
+        paint->setBlendMode(properties.xferMode());
+        paint->setColorFilter(properties.colorFilter());
+        return true;
+    }
+    return false;
+}
+
+void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
+    RenderNode* renderNode = mRenderNode.get();
+    float alphaMultiplier = 1.0f;
+    const RenderProperties& properties = renderNode->properties();
+
+    // If we are drawing the contents of layer, we don't want to apply any of
+    // the RenderNode's properties during this pass. Those will all be applied
+    // when the layer is composited.
+    if (mComposeLayer) {
+        setViewProperties(properties, canvas, &alphaMultiplier);
+    }
+
+    //TODO should we let the bound of the drawable do this for us?
+    const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
+    bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds);
+    if (!quickRejected) {
+        SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
+        const LayerProperties& layerProperties = properties.layerProperties();
+        // composing a hardware layer
+        if (renderNode->getLayerSurface() && mComposeLayer) {
+            SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
+            SkPaint* paint = nullptr;
+            SkPaint tmpPaint;
+            if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) {
+                paint = &tmpPaint;
+            }
+            renderNode->getLayerSurface()->draw(canvas, 0, 0, paint);
+        // composing a software layer with alpha
+        } else if (properties.effectiveLayerType() == LayerType::Software) {
+            SkPaint paint;
+            bool needsLayer = layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
+            if (needsLayer) {
+                canvas->saveLayer(bounds, &paint);
+            }
+            canvas->drawDrawable(displayList->mDrawable.get());
+            if (needsLayer) {
+                canvas->restore();
+            }
+        } else {
+            canvas->drawDrawable(displayList->mDrawable.get());
+        }
+    }
+}
+
+void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
+        float* alphaMultiplier) {
+    if (properties.getLeft() != 0 || properties.getTop() != 0) {
+        canvas->translate(properties.getLeft(), properties.getTop());
+    }
+    if (properties.getStaticMatrix()) {
+        canvas->concat(*properties.getStaticMatrix());
+    } else if (properties.getAnimationMatrix()) {
+        canvas->concat(*properties.getAnimationMatrix());
+    }
+    if (properties.hasTransformMatrix()) {
+        if (properties.isTransformTranslateOnly()) {
+            canvas->translate(properties.getTranslationX(), properties.getTranslationY());
+        } else {
+            canvas->concat(*properties.getTransformMatrix());
+        }
+    }
+    const bool isLayer = properties.effectiveLayerType() != LayerType::None;
+    int clipFlags = properties.getClippingFlags();
+    if (properties.getAlpha() < 1) {
+        if (isLayer) {
+            clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
+        }
+        if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
+            *alphaMultiplier = properties.getAlpha();
+        } else {
+            // savelayer needed to create an offscreen buffer
+            Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight());
+            if (clipFlags) {
+                properties.getClippingRectForFlags(clipFlags, &layerBounds);
+                clipFlags = 0; // all clipping done by savelayer
+            }
+            SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top,
+                    layerBounds.right, layerBounds.bottom);
+            canvas->saveLayerAlpha(&bounds, (int) (properties.getAlpha() * 255));
+        }
+
+        if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
+            // pretend alpha always causes savelayer to warn about
+            // performance problem affecting old versions
+            ATRACE_FORMAT("alpha caused saveLayer %dx%d", properties.getWidth(),
+                    properties.getHeight());
+        }
+    }
+
+    const SkRect* pendingClip = nullptr;
+    SkRect clipRect;
+
+    if (clipFlags) {
+        Rect tmpRect;
+        properties.getClippingRectForFlags(clipFlags, &tmpRect);
+        clipRect = tmpRect.toSkRect();
+        pendingClip = &clipRect;
+    }
+
+    if (properties.getRevealClip().willClip()) {
+        canvas->clipPath(*properties.getRevealClip().getPath(), SkRegion::kIntersect_Op, true);
+    } else if (properties.getOutline().willClip()) {
+        clipOutline(properties.getOutline(), canvas, pendingClip);
+        pendingClip = nullptr;
+    }
+
+    if (pendingClip) {
+        canvas->clipRect(*pendingClip);
+    }
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
new file mode 100644
index 0000000..0762f37
--- /dev/null
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -0,0 +1,148 @@
+/*
+ * 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 <SkCanvas.h>
+#include <SkDrawable.h>
+#include <SkMatrix.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+class RenderProperties;
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a RenderNode and enables it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class RenderNodeDrawable : public SkDrawable {
+public:
+    /**
+     * This struct contains a pointer to a node that is to be
+     * projected into the drawing order of its closest ancestor
+     * (excluding its parent) that is marked as a projection
+     * receiver. The matrix is used to ensure that the node is
+     * drawn with same matrix as it would have prior to projection.
+     */
+    struct ProjectedChild {
+        const RenderNodeDrawable* node;
+        const SkMatrix matrix;
+    };
+
+    /**
+     * Creates a new RenderNodeDrawable backed by a render node.
+     *
+     * @param node that has to be drawn
+     * @param canvas is a recording canvas used to extract its matrix
+     * @param composeLayer if the node's layer type is RenderLayer this flag determines whether
+     *      we should draw into the contents of the layer or compose the existing contents of the
+     *      layer into the canvas.
+     */
+    explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer = true)
+            : mRenderNode(node)
+            , mRecordedTransform(canvas->getTotalMatrix())
+            , mComposeLayer(composeLayer) {}
+
+    /**
+     * Draws into the canvas this render node and its children. If the node is marked as a
+     * projection receiver then all projected children (excluding direct children) will be drawn
+     * last. Any projected node not matching those requirements will not be drawn by this function.
+     */
+    void forceDraw(SkCanvas* canvas);
+
+    /**
+     * Returns readonly render properties for this render node.
+     */
+    const RenderProperties& getNodeProperties() const;
+
+    /**
+     * 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:
+    /*
+     * Return the (conservative) bounds of what the drawable will draw.
+     */
+    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();
+    }
+    /**
+     * This function draws into a canvas as forceDraw, but does nothing if the render node has a
+     * non-zero elevation.
+     */
+    virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+    /*
+     * Render node that is wrapped by this class.
+     */
+    sp<RenderNode> mRenderNode;
+
+    /**
+     * Applies the rendering properties of a view onto a SkCanvas.
+     */
+    static void setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
+            float* alphaMultiplier);
+
+    /**
+     * Stores transform on the canvas at time of recording and is used for
+     * computing total transform without rerunning DL contents.
+     */
+    const SkMatrix mRecordedTransform;
+
+    /**
+     * If mRenderNode's layer type is RenderLayer this flag determines whether we
+     * should draw into the contents of the layer or compose the existing contents
+     * of the layer into the canvas.
+     */
+    const bool mComposeLayer;
+
+    /**
+     * List to which we will add any projected children we encounter while walking our descendents.
+     * This pointer is valid only while the node (including its children) is actively being drawn.
+     */
+    std::vector<ProjectedChild>* mProjectedChildrenTarget = nullptr;
+
+    /**
+     * The value to which we should set our children's mProjectedChildrenTarget. We use two pointers
+     * (mProjectedChildrenTarget and mNextProjectedChildrenTarget) because we need to skip over our
+     * parent when looking for a projection receiver.
+     */
+    std::vector<ProjectedChild>* mNextProjectedChildrenTarget = nullptr;
+
+    /*
+     *  Draw the content into a canvas, depending on the render node layer type and mComposeLayer.
+     */
+    void drawContent(SkCanvas* canvas) const;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
new file mode 100644
index 0000000..8d77938
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -0,0 +1,704 @@
+/*
+ * 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 "ReorderBarrierDrawables.h"
+#include "RenderNode.h"
+#include "SkiaDisplayList.h"
+#include "SkiaFrameRenderer.h"
+
+#include <SkBlurMask.h>
+#include <SkBlurMaskFilter.h>
+#include <SkGaussianEdgeShader.h>
+#include <SkPathOps.h>
+#include <SkRRectsGaussianEdgeShader.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data)
+        : mEndChildIndex(0)
+        , mBeginChildIndex(data->mChildNodes.size())
+        , mDisplayList(data) {
+}
+
+void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
+    if (mChildren.empty()) {
+        //mChildren is allocated and initialized only the first time onDraw is called and cached for
+        //subsequent calls
+        mChildren.reserve(mEndChildIndex - mBeginChildIndex + 1);
+        for (unsigned int i = mBeginChildIndex; i <= mEndChildIndex; i++) {
+            mChildren.push_back(const_cast<RenderNodeDrawable*>(&mDisplayList->mChildNodes[i]));
+        }
+    }
+    std::stable_sort(mChildren.begin(), mChildren.end(),
+        [](RenderNodeDrawable* a, RenderNodeDrawable* b) {
+            const float aZValue = a->getNodeProperties().getZ();
+            const float bZValue = b->getNodeProperties().getZ();
+            return aZValue < bZValue;
+        });
+
+    SkASSERT(!mChildren.empty());
+
+    size_t drawIndex = 0;
+    const size_t endIndex = mChildren.size();
+    while (drawIndex < endIndex) {
+        RenderNodeDrawable* childNode = mChildren[drawIndex];
+        SkASSERT(childNode);
+        const float casterZ = childNode->getNodeProperties().getZ();
+        if (casterZ >= -NON_ZERO_EPSILON) { //draw only children with negative Z
+            return;
+        }
+        childNode->forceDraw(canvas);
+        drawIndex++;
+    }
+}
+
+EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier)
+        : mStartBarrier(startBarrier) {
+    mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1;
+}
+
+#define SHADOW_DELTA 0.1f
+
+void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
+    auto& zChildren = mStartBarrier->mChildren;
+    SkASSERT(!zChildren.empty());
+
+    /**
+     * 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.
+     */
+    size_t drawIndex = 0;
+
+    const size_t endIndex = zChildren.size();
+    while (drawIndex < endIndex     //draw only children with positive Z
+            && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON) drawIndex++;
+    size_t shadowIndex = drawIndex;
+
+    float lastCasterZ = 0.0f;
+    while (shadowIndex < endIndex || drawIndex < endIndex) {
+        if (shadowIndex < endIndex) {
+            const float casterZ = zChildren[shadowIndex]->getNodeProperties().getZ();
+
+            // 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) {
+                this->drawShadow(canvas, zChildren[shadowIndex]);
+                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+                shadowIndex++;
+                continue;
+            }
+        }
+
+        RenderNodeDrawable* childNode = zChildren[drawIndex];
+        SkASSERT(childNode);
+        childNode->forceDraw(canvas);
+
+        drawIndex++;
+    }
+}
+
+/**
+ * @param canvas             the destination for the shadow draws
+ * @param shape              the shape casting the shadow
+ * @param casterZValue       the Z value of the caster RRect
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param draw               the function used to draw 'shape'
+ */
+template <typename Shape, typename F>
+static void DrawAmbientShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
+        float ambientAlpha, F&& draw) {
+    if (ambientAlpha <= 0) {
+        return;
+    }
+
+    const float kHeightFactor = 1.f/128.f;
+    const float kGeomFactor = 64;
+
+    float umbraAlpha = 1 / (1 + SkMaxScalar(casterZValue*kHeightFactor, 0));
+    float radius = casterZValue*kHeightFactor*kGeomFactor;
+
+    sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
+            SkBlurMask::ConvertRadiusToSigma(radius), SkBlurMaskFilter::kNone_BlurFlag);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setMaskFilter(std::move(mf));
+    paint.setARGB(ambientAlpha*umbraAlpha, 0, 0, 0);
+
+    draw(shape, paint);
+}
+
+/**
+ * @param canvas             the destination for the shadow draws
+ * @param shape              the shape casting the shadow
+ * @param casterZValue       the Z value of the caster RRect
+ * @param lightPos           the position of the light casting the shadow
+ * @param lightWidth
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param draw               the function used to draw 'shape'
+ */
+template <typename Shape, typename F>
+static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
+        float spotAlpha, F&& draw) {
+    if (spotAlpha <= 0) {
+        return;
+    }
+
+    const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+    float zRatio = casterZValue / (lightPos.z - casterZValue);
+    // clamp
+    if (zRatio < 0.0f) {
+        zRatio = 0.0f;
+    } else if (zRatio > 0.95f) {
+        zRatio = 0.95f;
+    }
+
+    float blurRadius = SkiaFrameRenderer::getLightRadius()*zRatio;
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
+            SkBlurMask::ConvertRadiusToSigma(blurRadius), SkBlurMaskFilter::kNone_BlurFlag);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setMaskFilter(std::move(mf));
+    paint.setARGB(spotAlpha, 0, 0, 0);
+
+    // approximate projection by translating and scaling projected offset of bounds center
+    // TODO: compute the actual 2D projection
+    SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+    canvas->scale(scale, scale);
+    SkPoint center = SkPoint::Make(shape.getBounds().centerX(), shape.getBounds().centerY());
+    SkMatrix ctmInverse;
+    if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+        ALOGW("Matrix is degenerate. Will not render shadow!");
+        return;
+    }
+    SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+    ctmInverse.mapPoints(&lightPos2D, 1);
+    canvas->translate(zRatio*(center.fX - lightPos2D.fX), zRatio*(center.fY - lightPos2D.fY));
+
+    draw(shape, paint);
+}
+
+#define MAX_BLUR_RADIUS 16383.75f
+#define MAX_PAD         64
+
+/**
+ * @param casterRect         the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param casterAlpha        the alpha value of the RRect casting the shadow (0.0-1.0 range)
+ * @param casterZValue       the Z value of the caster RRect
+ * @param scaleFactor        the scale needed to map from src-space to device-space
+ * @param canvas             the destination for the shadow draws
+ */
+static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadius,
+        SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue,
+        SkScalar scaleFactor, SkCanvas* canvas) {
+    SkASSERT(cornerRadius >= 0.0f);
+
+    // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
+    const SkScalar minRadius = 0.5f / scaleFactor;
+
+    const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
+            SkScalarHalf(casterRect.height()));
+    const bool isRect = casterCornerRadius <= minRadius;
+
+    sk_sp<SkShader> edgeShader(SkGaussianEdgeShader::Make());
+
+    if (ambientAlpha > 0.0f) {
+        static const float kHeightFactor = 1.0f / 128.0f;
+        static const float kGeomFactor = 64.0f;
+
+        SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
+        // the device-space radius sent to the blur shader must fit in 14.2 fixed point
+        if (srcSpaceAmbientRadius*scaleFactor > MAX_BLUR_RADIUS) {
+            srcSpaceAmbientRadius = MAX_BLUR_RADIUS/scaleFactor;
+        }
+        const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
+        const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
+
+        // For the ambient rrect, we inset the offset rect by half the srcSpaceAmbientRadius
+        // to get our stroke shape.
+        SkScalar ambientPathOutset = std::max(ambientOffset - srcSpaceAmbientRadius * 0.5f,
+                minRadius);
+
+        SkRRect ambientRRect;
+        const SkRect temp = casterRect.makeOutset(ambientPathOutset, ambientPathOutset);
+        if (isOval) {
+            ambientRRect = SkRRect::MakeOval(temp);
+        } else if (isRect) {
+            ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
+        } else {
+            ambientRRect = SkRRect::MakeRectXY(temp, casterCornerRadius + ambientPathOutset,
+                    casterCornerRadius + ambientPathOutset);
+        }
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        // we outset the stroke a little to cover up AA on the interior edge
+        float pad = 0.5f;
+        paint.setStrokeWidth(srcSpaceAmbientRadius + 2.0f * pad);
+        // handle scale of radius and pad due to CTM
+        pad *= scaleFactor;
+        const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
+        SkASSERT(devSpaceAmbientRadius <= MAX_BLUR_RADIUS);
+        SkASSERT(pad < MAX_PAD);
+        // convert devSpaceAmbientRadius to 14.2 fixed point and place in the R & G components
+        // convert pad to 6.2 fixed point and place in the B component
+        uint16_t iDevSpaceAmbientRadius = (uint16_t)(4.0f * devSpaceAmbientRadius);
+        paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, iDevSpaceAmbientRadius >> 8,
+                iDevSpaceAmbientRadius & 0xff, (unsigned char)(4.0f * pad)));
+
+        paint.setShader(edgeShader);
+        canvas->drawRRect(ambientRRect, paint);
+    }
+
+    if (spotAlpha > 0.0f) {
+        const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+        float zRatio = casterZValue / (lightPos.z - casterZValue);
+        // clamp
+        if (zRatio < 0.0f) {
+            zRatio = 0.0f;
+        } else if (zRatio > 0.95f) {
+            zRatio = 0.95f;
+        }
+
+        const SkScalar lightWidth = SkiaFrameRenderer::getLightRadius();
+        SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
+        // the device-space radius sent to the blur shader must fit in 14.2 fixed point
+        if (srcSpaceSpotRadius*scaleFactor > MAX_BLUR_RADIUS) {
+            srcSpaceSpotRadius = MAX_BLUR_RADIUS/scaleFactor;
+        }
+
+        SkRRect spotRRect;
+        if (isOval) {
+            spotRRect = SkRRect::MakeOval(casterRect);
+        } else if (isRect) {
+            spotRRect = SkRRect::MakeRectXY(casterRect, minRadius, minRadius);
+        } else {
+            spotRRect = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
+        }
+
+        SkRRect spotShadowRRect;
+        // Compute the scale and translation for the spot shadow.
+        const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+        spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
+
+        SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(),
+                                       spotShadowRRect.rect().centerY());
+        SkMatrix ctmInverse;
+        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+            ALOGW("Matrix is degenerate. Will not render spot shadow!");
+            return;
+        }
+        SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+        ctmInverse.mapPoints(&lightPos2D, 1);
+        const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+                zRatio*(center.fY - lightPos2D.fY));
+
+        SkAutoCanvasRestore acr(canvas, true);
+
+        // We want to extend the stroked area in so that it meets up with the caster
+        // geometry. The stroked geometry will, by definition already be inset half the
+        // stroke width but we also have to account for the scaling.
+        // We also add 1/2 to cover up AA on the interior edge.
+        SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(casterRect.fLeft),
+                SkTAbs(casterRect.fRight)), SkTMax(SkTAbs(casterRect.fTop),
+                SkTAbs(casterRect.fBottom)));
+        SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) +
+                scaleOffset + 0.5f;
+
+        // Compute area
+        SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount;
+        SkScalar strokedArea = 2.0f*strokeWidth * (spotShadowRRect.width()
+                + spotShadowRRect.height());
+        SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius)
+                * (spotShadowRRect.width() + srcSpaceSpotRadius);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+
+        // If the area of the stroked geometry is larger than the fill geometry, just fill it.
+        if (strokedArea > filledArea || casterAlpha < 1.0f) {
+            paint.setStyle(SkPaint::kStrokeAndFill_Style);
+            paint.setStrokeWidth(srcSpaceSpotRadius);
+        } else {
+            // Since we can't have unequal strokes, inset the shadow rect so the inner
+            // and outer edges of the stroke will land where we want.
+            SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount/2.0f, insetAmount/2.0f);
+            SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount/2.0f,
+                    minRadius);
+            spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(strokeWidth);
+        }
+
+        // handle scale of radius and pad due to CTM
+        const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
+        SkASSERT(devSpaceSpotRadius <= MAX_BLUR_RADIUS);
+
+        const SkScalar devSpaceSpotPad = 0;
+        SkASSERT(devSpaceSpotPad < MAX_PAD);
+
+        // convert devSpaceSpotRadius to 14.2 fixed point and place in the R & G
+        // components convert devSpaceSpotPad to 6.2 fixed point and place in the B component
+        uint16_t iDevSpaceSpotRadius = (uint16_t)(4.0f * devSpaceSpotRadius);
+        paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, iDevSpaceSpotRadius >> 8,
+                iDevSpaceSpotRadius & 0xff, (unsigned char)(4.0f * devSpaceSpotPad)));
+        paint.setShader(edgeShader);
+
+        canvas->translate(spotOffset.fX, spotOffset.fY);
+        canvas->drawRRect(spotShadowRRect, paint);
+    }
+}
+
+/**
+ * @param casterRect         the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param casterZValue       the Z value of the caster RRect
+ * @param scaleFactor        the scale needed to map from src-space to device-space
+ * @param clipRR             the oval or rect with which the drawn roundrect must be intersected
+ * @param canvas             the destination for the shadow draws
+ */
+static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCornerRadius,
+        SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterZValue, SkScalar scaleFactor,
+        const SkRRect& clipRR, SkCanvas* canvas) {
+    SkASSERT(cornerRadius >= 0.0f);
+
+    const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
+            SkScalarHalf(casterRect.height()));
+
+    if (ambientAlpha > 0.0f) {
+        static const float kHeightFactor = 1.0f / 128.0f;
+        static const float kGeomFactor = 64.0f;
+
+        const SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
+        const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
+
+        const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
+        const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
+
+        const SkRect srcSpaceAmbientRect = casterRect.makeOutset(ambientOffset, ambientOffset);
+        SkRect devSpaceAmbientRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceAmbientRect, srcSpaceAmbientRect);
+
+        SkRRect devSpaceAmbientRRect;
+        if (isOval) {
+            devSpaceAmbientRRect = SkRRect::MakeOval(devSpaceAmbientRect);
+        } else {
+            const SkScalar devSpaceCornerRadius = scaleFactor * (casterCornerRadius + ambientOffset);
+            devSpaceAmbientRRect = SkRRect::MakeRectXY(devSpaceAmbientRect, devSpaceCornerRadius,
+                    devSpaceCornerRadius);
+        }
+
+        const SkRect srcSpaceAmbClipRect = clipRR.rect().makeOutset(ambientOffset, ambientOffset);
+        SkRect devSpaceAmbClipRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceAmbClipRect, srcSpaceAmbClipRect);
+        SkRRect devSpaceAmbientClipRR;
+        if (clipRR.isOval()) {
+            devSpaceAmbientClipRR = SkRRect::MakeOval(devSpaceAmbClipRect);
+        } else {
+            SkASSERT(clipRR.isRect());
+            devSpaceAmbientClipRR = SkRRect::MakeRect(devSpaceAmbClipRect);
+        }
+
+        SkRect cover = srcSpaceAmbClipRect;
+        if (!cover.intersect(srcSpaceAmbientRect)) {
+            return;
+        }
+
+        SkPaint paint;
+        paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, 0, 0, 0));
+        paint.setShader(SkRRectsGaussianEdgeShader::Make(devSpaceAmbientRRect,
+                devSpaceAmbientClipRR, devSpaceAmbientRadius));
+        canvas->drawRect(cover, paint);
+    }
+
+    if (spotAlpha > 0.0f) {
+        const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+        float zRatio = casterZValue / (lightPos.z - casterZValue);
+        // clamp
+        if (zRatio < 0.0f) {
+            zRatio = 0.0f;
+        } else if (zRatio > 0.95f) {
+            zRatio = 0.95f;
+        }
+
+        const SkScalar lightWidth = SkiaFrameRenderer::getLightRadius();
+        const SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
+        const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
+
+        // Compute the scale and translation for the spot shadow.
+        const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+        const SkMatrix spotMatrix = SkMatrix::MakeScale(scale, scale);
+
+        SkRect srcSpaceScaledRect = casterRect;
+        spotMatrix.mapRect(&srcSpaceScaledRect);
+        srcSpaceScaledRect.outset(SkScalarHalf(srcSpaceSpotRadius),
+                SkScalarHalf(srcSpaceSpotRadius));
+
+        SkRRect srcSpaceSpotRRect;
+        if (isOval) {
+            srcSpaceSpotRRect = SkRRect::MakeOval(srcSpaceScaledRect);
+        } else {
+            srcSpaceSpotRRect = SkRRect::MakeRectXY(srcSpaceScaledRect, casterCornerRadius * scale,
+                    casterCornerRadius * scale);
+        }
+
+        SkPoint center = SkPoint::Make(srcSpaceSpotRRect.rect().centerX(),
+                srcSpaceSpotRRect.rect().centerY());
+        SkMatrix ctmInverse;
+        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+            ALOGW("Matrix is degenerate. Will not render spot shadow!");
+            return;
+        }
+        SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+        ctmInverse.mapPoints(&lightPos2D, 1);
+        const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+                zRatio*(center.fY - lightPos2D.fY));
+
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->translate(spotOffset.fX, spotOffset.fY);
+
+        SkRect devSpaceScaledRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceScaledRect, srcSpaceScaledRect);
+
+        SkRRect devSpaceSpotRRect;
+        if (isOval) {
+            devSpaceSpotRRect = SkRRect::MakeOval(devSpaceScaledRect);
+        } else {
+            const SkScalar devSpaceScaledCornerRadius = casterCornerRadius * scale * scaleFactor;
+            devSpaceSpotRRect = SkRRect::MakeRectXY(devSpaceScaledRect, devSpaceScaledCornerRadius,
+                    devSpaceScaledCornerRadius);
+        }
+
+        SkPaint paint;
+        paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, 0, 0, 0));
+
+        SkRect srcSpaceScaledClipRect = clipRR.rect();
+        spotMatrix.mapRect(&srcSpaceScaledClipRect);
+        srcSpaceScaledClipRect.outset(SkScalarHalf(srcSpaceSpotRadius),
+                SkScalarHalf(srcSpaceSpotRadius));
+
+        SkRect devSpaceScaledClipRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceScaledClipRect, srcSpaceScaledClipRect);
+        SkRRect devSpaceSpotClipRR;
+        if (clipRR.isOval()) {
+            devSpaceSpotClipRR = SkRRect::MakeOval(devSpaceScaledClipRect);
+        } else {
+            SkASSERT(clipRR.isRect());
+            devSpaceSpotClipRR = SkRRect::MakeRect(devSpaceScaledClipRect);
+        }
+
+        paint.setShader(SkRRectsGaussianEdgeShader::Make(devSpaceSpotRRect, devSpaceSpotClipRR,
+                devSpaceSpotRadius));
+
+        SkRect cover = srcSpaceScaledClipRect;
+        if (!cover.intersect(srcSpaceSpotRRect.rect())) {
+            return;
+        }
+
+        canvas->drawRect(cover, paint);
+    }
+}
+
+/**
+ * @param casterRect         the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param casterClipRect     a rectangular clip that must be intersected with the
+ *                           shadow-casting RRect prior to casting the shadow
+ * @param revealClip         a circular clip that must be interested with the castClipRect
+ *                           and the shadow-casting rect prior to casting the shadow
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param casterAlpha        the alpha value of the RRect casting the shadow (0.0-1.0 range)
+ * @param casterZValue       the Z value of the caster RRect
+ * @param canvas             the destination for the shadow draws
+ *
+ * We have special cases for 4 round rect shadow draws:
+ *    1) a RRect clipped by a reveal animation
+ *    2) a RRect clipped by a rectangle
+ *    3) an unclipped RRect with non-uniform scale
+ *    4) an unclipped RRect with uniform scale
+ * 1,2 and 4 require that the scale is uniform.
+ * 1 and 2 require that rects stay rects.
+ */
+static bool DrawShadowsAsRRects(const SkRect& casterRect, SkScalar casterCornerRadius,
+        const SkRect& casterClipRect, const RevealClip& revealClip, SkScalar ambientAlpha,
+        SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue, SkCanvas* canvas) {
+    SkScalar scaleFactors[2];
+    if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
+        ALOGW("Matrix is degenerate. Will not render shadow!");
+        return false;
+    }
+
+    // The casterClipRect will contain the casterRect when bounds clipping is disabled
+    bool casterIsClippedByRect = !casterClipRect.contains(casterRect);
+    bool uniformScale = scaleFactors[0] == scaleFactors[1];
+
+    if (revealClip.willClip()) {
+        if (casterIsClippedByRect || !uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
+            return false;  // Fall back to the slow path since PathOps are required
+        }
+
+        const float revealRadius = revealClip.getRadius();
+        SkRect revealClipRect = SkRect::MakeLTRB(revealClip.getX()-revealRadius,
+                revealClip.getY()-revealRadius, revealClip.getX()+revealRadius,
+                revealClip.getY()+revealRadius);
+        SkRRect revealClipRR = SkRRect::MakeOval(revealClipRect);
+
+        DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
+                casterZValue, scaleFactors[0], revealClipRR, canvas);
+        return true;
+    }
+
+    if (casterIsClippedByRect) {
+        if (!uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
+            return false;  // Fall back to the slow path since PathOps are required
+        }
+
+        SkRRect casterClipRR = SkRRect::MakeRect(casterClipRect);
+
+        DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
+                casterZValue, scaleFactors[0], casterClipRR, canvas);
+        return true;
+    }
+
+    // The fast path needs uniform scale
+    if (!uniformScale) {
+        SkRRect casterRR = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
+        DrawAmbientShadowGeneral(canvas, casterRR, casterZValue, ambientAlpha,
+                [&](const SkRRect& rrect, const SkPaint& paint) {
+                    canvas->drawRRect(rrect, paint);
+                });
+        DrawSpotShadowGeneral(canvas, casterRR, casterZValue, spotAlpha,
+                [&](const SkRRect& rrect, const SkPaint& paint) {
+                canvas->drawRRect(rrect, paint);
+                });
+        return true;
+    }
+
+    DrawRRectShadows(casterRect, casterCornerRadius, ambientAlpha, spotAlpha, casterAlpha,
+            casterZValue, scaleFactors[0], canvas);
+    return true;
+}
+
+// copied from FrameBuilder::deferShadow
+void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
+    const RenderProperties& casterProperties = caster->getNodeProperties();
+
+    if (casterProperties.getAlpha() <= 0.0f
+            || casterProperties.getOutline().getAlpha() <= 0.0f
+            || !casterProperties.getOutline().getPath()
+            || casterProperties.getScaleX() == 0
+            || casterProperties.getScaleY() == 0) {
+        // no shadow to draw
+        return;
+    }
+
+    const SkScalar casterAlpha = casterProperties.getAlpha()
+            * casterProperties.getOutline().getAlpha();
+    if (casterAlpha <= 0.0f) {
+        return;
+    }
+
+    float ambientAlpha = SkiaFrameRenderer::getAmbientShadowAlpha()*casterAlpha;
+    float spotAlpha = SkiaFrameRenderer::getSpotShadowAlpha()*casterAlpha;
+    const float casterZValue = casterProperties.getZ();
+
+    const RevealClip& revealClip = casterProperties.getRevealClip();
+    const SkPath* revealClipPath = revealClip.getPath();
+    if (revealClipPath && revealClipPath->isEmpty()) {
+        // An empty reveal clip means nothing is drawn
+        return;
+    }
+
+    bool clippedToBounds = casterProperties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS;
+
+    SkRect casterClipRect = SkRect::MakeLargest();
+    if (clippedToBounds) {
+        Rect clipBounds;
+        casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
+        casterClipRect = clipBounds.toSkRect();
+    }
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    SkMatrix shadowMatrix;
+    mat4 hwuiMatrix(caster->getRecordedMatrix());
+    // TODO we don't pass the optional boolean to treat it as a 4x4 matrix
+    caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
+    hwuiMatrix.copyTo(shadowMatrix);
+    canvas->concat(shadowMatrix);
+
+    const Outline& casterOutline = casterProperties.getOutline();
+    Rect possibleRect;
+    float radius;
+    if (casterOutline.getAsRoundRect(&possibleRect, &radius)) {
+        if (DrawShadowsAsRRects(possibleRect.toSkRect(), radius, casterClipRect, revealClip,
+                ambientAlpha, spotAlpha, casterAlpha, casterZValue, canvas)) {
+            return;
+        }
+    }
+
+    // Hard cases and calls to general shadow code
+    const SkPath* casterOutlinePath = casterProperties.getOutline().getPath();
+
+    // holds temporary SkPath to store the result of intersections
+    SkPath tmpPath;
+    const SkPath* casterPath = casterOutlinePath;
+
+    // TODO: In to following course of code that calculates the final shape, is there an optimal
+    //       of doing the Op calculations?
+    // intersect the shadow-casting path with the reveal, if present
+    if (revealClipPath) {
+        Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath);
+        casterPath = &tmpPath;
+    }
+
+    // intersect the shadow-casting path with the clipBounds, if present
+    if (clippedToBounds) {
+        SkPath clipBoundsPath;
+        clipBoundsPath.addRect(casterClipRect);
+        Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath);
+        casterPath = &tmpPath;
+    }
+
+    DrawAmbientShadowGeneral(canvas, *casterPath, casterZValue, ambientAlpha,
+            [&](const SkPath& path, const SkPaint& paint) {
+                canvas->drawPath(path, paint);
+            });
+
+    DrawSpotShadowGeneral(canvas, *casterPath, casterZValue, spotAlpha,
+            [&](const SkPath& path, const SkPaint& paint) {
+                canvas->drawPath(path, paint);
+            });
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
new file mode 100644
index 0000000..298a732
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -0,0 +1,80 @@
+/*
+ * 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 "RenderNodeDrawable.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+#include <utils/FatVector.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaDisplayList;
+class EndReorderBarrierDrawable;
+
+/**
+ * StartReorderBarrierDrawable and EndReorderBarrierDrawable work together to define
+ * a sub-list in a display list that need to be drawn out-of-order sorted instead by render
+ * node Z index.
+ * StartReorderBarrierDrawable will sort the entire range and it will draw
+ * render nodes in the range with negative Z index.
+ */
+class StartReorderBarrierDrawable : public SkDrawable {
+public:
+    explicit StartReorderBarrierDrawable(SkiaDisplayList* data);
+
+protected:
+    virtual SkRect onGetBounds() override {
+        return SkRect::MakeLargest();
+    }
+    virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+    size_t mEndChildIndex;
+    size_t mBeginChildIndex;
+    FatVector<RenderNodeDrawable*, 16> mChildren;
+    SkiaDisplayList* mDisplayList;
+
+    friend class EndReorderBarrierDrawable;
+};
+
+/**
+ * See StartReorderBarrierDrawable.
+ * EndReorderBarrierDrawable relies on StartReorderBarrierDrawable to host and sort the render
+ * nodes by Z index. When EndReorderBarrierDrawable is drawn it will draw all render nodes in the
+ * range with positive Z index. It is also responsible for drawing shadows for the nodes
+ * corresponding to their z-index.
+ */
+class EndReorderBarrierDrawable : public SkDrawable {
+public:
+    explicit EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier);
+protected:
+    virtual SkRect onGetBounds() override {
+        return SkRect::MakeLargest();
+    }
+    virtual void onDraw(SkCanvas* canvas) override;
+private:
+    void drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster);
+    StartReorderBarrierDrawable* mStartBarrier;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
new file mode 100644
index 0000000..c734097e
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 {
+namespace skiapipeline {
+
+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 skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
new file mode 100644
index 0000000..734aae4a
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -0,0 +1,155 @@
+/*
+ * 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 "GLFunctorDrawable.h"
+#include "RenderNodeDrawable.h"
+
+#include <deque>
+#include <SkLiteDL.h>
+#include <SkPictureRecorder.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/**
+ * 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 skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaFrameRenderer.h b/libs/hwui/pipeline/skia/SkiaFrameRenderer.h
new file mode 100644
index 0000000..70207c1
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaFrameRenderer.h
@@ -0,0 +1,54 @@
+/*
+ * 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
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/**
+ * TODO: this is a stub that will be added in a subsquent CL
+ */
+class SkiaFrameRenderer {
+public:
+
+    static bool skpCaptureEnabled() { return false; }
+
+    // TODO avoids unused compile error but we need to pass this to the reorder drawables!
+    static float getLightRadius() {
+        return 1.0f;
+    }
+
+    static uint8_t getAmbientShadowAlpha() {
+        return 1;
+    }
+
+    static uint8_t getSpotShadowAlpha() {
+        return 1;
+    }
+
+    static Vector3 getLightCenter() {
+        Vector3 result;
+        result.x = result.y = result.z = 1.0f;
+        return result;
+    }
+
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
new file mode 100644
index 0000000..8a42983
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -0,0 +1,252 @@
+/*
+ * 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 "SkiaRecordingCanvas.h"
+
+#include "Layer.h"
+#include "RenderNode.h"
+#include "LayerDrawable.h"
+#include "NinePatchUtils.h"
+#include "pipeline/skia/AnimatedDrawables.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+// ----------------------------------------------------------------------------
+// Recording Canvas Setup
+// ----------------------------------------------------------------------------
+
+void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width,
+        int height) {
+    mBarrierPending = false;
+    mCurrentBarrier = nullptr;
+    SkASSERT(mDisplayList.get() == nullptr);
+
+    if (renderNode) {
+        mDisplayList = renderNode->detachAvailableList();
+    }
+    SkRect bounds = SkRect::MakeWH(width, height);
+    if (mDisplayList) {
+        mDisplayList->reset(nullptr, bounds);
+    } else {
+        mDisplayList.reset(new SkiaDisplayList(bounds));
+    }
+
+    mRecorder.reset(mDisplayList->mDrawable.get());
+    SkiaCanvas::reset(&mRecorder);
+}
+
+uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
+    // close any existing chunks if necessary
+    insertReorderBarrier(false);
+    mRecorder.restoreToCount(1);
+    return mDisplayList.release();
+}
+
+// ----------------------------------------------------------------------------
+// Recording Canvas draw operations: View System
+// ----------------------------------------------------------------------------
+
+void SkiaRecordingCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+        uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+        uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+        uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) {
+    drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom,
+            rx, ry, paint));
+}
+
+void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x,
+        uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
+        uirenderer::CanvasPropertyPaint* paint) {
+    drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint));
+}
+
+void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
+    mBarrierPending = enableReorder;
+
+    if (nullptr != mCurrentBarrier) {
+        // finish off the existing chunk
+        SkDrawable* drawable =
+                mDisplayList->allocateDrawable<EndReorderBarrierDrawable>(
+                mCurrentBarrier);
+        mCurrentBarrier = nullptr;
+        drawDrawable(drawable);
+    }
+}
+
+void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
+    if (layerUpdater != nullptr && layerUpdater->backingLayer() != nullptr) {
+        uirenderer::Layer* layer = layerUpdater->backingLayer();
+        sk_sp<SkDrawable> drawable(new LayerDrawable(layer));
+        drawDrawable(drawable.get());
+    }
+}
+
+void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
+    // lazily create the chunk if needed
+    if (mBarrierPending) {
+        mCurrentBarrier = (StartReorderBarrierDrawable*)
+                mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
+                mDisplayList.get());
+        drawDrawable(mCurrentBarrier);
+        mBarrierPending = false;
+    }
+
+    // record the child node
+    mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas());
+    drawDrawable(&mDisplayList->mChildNodes.back());
+
+    // use staging property, since recording on UI thread
+    if (renderNode->stagingProperties().isProjectionReceiver()) {
+        mDisplayList->mIsProjectionReceiver = true;
+        // set projectionReceiveIndex so that RenderNode.hasProjectionReceiver returns true
+        mDisplayList->projectionReceiveIndex = mDisplayList->mChildNodes.size() - 1;
+    }
+}
+
+void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
+        uirenderer::GlFunctorLifecycleListener* listener) {
+    mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
+    drawDrawable(&mDisplayList->mChildFunctors.back());
+}
+
+class VectorDrawable : public SkDrawable {
+ public:
+    VectorDrawable(VectorDrawableRoot* tree) : mRoot(tree) {}
+
+ protected:
+     virtual SkRect onGetBounds() override {
+         return SkRect::MakeLargest();
+     }
+     virtual void onDraw(SkCanvas* canvas) override {
+         Bitmap& hwuiBitmap = mRoot->getBitmapUpdateIfDirty();
+         SkBitmap bitmap;
+         hwuiBitmap.getSkBitmap(&bitmap);
+         SkPaint* paint = mRoot->getPaint();
+         canvas->drawBitmapRect(bitmap, mRoot->mutateProperties()->getBounds(), paint);
+         /*
+          * TODO we can draw this directly but need to address the following...
+          *
+          * 1) Add drawDirect(SkCanvas*) to VectorDrawableRoot
+          * 2) fix VectorDrawable.cpp's Path::draw to not make a temporary path
+          *    so that we don't break caching
+          * 3) figure out how to set path's as volatile during animation
+          * 4) if mRoot->getPaint() != null either promote to layer (during
+          *    animation) or cache in SkSurface (for static content)
+          *
+          */
+     }
+
+ private:
+    sp<VectorDrawableRoot> mRoot;
+};
+
+void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+    drawDrawable(mDisplayList->allocateDrawable<VectorDrawable>(tree));
+    mDisplayList->mVectorDrawables.push_back(tree);
+}
+
+// ----------------------------------------------------------------------------
+// Recording Canvas draw operations: Bitmaps
+// ----------------------------------------------------------------------------
+
+inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPaint) {
+    if (origPaint && origPaint->isAntiAlias()) {
+        *tmpPaint = *origPaint;
+        tmpPaint->setAntiAlias(false);
+        return tmpPaint;
+    } else {
+        return origPaint;
+    }
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
+    SkBitmap skBitmap;
+    bitmap.getSkBitmap(&skBitmap);
+
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
+    if (!skBitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+    SkPaint tmpPaint;
+    mRecorder.drawImage(image, left, top, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix,
+        const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
+    SkAutoCanvasRestore acr(&mRecorder, true);
+    concat(matrix);
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+    if (!bitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+    SkPaint tmpPaint;
+    mRecorder.drawImage(image, 0, 0, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop,
+        float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight,
+        float dstBottom, const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
+    SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
+    SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+    if (!bitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+    SkPaint tmpPaint;
+    mRecorder.drawImageRect(image, srcRect, dstRect, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk,
+        float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
+
+    SkCanvas::Lattice lattice;
+    NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
+
+    lattice.fFlags = nullptr;
+    int numFlags = 0;
+    if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
+        // We can expect the framework to give us a color for every distinct rect.
+        // Skia requires placeholder flags for degenerate rects.
+        numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
+    }
+
+    SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags);
+    if (numFlags > 0) {
+        NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk);
+    }
+
+    lattice.fBounds = nullptr;
+    SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+    if (!bitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+
+    SkPaint tmpPaint;
+    mRecorder.drawImageLattice(image.get(), lattice, dst, nonAAPaint(paint, &tmpPaint));
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
new file mode 100644
index 0000000..8aef97f
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -0,0 +1,94 @@
+/*
+ * 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 "SkiaCanvas.h"
+#include "SkiaDisplayList.h"
+#include "ReorderBarrierDrawables.h"
+#include <SkLiteRecorder.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/**
+ * A SkiaCanvas implementation that records drawing operations for deferred rendering backed by a
+ * SkLiteRecorder and a SkiaDisplayList.
+ */
+class SkiaRecordingCanvas : public SkiaCanvas {
+ public:
+    explicit SkiaRecordingCanvas(uirenderer::RenderNode* renderNode, int width, int height) {
+        initDisplayList(renderNode, width, height);
+    }
+
+    virtual void setBitmap(const SkBitmap& bitmap) override {
+        LOG_ALWAYS_FATAL("DisplayListCanvas is not backed by a bitmap.");
+    }
+
+    virtual void resetRecording(int width, int height,
+            uirenderer::RenderNode* renderNode) override {
+        initDisplayList(renderNode, width, height);
+    }
+
+    virtual uirenderer::DisplayList* finishRecording() override;
+
+    virtual void drawBitmap(Bitmap& bitmap, float left, float top,
+            const SkPaint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
+            const SkPaint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, const SkPaint* paint) override;
+    virtual void drawNinePatch(Bitmap& hwuiBitmap, const android::Res_png_9patch& chunk,
+            float dstLeft, float dstTop, float dstRight, float dstBottom,
+            const SkPaint* paint) override;
+
+    virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+            uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+            uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+            uirenderer::CanvasPropertyPrimitive* ry,
+            uirenderer::CanvasPropertyPaint* paint) override;
+    virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x,
+            uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
+            uirenderer::CanvasPropertyPaint* paint) override;
+
+    virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
+
+    virtual void insertReorderBarrier(bool enableReorder) override;
+    virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
+    virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
+    virtual void callDrawGLFunction(Functor* functor,
+                                    uirenderer::GlFunctorLifecycleListener* listener) override;
+
+private:
+    SkLiteRecorder mRecorder;
+    std::unique_ptr<SkiaDisplayList> mDisplayList;
+    bool mBarrierPending;
+    StartReorderBarrierDrawable* mCurrentBarrier;
+
+    /**
+     *  A new SkiaDisplayList is created or recycled if available.
+     *
+     *  @param renderNode is optional and used to recycle an old display list.
+     *  @param width used to calculate recording bounds.
+     *  @param height used to calculate recording bounds.
+     */
+    void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height);
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android