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