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