HardwareRenderer.java revision 735738c4ddf3229caa5f6e634bf591953ac29944
1/* 2 * Copyright (C) 2013 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 17package android.view; 18 19import android.content.ComponentCallbacks2; 20import android.graphics.Paint; 21import android.graphics.Rect; 22import android.graphics.SurfaceTexture; 23import android.opengl.EGL14; 24import android.opengl.GLUtils; 25import android.opengl.ManagedEGLContext; 26import android.os.Handler; 27import android.os.Looper; 28import android.os.SystemClock; 29import android.os.SystemProperties; 30import android.os.Trace; 31import android.util.DisplayMetrics; 32import android.util.Log; 33import com.google.android.gles_jni.EGLImpl; 34 35import javax.microedition.khronos.egl.EGL10; 36import javax.microedition.khronos.egl.EGL11; 37import javax.microedition.khronos.egl.EGLConfig; 38import javax.microedition.khronos.egl.EGLContext; 39import javax.microedition.khronos.egl.EGLDisplay; 40import javax.microedition.khronos.egl.EGLSurface; 41import javax.microedition.khronos.opengles.GL; 42 43import java.io.File; 44import java.io.PrintWriter; 45import java.util.Arrays; 46import java.util.concurrent.locks.ReentrantLock; 47 48import static javax.microedition.khronos.egl.EGL10.*; 49 50/** 51 * Interface for rendering a ViewAncestor using hardware acceleration. 52 * 53 * @hide 54 */ 55public abstract class HardwareRenderer { 56 static final String LOG_TAG = "HardwareRenderer"; 57 58 /** 59 * Name of the file that holds the shaders cache. 60 */ 61 private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache"; 62 63 /** 64 * Turn on to only refresh the parts of the screen that need updating. 65 * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY} 66 * must also have the value "true". 67 */ 68 public static final boolean RENDER_DIRTY_REGIONS = true; 69 70 /** 71 * System property used to enable or disable dirty regions invalidation. 72 * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true. 73 * The default value of this property is assumed to be true. 74 * 75 * Possible values: 76 * "true", to enable partial invalidates 77 * "false", to disable partial invalidates 78 */ 79 static final String RENDER_DIRTY_REGIONS_PROPERTY = "debug.hwui.render_dirty_regions"; 80 81 /** 82 * System property used to enable or disable hardware rendering profiling. 83 * The default value of this property is assumed to be false. 84 * 85 * When profiling is enabled, the adb shell dumpsys gfxinfo command will 86 * output extra information about the time taken to execute by the last 87 * frames. 88 * 89 * Possible values: 90 * "true", to enable profiling 91 * "visual_bars", to enable profiling and visualize the results on screen 92 * "visual_lines", to enable profiling and visualize the results on screen 93 * "false", to disable profiling 94 * 95 * @see #PROFILE_PROPERTY_VISUALIZE_BARS 96 * @see #PROFILE_PROPERTY_VISUALIZE_LINES 97 * 98 * @hide 99 */ 100 public static final String PROFILE_PROPERTY = "debug.hwui.profile"; 101 102 /** 103 * Value for {@link #PROFILE_PROPERTY}. When the property is set to this 104 * value, profiling data will be visualized on screen as a bar chart. 105 * 106 * @hide 107 */ 108 public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars"; 109 110 /** 111 * Value for {@link #PROFILE_PROPERTY}. When the property is set to this 112 * value, profiling data will be visualized on screen as a line chart. 113 * 114 * @hide 115 */ 116 public static final String PROFILE_PROPERTY_VISUALIZE_LINES = "visual_lines"; 117 118 /** 119 * System property used to specify the number of frames to be used 120 * when doing hardware rendering profiling. 121 * The default value of this property is #PROFILE_MAX_FRAMES. 122 * 123 * When profiling is enabled, the adb shell dumpsys gfxinfo command will 124 * output extra information about the time taken to execute by the last 125 * frames. 126 * 127 * Possible values: 128 * "60", to set the limit of frames to 60 129 */ 130 static final String PROFILE_MAXFRAMES_PROPERTY = "debug.hwui.profile.maxframes"; 131 132 /** 133 * System property used to debug EGL configuration choice. 134 * 135 * Possible values: 136 * "choice", print the chosen configuration only 137 * "all", print all possible configurations 138 */ 139 static final String PRINT_CONFIG_PROPERTY = "debug.hwui.print_config"; 140 141 /** 142 * Turn on to draw dirty regions every other frame. 143 * 144 * Possible values: 145 * "true", to enable dirty regions debugging 146 * "false", to disable dirty regions debugging 147 * 148 * @hide 149 */ 150 public static final String DEBUG_DIRTY_REGIONS_PROPERTY = "debug.hwui.show_dirty_regions"; 151 152 /** 153 * Turn on to flash hardware layers when they update. 154 * 155 * Possible values: 156 * "true", to enable hardware layers updates debugging 157 * "false", to disable hardware layers updates debugging 158 * 159 * @hide 160 */ 161 public static final String DEBUG_SHOW_LAYERS_UPDATES_PROPERTY = 162 "debug.hwui.show_layers_updates"; 163 164 /** 165 * Turn on to show overdraw level. 166 * 167 * Possible values: 168 * "true", to enable overdraw debugging 169 * "false", to disable overdraw debugging 170 * 171 * @hide 172 */ 173 public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw"; 174 175 /** 176 * Turn on to allow region clipping (see 177 * {@link android.graphics.Canvas#clipPath(android.graphics.Path)} and 178 * {@link android.graphics.Canvas#clipRegion(android.graphics.Region)}. 179 * 180 * When this option is turned on a stencil buffer is always required. 181 * If this option is off a stencil buffer is only created when the overdraw 182 * debugging mode is turned on. 183 */ 184 private static final boolean REGION_CLIPPING_ENABLED = false; 185 186 /** 187 * A process can set this flag to false to prevent the use of hardware 188 * rendering. 189 * 190 * @hide 191 */ 192 public static boolean sRendererDisabled = false; 193 194 /** 195 * Further hardware renderer disabling for the system process. 196 * 197 * @hide 198 */ 199 public static boolean sSystemRendererDisabled = false; 200 201 /** 202 * Number of frames to profile. 203 */ 204 private static final int PROFILE_MAX_FRAMES = 128; 205 206 /** 207 * Number of floats per profiled frame. 208 */ 209 private static final int PROFILE_FRAME_DATA_COUNT = 3; 210 211 private boolean mEnabled; 212 private boolean mRequested = true; 213 214 /** 215 * Invoke this method to disable hardware rendering in the current process. 216 * 217 * @hide 218 */ 219 public static void disable(boolean system) { 220 sRendererDisabled = true; 221 if (system) { 222 sSystemRendererDisabled = true; 223 } 224 } 225 226 /** 227 * Indicates whether hardware acceleration is available under any form for 228 * the view hierarchy. 229 * 230 * @return True if the view hierarchy can potentially be hardware accelerated, 231 * false otherwise 232 */ 233 public static boolean isAvailable() { 234 return GLES20Canvas.isAvailable(); 235 } 236 237 /** 238 * Destroys the hardware rendering context. 239 * 240 * @param full If true, destroys all associated resources. 241 */ 242 abstract void destroy(boolean full); 243 244 /** 245 * Initializes the hardware renderer for the specified surface. 246 * 247 * @param surface The surface to hardware accelerate 248 * 249 * @return True if the initialization was successful, false otherwise. 250 */ 251 abstract boolean initialize(Surface surface) throws Surface.OutOfResourcesException; 252 253 /** 254 * Updates the hardware renderer for the specified surface. 255 * 256 * @param surface The surface to hardware accelerate 257 */ 258 abstract void updateSurface(Surface surface) throws Surface.OutOfResourcesException; 259 260 /** 261 * Destroys the layers used by the specified view hierarchy. 262 * 263 * @param view The root of the view hierarchy 264 */ 265 abstract void destroyLayers(View view); 266 267 /** 268 * Destroys all hardware rendering resources associated with the specified 269 * view hierarchy. 270 * 271 * @param view The root of the view hierarchy 272 */ 273 abstract void destroyHardwareResources(View view); 274 275 /** 276 * This method should be invoked whenever the current hardware renderer 277 * context should be reset. 278 * 279 * @param surface The surface to hardware accelerate 280 */ 281 abstract void invalidate(Surface surface); 282 283 /** 284 * This method should be invoked to ensure the hardware renderer is in 285 * valid state (for instance, to ensure the correct EGL context is bound 286 * to the current thread.) 287 * 288 * @return true if the renderer is now valid, false otherwise 289 */ 290 abstract boolean validate(); 291 292 /** 293 * This method ensures the hardware renderer is in a valid state 294 * before executing the specified action. 295 * 296 * This method will attempt to set a valid state even if the window 297 * the renderer is attached to was destroyed. 298 * 299 * @return true if the action was run 300 */ 301 abstract boolean safelyRun(Runnable action); 302 303 /** 304 * Setup the hardware renderer for drawing. This is called whenever the 305 * size of the target surface changes or when the surface is first created. 306 * 307 * @param width Width of the drawing surface. 308 * @param height Height of the drawing surface. 309 */ 310 abstract void setup(int width, int height); 311 312 /** 313 * Gets the current width of the surface. This is the width that the surface 314 * was last set to in a call to {@link #setup(int, int)}. 315 * 316 * @return the current width of the surface 317 */ 318 abstract int getWidth(); 319 320 /** 321 * Gets the current height of the surface. This is the height that the surface 322 * was last set to in a call to {@link #setup(int, int)}. 323 * 324 * @return the current width of the surface 325 */ 326 abstract int getHeight(); 327 328 /** 329 * Gets the current canvas associated with this HardwareRenderer. 330 * 331 * @return the current HardwareCanvas 332 */ 333 abstract HardwareCanvas getCanvas(); 334 335 /** 336 * Outputs extra debugging information in the specified file descriptor. 337 * @param pw 338 */ 339 abstract void dumpGfxInfo(PrintWriter pw); 340 341 /** 342 * Outputs the total number of frames rendered (used for fps calculations) 343 * 344 * @return the number of frames rendered 345 */ 346 abstract long getFrameCount(); 347 348 /** 349 * Loads system properties used by the renderer. This method is invoked 350 * whenever system properties are modified. Implementations can use this 351 * to trigger live updates of the renderer based on properties. 352 * 353 * @param surface The surface to update with the new properties. 354 * Can be null. 355 * 356 * @return True if a property has changed. 357 */ 358 abstract boolean loadSystemProperties(Surface surface); 359 360 private static native boolean nLoadProperties(); 361 362 /** 363 * Sets the directory to use as a persistent storage for hardware rendering 364 * resources. 365 * 366 * @param cacheDir A directory the current process can write to 367 */ 368 public static void setupDiskCache(File cacheDir) { 369 nSetupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath()); 370 } 371 372 private static native void nSetupShadersDiskCache(String cacheFile); 373 374 /** 375 * Notifies EGL that the frame is about to be rendered. 376 * @param size 377 */ 378 static void beginFrame(int[] size) { 379 nBeginFrame(size); 380 } 381 382 private static native void nBeginFrame(int[] size); 383 384 /** 385 * Preserves the back buffer of the current surface after a buffer swap. 386 * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current 387 * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL 388 * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT. 389 * 390 * @return True if the swap behavior was successfully changed, 391 * false otherwise. 392 */ 393 static boolean preserveBackBuffer() { 394 return nPreserveBackBuffer(); 395 } 396 397 private static native boolean nPreserveBackBuffer(); 398 399 /** 400 * Indicates whether the current surface preserves its back buffer 401 * after a buffer swap. 402 * 403 * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED, 404 * false otherwise 405 */ 406 static boolean isBackBufferPreserved() { 407 return nIsBackBufferPreserved(); 408 } 409 410 private static native boolean nIsBackBufferPreserved(); 411 412 /** 413 * Indicates that the specified hardware layer needs to be updated 414 * as soon as possible. 415 * 416 * @param layer The hardware layer that needs an update 417 */ 418 abstract void pushLayerUpdate(HardwareLayer layer); 419 420 /** 421 * Interface used to receive callbacks whenever a view is drawn by 422 * a hardware renderer instance. 423 */ 424 interface HardwareDrawCallbacks { 425 /** 426 * Invoked before a view is drawn by a hardware renderer. 427 * 428 * @param canvas The Canvas used to render the view. 429 */ 430 void onHardwarePreDraw(HardwareCanvas canvas); 431 432 /** 433 * Invoked after a view is drawn by a hardware renderer. 434 * 435 * @param canvas The Canvas used to render the view. 436 */ 437 void onHardwarePostDraw(HardwareCanvas canvas); 438 } 439 440 /** 441 * Draws the specified view. 442 * 443 * @param view The view to draw. 444 * @param attachInfo AttachInfo tied to the specified view. 445 * @param callbacks Callbacks invoked when drawing happens. 446 * @param dirty The dirty rectangle to update, can be null. 447 * 448 * @return true if the dirty rect was ignored, false otherwise 449 */ 450 abstract boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, 451 Rect dirty); 452 453 /** 454 * Creates a new display list that can be used to record batches of 455 * drawing operations. 456 * 457 * @param name The name of the display list, used for debugging purpose. 458 * May be null 459 * 460 * @return A new display list. 461 */ 462 public abstract DisplayList createDisplayList(String name); 463 464 /** 465 * Creates a new hardware layer. A hardware layer built by calling this 466 * method will be treated as a texture layer, instead of as a render target. 467 * 468 * @param isOpaque Whether the layer should be opaque or not 469 * 470 * @return A hardware layer 471 */ 472 abstract HardwareLayer createHardwareLayer(boolean isOpaque); 473 474 /** 475 * Creates a new hardware layer. 476 * 477 * @param width The minimum width of the layer 478 * @param height The minimum height of the layer 479 * @param isOpaque Whether the layer should be opaque or not 480 * 481 * @return A hardware layer 482 */ 483 abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque); 484 485 /** 486 * Creates a new {@link SurfaceTexture} that can be used to render into the 487 * specified hardware layer. 488 * 489 * 490 * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture} 491 * 492 * @return A {@link SurfaceTexture} 493 */ 494 abstract SurfaceTexture createSurfaceTexture(HardwareLayer layer); 495 496 /** 497 * Sets the {@link android.graphics.SurfaceTexture} that will be used to 498 * render into the specified hardware layer. 499 * 500 * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture} 501 * @param surfaceTexture The {@link android.graphics.SurfaceTexture} to use for the layer 502 */ 503 abstract void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture); 504 505 /** 506 * Detaches the specified functor from the current functor execution queue. 507 * 508 * @param functor The native functor to remove from the execution queue. 509 * 510 * @see HardwareCanvas#callDrawGLFunction(int) 511 * @see #attachFunctor(android.view.View.AttachInfo, int) 512 */ 513 abstract void detachFunctor(int functor); 514 515 /** 516 * Schedules the specified functor in the functors execution queue. 517 * 518 * @param attachInfo AttachInfo tied to this renderer. 519 * @param functor The native functor to insert in the execution queue. 520 * 521 * @see HardwareCanvas#callDrawGLFunction(int) 522 * @see #detachFunctor(int) 523 * 524 * @return true if the functor was attached successfully 525 */ 526 abstract boolean attachFunctor(View.AttachInfo attachInfo, int functor); 527 528 /** 529 * Initializes the hardware renderer for the specified surface and setup the 530 * renderer for drawing, if needed. This is invoked when the ViewAncestor has 531 * potentially lost the hardware renderer. The hardware renderer should be 532 * reinitialized and setup when the render {@link #isRequested()} and 533 * {@link #isEnabled()}. 534 * 535 * @param width The width of the drawing surface. 536 * @param height The height of the drawing surface. 537 * @param surface The surface to hardware accelerate 538 * 539 * @return true if the surface was initialized, false otherwise. Returning 540 * false might mean that the surface was already initialized. 541 */ 542 boolean initializeIfNeeded(int width, int height, Surface surface) 543 throws Surface.OutOfResourcesException { 544 if (isRequested()) { 545 // We lost the gl context, so recreate it. 546 if (!isEnabled()) { 547 if (initialize(surface)) { 548 setup(width, height); 549 return true; 550 } 551 } 552 } 553 return false; 554 } 555 556 /** 557 * Creates a hardware renderer using OpenGL. 558 * 559 * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.) 560 * @param translucent True if the surface is translucent, false otherwise 561 * 562 * @return A hardware renderer backed by OpenGL. 563 */ 564 static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) { 565 switch (glVersion) { 566 case 2: 567 return Gl20Renderer.create(translucent); 568 } 569 throw new IllegalArgumentException("Unknown GL version: " + glVersion); 570 } 571 572 /** 573 * Invoke this method when the system is running out of memory. This 574 * method will attempt to recover as much memory as possible, based on 575 * the specified hint. 576 * 577 * @param level Hint about the amount of memory that should be trimmed, 578 * see {@link android.content.ComponentCallbacks} 579 */ 580 static void trimMemory(int level) { 581 startTrimMemory(level); 582 endTrimMemory(); 583 } 584 585 /** 586 * Starts the process of trimming memory. Usually this call will setup 587 * hardware rendering context and reclaim memory.Extra cleanup might 588 * be required by calling {@link #endTrimMemory()}. 589 * 590 * @param level Hint about the amount of memory that should be trimmed, 591 * see {@link android.content.ComponentCallbacks} 592 */ 593 static void startTrimMemory(int level) { 594 Gl20Renderer.startTrimMemory(level); 595 } 596 597 /** 598 * Finishes the process of trimming memory. This method will usually 599 * cleanup special resources used by the memory trimming process. 600 */ 601 static void endTrimMemory() { 602 Gl20Renderer.endTrimMemory(); 603 } 604 605 /** 606 * Indicates whether hardware acceleration is currently enabled. 607 * 608 * @return True if hardware acceleration is in use, false otherwise. 609 */ 610 boolean isEnabled() { 611 return mEnabled; 612 } 613 614 /** 615 * Indicates whether hardware acceleration is currently enabled. 616 * 617 * @param enabled True if the hardware renderer is in use, false otherwise. 618 */ 619 void setEnabled(boolean enabled) { 620 mEnabled = enabled; 621 } 622 623 /** 624 * Indicates whether hardware acceleration is currently request but not 625 * necessarily enabled yet. 626 * 627 * @return True if requested, false otherwise. 628 */ 629 boolean isRequested() { 630 return mRequested; 631 } 632 633 /** 634 * Indicates whether hardware acceleration is currently requested but not 635 * necessarily enabled yet. 636 * 637 * @return True to request hardware acceleration, false otherwise. 638 */ 639 void setRequested(boolean requested) { 640 mRequested = requested; 641 } 642 643 /** 644 * Describes a series of frames that should be drawn on screen as a graph. 645 * Each frame is composed of 1 or more elements. 646 */ 647 abstract class GraphDataProvider { 648 /** 649 * Draws the graph as bars. Frame elements are stacked on top of 650 * each other. 651 */ 652 public static final int GRAPH_TYPE_BARS = 0; 653 /** 654 * Draws the graph as lines. The number of series drawn corresponds 655 * to the number of elements. 656 */ 657 public static final int GRAPH_TYPE_LINES = 1; 658 659 /** 660 * Returns the type of graph to render. 661 * 662 * @return {@link #GRAPH_TYPE_BARS} or {@link #GRAPH_TYPE_LINES} 663 */ 664 abstract int getGraphType(); 665 666 /** 667 * This method is invoked before the graph is drawn. This method 668 * can be used to compute sizes, etc. 669 * 670 * @param metrics The display metrics 671 */ 672 abstract void prepare(DisplayMetrics metrics); 673 674 /** 675 * @return The size in pixels of a vertical unit. 676 */ 677 abstract int getVerticalUnitSize(); 678 679 /** 680 * @return The size in pixels of a horizontal unit. 681 */ 682 abstract int getHorizontalUnitSize(); 683 684 /** 685 * @return The size in pixels of the margin between horizontal units. 686 */ 687 abstract int getHorizontaUnitMargin(); 688 689 /** 690 * An optional threshold value. 691 * 692 * @return A value >= 0 to draw the threshold, a negative value 693 * to ignore it. 694 */ 695 abstract float getThreshold(); 696 697 /** 698 * The data to draw in the graph. The number of elements in the 699 * array must be at least {@link #getFrameCount()} * {@link #getElementCount()}. 700 * If a value is negative the following values will be ignored. 701 */ 702 abstract float[] getData(); 703 704 /** 705 * Returns the number of frames to render in the graph. 706 */ 707 abstract int getFrameCount(); 708 709 /** 710 * Returns the number of elements in each frame. This directly affects 711 * the number of series drawn in the graph. 712 */ 713 abstract int getElementCount(); 714 715 /** 716 * Returns the current frame, if any. If the returned value is negative 717 * the current frame is ignored. 718 */ 719 abstract int getCurrentFrame(); 720 721 /** 722 * Prepares the paint to draw the specified element (or series.) 723 */ 724 abstract void setupGraphPaint(Paint paint, int elementIndex); 725 726 /** 727 * Prepares the paint to draw the threshold. 728 */ 729 abstract void setupThresholdPaint(Paint paint); 730 731 /** 732 * Prepares the paint to draw the current frame indicator. 733 */ 734 abstract void setupCurrentFramePaint(Paint paint); 735 } 736 737 @SuppressWarnings({"deprecation"}) 738 static abstract class GlRenderer extends HardwareRenderer { 739 static final int SURFACE_STATE_ERROR = 0; 740 static final int SURFACE_STATE_SUCCESS = 1; 741 static final int SURFACE_STATE_UPDATED = 2; 742 743 static final int FUNCTOR_PROCESS_DELAY = 4; 744 745 private static final int PROFILE_DRAW_MARGIN = 0; 746 private static final int PROFILE_DRAW_WIDTH = 3; 747 private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 }; 748 private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d; 749 private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d; 750 private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2; 751 private static final int PROFILE_DRAW_DP_PER_MS = 7; 752 753 static EGL10 sEgl; 754 static EGLDisplay sEglDisplay; 755 static EGLConfig sEglConfig; 756 static final Object[] sEglLock = new Object[0]; 757 int mWidth = -1, mHeight = -1; 758 759 static final ThreadLocal<ManagedEGLContext> sEglContextStorage 760 = new ThreadLocal<ManagedEGLContext>(); 761 762 EGLContext mEglContext; 763 Thread mEglThread; 764 765 EGLSurface mEglSurface; 766 767 GL mGl; 768 HardwareCanvas mCanvas; 769 770 long mFrameCount; 771 Paint mDebugPaint; 772 773 static boolean sDirtyRegions; 774 static final boolean sDirtyRegionsRequested; 775 static { 776 String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); 777 //noinspection PointlessBooleanExpression,ConstantConditions 778 sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); 779 sDirtyRegionsRequested = sDirtyRegions; 780 } 781 782 boolean mDirtyRegionsEnabled; 783 boolean mUpdateDirtyRegions; 784 785 boolean mProfileEnabled; 786 int mProfileVisualizerType = -1; 787 float[] mProfileData; 788 ReentrantLock mProfileLock; 789 int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT; 790 791 GraphDataProvider mDebugDataProvider; 792 float[][] mProfileShapes; 793 Paint mProfilePaint; 794 795 boolean mDebugDirtyRegions; 796 boolean mShowOverdraw; 797 798 final int mGlVersion; 799 final boolean mTranslucent; 800 801 private boolean mDestroyed; 802 803 private final Rect mRedrawClip = new Rect(); 804 805 private final int[] mSurfaceSize = new int[2]; 806 private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable(); 807 808 GlRenderer(int glVersion, boolean translucent) { 809 mGlVersion = glVersion; 810 mTranslucent = translucent; 811 812 loadSystemProperties(null); 813 } 814 815 private static final String[] VISUALIZERS = { 816 PROFILE_PROPERTY_VISUALIZE_BARS, 817 PROFILE_PROPERTY_VISUALIZE_LINES 818 }; 819 820 @Override 821 boolean loadSystemProperties(Surface surface) { 822 boolean value; 823 boolean changed = false; 824 825 String profiling = SystemProperties.get(PROFILE_PROPERTY); 826 int graphType = Arrays.binarySearch(VISUALIZERS, profiling); 827 value = graphType >= 0; 828 829 if (graphType != mProfileVisualizerType) { 830 changed = true; 831 mProfileVisualizerType = graphType; 832 833 mProfileShapes = null; 834 mProfilePaint = null; 835 836 if (value) { 837 mDebugDataProvider = new DrawPerformanceDataProvider(graphType); 838 } else { 839 mDebugDataProvider = null; 840 } 841 } 842 843 // If on-screen profiling is not enabled, we need to check whether 844 // console profiling only is enabled 845 if (!value) { 846 value = Boolean.parseBoolean(profiling); 847 } 848 849 if (value != mProfileEnabled) { 850 changed = true; 851 mProfileEnabled = value; 852 853 if (mProfileEnabled) { 854 Log.d(LOG_TAG, "Profiling hardware renderer"); 855 856 int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY, 857 PROFILE_MAX_FRAMES); 858 mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT]; 859 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) { 860 mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1; 861 } 862 863 mProfileLock = new ReentrantLock(); 864 } else { 865 mProfileData = null; 866 mProfileLock = null; 867 mProfileVisualizerType = -1; 868 } 869 870 mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT; 871 } 872 873 value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false); 874 if (value != mDebugDirtyRegions) { 875 changed = true; 876 mDebugDirtyRegions = value; 877 878 if (mDebugDirtyRegions) { 879 Log.d(LOG_TAG, "Debugging dirty regions"); 880 } 881 } 882 883 value = SystemProperties.getBoolean( 884 HardwareRenderer.DEBUG_SHOW_OVERDRAW_PROPERTY, false); 885 if (value != mShowOverdraw) { 886 changed = true; 887 mShowOverdraw = value; 888 889 if (!REGION_CLIPPING_ENABLED) { 890 if (surface != null && isEnabled()) { 891 if (validate()) { 892 sEglConfig = loadEglConfig(); 893 invalidate(surface); 894 } 895 } 896 } 897 } 898 899 if (nLoadProperties()) { 900 changed = true; 901 } 902 903 return changed; 904 } 905 906 @Override 907 void dumpGfxInfo(PrintWriter pw) { 908 if (mProfileEnabled) { 909 pw.printf("\n\tDraw\tProcess\tExecute\n"); 910 911 mProfileLock.lock(); 912 try { 913 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) { 914 if (mProfileData[i] < 0) { 915 break; 916 } 917 pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1], 918 mProfileData[i + 2]); 919 mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1; 920 } 921 mProfileCurrentFrame = mProfileData.length; 922 } finally { 923 mProfileLock.unlock(); 924 } 925 } 926 } 927 928 @Override 929 long getFrameCount() { 930 return mFrameCount; 931 } 932 933 /** 934 * Indicates whether this renderer instance can track and update dirty regions. 935 */ 936 boolean hasDirtyRegions() { 937 return mDirtyRegionsEnabled; 938 } 939 940 /** 941 * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)} 942 * is invoked and the requested flag is turned off. The error code is 943 * also logged as a warning. 944 */ 945 void checkEglErrors() { 946 if (isEnabled()) { 947 checkEglErrorsForced(); 948 } 949 } 950 951 private void checkEglErrorsForced() { 952 int error = sEgl.eglGetError(); 953 if (error != EGL_SUCCESS) { 954 // something bad has happened revert to 955 // normal rendering. 956 Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error)); 957 fallback(error != EGL11.EGL_CONTEXT_LOST); 958 } 959 } 960 961 private void fallback(boolean fallback) { 962 destroy(true); 963 if (fallback) { 964 // we'll try again if it was context lost 965 setRequested(false); 966 Log.w(LOG_TAG, "Mountain View, we've had a problem here. " 967 + "Switching back to software rendering."); 968 } 969 } 970 971 @Override 972 boolean initialize(Surface surface) throws Surface.OutOfResourcesException { 973 if (isRequested() && !isEnabled()) { 974 initializeEgl(); 975 mGl = createEglSurface(surface); 976 mDestroyed = false; 977 978 if (mGl != null) { 979 int err = sEgl.eglGetError(); 980 if (err != EGL_SUCCESS) { 981 destroy(true); 982 setRequested(false); 983 } else { 984 if (mCanvas == null) { 985 mCanvas = createCanvas(); 986 } 987 if (mCanvas != null) { 988 setEnabled(true); 989 } else { 990 Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created"); 991 } 992 } 993 994 return mCanvas != null; 995 } 996 } 997 return false; 998 } 999 1000 @Override 1001 void updateSurface(Surface surface) throws Surface.OutOfResourcesException { 1002 if (isRequested() && isEnabled()) { 1003 createEglSurface(surface); 1004 } 1005 } 1006 1007 abstract HardwareCanvas createCanvas(); 1008 1009 abstract int[] getConfig(boolean dirtyRegions); 1010 1011 void initializeEgl() { 1012 synchronized (sEglLock) { 1013 if (sEgl == null && sEglConfig == null) { 1014 sEgl = (EGL10) EGLContext.getEGL(); 1015 1016 // Get to the default display. 1017 sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY); 1018 1019 if (sEglDisplay == EGL_NO_DISPLAY) { 1020 throw new RuntimeException("eglGetDisplay failed " 1021 + GLUtils.getEGLErrorString(sEgl.eglGetError())); 1022 } 1023 1024 // We can now initialize EGL for that display 1025 int[] version = new int[2]; 1026 if (!sEgl.eglInitialize(sEglDisplay, version)) { 1027 throw new RuntimeException("eglInitialize failed " + 1028 GLUtils.getEGLErrorString(sEgl.eglGetError())); 1029 } 1030 1031 checkEglErrorsForced(); 1032 1033 sEglConfig = loadEglConfig(); 1034 } 1035 } 1036 1037 ManagedEGLContext managedContext = sEglContextStorage.get(); 1038 mEglContext = managedContext != null ? managedContext.getContext() : null; 1039 mEglThread = Thread.currentThread(); 1040 1041 if (mEglContext == null) { 1042 mEglContext = createContext(sEgl, sEglDisplay, sEglConfig); 1043 sEglContextStorage.set(createManagedContext(mEglContext)); 1044 } 1045 } 1046 1047 private EGLConfig loadEglConfig() { 1048 EGLConfig eglConfig = chooseEglConfig(); 1049 if (eglConfig == null) { 1050 // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without 1051 if (sDirtyRegions) { 1052 sDirtyRegions = false; 1053 eglConfig = chooseEglConfig(); 1054 if (eglConfig == null) { 1055 throw new RuntimeException("eglConfig not initialized"); 1056 } 1057 } else { 1058 throw new RuntimeException("eglConfig not initialized"); 1059 } 1060 } 1061 return eglConfig; 1062 } 1063 1064 abstract ManagedEGLContext createManagedContext(EGLContext eglContext); 1065 1066 private EGLConfig chooseEglConfig() { 1067 EGLConfig[] configs = new EGLConfig[1]; 1068 int[] configsCount = new int[1]; 1069 int[] configSpec = getConfig(sDirtyRegions); 1070 1071 // Debug 1072 final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, ""); 1073 if ("all".equalsIgnoreCase(debug)) { 1074 sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount); 1075 1076 EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]]; 1077 sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs, 1078 configsCount[0], configsCount); 1079 1080 for (EGLConfig config : debugConfigs) { 1081 printConfig(config); 1082 } 1083 } 1084 1085 if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) { 1086 throw new IllegalArgumentException("eglChooseConfig failed " + 1087 GLUtils.getEGLErrorString(sEgl.eglGetError())); 1088 } else if (configsCount[0] > 0) { 1089 if ("choice".equalsIgnoreCase(debug)) { 1090 printConfig(configs[0]); 1091 } 1092 return configs[0]; 1093 } 1094 1095 return null; 1096 } 1097 1098 private static void printConfig(EGLConfig config) { 1099 int[] value = new int[1]; 1100 1101 Log.d(LOG_TAG, "EGL configuration " + config + ":"); 1102 1103 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value); 1104 Log.d(LOG_TAG, " RED_SIZE = " + value[0]); 1105 1106 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value); 1107 Log.d(LOG_TAG, " GREEN_SIZE = " + value[0]); 1108 1109 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value); 1110 Log.d(LOG_TAG, " BLUE_SIZE = " + value[0]); 1111 1112 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value); 1113 Log.d(LOG_TAG, " ALPHA_SIZE = " + value[0]); 1114 1115 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value); 1116 Log.d(LOG_TAG, " DEPTH_SIZE = " + value[0]); 1117 1118 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value); 1119 Log.d(LOG_TAG, " STENCIL_SIZE = " + value[0]); 1120 1121 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLE_BUFFERS, value); 1122 Log.d(LOG_TAG, " SAMPLE_BUFFERS = " + value[0]); 1123 1124 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLES, value); 1125 Log.d(LOG_TAG, " SAMPLES = " + value[0]); 1126 1127 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value); 1128 Log.d(LOG_TAG, " SURFACE_TYPE = 0x" + Integer.toHexString(value[0])); 1129 1130 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_CONFIG_CAVEAT, value); 1131 Log.d(LOG_TAG, " CONFIG_CAVEAT = 0x" + Integer.toHexString(value[0])); 1132 } 1133 1134 GL createEglSurface(Surface surface) throws Surface.OutOfResourcesException { 1135 // Check preconditions. 1136 if (sEgl == null) { 1137 throw new RuntimeException("egl not initialized"); 1138 } 1139 if (sEglDisplay == null) { 1140 throw new RuntimeException("eglDisplay not initialized"); 1141 } 1142 if (sEglConfig == null) { 1143 throw new RuntimeException("eglConfig not initialized"); 1144 } 1145 if (Thread.currentThread() != mEglThread) { 1146 throw new IllegalStateException("HardwareRenderer cannot be used " 1147 + "from multiple threads"); 1148 } 1149 1150 // In case we need to destroy an existing surface 1151 destroySurface(); 1152 1153 // Create an EGL surface we can render into. 1154 if (!createSurface(surface)) { 1155 return null; 1156 } 1157 1158 initCaches(); 1159 1160 return mEglContext.getGL(); 1161 } 1162 1163 private void enableDirtyRegions() { 1164 // If mDirtyRegions is set, this means we have an EGL configuration 1165 // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set 1166 if (sDirtyRegions) { 1167 if (!(mDirtyRegionsEnabled = preserveBackBuffer())) { 1168 Log.w(LOG_TAG, "Backbuffer cannot be preserved"); 1169 } 1170 } else if (sDirtyRegionsRequested) { 1171 // If mDirtyRegions is not set, our EGL configuration does not 1172 // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default 1173 // swap behavior might be EGL_BUFFER_PRESERVED, which means we 1174 // want to set mDirtyRegions. We try to do this only if dirty 1175 // regions were initially requested as part of the device 1176 // configuration (see RENDER_DIRTY_REGIONS) 1177 mDirtyRegionsEnabled = isBackBufferPreserved(); 1178 } 1179 } 1180 1181 abstract void initCaches(); 1182 1183 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { 1184 int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE }; 1185 1186 EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, 1187 mGlVersion != 0 ? attribs : null); 1188 if (context == null || context == EGL_NO_CONTEXT) { 1189 //noinspection ConstantConditions 1190 throw new IllegalStateException( 1191 "Could not create an EGL context. eglCreateContext failed with error: " + 1192 GLUtils.getEGLErrorString(sEgl.eglGetError())); 1193 } 1194 return context; 1195 } 1196 1197 @Override 1198 void destroy(boolean full) { 1199 if (full && mCanvas != null) { 1200 mCanvas = null; 1201 } 1202 1203 if (!isEnabled() || mDestroyed) { 1204 setEnabled(false); 1205 return; 1206 } 1207 1208 destroySurface(); 1209 setEnabled(false); 1210 1211 mDestroyed = true; 1212 mGl = null; 1213 } 1214 1215 void destroySurface() { 1216 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { 1217 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 1218 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 1219 mEglSurface = null; 1220 } 1221 } 1222 1223 @Override 1224 void invalidate(Surface surface) { 1225 // Cancels any existing buffer to ensure we'll get a buffer 1226 // of the right size before we call eglSwapBuffers 1227 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 1228 1229 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { 1230 sEgl.eglDestroySurface(sEglDisplay, mEglSurface); 1231 mEglSurface = null; 1232 setEnabled(false); 1233 } 1234 1235 if (surface.isValid()) { 1236 if (!createSurface(surface)) { 1237 return; 1238 } 1239 1240 mUpdateDirtyRegions = true; 1241 1242 if (mCanvas != null) { 1243 setEnabled(true); 1244 } 1245 } 1246 } 1247 1248 private boolean createSurface(Surface surface) { 1249 mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, surface, null); 1250 1251 if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) { 1252 int error = sEgl.eglGetError(); 1253 if (error == EGL_BAD_NATIVE_WINDOW) { 1254 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 1255 return false; 1256 } 1257 throw new RuntimeException("createWindowSurface failed " 1258 + GLUtils.getEGLErrorString(error)); 1259 } 1260 1261 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 1262 throw new IllegalStateException("eglMakeCurrent failed " + 1263 GLUtils.getEGLErrorString(sEgl.eglGetError())); 1264 } 1265 1266 enableDirtyRegions(); 1267 1268 return true; 1269 } 1270 1271 @Override 1272 boolean validate() { 1273 return checkCurrent() != SURFACE_STATE_ERROR; 1274 } 1275 1276 @Override 1277 void setup(int width, int height) { 1278 if (validate()) { 1279 mCanvas.setViewport(width, height); 1280 mWidth = width; 1281 mHeight = height; 1282 } 1283 } 1284 1285 @Override 1286 int getWidth() { 1287 return mWidth; 1288 } 1289 1290 @Override 1291 int getHeight() { 1292 return mHeight; 1293 } 1294 1295 @Override 1296 HardwareCanvas getCanvas() { 1297 return mCanvas; 1298 } 1299 1300 boolean canDraw() { 1301 return mGl != null && mCanvas != null; 1302 } 1303 1304 int onPreDraw(Rect dirty) { 1305 return DisplayList.STATUS_DONE; 1306 } 1307 1308 void onPostDraw() { 1309 } 1310 1311 class FunctorsRunnable implements Runnable { 1312 View.AttachInfo attachInfo; 1313 1314 @Override 1315 public void run() { 1316 final HardwareRenderer renderer = attachInfo.mHardwareRenderer; 1317 if (renderer == null || !renderer.isEnabled() || renderer != GlRenderer.this) { 1318 return; 1319 } 1320 1321 final int surfaceState = checkCurrent(); 1322 if (surfaceState != SURFACE_STATE_ERROR) { 1323 int status = mCanvas.invokeFunctors(mRedrawClip); 1324 handleFunctorStatus(attachInfo, status); 1325 } 1326 } 1327 } 1328 1329 @Override 1330 boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, 1331 Rect dirty) { 1332 if (canDraw()) { 1333 if (!hasDirtyRegions()) { 1334 dirty = null; 1335 } 1336 attachInfo.mIgnoreDirtyState = true; 1337 attachInfo.mDrawingTime = SystemClock.uptimeMillis(); 1338 1339 view.mPrivateFlags |= View.PFLAG_DRAWN; 1340 1341 final int surfaceState = checkCurrent(); 1342 if (surfaceState != SURFACE_STATE_ERROR) { 1343 HardwareCanvas canvas = mCanvas; 1344 attachInfo.mHardwareCanvas = canvas; 1345 1346 if (mProfileEnabled) { 1347 mProfileLock.lock(); 1348 } 1349 1350 dirty = beginFrame(canvas, dirty, surfaceState); 1351 1352 int saveCount = 0; 1353 int status = DisplayList.STATUS_DONE; 1354 1355 try { 1356 view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) 1357 == View.PFLAG_INVALIDATED; 1358 view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; 1359 1360 long buildDisplayListStartTime = startBuildDisplayListProfiling(); 1361 canvas.clearLayerUpdates(); 1362 1363 DisplayList displayList = buildDisplayList(view); 1364 status = prepareFrame(dirty); 1365 1366 saveCount = canvas.save(); 1367 callbacks.onHardwarePreDraw(canvas); 1368 1369 endBuildDisplayListProfiling(buildDisplayListStartTime); 1370 1371 if (displayList != null) { 1372 status = drawDisplayList(attachInfo, canvas, displayList, status); 1373 } else { 1374 // Shouldn't reach here 1375 view.draw(canvas); 1376 } 1377 } finally { 1378 callbacks.onHardwarePostDraw(canvas); 1379 canvas.restoreToCount(saveCount); 1380 view.mRecreateDisplayList = false; 1381 1382 mFrameCount++; 1383 1384 debugDirtyRegions(dirty, canvas); 1385 drawProfileData(attachInfo); 1386 } 1387 1388 onPostDraw(); 1389 1390 swapBuffers(status); 1391 1392 if (mProfileEnabled) { 1393 mProfileLock.unlock(); 1394 } 1395 1396 attachInfo.mIgnoreDirtyState = false; 1397 return dirty == null; 1398 } 1399 } 1400 1401 return false; 1402 } 1403 1404 abstract void drawProfileData(View.AttachInfo attachInfo); 1405 1406 private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) { 1407 // We had to change the current surface and/or context, redraw everything 1408 if (surfaceState == SURFACE_STATE_UPDATED) { 1409 dirty = null; 1410 beginFrame(null); 1411 } else { 1412 int[] size = mSurfaceSize; 1413 beginFrame(size); 1414 1415 if (size[1] != mHeight || size[0] != mWidth) { 1416 mWidth = size[0]; 1417 mHeight = size[1]; 1418 1419 canvas.setViewport(mWidth, mHeight); 1420 1421 dirty = null; 1422 } 1423 } 1424 1425 if (mDebugDataProvider != null) dirty = null; 1426 1427 return dirty; 1428 } 1429 1430 private long startBuildDisplayListProfiling() { 1431 if (mProfileEnabled) { 1432 mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT; 1433 if (mProfileCurrentFrame >= mProfileData.length) { 1434 mProfileCurrentFrame = 0; 1435 } 1436 1437 return System.nanoTime(); 1438 } 1439 return 0; 1440 } 1441 1442 private void endBuildDisplayListProfiling(long getDisplayListStartTime) { 1443 if (mProfileEnabled) { 1444 long now = System.nanoTime(); 1445 float total = (now - getDisplayListStartTime) * 0.000001f; 1446 //noinspection PointlessArithmeticExpression 1447 mProfileData[mProfileCurrentFrame] = total; 1448 } 1449 } 1450 1451 private static DisplayList buildDisplayList(View view) { 1452 DisplayList displayList; 1453 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList"); 1454 try { 1455 displayList = view.getDisplayList(); 1456 } finally { 1457 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 1458 } 1459 return displayList; 1460 } 1461 1462 private int prepareFrame(Rect dirty) { 1463 int status; 1464 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame"); 1465 try { 1466 status = onPreDraw(dirty); 1467 } finally { 1468 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 1469 } 1470 return status; 1471 } 1472 1473 private int drawDisplayList(View.AttachInfo attachInfo, HardwareCanvas canvas, 1474 DisplayList displayList, int status) { 1475 1476 long drawDisplayListStartTime = 0; 1477 if (mProfileEnabled) { 1478 drawDisplayListStartTime = System.nanoTime(); 1479 } 1480 1481 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList"); 1482 try { 1483 status |= canvas.drawDisplayList(displayList, mRedrawClip, 1484 DisplayList.FLAG_CLIP_CHILDREN); 1485 } finally { 1486 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 1487 } 1488 1489 if (mProfileEnabled) { 1490 long now = System.nanoTime(); 1491 float total = (now - drawDisplayListStartTime) * 0.000001f; 1492 mProfileData[mProfileCurrentFrame + 1] = total; 1493 } 1494 1495 handleFunctorStatus(attachInfo, status); 1496 return status; 1497 } 1498 1499 private void swapBuffers(int status) { 1500 if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) { 1501 long eglSwapBuffersStartTime = 0; 1502 if (mProfileEnabled) { 1503 eglSwapBuffersStartTime = System.nanoTime(); 1504 } 1505 1506 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); 1507 1508 if (mProfileEnabled) { 1509 long now = System.nanoTime(); 1510 float total = (now - eglSwapBuffersStartTime) * 0.000001f; 1511 mProfileData[mProfileCurrentFrame + 2] = total; 1512 } 1513 1514 checkEglErrors(); 1515 } 1516 } 1517 1518 private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) { 1519 if (mDebugDirtyRegions) { 1520 if (mDebugPaint == null) { 1521 mDebugPaint = new Paint(); 1522 mDebugPaint.setColor(0x7fff0000); 1523 } 1524 1525 if (dirty != null && (mFrameCount & 1) == 0) { 1526 canvas.drawRect(dirty, mDebugPaint); 1527 } 1528 } 1529 } 1530 1531 private void handleFunctorStatus(View.AttachInfo attachInfo, int status) { 1532 // If the draw flag is set, functors will be invoked while executing 1533 // the tree of display lists 1534 if ((status & DisplayList.STATUS_DRAW) != 0) { 1535 if (mRedrawClip.isEmpty()) { 1536 attachInfo.mViewRootImpl.invalidate(); 1537 } else { 1538 attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip); 1539 mRedrawClip.setEmpty(); 1540 } 1541 } 1542 1543 if ((status & DisplayList.STATUS_INVOKE) != 0 || 1544 attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) { 1545 attachInfo.mHandler.removeCallbacks(mFunctorsRunnable); 1546 mFunctorsRunnable.attachInfo = attachInfo; 1547 attachInfo.mHandler.postDelayed(mFunctorsRunnable, FUNCTOR_PROCESS_DELAY); 1548 } 1549 } 1550 1551 @Override 1552 void detachFunctor(int functor) { 1553 if (mCanvas != null) { 1554 mCanvas.detachFunctor(functor); 1555 } 1556 } 1557 1558 @Override 1559 boolean attachFunctor(View.AttachInfo attachInfo, int functor) { 1560 if (mCanvas != null) { 1561 mCanvas.attachFunctor(functor); 1562 mFunctorsRunnable.attachInfo = attachInfo; 1563 attachInfo.mHandler.removeCallbacks(mFunctorsRunnable); 1564 attachInfo.mHandler.postDelayed(mFunctorsRunnable, 0); 1565 return true; 1566 } 1567 return false; 1568 } 1569 1570 /** 1571 * Ensures the current EGL context is the one we expect. 1572 * 1573 * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current, 1574 * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or 1575 * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one 1576 */ 1577 int checkCurrent() { 1578 if (mEglThread != Thread.currentThread()) { 1579 throw new IllegalStateException("Hardware acceleration can only be used with a " + 1580 "single UI thread.\nOriginal thread: " + mEglThread + "\n" + 1581 "Current thread: " + Thread.currentThread()); 1582 } 1583 1584 if (!mEglContext.equals(sEgl.eglGetCurrentContext()) || 1585 !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) { 1586 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 1587 Log.e(LOG_TAG, "eglMakeCurrent failed " + 1588 GLUtils.getEGLErrorString(sEgl.eglGetError())); 1589 fallback(true); 1590 return SURFACE_STATE_ERROR; 1591 } else { 1592 if (mUpdateDirtyRegions) { 1593 enableDirtyRegions(); 1594 mUpdateDirtyRegions = false; 1595 } 1596 return SURFACE_STATE_UPDATED; 1597 } 1598 } 1599 return SURFACE_STATE_SUCCESS; 1600 } 1601 1602 private static int dpToPx(int dp, float density) { 1603 return (int) (dp * density + 0.5f); 1604 } 1605 1606 class DrawPerformanceDataProvider extends GraphDataProvider { 1607 private final int mGraphType; 1608 1609 private int mVerticalUnit; 1610 private int mHorizontalUnit; 1611 private int mHorizontalMargin; 1612 private int mThresholdStroke; 1613 1614 DrawPerformanceDataProvider(int graphType) { 1615 mGraphType = graphType; 1616 } 1617 1618 @Override 1619 void prepare(DisplayMetrics metrics) { 1620 final float density = metrics.density; 1621 1622 mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density); 1623 mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density); 1624 mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density); 1625 mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density); 1626 } 1627 1628 @Override 1629 int getGraphType() { 1630 return mGraphType; 1631 } 1632 1633 @Override 1634 int getVerticalUnitSize() { 1635 return mVerticalUnit; 1636 } 1637 1638 @Override 1639 int getHorizontalUnitSize() { 1640 return mHorizontalUnit; 1641 } 1642 1643 @Override 1644 int getHorizontaUnitMargin() { 1645 return mHorizontalMargin; 1646 } 1647 1648 @Override 1649 float[] getData() { 1650 return mProfileData; 1651 } 1652 1653 @Override 1654 float getThreshold() { 1655 return 16; 1656 } 1657 1658 @Override 1659 int getFrameCount() { 1660 return mProfileData.length / PROFILE_FRAME_DATA_COUNT; 1661 } 1662 1663 @Override 1664 int getElementCount() { 1665 return PROFILE_FRAME_DATA_COUNT; 1666 } 1667 1668 @Override 1669 int getCurrentFrame() { 1670 return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT; 1671 } 1672 1673 @Override 1674 void setupGraphPaint(Paint paint, int elementIndex) { 1675 paint.setColor(PROFILE_DRAW_COLORS[elementIndex]); 1676 if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke); 1677 } 1678 1679 @Override 1680 void setupThresholdPaint(Paint paint) { 1681 paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR); 1682 paint.setStrokeWidth(mThresholdStroke); 1683 } 1684 1685 @Override 1686 void setupCurrentFramePaint(Paint paint) { 1687 paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR); 1688 if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke); 1689 } 1690 } 1691 } 1692 1693 /** 1694 * Hardware renderer using OpenGL ES 2.0. 1695 */ 1696 static class Gl20Renderer extends GlRenderer { 1697 private GLES20Canvas mGlCanvas; 1698 1699 private DisplayMetrics mDisplayMetrics; 1700 1701 private static EGLSurface sPbuffer; 1702 private static final Object[] sPbufferLock = new Object[0]; 1703 1704 static class Gl20RendererEglContext extends ManagedEGLContext { 1705 final Handler mHandler = new Handler(); 1706 1707 public Gl20RendererEglContext(EGLContext context) { 1708 super(context); 1709 } 1710 1711 @Override 1712 public void onTerminate(final EGLContext eglContext) { 1713 // Make sure we do this on the correct thread. 1714 if (mHandler.getLooper() != Looper.myLooper()) { 1715 mHandler.post(new Runnable() { 1716 @Override 1717 public void run() { 1718 onTerminate(eglContext); 1719 } 1720 }); 1721 return; 1722 } 1723 1724 synchronized (sEglLock) { 1725 if (sEgl == null) return; 1726 1727 if (EGLImpl.getInitCount(sEglDisplay) == 1) { 1728 usePbufferSurface(eglContext); 1729 GLES20Canvas.terminateCaches(); 1730 1731 sEgl.eglDestroyContext(sEglDisplay, eglContext); 1732 sEglContextStorage.set(null); 1733 sEglContextStorage.remove(); 1734 1735 sEgl.eglDestroySurface(sEglDisplay, sPbuffer); 1736 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, 1737 EGL_NO_SURFACE, EGL_NO_CONTEXT); 1738 1739 sEgl.eglReleaseThread(); 1740 sEgl.eglTerminate(sEglDisplay); 1741 1742 sEgl = null; 1743 sEglDisplay = null; 1744 sEglConfig = null; 1745 sPbuffer = null; 1746 } 1747 } 1748 } 1749 } 1750 1751 Gl20Renderer(boolean translucent) { 1752 super(2, translucent); 1753 } 1754 1755 @Override 1756 HardwareCanvas createCanvas() { 1757 return mGlCanvas = new GLES20Canvas(mTranslucent); 1758 } 1759 1760 @Override 1761 ManagedEGLContext createManagedContext(EGLContext eglContext) { 1762 return new Gl20Renderer.Gl20RendererEglContext(mEglContext); 1763 } 1764 1765 @Override 1766 int[] getConfig(boolean dirtyRegions) { 1767 //noinspection PointlessBooleanExpression 1768 final int stencilSize = mShowOverdraw || REGION_CLIPPING_ENABLED ? 1769 GLES20Canvas.getStencilSize() : 0; 1770 final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; 1771 1772 return new int[] { 1773 EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, 1774 EGL_RED_SIZE, 8, 1775 EGL_GREEN_SIZE, 8, 1776 EGL_BLUE_SIZE, 8, 1777 EGL_ALPHA_SIZE, 8, 1778 EGL_DEPTH_SIZE, 0, 1779 EGL_CONFIG_CAVEAT, EGL_NONE, 1780 EGL_STENCIL_SIZE, stencilSize, 1781 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, 1782 EGL_NONE 1783 }; 1784 } 1785 1786 @Override 1787 void initCaches() { 1788 GLES20Canvas.initCaches(); 1789 } 1790 1791 @Override 1792 boolean canDraw() { 1793 return super.canDraw() && mGlCanvas != null; 1794 } 1795 1796 @Override 1797 int onPreDraw(Rect dirty) { 1798 return mGlCanvas.onPreDraw(dirty); 1799 } 1800 1801 @Override 1802 void onPostDraw() { 1803 mGlCanvas.onPostDraw(); 1804 } 1805 1806 @Override 1807 void drawProfileData(View.AttachInfo attachInfo) { 1808 if (mDebugDataProvider != null) { 1809 final GraphDataProvider provider = mDebugDataProvider; 1810 initProfileDrawData(attachInfo, provider); 1811 1812 final int height = provider.getVerticalUnitSize(); 1813 final int margin = provider.getHorizontaUnitMargin(); 1814 final int width = provider.getHorizontalUnitSize(); 1815 1816 int x = 0; 1817 int count = 0; 1818 int current = 0; 1819 1820 final float[] data = provider.getData(); 1821 final int elementCount = provider.getElementCount(); 1822 final int graphType = provider.getGraphType(); 1823 1824 int totalCount = provider.getFrameCount() * elementCount; 1825 if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) { 1826 totalCount -= elementCount; 1827 } 1828 1829 for (int i = 0; i < totalCount; i += elementCount) { 1830 if (data[i] < 0.0f) break; 1831 1832 int index = count * 4; 1833 if (i == provider.getCurrentFrame() * elementCount) current = index; 1834 1835 x += margin; 1836 int x2 = x + width; 1837 1838 int y2 = mHeight; 1839 int y1 = (int) (y2 - data[i] * height); 1840 1841 switch (graphType) { 1842 case GraphDataProvider.GRAPH_TYPE_BARS: { 1843 for (int j = 0; j < elementCount; j++) { 1844 //noinspection MismatchedReadAndWriteOfArray 1845 final float[] r = mProfileShapes[j]; 1846 r[index] = x; 1847 r[index + 1] = y1; 1848 r[index + 2] = x2; 1849 r[index + 3] = y2; 1850 1851 y2 = y1; 1852 if (j < elementCount - 1) { 1853 y1 = (int) (y2 - data[i + j + 1] * height); 1854 } 1855 } 1856 } break; 1857 case GraphDataProvider.GRAPH_TYPE_LINES: { 1858 for (int j = 0; j < elementCount; j++) { 1859 //noinspection MismatchedReadAndWriteOfArray 1860 final float[] r = mProfileShapes[j]; 1861 r[index] = (x + x2) * 0.5f; 1862 r[index + 1] = index == 0 ? y1 : r[index - 1]; 1863 r[index + 2] = r[index] + width; 1864 r[index + 3] = y1; 1865 1866 y2 = y1; 1867 if (j < elementCount - 1) { 1868 y1 = (int) (y2 - data[i + j + 1] * height); 1869 } 1870 } 1871 } break; 1872 } 1873 1874 1875 x += width; 1876 count++; 1877 } 1878 1879 x += margin; 1880 1881 drawGraph(graphType, count); 1882 drawCurrentFrame(graphType, current); 1883 drawThreshold(x, height); 1884 } 1885 } 1886 1887 private void drawGraph(int graphType, int count) { 1888 for (int i = 0; i < mProfileShapes.length; i++) { 1889 mDebugDataProvider.setupGraphPaint(mProfilePaint, i); 1890 switch (graphType) { 1891 case GraphDataProvider.GRAPH_TYPE_BARS: 1892 mGlCanvas.drawRects(mProfileShapes[i], count, mProfilePaint); 1893 break; 1894 case GraphDataProvider.GRAPH_TYPE_LINES: 1895 mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint); 1896 break; 1897 } 1898 } 1899 } 1900 1901 private void drawCurrentFrame(int graphType, int index) { 1902 if (index >= 0) { 1903 mDebugDataProvider.setupCurrentFramePaint(mProfilePaint); 1904 switch (graphType) { 1905 case GraphDataProvider.GRAPH_TYPE_BARS: 1906 mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1], 1907 mProfileShapes[2][index + 2], mProfileShapes[0][index + 3], 1908 mProfilePaint); 1909 break; 1910 case GraphDataProvider.GRAPH_TYPE_LINES: 1911 mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1], 1912 mProfileShapes[2][index], mHeight, mProfilePaint); 1913 break; 1914 } 1915 } 1916 } 1917 1918 private void drawThreshold(int x, int height) { 1919 float threshold = mDebugDataProvider.getThreshold(); 1920 if (threshold > 0.0f) { 1921 mDebugDataProvider.setupThresholdPaint(mProfilePaint); 1922 int y = (int) (mHeight - threshold * height); 1923 mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint); 1924 } 1925 } 1926 1927 private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) { 1928 if (mProfileShapes == null) { 1929 final int elementCount = provider.getElementCount(); 1930 final int frameCount = provider.getFrameCount(); 1931 1932 mProfileShapes = new float[elementCount][]; 1933 for (int i = 0; i < elementCount; i++) { 1934 mProfileShapes[i] = new float[frameCount * 4]; 1935 } 1936 1937 mProfilePaint = new Paint(); 1938 } 1939 1940 mProfilePaint.reset(); 1941 if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) { 1942 mProfilePaint.setAntiAlias(true); 1943 } 1944 1945 if (mDisplayMetrics == null) { 1946 mDisplayMetrics = new DisplayMetrics(); 1947 } 1948 1949 attachInfo.mDisplay.getMetrics(mDisplayMetrics); 1950 provider.prepare(mDisplayMetrics); 1951 } 1952 1953 @Override 1954 void destroy(boolean full) { 1955 try { 1956 super.destroy(full); 1957 } finally { 1958 if (full && mGlCanvas != null) { 1959 mGlCanvas = null; 1960 } 1961 } 1962 } 1963 1964 @Override 1965 void pushLayerUpdate(HardwareLayer layer) { 1966 mGlCanvas.pushLayerUpdate(layer); 1967 } 1968 1969 @Override 1970 public DisplayList createDisplayList(String name) { 1971 return new GLES20DisplayList(name); 1972 } 1973 1974 @Override 1975 HardwareLayer createHardwareLayer(boolean isOpaque) { 1976 return new GLES20TextureLayer(isOpaque); 1977 } 1978 1979 @Override 1980 HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { 1981 return new GLES20RenderLayer(width, height, isOpaque); 1982 } 1983 1984 @Override 1985 SurfaceTexture createSurfaceTexture(HardwareLayer layer) { 1986 return ((GLES20TextureLayer) layer).getSurfaceTexture(); 1987 } 1988 1989 @Override 1990 void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) { 1991 ((GLES20TextureLayer) layer).setSurfaceTexture(surfaceTexture); 1992 } 1993 1994 @Override 1995 boolean safelyRun(Runnable action) { 1996 boolean needsContext = true; 1997 if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false; 1998 1999 if (needsContext) { 2000 Gl20RendererEglContext managedContext = 2001 (Gl20RendererEglContext) sEglContextStorage.get(); 2002 if (managedContext == null) return false; 2003 usePbufferSurface(managedContext.getContext()); 2004 } 2005 2006 try { 2007 action.run(); 2008 } finally { 2009 if (needsContext) { 2010 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, 2011 EGL_NO_SURFACE, EGL_NO_CONTEXT); 2012 } 2013 } 2014 2015 return true; 2016 } 2017 2018 @Override 2019 void destroyLayers(final View view) { 2020 if (view != null) { 2021 safelyRun(new Runnable() { 2022 @Override 2023 public void run() { 2024 if (mCanvas != null) { 2025 mCanvas.clearLayerUpdates(); 2026 } 2027 destroyHardwareLayer(view); 2028 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); 2029 } 2030 }); 2031 } 2032 } 2033 2034 private static void destroyHardwareLayer(View view) { 2035 view.destroyLayer(true); 2036 2037 if (view instanceof ViewGroup) { 2038 ViewGroup group = (ViewGroup) view; 2039 2040 int count = group.getChildCount(); 2041 for (int i = 0; i < count; i++) { 2042 destroyHardwareLayer(group.getChildAt(i)); 2043 } 2044 } 2045 } 2046 2047 @Override 2048 void destroyHardwareResources(final View view) { 2049 if (view != null) { 2050 safelyRun(new Runnable() { 2051 @Override 2052 public void run() { 2053 if (mCanvas != null) { 2054 mCanvas.clearLayerUpdates(); 2055 } 2056 destroyResources(view); 2057 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); 2058 } 2059 }); 2060 } 2061 } 2062 2063 private static void destroyResources(View view) { 2064 view.destroyHardwareResources(); 2065 2066 if (view instanceof ViewGroup) { 2067 ViewGroup group = (ViewGroup) view; 2068 2069 int count = group.getChildCount(); 2070 for (int i = 0; i < count; i++) { 2071 destroyResources(group.getChildAt(i)); 2072 } 2073 } 2074 } 2075 2076 static HardwareRenderer create(boolean translucent) { 2077 if (GLES20Canvas.isAvailable()) { 2078 return new Gl20Renderer(translucent); 2079 } 2080 return null; 2081 } 2082 2083 static void startTrimMemory(int level) { 2084 if (sEgl == null || sEglConfig == null) return; 2085 2086 Gl20RendererEglContext managedContext = 2087 (Gl20RendererEglContext) sEglContextStorage.get(); 2088 // We do not have OpenGL objects 2089 if (managedContext == null) { 2090 return; 2091 } else { 2092 usePbufferSurface(managedContext.getContext()); 2093 } 2094 2095 if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { 2096 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL); 2097 } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { 2098 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE); 2099 } 2100 } 2101 2102 static void endTrimMemory() { 2103 if (sEgl != null && sEglDisplay != null) { 2104 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 2105 } 2106 } 2107 2108 private static void usePbufferSurface(EGLContext eglContext) { 2109 synchronized (sPbufferLock) { 2110 // Create a temporary 1x1 pbuffer so we have a context 2111 // to clear our OpenGL objects 2112 if (sPbuffer == null) { 2113 sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] { 2114 EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE 2115 }); 2116 } 2117 } 2118 sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext); 2119 } 2120 } 2121} 2122