CanvasContext.cpp revision b6d9211d31d29221501a8f2a33e6ba0fe18d6ef5
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, RenderNode* rootRenderNode) 312 : mRenderThread(RenderThread::getInstance()) 313 , mEglSurface(EGL_NO_SURFACE) 314 , mDirtyRegionsEnabled(false) 315 , mOpaque(!translucent) 316 , mCanvas(0) 317 , mHaveNewSurface(false) 318 , mRootRenderNode(rootRenderNode) { 319 mGlobalContext = GlobalContext::get(); 320} 321 322CanvasContext::~CanvasContext() { 323 destroyCanvasAndSurface(); 324 mRenderThread.removeFrameCallback(this); 325} 326 327void CanvasContext::destroyCanvasAndSurface() { 328 if (mCanvas) { 329 delete mCanvas; 330 mCanvas = 0; 331 } 332 setSurface(NULL); 333} 334 335void CanvasContext::setSurface(EGLNativeWindowType window) { 336 if (mEglSurface != EGL_NO_SURFACE) { 337 mGlobalContext->destroySurface(mEglSurface); 338 mEglSurface = EGL_NO_SURFACE; 339 } 340 341 if (window) { 342 mEglSurface = mGlobalContext->createSurface(window); 343 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 344 "Failed to create EGLSurface for window %p, eglErr = %s", 345 (void*) window, egl_error_str()); 346 } 347 348 if (mEglSurface != EGL_NO_SURFACE) { 349 mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); 350 mHaveNewSurface = true; 351 makeCurrent(); 352 } else { 353 mRenderThread.removeFrameCallback(this); 354 } 355} 356 357void CanvasContext::swapBuffers() { 358 mGlobalContext->swapBuffers(mEglSurface); 359 mHaveNewSurface = false; 360} 361 362void CanvasContext::requireSurface() { 363 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 364 "requireSurface() called but no surface set!"); 365 makeCurrent(); 366} 367 368bool CanvasContext::initialize(EGLNativeWindowType window) { 369 if (mCanvas) return false; 370 setSurface(window); 371 mCanvas = new OpenGLRenderer(); 372 mCanvas->initProperties(); 373 return true; 374} 375 376void CanvasContext::updateSurface(EGLNativeWindowType window) { 377 setSurface(window); 378} 379 380void CanvasContext::pauseSurface(EGLNativeWindowType window) { 381 // TODO: For now we just need a fence, in the future suspend any animations 382 // and such to prevent from trying to render into this surface 383} 384 385void CanvasContext::setup(int width, int height) { 386 if (!mCanvas) return; 387 mCanvas->setViewport(width, height); 388} 389 390void CanvasContext::setOpaque(bool opaque) { 391 mOpaque = opaque; 392} 393 394void CanvasContext::makeCurrent() { 395 // TODO: Figure out why this workaround is needed, see b/13913604 396 // In the meantime this matches the behavior of GLRenderer, so it is not a regression 397 mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface); 398} 399 400void CanvasContext::prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, 401 TreeInfo& info) { 402 LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot prepareDraw without a canvas!"); 403 makeCurrent(); 404 405 processLayerUpdates(layerUpdaters, info); 406 if (info.out.hasAnimations) { 407 // TODO: Uh... crap? 408 } 409 prepareTree(info); 410} 411 412void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, 413 TreeInfo& info) { 414 for (size_t i = 0; i < layerUpdaters->size(); i++) { 415 DeferredLayerUpdater* update = layerUpdaters->itemAt(i); 416 bool success = update->apply(info); 417 LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!"); 418 if (update->backingLayer()->deferredUpdateScheduled) { 419 mCanvas->pushLayerUpdate(update->backingLayer()); 420 } 421 } 422} 423 424void CanvasContext::prepareTree(TreeInfo& info) { 425 mRenderThread.removeFrameCallback(this); 426 427 info.frameTimeMs = mRenderThread.timeLord().frameTimeMs(); 428 mRootRenderNode->prepareTree(info); 429 430 if (info.out.hasAnimations) { 431 if (info.out.hasFunctors) { 432 info.out.requiresUiRedraw = true; 433 } else if (!info.out.requiresUiRedraw) { 434 // If animationsNeedsRedraw is set don't bother posting for an RT anim 435 // as we will just end up fighting the UI thread. 436 mRenderThread.postFrameCallback(this); 437 } 438 } 439} 440 441void CanvasContext::draw(Rect* dirty) { 442 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, 443 "drawDisplayList called on a context with no canvas or surface!"); 444 445 EGLint width, height; 446 mGlobalContext->beginFrame(mEglSurface, &width, &height); 447 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { 448 mCanvas->setViewport(width, height); 449 dirty = NULL; 450 } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { 451 dirty = NULL; 452 } 453 454 status_t status; 455 if (dirty && !dirty->isEmpty()) { 456 status = mCanvas->prepareDirty(dirty->left, dirty->top, 457 dirty->right, dirty->bottom, mOpaque); 458 } else { 459 status = mCanvas->prepare(mOpaque); 460 } 461 462 Rect outBounds; 463 status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds); 464 465 // TODO: Draw debug info 466 // TODO: Performance tracking 467 468 mCanvas->finish(); 469 470 if (status & DrawGlInfo::kStatusDrew) { 471 swapBuffers(); 472 } 473} 474 475// Called by choreographer to do an RT-driven animation 476void CanvasContext::doFrame() { 477 if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { 478 return; 479 } 480 481 ATRACE_CALL(); 482 483 TreeInfo info; 484 info.evaluateAnimations = true; 485 info.performStagingPush = false; 486 info.prepareTextures = false; 487 488 prepareTree(info); 489 draw(NULL); 490} 491 492void CanvasContext::invokeFunctor(Functor* functor) { 493 ATRACE_CALL(); 494 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 495 if (mGlobalContext->hasContext()) { 496 requireGlContext(); 497 mode = DrawGlInfo::kModeProcess; 498 } 499 // TODO: Remove the dummy info in the future 500 DrawGlInfo dummyInfo; 501 memset(&dummyInfo, 0, sizeof(DrawGlInfo)); 502 (*functor)(mode, &dummyInfo); 503 504 if (mCanvas) { 505 mCanvas->resume(); 506 } 507} 508 509bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 510 requireGlContext(); 511 TreeInfo info; 512 layer->apply(info); 513 return LayerRenderer::copyLayer(layer->backingLayer(), bitmap); 514} 515 516void CanvasContext::runWithGlContext(RenderTask* task) { 517 requireGlContext(); 518 task->run(); 519} 520 521Layer* CanvasContext::createRenderLayer(int width, int height) { 522 requireSurface(); 523 return LayerRenderer::createRenderLayer(width, height); 524} 525 526Layer* CanvasContext::createTextureLayer() { 527 requireSurface(); 528 return LayerRenderer::createTextureLayer(); 529} 530 531void CanvasContext::requireGlContext() { 532 if (mEglSurface != EGL_NO_SURFACE) { 533 makeCurrent(); 534 } else { 535 mGlobalContext->usePBufferSurface(); 536 } 537} 538 539} /* namespace renderthread */ 540} /* namespace uirenderer */ 541} /* namespace android */ 542