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