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