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