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