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