HardwareRenderer.java revision e6c966caa3aff3099e6fb00caefa10387f57b9c3
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 EGL10 sEgl; 507 static EGLDisplay sEglDisplay; 508 static EGLConfig sEglConfig; 509 static final Object[] sEglLock = new Object[0]; 510 int mWidth = -1, mHeight = -1; 511 512 static final ThreadLocal<ManagedEGLContext> sEglContextStorage 513 = new ThreadLocal<ManagedEGLContext>(); 514 515 EGLContext mEglContext; 516 Thread mEglThread; 517 518 EGLSurface mEglSurface; 519 520 GL mGl; 521 HardwareCanvas mCanvas; 522 523 long mFrameCount; 524 Paint mDebugPaint; 525 526 static boolean sDirtyRegions; 527 static final boolean sDirtyRegionsRequested; 528 static { 529 String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); 530 //noinspection PointlessBooleanExpression,ConstantConditions 531 sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); 532 sDirtyRegionsRequested = sDirtyRegions; 533 } 534 535 boolean mDirtyRegionsEnabled; 536 boolean mUpdateDirtyRegions; 537 538 final boolean mVsyncDisabled; 539 540 final boolean mProfileEnabled; 541 final float[] mProfileData; 542 int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT; 543 544 final boolean mDebugDirtyRegions; 545 546 final int mGlVersion; 547 final boolean mTranslucent; 548 549 private boolean mDestroyed; 550 551 private final Rect mRedrawClip = new Rect(); 552 private final int[] mSurfaceSize = new int[2]; 553 554 GlRenderer(int glVersion, boolean translucent) { 555 mGlVersion = glVersion; 556 mTranslucent = translucent; 557 558 String property; 559 560 property = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false"); 561 mVsyncDisabled = "true".equalsIgnoreCase(property); 562 if (mVsyncDisabled) { 563 Log.d(LOG_TAG, "Disabling v-sync"); 564 } 565 566 //noinspection PointlessBooleanExpression,ConstantConditions 567 if (!ViewDebug.DEBUG_LATENCY) { 568 property = SystemProperties.get(PROFILE_PROPERTY, "false"); 569 mProfileEnabled = "true".equalsIgnoreCase(property); 570 if (mProfileEnabled) { 571 Log.d(LOG_TAG, "Profiling hardware renderer"); 572 } 573 } else { 574 mProfileEnabled = true; 575 } 576 577 if (mProfileEnabled) { 578 mProfileData = new float[PROFILE_MAX_FRAMES * PROFILE_FRAME_DATA_COUNT]; 579 } else { 580 mProfileData = null; 581 } 582 583 property = SystemProperties.get(DEBUG_DIRTY_REGIONS_PROPERTY, "false"); 584 mDebugDirtyRegions = "true".equalsIgnoreCase(property); 585 if (mDebugDirtyRegions) { 586 Log.d(LOG_TAG, "Debugging dirty regions"); 587 } 588 } 589 590 @Override 591 void dumpGfxInfo(PrintWriter pw) { 592 if (mProfileEnabled) { 593 pw.printf("\n\tDraw\tProcess\tExecute\n"); 594 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) { 595 pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1], 596 mProfileData[i + 2]); 597 } 598 } 599 } 600 601 @Override 602 long getFrameCount() { 603 return mFrameCount; 604 } 605 606 /** 607 * Indicates whether this renderer instance can track and update dirty regions. 608 */ 609 boolean hasDirtyRegions() { 610 return mDirtyRegionsEnabled; 611 } 612 613 /** 614 * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)} 615 * is invoked and the requested flag is turned off. The error code is 616 * also logged as a warning. 617 */ 618 void checkEglErrors() { 619 if (isEnabled()) { 620 int error = sEgl.eglGetError(); 621 if (error != EGL_SUCCESS) { 622 // something bad has happened revert to 623 // normal rendering. 624 Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error)); 625 fallback(error != EGL11.EGL_CONTEXT_LOST); 626 } 627 } 628 } 629 630 private void fallback(boolean fallback) { 631 destroy(true); 632 if (fallback) { 633 // we'll try again if it was context lost 634 setRequested(false); 635 Log.w(LOG_TAG, "Mountain View, we've had a problem here. " 636 + "Switching back to software rendering."); 637 } 638 } 639 640 @Override 641 boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException { 642 if (isRequested() && !isEnabled()) { 643 initializeEgl(); 644 mGl = createEglSurface(holder); 645 mDestroyed = false; 646 647 if (mGl != null) { 648 int err = sEgl.eglGetError(); 649 if (err != EGL_SUCCESS) { 650 destroy(true); 651 setRequested(false); 652 } else { 653 if (mCanvas == null) { 654 mCanvas = createCanvas(); 655 } 656 if (mCanvas != null) { 657 setEnabled(true); 658 } else { 659 Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created"); 660 } 661 } 662 663 return mCanvas != null; 664 } 665 } 666 return false; 667 } 668 669 @Override 670 void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { 671 if (isRequested() && isEnabled()) { 672 createEglSurface(holder); 673 } 674 } 675 676 abstract HardwareCanvas createCanvas(); 677 678 abstract int[] getConfig(boolean dirtyRegions); 679 680 void initializeEgl() { 681 synchronized (sEglLock) { 682 if (sEgl == null && sEglConfig == null) { 683 sEgl = (EGL10) EGLContext.getEGL(); 684 685 // Get to the default display. 686 sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY); 687 688 if (sEglDisplay == EGL_NO_DISPLAY) { 689 throw new RuntimeException("eglGetDisplay failed " 690 + GLUtils.getEGLErrorString(sEgl.eglGetError())); 691 } 692 693 // We can now initialize EGL for that display 694 int[] version = new int[2]; 695 if (!sEgl.eglInitialize(sEglDisplay, version)) { 696 throw new RuntimeException("eglInitialize failed " + 697 GLUtils.getEGLErrorString(sEgl.eglGetError())); 698 } 699 700 sEglConfig = chooseEglConfig(); 701 if (sEglConfig == null) { 702 // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without 703 if (sDirtyRegions) { 704 sDirtyRegions = false; 705 sEglConfig = chooseEglConfig(); 706 if (sEglConfig == null) { 707 throw new RuntimeException("eglConfig not initialized"); 708 } 709 } else { 710 throw new RuntimeException("eglConfig not initialized"); 711 } 712 } 713 } 714 } 715 716 ManagedEGLContext managedContext = sEglContextStorage.get(); 717 mEglContext = managedContext != null ? managedContext.getContext() : null; 718 mEglThread = Thread.currentThread(); 719 720 if (mEglContext == null) { 721 mEglContext = createContext(sEgl, sEglDisplay, sEglConfig); 722 sEglContextStorage.set(createManagedContext(mEglContext)); 723 } 724 } 725 726 abstract ManagedEGLContext createManagedContext(EGLContext eglContext); 727 728 private EGLConfig chooseEglConfig() { 729 EGLConfig[] configs = new EGLConfig[1]; 730 int[] configsCount = new int[1]; 731 int[] configSpec = getConfig(sDirtyRegions); 732 733 // Debug 734 final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, ""); 735 if ("all".equalsIgnoreCase(debug)) { 736 sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount); 737 738 EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]]; 739 sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs, 740 configsCount[0], configsCount); 741 742 for (EGLConfig config : debugConfigs) { 743 printConfig(config); 744 } 745 } 746 747 if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) { 748 throw new IllegalArgumentException("eglChooseConfig failed " + 749 GLUtils.getEGLErrorString(sEgl.eglGetError())); 750 } else if (configsCount[0] > 0) { 751 if ("choice".equalsIgnoreCase(debug)) { 752 printConfig(configs[0]); 753 } 754 return configs[0]; 755 } 756 757 return null; 758 } 759 760 private static void printConfig(EGLConfig config) { 761 int[] value = new int[1]; 762 763 Log.d(LOG_TAG, "EGL configuration " + config + ":"); 764 765 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value); 766 Log.d(LOG_TAG, " RED_SIZE = " + value[0]); 767 768 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value); 769 Log.d(LOG_TAG, " GREEN_SIZE = " + value[0]); 770 771 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value); 772 Log.d(LOG_TAG, " BLUE_SIZE = " + value[0]); 773 774 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value); 775 Log.d(LOG_TAG, " ALPHA_SIZE = " + value[0]); 776 777 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value); 778 Log.d(LOG_TAG, " DEPTH_SIZE = " + value[0]); 779 780 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value); 781 Log.d(LOG_TAG, " STENCIL_SIZE = " + value[0]); 782 783 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value); 784 Log.d(LOG_TAG, " SURFACE_TYPE = 0x" + Integer.toHexString(value[0])); 785 } 786 787 GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { 788 // Check preconditions. 789 if (sEgl == null) { 790 throw new RuntimeException("egl not initialized"); 791 } 792 if (sEglDisplay == null) { 793 throw new RuntimeException("eglDisplay not initialized"); 794 } 795 if (sEglConfig == null) { 796 throw new RuntimeException("eglConfig not initialized"); 797 } 798 if (Thread.currentThread() != mEglThread) { 799 throw new IllegalStateException("HardwareRenderer cannot be used " 800 + "from multiple threads"); 801 } 802 803 // In case we need to destroy an existing surface 804 destroySurface(); 805 806 // Create an EGL surface we can render into. 807 if (!createSurface(holder)) { 808 return null; 809 } 810 811 /* 812 * Before we can issue GL commands, we need to make sure 813 * the context is current and bound to a surface. 814 */ 815 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 816 throw new Surface.OutOfResourcesException("eglMakeCurrent failed " 817 + GLUtils.getEGLErrorString(sEgl.eglGetError())); 818 } 819 820 initCaches(); 821 822 enableDirtyRegions(); 823 824 return mEglContext.getGL(); 825 } 826 827 private void enableDirtyRegions() { 828 // If mDirtyRegions is set, this means we have an EGL configuration 829 // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set 830 if (sDirtyRegions) { 831 if (!(mDirtyRegionsEnabled = preserveBackBuffer())) { 832 Log.w(LOG_TAG, "Backbuffer cannot be preserved"); 833 } 834 } else if (sDirtyRegionsRequested) { 835 // If mDirtyRegions is not set, our EGL configuration does not 836 // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default 837 // swap behavior might be EGL_BUFFER_PRESERVED, which means we 838 // want to set mDirtyRegions. We try to do this only if dirty 839 // regions were initially requested as part of the device 840 // configuration (see RENDER_DIRTY_REGIONS) 841 mDirtyRegionsEnabled = isBackBufferPreserved(); 842 } 843 } 844 845 abstract void initCaches(); 846 847 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { 848 int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE }; 849 850 return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, 851 mGlVersion != 0 ? attribs : null); 852 } 853 854 @Override 855 void destroy(boolean full) { 856 if (full && mCanvas != null) { 857 mCanvas = null; 858 } 859 860 if (!isEnabled() || mDestroyed) { 861 setEnabled(false); 862 return; 863 } 864 865 destroySurface(); 866 setEnabled(false); 867 868 mDestroyed = true; 869 mGl = null; 870 } 871 872 void destroySurface() { 873 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { 874 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 875 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 876 mEglSurface = null; 877 } 878 } 879 880 @Override 881 void invalidate(SurfaceHolder holder) { 882 // Cancels any existing buffer to ensure we'll get a buffer 883 // of the right size before we call eglSwapBuffers 884 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 885 886 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { 887 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 888 mEglSurface = null; 889 setEnabled(false); 890 } 891 892 if (holder.getSurface().isValid()) { 893 if (!createSurface(holder)) { 894 return; 895 } 896 897 mUpdateDirtyRegions = true; 898 899 if (mCanvas != null) { 900 setEnabled(true); 901 } 902 } 903 } 904 905 private boolean createSurface(SurfaceHolder holder) { 906 mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null); 907 908 if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) { 909 int error = sEgl.eglGetError(); 910 if (error == EGL_BAD_NATIVE_WINDOW) { 911 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 912 return false; 913 } 914 throw new RuntimeException("createWindowSurface failed " 915 + GLUtils.getEGLErrorString(error)); 916 } 917 return true; 918 } 919 920 @Override 921 boolean validate() { 922 return checkCurrent() != SURFACE_STATE_ERROR; 923 } 924 925 @Override 926 void setup(int width, int height) { 927 if (validate()) { 928 mCanvas.setViewport(width, height); 929 mWidth = width; 930 mHeight = height; 931 } 932 } 933 934 @Override 935 int getWidth() { 936 return mWidth; 937 } 938 939 @Override 940 int getHeight() { 941 return mHeight; 942 } 943 944 @Override 945 HardwareCanvas getCanvas() { 946 return mCanvas; 947 } 948 949 boolean canDraw() { 950 return mGl != null && mCanvas != null; 951 } 952 953 void onPreDraw(Rect dirty) { 954 955 } 956 957 void onPostDraw() { 958 } 959 960 @Override 961 boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, 962 Rect dirty) { 963 if (canDraw()) { 964 if (!hasDirtyRegions()) { 965 dirty = null; 966 } 967 attachInfo.mIgnoreDirtyState = true; 968 attachInfo.mDrawingTime = SystemClock.uptimeMillis(); 969 970 view.mPrivateFlags |= View.DRAWN; 971 972 final int surfaceState = checkCurrent(); 973 if (surfaceState != SURFACE_STATE_ERROR) { 974 HardwareCanvas canvas = mCanvas; 975 attachInfo.mHardwareCanvas = canvas; 976 977 // We had to change the current surface and/or context, redraw everything 978 if (surfaceState == SURFACE_STATE_UPDATED) { 979 dirty = null; 980 beginFrame(null); 981 } else { 982 int[] size = mSurfaceSize; 983 beginFrame(size); 984 985 if (size[1] != mHeight || size[0] != mWidth) { 986 mWidth = size[0]; 987 mHeight = size[1]; 988 989 canvas.setViewport(mWidth, mHeight); 990 991 dirty = null; 992 } 993 } 994 995 onPreDraw(dirty); 996 997 int saveCount = canvas.save(); 998 callbacks.onHardwarePreDraw(canvas); 999 1000 try { 1001 view.mRecreateDisplayList = 1002 (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED; 1003 view.mPrivateFlags &= ~View.INVALIDATED; 1004 1005 long getDisplayListStartTime = 0; 1006 if (mProfileEnabled) { 1007 mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT; 1008 if (mProfileCurrentFrame >= mProfileData.length) { 1009 mProfileCurrentFrame = 0; 1010 } 1011 1012 getDisplayListStartTime = System.nanoTime(); 1013 } 1014 1015 DisplayList displayList = view.getDisplayList(); 1016 1017 if (mProfileEnabled) { 1018 long now = System.nanoTime(); 1019 float total = (now - getDisplayListStartTime) * 0.000001f; 1020 //noinspection PointlessArithmeticExpression 1021 mProfileData[mProfileCurrentFrame] = total; 1022 1023 if (ViewDebug.DEBUG_LATENCY) { 1024 Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- getDisplayList() took " + 1025 total + "ms"); 1026 } 1027 if (View.USE_DISPLAY_LIST_PROPERTIES) { 1028 Log.d("DLProperties", "getDisplayList():\t" + 1029 mProfileData[mProfileCurrentFrame]); 1030 } 1031 } 1032 1033 if (displayList != null) { 1034 long drawDisplayListStartTime = 0; 1035 if (mProfileEnabled) { 1036 drawDisplayListStartTime = System.nanoTime(); 1037 } 1038 1039 int status = canvas.drawDisplayList(displayList, 1040 view.getWidth(), view.getHeight(), mRedrawClip, 1041 DisplayList.FLAG_CLIP_CHILDREN); 1042 1043 if (mProfileEnabled) { 1044 long now = System.nanoTime(); 1045 float total = (now - drawDisplayListStartTime) * 0.000001f; 1046 mProfileData[mProfileCurrentFrame + 1] = total; 1047 1048 if (ViewDebug.DEBUG_LATENCY) { 1049 Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- drawDisplayList() took " + 1050 total + "ms, status=" + status); 1051 } 1052 } 1053 1054 if (status != DisplayList.STATUS_DONE) { 1055 if (mRedrawClip.isEmpty()) { 1056 attachInfo.mViewRootImpl.invalidate(); 1057 } else { 1058 attachInfo.mViewRootImpl.invalidateChildInParent( 1059 null, mRedrawClip); 1060 mRedrawClip.setEmpty(); 1061 } 1062 } 1063 } else { 1064 // Shouldn't reach here 1065 view.draw(canvas); 1066 } 1067 } finally { 1068 callbacks.onHardwarePostDraw(canvas); 1069 canvas.restoreToCount(saveCount); 1070 view.mRecreateDisplayList = false; 1071 mFrameCount++; 1072 if (mDebugDirtyRegions) { 1073 if (mDebugPaint == null) { 1074 mDebugPaint = new Paint(); 1075 mDebugPaint.setColor(0x7fff0000); 1076 } 1077 if (dirty != null && (mFrameCount & 1) == 0) { 1078 canvas.drawRect(dirty, mDebugPaint); 1079 } 1080 } 1081 } 1082 1083 onPostDraw(); 1084 1085 attachInfo.mIgnoreDirtyState = false; 1086 1087 long eglSwapBuffersStartTime = 0; 1088 if (mProfileEnabled) { 1089 eglSwapBuffersStartTime = System.nanoTime(); 1090 } 1091 1092 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); 1093 1094 if (mProfileEnabled) { 1095 long now = System.nanoTime(); 1096 float total = (now - eglSwapBuffersStartTime) * 0.000001f; 1097 mProfileData[mProfileCurrentFrame + 2] = total; 1098 1099 if (ViewDebug.DEBUG_LATENCY) { 1100 Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- eglSwapBuffers() took " + 1101 total + "ms"); 1102 } 1103 } 1104 1105 checkEglErrors(); 1106 1107 return dirty == null; 1108 } 1109 } 1110 1111 return false; 1112 } 1113 1114 /** 1115 * Ensures the current EGL context is the one we expect. 1116 * 1117 * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current, 1118 * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or 1119 * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one 1120 */ 1121 int checkCurrent() { 1122 if (mEglThread != Thread.currentThread()) { 1123 throw new IllegalStateException("Hardware acceleration can only be used with a " + 1124 "single UI thread.\nOriginal thread: " + mEglThread + "\n" + 1125 "Current thread: " + Thread.currentThread()); 1126 } 1127 1128 if (!mEglContext.equals(sEgl.eglGetCurrentContext()) || 1129 !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) { 1130 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 1131 Log.e(LOG_TAG, "eglMakeCurrent failed " + 1132 GLUtils.getEGLErrorString(sEgl.eglGetError())); 1133 fallback(true); 1134 return SURFACE_STATE_ERROR; 1135 } else { 1136 if (mUpdateDirtyRegions) { 1137 enableDirtyRegions(); 1138 mUpdateDirtyRegions = false; 1139 } 1140 return SURFACE_STATE_UPDATED; 1141 } 1142 } 1143 return SURFACE_STATE_SUCCESS; 1144 } 1145 } 1146 1147 /** 1148 * Hardware renderer using OpenGL ES 2.0. 1149 */ 1150 static class Gl20Renderer extends GlRenderer { 1151 private GLES20Canvas mGlCanvas; 1152 1153 private static EGLSurface sPbuffer; 1154 private static final Object[] sPbufferLock = new Object[0]; 1155 1156 static class Gl20RendererEglContext extends ManagedEGLContext { 1157 final Handler mHandler = new Handler(); 1158 1159 public Gl20RendererEglContext(EGLContext context) { 1160 super(context); 1161 } 1162 1163 @Override 1164 public void onTerminate(final EGLContext eglContext) { 1165 // Make sure we do this on the correct thread. 1166 if (mHandler.getLooper() != Looper.myLooper()) { 1167 mHandler.post(new Runnable() { 1168 @Override 1169 public void run() { 1170 onTerminate(eglContext); 1171 } 1172 }); 1173 return; 1174 } 1175 1176 synchronized (sEglLock) { 1177 if (sEgl == null) return; 1178 1179 if (EGLImpl.getInitCount(sEglDisplay) == 1) { 1180 usePbufferSurface(eglContext); 1181 GLES20Canvas.terminateCaches(); 1182 1183 sEgl.eglDestroyContext(sEglDisplay, eglContext); 1184 sEglContextStorage.set(null); 1185 sEglContextStorage.remove(); 1186 1187 sEgl.eglDestroySurface(sEglDisplay, sPbuffer); 1188 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, 1189 EGL_NO_SURFACE, EGL_NO_CONTEXT); 1190 1191 sEgl.eglReleaseThread(); 1192 sEgl.eglTerminate(sEglDisplay); 1193 1194 sEgl = null; 1195 sEglDisplay = null; 1196 sEglConfig = null; 1197 sPbuffer = null; 1198 } 1199 } 1200 } 1201 } 1202 1203 Gl20Renderer(boolean translucent) { 1204 super(2, translucent); 1205 } 1206 1207 @Override 1208 HardwareCanvas createCanvas() { 1209 return mGlCanvas = new GLES20Canvas(mTranslucent); 1210 } 1211 1212 @Override 1213 ManagedEGLContext createManagedContext(EGLContext eglContext) { 1214 return new Gl20Renderer.Gl20RendererEglContext(mEglContext); 1215 } 1216 1217 @Override 1218 int[] getConfig(boolean dirtyRegions) { 1219 return new int[] { 1220 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 1221 EGL_RED_SIZE, 8, 1222 EGL_GREEN_SIZE, 8, 1223 EGL_BLUE_SIZE, 8, 1224 EGL_ALPHA_SIZE, 8, 1225 EGL_DEPTH_SIZE, 0, 1226 EGL_STENCIL_SIZE, GLES20Canvas.getStencilSize(), 1227 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | 1228 (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), 1229 EGL_NONE 1230 }; 1231 } 1232 1233 @Override 1234 void initCaches() { 1235 GLES20Canvas.initCaches(); 1236 } 1237 1238 @Override 1239 boolean canDraw() { 1240 return super.canDraw() && mGlCanvas != null; 1241 } 1242 1243 @Override 1244 void onPreDraw(Rect dirty) { 1245 mGlCanvas.onPreDraw(dirty); 1246 } 1247 1248 @Override 1249 void onPostDraw() { 1250 mGlCanvas.onPostDraw(); 1251 } 1252 1253 @Override 1254 void destroy(boolean full) { 1255 try { 1256 super.destroy(full); 1257 } finally { 1258 if (full && mGlCanvas != null) { 1259 mGlCanvas = null; 1260 } 1261 } 1262 } 1263 1264 @Override 1265 void setup(int width, int height) { 1266 super.setup(width, height); 1267 if (mVsyncDisabled) { 1268 disableVsync(); 1269 } 1270 } 1271 1272 @Override 1273 public DisplayList createDisplayList(String name) { 1274 return new GLES20DisplayList(name); 1275 } 1276 1277 @Override 1278 HardwareLayer createHardwareLayer(boolean isOpaque) { 1279 return new GLES20TextureLayer(isOpaque); 1280 } 1281 1282 @Override 1283 HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { 1284 return new GLES20RenderLayer(width, height, isOpaque); 1285 } 1286 1287 @Override 1288 SurfaceTexture createSurfaceTexture(HardwareLayer layer) { 1289 return ((GLES20TextureLayer) layer).getSurfaceTexture(); 1290 } 1291 1292 @Override 1293 void destroyLayers(View view) { 1294 if (view != null && isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) { 1295 destroyHardwareLayer(view); 1296 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); 1297 } 1298 } 1299 1300 private static void destroyHardwareLayer(View view) { 1301 view.destroyLayer(true); 1302 1303 if (view instanceof ViewGroup) { 1304 ViewGroup group = (ViewGroup) view; 1305 1306 int count = group.getChildCount(); 1307 for (int i = 0; i < count; i++) { 1308 destroyHardwareLayer(group.getChildAt(i)); 1309 } 1310 } 1311 } 1312 1313 @Override 1314 void destroyHardwareResources(View view) { 1315 if (view != null) { 1316 boolean needsContext = true; 1317 if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false; 1318 1319 if (needsContext) { 1320 Gl20RendererEglContext managedContext = 1321 (Gl20RendererEglContext) sEglContextStorage.get(); 1322 if (managedContext == null) return; 1323 usePbufferSurface(managedContext.getContext()); 1324 } 1325 1326 destroyResources(view); 1327 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); 1328 } 1329 } 1330 1331 private static void destroyResources(View view) { 1332 view.destroyHardwareResources(); 1333 1334 if (view instanceof ViewGroup) { 1335 ViewGroup group = (ViewGroup) view; 1336 1337 int count = group.getChildCount(); 1338 for (int i = 0; i < count; i++) { 1339 destroyResources(group.getChildAt(i)); 1340 } 1341 } 1342 } 1343 1344 static HardwareRenderer create(boolean translucent) { 1345 if (GLES20Canvas.isAvailable()) { 1346 return new Gl20Renderer(translucent); 1347 } 1348 return null; 1349 } 1350 1351 static void trimMemory(int level) { 1352 if (sEgl == null || sEglConfig == null) return; 1353 1354 Gl20RendererEglContext managedContext = 1355 (Gl20RendererEglContext) sEglContextStorage.get(); 1356 // We do not have OpenGL objects 1357 if (managedContext == null) { 1358 return; 1359 } else { 1360 usePbufferSurface(managedContext.getContext()); 1361 } 1362 1363 if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { 1364 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL); 1365 } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { 1366 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE); 1367 } 1368 } 1369 1370 private static void usePbufferSurface(EGLContext eglContext) { 1371 synchronized (sPbufferLock) { 1372 // Create a temporary 1x1 pbuffer so we have a context 1373 // to clear our OpenGL objects 1374 if (sPbuffer == null) { 1375 sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] { 1376 EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE 1377 }); 1378 } 1379 } 1380 sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext); 1381 } 1382 } 1383} 1384