HardwareRenderer.java revision a21f877434dd25a93869b8641e3c38618bf317a9
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.opengl.GLUtils; 25import android.opengl.ManagedEGLContext; 26import android.os.Handler; 27import android.os.Looper; 28import android.os.SystemClock; 29import android.os.SystemProperties; 30import android.util.Log; 31import com.google.android.gles_jni.EGLImpl; 32 33import javax.microedition.khronos.egl.EGL10; 34import javax.microedition.khronos.egl.EGL11; 35import javax.microedition.khronos.egl.EGLConfig; 36import javax.microedition.khronos.egl.EGLContext; 37import javax.microedition.khronos.egl.EGLDisplay; 38import javax.microedition.khronos.egl.EGLSurface; 39import javax.microedition.khronos.opengles.GL; 40 41import java.io.File; 42import java.io.PrintWriter; 43 44import static javax.microedition.khronos.egl.EGL10.*; 45 46/** 47 * Interface for rendering a ViewAncestor using hardware acceleration. 48 * 49 * @hide 50 */ 51public abstract class HardwareRenderer { 52 static final String LOG_TAG = "HardwareRenderer"; 53 54 /** 55 * Name of the file that holds the shaders cache. 56 */ 57 private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache"; 58 59 /** 60 * Turn on to only refresh the parts of the screen that need updating. 61 * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY} 62 * must also have the value "true". 63 */ 64 public static final boolean RENDER_DIRTY_REGIONS = true; 65 66 /** 67 * System property used to enable or disable dirty regions invalidation. 68 * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true. 69 * The default value of this property is assumed to be true. 70 * 71 * Possible values: 72 * "true", to enable partial invalidates 73 * "false", to disable partial invalidates 74 */ 75 static final String RENDER_DIRTY_REGIONS_PROPERTY = "debug.hwui.render_dirty_regions"; 76 77 /** 78 * System property used to enable or disable vsync. 79 * The default value of this property is assumed to be false. 80 * 81 * Possible values: 82 * "true", to disable vsync 83 * "false", to enable vsync 84 */ 85 static final String DISABLE_VSYNC_PROPERTY = "debug.hwui.disable_vsync"; 86 87 /** 88 * System property used to enable or disable hardware rendering profiling. 89 * The default value of this property is assumed to be false. 90 * 91 * When profiling is enabled, the adb shell dumpsys gfxinfo command will 92 * output extra information about the time taken to execute by the last 93 * frames. 94 * 95 * Possible values: 96 * "true", to enable profiling 97 * "false", to disable profiling 98 * 99 * @hide 100 */ 101 public static final String PROFILE_PROPERTY = "debug.hwui.profile"; 102 103 /** 104 * System property used to specify the number of frames to be used 105 * when doing hardware rendering profiling. 106 * The default value of this property is #PROFILE_MAX_FRAMES. 107 * 108 * When profiling is enabled, the adb shell dumpsys gfxinfo command will 109 * output extra information about the time taken to execute by the last 110 * frames. 111 * 112 * Possible values: 113 * "60", to set the limit of frames to 60 114 */ 115 static final String PROFILE_MAXFRAMES_PROPERTY = "debug.hwui.profile.maxframes"; 116 117 /** 118 * System property used to debug EGL configuration choice. 119 * 120 * Possible values: 121 * "choice", print the chosen configuration only 122 * "all", print all possible configurations 123 */ 124 static final String PRINT_CONFIG_PROPERTY = "debug.hwui.print_config"; 125 126 /** 127 * Turn on to draw dirty regions every other frame. 128 * 129 * Possible values: 130 * "true", to enable dirty regions debugging 131 * "false", to disable dirty regions debugging 132 * 133 * @hide 134 */ 135 public static final String DEBUG_DIRTY_REGIONS_PROPERTY = "debug.hwui.show_dirty_regions"; 136 137 /** 138 * A process can set this flag to false to prevent the use of hardware 139 * rendering. 140 * 141 * @hide 142 */ 143 public static boolean sRendererDisabled = false; 144 145 /** 146 * Further hardware renderer disabling for the system process. 147 * 148 * @hide 149 */ 150 public static boolean sSystemRendererDisabled = false; 151 152 /** 153 * Number of frames to profile. 154 */ 155 private static final int PROFILE_MAX_FRAMES = 128; 156 157 /** 158 * Number of floats per profiled frame. 159 */ 160 private static final int PROFILE_FRAME_DATA_COUNT = 3; 161 162 private boolean mEnabled; 163 private boolean mRequested = true; 164 165 /** 166 * Invoke this method to disable hardware rendering in the current process. 167 * 168 * @hide 169 */ 170 public static void disable(boolean system) { 171 sRendererDisabled = true; 172 if (system) { 173 sSystemRendererDisabled = true; 174 } 175 } 176 177 /** 178 * Indicates whether hardware acceleration is available under any form for 179 * the view hierarchy. 180 * 181 * @return True if the view hierarchy can potentially be hardware accelerated, 182 * false otherwise 183 */ 184 public static boolean isAvailable() { 185 return GLES20Canvas.isAvailable(); 186 } 187 188 /** 189 * Destroys the hardware rendering context. 190 * 191 * @param full If true, destroys all associated resources. 192 */ 193 abstract void destroy(boolean full); 194 195 /** 196 * Initializes the hardware renderer for the specified surface. 197 * 198 * @param holder The holder for the surface to hardware accelerate. 199 * 200 * @return True if the initialization was successful, false otherwise. 201 */ 202 abstract boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException; 203 204 /** 205 * Updates the hardware renderer for the specified surface. 206 * 207 * @param holder The holder for the surface to hardware accelerate 208 */ 209 abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException; 210 211 /** 212 * Destroys the layers used by the specified view hierarchy. 213 * 214 * @param view The root of the view hierarchy 215 */ 216 abstract void destroyLayers(View view); 217 218 /** 219 * Destroys all hardware rendering resources associated with the specified 220 * view hierarchy. 221 * 222 * @param view The root of the view hierarchy 223 */ 224 abstract void destroyHardwareResources(View view); 225 226 /** 227 * This method should be invoked whenever the current hardware renderer 228 * context should be reset. 229 * 230 * @param holder The holder for the surface to hardware accelerate 231 */ 232 abstract void invalidate(SurfaceHolder holder); 233 234 /** 235 * This method should be invoked to ensure the hardware renderer is in 236 * valid state (for instance, to ensure the correct EGL context is bound 237 * to the current thread.) 238 * 239 * @return true if the renderer is now valid, false otherwise 240 */ 241 abstract boolean validate(); 242 243 /** 244 * This method ensures the hardware renderer is in a valid state 245 * before executing the specified action. 246 * 247 * This method will attempt to set a valid state even if the window 248 * the renderer is attached to was destroyed. 249 * 250 * @return true if the action was run 251 */ 252 abstract boolean safelyRun(Runnable action); 253 254 /** 255 * Setup the hardware renderer for drawing. This is called whenever the 256 * size of the target surface changes or when the surface is first created. 257 * 258 * @param width Width of the drawing surface. 259 * @param height Height of the drawing surface. 260 */ 261 abstract void setup(int width, int height); 262 263 /** 264 * Gets the current width of the surface. This is the width that the surface 265 * was last set to in a call to {@link #setup(int, int)}. 266 * 267 * @return the current width of the surface 268 */ 269 abstract int getWidth(); 270 271 /** 272 * Gets the current height of the surface. This is the height that the surface 273 * was last set to in a call to {@link #setup(int, int)}. 274 * 275 * @return the current width of the surface 276 */ 277 abstract int getHeight(); 278 279 /** 280 * Gets the current canvas associated with this HardwareRenderer. 281 * 282 * @return the current HardwareCanvas 283 */ 284 abstract HardwareCanvas getCanvas(); 285 286 /** 287 * Outputs extra debugging information in the specified file descriptor. 288 * @param pw 289 */ 290 abstract void dumpGfxInfo(PrintWriter pw); 291 292 /** 293 * Outputs the total number of frames rendered (used for fps calculations) 294 * 295 * @return the number of frames rendered 296 */ 297 abstract long getFrameCount(); 298 299 /** 300 * Sets the directory to use as a persistent storage for hardware rendering 301 * resources. 302 * 303 * @param cacheDir A directory the current process can write to 304 */ 305 public static void setupDiskCache(File cacheDir) { 306 nSetupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath()); 307 } 308 309 private static native void nSetupShadersDiskCache(String cacheFile); 310 311 /** 312 * Notifies EGL that the frame is about to be rendered. 313 * @param size 314 */ 315 private static void beginFrame(int[] size) { 316 nBeginFrame(size); 317 } 318 319 private static native void nBeginFrame(int[] size); 320 321 /** 322 * Preserves the back buffer of the current surface after a buffer swap. 323 * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current 324 * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL 325 * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT. 326 * 327 * @return True if the swap behavior was successfully changed, 328 * false otherwise. 329 */ 330 static boolean preserveBackBuffer() { 331 return nPreserveBackBuffer(); 332 } 333 334 private static native boolean nPreserveBackBuffer(); 335 336 /** 337 * Indicates whether the current surface preserves its back buffer 338 * after a buffer swap. 339 * 340 * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED, 341 * false otherwise 342 */ 343 static boolean isBackBufferPreserved() { 344 return nIsBackBufferPreserved(); 345 } 346 347 private static native boolean nIsBackBufferPreserved(); 348 349 /** 350 * Disables v-sync. For performance testing only. 351 */ 352 static void disableVsync() { 353 nDisableVsync(); 354 } 355 356 private static native void nDisableVsync(); 357 358 /** 359 * Interface used to receive callbacks whenever a view is drawn by 360 * a hardware renderer instance. 361 */ 362 interface HardwareDrawCallbacks { 363 /** 364 * Invoked before a view is drawn by a hardware renderer. 365 * 366 * @param canvas The Canvas used to render the view. 367 */ 368 void onHardwarePreDraw(HardwareCanvas canvas); 369 370 /** 371 * Invoked after a view is drawn by a hardware renderer. 372 * 373 * @param canvas The Canvas used to render the view. 374 */ 375 void onHardwarePostDraw(HardwareCanvas canvas); 376 } 377 378 /** 379 * Draws the specified view. 380 * 381 * @param view The view to draw. 382 * @param attachInfo AttachInfo tied to the specified view. 383 * @param callbacks Callbacks invoked when drawing happens. 384 * @param dirty The dirty rectangle to update, can be null. 385 * 386 * @return true if the dirty rect was ignored, false otherwise 387 */ 388 abstract boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, 389 Rect dirty); 390 391 /** 392 * Creates a new display list that can be used to record batches of 393 * drawing operations. 394 * 395 * @param name The name of the display list, used for debugging purpose. 396 * May be null 397 * 398 * @return A new display list. 399 */ 400 public abstract DisplayList createDisplayList(String name); 401 402 /** 403 * Creates a new hardware layer. A hardware layer built by calling this 404 * method will be treated as a texture layer, instead of as a render target. 405 * 406 * @param isOpaque Whether the layer should be opaque or not 407 * 408 * @return A hardware layer 409 */ 410 abstract HardwareLayer createHardwareLayer(boolean isOpaque); 411 412 /** 413 * Creates a new hardware layer. 414 * 415 * @param width The minimum width of the layer 416 * @param height The minimum height of the layer 417 * @param isOpaque Whether the layer should be opaque or not 418 * 419 * @return A hardware layer 420 */ 421 abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque); 422 423 /** 424 * Creates a new {@link SurfaceTexture} that can be used to render into the 425 * specified hardware layer. 426 * 427 * 428 * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture} 429 * 430 * @return A {@link SurfaceTexture} 431 */ 432 abstract SurfaceTexture createSurfaceTexture(HardwareLayer layer); 433 434 /** 435 * Sets the {@link android.graphics.SurfaceTexture} that will be used to 436 * render into the specified hardware layer. 437 * 438 * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture} 439 * @param surfaceTexture The {@link android.graphics.SurfaceTexture} to use for the layer 440 */ 441 abstract void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture); 442 443 /** 444 * Detaches the specified functor from the current functor execution queue. 445 * 446 * @param functor The native functor to remove from the execution queue. 447 * 448 * @see HardwareCanvas#callDrawGLFunction(int) 449 * @see #attachFunctor(android.view.View.AttachInfo, int) 450 */ 451 abstract void detachFunctor(int functor); 452 453 /** 454 * Schedules the specified functor in the functors execution queue. 455 * 456 * @param attachInfo AttachInfo tied to this renderer. 457 * @param functor The native functor to insert in the execution queue. 458 * 459 * @see HardwareCanvas#callDrawGLFunction(int) 460 * @see #detachFunctor(int) 461 */ 462 abstract void attachFunctor(View.AttachInfo attachInfo, int functor); 463 464 /** 465 * Initializes the hardware renderer for the specified surface and setup the 466 * renderer for drawing, if needed. This is invoked when the ViewAncestor has 467 * potentially lost the hardware renderer. The hardware renderer should be 468 * reinitialized and setup when the render {@link #isRequested()} and 469 * {@link #isEnabled()}. 470 * 471 * @param width The width of the drawing surface. 472 * @param height The height of the drawing surface. 473 * @param holder The target surface 474 */ 475 void initializeIfNeeded(int width, int height, SurfaceHolder holder) 476 throws Surface.OutOfResourcesException { 477 if (isRequested()) { 478 // We lost the gl context, so recreate it. 479 if (!isEnabled()) { 480 if (initialize(holder)) { 481 setup(width, height); 482 } 483 } 484 } 485 } 486 487 /** 488 * Creates a hardware renderer using OpenGL. 489 * 490 * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.) 491 * @param translucent True if the surface is translucent, false otherwise 492 * 493 * @return A hardware renderer backed by OpenGL. 494 */ 495 static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) { 496 switch (glVersion) { 497 case 2: 498 return Gl20Renderer.create(translucent); 499 } 500 throw new IllegalArgumentException("Unknown GL version: " + glVersion); 501 } 502 503 /** 504 * Invoke this method when the system is running out of memory. This 505 * method will attempt to recover as much memory as possible, based on 506 * the specified hint. 507 * 508 * @param level Hint about the amount of memory that should be trimmed, 509 * see {@link android.content.ComponentCallbacks} 510 */ 511 static void trimMemory(int level) { 512 startTrimMemory(level); 513 endTrimMemory(); 514 } 515 516 /** 517 * Starts the process of trimming memory. Usually this call will setup 518 * hardware rendering context and reclaim memory.Extra cleanup might 519 * be required by calling {@link #endTrimMemory()}. 520 * 521 * @param level Hint about the amount of memory that should be trimmed, 522 * see {@link android.content.ComponentCallbacks} 523 */ 524 static void startTrimMemory(int level) { 525 Gl20Renderer.startTrimMemory(level); 526 } 527 528 /** 529 * Finishes the process of trimming memory. This method will usually 530 * cleanup special resources used by the memory trimming process. 531 */ 532 static void endTrimMemory() { 533 Gl20Renderer.endTrimMemory(); 534 } 535 536 /** 537 * Indicates whether hardware acceleration is currently enabled. 538 * 539 * @return True if hardware acceleration is in use, false otherwise. 540 */ 541 boolean isEnabled() { 542 return mEnabled; 543 } 544 545 /** 546 * Indicates whether hardware acceleration is currently enabled. 547 * 548 * @param enabled True if the hardware renderer is in use, false otherwise. 549 */ 550 void setEnabled(boolean enabled) { 551 mEnabled = enabled; 552 } 553 554 /** 555 * Indicates whether hardware acceleration is currently request but not 556 * necessarily enabled yet. 557 * 558 * @return True if requested, false otherwise. 559 */ 560 boolean isRequested() { 561 return mRequested; 562 } 563 564 /** 565 * Indicates whether hardware acceleration is currently requested but not 566 * necessarily enabled yet. 567 * 568 * @return True to request hardware acceleration, false otherwise. 569 */ 570 void setRequested(boolean requested) { 571 mRequested = requested; 572 } 573 574 @SuppressWarnings({"deprecation"}) 575 static abstract class GlRenderer extends HardwareRenderer { 576 // These values are not exposed in our EGL APIs 577 static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 578 static final int EGL_OPENGL_ES2_BIT = 4; 579 static final int EGL_SURFACE_TYPE = 0x3033; 580 static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400; 581 582 static final int SURFACE_STATE_ERROR = 0; 583 static final int SURFACE_STATE_SUCCESS = 1; 584 static final int SURFACE_STATE_UPDATED = 2; 585 586 static final int FUNCTOR_PROCESS_DELAY = 4; 587 588 static EGL10 sEgl; 589 static EGLDisplay sEglDisplay; 590 static EGLConfig sEglConfig; 591 static final Object[] sEglLock = new Object[0]; 592 int mWidth = -1, mHeight = -1; 593 594 static final ThreadLocal<ManagedEGLContext> sEglContextStorage 595 = new ThreadLocal<ManagedEGLContext>(); 596 597 EGLContext mEglContext; 598 Thread mEglThread; 599 600 EGLSurface mEglSurface; 601 602 GL mGl; 603 HardwareCanvas mCanvas; 604 605 long mFrameCount; 606 Paint mDebugPaint; 607 608 static boolean sDirtyRegions; 609 static final boolean sDirtyRegionsRequested; 610 static { 611 String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); 612 //noinspection PointlessBooleanExpression,ConstantConditions 613 sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); 614 sDirtyRegionsRequested = sDirtyRegions; 615 } 616 617 boolean mDirtyRegionsEnabled; 618 boolean mUpdateDirtyRegions; 619 620 final boolean mVsyncDisabled; 621 622 final boolean mProfileEnabled; 623 final float[] mProfileData; 624 int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT; 625 626 final boolean mDebugDirtyRegions; 627 628 final int mGlVersion; 629 final boolean mTranslucent; 630 631 private boolean mDestroyed; 632 633 private final Rect mRedrawClip = new Rect(); 634 635 private final int[] mSurfaceSize = new int[2]; 636 private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable(); 637 638 GlRenderer(int glVersion, boolean translucent) { 639 mGlVersion = glVersion; 640 mTranslucent = translucent; 641 642 String property; 643 644 property = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false"); 645 mVsyncDisabled = "true".equalsIgnoreCase(property); 646 if (mVsyncDisabled) { 647 Log.d(LOG_TAG, "Disabling v-sync"); 648 } 649 650 //noinspection PointlessBooleanExpression,ConstantConditions 651 if (!ViewDebug.DEBUG_LATENCY) { 652 property = SystemProperties.get(PROFILE_PROPERTY, "false"); 653 mProfileEnabled = "true".equalsIgnoreCase(property); 654 if (mProfileEnabled) { 655 Log.d(LOG_TAG, "Profiling hardware renderer"); 656 } 657 } else { 658 mProfileEnabled = true; 659 } 660 661 if (mProfileEnabled) { 662 property = SystemProperties.get(PROFILE_MAXFRAMES_PROPERTY, 663 Integer.toString(PROFILE_MAX_FRAMES)); 664 int maxProfileFrames = Integer.valueOf(property); 665 mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT]; 666 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) { 667 mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1; 668 } 669 } else { 670 mProfileData = null; 671 } 672 673 property = SystemProperties.get(DEBUG_DIRTY_REGIONS_PROPERTY, "false"); 674 mDebugDirtyRegions = "true".equalsIgnoreCase(property); 675 if (mDebugDirtyRegions) { 676 Log.d(LOG_TAG, "Debugging dirty regions"); 677 } 678 } 679 680 @Override 681 void dumpGfxInfo(PrintWriter pw) { 682 if (mProfileEnabled) { 683 pw.printf("\n\tDraw\tProcess\tExecute\n"); 684 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) { 685 if (mProfileData[i] < 0) { 686 break; 687 } 688 pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1], 689 mProfileData[i + 2]); 690 mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1; 691 } 692 mProfileCurrentFrame = mProfileData.length; 693 } 694 } 695 696 @Override 697 long getFrameCount() { 698 return mFrameCount; 699 } 700 701 /** 702 * Indicates whether this renderer instance can track and update dirty regions. 703 */ 704 boolean hasDirtyRegions() { 705 return mDirtyRegionsEnabled; 706 } 707 708 /** 709 * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)} 710 * is invoked and the requested flag is turned off. The error code is 711 * also logged as a warning. 712 */ 713 void checkEglErrors() { 714 if (isEnabled()) { 715 int error = sEgl.eglGetError(); 716 if (error != EGL_SUCCESS) { 717 // something bad has happened revert to 718 // normal rendering. 719 Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error)); 720 fallback(error != EGL11.EGL_CONTEXT_LOST); 721 } 722 } 723 } 724 725 private void fallback(boolean fallback) { 726 destroy(true); 727 if (fallback) { 728 // we'll try again if it was context lost 729 setRequested(false); 730 Log.w(LOG_TAG, "Mountain View, we've had a problem here. " 731 + "Switching back to software rendering."); 732 } 733 } 734 735 @Override 736 boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException { 737 if (isRequested() && !isEnabled()) { 738 initializeEgl(); 739 mGl = createEglSurface(holder); 740 mDestroyed = false; 741 742 if (mGl != null) { 743 int err = sEgl.eglGetError(); 744 if (err != EGL_SUCCESS) { 745 destroy(true); 746 setRequested(false); 747 } else { 748 if (mCanvas == null) { 749 mCanvas = createCanvas(); 750 } 751 if (mCanvas != null) { 752 setEnabled(true); 753 } else { 754 Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created"); 755 } 756 } 757 758 return mCanvas != null; 759 } 760 } 761 return false; 762 } 763 764 @Override 765 void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { 766 if (isRequested() && isEnabled()) { 767 createEglSurface(holder); 768 } 769 } 770 771 abstract HardwareCanvas createCanvas(); 772 773 abstract int[] getConfig(boolean dirtyRegions); 774 775 void initializeEgl() { 776 synchronized (sEglLock) { 777 if (sEgl == null && sEglConfig == null) { 778 sEgl = (EGL10) EGLContext.getEGL(); 779 780 // Get to the default display. 781 sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY); 782 783 if (sEglDisplay == EGL_NO_DISPLAY) { 784 throw new RuntimeException("eglGetDisplay failed " 785 + GLUtils.getEGLErrorString(sEgl.eglGetError())); 786 } 787 788 // We can now initialize EGL for that display 789 int[] version = new int[2]; 790 if (!sEgl.eglInitialize(sEglDisplay, version)) { 791 throw new RuntimeException("eglInitialize failed " + 792 GLUtils.getEGLErrorString(sEgl.eglGetError())); 793 } 794 795 sEglConfig = chooseEglConfig(); 796 if (sEglConfig == null) { 797 // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without 798 if (sDirtyRegions) { 799 sDirtyRegions = false; 800 sEglConfig = chooseEglConfig(); 801 if (sEglConfig == null) { 802 throw new RuntimeException("eglConfig not initialized"); 803 } 804 } else { 805 throw new RuntimeException("eglConfig not initialized"); 806 } 807 } 808 } 809 } 810 811 ManagedEGLContext managedContext = sEglContextStorage.get(); 812 mEglContext = managedContext != null ? managedContext.getContext() : null; 813 mEglThread = Thread.currentThread(); 814 815 if (mEglContext == null) { 816 mEglContext = createContext(sEgl, sEglDisplay, sEglConfig); 817 sEglContextStorage.set(createManagedContext(mEglContext)); 818 } 819 } 820 821 abstract ManagedEGLContext createManagedContext(EGLContext eglContext); 822 823 private EGLConfig chooseEglConfig() { 824 EGLConfig[] configs = new EGLConfig[1]; 825 int[] configsCount = new int[1]; 826 int[] configSpec = getConfig(sDirtyRegions); 827 828 // Debug 829 final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, ""); 830 if ("all".equalsIgnoreCase(debug)) { 831 sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount); 832 833 EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]]; 834 sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs, 835 configsCount[0], configsCount); 836 837 for (EGLConfig config : debugConfigs) { 838 printConfig(config); 839 } 840 } 841 842 if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) { 843 throw new IllegalArgumentException("eglChooseConfig failed " + 844 GLUtils.getEGLErrorString(sEgl.eglGetError())); 845 } else if (configsCount[0] > 0) { 846 if ("choice".equalsIgnoreCase(debug)) { 847 printConfig(configs[0]); 848 } 849 return configs[0]; 850 } 851 852 return null; 853 } 854 855 private static void printConfig(EGLConfig config) { 856 int[] value = new int[1]; 857 858 Log.d(LOG_TAG, "EGL configuration " + config + ":"); 859 860 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value); 861 Log.d(LOG_TAG, " RED_SIZE = " + value[0]); 862 863 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value); 864 Log.d(LOG_TAG, " GREEN_SIZE = " + value[0]); 865 866 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value); 867 Log.d(LOG_TAG, " BLUE_SIZE = " + value[0]); 868 869 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value); 870 Log.d(LOG_TAG, " ALPHA_SIZE = " + value[0]); 871 872 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value); 873 Log.d(LOG_TAG, " DEPTH_SIZE = " + value[0]); 874 875 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value); 876 Log.d(LOG_TAG, " STENCIL_SIZE = " + value[0]); 877 878 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value); 879 Log.d(LOG_TAG, " SURFACE_TYPE = 0x" + Integer.toHexString(value[0])); 880 } 881 882 GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { 883 // Check preconditions. 884 if (sEgl == null) { 885 throw new RuntimeException("egl not initialized"); 886 } 887 if (sEglDisplay == null) { 888 throw new RuntimeException("eglDisplay not initialized"); 889 } 890 if (sEglConfig == null) { 891 throw new RuntimeException("eglConfig not initialized"); 892 } 893 if (Thread.currentThread() != mEglThread) { 894 throw new IllegalStateException("HardwareRenderer cannot be used " 895 + "from multiple threads"); 896 } 897 898 // In case we need to destroy an existing surface 899 destroySurface(); 900 901 // Create an EGL surface we can render into. 902 if (!createSurface(holder)) { 903 return null; 904 } 905 906 /* 907 * Before we can issue GL commands, we need to make sure 908 * the context is current and bound to a surface. 909 */ 910 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 911 throw new Surface.OutOfResourcesException("eglMakeCurrent failed " 912 + GLUtils.getEGLErrorString(sEgl.eglGetError())); 913 } 914 915 initCaches(); 916 917 enableDirtyRegions(); 918 919 return mEglContext.getGL(); 920 } 921 922 private void enableDirtyRegions() { 923 // If mDirtyRegions is set, this means we have an EGL configuration 924 // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set 925 if (sDirtyRegions) { 926 if (!(mDirtyRegionsEnabled = preserveBackBuffer())) { 927 Log.w(LOG_TAG, "Backbuffer cannot be preserved"); 928 } 929 } else if (sDirtyRegionsRequested) { 930 // If mDirtyRegions is not set, our EGL configuration does not 931 // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default 932 // swap behavior might be EGL_BUFFER_PRESERVED, which means we 933 // want to set mDirtyRegions. We try to do this only if dirty 934 // regions were initially requested as part of the device 935 // configuration (see RENDER_DIRTY_REGIONS) 936 mDirtyRegionsEnabled = isBackBufferPreserved(); 937 } 938 } 939 940 abstract void initCaches(); 941 942 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { 943 int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE }; 944 945 return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, 946 mGlVersion != 0 ? attribs : null); 947 } 948 949 @Override 950 void destroy(boolean full) { 951 if (full && mCanvas != null) { 952 mCanvas = null; 953 } 954 955 if (!isEnabled() || mDestroyed) { 956 setEnabled(false); 957 return; 958 } 959 960 destroySurface(); 961 setEnabled(false); 962 963 mDestroyed = true; 964 mGl = null; 965 } 966 967 void destroySurface() { 968 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { 969 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 970 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 971 mEglSurface = null; 972 } 973 } 974 975 @Override 976 void invalidate(SurfaceHolder holder) { 977 // Cancels any existing buffer to ensure we'll get a buffer 978 // of the right size before we call eglSwapBuffers 979 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 980 981 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { 982 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 983 mEglSurface = null; 984 setEnabled(false); 985 } 986 987 if (holder.getSurface().isValid()) { 988 if (!createSurface(holder)) { 989 return; 990 } 991 992 mUpdateDirtyRegions = true; 993 994 if (mCanvas != null) { 995 setEnabled(true); 996 } 997 } 998 } 999 1000 private boolean createSurface(SurfaceHolder holder) { 1001 mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null); 1002 1003 if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) { 1004 int error = sEgl.eglGetError(); 1005 if (error == EGL_BAD_NATIVE_WINDOW) { 1006 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 1007 return false; 1008 } 1009 throw new RuntimeException("createWindowSurface failed " 1010 + GLUtils.getEGLErrorString(error)); 1011 } 1012 return true; 1013 } 1014 1015 @Override 1016 boolean validate() { 1017 return checkCurrent() != SURFACE_STATE_ERROR; 1018 } 1019 1020 @Override 1021 void setup(int width, int height) { 1022 if (validate()) { 1023 mCanvas.setViewport(width, height); 1024 mWidth = width; 1025 mHeight = height; 1026 } 1027 } 1028 1029 @Override 1030 int getWidth() { 1031 return mWidth; 1032 } 1033 1034 @Override 1035 int getHeight() { 1036 return mHeight; 1037 } 1038 1039 @Override 1040 HardwareCanvas getCanvas() { 1041 return mCanvas; 1042 } 1043 1044 boolean canDraw() { 1045 return mGl != null && mCanvas != null; 1046 } 1047 1048 void onPreDraw(Rect dirty) { 1049 1050 } 1051 1052 void onPostDraw() { 1053 } 1054 1055 class FunctorsRunnable implements Runnable { 1056 View.AttachInfo attachInfo; 1057 1058 @Override 1059 public void run() { 1060 final HardwareRenderer renderer = attachInfo.mHardwareRenderer; 1061 if (renderer == null || !renderer.isEnabled() || renderer != GlRenderer.this) { 1062 return; 1063 } 1064 1065 final int surfaceState = checkCurrent(); 1066 if (surfaceState != SURFACE_STATE_ERROR) { 1067 int status = mCanvas.invokeFunctors(mRedrawClip); 1068 handleFunctorStatus(attachInfo, status); 1069 } 1070 } 1071 } 1072 1073 @Override 1074 boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, 1075 Rect dirty) { 1076 if (canDraw()) { 1077 if (!hasDirtyRegions()) { 1078 dirty = null; 1079 } 1080 attachInfo.mIgnoreDirtyState = true; 1081 attachInfo.mDrawingTime = SystemClock.uptimeMillis(); 1082 1083 view.mPrivateFlags |= View.DRAWN; 1084 1085 final int surfaceState = checkCurrent(); 1086 if (surfaceState != SURFACE_STATE_ERROR) { 1087 HardwareCanvas canvas = mCanvas; 1088 attachInfo.mHardwareCanvas = canvas; 1089 1090 // We had to change the current surface and/or context, redraw everything 1091 if (surfaceState == SURFACE_STATE_UPDATED) { 1092 dirty = null; 1093 beginFrame(null); 1094 } else { 1095 int[] size = mSurfaceSize; 1096 beginFrame(size); 1097 1098 if (size[1] != mHeight || size[0] != mWidth) { 1099 mWidth = size[0]; 1100 mHeight = size[1]; 1101 1102 canvas.setViewport(mWidth, mHeight); 1103 1104 dirty = null; 1105 } 1106 } 1107 1108 onPreDraw(dirty); 1109 1110 int saveCount = canvas.save(); 1111 callbacks.onHardwarePreDraw(canvas); 1112 1113 try { 1114 view.mRecreateDisplayList = 1115 (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED; 1116 view.mPrivateFlags &= ~View.INVALIDATED; 1117 1118 long getDisplayListStartTime = 0; 1119 if (mProfileEnabled) { 1120 mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT; 1121 if (mProfileCurrentFrame >= mProfileData.length) { 1122 mProfileCurrentFrame = 0; 1123 } 1124 1125 getDisplayListStartTime = System.nanoTime(); 1126 } 1127 1128 DisplayList displayList = view.getDisplayList(); 1129 1130 if (mProfileEnabled) { 1131 long now = System.nanoTime(); 1132 float total = (now - getDisplayListStartTime) * 0.000001f; 1133 //noinspection PointlessArithmeticExpression 1134 mProfileData[mProfileCurrentFrame] = total; 1135 1136 if (ViewDebug.DEBUG_LATENCY) { 1137 Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- getDisplayList() took " + 1138 total + "ms"); 1139 } 1140 } 1141 1142 if (displayList != null) { 1143 long drawDisplayListStartTime = 0; 1144 if (mProfileEnabled) { 1145 drawDisplayListStartTime = System.nanoTime(); 1146 } 1147 1148 int status = canvas.drawDisplayList(displayList, mRedrawClip, 1149 DisplayList.FLAG_CLIP_CHILDREN); 1150 1151 if (mProfileEnabled) { 1152 long now = System.nanoTime(); 1153 float total = (now - drawDisplayListStartTime) * 0.000001f; 1154 mProfileData[mProfileCurrentFrame + 1] = total; 1155 1156 if (ViewDebug.DEBUG_LATENCY) { 1157 Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- drawDisplayList() took " + 1158 total + "ms, status=" + status); 1159 } 1160 } 1161 1162 handleFunctorStatus(attachInfo, status); 1163 } else { 1164 // Shouldn't reach here 1165 view.draw(canvas); 1166 } 1167 } finally { 1168 callbacks.onHardwarePostDraw(canvas); 1169 canvas.restoreToCount(saveCount); 1170 view.mRecreateDisplayList = false; 1171 1172 mFrameCount++; 1173 1174 if (mDebugDirtyRegions) { 1175 if (mDebugPaint == null) { 1176 mDebugPaint = new Paint(); 1177 mDebugPaint.setColor(0x7fff0000); 1178 } 1179 1180 if (dirty != null && (mFrameCount & 1) == 0) { 1181 canvas.drawRect(dirty, mDebugPaint); 1182 } 1183 } 1184 } 1185 1186 onPostDraw(); 1187 1188 attachInfo.mIgnoreDirtyState = false; 1189 1190 long eglSwapBuffersStartTime = 0; 1191 if (mProfileEnabled) { 1192 eglSwapBuffersStartTime = System.nanoTime(); 1193 } 1194 1195 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); 1196 1197 if (mProfileEnabled) { 1198 long now = System.nanoTime(); 1199 float total = (now - eglSwapBuffersStartTime) * 0.000001f; 1200 mProfileData[mProfileCurrentFrame + 2] = total; 1201 1202 if (ViewDebug.DEBUG_LATENCY) { 1203 Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- eglSwapBuffers() took " + 1204 total + "ms"); 1205 } 1206 } 1207 1208 checkEglErrors(); 1209 1210 return dirty == null; 1211 } 1212 } 1213 1214 return false; 1215 } 1216 1217 private void handleFunctorStatus(View.AttachInfo attachInfo, int status) { 1218 // If the draw flag is set, functors will be invoked while executing 1219 // the tree of display lists 1220 if ((status & DisplayList.STATUS_DRAW) != 0) { 1221 if (mRedrawClip.isEmpty()) { 1222 attachInfo.mViewRootImpl.invalidate(); 1223 } else { 1224 attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip); 1225 mRedrawClip.setEmpty(); 1226 } 1227 } 1228 1229 if ((status & DisplayList.STATUS_INVOKE) != 0) { 1230 scheduleFunctors(attachInfo); 1231 } 1232 } 1233 1234 private void scheduleFunctors(View.AttachInfo attachInfo) { 1235 mFunctorsRunnable.attachInfo = attachInfo; 1236 if (!attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) { 1237 // delay the functor callback by a few ms so it isn't polled constantly 1238 attachInfo.mHandler.postDelayed(mFunctorsRunnable, FUNCTOR_PROCESS_DELAY); 1239 } 1240 } 1241 1242 @Override 1243 void detachFunctor(int functor) { 1244 if (mCanvas != null) { 1245 mCanvas.detachFunctor(functor); 1246 } 1247 } 1248 1249 @Override 1250 void attachFunctor(View.AttachInfo attachInfo, int functor) { 1251 if (mCanvas != null) { 1252 mCanvas.attachFunctor(functor); 1253 scheduleFunctors(attachInfo); 1254 } 1255 } 1256 1257 /** 1258 * Ensures the current EGL context is the one we expect. 1259 * 1260 * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current, 1261 * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or 1262 * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one 1263 */ 1264 int checkCurrent() { 1265 if (mEglThread != Thread.currentThread()) { 1266 throw new IllegalStateException("Hardware acceleration can only be used with a " + 1267 "single UI thread.\nOriginal thread: " + mEglThread + "\n" + 1268 "Current thread: " + Thread.currentThread()); 1269 } 1270 1271 if (!mEglContext.equals(sEgl.eglGetCurrentContext()) || 1272 !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) { 1273 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 1274 Log.e(LOG_TAG, "eglMakeCurrent failed " + 1275 GLUtils.getEGLErrorString(sEgl.eglGetError())); 1276 fallback(true); 1277 return SURFACE_STATE_ERROR; 1278 } else { 1279 if (mUpdateDirtyRegions) { 1280 enableDirtyRegions(); 1281 mUpdateDirtyRegions = false; 1282 } 1283 return SURFACE_STATE_UPDATED; 1284 } 1285 } 1286 return SURFACE_STATE_SUCCESS; 1287 } 1288 } 1289 1290 /** 1291 * Hardware renderer using OpenGL ES 2.0. 1292 */ 1293 static class Gl20Renderer extends GlRenderer { 1294 private GLES20Canvas mGlCanvas; 1295 1296 private static EGLSurface sPbuffer; 1297 private static final Object[] sPbufferLock = new Object[0]; 1298 1299 static class Gl20RendererEglContext extends ManagedEGLContext { 1300 final Handler mHandler = new Handler(); 1301 1302 public Gl20RendererEglContext(EGLContext context) { 1303 super(context); 1304 } 1305 1306 @Override 1307 public void onTerminate(final EGLContext eglContext) { 1308 // Make sure we do this on the correct thread. 1309 if (mHandler.getLooper() != Looper.myLooper()) { 1310 mHandler.post(new Runnable() { 1311 @Override 1312 public void run() { 1313 onTerminate(eglContext); 1314 } 1315 }); 1316 return; 1317 } 1318 1319 synchronized (sEglLock) { 1320 if (sEgl == null) return; 1321 1322 if (EGLImpl.getInitCount(sEglDisplay) == 1) { 1323 usePbufferSurface(eglContext); 1324 GLES20Canvas.terminateCaches(); 1325 1326 sEgl.eglDestroyContext(sEglDisplay, eglContext); 1327 sEglContextStorage.set(null); 1328 sEglContextStorage.remove(); 1329 1330 sEgl.eglDestroySurface(sEglDisplay, sPbuffer); 1331 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, 1332 EGL_NO_SURFACE, EGL_NO_CONTEXT); 1333 1334 sEgl.eglReleaseThread(); 1335 sEgl.eglTerminate(sEglDisplay); 1336 1337 sEgl = null; 1338 sEglDisplay = null; 1339 sEglConfig = null; 1340 sPbuffer = null; 1341 } 1342 } 1343 } 1344 } 1345 1346 Gl20Renderer(boolean translucent) { 1347 super(2, translucent); 1348 } 1349 1350 @Override 1351 HardwareCanvas createCanvas() { 1352 return mGlCanvas = new GLES20Canvas(mTranslucent); 1353 } 1354 1355 @Override 1356 ManagedEGLContext createManagedContext(EGLContext eglContext) { 1357 return new Gl20Renderer.Gl20RendererEglContext(mEglContext); 1358 } 1359 1360 @Override 1361 int[] getConfig(boolean dirtyRegions) { 1362 return new int[] { 1363 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 1364 EGL_RED_SIZE, 8, 1365 EGL_GREEN_SIZE, 8, 1366 EGL_BLUE_SIZE, 8, 1367 EGL_ALPHA_SIZE, 8, 1368 EGL_DEPTH_SIZE, 0, 1369 EGL_STENCIL_SIZE, GLES20Canvas.getStencilSize(), 1370 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | 1371 (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), 1372 EGL_NONE 1373 }; 1374 } 1375 1376 @Override 1377 void initCaches() { 1378 GLES20Canvas.initCaches(); 1379 } 1380 1381 @Override 1382 boolean canDraw() { 1383 return super.canDraw() && mGlCanvas != null; 1384 } 1385 1386 @Override 1387 void onPreDraw(Rect dirty) { 1388 mGlCanvas.onPreDraw(dirty); 1389 } 1390 1391 @Override 1392 void onPostDraw() { 1393 mGlCanvas.onPostDraw(); 1394 } 1395 1396 @Override 1397 void destroy(boolean full) { 1398 try { 1399 super.destroy(full); 1400 } finally { 1401 if (full && mGlCanvas != null) { 1402 mGlCanvas = null; 1403 } 1404 } 1405 } 1406 1407 @Override 1408 void setup(int width, int height) { 1409 super.setup(width, height); 1410 if (mVsyncDisabled) { 1411 disableVsync(); 1412 } 1413 } 1414 1415 @Override 1416 public DisplayList createDisplayList(String name) { 1417 return new GLES20DisplayList(name); 1418 } 1419 1420 @Override 1421 HardwareLayer createHardwareLayer(boolean isOpaque) { 1422 return new GLES20TextureLayer(isOpaque); 1423 } 1424 1425 @Override 1426 HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { 1427 return new GLES20RenderLayer(width, height, isOpaque); 1428 } 1429 1430 @Override 1431 SurfaceTexture createSurfaceTexture(HardwareLayer layer) { 1432 return ((GLES20TextureLayer) layer).getSurfaceTexture(); 1433 } 1434 1435 @Override 1436 void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) { 1437 ((GLES20TextureLayer) layer).setSurfaceTexture(surfaceTexture); 1438 } 1439 1440 @Override 1441 void destroyLayers(View view) { 1442 if (view != null && isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) { 1443 destroyHardwareLayer(view); 1444 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); 1445 } 1446 } 1447 1448 private static void destroyHardwareLayer(View view) { 1449 view.destroyLayer(true); 1450 1451 if (view instanceof ViewGroup) { 1452 ViewGroup group = (ViewGroup) view; 1453 1454 int count = group.getChildCount(); 1455 for (int i = 0; i < count; i++) { 1456 destroyHardwareLayer(group.getChildAt(i)); 1457 } 1458 } 1459 } 1460 1461 @Override 1462 boolean safelyRun(Runnable action) { 1463 boolean needsContext = true; 1464 if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false; 1465 1466 if (needsContext) { 1467 Gl20RendererEglContext managedContext = 1468 (Gl20RendererEglContext) sEglContextStorage.get(); 1469 if (managedContext == null) return false; 1470 usePbufferSurface(managedContext.getContext()); 1471 } 1472 1473 try { 1474 action.run(); 1475 } finally { 1476 if (needsContext) { 1477 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, 1478 EGL_NO_SURFACE, EGL_NO_CONTEXT); 1479 } 1480 } 1481 1482 return true; 1483 } 1484 1485 @Override 1486 void destroyHardwareResources(final View view) { 1487 if (view != null) { 1488 safelyRun(new Runnable() { 1489 @Override 1490 public void run() { 1491 destroyResources(view); 1492 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); 1493 } 1494 }); 1495 } 1496 } 1497 1498 private static void destroyResources(View view) { 1499 view.destroyHardwareResources(); 1500 1501 if (view instanceof ViewGroup) { 1502 ViewGroup group = (ViewGroup) view; 1503 1504 int count = group.getChildCount(); 1505 for (int i = 0; i < count; i++) { 1506 destroyResources(group.getChildAt(i)); 1507 } 1508 } 1509 } 1510 1511 static HardwareRenderer create(boolean translucent) { 1512 if (GLES20Canvas.isAvailable()) { 1513 return new Gl20Renderer(translucent); 1514 } 1515 return null; 1516 } 1517 1518 static void startTrimMemory(int level) { 1519 if (sEgl == null || sEglConfig == null) return; 1520 1521 Gl20RendererEglContext managedContext = 1522 (Gl20RendererEglContext) sEglContextStorage.get(); 1523 // We do not have OpenGL objects 1524 if (managedContext == null) { 1525 return; 1526 } else { 1527 usePbufferSurface(managedContext.getContext()); 1528 } 1529 1530 if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { 1531 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL); 1532 } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { 1533 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE); 1534 } 1535 } 1536 1537 static void endTrimMemory() { 1538 if (sEgl != null && sEglDisplay != null) { 1539 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 1540 } 1541 } 1542 1543 private static void usePbufferSurface(EGLContext eglContext) { 1544 synchronized (sPbufferLock) { 1545 // Create a temporary 1x1 pbuffer so we have a context 1546 // to clear our OpenGL objects 1547 if (sPbuffer == null) { 1548 sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] { 1549 EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE 1550 }); 1551 } 1552 } 1553 sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext); 1554 } 1555 } 1556} 1557