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