Refactor CanvasContext: move OpenGL specific code

Move OpenGL specific code from CanvasContext into a new class
OpenGLPipeline.

Change-Id: I4363053f890701a4235927b59ec588861488ea8f
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 50e982f..5637dbc 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -504,7 +504,7 @@
     ContextFactory factory;
     RenderProxy* proxy = new RenderProxy(false, rootNode, &factory);
     proxy->loadSystemProperties();
-    proxy->setSwapBehavior(kSwap_discardBuffer);
+    proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
     proxy->initialize(surface);
     // Shadows can't be used via this interface, so just set the light source
     // to all 0s.
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 95b28d3..3cce353 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -24,6 +24,7 @@
     renderstate/Stencil.cpp \
     renderstate/TextureState.cpp \
     renderthread/CanvasContext.cpp \
+    renderthread/OpenGLPipeline.cpp \
     renderthread/DrawFrameTask.cpp \
     renderthread/EglManager.cpp \
     renderthread/RenderProxy.cpp \
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d36ebc7..0028aec 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -30,6 +30,7 @@
 #include "renderstate/RenderState.h"
 #include "renderstate/Stencil.h"
 #include "protos/hwui.pb.h"
+#include "OpenGLPipeline.h"
 #include "utils/GLUtils.h"
 #include "utils/TimeUtils.h"
 
@@ -65,9 +66,11 @@
         bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) {
 
     auto renderType = Properties::getRenderPipelineType();
+
     switch (renderType) {
         case RenderPipelineType::OpenGL:
-            return new CanvasContext(thread, translucent, rootRenderNode, contextFactory);
+            return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
+                    std::make_unique<OpenGLPipeline>(thread));
         case RenderPipelineType::SkiaGL:
             //TODO: implement SKIA GL
             LOG_ALWAYS_FATAL("skiaGL canvas type not implemented.");
@@ -84,14 +87,15 @@
 }
 
 CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
-        RenderNode* rootRenderNode, IContextFactory* contextFactory)
+        RenderNode* rootRenderNode, IContextFactory* contextFactory,
+        std::unique_ptr<IRenderPipeline> renderPipeline)
         : mRenderThread(thread)
-        , mEglManager(thread.eglManager())
         , mOpaque(!translucent)
         , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
         , mJankTracker(thread.timeLord().frameIntervalNanos())
         , mProfiler(mFrames)
-        , mContentDrawBounds(0, 0, 0, 0) {
+        , mContentDrawBounds(0, 0, 0, 0)
+        , mRenderPipeline(std::move(renderPipeline)) {
     mRenderNodes.emplace_back(rootRenderNode);
     mRenderThread.renderState().registerCanvasContext(this);
     mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
@@ -115,24 +119,15 @@
 
     mNativeSurface = surface;
 
-    if (mEglSurface != EGL_NO_SURFACE) {
-        mEglManager.destroySurface(mEglSurface);
-        mEglSurface = EGL_NO_SURFACE;
-    }
-
-    if (surface) {
-        mEglSurface = mEglManager.createSurface(surface);
-    }
+    bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior);
 
     mFrameNumber = -1;
 
-    if (mEglSurface != EGL_NO_SURFACE) {
-        const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer);
-        mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
-        mHaveNewSurface = true;
-        mSwapHistory.clear();
+    if (hasSurface) {
+         mHaveNewSurface = true;
+         mSwapHistory.clear();
     } else {
-        mRenderThread.removeFrameCallback(this);
+         mRenderThread.removeFrameCallback(this);
     }
 }
 
@@ -157,9 +152,7 @@
         mStopped = stopped;
         if (mStopped) {
             mRenderThread.removeFrameCallback(this);
-            if (mEglManager.isCurrent(mEglSurface)) {
-                mEglManager.makeCurrent(EGL_NO_SURFACE);
-            }
+            mRenderPipeline->onStop();
         } else if (mIsDirty && hasSurface()) {
             mRenderThread.postFrameCallback(this);
         }
