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