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