@@ -184,14 +177,23 @@
 bool CanvasContext::makeCurrent() {
     if (mStopped) return false;
 
-    // TODO: Figure out why this workaround is needed, see b/13913604
-    // In the meantime this matches the behavior of GLRenderer, so it is not a regression
-    EGLint error = 0;
-    mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface, &error);
-    if (error) {
-        setSurface(nullptr);
+    auto result = mRenderPipeline->makeCurrent();
+    switch (result) {
+        case MakeCurrentResult::AlreadyCurrent:
+            return true;
+        case MakeCurrentResult::Failed:
+            mHaveNewSurface = true;
+            setSurface(nullptr);
+            return false;
+        case MakeCurrentResult::Succeeded:
+            mHaveNewSurface = true;
+            return true;
+        default:
+            LOG_ALWAYS_FATAL("unexpected result %d from IRenderPipeline::makeCurrent",
+                    (int32_t) result);
     }
-    return !error;
+
+    return true;
 }
 
 static bool wasSkipped(FrameInfo* info) {
@@ -328,9 +330,6 @@
 }
 
 void CanvasContext::draw() {
-    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
-            "drawRenderNode called on a context with no surface!");
-
     SkRect dirty;
     mDamageAccumulator.finish(&dirty);
 
@@ -342,98 +341,27 @@
 
     mCurrentFrameInfo->markIssueDrawCommandsStart();
 
-    Frame frame = mEglManager.beginFrame(mEglSurface);
+    Frame frame = mRenderPipeline->getFrame();
 
-    if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) {
-        // can't rely on prior content of window if viewport size changes
-        dirty.setEmpty();
-        mLastFrameWidth = frame.width();
-        mLastFrameHeight = frame.height();
-    } else if (mHaveNewSurface || frame.bufferAge() == 0) {
-        // New surface needs a full draw
-        dirty.setEmpty();
-    } else {
-        if (!dirty.isEmpty() && !dirty.intersect(0, 0, frame.width(), frame.height())) {
-            ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
-                    SK_RECT_ARGS(dirty), frame.width(), frame.height());
-            dirty.setEmpty();
-        }
-        profiler().unionDirty(&dirty);
-    }
+    SkRect windowDirty = computeDirtyRect(frame, &dirty);
 
-    if (dirty.isEmpty()) {
-        dirty.set(0, 0, frame.width(), frame.height());
-    }
-
-    // At this point dirty is the area of the screen to update. However,
-    // the area of the frame we need to repaint is potentially different, so
-    // stash the screen area for later
-    SkRect screenDirty(dirty);
-
-    // If the buffer age is 0 we do a full-screen repaint (handled above)
-    // If the buffer age is 1 the buffer contents are the same as they were
-    // last frame so there's nothing to union() against
-    // Therefore we only care about the > 1 case.
-    if (frame.bufferAge() > 1) {
-        if (frame.bufferAge() > (int) mSwapHistory.size()) {
-            // We don't have enough history to handle this old of a buffer
-            // Just do a full-draw
-            dirty.set(0, 0, frame.width(), frame.height());
-        } else {
-            // At this point we haven't yet added the latest frame
-            // to the damage history (happens below)
-            // So we need to damage
-            for (int i = mSwapHistory.size() - 1;
-                    i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) {
-                dirty.join(mSwapHistory[i].damage);
-            }
-        }
-    }
-
-    mEglManager.damageFrame(frame, dirty);
-
-    auto& caches = Caches::getInstance();
-    FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), mLightGeometry, caches);
-
-    frameBuilder.deferLayers(mLayerUpdateQueue);
-    mLayerUpdateQueue.clear();
-
-    frameBuilder.deferRenderNodeScene(mRenderNodes, mContentDrawBounds);
-
-    BakedOpRenderer renderer(caches, mRenderThread.renderState(),
-            mOpaque, mLightInfo);
-    frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
-    profiler().draw(&renderer);
-    bool drew = renderer.didDraw();
-
-    // post frame cleanup
-    caches.clearGarbage();
-    caches.pathCache.trim();
-    caches.tessellationCache.trim();
-
-#if DEBUG_MEMORY_USAGE
-    mCaches.dumpMemoryUsage();
-#else
-    if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) {
-        caches.dumpMemoryUsage();
-    }
-#endif
+    bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
+            mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler()));
 
     waitOnFences();
 
-    GL_CHECKPOINT(LOW);
+    bool requireSwap = false;
+    bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo,
+            &requireSwap);
 
