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