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