HardwareRenderer.java revision ba6be8a62dcdb3ffd210cd36b9af4e3a658eac47
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 * Detaches the specified functor from the current functor execution queue. 441 * 442 * @param functor The native functor to remove from the execution queue. 443 * 444 * @see HardwareCanvas#callDrawGLFunction(int) 445 * @see #attachFunctor(android.view.View.AttachInfo, int) 446 */ 447 abstract void detachFunctor(int functor); 448 449 /** 450 * Schedules the specified functor in the functors execution queue. 451 * 452 * @param attachInfo AttachInfo tied to this renderer. 453 * @param functor The native functor to insert in the execution queue. 454 * 455 * @see HardwareCanvas#callDrawGLFunction(int) 456 * @see #detachFunctor(int) 457 */ 458 abstract void attachFunctor(View.AttachInfo attachInfo, int functor); 459 460 /** 461 * Initializes the hardware renderer for the specified surface and setup the 462 * renderer for drawing, if needed. This is invoked when the ViewAncestor has 463 * potentially lost the hardware renderer. The hardware renderer should be 464 * reinitialized and setup when the render {@link #isRequested()} and 465 * {@link #isEnabled()}. 466 * 467 * @param width The width of the drawing surface. 468 * @param height The height of the drawing surface. 469 * @param holder The target surface 470 */ 471 void initializeIfNeeded(int width, int height, SurfaceHolder holder) 472 throws Surface.OutOfResourcesException { 473 if (isRequested()) { 474 // We lost the gl context, so recreate it. 475 if (!isEnabled()) { 476 if (initialize(holder)) { 477 setup(width, height); 478 } 479 } 480 } 481 } 482 483 /** 484 * Creates a hardware renderer using OpenGL. 485 * 486 * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.) 487 * @param translucent True if the surface is translucent, false otherwise 488 * 489 * @return A hardware renderer backed by OpenGL. 490 */ 491 static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) { 492 switch (glVersion) { 493 case 2: 494 return Gl20Renderer.create(translucent); 495 } 496 throw new IllegalArgumentException("Unknown GL version: " + glVersion); 497 } 498 499 /** 500 * Invoke this method when the system is running out of memory. This 501 * method will attempt to recover as much memory as possible, based on 502 * the specified hint. 503 * 504 * @param level Hint about the amount of memory that should be trimmed, 505 * see {@link android.content.ComponentCallbacks} 506 */ 507 static void trimMemory(int level) { 508 startTrimMemory(level); 509 endTrimMemory(); 510 } 511 512 /** 513 * Starts the process of trimming memory. Usually this call will setup 514 * hardware rendering context and reclaim memory.Extra cleanup might 515 * be required by calling {@link #endTrimMemory()}. 516 * 517 * @param level Hint about the amount of memory that should be trimmed, 518 * see {@link android.content.ComponentCallbacks} 519 */ 520 static void startTrimMemory(int level) { 521 Gl20Renderer.startTrimMemory(level); 522 } 523 524 /** 525 * Finishes the process of trimming memory. This method will usually 526 * cleanup special resources used by the memory trimming process. 527 */ 528 static void endTrimMemory() { 529 Gl20Renderer.endTrimMemory(); 530 } 531 532 /** 533 * Indicates whether hardware acceleration is currently enabled. 534 * 535 * @return True if hardware acceleration is in use, false otherwise. 536 */ 537 boolean isEnabled() { 538 return mEnabled; 539 } 540 541 /** 542 * Indicates whether hardware acceleration is currently enabled. 543 * 544 * @param enabled True if the hardware renderer is in use, false otherwise. 545 */ 546 void setEnabled(boolean enabled) { 547 mEnabled = enabled; 548 } 549 550 /** 551 * Indicates whether hardware acceleration is currently request but not 552 * necessarily enabled yet. 553 * 554 * @return True if requested, false otherwise. 555 */ 556 boolean isRequested() { 557 return mRequested; 558 } 559 560 /** 561 * Indicates whether hardware acceleration is currently requested but not 562 * necessarily enabled yet. 563 * 564 * @return True to request hardware acceleration, false otherwise. 565 */ 566 void setRequested(boolean requested) { 567 mRequested = requested; 568 } 569 570 @SuppressWarnings({"deprecation"}) 571 static abstract class GlRenderer extends HardwareRenderer { 572 // These values are not exposed in our EGL APIs 573 static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 574 static final int EGL_OPENGL_ES2_BIT = 4; 575 static final int EGL_SURFACE_TYPE = 0x3033; 576 static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400; 577 578 static final int SURFACE_STATE_ERROR = 0; 579 static final int SURFACE_STATE_SUCCESS = 1; 580 static final int SURFACE_STATE_UPDATED = 2; 581 582 static final int FUNCTOR_PROCESS_DELAY = 4; 583 584 static EGL10 sEgl; 585 static EGLDisplay sEglDisplay; 586 static EGLConfig sEglConfig; 587 static final Object[] sEglLock = new Object[0]; 588 int mWidth = -1, mHeight = -1; 589 590 static final ThreadLocal<ManagedEGLContext> sEglContextStorage 591 = new ThreadLocal<ManagedEGLContext>(); 592 593 EGLContext mEglContext; 594 Thread mEglThread; 595 596 EGLSurface mEglSurface; 597 598 GL mGl; 599 HardwareCanvas mCanvas; 600 601 long mFrameCount; 602 Paint mDebugPaint; 603 604 static boolean sDirtyRegions; 605 static final boolean sDirtyRegionsRequested; 606 static { 607 String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); 608 //noinspection PointlessBooleanExpression,ConstantConditions 609 sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); 610 sDirtyRegionsRequested = sDirtyRegions; 611 } 612 613 boolean mDirtyRegionsEnabled; 614 boolean mUpdateDirtyRegions; 615 616 final boolean mVsyncDisabled; 617 618 final boolean mProfileEnabled; 619 final float[] mProfileData; 620 int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT; 621 622 final boolean mDebugDirtyRegions; 623 624 final int mGlVersion; 625 final boolean mTranslucent; 626 627 private boolean mDestroyed; 628 629 private final Rect mRedrawClip = new Rect(); 630 631 private final int[] mSurfaceSize = new int[2]; 632 private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable(); 633 634 GlRenderer(int glVersion, boolean translucent) { 635 mGlVersion = glVersion; 636 mTranslucent = translucent; 637 638 String property; 639 640 property = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false"); 641 mVsyncDisabled = "true".equalsIgnoreCase(property); 642 if (mVsyncDisabled) { 643 Log.d(LOG_TAG, "Disabling v-sync"); 644 } 645 646 //noinspection PointlessBooleanExpression,ConstantConditions 647 if (!ViewDebug.DEBUG_LATENCY) { 648 property = SystemProperties.get(PROFILE_PROPERTY, "false"); 649 mProfileEnabled = "true".equalsIgnoreCase(property); 650 if (mProfileEnabled) { 651 Log.d(LOG_TAG, "Profiling hardware renderer"); 652 } 653 } else { 654 mProfileEnabled = true; 655 } 656 657 if (mProfileEnabled) { 658 property = SystemProperties.get(PROFILE_MAXFRAMES_PROPERTY, 659 Integer.toString(PROFILE_MAX_FRAMES)); 660 int maxProfileFrames = Integer.valueOf(property); 661 mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT]; 662 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) { 663 mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1; 664 } 665 } else { 666 mProfileData = null; 667 } 668 669 property = SystemProperties.get(DEBUG_DIRTY_REGIONS_PROPERTY, "false"); 670 mDebugDirtyRegions = "true".equalsIgnoreCase(property); 671 if (mDebugDirtyRegions) { 672 Log.d(LOG_TAG, "Debugging dirty regions"); 673 } 674 } 675 676 @Override 677 void dumpGfxInfo(PrintWriter pw) { 678 if (mProfileEnabled) { 679 pw.printf("\n\tDraw\tProcess\tExecute\n"); 680 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) { 681 if (mProfileData[i] < 0) { 682 break; 683 } 684 pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1], 685 mProfileData[i + 2]); 686 mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1; 687 } 688 mProfileCurrentFrame = mProfileData.length; 689 } 690 } 691 692 @Override 693 long getFrameCount() { 694 return mFrameCount; 695 } 696 697 /** 698 * Indicates whether this renderer instance can track and update dirty regions. 699 */ 700 boolean hasDirtyRegions() { 701 return mDirtyRegionsEnabled; 702 } 703 704 /** 705 * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)} 706 * is invoked and the requested flag is turned off. The error code is 707 * also logged as a warning. 708 */ 709 void checkEglErrors() { 710 if (isEnabled()) { 711 int error = sEgl.eglGetError(); 712 if (error != EGL_SUCCESS) { 713 // something bad has happened revert to 714 // normal rendering. 715 Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error)); 716 fallback(error != EGL11.EGL_CONTEXT_LOST); 717 } 718 } 719 } 720 721 private void fallback(boolean fallback) { 722 destroy(true); 723 if (fallback) { 724 // we'll try again if it was context lost 725 setRequested(false); 726 Log.w(LOG_TAG, "Mountain View, we've had a problem here. " 727 + "Switching back to software rendering."); 728 } 729 } 730 731 @Override 732 boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException { 733 if (isRequested() && !isEnabled()) { 734 initializeEgl(); 735 mGl = createEglSurface(holder); 736 mDestroyed = false; 737 738 if (mGl != null) { 739 int err = sEgl.eglGetError(); 740 if (err != EGL_SUCCESS) { 741 destroy(true); 742 setRequested(false); 743 } else { 744 if (mCanvas == null) { 745 mCanvas = createCanvas(); 746 } 747 if (mCanvas != null) { 748 setEnabled(true); 749 } else { 750 Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created"); 751 } 752 } 753 754 return mCanvas != null; 755 } 756 } 757 return false; 758 } 759 760 @Override 761 void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { 762 if (isRequested() && isEnabled()) { 763 createEglSurface(holder); 764 } 765 } 766 767 abstract HardwareCanvas createCanvas(); 768 769 abstract int[] getConfig(boolean dirtyRegions); 770 771 void initializeEgl() { 772 synchronized (sEglLock) { 773 if (sEgl == null && sEglConfig == null) { 774 sEgl = (EGL10) EGLContext.getEGL(); 775 776 // Get to the default display. 777 sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY); 778 779 if (sEglDisplay == EGL_NO_DISPLAY) { 780 throw new RuntimeException("eglGetDisplay failed " 781 + GLUtils.getEGLErrorString(sEgl.eglGetError())); 782 } 783 784 // We can now initialize EGL for that display 785 int[] version = new int[2]; 786 if (!sEgl.eglInitialize(sEglDisplay, version)) { 787 throw new RuntimeException("eglInitialize failed " + 788 GLUtils.getEGLErrorString(sEgl.eglGetError())); 789 } 790 791 sEglConfig = chooseEglConfig(); 792 if (sEglConfig == null) { 793 // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without 794 if (sDirtyRegions) { 795 sDirtyRegions = false; 796 sEglConfig = chooseEglConfig(); 797 if (sEglConfig == null) { 798 throw new RuntimeException("eglConfig not initialized"); 799 } 800 } else { 801 throw new RuntimeException("eglConfig not initialized"); 802 } 803 } 804 } 805 } 806 807 ManagedEGLContext managedContext = sEglContextStorage.get(); 808 mEglContext = managedContext != null ? managedContext.getContext() : null; 809 mEglThread = Thread.currentThread(); 810 811 if (mEglContext == null) { 812 mEglContext = createContext(sEgl, sEglDisplay, sEglConfig); 813 sEglContextStorage.set(createManagedContext(mEglContext)); 814 } 815 } 816 817 abstract ManagedEGLContext createManagedContext(EGLContext eglContext); 818 819 private EGLConfig chooseEglConfig() { 820 EGLConfig[] configs = new EGLConfig[1]; 821 int[] configsCount = new int[1]; 822 int[] configSpec = getConfig(sDirtyRegions); 823 824 // Debug 825 final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, ""); 826 if ("all".equalsIgnoreCase(debug)) { 827 sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount); 828 829 EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]]; 830 sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs, 831 configsCount[0], configsCount); 832 833 for (EGLConfig config : debugConfigs) { 834 printConfig(config); 835 } 836 } 837 838 if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) { 839 throw new IllegalArgumentException("eglChooseConfig failed " + 840 GLUtils.getEGLErrorString(sEgl.eglGetError())); 841 } else if (configsCount[0] > 0) { 842 if ("choice".equalsIgnoreCase(debug)) { 843 printConfig(configs[0]); 844 } 845 return configs[0]; 846 } 847 848 return null; 849 } 850 851 private static void printConfig(EGLConfig config) { 852 int[] value = new int[1]; 853 854 Log.d(LOG_TAG, "EGL configuration " + config + ":"); 855 856 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value); 857 Log.d(LOG_TAG, " RED_SIZE = " + value[0]); 858 859 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value); 860 Log.d(LOG_TAG, " GREEN_SIZE = " + value[0]); 861 862 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value); 863 Log.d(LOG_TAG, " BLUE_SIZE = " + value[0]); 864 865 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value); 866 Log.d(LOG_TAG, " ALPHA_SIZE = " + value[0]); 867 868 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value); 869 Log.d(LOG_TAG, " DEPTH_SIZE = " + value[0]); 870 871 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value); 872 Log.d(LOG_TAG, " STENCIL_SIZE = " + value[0]); 873 874 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value); 875 Log.d(LOG_TAG, " SURFACE_TYPE = 0x" + Integer.toHexString(value[0])); 876 } 877 878 GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { 879 // Check preconditions. 880 if (sEgl == null) { 881 throw new RuntimeException("egl not initialized"); 882 } 883 if (sEglDisplay == null) { 884 throw new RuntimeException("eglDisplay not initialized"); 885 } 886 if (sEglConfig == null) { 887 throw new RuntimeException("eglConfig not initialized"); 888 } 889 if (Thread.currentThread() != mEglThread) { 890 throw new IllegalStateException("HardwareRenderer cannot be used " 891 + "from multiple threads"); 892 } 893 894 // In case we need to destroy an existing surface 895 destroySurface(); 896 897 // Create an EGL surface we can render into. 898 if (!createSurface(holder)) { 899 return null; 900 } 901 902 /* 903 * Before we can issue GL commands, we need to make sure 904 * the context is current and bound to a surface. 905 */ 906 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 907 throw new Surface.OutOfResourcesException("eglMakeCurrent failed " 908 + GLUtils.getEGLErrorString(sEgl.eglGetError())); 909 } 910 911 initCaches(); 912 913 enableDirtyRegions(); 914 915 return mEglContext.getGL(); 916 } 917 918 private void enableDirtyRegions() { 919 // If mDirtyRegions is set, this means we have an EGL configuration 920 // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set 921 if (sDirtyRegions) { 922 if (!(mDirtyRegionsEnabled = preserveBackBuffer())) { 923 Log.w(LOG_TAG, "Backbuffer cannot be preserved"); 924 } 925 } else if (sDirtyRegionsRequested) { 926 // If mDirtyRegions is not set, our EGL configuration does not 927 // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default 928 // swap behavior might be EGL_BUFFER_PRESERVED, which means we 929 // want to set mDirtyRegions. We try to do this only if dirty 930 // regions were initially requested as part of the device 931 // configuration (see RENDER_DIRTY_REGIONS) 932 mDirtyRegionsEnabled = isBackBufferPreserved(); 933 } 934 } 935 936 abstract void initCaches(); 937 938 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { 939 int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE }; 940 941 return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, 942 mGlVersion != 0 ? attribs : null); 943 } 944 945 @Override 946 void destroy(boolean full) { 947 if (full && mCanvas != null) { 948 mCanvas = null; 949 } 950 951 if (!isEnabled() || mDestroyed) { 952 setEnabled(false); 953 return; 954 } 955 956 destroySurface(); 957 setEnabled(false); 958 959 mDestroyed = true; 960 mGl = null; 961 } 962 963 void destroySurface() { 964 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { 965 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 966 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 967 mEglSurface = null; 968 } 969 } 970 971 @Override 972 void invalidate(SurfaceHolder holder) { 973 // Cancels any existing buffer to ensure we'll get a buffer 974 // of the right size before we call eglSwapBuffers 975 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 976 977 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { 978 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 979 mEglSurface = null; 980 setEnabled(false); 981 } 982 983 if (holder.getSurface().isValid()) { 984 if (!createSurface(holder)) { 985 return; 986 } 987 988 mUpdateDirtyRegions = true; 989 990 if (mCanvas != null) { 991 setEnabled(true); 992 } 993 } 994 } 995 996 private boolean createSurface(SurfaceHolder holder) { 997 mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null); 998 999 if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) { 1000 int error = sEgl.eglGetError(); 1001 if (error == EGL_BAD_NATIVE_WINDOW) { 1002 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 1003 return false; 1004 } 1005 throw new RuntimeException("createWindowSurface failed " 1006 + GLUtils.getEGLErrorString(error)); 1007 } 1008 return true; 1009 } 1010 1011 @Override 1012 boolean validate() { 1013 return checkCurrent() != SURFACE_STATE_ERROR; 1014 } 1015 1016 @Override 1017 void setup(int width, int height) { 1018 if (validate()) { 1019 mCanvas.setViewport(width, height); 1020 mWidth = width; 1021 mHeight = height; 1022 } 1023 } 1024 1025 @Override 1026 int getWidth() { 1027 return mWidth; 1028 } 1029 1030 @Override 1031 int getHeight() { 1032 return mHeight; 1033 } 1034 1035 @Override 1036 HardwareCanvas getCanvas() { 1037 return mCanvas; 1038 } 1039 1040 boolean canDraw() { 1041 return mGl != null && mCanvas != null; 1042 } 1043 1044 void onPreDraw(Rect dirty) { 1045 1046 } 1047 1048 void onPostDraw() { 1049 } 1050 1051 class FunctorsRunnable implements Runnable { 1052 View.AttachInfo attachInfo; 1053 1054 @Override 1055 public void run() { 1056 final HardwareRenderer renderer = attachInfo.mHardwareRenderer; 1057 if (renderer == null || !renderer.isEnabled() || renderer != GlRenderer.this) { 1058 return; 1059 } 1060 1061 final int surfaceState = checkCurrent(); 1062 if (surfaceState != SURFACE_STATE_ERROR) { 1063 int status = mCanvas.invokeFunctors(mRedrawClip); 1064 handleFunctorStatus(attachInfo, status); 1065 } 1066 } 1067 } 1068 1069 @Override 1070 boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, 1071 Rect dirty) { 1072 if (canDraw()) { 1073 if (!hasDirtyRegions()) { 1074 dirty = null; 1075 } 1076 attachInfo.mIgnoreDirtyState = true; 1077 attachInfo.mDrawingTime = SystemClock.uptimeMillis(); 1078 1079 view.mPrivateFlags |= View.DRAWN; 1080 1081 final int surfaceState = checkCurrent(); 1082 if (surfaceState != SURFACE_STATE_ERROR) { 1083 HardwareCanvas canvas = mCanvas; 1084 attachInfo.mHardwareCanvas = canvas; 1085 1086 // We had to change the current surface and/or context, redraw everything 1087 if (surfaceState == SURFACE_STATE_UPDATED) { 1088 dirty = null; 1089 beginFrame(null); 1090 } else { 1091 int[] size = mSurfaceSize; 1092 beginFrame(size); 1093 1094 if (size[1] != mHeight || size[0] != mWidth) { 1095 mWidth = size[0]; 1096 mHeight = size[1]; 1097 1098 canvas.setViewport(mWidth, mHeight); 1099 1100 dirty = null; 1101 } 1102 } 1103 1104 onPreDraw(dirty); 1105 1106 int saveCount = canvas.save(); 1107 callbacks.onHardwarePreDraw(canvas); 1108 1109 try { 1110 view.mRecreateDisplayList = 1111 (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED; 1112 view.mPrivateFlags &= ~View.INVALIDATED; 1113 1114 long getDisplayListStartTime = 0; 1115 if (mProfileEnabled) { 1116 mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT; 1117 if (mProfileCurrentFrame >= mProfileData.length) { 1118 mProfileCurrentFrame = 0; 1119 } 1120 1121 getDisplayListStartTime = System.nanoTime(); 1122 } 1123 1124 DisplayList displayList = view.getDisplayList(); 1125 1126 if (mProfileEnabled) { 1127 long now = System.nanoTime(); 1128 float total = (now - getDisplayListStartTime) * 0.000001f; 1129 //noinspection PointlessArithmeticExpression 1130 mProfileData[mProfileCurrentFrame] = total; 1131 1132 if (ViewDebug.DEBUG_LATENCY) { 1133 Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- getDisplayList() took " + 1134 total + "ms"); 1135 } 1136 } 1137 1138 if (displayList != null) { 1139 long drawDisplayListStartTime = 0; 1140 if (mProfileEnabled) { 1141 drawDisplayListStartTime = System.nanoTime(); 1142 } 1143 1144 int status = canvas.drawDisplayList(displayList, mRedrawClip, 1145 DisplayList.FLAG_CLIP_CHILDREN); 1146 1147 if (mProfileEnabled) { 1148 long now = System.nanoTime(); 1149 float total = (now - drawDisplayListStartTime) * 0.000001f; 1150 mProfileData[mProfileCurrentFrame + 1] = total; 1151 1152 if (ViewDebug.DEBUG_LATENCY) { 1153 Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- drawDisplayList() took " + 1154 total + "ms, status=" + status); 1155 } 1156 } 1157 1158 handleFunctorStatus(attachInfo, status); 1159 } else { 1160 // Shouldn't reach here 1161 view.draw(canvas); 1162 } 1163 } finally { 1164 callbacks.onHardwarePostDraw(canvas); 1165 canvas.restoreToCount(saveCount); 1166 view.mRecreateDisplayList = false; 1167 1168 mFrameCount++; 1169 1170 if (mDebugDirtyRegions) { 1171 if (mDebugPaint == null) { 1172 mDebugPaint = new Paint(); 1173 mDebugPaint.setColor(0x7fff0000); 1174 } 1175 1176 if (dirty != null && (mFrameCount & 1) == 0) { 1177 canvas.drawRect(dirty, mDebugPaint); 1178 } 1179 } 1180 } 1181 1182 onPostDraw(); 1183 1184 attachInfo.mIgnoreDirtyState = false; 1185 1186 long eglSwapBuffersStartTime = 0; 1187 if (mProfileEnabled) { 1188 eglSwapBuffersStartTime = System.nanoTime(); 1189 } 1190 1191 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); 1192 1193 if (mProfileEnabled) { 1194 long now = System.nanoTime(); 1195 float total = (now - eglSwapBuffersStartTime) * 0.000001f; 1196 mProfileData[mProfileCurrentFrame + 2] = total; 1197 1198 if (ViewDebug.DEBUG_LATENCY) { 1199 Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- eglSwapBuffers() took " + 1200 total + "ms"); 1201 } 1202 } 1203 1204 checkEglErrors(); 1205 1206 return dirty == null; 1207 } 1208 } 1209 1210 return false; 1211 } 1212 1213 private void handleFunctorStatus(View.AttachInfo attachInfo, int status) { 1214 // If the draw flag is set, functors will be invoked while executing 1215 // the tree of display lists 1216 if ((status & DisplayList.STATUS_DRAW) != 0) { 1217 if (mRedrawClip.isEmpty()) { 1218 attachInfo.mViewRootImpl.invalidate(); 1219 } else { 1220 attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip); 1221 mRedrawClip.setEmpty(); 1222 } 1223 } 1224 1225 if ((status & DisplayList.STATUS_INVOKE) != 0) { 1226 scheduleFunctors(attachInfo); 1227 } 1228 } 1229 1230 private void scheduleFunctors(View.AttachInfo attachInfo) { 1231 mFunctorsRunnable.attachInfo = attachInfo; 1232 if (!attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) { 1233 // delay the functor callback by a few ms so it isn't polled constantly 1234 attachInfo.mHandler.postDelayed(mFunctorsRunnable, FUNCTOR_PROCESS_DELAY); 1235 } 1236 } 1237 1238 @Override 1239 void detachFunctor(int functor) { 1240 if (mCanvas != null) { 1241 mCanvas.detachFunctor(functor); 1242 } 1243 } 1244 1245 @Override 1246 void attachFunctor(View.AttachInfo attachInfo, int functor) { 1247 if (mCanvas != null) { 1248 mCanvas.attachFunctor(functor); 1249 scheduleFunctors(attachInfo); 1250 } 1251 } 1252 1253 /** 1254 * Ensures the current EGL context is the one we expect. 1255 * 1256 * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current, 1257 * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or 1258 * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one 1259 */ 1260 int checkCurrent() { 1261 if (mEglThread != Thread.currentThread()) { 1262 throw new IllegalStateException("Hardware acceleration can only be used with a " + 1263 "single UI thread.\nOriginal thread: " + mEglThread + "\n" + 1264 "Current thread: " + Thread.currentThread()); 1265 } 1266 1267 if (!mEglContext.equals(sEgl.eglGetCurrentContext()) || 1268 !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) { 1269 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 1270 Log.e(LOG_TAG, "eglMakeCurrent failed " + 1271 GLUtils.getEGLErrorString(sEgl.eglGetError())); 1272 fallback(true); 1273 return SURFACE_STATE_ERROR; 1274 } else { 1275 if (mUpdateDirtyRegions) { 1276 enableDirtyRegions(); 1277 mUpdateDirtyRegions = false; 1278 } 1279 return SURFACE_STATE_UPDATED; 1280 } 1281 } 1282 return SURFACE_STATE_SUCCESS; 1283 } 1284 } 1285 1286 /** 1287 * Hardware renderer using OpenGL ES 2.0. 1288 */ 1289 static class Gl20Renderer extends GlRenderer { 1290 private GLES20Canvas mGlCanvas; 1291 1292 private static EGLSurface sPbuffer; 1293 private static final Object[] sPbufferLock = new Object[0]; 1294 1295 static class Gl20RendererEglContext extends ManagedEGLContext { 1296 final Handler mHandler = new Handler(); 1297 1298 public Gl20RendererEglContext(EGLContext context) { 1299 super(context); 1300 } 1301 1302 @Override 1303 public void onTerminate(final EGLContext eglContext) { 1304 // Make sure we do this on the correct thread. 1305 if (mHandler.getLooper() != Looper.myLooper()) { 1306 mHandler.post(new Runnable() { 1307 @Override 1308 public void run() { 1309 onTerminate(eglContext); 1310 } 1311 }); 1312 return; 1313 } 1314 1315 synchronized (sEglLock) { 1316 if (sEgl == null) return; 1317 1318 if (EGLImpl.getInitCount(sEglDisplay) == 1) { 1319 usePbufferSurface(eglContext); 1320 GLES20Canvas.terminateCaches(); 1321 1322 sEgl.eglDestroyContext(sEglDisplay, eglContext); 1323 sEglContextStorage.set(null); 1324 sEglContextStorage.remove(); 1325 1326 sEgl.eglDestroySurface(sEglDisplay, sPbuffer); 1327 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, 1328 EGL_NO_SURFACE, EGL_NO_CONTEXT); 1329 1330 sEgl.eglReleaseThread(); 1331 sEgl.eglTerminate(sEglDisplay); 1332 1333 sEgl = null; 1334 sEglDisplay = null; 1335 sEglConfig = null; 1336 sPbuffer = null; 1337 } 1338 } 1339 } 1340 } 1341 1342 Gl20Renderer(boolean translucent) { 1343 super(2, translucent); 1344 } 1345 1346 @Override 1347 HardwareCanvas createCanvas() { 1348 return mGlCanvas = new GLES20Canvas(mTranslucent); 1349 } 1350 1351 @Override 1352 ManagedEGLContext createManagedContext(EGLContext eglContext) { 1353 return new Gl20Renderer.Gl20RendererEglContext(mEglContext); 1354 } 1355 1356 @Override 1357 int[] getConfig(boolean dirtyRegions) { 1358 return new int[] { 1359 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 1360 EGL_RED_SIZE, 8, 1361 EGL_GREEN_SIZE, 8, 1362 EGL_BLUE_SIZE, 8, 1363 EGL_ALPHA_SIZE, 8, 1364 EGL_DEPTH_SIZE, 0, 1365 EGL_STENCIL_SIZE, GLES20Canvas.getStencilSize(), 1366 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | 1367 (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), 1368 EGL_NONE 1369 }; 1370 } 1371 1372 @Override 1373 void initCaches() { 1374 GLES20Canvas.initCaches(); 1375 } 1376 1377 @Override 1378 boolean canDraw() { 1379 return super.canDraw() && mGlCanvas != null; 1380 } 1381 1382 @Override 1383 void onPreDraw(Rect dirty) { 1384 mGlCanvas.onPreDraw(dirty); 1385 } 1386 1387 @Override 1388 void onPostDraw() { 1389 mGlCanvas.onPostDraw(); 1390 } 1391 1392 @Override 1393 void destroy(boolean full) { 1394 try { 1395 super.destroy(full); 1396 } finally { 1397 if (full && mGlCanvas != null) { 1398 mGlCanvas = null; 1399 } 1400 } 1401 } 1402 1403 @Override 1404 void setup(int width, int height) { 1405 super.setup(width, height); 1406 if (mVsyncDisabled) { 1407 disableVsync(); 1408 } 1409 } 1410 1411 @Override 1412 public DisplayList createDisplayList(String name) { 1413 return new GLES20DisplayList(name); 1414 } 1415 1416 @Override 1417 HardwareLayer createHardwareLayer(boolean isOpaque) { 1418 return new GLES20TextureLayer(isOpaque); 1419 } 1420 1421 @Override 1422 HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { 1423 return new GLES20RenderLayer(width, height, isOpaque); 1424 } 1425 1426 @Override 1427 SurfaceTexture createSurfaceTexture(HardwareLayer layer) { 1428 return ((GLES20TextureLayer) layer).getSurfaceTexture(); 1429 } 1430 1431 @Override 1432 void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) { 1433 ((GLES20TextureLayer) layer).setSurfaceTexture(surfaceTexture); 1434 } 1435 1436 @Override 1437 void destroyLayers(View view) { 1438 if (view != null && isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) { 1439 destroyHardwareLayer(view); 1440 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); 1441 } 1442 } 1443 1444 private static void destroyHardwareLayer(View view) { 1445 view.destroyLayer(true); 1446 1447 if (view instanceof ViewGroup) { 1448 ViewGroup group = (ViewGroup) view; 1449 1450 int count = group.getChildCount(); 1451 for (int i = 0; i < count; i++) { 1452 destroyHardwareLayer(group.getChildAt(i)); 1453 } 1454 } 1455 } 1456 1457 @Override 1458 boolean safelyRun(Runnable action) { 1459 boolean needsContext = true; 1460 if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false; 1461 1462 if (needsContext) { 1463 Gl20RendererEglContext managedContext = 1464 (Gl20RendererEglContext) sEglContextStorage.get(); 1465 if (managedContext == null) return false; 1466 usePbufferSurface(managedContext.getContext()); 1467 } 1468 1469 try { 1470 action.run(); 1471 } finally { 1472 if (needsContext) { 1473 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, 1474 EGL_NO_SURFACE, EGL_NO_CONTEXT); 1475 } 1476 } 1477 1478 return true; 1479 } 1480 1481 @Override 1482 void destroyHardwareResources(final View view) { 1483 if (view != null) { 1484 safelyRun(new Runnable() { 1485 @Override 1486 public void run() { 1487 destroyResources(view); 1488 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); 1489 } 1490 }); 1491 } 1492 } 1493 1494 private static void destroyResources(View view) { 1495 view.destroyHardwareResources(); 1496 1497 if (view instanceof ViewGroup) { 1498 ViewGroup group = (ViewGroup) view; 1499 1500 int count = group.getChildCount(); 1501 for (int i = 0; i < count; i++) { 1502 destroyResources(group.getChildAt(i)); 1503 } 1504 } 1505 } 1506 1507 static HardwareRenderer create(boolean translucent) { 1508 if (GLES20Canvas.isAvailable()) { 1509 return new Gl20Renderer(translucent); 1510 } 1511 return null; 1512 } 1513 1514 static void startTrimMemory(int level) { 1515 if (sEgl == null || sEglConfig == null) return; 1516 1517 Gl20RendererEglContext managedContext = 1518 (Gl20RendererEglContext) sEglContextStorage.get(); 1519 // We do not have OpenGL objects 1520 if (managedContext == null) { 1521 return; 1522 } else { 1523 usePbufferSurface(managedContext.getContext()); 1524 } 1525 1526 if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { 1527 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL); 1528 } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { 1529 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE); 1530 } 1531 } 1532 1533 static void endTrimMemory() { 1534 if (sEgl != null && sEglDisplay != null) { 1535 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 1536 } 1537 } 1538 1539 private static void usePbufferSurface(EGLContext eglContext) { 1540 synchronized (sPbufferLock) { 1541 // Create a temporary 1x1 pbuffer so we have a context 1542 // to clear our OpenGL objects 1543 if (sPbuffer == null) { 1544 sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] { 1545 EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE 1546 }); 1547 } 1548 } 1549 sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext); 1550 } 1551 } 1552} 1553