diff options
author | John Reck <jreck@google.com> | 2014-06-23 13:13:08 -0700 |
---|---|---|
committer | John Reck <jreck@google.com> | 2014-06-23 15:26:49 -0700 |
commit | 3b20251a355c88193c439f928a84ae69483fb488 (patch) | |
tree | 33c878ebacf17cf03d089404474fa66ca041ffb9 /libs/hwui/renderthread | |
parent | 97a6c20a6a52c9429ed2c8837086f3003e5da274 (diff) | |
download | frameworks_base-3b20251a355c88193c439f928a84ae69483fb488.zip frameworks_base-3b20251a355c88193c439f928a84ae69483fb488.tar.gz frameworks_base-3b20251a355c88193c439f928a84ae69483fb488.tar.bz2 |
No-fail invokeFunctor
Bug: 15513308
Bug: 15449247
Change-Id: I13a29f9c8d4975cdda6dcb33b6332c2555ff0f7c
Diffstat (limited to 'libs/hwui/renderthread')
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 364 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 12 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/renderthread/EglManager.cpp | 288 | ||||
-rw-r--r-- | libs/hwui/renderthread/EglManager.h | 88 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 46 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 5 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderThread.cpp | 15 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderThread.h | 14 |
9 files changed, 476 insertions, 358 deletions
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index eab9810..fe4edf8 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -18,332 +18,31 @@ #include "CanvasContext.h" -#include <cutils/properties.h> #include <private/hwui/DrawGlInfo.h> #include <strings.h> +#include "EglManager.h" #include "RenderThread.h" #include "../Caches.h" #include "../DeferredLayerUpdater.h" +#include "../RenderState.h" #include "../LayerRenderer.h" #include "../OpenGLRenderer.h" #include "../Stencil.h" -#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" -#define GLES_VERSION 2 - -// 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 (mAtlasBuffer.get()) { - 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()) +CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode) + : mRenderThread(thread) + , mEglManager(thread.eglManager()) , mEglSurface(EGL_NO_SURFACE) , mDirtyRegionsEnabled(false) , mOpaque(!translucent) - , mCanvas(0) + , mCanvas(NULL) , mHaveNewSurface(false) , mRootRenderNode(rootRenderNode) { - mGlobalContext = GlobalContext::get(); } CanvasContext::~CanvasContext() { @@ -363,19 +62,16 @@ void CanvasContext::setSurface(ANativeWindow* window) { mNativeWindow = window; if (mEglSurface != EGL_NO_SURFACE) { - mGlobalContext->destroySurface(mEglSurface); + mEglManager.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()); + mEglSurface = mEglManager.createSurface(window); } if (mEglSurface != EGL_NO_SURFACE) { - mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); + mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface); mHaveNewSurface = true; makeCurrent(); } else { @@ -384,7 +80,7 @@ void CanvasContext::setSurface(ANativeWindow* window) { } void CanvasContext::swapBuffers() { - mGlobalContext->swapBuffers(mEglSurface); + mEglManager.swapBuffers(mEglSurface); mHaveNewSurface = false; } @@ -397,7 +93,7 @@ void CanvasContext::requireSurface() { bool CanvasContext::initialize(ANativeWindow* window) { if (mCanvas) return false; setSurface(window); - mCanvas = new OpenGLRenderer(); + mCanvas = new OpenGLRenderer(mRenderThread.renderState()); mCanvas->initProperties(); return true; } @@ -424,7 +120,7 @@ void CanvasContext::setOpaque(bool 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); + mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface); } void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info) { @@ -477,7 +173,7 @@ void CanvasContext::draw() { mDamageAccumulator.finish(&dirty); EGLint width, height; - mGlobalContext->beginFrame(mEglSurface, &width, &height); + mEglManager.beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); dirty.setEmpty(); @@ -521,7 +217,7 @@ void CanvasContext::doFrame() { profiler().startFrame(); - TreeInfo info(TreeInfo::MODE_RT_ONLY); + TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); info.prepareTextures = false; prepareTree(info); @@ -530,32 +226,26 @@ void CanvasContext::doFrame() { } } -void CanvasContext::invokeFunctor(Functor* functor) { +void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { ATRACE_CALL(); DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; - if (mGlobalContext->hasContext()) { - requireGlContext(); + if (thread.eglManager().hasEglContext()) { + thread.eglManager().requireGlContext(); mode = DrawGlInfo::kModeProcess; } - if (mCanvas) { - mCanvas->interrupt(); - } - (*functor)(mode, NULL); - if (mCanvas) { - mCanvas->resume(); - } + thread.renderState().invokeFunctor(functor, mode, NULL); } bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { requireGlContext(); - TreeInfo info(TreeInfo::MODE_FULL); + TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState()); layer->apply(info); - return LayerRenderer::copyLayer(layer->backingLayer(), bitmap); + return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap); } void CanvasContext::flushCaches(Caches::FlushMode flushMode) { - if (mGlobalContext->hasContext()) { + if (mEglManager.hasEglContext()) { requireGlContext(); Caches::getInstance().flush(flushMode); } @@ -568,25 +258,25 @@ void CanvasContext::runWithGlContext(RenderTask* task) { Layer* CanvasContext::createRenderLayer(int width, int height) { requireSurface(); - return LayerRenderer::createRenderLayer(width, height); + return LayerRenderer::createRenderLayer(mRenderThread.renderState(), width, height); } Layer* CanvasContext::createTextureLayer() { requireSurface(); - return LayerRenderer::createTextureLayer(); + return LayerRenderer::createTextureLayer(mRenderThread.renderState()); } void CanvasContext::requireGlContext() { if (mEglSurface != EGL_NO_SURFACE) { makeCurrent(); } else { - mGlobalContext->usePBufferSurface(); + mEglManager.usePBufferSurface(); } } -void CanvasContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, - int64_t* map, size_t mapSize) { - GlobalContext::get()->setTextureAtlas(buffer, map, mapSize); +void CanvasContext::setTextureAtlas(RenderThread& thread, + const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { + thread.eglManager().setTextureAtlas(buffer, map, mapSize); } } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index d926b38..d2ce1a6 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -41,13 +41,13 @@ class Layer; namespace renderthread { -class GlobalContext; +class EglManager; // 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); + CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode); virtual ~CanvasContext(); bool initialize(ANativeWindow* window); @@ -68,15 +68,15 @@ public: void flushCaches(Caches::FlushMode flushMode); - void invokeFunctor(Functor* functor); + static void invokeFunctor(RenderThread& thread, 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); + ANDROID_API static void setTextureAtlas(RenderThread& thread, + const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); void notifyFramePending(); @@ -91,8 +91,8 @@ private: void requireGlContext(); - GlobalContext* mGlobalContext; RenderThread& mRenderThread; + EglManager& mEglManager; sp<ANativeWindow> mNativeWindow; EGLSurface mEglSurface; bool mDirtyRegionsEnabled; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 797566f..7d75e0f 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -98,7 +98,7 @@ void DrawFrameTask::run() { bool canUnblockUiThread; bool canDrawThisFrame; { - TreeInfo info(TreeInfo::MODE_FULL); + TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState()); canUnblockUiThread = syncFrameState(info); canDrawThisFrame = info.out.canDrawThisFrame; } diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp new file mode 100644 index 0000000..05ca34d --- /dev/null +++ b/libs/hwui/renderthread/EglManager.cpp @@ -0,0 +1,288 @@ +/* + * 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 "EglContext" + +#include "EglManager.h" + +#include <cutils/log.h> +#include <cutils/properties.h> + +#include "../RenderState.h" +#include "RenderThread.h" + +#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" +#define GLES_VERSION 2 + +// 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); +} + +EglManager::EglManager(RenderThread& thread) + : mRenderThread(thread) + , 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 EglManager::initialize() { + if (hasEglContext()) 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(); + mRenderThread.renderState().onGLContextCreated(); + initAtlas(); +} + +bool EglManager::hasEglContext() { + return mEglDisplay != EGL_NO_DISPLAY; +} + +void EglManager::requireGlContext() { + LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "No EGL context"); + + // We don't care *WHAT* surface is active, just that one is active to give + // us access to the GL context + if (mCurrentSurface == EGL_NO_SURFACE) { + usePBufferSurface(); + } +} + +void EglManager::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 EglManager::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 EglManager::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 (hasEglContext()) { + usePBufferSurface(); + initAtlas(); + } +} + +void EglManager::initAtlas() { + if (mAtlasBuffer.get()) { + Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); + } +} + +void EglManager::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 EglManager::createSurface(EGLNativeWindowType window) { + initialize(); + EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); + LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, + "Failed to create EGLSurface for window %p, eglErr = %s", + (void*) window, egl_error_str()); + return surface; +} + +void EglManager::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 EglManager::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 EglManager::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 EglManager::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 EglManager::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 EglManager::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; +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h new file mode 100644 index 0000000..a844cfc --- /dev/null +++ b/libs/hwui/renderthread/EglManager.h @@ -0,0 +1,88 @@ +/* + * 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 EGLMANAGER_H +#define EGLMANAGER_H + +#include <cutils/compiler.h> +#include <EGL/egl.h> +#include <ui/GraphicBuffer.h> +#include <utils/StrongPointer.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +class RenderThread; + +// This class contains the shared global EGL objects, such as EGLDisplay +// and EGLConfig, which are re-used by CanvasContext +class EglManager { +public: + // Returns true on success, false on failure + void initialize(); + + bool hasEglContext(); + void requireGlContext(); + + 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: + friend class RenderThread; + + EglManager(RenderThread& thread); + // EglContext is never destroyed, method is purposely not implemented + ~EglManager(); + + void loadConfig(); + void createContext(); + void initAtlas(); + + RenderThread& mRenderThread; + + EGLDisplay mEglDisplay; + EGLConfig mEglConfig; + EGLContext mEglContext; + EGLSurface mPBufferSurface; + + const bool mRequestDirtyRegions; + bool mCanSetDirtyRegions; + + EGLSurface mCurrentSurface; + + sp<GraphicBuffer> mAtlasBuffer; + int64_t* mAtlasMap; + size_t mAtlasMapSize; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* EGLMANAGER_H */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 4988f19..f90a26a 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -52,8 +52,8 @@ namespace renderthread { 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); +CREATE_BRIDGE3(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode) { + return new CanvasContext(*args->thread, args->translucent, args->rootRenderNode); } RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode) @@ -62,6 +62,7 @@ RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode) SETUP_TASK(createContext); args->translucent = translucent; args->rootRenderNode = rootRenderNode; + args->thread = &mRenderThread; mContext = (CanvasContext*) postAndWait(task); mDrawFrameTask.setContext(&mRenderThread, mContext); } @@ -199,20 +200,29 @@ void RenderProxy::destroyCanvasAndSurface() { postAndWait(task); } -CREATE_BRIDGE2(invokeFunctor, CanvasContext* context, Functor* functor) { - args->context->invokeFunctor(args->functor); +CREATE_BRIDGE2(invokeFunctor, RenderThread* thread, Functor* functor) { + CanvasContext::invokeFunctor(*args->thread, args->functor); return NULL; } void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { ATRACE_CALL(); + RenderThread& thread = RenderThread::getInstance(); SETUP_TASK(invokeFunctor); - args->context = mContext; + args->thread = &thread; args->functor = functor; if (waitForCompletion) { - postAndWait(task); + // waitForCompletion = true is expected to be fairly rare and only + // happen in destruction. Thus it should be fine to temporarily + // create a Mutex + Mutex mutex; + Condition condition; + SignalingRenderTask syncTask(task, &mutex, &condition); + AutoMutex _lock(mutex); + thread.queue(&syncTask); + condition.wait(mutex); } else { - post(task); + thread.queue(task); } } @@ -233,7 +243,7 @@ CREATE_BRIDGE1(destroyLayer, Layer* layer) { return NULL; } -static void enqueueDestroyLayer(Layer* layer) { +void RenderProxy::enqueueDestroyLayer(Layer* layer) { SETUP_TASK(destroyLayer); args->layer = layer; RenderThread::getInstance().queue(task); @@ -242,7 +252,7 @@ static void enqueueDestroyLayer(Layer* layer) { 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, enqueueDestroyLayer); + return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer); } DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) { @@ -258,7 +268,7 @@ DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) { Layer* layer = args->context->createTextureLayer(); if (!layer) return 0; - return new DeferredLayerUpdater(layer, enqueueDestroyLayer); + return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer); } DeferredLayerUpdater* RenderProxy::createTextureLayer() { @@ -336,6 +346,22 @@ void RenderProxy::dumpProfileInfo(int fd) { postAndWait(task); } +CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) { + CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size); + args->buffer->decStrong(0); + return NULL; +} + +void RenderProxy::setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size) { + SETUP_TASK(setTextureAtlas); + args->thread = &mRenderThread; + args->buffer = buffer.get(); + args->buffer->incStrong(0); + args->map = map; + args->size = size; + post(task); +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index a95f8f0..df0aff0 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -73,10 +73,11 @@ public: float density); ANDROID_API void destroyCanvasAndSurface(); - ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion); + ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion); ANDROID_API void runWithGlContext(RenderTask* task); + static void enqueueDestroyLayer(Layer* layer); ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height); ANDROID_API DeferredLayerUpdater* createTextureLayer(); ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); @@ -90,6 +91,8 @@ public: ANDROID_API void dumpProfileInfo(int fd); + ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size); + private: RenderThread& mRenderThread; CanvasContext* mContext; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 4a4e254..03e98d5 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -21,7 +21,9 @@ #include <gui/DisplayEventReceiver.h> #include <utils/Log.h> +#include "../RenderState.h" #include "CanvasContext.h" +#include "EglManager.h" #include "RenderProxy.h" namespace android { @@ -138,13 +140,16 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() , mDisplayEventReceiver(0) , mVsyncRequested(false) , mFrameCallbackTaskPending(false) - , mFrameCallbackTask(0) { + , mFrameCallbackTask(0) + , mRenderState(NULL) + , mEglManager(NULL) { mFrameCallbackTask = new DispatchFrameCallbacks(this); mLooper = new Looper(false); run("RenderThread"); } RenderThread::~RenderThread() { + LOG_ALWAYS_FATAL("Can't destroy the render thread"); } void RenderThread::initializeDisplayEventReceiver() { @@ -159,6 +164,12 @@ void RenderThread::initializeDisplayEventReceiver() { Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this); } +void RenderThread::initThreadLocals() { + initializeDisplayEventReceiver(); + mEglManager = new EglManager(*this); + mRenderState = new RenderState(); +} + 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. " @@ -233,7 +244,7 @@ void RenderThread::requestVsync() { } bool RenderThread::threadLoop() { - initializeDisplayEventReceiver(); + initThreadLocals(); int timeoutMillis = -1; for (;;) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 4412584..0b91e9d 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -31,12 +31,18 @@ #include "TimeLord.h" namespace android { + class DisplayEventReceiver; namespace uirenderer { + +class RenderState; + namespace renderthread { class DispatchFrameCallbacks; +class EglManager; +class RenderProxy; class TaskQueue { public: @@ -62,7 +68,7 @@ protected: ~IFrameCallback() {} }; -class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> { +class ANDROID_API RenderThread : public Thread, protected Singleton<RenderThread> { public: // RenderThread takes complete ownership of tasks that are queued // and will delete them after they are run @@ -79,6 +85,8 @@ public: void pushBackFrameCallback(IFrameCallback* callback); TimeLord& timeLord() { return mTimeLord; } + RenderState& renderState() { return *mRenderState; } + EglManager& eglManager() { return *mEglManager; } protected: virtual bool threadLoop(); @@ -86,10 +94,12 @@ protected: private: friend class Singleton<RenderThread>; friend class DispatchFrameCallbacks; + friend class RenderProxy; RenderThread(); virtual ~RenderThread(); + void initThreadLocals(); void initializeDisplayEventReceiver(); static int displayEventReceiverCallback(int fd, int events, void* data); void drainDisplayEventQueue(bool skipCallbacks = false); @@ -119,6 +129,8 @@ private: DispatchFrameCallbacks* mFrameCallbackTask; TimeLord mTimeLord; + RenderState* mRenderState; + EglManager* mEglManager; }; } /* namespace renderthread */ |