1 /* 2 * Copyright (C) 2011 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 static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP; 20 import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER; 21 22 import android.annotation.TestApi; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.graphics.FrameInfo; 25 import android.graphics.Insets; 26 import android.hardware.display.DisplayManagerGlobal; 27 import android.os.Build; 28 import android.os.Handler; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.os.SystemClock; 32 import android.os.SystemProperties; 33 import android.os.Trace; 34 import android.util.Log; 35 import android.util.TimeUtils; 36 import android.view.animation.AnimationUtils; 37 38 import java.io.PrintWriter; 39 40 /** 41 * Coordinates the timing of animations, input and drawing. 42 * <p> 43 * The choreographer receives timing pulses (such as vertical synchronization) 44 * from the display subsystem then schedules work to occur as part of rendering 45 * the next display frame. 46 * </p><p> 47 * Applications typically interact with the choreographer indirectly using 48 * higher level abstractions in the animation framework or the view hierarchy. 49 * Here are some examples of things you can do using the higher-level APIs. 50 * </p> 51 * <ul> 52 * <li>To post an animation to be processed on a regular time basis synchronized with 53 * display frame rendering, use {@link android.animation.ValueAnimator#start}.</li> 54 * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display 55 * frame, use {@link View#postOnAnimation}.</li> 56 * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display 57 * frame after a delay, use {@link View#postOnAnimationDelayed}.</li> 58 * <li>To post a call to {@link View#invalidate()} to occur once at the beginning of the 59 * next display frame, use {@link View#postInvalidateOnAnimation()} or 60 * {@link View#postInvalidateOnAnimation(int, int, int, int)}.</li> 61 * <li>To ensure that the contents of a {@link View} scroll smoothly and are drawn in 62 * sync with display frame rendering, do nothing. This already happens automatically. 63 * {@link View#onDraw} will be called at the appropriate time.</li> 64 * </ul> 65 * <p> 66 * However, there are a few cases where you might want to use the functions of the 67 * choreographer directly in your application. Here are some examples. 68 * </p> 69 * <ul> 70 * <li>If your application does its rendering in a different thread, possibly using GL, 71 * or does not use the animation framework or view hierarchy at all 72 * and you want to ensure that it is appropriately synchronized with the display, then use 73 * {@link Choreographer#postFrameCallback}.</li> 74 * <li>... and that's about it.</li> 75 * </ul> 76 * <p> 77 * Each {@link Looper} thread has its own choreographer. Other threads can 78 * post callbacks to run on the choreographer but they will run on the {@link Looper} 79 * to which the choreographer belongs. 80 * </p> 81 */ 82 public final class Choreographer { 83 private static final String TAG = "Choreographer"; 84 85 // Prints debug messages about jank which was detected (low volume). 86 private static final boolean DEBUG_JANK = false; 87 88 // Prints debug messages about every frame and callback registered (high volume). 89 private static final boolean DEBUG_FRAMES = false; 90 91 // The default amount of time in ms between animation frames. 92 // When vsync is not enabled, we want to have some idea of how long we should 93 // wait before posting the next animation message. It is important that the 94 // default value be less than the true inter-frame delay on all devices to avoid 95 // situations where we might skip frames by waiting too long (we must compensate 96 // for jitter and hardware variations). Regardless of this value, the animation 97 // and display loop is ultimately rate-limited by how fast new graphics buffers can 98 // be dequeued. 99 private static final long DEFAULT_FRAME_DELAY = 10; 100 101 // The number of milliseconds between animation frames. 102 private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY; 103 104 // Thread local storage for the choreographer. 105 private static final ThreadLocal<Choreographer> sThreadInstance = 106 new ThreadLocal<Choreographer>() { 107 @Override 108 protected Choreographer initialValue() { 109 Looper looper = Looper.myLooper(); 110 if (looper == null) { 111 throw new IllegalStateException("The current thread must have a looper!"); 112 } 113 Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP); 114 if (looper == Looper.getMainLooper()) { 115 mMainInstance = choreographer; 116 } 117 return choreographer; 118 } 119 }; 120 121 private static volatile Choreographer mMainInstance; 122 123 // Thread local storage for the SF choreographer. 124 private static final ThreadLocal<Choreographer> sSfThreadInstance = 125 new ThreadLocal<Choreographer>() { 126 @Override 127 protected Choreographer initialValue() { 128 Looper looper = Looper.myLooper(); 129 if (looper == null) { 130 throw new IllegalStateException("The current thread must have a looper!"); 131 } 132 return new Choreographer(looper, VSYNC_SOURCE_SURFACE_FLINGER); 133 } 134 }; 135 136 // Enable/disable vsync for animations and drawing. 137 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769497) 138 private static final boolean USE_VSYNC = SystemProperties.getBoolean( 139 "debug.choreographer.vsync", true); 140 141 // Enable/disable using the frame time instead of returning now. 142 private static final boolean USE_FRAME_TIME = SystemProperties.getBoolean( 143 "debug.choreographer.frametime", true); 144 145 // Set a limit to warn about skipped frames. 146 // Skipped frames imply jank. 147 private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt( 148 "debug.choreographer.skipwarning", 30); 149 150 private static final int MSG_DO_FRAME = 0; 151 private static final int MSG_DO_SCHEDULE_VSYNC = 1; 152 private static final int MSG_DO_SCHEDULE_CALLBACK = 2; 153 154 // All frame callbacks posted by applications have this token. 155 private static final Object FRAME_CALLBACK_TOKEN = new Object() { 156 public String toString() { return "FRAME_CALLBACK_TOKEN"; } 157 }; 158 159 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 160 private final Object mLock = new Object(); 161 162 private final Looper mLooper; 163 private final FrameHandler mHandler; 164 165 // The display event receiver can only be accessed by the looper thread to which 166 // it is attached. We take care to ensure that we post message to the looper 167 // if appropriate when interacting with the display event receiver. 168 @UnsupportedAppUsage 169 private final FrameDisplayEventReceiver mDisplayEventReceiver; 170 171 private CallbackRecord mCallbackPool; 172 173 @UnsupportedAppUsage 174 private final CallbackQueue[] mCallbackQueues; 175 176 private boolean mFrameScheduled; 177 private boolean mCallbacksRunning; 178 @UnsupportedAppUsage 179 private long mLastFrameTimeNanos; 180 @UnsupportedAppUsage 181 private long mFrameIntervalNanos; 182 private boolean mDebugPrintNextFrameTimeDelta; 183 private int mFPSDivisor = 1; 184 185 /** 186 * Contains information about the current frame for jank-tracking, 187 * mainly timings of key events along with a bit of metadata about 188 * view tree state 189 * 190 * TODO: Is there a better home for this? Currently Choreographer 191 * is the only one with CALLBACK_ANIMATION start time, hence why this 192 * resides here. 193 * 194 * @hide 195 */ 196 FrameInfo mFrameInfo = new FrameInfo(); 197 198 /** 199 * Must be kept in sync with CALLBACK_* ints below, used to index into this array. 200 * @hide 201 */ 202 private static final String[] CALLBACK_TRACE_TITLES = { 203 "input", "animation", "insets_animation", "traversal", "commit" 204 }; 205 206 /** 207 * Callback type: Input callback. Runs first. 208 * @hide 209 */ 210 public static final int CALLBACK_INPUT = 0; 211 212 /** 213 * Callback type: Animation callback. Runs before {@link #CALLBACK_INSETS_ANIMATION}. 214 * @hide 215 */ 216 @TestApi 217 public static final int CALLBACK_ANIMATION = 1; 218 219 /** 220 * Callback type: Animation callback to handle inset updates. This is separate from 221 * {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via 222 * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} for multiple 223 * ongoing animations but then update the whole view system with a single callback to 224 * {@link View#dispatchWindowInsetsAnimationProgress} that contains all the combined updated 225 * insets. 226 * <p> 227 * Both input and animation may change insets, so we need to run this after these callbacks, but 228 * before traversals. 229 * <p> 230 * Runs before traversals. 231 * @hide 232 */ 233 public static final int CALLBACK_INSETS_ANIMATION = 2; 234 235 /** 236 * Callback type: Traversal callback. Handles layout and draw. Runs 237 * after all other asynchronous messages have been handled. 238 * @hide 239 */ 240 public static final int CALLBACK_TRAVERSAL = 3; 241 242 /** 243 * Callback type: Commit callback. Handles post-draw operations for the frame. 244 * Runs after traversal completes. The {@link #getFrameTime() frame time} reported 245 * during this callback may be updated to reflect delays that occurred while 246 * traversals were in progress in case heavy layout operations caused some frames 247 * to be skipped. The frame time reported during this callback provides a better 248 * estimate of the start time of the frame in which animations (and other updates 249 * to the view hierarchy state) actually took effect. 250 * @hide 251 */ 252 public static final int CALLBACK_COMMIT = 4; 253 254 private static final int CALLBACK_LAST = CALLBACK_COMMIT; 255 Choreographer(Looper looper, int vsyncSource)256 private Choreographer(Looper looper, int vsyncSource) { 257 mLooper = looper; 258 mHandler = new FrameHandler(looper); 259 mDisplayEventReceiver = USE_VSYNC 260 ? new FrameDisplayEventReceiver(looper, vsyncSource) 261 : null; 262 mLastFrameTimeNanos = Long.MIN_VALUE; 263 264 mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); 265 266 mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; 267 for (int i = 0; i <= CALLBACK_LAST; i++) { 268 mCallbackQueues[i] = new CallbackQueue(); 269 } 270 // b/68769804: For low FPS experiments. 271 setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1)); 272 } 273 getRefreshRate()274 private static float getRefreshRate() { 275 DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo( 276 Display.DEFAULT_DISPLAY); 277 return di.getMode().getRefreshRate(); 278 } 279 280 /** 281 * Gets the choreographer for the calling thread. Must be called from 282 * a thread that already has a {@link android.os.Looper} associated with it. 283 * 284 * @return The choreographer for this thread. 285 * @throws IllegalStateException if the thread does not have a looper. 286 */ getInstance()287 public static Choreographer getInstance() { 288 return sThreadInstance.get(); 289 } 290 291 /** 292 * @hide 293 */ 294 @UnsupportedAppUsage getSfInstance()295 public static Choreographer getSfInstance() { 296 return sSfThreadInstance.get(); 297 } 298 299 /** 300 * @return The Choreographer of the main thread, if it exists, or {@code null} otherwise. 301 * @hide 302 */ getMainThreadInstance()303 public static Choreographer getMainThreadInstance() { 304 return mMainInstance; 305 } 306 307 /** Destroys the calling thread's choreographer 308 * @hide 309 */ releaseInstance()310 public static void releaseInstance() { 311 Choreographer old = sThreadInstance.get(); 312 sThreadInstance.remove(); 313 old.dispose(); 314 } 315 dispose()316 private void dispose() { 317 mDisplayEventReceiver.dispose(); 318 } 319 320 /** 321 * The amount of time, in milliseconds, between each frame of the animation. 322 * <p> 323 * This is a requested time that the animation will attempt to honor, but the actual delay 324 * between frames may be different, depending on system load and capabilities. This is a static 325 * function because the same delay will be applied to all animations, since they are all 326 * run off of a single timing loop. 327 * </p><p> 328 * The frame delay may be ignored when the animation system uses an external timing 329 * source, such as the display refresh rate (vsync), to govern animations. 330 * </p> 331 * 332 * @return the requested time between frames, in milliseconds 333 * @hide 334 */ 335 @UnsupportedAppUsage 336 @TestApi getFrameDelay()337 public static long getFrameDelay() { 338 return sFrameDelay; 339 } 340 341 /** 342 * The amount of time, in milliseconds, between each frame of the animation. 343 * <p> 344 * This is a requested time that the animation will attempt to honor, but the actual delay 345 * between frames may be different, depending on system load and capabilities. This is a static 346 * function because the same delay will be applied to all animations, since they are all 347 * run off of a single timing loop. 348 * </p><p> 349 * The frame delay may be ignored when the animation system uses an external timing 350 * source, such as the display refresh rate (vsync), to govern animations. 351 * </p> 352 * 353 * @param frameDelay the requested time between frames, in milliseconds 354 * @hide 355 */ 356 @TestApi setFrameDelay(long frameDelay)357 public static void setFrameDelay(long frameDelay) { 358 sFrameDelay = frameDelay; 359 } 360 361 /** 362 * Subtracts typical frame delay time from a delay interval in milliseconds. 363 * <p> 364 * This method can be used to compensate for animation delay times that have baked 365 * in assumptions about the frame delay. For example, it's quite common for code to 366 * assume a 60Hz frame time and bake in a 16ms delay. When we call 367 * {@link #postAnimationCallbackDelayed} we want to know how long to wait before 368 * posting the animation callback but let the animation timer take care of the remaining 369 * frame delay time. 370 * </p><p> 371 * This method is somewhat conservative about how much of the frame delay it 372 * subtracts. It uses the same value returned by {@link #getFrameDelay} which by 373 * default is 10ms even though many parts of the system assume 16ms. Consequently, 374 * we might still wait 6ms before posting an animation callback that we want to run 375 * on the next frame, but this is much better than waiting a whole 16ms and likely 376 * missing the deadline. 377 * </p> 378 * 379 * @param delayMillis The original delay time including an assumed frame delay. 380 * @return The adjusted delay time with the assumed frame delay subtracted out. 381 * @hide 382 */ subtractFrameDelay(long delayMillis)383 public static long subtractFrameDelay(long delayMillis) { 384 final long frameDelay = sFrameDelay; 385 return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay; 386 } 387 388 /** 389 * @return The refresh rate as the nanoseconds between frames 390 * @hide 391 */ getFrameIntervalNanos()392 public long getFrameIntervalNanos() { 393 return mFrameIntervalNanos; 394 } 395 dump(String prefix, PrintWriter writer)396 void dump(String prefix, PrintWriter writer) { 397 String innerPrefix = prefix + " "; 398 writer.print(prefix); writer.println("Choreographer:"); 399 writer.print(innerPrefix); writer.print("mFrameScheduled="); 400 writer.println(mFrameScheduled); 401 writer.print(innerPrefix); writer.print("mLastFrameTime="); 402 writer.println(TimeUtils.formatUptime(mLastFrameTimeNanos / 1000000)); 403 } 404 405 /** 406 * Posts a callback to run on the next frame. 407 * <p> 408 * The callback runs once then is automatically removed. 409 * </p> 410 * 411 * @param callbackType The callback type. 412 * @param action The callback action to run during the next frame. 413 * @param token The callback token, or null if none. 414 * 415 * @see #removeCallbacks 416 * @hide 417 */ 418 @UnsupportedAppUsage 419 @TestApi postCallback(int callbackType, Runnable action, Object token)420 public void postCallback(int callbackType, Runnable action, Object token) { 421 postCallbackDelayed(callbackType, action, token, 0); 422 } 423 424 /** 425 * Posts a callback to run on the next frame after the specified delay. 426 * <p> 427 * The callback runs once then is automatically removed. 428 * </p> 429 * 430 * @param callbackType The callback type. 431 * @param action The callback action to run during the next frame after the specified delay. 432 * @param token The callback token, or null if none. 433 * @param delayMillis The delay time in milliseconds. 434 * 435 * @see #removeCallback 436 * @hide 437 */ 438 @UnsupportedAppUsage 439 @TestApi postCallbackDelayed(int callbackType, Runnable action, Object token, long delayMillis)440 public void postCallbackDelayed(int callbackType, 441 Runnable action, Object token, long delayMillis) { 442 if (action == null) { 443 throw new IllegalArgumentException("action must not be null"); 444 } 445 if (callbackType < 0 || callbackType > CALLBACK_LAST) { 446 throw new IllegalArgumentException("callbackType is invalid"); 447 } 448 449 postCallbackDelayedInternal(callbackType, action, token, delayMillis); 450 } 451 postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis)452 private void postCallbackDelayedInternal(int callbackType, 453 Object action, Object token, long delayMillis) { 454 if (DEBUG_FRAMES) { 455 Log.d(TAG, "PostCallback: type=" + callbackType 456 + ", action=" + action + ", token=" + token 457 + ", delayMillis=" + delayMillis); 458 } 459 460 synchronized (mLock) { 461 final long now = SystemClock.uptimeMillis(); 462 final long dueTime = now + delayMillis; 463 mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); 464 465 if (dueTime <= now) { 466 scheduleFrameLocked(now); 467 } else { 468 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); 469 msg.arg1 = callbackType; 470 msg.setAsynchronous(true); 471 mHandler.sendMessageAtTime(msg, dueTime); 472 } 473 } 474 } 475 476 /** 477 * Removes callbacks that have the specified action and token. 478 * 479 * @param callbackType The callback type. 480 * @param action The action property of the callbacks to remove, or null to remove 481 * callbacks with any action. 482 * @param token The token property of the callbacks to remove, or null to remove 483 * callbacks with any token. 484 * 485 * @see #postCallback 486 * @see #postCallbackDelayed 487 * @hide 488 */ 489 @UnsupportedAppUsage 490 @TestApi removeCallbacks(int callbackType, Runnable action, Object token)491 public void removeCallbacks(int callbackType, Runnable action, Object token) { 492 if (callbackType < 0 || callbackType > CALLBACK_LAST) { 493 throw new IllegalArgumentException("callbackType is invalid"); 494 } 495 496 removeCallbacksInternal(callbackType, action, token); 497 } 498 removeCallbacksInternal(int callbackType, Object action, Object token)499 private void removeCallbacksInternal(int callbackType, Object action, Object token) { 500 if (DEBUG_FRAMES) { 501 Log.d(TAG, "RemoveCallbacks: type=" + callbackType 502 + ", action=" + action + ", token=" + token); 503 } 504 505 synchronized (mLock) { 506 mCallbackQueues[callbackType].removeCallbacksLocked(action, token); 507 if (action != null && token == null) { 508 mHandler.removeMessages(MSG_DO_SCHEDULE_CALLBACK, action); 509 } 510 } 511 } 512 513 /** 514 * Posts a frame callback to run on the next frame. 515 * <p> 516 * The callback runs once then is automatically removed. 517 * </p> 518 * 519 * @param callback The frame callback to run during the next frame. 520 * 521 * @see #postFrameCallbackDelayed 522 * @see #removeFrameCallback 523 */ postFrameCallback(FrameCallback callback)524 public void postFrameCallback(FrameCallback callback) { 525 postFrameCallbackDelayed(callback, 0); 526 } 527 528 /** 529 * Posts a frame callback to run on the next frame after the specified delay. 530 * <p> 531 * The callback runs once then is automatically removed. 532 * </p> 533 * 534 * @param callback The frame callback to run during the next frame. 535 * @param delayMillis The delay time in milliseconds. 536 * 537 * @see #postFrameCallback 538 * @see #removeFrameCallback 539 */ postFrameCallbackDelayed(FrameCallback callback, long delayMillis)540 public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) { 541 if (callback == null) { 542 throw new IllegalArgumentException("callback must not be null"); 543 } 544 545 postCallbackDelayedInternal(CALLBACK_ANIMATION, 546 callback, FRAME_CALLBACK_TOKEN, delayMillis); 547 } 548 549 /** 550 * Removes a previously posted frame callback. 551 * 552 * @param callback The frame callback to remove. 553 * 554 * @see #postFrameCallback 555 * @see #postFrameCallbackDelayed 556 */ removeFrameCallback(FrameCallback callback)557 public void removeFrameCallback(FrameCallback callback) { 558 if (callback == null) { 559 throw new IllegalArgumentException("callback must not be null"); 560 } 561 562 removeCallbacksInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN); 563 } 564 565 /** 566 * Gets the time when the current frame started. 567 * <p> 568 * This method provides the time in milliseconds when the frame started being rendered. 569 * The frame time provides a stable time base for synchronizing animations 570 * and drawing. It should be used instead of {@link SystemClock#uptimeMillis()} 571 * or {@link System#nanoTime()} for animations and drawing in the UI. Using the frame 572 * time helps to reduce inter-frame jitter because the frame time is fixed at the time 573 * the frame was scheduled to start, regardless of when the animations or drawing 574 * callback actually runs. All callbacks that run as part of rendering a frame will 575 * observe the same frame time so using the frame time also helps to synchronize effects 576 * that are performed by different callbacks. 577 * </p><p> 578 * Please note that the framework already takes care to process animations and 579 * drawing using the frame time as a stable time base. Most applications should 580 * not need to use the frame time information directly. 581 * </p><p> 582 * This method should only be called from within a callback. 583 * </p> 584 * 585 * @return The frame start time, in the {@link SystemClock#uptimeMillis()} time base. 586 * 587 * @throws IllegalStateException if no frame is in progress. 588 * @hide 589 */ 590 @UnsupportedAppUsage getFrameTime()591 public long getFrameTime() { 592 return getFrameTimeNanos() / TimeUtils.NANOS_PER_MS; 593 } 594 595 /** 596 * Same as {@link #getFrameTime()} but with nanosecond precision. 597 * 598 * @return The frame start time, in the {@link System#nanoTime()} time base. 599 * 600 * @throws IllegalStateException if no frame is in progress. 601 * @hide 602 */ 603 @UnsupportedAppUsage getFrameTimeNanos()604 public long getFrameTimeNanos() { 605 synchronized (mLock) { 606 if (!mCallbacksRunning) { 607 throw new IllegalStateException("This method must only be called as " 608 + "part of a callback while a frame is in progress."); 609 } 610 return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime(); 611 } 612 } 613 614 /** 615 * Like {@link #getLastFrameTimeNanos}, but always returns the last frame time, not matter 616 * whether callbacks are currently running. 617 * @return The frame start time of the last frame, in the {@link System#nanoTime()} time base. 618 * @hide 619 */ getLastFrameTimeNanos()620 public long getLastFrameTimeNanos() { 621 synchronized (mLock) { 622 return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime(); 623 } 624 } 625 scheduleFrameLocked(long now)626 private void scheduleFrameLocked(long now) { 627 if (!mFrameScheduled) { 628 mFrameScheduled = true; 629 if (USE_VSYNC) { 630 if (DEBUG_FRAMES) { 631 Log.d(TAG, "Scheduling next frame on vsync."); 632 } 633 634 // If running on the Looper thread, then schedule the vsync immediately, 635 // otherwise post a message to schedule the vsync from the UI thread 636 // as soon as possible. 637 if (isRunningOnLooperThreadLocked()) { 638 scheduleVsyncLocked(); 639 } else { 640 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); 641 msg.setAsynchronous(true); 642 mHandler.sendMessageAtFrontOfQueue(msg); 643 } 644 } else { 645 final long nextFrameTime = Math.max( 646 mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now); 647 if (DEBUG_FRAMES) { 648 Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms."); 649 } 650 Message msg = mHandler.obtainMessage(MSG_DO_FRAME); 651 msg.setAsynchronous(true); 652 mHandler.sendMessageAtTime(msg, nextFrameTime); 653 } 654 } 655 } 656 setFPSDivisor(int divisor)657 void setFPSDivisor(int divisor) { 658 if (divisor <= 0) divisor = 1; 659 mFPSDivisor = divisor; 660 ThreadedRenderer.setFPSDivisor(divisor); 661 } 662 663 @UnsupportedAppUsage doFrame(long frameTimeNanos, int frame)664 void doFrame(long frameTimeNanos, int frame) { 665 final long startNanos; 666 synchronized (mLock) { 667 if (!mFrameScheduled) { 668 return; // no work to do 669 } 670 671 if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) { 672 mDebugPrintNextFrameTimeDelta = false; 673 Log.d(TAG, "Frame time delta: " 674 + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms"); 675 } 676 677 long intendedFrameTimeNanos = frameTimeNanos; 678 startNanos = System.nanoTime(); 679 final long jitterNanos = startNanos - frameTimeNanos; 680 if (jitterNanos >= mFrameIntervalNanos) { 681 final long skippedFrames = jitterNanos / mFrameIntervalNanos; 682 if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { 683 Log.i(TAG, "Skipped " + skippedFrames + " frames! " 684 + "The application may be doing too much work on its main thread."); 685 } 686 final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; 687 if (DEBUG_JANK) { 688 Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms " 689 + "which is more than the frame interval of " 690 + (mFrameIntervalNanos * 0.000001f) + " ms! " 691 + "Skipping " + skippedFrames + " frames and setting frame " 692 + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past."); 693 } 694 frameTimeNanos = startNanos - lastFrameOffset; 695 } 696 697 if (frameTimeNanos < mLastFrameTimeNanos) { 698 if (DEBUG_JANK) { 699 Log.d(TAG, "Frame time appears to be going backwards. May be due to a " 700 + "previously skipped frame. Waiting for next vsync."); 701 } 702 scheduleVsyncLocked(); 703 return; 704 } 705 706 if (mFPSDivisor > 1) { 707 long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos; 708 if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) { 709 scheduleVsyncLocked(); 710 return; 711 } 712 } 713 714 mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos); 715 mFrameScheduled = false; 716 mLastFrameTimeNanos = frameTimeNanos; 717 } 718 719 try { 720 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame"); 721 AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); 722 723 mFrameInfo.markInputHandlingStart(); 724 doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); 725 726 mFrameInfo.markAnimationsStart(); 727 doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); 728 doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos); 729 730 mFrameInfo.markPerformTraversalsStart(); 731 doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); 732 733 doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); 734 } finally { 735 AnimationUtils.unlockAnimationClock(); 736 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 737 } 738 739 if (DEBUG_FRAMES) { 740 final long endNanos = System.nanoTime(); 741 Log.d(TAG, "Frame " + frame + ": Finished, took " 742 + (endNanos - startNanos) * 0.000001f + " ms, latency " 743 + (startNanos - frameTimeNanos) * 0.000001f + " ms."); 744 } 745 } 746 doCallbacks(int callbackType, long frameTimeNanos)747 void doCallbacks(int callbackType, long frameTimeNanos) { 748 CallbackRecord callbacks; 749 synchronized (mLock) { 750 // We use "now" to determine when callbacks become due because it's possible 751 // for earlier processing phases in a frame to post callbacks that should run 752 // in a following phase, such as an input event that causes an animation to start. 753 final long now = System.nanoTime(); 754 callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( 755 now / TimeUtils.NANOS_PER_MS); 756 if (callbacks == null) { 757 return; 758 } 759 mCallbacksRunning = true; 760 761 // Update the frame time if necessary when committing the frame. 762 // We only update the frame time if we are more than 2 frames late reaching 763 // the commit phase. This ensures that the frame time which is observed by the 764 // callbacks will always increase from one frame to the next and never repeat. 765 // We never want the next frame's starting frame time to end up being less than 766 // or equal to the previous frame's commit frame time. Keep in mind that the 767 // next frame has most likely already been scheduled by now so we play it 768 // safe by ensuring the commit time is always at least one frame behind. 769 if (callbackType == Choreographer.CALLBACK_COMMIT) { 770 final long jitterNanos = now - frameTimeNanos; 771 Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos); 772 if (jitterNanos >= 2 * mFrameIntervalNanos) { 773 final long lastFrameOffset = jitterNanos % mFrameIntervalNanos 774 + mFrameIntervalNanos; 775 if (DEBUG_JANK) { 776 Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f) 777 + " ms which is more than twice the frame interval of " 778 + (mFrameIntervalNanos * 0.000001f) + " ms! " 779 + "Setting frame time to " + (lastFrameOffset * 0.000001f) 780 + " ms in the past."); 781 mDebugPrintNextFrameTimeDelta = true; 782 } 783 frameTimeNanos = now - lastFrameOffset; 784 mLastFrameTimeNanos = frameTimeNanos; 785 } 786 } 787 } 788 try { 789 Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]); 790 for (CallbackRecord c = callbacks; c != null; c = c.next) { 791 if (DEBUG_FRAMES) { 792 Log.d(TAG, "RunCallback: type=" + callbackType 793 + ", action=" + c.action + ", token=" + c.token 794 + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime)); 795 } 796 c.run(frameTimeNanos); 797 } 798 } finally { 799 synchronized (mLock) { 800 mCallbacksRunning = false; 801 do { 802 final CallbackRecord next = callbacks.next; 803 recycleCallbackLocked(callbacks); 804 callbacks = next; 805 } while (callbacks != null); 806 } 807 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 808 } 809 } 810 doScheduleVsync()811 void doScheduleVsync() { 812 synchronized (mLock) { 813 if (mFrameScheduled) { 814 scheduleVsyncLocked(); 815 } 816 } 817 } 818 doScheduleCallback(int callbackType)819 void doScheduleCallback(int callbackType) { 820 synchronized (mLock) { 821 if (!mFrameScheduled) { 822 final long now = SystemClock.uptimeMillis(); 823 if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) { 824 scheduleFrameLocked(now); 825 } 826 } 827 } 828 } 829 830 @UnsupportedAppUsage scheduleVsyncLocked()831 private void scheduleVsyncLocked() { 832 mDisplayEventReceiver.scheduleVsync(); 833 } 834 isRunningOnLooperThreadLocked()835 private boolean isRunningOnLooperThreadLocked() { 836 return Looper.myLooper() == mLooper; 837 } 838 obtainCallbackLocked(long dueTime, Object action, Object token)839 private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) { 840 CallbackRecord callback = mCallbackPool; 841 if (callback == null) { 842 callback = new CallbackRecord(); 843 } else { 844 mCallbackPool = callback.next; 845 callback.next = null; 846 } 847 callback.dueTime = dueTime; 848 callback.action = action; 849 callback.token = token; 850 return callback; 851 } 852 recycleCallbackLocked(CallbackRecord callback)853 private void recycleCallbackLocked(CallbackRecord callback) { 854 callback.action = null; 855 callback.token = null; 856 callback.next = mCallbackPool; 857 mCallbackPool = callback; 858 } 859 860 /** 861 * Implement this interface to receive a callback when a new display frame is 862 * being rendered. The callback is invoked on the {@link Looper} thread to 863 * which the {@link Choreographer} is attached. 864 */ 865 public interface FrameCallback { 866 /** 867 * Called when a new display frame is being rendered. 868 * <p> 869 * This method provides the time in nanoseconds when the frame started being rendered. 870 * The frame time provides a stable time base for synchronizing animations 871 * and drawing. It should be used instead of {@link SystemClock#uptimeMillis()} 872 * or {@link System#nanoTime()} for animations and drawing in the UI. Using the frame 873 * time helps to reduce inter-frame jitter because the frame time is fixed at the time 874 * the frame was scheduled to start, regardless of when the animations or drawing 875 * callback actually runs. All callbacks that run as part of rendering a frame will 876 * observe the same frame time so using the frame time also helps to synchronize effects 877 * that are performed by different callbacks. 878 * </p><p> 879 * Please note that the framework already takes care to process animations and 880 * drawing using the frame time as a stable time base. Most applications should 881 * not need to use the frame time information directly. 882 * </p> 883 * 884 * @param frameTimeNanos The time in nanoseconds when the frame started being rendered, 885 * in the {@link System#nanoTime()} timebase. Divide this value by {@code 1000000} 886 * to convert it to the {@link SystemClock#uptimeMillis()} time base. 887 */ doFrame(long frameTimeNanos)888 public void doFrame(long frameTimeNanos); 889 } 890 891 private final class FrameHandler extends Handler { FrameHandler(Looper looper)892 public FrameHandler(Looper looper) { 893 super(looper); 894 } 895 896 @Override handleMessage(Message msg)897 public void handleMessage(Message msg) { 898 switch (msg.what) { 899 case MSG_DO_FRAME: 900 doFrame(System.nanoTime(), 0); 901 break; 902 case MSG_DO_SCHEDULE_VSYNC: 903 doScheduleVsync(); 904 break; 905 case MSG_DO_SCHEDULE_CALLBACK: 906 doScheduleCallback(msg.arg1); 907 break; 908 } 909 } 910 } 911 912 private final class FrameDisplayEventReceiver extends DisplayEventReceiver 913 implements Runnable { 914 private boolean mHavePendingVsync; 915 private long mTimestampNanos; 916 private int mFrame; 917 FrameDisplayEventReceiver(Looper looper, int vsyncSource)918 public FrameDisplayEventReceiver(Looper looper, int vsyncSource) { 919 super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS); 920 } 921 922 // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for 923 // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC 924 // for the internal display implicitly. 925 @Override onVsync(long timestampNanos, long physicalDisplayId, int frame)926 public void onVsync(long timestampNanos, long physicalDisplayId, int frame) { 927 // Post the vsync event to the Handler. 928 // The idea is to prevent incoming vsync events from completely starving 929 // the message queue. If there are no messages in the queue with timestamps 930 // earlier than the frame time, then the vsync event will be processed immediately. 931 // Otherwise, messages that predate the vsync event will be handled first. 932 long now = System.nanoTime(); 933 if (timestampNanos > now) { 934 Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f) 935 + " ms in the future! Check that graphics HAL is generating vsync " 936 + "timestamps using the correct timebase."); 937 timestampNanos = now; 938 } 939 940 if (mHavePendingVsync) { 941 Log.w(TAG, "Already have a pending vsync event. There should only be " 942 + "one at a time."); 943 } else { 944 mHavePendingVsync = true; 945 } 946 947 mTimestampNanos = timestampNanos; 948 mFrame = frame; 949 Message msg = Message.obtain(mHandler, this); 950 msg.setAsynchronous(true); 951 mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); 952 } 953 954 @Override run()955 public void run() { 956 mHavePendingVsync = false; 957 doFrame(mTimestampNanos, mFrame); 958 } 959 } 960 961 private static final class CallbackRecord { 962 public CallbackRecord next; 963 public long dueTime; 964 public Object action; // Runnable or FrameCallback 965 public Object token; 966 967 @UnsupportedAppUsage run(long frameTimeNanos)968 public void run(long frameTimeNanos) { 969 if (token == FRAME_CALLBACK_TOKEN) { 970 ((FrameCallback)action).doFrame(frameTimeNanos); 971 } else { 972 ((Runnable)action).run(); 973 } 974 } 975 } 976 977 private final class CallbackQueue { 978 private CallbackRecord mHead; 979 hasDueCallbacksLocked(long now)980 public boolean hasDueCallbacksLocked(long now) { 981 return mHead != null && mHead.dueTime <= now; 982 } 983 extractDueCallbacksLocked(long now)984 public CallbackRecord extractDueCallbacksLocked(long now) { 985 CallbackRecord callbacks = mHead; 986 if (callbacks == null || callbacks.dueTime > now) { 987 return null; 988 } 989 990 CallbackRecord last = callbacks; 991 CallbackRecord next = last.next; 992 while (next != null) { 993 if (next.dueTime > now) { 994 last.next = null; 995 break; 996 } 997 last = next; 998 next = next.next; 999 } 1000 mHead = next; 1001 return callbacks; 1002 } 1003 1004 @UnsupportedAppUsage addCallbackLocked(long dueTime, Object action, Object token)1005 public void addCallbackLocked(long dueTime, Object action, Object token) { 1006 CallbackRecord callback = obtainCallbackLocked(dueTime, action, token); 1007 CallbackRecord entry = mHead; 1008 if (entry == null) { 1009 mHead = callback; 1010 return; 1011 } 1012 if (dueTime < entry.dueTime) { 1013 callback.next = entry; 1014 mHead = callback; 1015 return; 1016 } 1017 while (entry.next != null) { 1018 if (dueTime < entry.next.dueTime) { 1019 callback.next = entry.next; 1020 break; 1021 } 1022 entry = entry.next; 1023 } 1024 entry.next = callback; 1025 } 1026 removeCallbacksLocked(Object action, Object token)1027 public void removeCallbacksLocked(Object action, Object token) { 1028 CallbackRecord predecessor = null; 1029 for (CallbackRecord callback = mHead; callback != null;) { 1030 final CallbackRecord next = callback.next; 1031 if ((action == null || callback.action == action) 1032 && (token == null || callback.token == token)) { 1033 if (predecessor != null) { 1034 predecessor.next = next; 1035 } else { 1036 mHead = next; 1037 } 1038 recycleCallbackLocked(callback); 1039 } else { 1040 predecessor = callback; 1041 } 1042 callback = next; 1043 } 1044 } 1045 } 1046 } 1047