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