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