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