summaryrefslogtreecommitdiffstats
path: root/libs/hwui/renderthread
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/renderthread')
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp571
-rw-r--r--libs/hwui/renderthread/CanvasContext.h101
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp140
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h95
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp307
-rw-r--r--libs/hwui/renderthread/RenderProxy.h107
-rw-r--r--libs/hwui/renderthread/RenderTask.cpp38
-rw-r--r--libs/hwui/renderthread/RenderTask.h99
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp298
-rw-r--r--libs/hwui/renderthread/RenderThread.h116
-rw-r--r--libs/hwui/renderthread/TimeLord.cpp46
-rw-r--r--libs/hwui/renderthread/TimeLord.h49
12 files changed, 1967 insertions, 0 deletions
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
new file mode 100644
index 0000000..48cd8fc
--- /dev/null
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CanvasContext"
+
+#include "CanvasContext.h"
+
+#include <cutils/properties.h>
+#include <private/hwui/DrawGlInfo.h>
+#include <strings.h>
+
+#include "RenderThread.h"
+#include "../Caches.h"
+#include "../DeferredLayerUpdater.h"
+#include "../LayerRenderer.h"
+#include "../OpenGLRenderer.h"
+#include "../Stencil.h"
+
+#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
+#define GLES_VERSION 2
+#define USE_TEXTURE_ATLAS false
+
+// Android-specific addition that is used to show when frames began in systrace
+EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+#define ERROR_CASE(x) case x: return #x;
+static const char* egl_error_str(EGLint error) {
+ switch (error) {
+ ERROR_CASE(EGL_SUCCESS)
+ ERROR_CASE(EGL_NOT_INITIALIZED)
+ ERROR_CASE(EGL_BAD_ACCESS)
+ ERROR_CASE(EGL_BAD_ALLOC)
+ ERROR_CASE(EGL_BAD_ATTRIBUTE)
+ ERROR_CASE(EGL_BAD_CONFIG)
+ ERROR_CASE(EGL_BAD_CONTEXT)
+ ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
+ ERROR_CASE(EGL_BAD_DISPLAY)
+ ERROR_CASE(EGL_BAD_MATCH)
+ ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
+ ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
+ ERROR_CASE(EGL_BAD_PARAMETER)
+ ERROR_CASE(EGL_BAD_SURFACE)
+ ERROR_CASE(EGL_CONTEXT_LOST)
+ default:
+ return "Unknown error";
+ }
+}
+static const char* egl_error_str() {
+ return egl_error_str(eglGetError());
+}
+
+static bool load_dirty_regions_property() {
+ char buf[PROPERTY_VALUE_MAX];
+ int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
+ return !strncasecmp("true", buf, len);
+}
+
+// This class contains the shared global EGL objects, such as EGLDisplay
+// and EGLConfig, which are re-used by CanvasContext
+class GlobalContext {
+public:
+ static GlobalContext* get();
+
+ // Returns true on success, false on failure
+ void initialize();
+
+ bool hasContext();
+
+ void usePBufferSurface();
+ EGLSurface createSurface(EGLNativeWindowType window);
+ void destroySurface(EGLSurface surface);
+
+ void destroy();
+
+ bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
+ // Returns true if the current surface changed, false if it was already current
+ bool makeCurrent(EGLSurface surface);
+ void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
+ void swapBuffers(EGLSurface surface);
+
+ bool enableDirtyRegions(EGLSurface surface);
+
+ void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
+
+private:
+ GlobalContext();
+ // GlobalContext is never destroyed, method is purposely not implemented
+ ~GlobalContext();
+
+ void loadConfig();
+ void createContext();
+ void initAtlas();
+
+ static GlobalContext* sContext;
+
+ EGLDisplay mEglDisplay;
+ EGLConfig mEglConfig;
+ EGLContext mEglContext;
+ EGLSurface mPBufferSurface;
+
+ const bool mRequestDirtyRegions;
+ bool mCanSetDirtyRegions;
+
+ EGLSurface mCurrentSurface;
+
+ sp<GraphicBuffer> mAtlasBuffer;
+ int64_t* mAtlasMap;
+ size_t mAtlasMapSize;
+};
+
+GlobalContext* GlobalContext::sContext = 0;
+
+GlobalContext* GlobalContext::get() {
+ if (!sContext) {
+ sContext = new GlobalContext();
+ }
+ return sContext;
+}
+
+GlobalContext::GlobalContext()
+ : mEglDisplay(EGL_NO_DISPLAY)
+ , mEglConfig(0)
+ , mEglContext(EGL_NO_CONTEXT)
+ , mPBufferSurface(EGL_NO_SURFACE)
+ , mRequestDirtyRegions(load_dirty_regions_property())
+ , mCurrentSurface(EGL_NO_SURFACE)
+ , mAtlasMap(NULL)
+ , mAtlasMapSize(0) {
+ mCanSetDirtyRegions = mRequestDirtyRegions;
+ ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
+}
+
+void GlobalContext::initialize() {
+ if (hasContext()) return;
+
+ mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+ "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
+
+ EGLint major, minor;
+ LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
+ "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
+
+ ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
+
+ loadConfig();
+ createContext();
+ usePBufferSurface();
+ Caches::getInstance().init();
+ initAtlas();
+}
+
+bool GlobalContext::hasContext() {
+ return mEglDisplay != EGL_NO_DISPLAY;
+}
+
+void GlobalContext::loadConfig() {
+ EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+ EGLint attribs[] = {
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_DEPTH_SIZE, 0,
+ EGL_CONFIG_CAVEAT, EGL_NONE,
+ EGL_STENCIL_SIZE, Stencil::getStencilSize(),
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
+ EGL_NONE
+ };
+
+ EGLint num_configs = 1;
+ if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
+ || num_configs != 1) {
+ // Failed to get a valid config
+ if (mCanSetDirtyRegions) {
+ ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
+ // Try again without dirty regions enabled
+ mCanSetDirtyRegions = false;
+ loadConfig();
+ } else {
+ LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
+ }
+ }
+}
+
+void GlobalContext::createContext() {
+ EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
+ mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
+ LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
+ "Failed to create context, error = %s", egl_error_str());
+}
+
+void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer,
+ int64_t* map, size_t mapSize) {
+
+ // Already initialized
+ if (mAtlasBuffer.get()) {
+ ALOGW("Multiple calls to setTextureAtlas!");
+ delete map;
+ return;
+ }
+
+ mAtlasBuffer = buffer;
+ mAtlasMap = map;
+ mAtlasMapSize = mapSize;
+
+ if (hasContext()) {
+ usePBufferSurface();
+ initAtlas();
+ }
+}
+
+void GlobalContext::initAtlas() {
+ if (USE_TEXTURE_ATLAS) {
+ Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize);
+ }
+}
+
+void GlobalContext::usePBufferSurface() {
+ LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+ "usePBufferSurface() called on uninitialized GlobalContext!");
+
+ if (mPBufferSurface == EGL_NO_SURFACE) {
+ EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
+ mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
+ }
+ makeCurrent(mPBufferSurface);
+}
+
+EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) {
+ initialize();
+ return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
+}
+
+void GlobalContext::destroySurface(EGLSurface surface) {
+ if (isCurrent(surface)) {
+ makeCurrent(EGL_NO_SURFACE);
+ }
+ if (!eglDestroySurface(mEglDisplay, surface)) {
+ ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
+ }
+}
+
+void GlobalContext::destroy() {
+ if (mEglDisplay == EGL_NO_DISPLAY) return;
+
+ usePBufferSurface();
+ if (Caches::hasInstance()) {
+ Caches::getInstance().terminate();
+ }
+
+ eglDestroyContext(mEglDisplay, mEglContext);
+ eglDestroySurface(mEglDisplay, mPBufferSurface);
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglTerminate(mEglDisplay);
+ eglReleaseThread();
+
+ mEglDisplay = EGL_NO_DISPLAY;
+ mEglContext = EGL_NO_CONTEXT;
+ mPBufferSurface = EGL_NO_SURFACE;
+ mCurrentSurface = EGL_NO_SURFACE;
+}
+
+bool GlobalContext::makeCurrent(EGLSurface surface) {
+ if (isCurrent(surface)) return false;
+
+ if (surface == EGL_NO_SURFACE) {
+ // If we are setting EGL_NO_SURFACE we don't care about any of the potential
+ // return errors, which would only happen if mEglDisplay had already been
+ // destroyed in which case the current context is already NO_CONTEXT
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
+ LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
+ (void*)surface, egl_error_str());
+ }
+ mCurrentSurface = surface;
+ return true;
+}
+
+void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
+ LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
+ "Tried to beginFrame on EGL_NO_SURFACE!");
+ makeCurrent(surface);
+ if (width) {
+ eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
+ }
+ if (height) {
+ eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
+ }
+ eglBeginFrame(mEglDisplay, surface);
+}
+
+void GlobalContext::swapBuffers(EGLSurface surface) {
+ eglSwapBuffers(mEglDisplay, surface);
+ EGLint err = eglGetError();
+ LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS,
+ "Encountered EGL error %d %s during rendering", err, egl_error_str(err));
+}
+
+bool GlobalContext::enableDirtyRegions(EGLSurface surface) {
+ if (!mRequestDirtyRegions) return false;
+
+ if (mCanSetDirtyRegions) {
+ if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) {
+ ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
+ (void*) surface, egl_error_str());
+ return false;
+ }
+ return true;
+ }
+ // Perhaps it is already enabled?
+ EGLint value;
+ if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) {
+ ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
+ (void*) surface, egl_error_str());
+ return false;
+ }
+ return value == EGL_BUFFER_PRESERVED;
+}
+
+CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode)
+ : mRenderThread(RenderThread::getInstance())
+ , mEglSurface(EGL_NO_SURFACE)
+ , mDirtyRegionsEnabled(false)
+ , mOpaque(!translucent)
+ , mCanvas(0)
+ , mHaveNewSurface(false)
+ , mRootRenderNode(rootRenderNode) {
+ mGlobalContext = GlobalContext::get();
+}
+
+CanvasContext::~CanvasContext() {
+ destroyCanvasAndSurface();
+ mRenderThread.removeFrameCallback(this);
+}
+
+void CanvasContext::destroyCanvasAndSurface() {
+ if (mCanvas) {
+ delete mCanvas;
+ mCanvas = 0;
+ }
+ setSurface(NULL);
+}
+
+void CanvasContext::setSurface(EGLNativeWindowType window) {
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mGlobalContext->destroySurface(mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+ }
+
+ if (window) {
+ mEglSurface = mGlobalContext->createSurface(window);
+ LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+ "Failed to create EGLSurface for window %p, eglErr = %s",
+ (void*) window, egl_error_str());
+ }
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
+ mHaveNewSurface = true;
+ makeCurrent();
+ } else {
+ mRenderThread.removeFrameCallback(this);
+ }
+}
+
+void CanvasContext::swapBuffers() {
+ mGlobalContext->swapBuffers(mEglSurface);
+ mHaveNewSurface = false;
+}
+
+void CanvasContext::requireSurface() {
+ LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+ "requireSurface() called but no surface set!");
+ makeCurrent();
+}
+
+bool CanvasContext::initialize(EGLNativeWindowType window) {
+ if (mCanvas) return false;
+ setSurface(window);
+ mCanvas = new OpenGLRenderer();
+ mCanvas->initProperties();
+ return true;
+}
+
+void CanvasContext::updateSurface(EGLNativeWindowType window) {
+ setSurface(window);
+}
+
+void CanvasContext::pauseSurface(EGLNativeWindowType window) {
+ // TODO: For now we just need a fence, in the future suspend any animations
+ // and such to prevent from trying to render into this surface
+}
+
+void CanvasContext::setup(int width, int height) {
+ if (!mCanvas) return;
+ mCanvas->setViewport(width, height);
+}
+
+void CanvasContext::setOpaque(bool opaque) {
+ mOpaque = opaque;
+}
+
+void CanvasContext::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
+ mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface);
+}
+
+void CanvasContext::prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters,
+ TreeInfo& info) {
+ LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot prepareDraw without a canvas!");
+ makeCurrent();
+
+ processLayerUpdates(layerUpdaters, info);
+ if (info.out.hasAnimations) {
+ // TODO: Uh... crap?
+ }
+ prepareTree(info);
+}
+
+void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters,
+ TreeInfo& info) {
+ for (size_t i = 0; i < layerUpdaters->size(); i++) {
+ DeferredLayerUpdater* update = layerUpdaters->itemAt(i);
+ bool success = update->apply(info);
+ LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
+ if (update->backingLayer()->deferredUpdateScheduled) {
+ mCanvas->pushLayerUpdate(update->backingLayer());
+ }
+ }
+}
+
+void CanvasContext::prepareTree(TreeInfo& info) {
+ mRenderThread.removeFrameCallback(this);
+
+ info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
+ mRootRenderNode->prepareTree(info);
+
+ if (info.out.hasAnimations) {
+ if (info.out.hasFunctors) {
+ info.out.requiresUiRedraw = true;
+ } else if (!info.out.requiresUiRedraw) {
+ // If animationsNeedsRedraw is set don't bother posting for an RT anim
+ // as we will just end up fighting the UI thread.
+ mRenderThread.postFrameCallback(this);
+ }
+ }
+}
+
+void CanvasContext::draw(Rect* dirty) {
+ LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
+ "drawDisplayList called on a context with no canvas or surface!");
+
+ EGLint width, height;
+ mGlobalContext->beginFrame(mEglSurface, &width, &height);
+ if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
+ mCanvas->setViewport(width, height);
+ dirty = NULL;
+ } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
+ dirty = NULL;
+ }
+
+ status_t status;
+ if (dirty && !dirty->isEmpty()) {
+ status = mCanvas->prepareDirty(dirty->left, dirty->top,
+ dirty->right, dirty->bottom, mOpaque);
+ } else {
+ status = mCanvas->prepare(mOpaque);
+ }
+
+ Rect outBounds;
+ status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds);
+
+ // TODO: Draw debug info
+ // TODO: Performance tracking
+
+ mCanvas->finish();
+
+ if (status & DrawGlInfo::kStatusDrew) {
+ swapBuffers();
+ }
+}
+
+// Called by choreographer to do an RT-driven animation
+void CanvasContext::doFrame() {
+ if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
+ return;
+ }
+
+ ATRACE_CALL();
+
+ TreeInfo info;
+ info.evaluateAnimations = true;
+ info.performStagingPush = false;
+ info.prepareTextures = false;
+
+ prepareTree(info);
+ draw(NULL);
+}
+
+void CanvasContext::invokeFunctor(Functor* functor) {
+ ATRACE_CALL();
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (mGlobalContext->hasContext()) {
+ requireGlContext();
+ mode = DrawGlInfo::kModeProcess;
+ }
+ (*functor)(mode, NULL);
+
+ if (mCanvas) {
+ mCanvas->resume();
+ }
+}
+
+bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+ requireGlContext();
+ TreeInfo info;
+ layer->apply(info);
+ return LayerRenderer::copyLayer(layer->backingLayer(), bitmap);
+}
+
+void CanvasContext::runWithGlContext(RenderTask* task) {
+ requireGlContext();
+ task->run();
+}
+
+Layer* CanvasContext::createRenderLayer(int width, int height) {
+ requireSurface();
+ return LayerRenderer::createRenderLayer(width, height);
+}
+
+Layer* CanvasContext::createTextureLayer() {
+ requireSurface();
+ return LayerRenderer::createTextureLayer();
+}
+
+void CanvasContext::requireGlContext() {
+ if (mEglSurface != EGL_NO_SURFACE) {
+ makeCurrent();
+ } else {
+ mGlobalContext->usePBufferSurface();
+ }
+}
+
+void CanvasContext::setTextureAtlas(const sp<GraphicBuffer>& buffer,
+ int64_t* map, size_t mapSize) {
+ GlobalContext::get()->setTextureAtlas(buffer, map, mapSize);
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
new file mode 100644
index 0000000..a793d42
--- /dev/null
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANVASCONTEXT_H_
+#define CANVASCONTEXT_H_
+
+#include <cutils/compiler.h>
+#include <EGL/egl.h>
+#include <SkBitmap.h>
+#include <utils/Functor.h>
+#include <utils/Vector.h>
+
+#include "../RenderNode.h"
+#include "RenderTask.h"
+#include "RenderThread.h"
+
+#define FUNCTOR_PROCESS_DELAY 4
+
+namespace android {
+namespace uirenderer {
+
+class DeferredLayerUpdater;
+class OpenGLRenderer;
+class Rect;
+class Layer;
+
+namespace renderthread {
+
+class GlobalContext;
+
+// This per-renderer class manages the bridge between the global EGL context
+// and the render surface.
+class CanvasContext : public IFrameCallback {
+public:
+ CanvasContext(bool translucent, RenderNode* rootRenderNode);
+ virtual ~CanvasContext();
+
+ bool initialize(EGLNativeWindowType window);
+ void updateSurface(EGLNativeWindowType window);
+ void pauseSurface(EGLNativeWindowType window);
+ void setup(int width, int height);
+ void setOpaque(bool opaque);
+ void makeCurrent();
+ void prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
+ void draw(Rect* dirty);
+ void destroyCanvasAndSurface();
+
+ // IFrameCallback, Chroreographer-driven frame callback entry point
+ virtual void doFrame();
+
+ bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+
+ void invokeFunctor(Functor* functor);
+
+ void runWithGlContext(RenderTask* task);
+
+ Layer* createRenderLayer(int width, int height);
+ Layer* createTextureLayer();
+
+ ANDROID_API static void setTextureAtlas(const sp<GraphicBuffer>& buffer,
+ int64_t* map, size_t mapSize);
+
+private:
+ void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
+ void prepareTree(TreeInfo& info);
+
+ void setSurface(EGLNativeWindowType window);
+ void swapBuffers();
+ void requireSurface();
+
+ void requireGlContext();
+
+ GlobalContext* mGlobalContext;
+ RenderThread& mRenderThread;
+ EGLSurface mEglSurface;
+ bool mDirtyRegionsEnabled;
+
+ bool mOpaque;
+ OpenGLRenderer* mCanvas;
+ bool mHaveNewSurface;
+
+ const sp<RenderNode> mRootRenderNode;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+#endif /* CANVASCONTEXT_H_ */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
new file mode 100644
index 0000000..3b8786c
--- /dev/null
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include "DrawFrameTask.h"
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "../DisplayList.h"
+#include "../RenderNode.h"
+#include "CanvasContext.h"
+#include "RenderThread.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+DrawFrameTask::DrawFrameTask()
+ : mRenderThread(NULL)
+ , mContext(NULL)
+ , mFrameTimeNanos(0)
+ , mSyncResult(kSync_OK) {
+}
+
+DrawFrameTask::~DrawFrameTask() {
+}
+
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) {
+ mRenderThread = thread;
+ mContext = context;
+}
+
+void DrawFrameTask::addLayer(DeferredLayerUpdater* layer) {
+ LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to addLayer with!");
+
+ mLayers.push(layer);
+}
+
+void DrawFrameTask::removeLayer(DeferredLayerUpdater* layer) {
+ for (size_t i = 0; i < mLayers.size(); i++) {
+ if (mLayers[i] == layer) {
+ mLayers.removeAt(i);
+ break;
+ }
+ }
+}
+
+void DrawFrameTask::setDirty(int left, int top, int right, int bottom) {
+ mDirty.set(left, top, right, bottom);
+}
+
+int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos) {
+ LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
+
+ mSyncResult = kSync_OK;
+ mFrameTimeNanos = frameTimeNanos;
+ postAndWait();
+
+ // Reset the single-frame data
+ mFrameTimeNanos = 0;
+ mDirty.setEmpty();
+
+ return mSyncResult;
+}
+
+void DrawFrameTask::postAndWait() {
+ AutoMutex _lock(mLock);
+ mRenderThread->queue(this);
+ mSignal.wait(mLock);
+}
+
+void DrawFrameTask::run() {
+ ATRACE_NAME("DrawFrame");
+
+ bool canUnblockUiThread = syncFrameState();
+
+ // Grab a copy of everything we need
+ Rect dirty(mDirty);
+ CanvasContext* context = mContext;
+
+ // From this point on anything in "this" is *UNSAFE TO ACCESS*
+ if (canUnblockUiThread) {
+ unblockUiThread();
+ }
+
+ context->draw(&dirty);
+
+ if (!canUnblockUiThread) {
+ unblockUiThread();
+ }
+}
+
+static void initTreeInfo(TreeInfo& info) {
+ info.prepareTextures = true;
+ info.performStagingPush = true;
+ info.evaluateAnimations = true;
+}
+
+bool DrawFrameTask::syncFrameState() {
+ ATRACE_CALL();
+ mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
+ mContext->makeCurrent();
+ Caches::getInstance().textureCache.resetMarkInUse();
+ TreeInfo info;
+ initTreeInfo(info);
+ mContext->prepareDraw(&mLayers, info);
+ if (info.out.hasAnimations) {
+ // TODO: dirty calculations, for now just do a full-screen inval
+ mDirty.setEmpty();
+ if (info.out.requiresUiRedraw) {
+ mSyncResult |= kSync_UIRedrawRequired;
+ }
+ }
+ // If prepareTextures is false, we ran out of texture cache space
+ return !info.out.hasFunctors && info.prepareTextures;
+}
+
+void DrawFrameTask::unblockUiThread() {
+ AutoMutex _lock(mLock);
+ mSignal.signal();
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
new file mode 100644
index 0000000..b9307e1
--- /dev/null
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef DRAWFRAMETASK_H
+#define DRAWFRAMETASK_H
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include "RenderTask.h"
+
+#include "../Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+class DeferredLayerUpdater;
+class DisplayListData;
+class RenderNode;
+
+namespace renderthread {
+
+class CanvasContext;
+class RenderThread;
+
+enum SyncResult {
+ kSync_OK = 0,
+ kSync_UIRedrawRequired = 1 << 1,
+};
+
+/*
+ * This is a special Super Task. It is re-used multiple times by RenderProxy,
+ * and contains state (such as layer updaters & new DisplayListDatas) that is
+ * tracked across many frames not just a single frame.
+ * It is the sync-state task, and will kick off the post-sync draw
+ */
+class DrawFrameTask : public RenderTask {
+public:
+ DrawFrameTask();
+ virtual ~DrawFrameTask();
+
+ void setContext(RenderThread* thread, CanvasContext* context);
+
+ void addLayer(DeferredLayerUpdater* layer);
+ void removeLayer(DeferredLayerUpdater* layer);
+
+ void setDirty(int left, int top, int right, int bottom);
+ int drawFrame(nsecs_t frameTimeNanos);
+
+ virtual void run();
+
+private:
+ void postAndWait();
+ bool syncFrameState();
+ void unblockUiThread();
+
+ Mutex mLock;
+ Condition mSignal;
+
+ RenderThread* mRenderThread;
+ CanvasContext* mContext;
+
+ /*********************************************
+ * Single frame data
+ *********************************************/
+ Rect mDirty;
+ nsecs_t mFrameTimeNanos;
+
+ int mSyncResult;
+
+ /*********************************************
+ * Multi frame data
+ *********************************************/
+ Vector<DeferredLayerUpdater*> mLayers;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* DRAWFRAMETASK_H */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
new file mode 100644
index 0000000..82a2dbc
--- /dev/null
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RenderProxy"
+
+#include "RenderProxy.h"
+
+#include "CanvasContext.h"
+#include "RenderTask.h"
+#include "RenderThread.h"
+
+#include "../DeferredLayerUpdater.h"
+#include "../DisplayList.h"
+#include "../LayerRenderer.h"
+#include "../Rect.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+#define ARGS(method) method ## Args
+
+#define CREATE_BRIDGE0(name) CREATE_BRIDGE(name,,,,,,,,)
+#define CREATE_BRIDGE1(name, a1) CREATE_BRIDGE(name, a1,,,,,,,)
+#define CREATE_BRIDGE2(name, a1, a2) CREATE_BRIDGE(name, a1,a2,,,,,,)
+#define CREATE_BRIDGE3(name, a1, a2, a3) CREATE_BRIDGE(name, a1,a2,a3,,,,,)
+#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,)
+#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) \
+ typedef struct { \
+ a1; a2; a3; a4; a5; a6; a7; a8; \
+ } ARGS(name); \
+ static void* Bridge_ ## name(ARGS(name)* args)
+
+#define SETUP_TASK(method) \
+ LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \
+ "METHOD_INVOKE_PAYLOAD_SIZE %d is smaller than sizeof(" #method "Args) %d", \
+ METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \
+ MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
+ ARGS(method) *args = (ARGS(method) *) task->payload()
+
+CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) {
+ return new CanvasContext(args->translucent, args->rootRenderNode);
+}
+
+RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode)
+ : mRenderThread(RenderThread::getInstance())
+ , mContext(0) {
+ SETUP_TASK(createContext);
+ args->translucent = translucent;
+ args->rootRenderNode = rootRenderNode;
+ mContext = (CanvasContext*) postAndWait(task);
+ mDrawFrameTask.setContext(&mRenderThread, mContext);
+}
+
+RenderProxy::~RenderProxy() {
+ destroyContext();
+}
+
+CREATE_BRIDGE1(destroyContext, CanvasContext* context) {
+ delete args->context;
+ return NULL;
+}
+
+void RenderProxy::destroyContext() {
+ if (mContext) {
+ SETUP_TASK(destroyContext);
+ args->context = mContext;
+ mContext = 0;
+ mDrawFrameTask.setContext(NULL, NULL);
+ // This is also a fence as we need to be certain that there are no
+ // outstanding mDrawFrame tasks posted before it is destroyed
+ postAndWait(task);
+ }
+}
+
+CREATE_BRIDGE2(setFrameInterval, RenderThread* thread, nsecs_t frameIntervalNanos) {
+ args->thread->timeLord().setFrameInterval(args->frameIntervalNanos);
+ return NULL;
+}
+
+void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) {
+ SETUP_TASK(setFrameInterval);
+ args->thread = &mRenderThread;
+ args->frameIntervalNanos = frameIntervalNanos;
+ post(task);
+}
+
+CREATE_BRIDGE0(loadSystemProperties) {
+ bool needsRedraw = false;
+ if (Caches::hasInstance()) {
+ needsRedraw = Caches::getInstance().initProperties();
+ }
+ return (void*) needsRedraw;
+}
+
+bool RenderProxy::loadSystemProperties() {
+ SETUP_TASK(loadSystemProperties);
+ return (bool) postAndWait(task);
+}
+
+CREATE_BRIDGE2(initialize, CanvasContext* context, EGLNativeWindowType window) {
+ return (void*) args->context->initialize(args->window);
+}
+
+bool RenderProxy::initialize(const sp<ANativeWindow>& window) {
+ SETUP_TASK(initialize);
+ args->context = mContext;
+ args->window = window.get();
+ return (bool) postAndWait(task);
+}
+
+CREATE_BRIDGE2(updateSurface, CanvasContext* context, EGLNativeWindowType window) {
+ args->context->updateSurface(args->window);
+ return NULL;
+}
+
+void RenderProxy::updateSurface(const sp<ANativeWindow>& window) {
+ SETUP_TASK(updateSurface);
+ args->context = mContext;
+ args->window = window.get();
+ postAndWait(task);
+}
+
+CREATE_BRIDGE2(pauseSurface, CanvasContext* context, EGLNativeWindowType window) {
+ args->context->pauseSurface(args->window);
+ return NULL;
+}
+
+void RenderProxy::pauseSurface(const sp<ANativeWindow>& window) {
+ SETUP_TASK(pauseSurface);
+ args->context = mContext;
+ args->window = window.get();
+ postAndWait(task);
+}
+
+CREATE_BRIDGE3(setup, CanvasContext* context, int width, int height) {
+ args->context->setup(args->width, args->height);
+ return NULL;
+}
+
+void RenderProxy::setup(int width, int height) {
+ SETUP_TASK(setup);
+ args->context = mContext;
+ args->width = width;
+ args->height = height;
+ post(task);
+}
+
+CREATE_BRIDGE2(setOpaque, CanvasContext* context, bool opaque) {
+ args->context->setOpaque(args->opaque);
+ return NULL;
+}
+
+void RenderProxy::setOpaque(bool opaque) {
+ SETUP_TASK(setOpaque);
+ args->context = mContext;
+ args->opaque = opaque;
+ post(task);
+}
+
+int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos,
+ int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
+ mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+ return mDrawFrameTask.drawFrame(frameTimeNanos);
+}
+
+CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) {
+ args->context->destroyCanvasAndSurface();
+ return NULL;
+}
+
+void RenderProxy::destroyCanvasAndSurface() {
+ SETUP_TASK(destroyCanvasAndSurface);
+ args->context = mContext;
+ // destroyCanvasAndSurface() needs a fence as when it returns the
+ // underlying BufferQueue is going to be released from under
+ // the render thread.
+ postAndWait(task);
+}
+
+CREATE_BRIDGE2(invokeFunctor, CanvasContext* context, Functor* functor) {
+ args->context->invokeFunctor(args->functor);
+ return NULL;
+}
+
+void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
+ ATRACE_CALL();
+ SETUP_TASK(invokeFunctor);
+ args->context = mContext;
+ args->functor = functor;
+ if (waitForCompletion) {
+ postAndWait(task);
+ } else {
+ post(task);
+ }
+}
+
+CREATE_BRIDGE2(runWithGlContext, CanvasContext* context, RenderTask* task) {
+ args->context->runWithGlContext(args->task);
+ return NULL;
+}
+
+void RenderProxy::runWithGlContext(RenderTask* gltask) {
+ SETUP_TASK(runWithGlContext);
+ args->context = mContext;
+ args->task = gltask;
+ postAndWait(task);
+}
+
+CREATE_BRIDGE3(createDisplayListLayer, CanvasContext* context, int width, int height) {
+ Layer* layer = args->context->createRenderLayer(args->width, args->height);
+ if (!layer) return 0;
+ return new DeferredLayerUpdater(layer);
+}
+
+DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) {
+ SETUP_TASK(createDisplayListLayer);
+ args->width = width;
+ args->height = height;
+ args->context = mContext;
+ void* retval = postAndWait(task);
+ DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval);
+ mDrawFrameTask.addLayer(layer);
+ return layer;
+}
+
+CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) {
+ Layer* layer = args->context->createTextureLayer();
+ if (!layer) return 0;
+ return new DeferredLayerUpdater(layer);
+}
+
+DeferredLayerUpdater* RenderProxy::createTextureLayer() {
+ SETUP_TASK(createTextureLayer);
+ args->context = mContext;
+ void* retval = postAndWait(task);
+ DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval);
+ mDrawFrameTask.addLayer(layer);
+ return layer;
+}
+
+CREATE_BRIDGE1(destroyLayer, Layer* layer) {
+ LayerRenderer::destroyLayer(args->layer);
+ return NULL;
+}
+
+CREATE_BRIDGE3(copyLayerInto, CanvasContext* context, DeferredLayerUpdater* layer,
+ SkBitmap* bitmap) {
+ bool success = args->context->copyLayerInto(args->layer, args->bitmap);
+ return (void*) success;
+}
+
+bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+ SETUP_TASK(copyLayerInto);
+ args->context = mContext;
+ args->layer = layer;
+ args->bitmap = bitmap;
+ return (bool) postAndWait(task);
+}
+
+void RenderProxy::destroyLayer(DeferredLayerUpdater* layer) {
+ mDrawFrameTask.removeLayer(layer);
+ SETUP_TASK(destroyLayer);
+ args->layer = layer->detachBackingLayer();
+ post(task);
+}
+
+CREATE_BRIDGE0(fence) {
+ // Intentionally empty
+ return NULL;
+}
+
+void RenderProxy::fence() {
+ SETUP_TASK(fence);
+ postAndWait(task);
+}
+
+void RenderProxy::post(RenderTask* task) {
+ mRenderThread.queue(task);
+}
+
+void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) {
+ void* retval;
+ task->setReturnPtr(&retval);
+ SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition);
+ AutoMutex _lock(mSyncMutex);
+ mRenderThread.queue(&syncTask);
+ mSyncCondition.wait(mSyncMutex);
+ return retval;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
new file mode 100644
index 0000000..4a7e70a
--- /dev/null
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RENDERPROXY_H_
+#define RENDERPROXY_H_
+
+#include "RenderTask.h"
+
+#include <cutils/compiler.h>
+#include <EGL/egl.h>
+#include <SkBitmap.h>
+#include <utils/Condition.h>
+#include <utils/Functor.h>
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include "DrawFrameTask.h"
+
+namespace android {
+namespace uirenderer {
+
+class DeferredLayerUpdater;
+class RenderNode;
+class DisplayListData;
+class Layer;
+class Rect;
+
+namespace renderthread {
+
+class CanvasContext;
+class ErrorChannel;
+class RenderThread;
+class RenderProxyBridge;
+
+/*
+ * RenderProxy is strictly single threaded. All methods must be invoked on the owning
+ * thread. It is important to note that RenderProxy may be deleted while it has
+ * tasks post()'d as a result. Therefore any RenderTask that is post()'d must not
+ * reference RenderProxy or any of its fields. The exception here is that postAndWait()
+ * references RenderProxy fields. This is safe as RenderProxy cannot
+ * be deleted if it is blocked inside a call.
+ */
+class ANDROID_API RenderProxy {
+public:
+ ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode);
+ ANDROID_API virtual ~RenderProxy();
+
+ ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos);
+ ANDROID_API bool loadSystemProperties();
+
+ ANDROID_API bool initialize(const sp<ANativeWindow>& window);
+ ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
+ ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
+ ANDROID_API void setup(int width, int height);
+ ANDROID_API void setOpaque(bool opaque);
+ ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos,
+ int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
+ ANDROID_API void destroyCanvasAndSurface();
+
+ ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion);
+
+ ANDROID_API void runWithGlContext(RenderTask* task);
+
+ ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height);
+ ANDROID_API DeferredLayerUpdater* createTextureLayer();
+ ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+ ANDROID_API void destroyLayer(DeferredLayerUpdater* layer);
+
+ ANDROID_API void fence();
+
+private:
+ RenderThread& mRenderThread;
+ CanvasContext* mContext;
+
+ DrawFrameTask mDrawFrameTask;
+
+ Mutex mSyncMutex;
+ Condition mSyncCondition;
+
+ void destroyContext();
+
+ void post(RenderTask* task);
+ void* postAndWait(MethodInvokeRenderTask* task);
+
+ // Friend class to help with bridging
+ friend class RenderProxyBridge;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+#endif /* RENDERPROXY_H_ */
diff --git a/libs/hwui/renderthread/RenderTask.cpp b/libs/hwui/renderthread/RenderTask.cpp
new file mode 100644
index 0000000..7ca61e4
--- /dev/null
+++ b/libs/hwui/renderthread/RenderTask.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RenderTask"
+
+#include "RenderTask.h"
+
+#include <utils/Log.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+void SignalingRenderTask::run() {
+ mTask->run();
+ mLock->lock();
+ mSignal->signal();
+ mLock->unlock();
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h
new file mode 100644
index 0000000..1554a16
--- /dev/null
+++ b/libs/hwui/renderthread/RenderTask.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RENDERTASK_H_
+#define RENDERTASK_H_
+
+#include <cutils/compiler.h>
+#include <utils/Timers.h>
+
+namespace android {
+class Mutex;
+class Condition;
+namespace uirenderer {
+namespace renderthread {
+
+#define METHOD_INVOKE_PAYLOAD_SIZE (8 * sizeof(void*))
+
+/*
+ * Notes about memory management
+ *
+ * RenderThread will only invoke RenderTask::run(). It is the responsibility
+ * of the RenderTask to know if it needs to suicide at the end of run() or
+ * if some other lifecycle is being used. As such, it is not valid to reference
+ * anything on RenderTask after the first call to run().
+ *
+ * For example SignalingRenderTask
+ * is expected to be stack allocated by the calling thread, so it does not
+ * suicide in run() but instead relies on the caller to destroy it.
+ *
+ * MethodInvokeRenderTask however is currently allocated with new, so it will
+ * suicide at the end of run(). TODO: Replace this with a small pool to avoid
+ * malloc/free churn of small objects?
+ */
+
+class ANDROID_API RenderTask {
+public:
+ ANDROID_API RenderTask() : mNext(0), mRunAt(0) {}
+ ANDROID_API virtual ~RenderTask() {}
+
+ ANDROID_API virtual void run() = 0;
+
+ RenderTask* mNext;
+ nsecs_t mRunAt; // nano-seconds on the SYSTEM_TIME_MONOTONIC clock
+};
+
+class SignalingRenderTask : public RenderTask {
+public:
+ // Takes ownership of task, caller owns lock and signal
+ SignalingRenderTask(RenderTask* task, Mutex* lock, Condition* signal)
+ : mTask(task), mLock(lock), mSignal(signal) {}
+ virtual void run();
+
+private:
+ RenderTask* mTask;
+ Mutex* mLock;
+ Condition* mSignal;
+};
+
+typedef void* (*RunnableMethod)(void* data);
+
+class MethodInvokeRenderTask : public RenderTask {
+public:
+ MethodInvokeRenderTask(RunnableMethod method)
+ : mMethod(method), mReturnPtr(0) {}
+
+ void* payload() { return mData; }
+ void setReturnPtr(void** retptr) { mReturnPtr = retptr; }
+
+ virtual void run() {
+ void* retval = mMethod(mData);
+ if (mReturnPtr) {
+ *mReturnPtr = retval;
+ }
+ // Commit suicide
+ delete this;
+ }
+private:
+ RunnableMethod mMethod;
+ char mData[METHOD_INVOKE_PAYLOAD_SIZE];
+ void** mReturnPtr;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+#endif /* RENDERTASK_H_ */
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
new file mode 100644
index 0000000..35a3eab
--- /dev/null
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RenderThread"
+
+#include "RenderThread.h"
+
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+
+#include "CanvasContext.h"
+#include "RenderProxy.h"
+
+namespace android {
+using namespace uirenderer::renderthread;
+ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread);
+
+namespace uirenderer {
+namespace renderthread {
+
+// Number of events to read at a time from the DisplayEventReceiver pipe.
+// The value should be large enough that we can quickly drain the pipe
+// using just a few large reads.
+static const size_t EVENT_BUFFER_SIZE = 100;
+
+// Slight delay to give the UI time to push us a new frame before we replay
+static const int DISPATCH_FRAME_CALLBACKS_DELAY = 0;
+
+TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
+
+RenderTask* TaskQueue::next() {
+ RenderTask* ret = mHead;
+ if (ret) {
+ mHead = ret->mNext;
+ if (!mHead) {
+ mTail = 0;
+ }
+ ret->mNext = 0;
+ }
+ return ret;
+}
+
+RenderTask* TaskQueue::peek() {
+ return mHead;
+}
+
+void TaskQueue::queue(RenderTask* task) {
+ // Since the RenderTask itself forms the linked list it is not allowed
+ // to have the same task queued twice
+ LOG_ALWAYS_FATAL_IF(task->mNext || mTail == task, "Task is already in the queue!");
+ if (mTail) {
+ // Fast path if we can just append
+ if (mTail->mRunAt <= task->mRunAt) {
+ mTail->mNext = task;
+ mTail = task;
+ } else {
+ // Need to find the proper insertion point
+ RenderTask* previous = 0;
+ RenderTask* next = mHead;
+ while (next && next->mRunAt <= task->mRunAt) {
+ previous = next;
+ next = next->mNext;
+ }
+ if (!previous) {
+ task->mNext = mHead;
+ mHead = task;
+ } else {
+ previous->mNext = task;
+ if (next) {
+ task->mNext = next;
+ } else {
+ mTail = task;
+ }
+ }
+ }
+ } else {
+ mTail = mHead = task;
+ }
+}
+
+void TaskQueue::remove(RenderTask* task) {
+ // TaskQueue is strict here to enforce that users are keeping track of
+ // their RenderTasks due to how their memory is managed
+ LOG_ALWAYS_FATAL_IF(!task->mNext && mTail != task,
+ "Cannot remove a task that isn't in the queue!");
+
+ // If task is the head we can just call next() to pop it off
+ // Otherwise we need to scan through to find the task before it
+ if (peek() == task) {
+ next();
+ } else {
+ RenderTask* previous = mHead;
+ while (previous->mNext != task) {
+ previous = previous->mNext;
+ }
+ previous->mNext = task->mNext;
+ if (mTail == task) {
+ mTail = previous;
+ }
+ }
+}
+
+class DispatchFrameCallbacks : public RenderTask {
+private:
+ RenderThread* mRenderThread;
+public:
+ DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {}
+
+ virtual void run() {
+ mRenderThread->dispatchFrameCallbacks();
+ }
+};
+
+RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
+ , mNextWakeup(LLONG_MAX)
+ , mDisplayEventReceiver(0)
+ , mVsyncRequested(false)
+ , mFrameCallbackTaskPending(false)
+ , mFrameCallbackTask(0) {
+ mFrameCallbackTask = new DispatchFrameCallbacks(this);
+ mLooper = new Looper(false);
+ run("RenderThread");
+}
+
+RenderThread::~RenderThread() {
+}
+
+void RenderThread::initializeDisplayEventReceiver() {
+ LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?");
+ mDisplayEventReceiver = new DisplayEventReceiver();
+ status_t status = mDisplayEventReceiver->initCheck();
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver "
+ "failed with status: %d", status);
+
+ // Register the FD
+ mLooper->addFd(mDisplayEventReceiver->getFd(), 0,
+ Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);
+}
+
+int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
+ if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+ ALOGE("Display event receiver pipe was closed or an error occurred. "
+ "events=0x%x", events);
+ return 0; // remove the callback
+ }
+
+ if (!(events & Looper::EVENT_INPUT)) {
+ ALOGW("Received spurious callback for unhandled poll event. "
+ "events=0x%x", events);
+ return 1; // keep the callback
+ }
+
+ reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue();
+
+ return 1; // keep the callback
+}
+
+static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) {
+ DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+ nsecs_t latest = 0;
+ ssize_t n;
+ while ((n = receiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ for (ssize_t i = 0; i < n; i++) {
+ const DisplayEventReceiver::Event& ev = buf[i];
+ switch (ev.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ latest = ev.header.timestamp;
+ break;
+ }
+ }
+ }
+ if (n < 0) {
+ ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
+ }
+ return latest;
+}
+
+void RenderThread::drainDisplayEventQueue() {
+ nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
+ if (vsyncEvent > 0) {
+ mVsyncRequested = false;
+ mTimeLord.vsyncReceived(vsyncEvent);
+ if (!mFrameCallbackTaskPending) {
+ mFrameCallbackTaskPending = true;
+ //queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
+ queue(mFrameCallbackTask);
+ }
+ }
+}
+
+void RenderThread::dispatchFrameCallbacks() {
+ mFrameCallbackTaskPending = false;
+
+ std::set<IFrameCallback*> callbacks;
+ mFrameCallbacks.swap(callbacks);
+
+ for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) {
+ (*it)->doFrame();
+ }
+}
+
+bool RenderThread::threadLoop() {
+ initializeDisplayEventReceiver();
+
+ int timeoutMillis = -1;
+ for (;;) {
+ int result = mLooper->pollOnce(timeoutMillis);
+ LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,
+ "RenderThread Looper POLL_ERROR!");
+
+ nsecs_t nextWakeup;
+ // Process our queue, if we have anything
+ while (RenderTask* task = nextTask(&nextWakeup)) {
+ task->run();
+ // task may have deleted itself, do not reference it again
+ }
+ if (nextWakeup == LLONG_MAX) {
+ timeoutMillis = -1;
+ } else {
+ nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC);
+ timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos);
+ if (timeoutMillis < 0) {
+ timeoutMillis = 0;
+ }
+ }
+ }
+
+ return false;
+}
+
+void RenderThread::queue(RenderTask* task) {
+ AutoMutex _lock(mLock);
+ mQueue.queue(task);
+ if (mNextWakeup && task->mRunAt < mNextWakeup) {
+ mNextWakeup = 0;
+ mLooper->wake();
+ }
+}
+
+void RenderThread::queueDelayed(RenderTask* task, int delayMs) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ task->mRunAt = now + milliseconds_to_nanoseconds(delayMs);
+ queue(task);
+}
+
+void RenderThread::remove(RenderTask* task) {
+ AutoMutex _lock(mLock);
+ mQueue.remove(task);
+}
+
+void RenderThread::postFrameCallback(IFrameCallback* callback) {
+ mFrameCallbacks.insert(callback);
+ if (!mVsyncRequested) {
+ mVsyncRequested = true;
+ status_t status = mDisplayEventReceiver->requestNextVsync();
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "requestNextVsync failed with status: %d", status);
+ }
+}
+
+void RenderThread::removeFrameCallback(IFrameCallback* callback) {
+ mFrameCallbacks.erase(callback);
+}
+
+RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
+ AutoMutex _lock(mLock);
+ RenderTask* next = mQueue.peek();
+ if (!next) {
+ mNextWakeup = LLONG_MAX;
+ } else {
+ // Most tasks won't be delayed, so avoid unnecessary systemTime() calls
+ if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) {
+ next = mQueue.next();
+ }
+ mNextWakeup = next->mRunAt;
+ }
+ if (nextWakeup) {
+ *nextWakeup = mNextWakeup;
+ }
+ return next;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
new file mode 100644
index 0000000..215d294
--- /dev/null
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RENDERTHREAD_H_
+#define RENDERTHREAD_H_
+
+#include "RenderTask.h"
+
+#include <memory>
+#include <set>
+
+#include <cutils/compiler.h>
+#include <utils/Looper.h>
+#include <utils/Mutex.h>
+#include <utils/Singleton.h>
+#include <utils/Thread.h>
+
+#include "TimeLord.h"
+
+namespace android {
+class DisplayEventReceiver;
+
+namespace uirenderer {
+namespace renderthread {
+
+class DispatchFrameCallbacks;
+
+class TaskQueue {
+public:
+ TaskQueue();
+
+ RenderTask* next();
+ void queue(RenderTask* task);
+ RenderTask* peek();
+ void remove(RenderTask* task);
+
+private:
+ RenderTask* mHead;
+ RenderTask* mTail;
+};
+
+// Mimics android.view.Choreographer.FrameCallback
+class IFrameCallback {
+public:
+ virtual void doFrame() = 0;
+
+protected:
+ ~IFrameCallback() {}
+};
+
+class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> {
+public:
+ // RenderThread takes complete ownership of tasks that are queued
+ // and will delete them after they are run
+ ANDROID_API void queue(RenderTask* task);
+ void queueDelayed(RenderTask* task, int delayMs);
+ void remove(RenderTask* task);
+
+ // Mimics android.view.Choreographer
+ void postFrameCallback(IFrameCallback* callback);
+ void removeFrameCallback(IFrameCallback* callback);
+
+ TimeLord& timeLord() { return mTimeLord; }
+
+protected:
+ virtual bool threadLoop();
+
+private:
+ friend class Singleton<RenderThread>;
+ friend class DispatchFrameCallbacks;
+
+ RenderThread();
+ virtual ~RenderThread();
+
+ void initializeDisplayEventReceiver();
+ static int displayEventReceiverCallback(int fd, int events, void* data);
+ void drainDisplayEventQueue();
+ void dispatchFrameCallbacks();
+
+ // Returns the next task to be run. If this returns NULL nextWakeup is set
+ // to the time to requery for the nextTask to run. mNextWakeup is also
+ // set to this time
+ RenderTask* nextTask(nsecs_t* nextWakeup);
+
+ sp<Looper> mLooper;
+ Mutex mLock;
+
+ nsecs_t mNextWakeup;
+ TaskQueue mQueue;
+
+ DisplayEventReceiver* mDisplayEventReceiver;
+ bool mVsyncRequested;
+ std::set<IFrameCallback*> mFrameCallbacks;
+ bool mFrameCallbackTaskPending;
+ DispatchFrameCallbacks* mFrameCallbackTask;
+
+ TimeLord mTimeLord;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+#endif /* RENDERTHREAD_H_ */
diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp
new file mode 100644
index 0000000..758d96e
--- /dev/null
+++ b/libs/hwui/renderthread/TimeLord.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "TimeLord.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+TimeLord::TimeLord()
+ : mFrameIntervalNanos(0)
+ , mFrameTimeNanos(0) {
+}
+
+void TimeLord::vsyncReceived(nsecs_t vsync) {
+ if (vsync > mFrameTimeNanos) {
+ mFrameTimeNanos = vsync;
+ }
+}
+
+nsecs_t TimeLord::frameTimeMs() {
+ // Logic copied from Choreographer.java
+ nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ nsecs_t jitterNanos = now - mFrameTimeNanos;
+ if (jitterNanos >= mFrameIntervalNanos) {
+ nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos;
+ mFrameTimeNanos = now - lastFrameOffset;
+ }
+ return nanoseconds_to_milliseconds(mFrameTimeNanos);
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h
new file mode 100644
index 0000000..52c6d9e
--- /dev/null
+++ b/libs/hwui/renderthread/TimeLord.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef TIMELORD_H
+#define TIMELORD_H
+
+#include <utils/Timers.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class RenderThread;
+
+// This class serves as a helper to filter & manage frame times from multiple sources
+// ensuring that time flows linearly and smoothly
+class TimeLord {
+public:
+ void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; }
+ void vsyncReceived(nsecs_t vsync);
+ nsecs_t frameTimeMs();
+
+private:
+ friend class RenderThread;
+
+ TimeLord();
+ ~TimeLord() {}
+
+ nsecs_t mFrameIntervalNanos;
+ nsecs_t mFrameTimeNanos;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TIMELORD_H */