CanvasContext.cpp revision dbc9a86d05e5e835051de22f6cb30ec1921e9705
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 <private/hwui/DrawGlInfo.h> 23#include <strings.h> 24 25#include "RenderThread.h" 26#include "../Caches.h" 27#include "../DeferredLayerUpdater.h" 28#include "../LayerRenderer.h" 29#include "../OpenGLRenderer.h" 30#include "../Stencil.h" 31 32#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" 33#define GLES_VERSION 2 34 35#ifdef USE_OPENGL_RENDERER 36// Android-specific addition that is used to show when frames began in systrace 37EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); 38#endif 39 40namespace android { 41namespace uirenderer { 42namespace renderthread { 43 44#define ERROR_CASE(x) case x: return #x; 45static const char* egl_error_str(EGLint error) { 46 switch (error) { 47 ERROR_CASE(EGL_SUCCESS) 48 ERROR_CASE(EGL_NOT_INITIALIZED) 49 ERROR_CASE(EGL_BAD_ACCESS) 50 ERROR_CASE(EGL_BAD_ALLOC) 51 ERROR_CASE(EGL_BAD_ATTRIBUTE) 52 ERROR_CASE(EGL_BAD_CONFIG) 53 ERROR_CASE(EGL_BAD_CONTEXT) 54 ERROR_CASE(EGL_BAD_CURRENT_SURFACE) 55 ERROR_CASE(EGL_BAD_DISPLAY) 56 ERROR_CASE(EGL_BAD_MATCH) 57 ERROR_CASE(EGL_BAD_NATIVE_PIXMAP) 58 ERROR_CASE(EGL_BAD_NATIVE_WINDOW) 59 ERROR_CASE(EGL_BAD_PARAMETER) 60 ERROR_CASE(EGL_BAD_SURFACE) 61 ERROR_CASE(EGL_CONTEXT_LOST) 62 default: 63 return "Unknown error"; 64 } 65} 66static const char* egl_error_str() { 67 return egl_error_str(eglGetError()); 68} 69 70static bool load_dirty_regions_property() { 71 char buf[PROPERTY_VALUE_MAX]; 72 int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true"); 73 return !strncasecmp("true", buf, len); 74} 75 76// This class contains the shared global EGL objects, such as EGLDisplay 77// and EGLConfig, which are re-used by CanvasContext 78class GlobalContext { 79public: 80 static GlobalContext* get(); 81 82 // Returns true on success, false on failure 83 void initialize(); 84 85 bool hasContext(); 86 87 void usePBufferSurface(); 88 EGLSurface createSurface(EGLNativeWindowType window); 89 void destroySurface(EGLSurface surface); 90 91 void destroy(); 92 93 bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; } 94 // Returns true if the current surface changed, false if it was already current 95 bool makeCurrent(EGLSurface surface); 96 void beginFrame(EGLSurface surface, EGLint* width, EGLint* height); 97 void swapBuffers(EGLSurface surface); 98 99 bool enableDirtyRegions(EGLSurface surface); 100 101private: 102 GlobalContext(); 103 // GlobalContext is never destroyed, method is purposely not implemented 104 ~GlobalContext(); 105 106 void loadConfig(); 107 void createContext(); 108 void initAtlas(); 109 110 static GlobalContext* sContext; 111 112 EGLDisplay mEglDisplay; 113 EGLConfig mEglConfig; 114 EGLContext mEglContext; 115 EGLSurface mPBufferSurface; 116 117 const bool mRequestDirtyRegions; 118 bool mCanSetDirtyRegions; 119 120 EGLSurface mCurrentSurface; 121}; 122 123GlobalContext* GlobalContext::sContext = 0; 124 125GlobalContext* GlobalContext::get() { 126 if (!sContext) { 127 sContext = new GlobalContext(); 128 } 129 return sContext; 130} 131 132GlobalContext::GlobalContext() 133 : mEglDisplay(EGL_NO_DISPLAY) 134 , mEglConfig(0) 135 , mEglContext(EGL_NO_CONTEXT) 136 , mPBufferSurface(EGL_NO_SURFACE) 137 , mRequestDirtyRegions(load_dirty_regions_property()) 138 , mCurrentSurface(EGL_NO_SURFACE) { 139 mCanSetDirtyRegions = mRequestDirtyRegions; 140 ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); 141} 142 143void GlobalContext::initialize() { 144 if (hasContext()) return; 145 146 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 147 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, 148 "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); 149 150 EGLint major, minor; 151 LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE, 152 "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str()); 153 154 ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); 155 156 loadConfig(); 157 createContext(); 158 usePBufferSurface(); 159 Caches::getInstance().init(); 160 initAtlas(); 161} 162 163bool GlobalContext::hasContext() { 164 return mEglDisplay != EGL_NO_DISPLAY; 165} 166 167void GlobalContext::loadConfig() { 168 EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; 169 EGLint attribs[] = { 170 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 171 EGL_RED_SIZE, 8, 172 EGL_GREEN_SIZE, 8, 173 EGL_BLUE_SIZE, 8, 174 EGL_ALPHA_SIZE, 8, 175 EGL_DEPTH_SIZE, 0, 176 EGL_CONFIG_CAVEAT, EGL_NONE, 177 EGL_STENCIL_SIZE, Stencil::getStencilSize(), 178 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, 179 EGL_NONE 180 }; 181 182 EGLint num_configs = 1; 183 if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) 184 || num_configs != 1) { 185 // Failed to get a valid config 186 if (mCanSetDirtyRegions) { 187 ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); 188 // Try again without dirty regions enabled 189 mCanSetDirtyRegions = false; 190 loadConfig(); 191 } else { 192 LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); 193 } 194 } 195} 196 197void GlobalContext::createContext() { 198 EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; 199 mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); 200 LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, 201 "Failed to create context, error = %s", egl_error_str()); 202} 203 204void GlobalContext::initAtlas() { 205 // TODO implement 206 // For now just run without an atlas 207} 208 209void GlobalContext::usePBufferSurface() { 210 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, 211 "usePBufferSurface() called on uninitialized GlobalContext!"); 212 213 if (mPBufferSurface == EGL_NO_SURFACE) { 214 EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; 215 mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); 216 } 217 makeCurrent(mPBufferSurface); 218} 219 220EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) { 221 initialize(); 222 return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); 223} 224 225void GlobalContext::destroySurface(EGLSurface surface) { 226 if (isCurrent(surface)) { 227 makeCurrent(EGL_NO_SURFACE); 228 } 229 if (!eglDestroySurface(mEglDisplay, surface)) { 230 ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str()); 231 } 232} 233 234void GlobalContext::destroy() { 235 if (mEglDisplay == EGL_NO_DISPLAY) return; 236 237 usePBufferSurface(); 238 if (Caches::hasInstance()) { 239 Caches::getInstance().terminate(); 240 } 241 242 eglDestroyContext(mEglDisplay, mEglContext); 243 eglDestroySurface(mEglDisplay, mPBufferSurface); 244 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 245 eglTerminate(mEglDisplay); 246 eglReleaseThread(); 247 248 mEglDisplay = EGL_NO_DISPLAY; 249 mEglContext = EGL_NO_CONTEXT; 250 mPBufferSurface = EGL_NO_SURFACE; 251 mCurrentSurface = EGL_NO_SURFACE; 252} 253 254bool GlobalContext::makeCurrent(EGLSurface surface) { 255 if (isCurrent(surface)) return false; 256 257 if (surface == EGL_NO_SURFACE) { 258 // If we are setting EGL_NO_SURFACE we don't care about any of the potential 259 // return errors, which would only happen if mEglDisplay had already been 260 // destroyed in which case the current context is already NO_CONTEXT 261 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 262 } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { 263 LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s", 264 (void*)surface, egl_error_str()); 265 } 266 mCurrentSurface = surface; 267 return true; 268} 269 270void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) { 271 LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, 272 "Tried to beginFrame on EGL_NO_SURFACE!"); 273 makeCurrent(surface); 274 if (width) { 275 eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width); 276 } 277 if (height) { 278 eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height); 279 } 280 eglBeginFrame(mEglDisplay, surface); 281} 282 283void GlobalContext::swapBuffers(EGLSurface surface) { 284 eglSwapBuffers(mEglDisplay, surface); 285 EGLint err = eglGetError(); 286 LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS, 287 "Encountered EGL error %d %s during rendering", err, egl_error_str(err)); 288} 289 290bool GlobalContext::enableDirtyRegions(EGLSurface surface) { 291 if (!mRequestDirtyRegions) return false; 292 293 if (mCanSetDirtyRegions) { 294 if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { 295 ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", 296 (void*) surface, egl_error_str()); 297 return false; 298 } 299 return true; 300 } 301 // Perhaps it is already enabled? 302 EGLint value; 303 if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) { 304 ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", 305 (void*) surface, egl_error_str()); 306 return false; 307 } 308 return value == EGL_BUFFER_PRESERVED; 309} 310 311CanvasContext::CanvasContext(bool translucent) 312 : mRenderThread(RenderThread::getInstance()) 313 , mEglSurface(EGL_NO_SURFACE) 314 , mDirtyRegionsEnabled(false) 315 , mOpaque(!translucent) 316 , mCanvas(0) 317 , mHaveNewSurface(false) { 318 mGlobalContext = GlobalContext::get(); 319} 320 321CanvasContext::~CanvasContext() { 322 destroyCanvasAndSurface(); 323} 324 325void CanvasContext::destroyCanvasAndSurface() { 326 if (mCanvas) { 327 delete mCanvas; 328 mCanvas = 0; 329 } 330 setSurface(NULL); 331} 332 333void CanvasContext::setSurface(EGLNativeWindowType window) { 334 if (mEglSurface != EGL_NO_SURFACE) { 335 mGlobalContext->destroySurface(mEglSurface); 336 mEglSurface = EGL_NO_SURFACE; 337 } 338 339 if (window) { 340 mEglSurface = mGlobalContext->createSurface(window); 341 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 342 "Failed to create EGLSurface for window %p, eglErr = %s", 343 (void*) window, egl_error_str()); 344 } 345 346 if (mEglSurface != EGL_NO_SURFACE) { 347 mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); 348 mHaveNewSurface = true; 349 makeCurrent(); 350 } 351} 352 353void CanvasContext::swapBuffers() { 354 mGlobalContext->swapBuffers(mEglSurface); 355 mHaveNewSurface = false; 356} 357 358void CanvasContext::requireSurface() { 359 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 360 "requireSurface() called but no surface set!"); 361 makeCurrent(); 362} 363 364bool CanvasContext::initialize(EGLNativeWindowType window) { 365 if (mCanvas) return false; 366 setSurface(window); 367 mCanvas = new OpenGLRenderer(); 368 mCanvas->initProperties(); 369 return true; 370} 371 372void CanvasContext::updateSurface(EGLNativeWindowType window) { 373 setSurface(window); 374} 375 376void CanvasContext::pauseSurface(EGLNativeWindowType window) { 377 // TODO: For now we just need a fence, in the future suspend any animations 378 // and such to prevent from trying to render into this surface 379} 380 381void CanvasContext::setup(int width, int height) { 382 if (!mCanvas) return; 383 mCanvas->setViewport(width, height); 384} 385 386void CanvasContext::makeCurrent() { 387 // TODO: Figure out why this workaround is needed, see b/13913604 388 // In the meantime this matches the behavior of GLRenderer, so it is not a regression 389 mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface); 390} 391 392void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, 393 TreeInfo& info) { 394 LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot process layer updates without a canvas!"); 395 makeCurrent(); 396 for (size_t i = 0; i < layerUpdaters->size(); i++) { 397 DeferredLayerUpdater* update = layerUpdaters->itemAt(i); 398 bool success = update->apply(info); 399 LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!"); 400 if (update->backingLayer()->deferredUpdateScheduled) { 401 mCanvas->pushLayerUpdate(update->backingLayer()); 402 } 403 } 404} 405 406void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) { 407 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, 408 "drawDisplayList called on a context with no canvas or surface!"); 409 410 EGLint width, height; 411 mGlobalContext->beginFrame(mEglSurface, &width, &height); 412 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { 413 mCanvas->setViewport(width, height); 414 dirty = NULL; 415 } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { 416 dirty = NULL; 417 } 418 419 status_t status; 420 if (dirty) { 421 status = mCanvas->prepareDirty(dirty->left, dirty->top, 422 dirty->right, dirty->bottom, mOpaque); 423 } else { 424 status = mCanvas->prepare(mOpaque); 425 } 426 427 Rect outBounds; 428 status |= mCanvas->drawDisplayList(displayList, outBounds); 429 430 // TODO: Draw debug info 431 // TODO: Performance tracking 432 433 mCanvas->finish(); 434 435 if (status & DrawGlInfo::kStatusDrew) { 436 swapBuffers(); 437 } 438} 439 440void CanvasContext::invokeFunctor(Functor* functor) { 441 ATRACE_CALL(); 442 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 443 if (mGlobalContext->hasContext()) { 444 requireGlContext(); 445 mode = DrawGlInfo::kModeProcess; 446 } 447 // TODO: Remove the dummy info in the future 448 DrawGlInfo dummyInfo; 449 memset(&dummyInfo, 0, sizeof(DrawGlInfo)); 450 (*functor)(mode, &dummyInfo); 451 452 if (mCanvas) { 453 mCanvas->resume(); 454 } 455} 456 457bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 458 requireGlContext(); 459 TreeInfo info; 460 layer->apply(info); 461 return LayerRenderer::copyLayer(layer->backingLayer(), bitmap); 462} 463 464void CanvasContext::runWithGlContext(RenderTask* task) { 465 requireGlContext(); 466 task->run(); 467} 468 469Layer* CanvasContext::createRenderLayer(int width, int height) { 470 requireSurface(); 471 return LayerRenderer::createRenderLayer(width, height); 472} 473 474Layer* CanvasContext::createTextureLayer() { 475 requireSurface(); 476 return LayerRenderer::createTextureLayer(); 477} 478 479void CanvasContext::requireGlContext() { 480 if (mEglSurface != EGL_NO_SURFACE) { 481 makeCurrent(); 482 } else { 483 mGlobalContext->usePBufferSurface(); 484 } 485} 486 487} /* namespace renderthread */ 488} /* namespace uirenderer */ 489} /* namespace android */ 490