-    // Even if we decided to cancel the frame, from the perspective of jank
-    // metrics the frame was swapped at this point
-    mCurrentFrameInfo->markSwapBuffers();
     mIsDirty = false;
 
-    if (drew || mEglManager.damageRequiresSwap()) {
-        if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) {
+    if (requireSwap) {
+        if (!didSwap) { //some error happened
             setSurface(nullptr);
         }
         SwapHistory& swap = mSwapHistory.next();
-        swap.damage = screenDirty;
+        swap.damage = windowDirty;
         swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC);
         swap.vsyncTime = mRenderThread.timeLord().latestVsync();
         mHaveNewSurface = false;
@@ -469,7 +397,7 @@
 
 // Called by choreographer to do an RT-driven animation
 void CanvasContext::doFrame() {
-    if (CC_UNLIKELY(mEglSurface == EGL_NO_SURFACE)) return;
+    if (!mRenderPipeline->isSurfaceReady()) return;
     prepareAndDraw(nullptr);
 }
 
@@ -519,7 +447,7 @@
 
 void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) {
     ATRACE_CALL();
-    if (!mEglManager.hasEglContext()) return;
+    if (!mRenderPipeline->isContextReady()) return;
 
     // buildLayer() will leave the tree in an unknown state, so we must stop drawing
     stopDrawing();
@@ -536,37 +464,24 @@
     // purposes when the frame is actually drawn
     node->setPropertyFieldsDirty(RenderNode::GENERIC);
 
-    static const std::vector< sp<RenderNode> > emptyNodeList;
-    auto& caches = Caches::getInstance();
-    FrameBuilder frameBuilder(mLayerUpdateQueue, mLightGeometry, caches);
-    mLayerUpdateQueue.clear();
-    BakedOpRenderer renderer(caches, mRenderThread.renderState(),
-            mOpaque, mLightInfo);
-    LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case");
-    frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+    mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo);
 
     node->incStrong(nullptr);
     mPrefetchedLayers.insert(node);
 }
 
 bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
