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