From 59bbef0cd781f4933fd8a0a85b6067f36e529e02 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Mon, 7 May 2012 16:43:25 -0700 Subject: [PATCH] Improve animation timing. Detect when a vsync message was significantly delayed which may indicate that a frame was skipped. When this happens, update the frame time to reflect the approximate start time of the current frame instead of the start time of the frame that was skipped a long time ago. Removed an unnecessary call to getCurrentPlayTime() in the animator framework. The result was always zero and the call just made the code confusing. Bug: 6443611 Change-Id: I92b24f7ffd74c59b75a727b6bfc0bb51fc92a73a --- .../java/android/animation/ValueAnimator.java | 2 +- core/java/android/view/Choreographer.java | 33 ++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 326f27c65eb7f..f3a442ab35712 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -910,7 +910,7 @@ public class ValueAnimator extends Animator { animationHandler.mPendingAnimations.add(this); if (mStartDelay == 0) { // This sets the initial value of the animation, prior to actually starting it running - setCurrentPlayTime(getCurrentPlayTime()); + setCurrentPlayTime(0); mPlayingState = STOPPED; mRunning = true; notifyStartListeners(); diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index b319cd56cc09b..825f3518548ac 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -92,6 +92,7 @@ public final class Choreographer { private boolean mFrameScheduled; private boolean mCallbacksRunning; private long mLastFrameTimeNanos; + private long mFrameIntervalNanos; /** * Callback type: Input callback. Runs first. @@ -116,6 +117,8 @@ public final class Choreographer { mHandler = new FrameHandler(looper); mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null; mLastFrameTimeNanos = Long.MIN_VALUE; + mFrameIntervalNanos = (long)(1000000000 / + new Display(Display.DEFAULT_DISPLAY, null).getRefreshRate()); mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { @@ -343,19 +346,39 @@ public final class Choreographer { } void doFrame(long timestampNanos, int frame) { + final long startNanos; synchronized (mLock) { if (!mFrameScheduled) { return; // no work to do } + + startNanos = System.nanoTime(); + final long jitterNanos = startNanos - timestampNanos; + if (jitterNanos >= mFrameIntervalNanos) { + final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; + if (DEBUG) { + Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms " + + "which is more than the frame interval of " + + (mFrameIntervalNanos * 0.000001f) + " ms! " + + "Setting frame time to " + (lastFrameOffset * 0.000001f) + + " ms in the past."); + } + timestampNanos = startNanos - lastFrameOffset; + } + + if (timestampNanos < mLastFrameTimeNanos) { + if (DEBUG) { + Log.d(TAG, "Frame time appears to be going backwards. May be due to a " + + "previously skipped frame. Waiting for next vsync"); + } + scheduleVsyncLocked(); + return; + } + mFrameScheduled = false; mLastFrameTimeNanos = timestampNanos; } - final long startNanos; - if (DEBUG) { - startNanos = System.nanoTime(); - } - doCallbacks(Choreographer.CALLBACK_INPUT); doCallbacks(Choreographer.CALLBACK_ANIMATION); doCallbacks(Choreographer.CALLBACK_TRAVERSAL);