CanvasContext.cpp revision 8de65a8e05285df52a1e6f0c1d5616dd233298a7
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 void makeCurrent(EGLSurface surface); 95 void beginFrame(EGLSurface surface, EGLint* width, EGLint* height); 96 void swapBuffers(EGLSurface surface); 97 98 bool enableDirtyRegions(EGLSurface surface); 99 100private: 101 GlobalContext(); 102 // GlobalContext is never destroyed, method is purposely not implemented 103 ~GlobalContext(); 104 105 void loadConfig(); 106 void createContext(); 107 void initAtlas(); 108 109 static GlobalContext* sContext; 110 111 EGLDisplay mEglDisplay; 112 EGLConfig mEglConfig; 113 EGLContext mEglContext; 114 EGLSurface mPBufferSurface; 115 116 const bool mRequestDirtyRegions; 117 bool mCanSetDirtyRegions; 118 119 EGLSurface mCurrentSurface; 120}; 121 122GlobalContext* GlobalContext::sContext = 0; 123 124GlobalContext* GlobalContext::get() { 125 if (!sContext) { 126 sContext = new GlobalContext(); 127 } 128 return sContext; 129} 130 131GlobalContext::GlobalContext() 132 : mEglDisplay(EGL_NO_DISPLAY) 133 , mEglConfig(0) 134 , mEglContext(EGL_NO_CONTEXT) 135 , mPBufferSurface(EGL_NO_SURFACE) 136 , mRequestDirtyRegions(load_dirty_regions_property()) 137 , mCurrentSurface(EGL_NO_SURFACE) { 138 mCanSetDirtyRegions = mRequestDirtyRegions; 139 ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); 140} 141 142void GlobalContext::initialize() { 143 if (hasContext()) return; 144 145 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 146 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, 147 "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); 148 149 EGLint major, minor; 150 LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE, 151 "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str()); 152 153 ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); 154 155 loadConfig(); 156 createContext(); 157 usePBufferSurface(); 158 Caches::getInstance().init(); 159 initAtlas(); 160} 161 162bool GlobalContext::hasContext() { 163 return mEglDisplay != EGL_NO_DISPLAY; 164} 165 166void GlobalContext::loadConfig() { 167 EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; 168 EGLint attribs[] = { 169 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 170 EGL_RED_SIZE, 8, 171 EGL_GREEN_SIZE, 8, 172 EGL_BLUE_SIZE, 8, 173 EGL_ALPHA_SIZE, 8, 174 EGL_DEPTH_SIZE, 0, 175 EGL_CONFIG_CAVEAT, EGL_NONE, 176 EGL_STENCIL_SIZE, Stencil::getStencilSize(), 177 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, 178 EGL_NONE 179 }; 180 181 EGLint num_configs = 1; 182 if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) 183 || num_configs != 1) { 184 // Failed to get a valid config 185 if (mCanSetDirtyRegions) { 186 ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); 187 // Try again without dirty regions enabled 188 mCanSetDirtyRegions = false; 189 loadConfig(); 190 } else { 191 LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); 192 } 193 } 194} 195 196void GlobalContext::createContext() { 197 EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; 198 mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); 199 LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, 200 "Failed to create context, error = %s", egl_error_str()); 201} 202 203void GlobalContext::initAtlas() { 204 // TODO implement 205 // For now just run without an atlas 206} 207 208void GlobalContext::usePBufferSurface() { 209 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, 210 "usePBufferSurface() called on uninitialized GlobalContext!"); 211 212 if (mPBufferSurface == EGL_NO_SURFACE) { 213 EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; 214 mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); 215 } 216 makeCurrent(mPBufferSurface); 217} 218 219EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) { 220 initialize(); 221 return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); 222} 223 224void GlobalContext::destroySurface(EGLSurface surface) { 225 if (isCurrent(surface)) { 226 makeCurrent(EGL_NO_SURFACE); 227 } 228 if (!eglDestroySurface(mEglDisplay, surface)) { 229 ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str()); 230 } 231} 232 233void GlobalContext::destroy() { 234 if (mEglDisplay == EGL_NO_DISPLAY) return; 235 236 usePBufferSurface(); 237 if (Caches::hasInstance()) { 238 Caches::getInstance().terminate(); 239 } 240 241 eglDestroyContext(mEglDisplay, mEglContext); 242 eglDestroySurface(mEglDisplay, mPBufferSurface); 243 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 244 eglTerminate(mEglDisplay); 245 eglReleaseThread(); 246 247 mEglDisplay = EGL_NO_DISPLAY; 248 mEglContext = EGL_NO_CONTEXT; 249 mPBufferSurface = EGL_NO_SURFACE; 250 mCurrentSurface = EGL_NO_SURFACE; 251} 252 253void GlobalContext::makeCurrent(EGLSurface surface) { 254 if (isCurrent(surface)) return; 255 256 if (surface == EGL_NO_SURFACE) { 257 // If we are setting EGL_NO_SURFACE we don't care about any of the potential 258 // return errors, which would only happen if mEglDisplay had already been 259 // destroyed in which case the current context is already NO_CONTEXT 260 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 261 } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { 262 LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s", 263 (void*)surface, egl_error_str()); 264 } 265 mCurrentSurface = surface; 266} 267 268void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) { 269 LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, 270 "Tried to beginFrame on EGL_NO_SURFACE!"); 271 makeCurrent(surface); 272 if (width) { 273 eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width); 274 } 275 if (height) { 276 eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height); 277 } 278 eglBeginFrame(mEglDisplay, surface); 279} 280 281void GlobalContext::swapBuffers(EGLSurface surface) { 282 eglSwapBuffers(mEglDisplay, surface); 283 EGLint err = eglGetError(); 284 // TODO: Check whether we need to special case EGL_CONTEXT_LOST 285 LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS, 286 "Encountered EGL error %d %s during rendering", err, egl_error_str(err)); 287} 288 289bool GlobalContext::enableDirtyRegions(EGLSurface surface) { 290 if (!mRequestDirtyRegions) return false; 291 292 if (mCanSetDirtyRegions) { 293 if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { 294 ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", 295 (void*) surface, egl_error_str()); 296 return false; 297 } 298 return true; 299 } 300 // Perhaps it is already enabled? 301 EGLint value; 302 if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) { 303 ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", 304 (void*) surface, egl_error_str()); 305 return false; 306 } 307 return value == EGL_BUFFER_PRESERVED; 308} 309 310CanvasContext::CanvasContext(bool translucent) 311 : mRenderThread(RenderThread::getInstance()) 312 , mEglSurface(EGL_NO_SURFACE) 313 , mDirtyRegionsEnabled(false) 314 , mOpaque(!translucent) 315 , mCanvas(0) 316 , mHaveNewSurface(false) 317 , mInvokeFunctorsPending(false) 318 , mInvokeFunctorsTask(this) { 319 mGlobalContext = GlobalContext::get(); 320} 321 322CanvasContext::~CanvasContext() { 323 removeFunctorsTask(); 324 destroyCanvas(); 325} 326 327void CanvasContext::destroyCanvas() { 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 } 352} 353 354void CanvasContext::swapBuffers() { 355 mGlobalContext->swapBuffers(mEglSurface); 356 mHaveNewSurface = false; 357} 358 359void CanvasContext::makeCurrent() { 360 mGlobalContext->makeCurrent(mEglSurface); 361} 362 363bool CanvasContext::initialize(EGLNativeWindowType window) { 364 if (mCanvas) return false; 365 setSurface(window); 366 makeCurrent(); 367 mCanvas = new OpenGLRenderer(); 368 mCanvas->initProperties(); 369 return true; 370} 371 372void CanvasContext::updateSurface(EGLNativeWindowType window) { 373 setSurface(window); 374 makeCurrent(); 375} 376 377void CanvasContext::setup(int width, int height) { 378 if (!mCanvas) return; 379 mCanvas->setViewport(width, height); 380} 381 382void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters) { 383 mGlobalContext->makeCurrent(mEglSurface); 384 for (size_t i = 0; i < layerUpdaters->size(); i++) { 385 DeferredLayerUpdater* update = layerUpdaters->itemAt(i); 386 LOG_ALWAYS_FATAL_IF(!update->apply(), "Failed to update layer!"); 387 if (update->backingLayer()->deferredUpdateScheduled) { 388 mCanvas->pushLayerUpdate(update->backingLayer()); 389 } 390 } 391} 392 393void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) { 394 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, 395 "drawDisplayList called on a context with no canvas or surface!"); 396 397 EGLint width, height; 398 mGlobalContext->beginFrame(mEglSurface, &width, &height); 399 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { 400 mCanvas->setViewport(width, height); 401 dirty = NULL; 402 } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { 403 dirty = NULL; 404 } 405 406 status_t status; 407 if (dirty) { 408 status = mCanvas->prepareDirty(dirty->left, dirty->top, 409 dirty->right, dirty->bottom, mOpaque); 410 } else { 411 status = mCanvas->prepare(mOpaque); 412 } 413 414 Rect outBounds; 415 status |= mCanvas->drawDisplayList(displayList, outBounds); 416 417 // TODO: Draw debug info 418 // TODO: Performance tracking 419 420 mCanvas->finish(); 421 422 if (status & DrawGlInfo::kStatusDrew) { 423 swapBuffers(); 424 } 425} 426 427void InvokeFunctorsTask::run() { 428 mContext->invokeFunctors(); 429} 430 431void CanvasContext::attachFunctor(Functor* functor) { 432 if (!mCanvas) return; 433 434 mCanvas->attachFunctor(functor); 435 removeFunctorsTask(); 436 queueFunctorsTask(0); 437} 438 439void CanvasContext::detachFunctor(Functor* functor) { 440 if (!mCanvas) return; 441 442 mCanvas->detachFunctor(functor); 443} 444 445void CanvasContext::invokeFunctor(Functor* functor) { 446 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 447 if (mGlobalContext->hasContext()) { 448 requireGlContext(); 449 mode = DrawGlInfo::kModeProcess; 450 } 451 (*functor)(mode, NULL); 452} 453 454void CanvasContext::invokeFunctors() { 455 mInvokeFunctorsPending = false; 456 457 if (!mCanvas) return; 458 459 makeCurrent(); 460 Rect dirty; 461 mCanvas->invokeFunctors(dirty); 462} 463 464void CanvasContext::removeFunctorsTask() { 465 if (!mInvokeFunctorsPending) return; 466 467 mInvokeFunctorsPending = false; 468 mRenderThread.remove(&mInvokeFunctorsTask); 469} 470 471void CanvasContext::queueFunctorsTask(int delayMs) { 472 if (mInvokeFunctorsPending) return; 473 474 mInvokeFunctorsPending = true; 475 mRenderThread.queueDelayed(&mInvokeFunctorsTask, delayMs); 476} 477 478bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 479 requireGlContext(); 480 layer->apply(); 481 return LayerRenderer::copyLayer(layer->backingLayer(), bitmap); 482} 483 484void CanvasContext::runWithGlContext(RenderTask* task) { 485 requireGlContext(); 486 task->run(); 487} 488 489Layer* CanvasContext::createRenderLayer(int width, int height) { 490 requireGlContext(); 491 return LayerRenderer::createRenderLayer(width, height); 492} 493 494Layer* CanvasContext::createTextureLayer() { 495 requireGlContext(); 496 return LayerRenderer::createTextureLayer(); 497} 498 499void CanvasContext::requireGlContext() { 500 if (mEglSurface != EGL_NO_SURFACE) { 501 mGlobalContext->makeCurrent(mEglSurface); 502 } else { 503 mGlobalContext->usePBufferSurface(); 504 } 505} 506 507} /* namespace renderthread */ 508} /* namespace uirenderer */ 509} /* namespace android */ 510