CanvasContext.cpp revision 23b797ab5151eb2474f3bdd679f2f07bfd723042
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "CanvasContext" 18 19#include "CanvasContext.h" 20 21#include <cutils/properties.h> 22#include <strings.h> 23 24#include "../Caches.h" 25#include "../Stencil.h" 26 27#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" 28#define GLES_VERSION 2 29 30namespace android { 31namespace uirenderer { 32namespace renderthread { 33 34#define ERROR_CASE(x) case x: return #x; 35static const char* egl_error_str(EGLint error) { 36 switch (error) { 37 ERROR_CASE(EGL_SUCCESS) 38 ERROR_CASE(EGL_NOT_INITIALIZED) 39 ERROR_CASE(EGL_BAD_ACCESS) 40 ERROR_CASE(EGL_BAD_ALLOC) 41 ERROR_CASE(EGL_BAD_ATTRIBUTE) 42 ERROR_CASE(EGL_BAD_CONFIG) 43 ERROR_CASE(EGL_BAD_CONTEXT) 44 ERROR_CASE(EGL_BAD_CURRENT_SURFACE) 45 ERROR_CASE(EGL_BAD_DISPLAY) 46 ERROR_CASE(EGL_BAD_MATCH) 47 ERROR_CASE(EGL_BAD_NATIVE_PIXMAP) 48 ERROR_CASE(EGL_BAD_NATIVE_WINDOW) 49 ERROR_CASE(EGL_BAD_PARAMETER) 50 ERROR_CASE(EGL_BAD_SURFACE) 51 ERROR_CASE(EGL_CONTEXT_LOST) 52 default: 53 return "Unknown error"; 54 } 55} 56static const char* egl_error_str() { 57 return egl_error_str(eglGetError()); 58} 59 60static bool load_dirty_regions_property() { 61 char buf[PROPERTY_VALUE_MAX]; 62 int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true"); 63 return !strncasecmp("true", buf, len); 64} 65 66// This class contains the shared global EGL objects, such as EGLDisplay 67// and EGLConfig, which are re-used by CanvasContext 68class GlobalContext { 69public: 70 static GlobalContext* get(); 71 72 // Returns true if EGL was initialized, 73 // false if it was already initialized 74 bool initialize(); 75 76 bool usePBufferSurface(); 77 EGLSurface createSurface(EGLNativeWindowType window); 78 void destroySurface(EGLSurface surface); 79 80 void destroy(); 81 82 bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; } 83 bool makeCurrent(EGLSurface surface); 84 bool swapBuffers(EGLSurface surface); 85 86 bool enableDirtyRegions(EGLSurface surface); 87 88private: 89 GlobalContext(); 90 // GlobalContext is never destroyed, method is purposely not implemented 91 ~GlobalContext(); 92 93 bool loadConfig(); 94 bool createContext(); 95 96 static GlobalContext* sContext; 97 98 EGLDisplay mEglDisplay; 99 EGLConfig mEglConfig; 100 EGLContext mEglContext; 101 EGLSurface mPBufferSurface; 102 103 const bool mRequestDirtyRegions; 104 bool mCanSetDirtyRegions; 105 106 EGLSurface mCurrentSurface; 107}; 108 109GlobalContext* GlobalContext::sContext = 0; 110 111GlobalContext* GlobalContext::get() { 112 if (!sContext) { 113 sContext = new GlobalContext(); 114 } 115 return sContext; 116} 117 118GlobalContext::GlobalContext() 119 : mEglDisplay(EGL_NO_DISPLAY) 120 , mEglConfig(0) 121 , mEglContext(EGL_NO_CONTEXT) 122 , mPBufferSurface(EGL_NO_SURFACE) 123 , mRequestDirtyRegions(load_dirty_regions_property()) 124 , mCurrentSurface(EGL_NO_SURFACE) { 125 mCanSetDirtyRegions = mRequestDirtyRegions; 126 ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); 127} 128 129bool GlobalContext::initialize() { 130 if (mEglDisplay != EGL_NO_DISPLAY) return false; 131 132 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 133 if (mEglDisplay == EGL_NO_DISPLAY) { 134 ALOGE("Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); 135 return false; 136 } 137 138 EGLint major, minor; 139 if (eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE) { 140 ALOGE("Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str()); 141 return false; 142 } 143 ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); 144 145 if (!loadConfig()) { 146 return false; 147 } 148 if (!createContext()) { 149 return false; 150 } 151 152 return true; 153} 154 155bool GlobalContext::loadConfig() { 156 EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; 157 EGLint attribs[] = { 158 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 159 EGL_RED_SIZE, 8, 160 EGL_GREEN_SIZE, 8, 161 EGL_BLUE_SIZE, 8, 162 EGL_ALPHA_SIZE, 8, 163 EGL_DEPTH_SIZE, 0, 164 EGL_CONFIG_CAVEAT, EGL_NONE, 165 EGL_STENCIL_SIZE, Stencil::getStencilSize(), 166 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, 167 EGL_NONE 168 }; 169 170 EGLint num_configs = 1; 171 if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) 172 || num_configs != 1) { 173 // Failed to get a valid config 174 if (mCanSetDirtyRegions) { 175 ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); 176 // Try again without dirty regions enabled 177 mCanSetDirtyRegions = false; 178 loadConfig(); 179 } else { 180 ALOGE("Failed to choose config, error = %s", egl_error_str()); 181 return false; 182 } 183 } 184 return true; 185} 186 187bool GlobalContext::createContext() { 188 EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; 189 mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); 190 if (mEglContext == EGL_NO_CONTEXT) { 191 ALOGE("Failed to create context, error = %s", egl_error_str()); 192 return false; 193 } 194 return true; 195} 196 197bool GlobalContext::usePBufferSurface() { 198 if (mEglDisplay == EGL_NO_DISPLAY) return false; 199 200 if (mPBufferSurface == EGL_NO_SURFACE) { 201 EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; 202 mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); 203 } 204 return makeCurrent(mPBufferSurface); 205} 206 207EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) { 208 initialize(); 209 return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); 210} 211 212void GlobalContext::destroySurface(EGLSurface surface) { 213 if (isCurrent(surface)) { 214 makeCurrent(EGL_NO_SURFACE); 215 } 216 if (!eglDestroySurface(mEglDisplay, surface)) { 217 ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str()); 218 } 219} 220 221void GlobalContext::destroy() { 222 if (mEglDisplay == EGL_NO_DISPLAY) return; 223 224 usePBufferSurface(); 225 if (Caches::hasInstance()) { 226 Caches::getInstance().terminate(); 227 } 228 229 eglDestroyContext(mEglDisplay, mEglContext); 230 eglDestroySurface(mEglDisplay, mPBufferSurface); 231 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 232 eglTerminate(mEglDisplay); 233 eglReleaseThread(); 234 235 mEglDisplay = EGL_NO_DISPLAY; 236 mEglContext = EGL_NO_CONTEXT; 237 mPBufferSurface = EGL_NO_SURFACE; 238 mCurrentSurface = EGL_NO_SURFACE; 239} 240 241bool GlobalContext::makeCurrent(EGLSurface surface) { 242 if (isCurrent(surface)) return true; 243 244 if (surface == EGL_NO_SURFACE) { 245 // If we are setting EGL_NO_SURFACE we don't care about any of the potential 246 // return errors, which would only happen if mEglDisplay had already been 247 // destroyed in which case the current context is already NO_CONTEXT 248 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 249 } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { 250 ALOGE("Failed to make current on surface %p, error=%s", (void*)surface, egl_error_str()); 251 return false; 252 } 253 mCurrentSurface = surface; 254 return true; 255} 256 257bool GlobalContext::swapBuffers(EGLSurface surface) { 258 if (!eglSwapBuffers(mEglDisplay, surface)) { 259 ALOGW("eglSwapBuffers failed on surface %p, error=%s", (void*)surface, egl_error_str()); 260 return false; 261 } 262 return true; 263} 264 265bool GlobalContext::enableDirtyRegions(EGLSurface surface) { 266 if (!mRequestDirtyRegions) return false; 267 268 if (mCanSetDirtyRegions) { 269 if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { 270 ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", 271 (void*) surface, egl_error_str()); 272 return false; 273 } 274 return true; 275 } 276 // Perhaps it is already enabled? 277 EGLint value; 278 if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) { 279 ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", 280 (void*) surface, egl_error_str()); 281 return false; 282 } 283 return value == EGL_BUFFER_PRESERVED; 284} 285 286CanvasContext::CanvasContext() 287 : mEglSurface(EGL_NO_SURFACE) 288 , mDirtyRegionsEnabled(false) { 289 mGlobalContext = GlobalContext::get(); 290} 291 292CanvasContext::~CanvasContext() { 293 setSurface(NULL); 294} 295 296bool CanvasContext::setSurface(EGLNativeWindowType window) { 297 if (mEglSurface != EGL_NO_SURFACE) { 298 mGlobalContext->destroySurface(mEglSurface); 299 mEglSurface = EGL_NO_SURFACE; 300 } 301 302 if (window) { 303 mEglSurface = mGlobalContext->createSurface(window); 304 } 305 306 if (mEglSurface != EGL_NO_SURFACE) { 307 mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); 308 } 309 return !window || mEglSurface != EGL_NO_SURFACE; 310} 311 312bool CanvasContext::swapBuffers() { 313 return mGlobalContext->swapBuffers(mEglSurface); 314} 315 316bool CanvasContext::makeCurrent() { 317 return mGlobalContext->makeCurrent(mEglSurface); 318} 319 320bool CanvasContext::useGlobalPBufferSurface() { 321 return GlobalContext::get()->usePBufferSurface(); 322} 323 324} /* namespace renderthread */ 325} /* namespace uirenderer */ 326} /* namespace android */ 327