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