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