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