HardwareRenderer.java revision 29d23ecfd8612ecd4a7b2140acd344934b73a558
1/* 2 * Copyright (C) 2010 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 18package android.view; 19 20import android.content.ComponentCallbacks; 21import android.graphics.Paint; 22import android.graphics.Rect; 23import android.graphics.SurfaceTexture; 24import android.os.SystemClock; 25import android.os.SystemProperties; 26import android.util.Log; 27 28import javax.microedition.khronos.egl.EGL10; 29import javax.microedition.khronos.egl.EGL11; 30import javax.microedition.khronos.egl.EGLConfig; 31import javax.microedition.khronos.egl.EGLContext; 32import javax.microedition.khronos.egl.EGLDisplay; 33import javax.microedition.khronos.egl.EGLSurface; 34import javax.microedition.khronos.opengles.GL; 35 36import static javax.microedition.khronos.egl.EGL10.*; 37 38/** 39 * Interface for rendering a ViewAncestor using hardware acceleration. 40 * 41 * @hide 42 */ 43public abstract class HardwareRenderer { 44 static final String LOG_TAG = "HardwareRenderer"; 45 46 /** 47 * Turn on to only refresh the parts of the screen that need updating. 48 * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY} 49 * must also have the value "true". 50 */ 51 public static final boolean RENDER_DIRTY_REGIONS = true; 52 53 /** 54 * System property used to enable or disable dirty regions invalidation. 55 * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true. 56 * The default value of this property is assumed to be true. 57 * 58 * Possible values: 59 * "true", to enable partial invalidates 60 * "false", to disable partial invalidates 61 */ 62 static final String RENDER_DIRTY_REGIONS_PROPERTY = "hwui.render_dirty_regions"; 63 64 /** 65 * System property used to enable or disable vsync. 66 * The default value of this property is assumed to be false. 67 * 68 * Possible values: 69 * "true", to disable vsync 70 * "false", to enable vsync 71 */ 72 static final String DISABLE_VSYNC_PROPERTY = "hwui.disable_vsync"; 73 74 /** 75 * System property used to debug EGL configuration choice. 76 * 77 * Possible values: 78 * "choice", print the chosen configuration only 79 * "all", print all possible configurations 80 */ 81 static final String PRINT_CONFIG_PROPERTY = "hwui.print_config"; 82 83 /** 84 * Turn on to draw dirty regions every other frame. 85 */ 86 private static final boolean DEBUG_DIRTY_REGION = false; 87 88 /** 89 * A process can set this flag to false to prevent the use of hardware 90 * rendering. 91 * 92 * @hide 93 */ 94 public static boolean sRendererDisabled = false; 95 96 private boolean mEnabled; 97 private boolean mRequested = true; 98 99 /** 100 * Invoke this method to disable hardware rendering in the current process. 101 * 102 * @hide 103 */ 104 public static void disable() { 105 sRendererDisabled = true; 106 } 107 108 /** 109 * Indicates whether hardware acceleration is available under any form for 110 * the view hierarchy. 111 * 112 * @return True if the view hierarchy can potentially be hardware accelerated, 113 * false otherwise 114 */ 115 public static boolean isAvailable() { 116 return GLES20Canvas.isAvailable(); 117 } 118 119 /** 120 * Destroys the hardware rendering context. 121 * 122 * @param full If true, destroys all associated resources. 123 */ 124 abstract void destroy(boolean full); 125 126 /** 127 * Initializes the hardware renderer for the specified surface. 128 * 129 * @param holder The holder for the surface to hardware accelerate. 130 * 131 * @return True if the initialization was successful, false otherwise. 132 */ 133 abstract boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException; 134 135 /** 136 * Updates the hardware renderer for the specified surface. 137 * 138 * @param holder The holder for the surface to hardware accelerate. 139 */ 140 abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException; 141 142 /** 143 * This method should be invoked whenever the current hardware renderer 144 * context should be reset. 145 */ 146 abstract void invalidate(); 147 148 /** 149 * This method should be invoked to ensure the hardware renderer is in 150 * valid state (for instance, to ensure the correct EGL context is bound 151 * to the current thread.) 152 * 153 * @return true if the renderer is now valid, false otherwise 154 */ 155 abstract boolean validate(); 156 157 /** 158 * Setup the hardware renderer for drawing. This is called whenever the 159 * size of the target surface changes or when the surface is first created. 160 * 161 * @param width Width of the drawing surface. 162 * @param height Height of the drawing surface. 163 */ 164 abstract void setup(int width, int height); 165 166 /** 167 * Interface used to receive callbacks whenever a view is drawn by 168 * a hardware renderer instance. 169 */ 170 interface HardwareDrawCallbacks { 171 /** 172 * Invoked before a view is drawn by a hardware renderer. 173 * 174 * @param canvas The Canvas used to render the view. 175 */ 176 void onHardwarePreDraw(HardwareCanvas canvas); 177 178 /** 179 * Invoked after a view is drawn by a hardware renderer. 180 * 181 * @param canvas The Canvas used to render the view. 182 */ 183 void onHardwarePostDraw(HardwareCanvas canvas); 184 } 185 186 /** 187 * Draws the specified view. 188 * 189 * @param view The view to draw. 190 * @param attachInfo AttachInfo tied to the specified view. 191 * @param callbacks Callbacks invoked when drawing happens. 192 * @param dirty The dirty rectangle to update, can be null. 193 */ 194 abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, 195 Rect dirty); 196 197 /** 198 * Creates a new display list that can be used to record batches of 199 * drawing operations. 200 * 201 * @return A new display list. 202 */ 203 abstract DisplayList createDisplayList(); 204 205 /** 206 * Creates a new hardware layer. A hardware layer built by calling this 207 * method will be treated as a texture layer, instead of as a render target. 208 * 209 * @param isOpaque Whether the layer should be opaque or not 210 * 211 * @return A hardware layer 212 */ 213 abstract HardwareLayer createHardwareLayer(boolean isOpaque); 214 215 /** 216 * Creates a new hardware layer. 217 * 218 * @param width The minimum width of the layer 219 * @param height The minimum height of the layer 220 * @param isOpaque Whether the layer should be opaque or not 221 * 222 * @return A hardware layer 223 */ 224 abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque); 225 226 /** 227 * Creates a new {@link SurfaceTexture} that can be used to render into the 228 * specified hardware layer. 229 * 230 * 231 * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture} 232 * 233 * @return A {@link SurfaceTexture} 234 */ 235 abstract SurfaceTexture createSurfaceTexture(HardwareLayer layer); 236 237 /** 238 * Initializes the hardware renderer for the specified surface and setup the 239 * renderer for drawing, if needed. This is invoked when the ViewAncestor has 240 * potentially lost the hardware renderer. The hardware renderer should be 241 * reinitialized and setup when the render {@link #isRequested()} and 242 * {@link #isEnabled()}. 243 * 244 * @param width The width of the drawing surface. 245 * @param height The height of the drawing surface. 246 * @param attachInfo The 247 * @param holder 248 */ 249 void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, 250 SurfaceHolder holder) throws Surface.OutOfResourcesException { 251 if (isRequested()) { 252 // We lost the gl context, so recreate it. 253 if (!isEnabled()) { 254 if (initialize(holder)) { 255 setup(width, height); 256 } 257 } 258 } 259 } 260 261 /** 262 * Creates a hardware renderer using OpenGL. 263 * 264 * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.) 265 * @param translucent True if the surface is translucent, false otherwise 266 * 267 * @return A hardware renderer backed by OpenGL. 268 */ 269 static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) { 270 switch (glVersion) { 271 case 2: 272 return Gl20Renderer.create(translucent); 273 } 274 throw new IllegalArgumentException("Unknown GL version: " + glVersion); 275 } 276 277 /** 278 * Invoke this method when the system is running out of memory. This 279 * method will attempt to recover as much memory as possible, based on 280 * the specified hint. 281 * 282 * @param level Hint about the amount of memory that should be trimmed, 283 * see {@link android.content.ComponentCallbacks} 284 */ 285 static void trimMemory(int level) { 286 Gl20Renderer.flushCaches(level); 287 } 288 289 /** 290 * Indicates whether hardware acceleration is currently enabled. 291 * 292 * @return True if hardware acceleration is in use, false otherwise. 293 */ 294 boolean isEnabled() { 295 return mEnabled; 296 } 297 298 /** 299 * Indicates whether hardware acceleration is currently enabled. 300 * 301 * @param enabled True if the hardware renderer is in use, false otherwise. 302 */ 303 void setEnabled(boolean enabled) { 304 mEnabled = enabled; 305 } 306 307 /** 308 * Indicates whether hardware acceleration is currently request but not 309 * necessarily enabled yet. 310 * 311 * @return True if requested, false otherwise. 312 */ 313 boolean isRequested() { 314 return mRequested; 315 } 316 317 /** 318 * Indicates whether hardware acceleration is currently requested but not 319 * necessarily enabled yet. 320 * 321 * @return True to request hardware acceleration, false otherwise. 322 */ 323 void setRequested(boolean requested) { 324 mRequested = requested; 325 } 326 327 @SuppressWarnings({"deprecation"}) 328 static abstract class GlRenderer extends HardwareRenderer { 329 // These values are not exposed in our EGL APIs 330 static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 331 static final int EGL_OPENGL_ES2_BIT = 4; 332 static final int EGL_SURFACE_TYPE = 0x3033; 333 static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400; 334 335 private static final int SURFACE_STATE_ERROR = 0; 336 private static final int SURFACE_STATE_SUCCESS = 1; 337 private static final int SURFACE_STATE_UPDATED = 2; 338 339 static EGL10 sEgl; 340 static EGLDisplay sEglDisplay; 341 static EGLConfig sEglConfig; 342 static final Object[] sEglLock = new Object[0]; 343 344 static final ThreadLocal<EGLContext> sEglContextStorage = new ThreadLocal<EGLContext>(); 345 346 EGLContext mEglContext; 347 Thread mEglThread; 348 349 EGLSurface mEglSurface; 350 351 GL mGl; 352 HardwareCanvas mCanvas; 353 int mFrameCount; 354 Paint mDebugPaint; 355 356 static boolean sDirtyRegions; 357 static final boolean sDirtyRegionsRequested; 358 static { 359 String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); 360 //noinspection PointlessBooleanExpression,ConstantConditions 361 sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); 362 sDirtyRegionsRequested = sDirtyRegions; 363 } 364 365 boolean mDirtyRegionsEnabled; 366 final boolean mVsyncDisabled; 367 368 final int mGlVersion; 369 final boolean mTranslucent; 370 371 private boolean mDestroyed; 372 373 private final Rect mRedrawClip = new Rect(); 374 375 GlRenderer(int glVersion, boolean translucent) { 376 mGlVersion = glVersion; 377 mTranslucent = translucent; 378 379 final String vsyncProperty = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false"); 380 mVsyncDisabled = "true".equalsIgnoreCase(vsyncProperty); 381 if (mVsyncDisabled) { 382 Log.d(LOG_TAG, "Disabling v-sync"); 383 } 384 } 385 386 /** 387 * Indicates whether this renderer instance can track and update dirty regions. 388 */ 389 boolean hasDirtyRegions() { 390 return mDirtyRegionsEnabled; 391 } 392 393 /** 394 * Return a string for the EGL error code, or the hex representation 395 * if the error is unknown. 396 * 397 * @param error The EGL error to convert into a String. 398 * 399 * @return An error string correponding to the EGL error code. 400 */ 401 static String getEGLErrorString(int error) { 402 switch (error) { 403 case EGL_SUCCESS: 404 return "EGL_SUCCESS"; 405 case EGL_NOT_INITIALIZED: 406 return "EGL_NOT_INITIALIZED"; 407 case EGL_BAD_ACCESS: 408 return "EGL_BAD_ACCESS"; 409 case EGL_BAD_ALLOC: 410 return "EGL_BAD_ALLOC"; 411 case EGL_BAD_ATTRIBUTE: 412 return "EGL_BAD_ATTRIBUTE"; 413 case EGL_BAD_CONFIG: 414 return "EGL_BAD_CONFIG"; 415 case EGL_BAD_CONTEXT: 416 return "EGL_BAD_CONTEXT"; 417 case EGL_BAD_CURRENT_SURFACE: 418 return "EGL_BAD_CURRENT_SURFACE"; 419 case EGL_BAD_DISPLAY: 420 return "EGL_BAD_DISPLAY"; 421 case EGL_BAD_MATCH: 422 return "EGL_BAD_MATCH"; 423 case EGL_BAD_NATIVE_PIXMAP: 424 return "EGL_BAD_NATIVE_PIXMAP"; 425 case EGL_BAD_NATIVE_WINDOW: 426 return "EGL_BAD_NATIVE_WINDOW"; 427 case EGL_BAD_PARAMETER: 428 return "EGL_BAD_PARAMETER"; 429 case EGL_BAD_SURFACE: 430 return "EGL_BAD_SURFACE"; 431 case EGL11.EGL_CONTEXT_LOST: 432 return "EGL_CONTEXT_LOST"; 433 default: 434 return "0x" + Integer.toHexString(error); 435 } 436 } 437 438 /** 439 * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)} 440 * is invoked and the requested flag is turned off. The error code is 441 * also logged as a warning. 442 */ 443 void checkEglErrors() { 444 if (isEnabled()) { 445 int error = sEgl.eglGetError(); 446 if (error != EGL_SUCCESS) { 447 // something bad has happened revert to 448 // normal rendering. 449 fallback(error != EGL11.EGL_CONTEXT_LOST); 450 Log.w(LOG_TAG, "EGL error: " + getEGLErrorString(error)); 451 } 452 } 453 } 454 455 private void fallback(boolean fallback) { 456 destroy(true); 457 if (fallback) { 458 // we'll try again if it was context lost 459 setRequested(false); 460 Log.w(LOG_TAG, "Mountain View, we've had a problem here. " 461 + "Switching back to software rendering."); 462 } 463 } 464 465 @Override 466 boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException { 467 if (isRequested() && !isEnabled()) { 468 initializeEgl(); 469 mGl = createEglSurface(holder); 470 mDestroyed = false; 471 472 if (mGl != null) { 473 int err = sEgl.eglGetError(); 474 if (err != EGL_SUCCESS) { 475 destroy(true); 476 setRequested(false); 477 } else { 478 if (mCanvas == null) { 479 mCanvas = createCanvas(); 480 } 481 if (mCanvas != null) { 482 setEnabled(true); 483 } else { 484 Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created"); 485 } 486 } 487 488 return mCanvas != null; 489 } 490 } 491 return false; 492 } 493 494 @Override 495 void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { 496 if (isRequested() && isEnabled()) { 497 createEglSurface(holder); 498 } 499 } 500 501 abstract GLES20Canvas createCanvas(); 502 503 abstract int[] getConfig(boolean dirtyRegions); 504 505 void initializeEgl() { 506 synchronized (sEglLock) { 507 if (sEgl == null && sEglConfig == null) { 508 sEgl = (EGL10) EGLContext.getEGL(); 509 510 // Get to the default display. 511 sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY); 512 513 if (sEglDisplay == EGL_NO_DISPLAY) { 514 throw new RuntimeException("eglGetDisplay failed " 515 + getEGLErrorString(sEgl.eglGetError())); 516 } 517 518 // We can now initialize EGL for that display 519 int[] version = new int[2]; 520 if (!sEgl.eglInitialize(sEglDisplay, version)) { 521 throw new RuntimeException("eglInitialize failed " + 522 getEGLErrorString(sEgl.eglGetError())); 523 } 524 525 sEglConfig = chooseEglConfig(); 526 if (sEglConfig == null) { 527 // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without 528 if (sDirtyRegions) { 529 sDirtyRegions = false; 530 sEglConfig = chooseEglConfig(); 531 if (sEglConfig == null) { 532 throw new RuntimeException("eglConfig not initialized"); 533 } 534 } else { 535 throw new RuntimeException("eglConfig not initialized"); 536 } 537 } 538 } 539 } 540 541 mEglContext = sEglContextStorage.get(); 542 mEglThread = Thread.currentThread(); 543 544 if (mEglContext == null) { 545 mEglContext = createContext(sEgl, sEglDisplay, sEglConfig); 546 sEglContextStorage.set(mEglContext); 547 } 548 } 549 550 private EGLConfig chooseEglConfig() { 551 EGLConfig[] configs = new EGLConfig[1]; 552 int[] configsCount = new int[1]; 553 int[] configSpec = getConfig(sDirtyRegions); 554 555 // Debug 556 final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, ""); 557 if ("all".equalsIgnoreCase(debug)) { 558 sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount); 559 560 EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]]; 561 sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs, 562 configsCount[0], configsCount); 563 564 for (EGLConfig config : debugConfigs) { 565 printConfig(config); 566 } 567 } 568 569 if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) { 570 throw new IllegalArgumentException("eglChooseConfig failed " + 571 getEGLErrorString(sEgl.eglGetError())); 572 } else if (configsCount[0] > 0) { 573 if ("choice".equalsIgnoreCase(debug)) { 574 printConfig(configs[0]); 575 } 576 return configs[0]; 577 } 578 579 return null; 580 } 581 582 private void printConfig(EGLConfig config) { 583 int[] value = new int[1]; 584 585 Log.d(LOG_TAG, "EGL configuration " + config + ":"); 586 587 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value); 588 Log.d(LOG_TAG, " RED_SIZE = " + value[0]); 589 590 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value); 591 Log.d(LOG_TAG, " GREEN_SIZE = " + value[0]); 592 593 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value); 594 Log.d(LOG_TAG, " BLUE_SIZE = " + value[0]); 595 596 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value); 597 Log.d(LOG_TAG, " ALPHA_SIZE = " + value[0]); 598 599 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value); 600 Log.d(LOG_TAG, " DEPTH_SIZE = " + value[0]); 601 602 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value); 603 Log.d(LOG_TAG, " STENCIL_SIZE = " + value[0]); 604 605 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value); 606 Log.d(LOG_TAG, " SURFACE_TYPE = 0x" + Integer.toHexString(value[0])); 607 } 608 609 GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { 610 // Check preconditions. 611 if (sEgl == null) { 612 throw new RuntimeException("egl not initialized"); 613 } 614 if (sEglDisplay == null) { 615 throw new RuntimeException("eglDisplay not initialized"); 616 } 617 if (sEglConfig == null) { 618 throw new RuntimeException("eglConfig not initialized"); 619 } 620 if (Thread.currentThread() != mEglThread) { 621 throw new IllegalStateException("HardwareRenderer cannot be used " 622 + "from multiple threads"); 623 } 624 625 /* 626 * The window size has changed, so we need to create a new 627 * surface. 628 */ 629 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { 630 /* 631 * Unbind and destroy the old EGL surface, if 632 * there is one. 633 */ 634 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 635 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 636 } 637 638 // Create an EGL surface we can render into. 639 mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null); 640 641 if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) { 642 int error = sEgl.eglGetError(); 643 if (error == EGL_BAD_NATIVE_WINDOW) { 644 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 645 return null; 646 } 647 throw new RuntimeException("createWindowSurface failed " 648 + getEGLErrorString(error)); 649 } 650 651 /* 652 * Before we can issue GL commands, we need to make sure 653 * the context is current and bound to a surface. 654 */ 655 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 656 throw new Surface.OutOfResourcesException("eglMakeCurrent failed " 657 + getEGLErrorString(sEgl.eglGetError())); 658 } 659 660 // If mDirtyRegions is set, this means we have an EGL configuration 661 // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set 662 if (sDirtyRegions) { 663 if (!(mDirtyRegionsEnabled = GLES20Canvas.preserveBackBuffer())) { 664 Log.w(LOG_TAG, "Backbuffer cannot be preserved"); 665 } 666 } else if (sDirtyRegionsRequested) { 667 // If mDirtyRegions is not set, our EGL configuration does not 668 // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default 669 // swap behavior might be EGL_BUFFER_PRESERVED, which means we 670 // want to set mDirtyRegions. We try to do this only if dirty 671 // regions were initially requested as part of the device 672 // configuration (see RENDER_DIRTY_REGIONS) 673 mDirtyRegionsEnabled = GLES20Canvas.isBackBufferPreserved(); 674 } 675 676 return mEglContext.getGL(); 677 } 678 679 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { 680 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE }; 681 682 return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, 683 mGlVersion != 0 ? attrib_list : null); 684 } 685 686 @Override 687 void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, 688 SurfaceHolder holder) throws Surface.OutOfResourcesException { 689 if (isRequested()) { 690 checkEglErrors(); 691 super.initializeIfNeeded(width, height, attachInfo, holder); 692 } 693 } 694 695 @Override 696 void destroy(boolean full) { 697 if (full && mCanvas != null) { 698 mCanvas = null; 699 } 700 701 if (!isEnabled() || mDestroyed) return; 702 703 mDestroyed = true; 704 705 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 706 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 707 708 mEglSurface = null; 709 mGl = null; 710 711 setEnabled(false); 712 } 713 714 @Override 715 void invalidate() { 716 // Cancels any existing buffer to ensure we'll get a buffer 717 // of the right size before we call eglSwapBuffers 718 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, 719 EGL_NO_SURFACE, EGL_NO_CONTEXT); 720 } 721 722 @Override 723 boolean validate() { 724 return checkCurrent() != SURFACE_STATE_ERROR; 725 } 726 727 @Override 728 void setup(int width, int height) { 729 mCanvas.setViewport(width, height); 730 } 731 732 boolean canDraw() { 733 return mGl != null && mCanvas != null; 734 } 735 736 void onPreDraw(Rect dirty) { 737 } 738 739 void onPostDraw() { 740 } 741 742 @Override 743 void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, 744 Rect dirty) { 745 if (canDraw()) { 746 if (!hasDirtyRegions()) { 747 dirty = null; 748 } 749 attachInfo.mIgnoreDirtyState = true; 750 attachInfo.mDrawingTime = SystemClock.uptimeMillis(); 751 752 view.mPrivateFlags |= View.DRAWN; 753 754 final int surfaceState = checkCurrent(); 755 if (surfaceState != SURFACE_STATE_ERROR) { 756 // We had to change the current surface and/or context, redraw everything 757 if (surfaceState == SURFACE_STATE_UPDATED) { 758 dirty = null; 759 } 760 761 onPreDraw(dirty); 762 763 HardwareCanvas canvas = mCanvas; 764 attachInfo.mHardwareCanvas = canvas; 765 766 int saveCount = canvas.save(); 767 callbacks.onHardwarePreDraw(canvas); 768 769 try { 770 view.mRecreateDisplayList = 771 (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED; 772 view.mPrivateFlags &= ~View.INVALIDATED; 773 774 DisplayList displayList = view.getDisplayList(); 775 if (displayList != null) { 776 if (canvas.drawDisplayList(displayList, view.getWidth(), 777 view.getHeight(), mRedrawClip)) { 778 if (mRedrawClip.isEmpty() || view.getParent() == null) { 779 view.invalidate(); 780 } else { 781 view.getParent().invalidateChild(view, mRedrawClip); 782 } 783 mRedrawClip.setEmpty(); 784 } 785 } else { 786 // Shouldn't reach here 787 view.draw(canvas); 788 } 789 790 if (DEBUG_DIRTY_REGION) { 791 if (mDebugPaint == null) { 792 mDebugPaint = new Paint(); 793 mDebugPaint.setColor(0x7fff0000); 794 } 795 if (dirty != null && (mFrameCount++ & 1) == 0) { 796 canvas.drawRect(dirty, mDebugPaint); 797 } 798 } 799 } finally { 800 callbacks.onHardwarePostDraw(canvas); 801 canvas.restoreToCount(saveCount); 802 view.mRecreateDisplayList = false; 803 } 804 805 onPostDraw(); 806 807 attachInfo.mIgnoreDirtyState = false; 808 809 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); 810 checkEglErrors(); 811 } 812 } 813 } 814 815 /** 816 * Ensures the current EGL context is the one we expect. 817 * 818 * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current, 819 * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or 820 * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one 821 */ 822 private int checkCurrent() { 823 if (mEglThread != Thread.currentThread()) { 824 throw new IllegalStateException("Hardware acceleration can only be used with a " + 825 "single UI thread.\nOriginal thread: " + mEglThread + "\n" + 826 "Current thread: " + Thread.currentThread()); 827 } 828 829 if (!mEglContext.equals(sEgl.eglGetCurrentContext()) || 830 !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) { 831 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 832 fallback(true); 833 Log.e(LOG_TAG, "eglMakeCurrent failed " + 834 getEGLErrorString(sEgl.eglGetError())); 835 return SURFACE_STATE_ERROR; 836 } else { 837 return SURFACE_STATE_UPDATED; 838 } 839 } 840 return SURFACE_STATE_SUCCESS; 841 } 842 } 843 844 /** 845 * Hardware renderer using OpenGL ES 2.0. 846 */ 847 static class Gl20Renderer extends GlRenderer { 848 private GLES20Canvas mGlCanvas; 849 850 Gl20Renderer(boolean translucent) { 851 super(2, translucent); 852 } 853 854 @Override 855 GLES20Canvas createCanvas() { 856 return mGlCanvas = new GLES20Canvas(mTranslucent); 857 } 858 859 @Override 860 int[] getConfig(boolean dirtyRegions) { 861 return new int[] { 862 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 863 EGL_RED_SIZE, 8, 864 EGL_GREEN_SIZE, 8, 865 EGL_BLUE_SIZE, 8, 866 EGL_ALPHA_SIZE, 8, 867 EGL_DEPTH_SIZE, 0, 868 EGL_STENCIL_SIZE, 0, 869 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | 870 (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), 871 EGL_NONE 872 }; 873 } 874 875 @Override 876 boolean canDraw() { 877 return super.canDraw() && mGlCanvas != null; 878 } 879 880 @Override 881 void onPreDraw(Rect dirty) { 882 mGlCanvas.onPreDraw(dirty); 883 } 884 885 @Override 886 void onPostDraw() { 887 mGlCanvas.onPostDraw(); 888 } 889 890 @Override 891 void destroy(boolean full) { 892 try { 893 super.destroy(full); 894 } finally { 895 if (full && mGlCanvas != null) { 896 mGlCanvas = null; 897 } 898 } 899 } 900 901 @Override 902 void setup(int width, int height) { 903 super.setup(width, height); 904 if (mVsyncDisabled) { 905 GLES20Canvas.disableVsync(); 906 } 907 } 908 909 @Override 910 DisplayList createDisplayList() { 911 return new GLES20DisplayList(); 912 } 913 914 @Override 915 HardwareLayer createHardwareLayer(boolean isOpaque) { 916 return new GLES20TextureLayer(isOpaque); 917 } 918 919 @Override 920 HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { 921 return new GLES20RenderLayer(width, height, isOpaque); 922 } 923 924 @Override 925 SurfaceTexture createSurfaceTexture(HardwareLayer layer) { 926 return ((GLES20TextureLayer) layer).getSurfaceTexture(); 927 } 928 929 static HardwareRenderer create(boolean translucent) { 930 if (GLES20Canvas.isAvailable()) { 931 return new Gl20Renderer(translucent); 932 } 933 return null; 934 } 935 936 static void flushCaches(int level) { 937 switch (level) { 938 case ComponentCallbacks.TRIM_MEMORY_MODERATE: 939 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE); 940 break; 941 case ComponentCallbacks.TRIM_MEMORY_COMPLETE: 942 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL); 943 break; 944 } 945 } 946 } 947} 948