-    layer->apply();
-    return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap)
-            == CopyResult::Success;
+    return mRenderPipeline->copyLayerInto(layer, bitmap);
 }
 
 void CanvasContext::destroyHardwareResources(TreeObserver* observer) {
     stopDrawing();
-    if (mEglManager.hasEglContext()) {
+    if (mRenderPipeline->isContextReady()) {
         freePrefetchedLayers(observer);
         for (const sp<RenderNode>& node : mRenderNodes) {
             node->destroyHardwareResources(observer);
         }
-        Caches& caches = Caches::getInstance();
-        // Make sure to release all the textures we were owning as there won't
-        // be another draw
-        caches.textureCache.resetMarkInUse(this);
-        mRenderThread.renderState().flush(Caches::FlushMode::Layers);
+        mRenderPipeline->onDestroyHardwareResources();
     }
 }
 
@@ -584,8 +499,7 @@
 }
 
 Layer* CanvasContext::createTextureLayer() {
-    mEglManager.initialize();
-    return LayerRenderer::createTextureLayer(mRenderThread.renderState());
+    return mRenderPipeline->createTextureLayer();
 }
 
 void CanvasContext::setTextureAtlas(RenderThread& thread,
@@ -665,8 +579,8 @@
 
 class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> {
 public:
-    explicit FuncTaskProcessor(Caches& caches)
-            : TaskProcessor<bool>(&caches.tasks) {}
+    explicit FuncTaskProcessor(TaskManager* taskManager)
+            : TaskProcessor<bool>(taskManager) {}
 
     virtual void onProcess(const sp<Task<bool> >& task) override {
         FuncTask* t = static_cast<FuncTask*>(task.get());
@@ -677,7 +591,7 @@
 
 void CanvasContext::enqueueFrameWork(std::function<void()>&& func) {
     if (!mFrameWorkProcessor.get()) {
-        mFrameWorkProcessor = new FuncTaskProcessor(Caches::getInstance());
+        mFrameWorkProcessor = new FuncTaskProcessor(mRenderPipeline->getTaskManager());
     }
     sp<FuncTask> task(new FuncTask());
     task->func = func;
@@ -698,6 +612,56 @@
     return RenderPipelineType::SkiaGL == renderType || RenderPipelineType::Vulkan == renderType;
 }
 
+SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
+    if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) {
+        // can't rely on prior content of window if viewport size changes
+        dirty->setEmpty();
+        mLastFrameWidth = frame.width();
+        mLastFrameHeight = frame.height();
+    } else if (mHaveNewSurface || frame.bufferAge() == 0) {
+        // New surface needs a full draw
+        dirty->setEmpty();
+    } else {
+        if (!dirty->isEmpty() && !dirty->intersect(0, 0, frame.width(), frame.height())) {
+            ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
+                    SK_RECT_ARGS(*dirty), frame.width(), frame.height());
+            dirty->setEmpty();
+        }
+        profiler().unionDirty(dirty);
+    }
+
+    if (dirty->isEmpty()) {
+        dirty->set(0, 0, frame.width(), frame.height());
+    }
+
+    // At this point dirty is the area of the window to update. However,
+    // the area of the frame we need to repaint is potentially different, so
+    // stash the screen area for later
+    SkRect windowDirty(*dirty);
+
+    // If the buffer age is 0 we do a full-screen repaint (handled above)
+    // If the buffer age is 1 the buffer contents are the same as they were
+    // last frame so there's nothing to union() against
+    // Therefore we only care about the > 1 case.
+    if (frame.bufferAge() > 1) {
+        if (frame.bufferAge() > (int) mSwapHistory.size()) {
+            // We don't have enough history to handle this old of a buffer
+            // Just do a full-draw
+            dirty->set(0, 0, frame.width(), frame.height());
+        } else {
+            // At this point we haven't yet added the latest frame
+            // to the damage history (happens below)
+            // So we need to damage
+            for (int i = mSwapHistory.size() - 1;
+                    i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) {
+                dirty->join(mSwapHistory[i].damage);
+            }
+        }
+    }
+
+    return windowDirty;
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 72f1268..de4cc48 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -24,6 +24,7 @@
 #include "FrameInfoVisualizer.h"
 #include "FrameMetricsReporter.h"
 #include "IContextFactory.h"
+#include "IRenderPipeline.h"
 #include "LayerUpdateQueue.h"
 #include "RenderNode.h"
 #include "thread/Task.h"
@@ -56,11 +57,7 @@
 namespace renderthread {
 
 class EglManager;
-
-enum SwapBehavior {
-    kSwap_default,
-    kSwap_discardBuffer,
-};
+class Frame;
 
 // This per-renderer class manages the bridge between the global EGL context
 // and the render surface.
@@ -164,7 +161,7 @@
 
 private:
     CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
-            IContextFactory* contextFactory);
+            IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
 
     friend class RegisterFrameCallbackTask;
     // TODO: Replace with something better for layer & other GL object
@@ -179,21 +176,20 @@
 
     bool isSwapChainStuffed();
 
+    SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
+
     EGLint mLastFrameWidth = 0;
     EGLint mLastFrameHeight = 0;
 
     RenderThread& mRenderThread;
-    EglManager& mEglManager;
     sp<Surface> mNativeSurface;
-    EGLSurface mEglSurface = EGL_NO_SURFACE;
     // stopped indicates the CanvasContext will reject actual redraw operations,
     // and defer repaint until it is un-stopped
     bool mStopped = false;
     // CanvasContext is dirty if it has received an update that it has not
     // painted onto its surface.
     bool mIsDirty = false;
-    bool mBufferPreserved = false;
-    SwapBehavior mSwapBehavior = kSwap_default;
+    SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default;
     struct SwapHistory {
         SkRect damage;
         nsecs_t vsyncTime;
@@ -238,6 +234,7 @@
 
     std::vector< sp<FuncTask> > mFrameFences;
     sp<TaskProcessor<bool> > mFrameWorkProcessor;
+    std::unique_ptr<IRenderPipeline> mRenderPipeline;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
new file mode 100644
index 0000000..0c0fbe9
--- /dev/null
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -0,0 +1,76 @@
+/*
+ * 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 "FrameInfoVisualizer.h"
+#include "EglManager.h"
+
+#include <SkRect.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class Surface;
+
+namespace uirenderer {
+
+class DeferredLayerUpdater;
+
+namespace renderthread {
+
+enum class SwapBehavior {
+    kSwap_default,
+    kSwap_discardBuffer,
+};
+
+enum class MakeCurrentResult {
+    AlreadyCurrent,
+    Failed,
+    Succeeded
+};
+
+class IRenderPipeline {
+public:
+    virtual MakeCurrentResult makeCurrent() = 0;
+    virtual Frame getFrame() = 0;
+    virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+            const FrameBuilder::LightGeometry& lightGeometry,
+            LayerUpdateQueue* layerUpdateQueue,
+            const Rect& contentDrawBounds, bool opaque,
+            const BakedOpRenderer::LightInfo& lightInfo,
+            const std::vector< sp<RenderNode> >& renderNodes,
+            FrameInfoVisualizer* profiler) = 0;
+    virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
+            FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
+    virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0;
+    virtual Layer* createTextureLayer() = 0;
+    virtual bool setSurface(Surface* window, SwapBehavior swapBehavior) = 0;
+    virtual void onStop() = 0;
+    virtual bool isSurfaceReady() = 0;
+    virtual bool isContextReady() = 0;
+    virtual void onDestroyHardwareResources() = 0;
+    virtual void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+            LayerUpdateQueue* layerUpdateQueue, bool opaque,
+            const BakedOpRenderer::LightInfo& lightInfo) = 0;
+    virtual TaskManager* getTaskManager() = 0;
+
+    virtual ~IRenderPipeline() {}
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
new file mode 100644
index 0000000..3a2b155
--- /dev/null
+++ b/libs/hwui/renderthread/OpenGLPipeline.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 "OpenGLPipeline.h"
+
+#include "DeferredLayerUpdater.h"
+#include "EglManager.h"
+#include "LayerRenderer.h"
+#include "renderstate/RenderState.h"
+#include "Readback.h"
+
+#include <android/native_window.h>
+#include <cutils/properties.h>
+#include <strings.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+OpenGLPipeline::OpenGLPipeline(RenderThread& thread)
+        :  mEglManager(thread.eglManager()), mRenderThread(thread) {
+}
+
+MakeCurrentResult OpenGLPipeline::makeCurrent() {
+    // TODO: Figure out why this workaround is needed, see b/13913604
+    // In the meantime this matches the behavior of GLRenderer, so it is not a regression
+    EGLint error = 0;
+    bool haveNewSurface = mEglManager.makeCurrent(mEglSurface, &error);
+
+    Caches::getInstance().textureCache.resetMarkInUse(this);
+    if (!haveNewSurface) {
+        return MakeCurrentResult::AlreadyCurrent;
+    }
+    return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;
+}
+
+Frame OpenGLPipeline::getFrame() {
+    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+                "drawRenderNode called on a context with no surface!");
+    return mEglManager.beginFrame(mEglSurface);
+}
+
+bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+        const FrameBuilder::LightGeometry& lightGeometry,
+        LayerUpdateQueue* layerUpdateQueue,
+        const Rect& contentDrawBounds, bool opaque,
+        const BakedOpRenderer::LightInfo& lightInfo,
+        const std::vector< sp<RenderNode> >& renderNodes,
+        FrameInfoVisualizer* profiler) {
+
+    mEglManager.damageFrame(frame, dirty);
+
+    bool drew = false;
+
+
+    auto& caches = Caches::getInstance();
+    FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), lightGeometry, caches);
+
+    frameBuilder.deferLayers(*layerUpdateQueue);
+    layerUpdateQueue->clear();
+
+    frameBuilder.deferRenderNodeScene(renderNodes, contentDrawBounds);
+
+    BakedOpRenderer renderer(caches, mRenderThread.renderState(),
+            opaque, lightInfo);
+    frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+    profiler->draw(&renderer);
+    drew = renderer.didDraw();
+
+    // post frame cleanup
+    caches.clearGarbage();
+    caches.pathCache.trim();
+    caches.tessellationCache.trim();
+
+#if DEBUG_MEMORY_USAGE
+    mCaches.dumpMemoryUsage();
+#else
+    if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) {
+        caches.dumpMemoryUsage();
+    }
+#endif
+
+    return drew;
+}
+
+bool OpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
+        FrameInfo* currentFrameInfo, bool* requireSwap) {
+
+    GL_CHECKPOINT(LOW);
+
+    // Even if we decided to cancel the frame, from the perspective of jank
+    // metrics the frame was swapped at this point
+    currentFrameInfo->markSwapBuffers();
+
+    *requireSwap = drew || mEglManager.damageRequiresSwap();
+
+    if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
+        return false;
+    }
+
+    return *requireSwap;
+}
+
+bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+    layer->apply();
+    return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap)
+            == CopyResult::Success;
+}
+
+Layer* OpenGLPipeline::createTextureLayer() {
+    mEglManager.initialize();
+    return LayerRenderer::createTextureLayer(mRenderThread.renderState());
+}
+
+void OpenGLPipeline::onStop() {
+    if (mEglManager.isCurrent(mEglSurface)) {
+        mEglManager.makeCurrent(EGL_NO_SURFACE);
+    }
+}
+
+bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
+
+    if (mEglSurface != EGL_NO_SURFACE) {
+        mEglManager.destroySurface(mEglSurface);
+        mEglSurface = EGL_NO_SURFACE;
+    }
+
+    if (surface) {
+        mEglSurface = mEglManager.createSurface(surface);
+    }
+
+    if (mEglSurface != EGL_NO_SURFACE) {
+        const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
+        mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
+        return true;
+    }
+
+    return false;
+}
+
+bool OpenGLPipeline::isSurfaceReady() {
+    return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);
+}
+
+bool OpenGLPipeline::isContextReady() {
+    return CC_LIKELY(mEglManager.hasEglContext());
+}
+
+void OpenGLPipeline::onDestroyHardwareResources() {
+    Caches& caches = Caches::getInstance();
+    // Make sure to release all the textures we were owning as there won't
+    // be another draw
+    caches.textureCache.resetMarkInUse(this);
+    mRenderThread.renderState().flush(Caches::FlushMode::Layers);
+}
+
+void OpenGLPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+        LayerUpdateQueue* layerUpdateQueue, bool opaque,
+        const BakedOpRenderer::LightInfo& lightInfo) {
+    static const std::vector< sp<RenderNode> > emptyNodeList;
+    auto& caches = Caches::getInstance();
+    FrameBuilder frameBuilder(*layerUpdateQueue, lightGeometry, caches);
+    layerUpdateQueue->clear();
+    BakedOpRenderer renderer(caches, mRenderThread.renderState(),
+            opaque, lightInfo);
+    LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case");
+    frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+}
+
+TaskManager* OpenGLPipeline::getTaskManager() {
+    return &Caches::getInstance().tasks;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h
new file mode 100644
index 0000000..a6d22ee
--- /dev/null
+++ b/libs/hwui/renderthread/OpenGLPipeline.h
@@ -0,0 +1,69 @@
+/*
+ * 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 "CanvasContext.h"
+#include "BakedOpDispatcher.h"
+#include "BakedOpRenderer.h"
+#include "FrameBuilder.h"
+#include "IRenderPipeline.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class Frame;
+
+
+class OpenGLPipeline : public IRenderPipeline {
+public:
+    OpenGLPipeline(RenderThread& thread);
+    virtual ~OpenGLPipeline() {}
+
+    MakeCurrentResult makeCurrent() override;
+    Frame getFrame() override;
+    bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+            const FrameBuilder::LightGeometry& lightGeometry,
+            LayerUpdateQueue* layerUpdateQueue,
+            const Rect& contentDrawBounds, bool opaque,
+            const BakedOpRenderer::LightInfo& lightInfo,
+            const std::vector< sp<RenderNode> >& renderNodes,
+            FrameInfoVisualizer* profiler) override;
+    bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
+            FrameInfo* currentFrameInfo, bool* requireSwap) override;
+    bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
+    Layer* createTextureLayer() override;
+    bool setSurface(Surface* window, SwapBehavior swapBehavior) override;
+    void onStop() override;
+    bool isSurfaceReady() override;
+    bool isContextReady() override;
+    void onDestroyHardwareResources() override;
+    void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+            LayerUpdateQueue* layerUpdateQueue, bool opaque,
+            const BakedOpRenderer::LightInfo& lightInfo) override;
+    TaskManager* getTaskManager() override;
+
+private:
+    EglManager& mEglManager;
+    EGLSurface mEglSurface = EGL_NO_SURFACE;
+    bool mBufferPreserved = false;
+    RenderThread& mRenderThread;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */