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 */