CanvasContext.cpp revision 66f0be65a1046f54ddce0498b242c1fa0776b1ea
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 101 void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); 102 103private: 104 GlobalContext(); 105 // GlobalContext is never destroyed, method is purposely not implemented 106 ~GlobalContext(); 107 108 void loadConfig(); 109 void createContext(); 110 void initAtlas(); 111 112 static GlobalContext* sContext; 113 114 EGLDisplay mEglDisplay; 115 EGLConfig mEglConfig; 116 EGLContext mEglContext; 117 EGLSurface mPBufferSurface; 118 119 const bool mRequestDirtyRegions; 120 bool mCanSetDirtyRegions; 121 122 EGLSurface mCurrentSurface; 123 124 sp<GraphicBuffer> mAtlasBuffer; 125 int64_t* mAtlasMap; 126 size_t mAtlasMapSize; 127}; 128 129GlobalContext* GlobalContext::sContext = 0; 130 131GlobalContext* GlobalContext::get() { 132 if (!sContext) { 133 sContext = new GlobalContext(); 134 } 135 return sContext; 136} 137 138GlobalContext::GlobalContext() 139 : mEglDisplay(EGL_NO_DISPLAY) 140 , mEglConfig(0) 141 , mEglContext(EGL_NO_CONTEXT) 142 , mPBufferSurface(EGL_NO_SURFACE) 143 , mRequestDirtyRegions(load_dirty_regions_property()) 144 , mCurrentSurface(EGL_NO_SURFACE) 145 , mAtlasMap(NULL) 146 , mAtlasMapSize(0) { 147 mCanSetDirtyRegions = mRequestDirtyRegions; 148 ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); 149} 150 151void GlobalContext::initialize() { 152 if (hasContext()) return; 153 154 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 155 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, 156 "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); 157 158 EGLint major, minor; 159 LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE, 160 "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str()); 161 162 ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); 163 164 loadConfig(); 165 createContext(); 166 usePBufferSurface(); 167 Caches::getInstance().init(); 168 initAtlas(); 169} 170 171bool GlobalContext::hasContext() { 172 return mEglDisplay != EGL_NO_DISPLAY; 173} 174 175void GlobalContext::loadConfig() { 176 EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; 177 EGLint attribs[] = { 178 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 179 EGL_RED_SIZE, 8, 180 EGL_GREEN_SIZE, 8, 181 EGL_BLUE_SIZE, 8, 182 EGL_ALPHA_SIZE, 8, 183 EGL_DEPTH_SIZE, 0, 184 EGL_CONFIG_CAVEAT, EGL_NONE, 185 EGL_STENCIL_SIZE, Stencil::getStencilSize(), 186 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, 187 EGL_NONE 188 }; 189 190 EGLint num_configs = 1; 191 if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) 192 || num_configs != 1) { 193 // Failed to get a valid config 194 if (mCanSetDirtyRegions) { 195 ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); 196 // Try again without dirty regions enabled 197 mCanSetDirtyRegions = false; 198 loadConfig(); 199 } else { 200 LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); 201 } 202 } 203} 204 205void GlobalContext::createContext() { 206 EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; 207 mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); 208 LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, 209 "Failed to create context, error = %s", egl_error_str()); 210} 211 212void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, 213 int64_t* map, size_t mapSize) { 214 215 // Already initialized 216 if (mAtlasBuffer.get()) { 217 ALOGW("Multiple calls to setTextureAtlas!"); 218 delete map; 219 return; 220 } 221 222 mAtlasBuffer = buffer; 223 mAtlasMap = map; 224 mAtlasMapSize = mapSize; 225 226 if (hasContext()) { 227 usePBufferSurface(); 228 initAtlas(); 229 } 230} 231 232void GlobalContext::initAtlas() { 233 Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); 234} 235 236void GlobalContext::usePBufferSurface() { 237 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, 238 "usePBufferSurface() called on uninitialized GlobalContext!"); 239 240 if (mPBufferSurface == EGL_NO_SURFACE) { 241 EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; 242 mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); 243 } 244 makeCurrent(mPBufferSurface); 245} 246 247EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) { 248 initialize(); 249 return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); 250} 251 252void GlobalContext::destroySurface(EGLSurface surface) { 253 if (isCurrent(surface)) { 254 makeCurrent(EGL_NO_SURFACE); 255 } 256 if (!eglDestroySurface(mEglDisplay, surface)) { 257 ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str()); 258 } 259} 260 261void GlobalContext::destroy() { 262 if (mEglDisplay == EGL_NO_DISPLAY) return; 263 264 usePBufferSurface(); 265 if (Caches::hasInstance()) { 266 Caches::getInstance().terminate(); 267 } 268 269 eglDestroyContext(mEglDisplay, mEglContext); 270 eglDestroySurface(mEglDisplay, mPBufferSurface); 271 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 272 eglTerminate(mEglDisplay); 273 eglReleaseThread(); 274 275 mEglDisplay = EGL_NO_DISPLAY; 276 mEglContext = EGL_NO_CONTEXT; 277 mPBufferSurface = EGL_NO_SURFACE; 278 mCurrentSurface = EGL_NO_SURFACE; 279} 280 281bool GlobalContext::makeCurrent(EGLSurface surface) { 282 if (isCurrent(surface)) return false; 283 284 if (surface == EGL_NO_SURFACE) { 285 // If we are setting EGL_NO_SURFACE we don't care about any of the potential 286 // return errors, which would only happen if mEglDisplay had already been 287 // destroyed in which case the current context is already NO_CONTEXT 288 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 289 } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { 290 LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s", 291 (void*)surface, egl_error_str()); 292 } 293 mCurrentSurface = surface; 294 return true; 295} 296 297void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) { 298 LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, 299 "Tried to beginFrame on EGL_NO_SURFACE!"); 300 makeCurrent(surface); 301 if (width) { 302 eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width); 303 } 304 if (height) { 305 eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height); 306 } 307 eglBeginFrame(mEglDisplay, surface); 308} 309 310void GlobalContext::swapBuffers(EGLSurface surface) { 311 eglSwapBuffers(mEglDisplay, surface); 312 EGLint err = eglGetError(); 313 LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS, 314 "Encountered EGL error %d %s during rendering", err, egl_error_str(err)); 315} 316 317bool GlobalContext::enableDirtyRegions(EGLSurface surface) { 318 if (!mRequestDirtyRegions) return false; 319 320 if (mCanSetDirtyRegions) { 321 if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { 322 ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", 323 (void*) surface, egl_error_str()); 324 return false; 325 } 326 return true; 327 } 328 // Perhaps it is already enabled? 329 EGLint value; 330 if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) { 331 ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", 332 (void*) surface, egl_error_str()); 333 return false; 334 } 335 return value == EGL_BUFFER_PRESERVED; 336} 337 338CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode) 339 : mRenderThread(RenderThread::getInstance()) 340 , mEglSurface(EGL_NO_SURFACE) 341 , mDirtyRegionsEnabled(false) 342 , mOpaque(!translucent) 343 , mCanvas(0) 344 , mHaveNewSurface(false) 345 , mRootRenderNode(rootRenderNode) { 346 mGlobalContext = GlobalContext::get(); 347} 348 349CanvasContext::~CanvasContext() { 350 destroyCanvasAndSurface(); 351 mRenderThread.removeFrameCallback(this); 352} 353 354void CanvasContext::destroyCanvasAndSurface() { 355 if (mCanvas) { 356 delete mCanvas; 357 mCanvas = 0; 358 } 359 setSurface(NULL); 360} 361 362void CanvasContext::setSurface(EGLNativeWindowType window) { 363 if (mEglSurface != EGL_NO_SURFACE) { 364 mGlobalContext->destroySurface(mEglSurface); 365 mEglSurface = EGL_NO_SURFACE; 366 } 367 368 if (window) { 369 mEglSurface = mGlobalContext->createSurface(window); 370 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 371 "Failed to create EGLSurface for window %p, eglErr = %s", 372 (void*) window, egl_error_str()); 373 } 374 375 if (mEglSurface != EGL_NO_SURFACE) { 376 mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); 377 mHaveNewSurface = true; 378 makeCurrent(); 379 } else { 380 mRenderThread.removeFrameCallback(this); 381 } 382} 383 384void CanvasContext::swapBuffers() { 385 mGlobalContext->swapBuffers(mEglSurface); 386 mHaveNewSurface = false; 387} 388 389void CanvasContext::requireSurface() { 390 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 391 "requireSurface() called but no surface set!"); 392 makeCurrent(); 393} 394 395bool CanvasContext::initialize(EGLNativeWindowType window) { 396 if (mCanvas) return false; 397 setSurface(window); 398 mCanvas = new OpenGLRenderer(); 399 mCanvas->initProperties(); 400 return true; 401} 402 403void CanvasContext::updateSurface(EGLNativeWindowType window) { 404 setSurface(window); 405} 406 407void CanvasContext::pauseSurface(EGLNativeWindowType window) { 408 // TODO: For now we just need a fence, in the future suspend any animations 409 // and such to prevent from trying to render into this surface 410} 411 412void CanvasContext::setup(int width, int height) { 413 if (!mCanvas) return; 414 mCanvas->setViewport(width, height); 415} 416 417void CanvasContext::setOpaque(bool opaque) { 418 mOpaque = opaque; 419} 420 421void CanvasContext::makeCurrent() { 422 // TODO: Figure out why this workaround is needed, see b/13913604 423 // In the meantime this matches the behavior of GLRenderer, so it is not a regression 424 mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface); 425} 426 427void CanvasContext::prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, 428 TreeInfo& info) { 429 LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot prepareDraw without a canvas!"); 430 makeCurrent(); 431 432 processLayerUpdates(layerUpdaters, info); 433 if (info.out.hasAnimations) { 434 // TODO: Uh... crap? 435 } 436 prepareTree(info); 437} 438 439void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, 440 TreeInfo& info) { 441 for (size_t i = 0; i < layerUpdaters->size(); i++) { 442 DeferredLayerUpdater* update = layerUpdaters->itemAt(i); 443 bool success = update->apply(info); 444 LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!"); 445 if (update->backingLayer()->deferredUpdateScheduled) { 446 mCanvas->pushLayerUpdate(update->backingLayer()); 447 } 448 } 449} 450 451void CanvasContext::prepareTree(TreeInfo& info) { 452 mRenderThread.removeFrameCallback(this); 453 454 info.frameTimeMs = mRenderThread.timeLord().frameTimeMs(); 455 mRootRenderNode->prepareTree(info); 456 457 if (info.out.hasAnimations) { 458 if (info.out.hasFunctors) { 459 info.out.requiresUiRedraw = true; 460 } else if (!info.out.requiresUiRedraw) { 461 // If animationsNeedsRedraw is set don't bother posting for an RT anim 462 // as we will just end up fighting the UI thread. 463 mRenderThread.postFrameCallback(this); 464 } 465 } 466} 467 468void CanvasContext::draw(Rect* dirty) { 469 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, 470 "drawDisplayList called on a context with no canvas or surface!"); 471 472 EGLint width, height; 473 mGlobalContext->beginFrame(mEglSurface, &width, &height); 474 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { 475 mCanvas->setViewport(width, height); 476 dirty = NULL; 477 } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { 478 dirty = NULL; 479 } 480 481 status_t status; 482 if (dirty && !dirty->isEmpty()) { 483 status = mCanvas->prepareDirty(dirty->left, dirty->top, 484 dirty->right, dirty->bottom, mOpaque); 485 } else { 486 status = mCanvas->prepare(mOpaque); 487 } 488 489 Rect outBounds; 490 status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds); 491 492 // TODO: Draw debug info 493 // TODO: Performance tracking 494 495 mCanvas->finish(); 496 497 if (status & DrawGlInfo::kStatusDrew) { 498 swapBuffers(); 499 } 500} 501 502// Called by choreographer to do an RT-driven animation 503void CanvasContext::doFrame() { 504 if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { 505 return; 506 } 507 508 ATRACE_CALL(); 509 510 TreeInfo info; 511 info.evaluateAnimations = true; 512 info.performStagingPush = false; 513 info.prepareTextures = false; 514 515 prepareTree(info); 516 draw(NULL); 517} 518 519void CanvasContext::invokeFunctor(Functor* functor) { 520 ATRACE_CALL(); 521 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 522 if (mGlobalContext->hasContext()) { 523 requireGlContext(); 524 mode = DrawGlInfo::kModeProcess; 525 } 526 (*functor)(mode, NULL); 527 528 if (mCanvas) { 529 mCanvas->resume(); 530 } 531} 532 533bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 534 requireGlContext(); 535 TreeInfo info; 536 layer->apply(info); 537 return LayerRenderer::copyLayer(layer->backingLayer(), bitmap); 538} 539 540void CanvasContext::runWithGlContext(RenderTask* task) { 541 requireGlContext(); 542 task->run(); 543} 544 545Layer* CanvasContext::createRenderLayer(int width, int height) { 546 requireSurface(); 547 return LayerRenderer::createRenderLayer(width, height); 548} 549 550Layer* CanvasContext::createTextureLayer() { 551 requireSurface(); 552 return LayerRenderer::createTextureLayer(); 553} 554 555void CanvasContext::requireGlContext() { 556 if (mEglSurface != EGL_NO_SURFACE) { 557 makeCurrent(); 558 } else { 559 mGlobalContext->usePBufferSurface(); 560 } 561} 562 563void CanvasContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, 564 int64_t* map, size_t mapSize) { 565 GlobalContext::get()->setTextureAtlas(buffer, map, mapSize); 566} 567 568} /* namespace renderthread */ 569} /* namespace uirenderer */ 570} /* namespace android */ 571