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