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