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