CanvasContext.cpp revision e4267ea4f20740c37c01bfb6aefcf61fddc4566a
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// Android-specific addition that is used to show when frames began in systrace 36EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); 37 38namespace android { 39namespace uirenderer { 40namespace renderthread { 41 42#define ERROR_CASE(x) case x: return #x; 43static const char* egl_error_str(EGLint error) { 44 switch (error) { 45 ERROR_CASE(EGL_SUCCESS) 46 ERROR_CASE(EGL_NOT_INITIALIZED) 47 ERROR_CASE(EGL_BAD_ACCESS) 48 ERROR_CASE(EGL_BAD_ALLOC) 49 ERROR_CASE(EGL_BAD_ATTRIBUTE) 50 ERROR_CASE(EGL_BAD_CONFIG) 51 ERROR_CASE(EGL_BAD_CONTEXT) 52 ERROR_CASE(EGL_BAD_CURRENT_SURFACE) 53 ERROR_CASE(EGL_BAD_DISPLAY) 54 ERROR_CASE(EGL_BAD_MATCH) 55 ERROR_CASE(EGL_BAD_NATIVE_PIXMAP) 56 ERROR_CASE(EGL_BAD_NATIVE_WINDOW) 57 ERROR_CASE(EGL_BAD_PARAMETER) 58 ERROR_CASE(EGL_BAD_SURFACE) 59 ERROR_CASE(EGL_CONTEXT_LOST) 60 default: 61 return "Unknown error"; 62 } 63} 64static const char* egl_error_str() { 65 return egl_error_str(eglGetError()); 66} 67 68static bool load_dirty_regions_property() { 69 char buf[PROPERTY_VALUE_MAX]; 70 int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true"); 71 return !strncasecmp("true", buf, len); 72} 73 74// This class contains the shared global EGL objects, such as EGLDisplay 75// and EGLConfig, which are re-used by CanvasContext 76class GlobalContext { 77public: 78 static GlobalContext* get(); 79 80 // Returns true on success, false on failure 81 void initialize(); 82 83 bool hasContext(); 84 85 void usePBufferSurface(); 86 EGLSurface createSurface(EGLNativeWindowType window); 87 void destroySurface(EGLSurface surface); 88 89 void destroy(); 90 91 bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; } 92 // Returns true if the current surface changed, false if it was already current 93 bool makeCurrent(EGLSurface surface); 94 void beginFrame(EGLSurface surface, EGLint* width, EGLint* height); 95 void swapBuffers(EGLSurface surface); 96 97 bool enableDirtyRegions(EGLSurface surface); 98 99 void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); 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 sp<GraphicBuffer> mAtlasBuffer; 123 int64_t* mAtlasMap; 124 size_t mAtlasMapSize; 125}; 126 127GlobalContext* GlobalContext::sContext = 0; 128 129GlobalContext* GlobalContext::get() { 130 if (!sContext) { 131 sContext = new GlobalContext(); 132 } 133 return sContext; 134} 135 136GlobalContext::GlobalContext() 137 : mEglDisplay(EGL_NO_DISPLAY) 138 , mEglConfig(0) 139 , mEglContext(EGL_NO_CONTEXT) 140 , mPBufferSurface(EGL_NO_SURFACE) 141 , mRequestDirtyRegions(load_dirty_regions_property()) 142 , mCurrentSurface(EGL_NO_SURFACE) 143 , mAtlasMap(NULL) 144 , mAtlasMapSize(0) { 145 mCanSetDirtyRegions = mRequestDirtyRegions; 146 ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); 147} 148 149void GlobalContext::initialize() { 150 if (hasContext()) return; 151 152 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 153 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, 154 "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); 155 156 EGLint major, minor; 157 LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE, 158 "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str()); 159 160 ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); 161 162 loadConfig(); 163 createContext(); 164 usePBufferSurface(); 165 Caches::getInstance().init(); 166 initAtlas(); 167} 168 169bool GlobalContext::hasContext() { 170 return mEglDisplay != EGL_NO_DISPLAY; 171} 172 173void GlobalContext::loadConfig() { 174 EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; 175 EGLint attribs[] = { 176 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 177 EGL_RED_SIZE, 8, 178 EGL_GREEN_SIZE, 8, 179 EGL_BLUE_SIZE, 8, 180 EGL_ALPHA_SIZE, 8, 181 EGL_DEPTH_SIZE, 0, 182 EGL_CONFIG_CAVEAT, EGL_NONE, 183 EGL_STENCIL_SIZE, Stencil::getStencilSize(), 184 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, 185 EGL_NONE 186 }; 187 188 EGLint num_configs = 1; 189 if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) 190 || num_configs != 1) { 191 // Failed to get a valid config 192 if (mCanSetDirtyRegions) { 193 ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); 194 // Try again without dirty regions enabled 195 mCanSetDirtyRegions = false; 196 loadConfig(); 197 } else { 198 LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); 199 } 200 } 201} 202 203void GlobalContext::createContext() { 204 EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; 205 mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); 206 LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, 207 "Failed to create context, error = %s", egl_error_str()); 208} 209 210void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, 211 int64_t* map, size_t mapSize) { 212 213 // Already initialized 214 if (mAtlasBuffer.get()) { 215 ALOGW("Multiple calls to setTextureAtlas!"); 216 delete map; 217 return; 218 } 219 220 mAtlasBuffer = buffer; 221 mAtlasMap = map; 222 mAtlasMapSize = mapSize; 223 224 if (hasContext()) { 225 usePBufferSurface(); 226 initAtlas(); 227 } 228} 229 230void GlobalContext::initAtlas() { 231 if (mAtlasBuffer.get()) { 232 Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); 233 } 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(ANativeWindow* window) { 363 mNativeWindow = window; 364 365 if (mEglSurface != EGL_NO_SURFACE) { 366 mGlobalContext->destroySurface(mEglSurface); 367 mEglSurface = EGL_NO_SURFACE; 368 } 369 370 if (window) { 371 mEglSurface = mGlobalContext->createSurface(window); 372 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 373 "Failed to create EGLSurface for window %p, eglErr = %s", 374 (void*) window, egl_error_str()); 375 } 376 377 if (mEglSurface != EGL_NO_SURFACE) { 378 mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); 379 mHaveNewSurface = true; 380 makeCurrent(); 381 } else { 382 mRenderThread.removeFrameCallback(this); 383 } 384} 385 386void CanvasContext::swapBuffers() { 387 mGlobalContext->swapBuffers(mEglSurface); 388 mHaveNewSurface = false; 389} 390 391void CanvasContext::requireSurface() { 392 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 393 "requireSurface() called but no surface set!"); 394 makeCurrent(); 395} 396 397bool CanvasContext::initialize(ANativeWindow* window) { 398 if (mCanvas) return false; 399 setSurface(window); 400 mCanvas = new OpenGLRenderer(); 401 mCanvas->initProperties(); 402 return true; 403} 404 405void CanvasContext::updateSurface(ANativeWindow* window) { 406 setSurface(window); 407} 408 409void CanvasContext::pauseSurface(ANativeWindow* window) { 410 // TODO: For now we just need a fence, in the future suspend any animations 411 // and such to prevent from trying to render into this surface 412} 413 414void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius) { 415 if (!mCanvas) return; 416 mCanvas->setViewport(width, height); 417 mCanvas->initializeLight(lightCenter, lightRadius); 418} 419 420void CanvasContext::setOpaque(bool opaque) { 421 mOpaque = opaque; 422} 423 424void CanvasContext::makeCurrent() { 425 // TODO: Figure out why this workaround is needed, see b/13913604 426 // In the meantime this matches the behavior of GLRenderer, so it is not a regression 427 mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface); 428} 429 430void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info) { 431 bool success = layerUpdater->apply(info); 432 LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!"); 433 if (layerUpdater->backingLayer()->deferredUpdateScheduled) { 434 mCanvas->pushLayerUpdate(layerUpdater->backingLayer()); 435 } 436} 437 438void CanvasContext::prepareTree(TreeInfo& info) { 439 mRenderThread.removeFrameCallback(this); 440 441 info.frameTimeMs = mRenderThread.timeLord().frameTimeMs(); 442 info.damageAccumulator = &mDamageAccumulator; 443 mRootRenderNode->prepareTree(info); 444 445 int runningBehind = 0; 446 // TODO: This query is moderately expensive, investigate adding some sort 447 // of fast-path based off when we last called eglSwapBuffers() as well as 448 // last vsync time. Or something. 449 mNativeWindow->query(mNativeWindow.get(), 450 NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); 451 info.out.canDrawThisFrame = !runningBehind; 452 453 if (info.out.hasAnimations || !info.out.canDrawThisFrame) { 454 if (info.out.hasFunctors) { 455 info.out.requiresUiRedraw = true; 456 } else if (!info.out.requiresUiRedraw) { 457 // If animationsNeedsRedraw is set don't bother posting for an RT anim 458 // as we will just end up fighting the UI thread. 459 mRenderThread.postFrameCallback(this); 460 } 461 } 462} 463 464void CanvasContext::notifyFramePending() { 465 ATRACE_CALL(); 466 mRenderThread.pushBackFrameCallback(this); 467} 468 469void CanvasContext::draw() { 470 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, 471 "drawDisplayList called on a context with no canvas or surface!"); 472 473 profiler().markPlaybackStart(); 474 475 SkRect dirty; 476 mDamageAccumulator.finish(&dirty); 477 478 EGLint width, height; 479 mGlobalContext->beginFrame(mEglSurface, &width, &height); 480 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { 481 mCanvas->setViewport(width, height); 482 dirty.setEmpty(); 483 } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { 484 dirty.setEmpty(); 485 } else { 486 profiler().unionDirty(&dirty); 487 } 488 489 status_t status; 490 if (!dirty.isEmpty()) { 491 status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, 492 dirty.fRight, dirty.fBottom, mOpaque); 493 } else { 494 status = mCanvas->prepare(mOpaque); 495 } 496 497 Rect outBounds; 498 status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds); 499 500 profiler().draw(mCanvas); 501 502 mCanvas->finish(); 503 504 profiler().markPlaybackEnd(); 505 506 if (status & DrawGlInfo::kStatusDrew) { 507 swapBuffers(); 508 } 509 510 profiler().finishFrame(); 511} 512 513// Called by choreographer to do an RT-driven animation 514void CanvasContext::doFrame() { 515 if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { 516 return; 517 } 518 519 ATRACE_CALL(); 520 521 profiler().startFrame(); 522 523 TreeInfo info(TreeInfo::MODE_RT_ONLY); 524 info.prepareTextures = false; 525 526 prepareTree(info); 527 if (info.out.canDrawThisFrame) { 528 draw(); 529 } 530} 531 532void CanvasContext::invokeFunctor(Functor* functor) { 533 ATRACE_CALL(); 534 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 535 if (mGlobalContext->hasContext()) { 536 requireGlContext(); 537 mode = DrawGlInfo::kModeProcess; 538 } 539 (*functor)(mode, NULL); 540 541 if (mCanvas) { 542 mCanvas->resume(); 543 } 544} 545 546bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 547 requireGlContext(); 548 TreeInfo info(TreeInfo::MODE_FULL); 549 layer->apply(info); 550 return LayerRenderer::copyLayer(layer->backingLayer(), bitmap); 551} 552 553void CanvasContext::flushCaches(Caches::FlushMode flushMode) { 554 if (mGlobalContext->hasContext()) { 555 requireGlContext(); 556 Caches::getInstance().flush(flushMode); 557 } 558} 559 560void CanvasContext::runWithGlContext(RenderTask* task) { 561 requireGlContext(); 562 task->run(); 563} 564 565Layer* CanvasContext::createRenderLayer(int width, int height) { 566 requireSurface(); 567 return LayerRenderer::createRenderLayer(width, height); 568} 569 570Layer* CanvasContext::createTextureLayer() { 571 requireSurface(); 572 return LayerRenderer::createTextureLayer(); 573} 574 575void CanvasContext::requireGlContext() { 576 if (mEglSurface != EGL_NO_SURFACE) { 577 makeCurrent(); 578 } else { 579 mGlobalContext->usePBufferSurface(); 580 } 581} 582 583void CanvasContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, 584 int64_t* map, size_t mapSize) { 585 GlobalContext::get()->setTextureAtlas(buffer, map, mapSize); 586} 587 588} /* namespace renderthread */ 589} /* namespace uirenderer */ 590} /* namespace android */ 591