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