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