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