Loading core/api/current.txt +18 −0 Original line number Diff line number Diff line Loading @@ -47595,15 +47595,33 @@ package android.view { public final class Choreographer { method public static android.view.Choreographer getInstance(); method public void postExtendedFrameCallback(@NonNull android.view.Choreographer.ExtendedFrameCallback); method public void postFrameCallback(android.view.Choreographer.FrameCallback); method public void postFrameCallbackDelayed(android.view.Choreographer.FrameCallback, long); method public void removeExtendedFrameCallback(@Nullable android.view.Choreographer.ExtendedFrameCallback); method public void removeFrameCallback(android.view.Choreographer.FrameCallback); } public static interface Choreographer.ExtendedFrameCallback { method public void onVsync(@NonNull android.view.Choreographer.FrameData); } public static interface Choreographer.FrameCallback { method public void doFrame(long); } public static class Choreographer.FrameData { method public long getFrameTimeNanos(); method @NonNull public android.view.Choreographer.FrameTimeline[] getFrameTimelines(); method @NonNull public android.view.Choreographer.FrameTimeline getPreferredFrameTimeline(); } public static class Choreographer.FrameTimeline { method public long getDeadlineNanos(); method public long getExpectedPresentTimeNanos(); method public long getVsyncId(); } public interface CollapsibleActionView { method public void onActionViewCollapsed(); method public void onActionViewExpanded(); core/java/android/view/Choreographer.java +197 −15 Original line number Diff line number Diff line Loading @@ -19,6 +19,9 @@ package android.view; import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP; import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.FrameInfo; Loading Loading @@ -151,10 +154,15 @@ public final class Choreographer { private static final int MSG_DO_SCHEDULE_VSYNC = 1; private static final int MSG_DO_SCHEDULE_CALLBACK = 2; // All frame callbacks posted by applications have this token. // All frame callbacks posted by applications have this token or EXTENDED_FRAME_CALLBACK_TOKEN. private static final Object FRAME_CALLBACK_TOKEN = new Object() { public String toString() { return "FRAME_CALLBACK_TOKEN"; } }; private static final Object EXTENDED_FRAME_CALLBACK_TOKEN = new Object() { public String toString() { return "EXTENDED_FRAME_CALLBACK_TOKEN"; } }; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final Object mLock = new Object(); Loading Loading @@ -483,6 +491,24 @@ public final class Choreographer { } } /** * Posts an extended frame callback to run on the next frame. * <p> * The callback runs once then is automatically removed. * </p> * * @param callback The extended frame callback to run during the next frame. * * @see #removeExtendedFrameCallback */ public void postExtendedFrameCallback(@NonNull ExtendedFrameCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } postCallbackDelayedInternal(CALLBACK_ANIMATION, callback, EXTENDED_FRAME_CALLBACK_TOKEN, 0); } /** * Removes callbacks that have the specified action and token. * Loading Loading @@ -572,6 +598,21 @@ public final class Choreographer { removeCallbacksInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN); } /** * Removes a previously posted extended frame callback. * * @param callback The extended frame callback to remove. * * @see #postExtendedFrameCallback */ public void removeExtendedFrameCallback(@Nullable ExtendedFrameCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } removeCallbacksInternal(CALLBACK_ANIMATION, callback, EXTENDED_FRAME_CALLBACK_TOKEN); } /** * Gets the time when the current frame started. * <p> Loading Loading @@ -673,7 +714,7 @@ public final class Choreographer { * @hide */ public long getVsyncId() { return mLastVsyncEventData.id; return mLastVsyncEventData.preferredFrameTimeline().vsyncId; } /** Loading @@ -684,7 +725,7 @@ public final class Choreographer { * @hide */ public long getFrameDeadline() { return mLastVsyncEventData.frameDeadline; return mLastVsyncEventData.preferredFrameTimeline().deadline; } void setFPSDivisor(int divisor) { Loading @@ -705,8 +746,9 @@ public final class Choreographer { try { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame " + vsyncEventData.id); "Choreographer#doFrame " + vsyncEventData.preferredFrameTimeline().vsyncId); } FrameData frameData = new FrameData(frameTimeNanos, vsyncEventData); synchronized (mLock) { if (!mFrameScheduled) { traceMessage("Frame not scheduled"); Loading Loading @@ -737,6 +779,7 @@ public final class Choreographer { + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past."); } frameTimeNanos = startNanos - lastFrameOffset; frameData.setFrameTimeNanos(-lastFrameOffset); } if (frameTimeNanos < mLastFrameTimeNanos) { Loading @@ -758,8 +801,10 @@ public final class Choreographer { } } mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id, vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval); mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.preferredFrameTimeline().vsyncId, vsyncEventData.preferredFrameTimeline().deadline, startNanos, vsyncEventData.frameInterval); mFrameScheduled = false; mLastFrameTimeNanos = frameTimeNanos; mLastFrameIntervalNanos = frameIntervalNanos; Loading @@ -769,17 +814,17 @@ public final class Choreographer { AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); mFrameInfo.markInputHandlingStart(); doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_INPUT, frameData, frameIntervalNanos); mFrameInfo.markAnimationsStart(); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos, doCallbacks(Choreographer.CALLBACK_ANIMATION, frameData, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameData, frameIntervalNanos); mFrameInfo.markPerformTraversalsStart(); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameData, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameData, frameIntervalNanos); } finally { AnimationUtils.unlockAnimationClock(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); Loading @@ -793,8 +838,9 @@ public final class Choreographer { } } void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) { void doCallbacks(int callbackType, FrameData frameData, long frameIntervalNanos) { CallbackRecord callbacks; long frameTimeNanos = frameData.mFrameTimeNanos; synchronized (mLock) { // We use "now" to determine when callbacks become due because it's possible // for earlier processing phases in a frame to post callbacks that should run Loading Loading @@ -831,6 +877,7 @@ public final class Choreographer { } frameTimeNanos = now - lastFrameOffset; mLastFrameTimeNanos = frameTimeNanos; frameData.setFrameTimeNanos(frameTimeNanos); } } } Loading @@ -842,7 +889,7 @@ public final class Choreographer { + ", action=" + c.action + ", token=" + c.token + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime)); } c.run(frameTimeNanos); c.run(frameData); } } finally { synchronized (mLock) { Loading Loading @@ -942,6 +989,130 @@ public final class Choreographer { public void doFrame(long frameTimeNanos); } /** Holds data that describes one possible VSync frame event to render at. */ public static class FrameTimeline { static final FrameTimeline INVALID_FRAME_TIMELINE = new FrameTimeline( FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE); FrameTimeline(long vsyncId, long expectedPresentTimeNanos, long deadlineNanos) { this.mVsyncId = vsyncId; this.mExpectedPresentTimeNanos = expectedPresentTimeNanos; this.mDeadlineNanos = deadlineNanos; } private long mVsyncId; private long mExpectedPresentTimeNanos; private long mDeadlineNanos; /** * The id that corresponds to this frame timeline, used to correlate a frame * produced by HWUI with the timeline data stored in Surface Flinger. */ public long getVsyncId() { return mVsyncId; } /** Sets the vsync ID. */ void resetVsyncId() { mVsyncId = FrameInfo.INVALID_VSYNC_ID; } /** * The time in {@link System#nanoTime()} timebase which this frame is expected to be * presented. */ public long getExpectedPresentTimeNanos() { return mExpectedPresentTimeNanos; } /** * The time in {@link System#nanoTime()} timebase which this frame needs to be ready by. */ public long getDeadlineNanos() { return mDeadlineNanos; } } /** * The payload for {@link ExtendedFrameCallback} which includes frame information such as when * the frame started being rendered, and multiple possible frame timelines and their * information including deadline and expected present time. */ public static class FrameData { static final FrameTimeline[] INVALID_FRAME_TIMELINES = new FrameTimeline[0]; FrameData() { this.mFrameTimelines = INVALID_FRAME_TIMELINES; this.mPreferredFrameTimeline = FrameTimeline.INVALID_FRAME_TIMELINE; } FrameData(long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) { FrameTimeline[] frameTimelines = new FrameTimeline[vsyncEventData.frameTimelines.length]; for (int i = 0; i < vsyncEventData.frameTimelines.length; i++) { DisplayEventReceiver.VsyncEventData.FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i]; frameTimelines[i] = new FrameTimeline(frameTimeline.vsyncId, frameTimeline.expectedPresentTime, frameTimeline.deadline); } this.mFrameTimeNanos = frameTimeNanos; this.mFrameTimelines = frameTimelines; this.mPreferredFrameTimeline = frameTimelines[vsyncEventData.preferredFrameTimelineIndex]; } private long mFrameTimeNanos; private final FrameTimeline[] mFrameTimelines; private final FrameTimeline mPreferredFrameTimeline; void setFrameTimeNanos(long frameTimeNanos) { mFrameTimeNanos = frameTimeNanos; for (FrameTimeline ft : mFrameTimelines) { // The ID is no longer valid because the frame time that was registered with the ID // no longer matches. // TODO(b/205721584): Ask SF for valid vsync information. ft.resetVsyncId(); } } /** The time in nanoseconds when the frame started being rendered. */ public long getFrameTimeNanos() { return mFrameTimeNanos; } /** The possible frame timelines, sorted chronologically. */ @NonNull @SuppressLint("ArrayReturn") // For API consistency and speed. public FrameTimeline[] getFrameTimelines() { return mFrameTimelines; } /** The platform-preferred frame timeline. */ @NonNull public FrameTimeline getPreferredFrameTimeline() { return mPreferredFrameTimeline; } } /** * Implement this interface to receive a callback to start the next frame. The callback is * invoked on the {@link Looper} thread to which the {@link Choreographer} is attached. The * callback payload contains information about multiple possible frames, allowing choice of * the appropriate frame based on latency requirements. * * @see FrameCallback */ public interface ExtendedFrameCallback { /** * Called when a new display frame is being rendered. * * @param data The payload which includes frame information. Divide nanosecond values by * {@code 1000000} to convert it to the {@link SystemClock#uptimeMillis()} * time base. * @see FrameCallback#doFrame **/ void onVsync(@NonNull FrameData data); } private final class FrameHandler extends Handler { public FrameHandler(Looper looper) { super(looper); Loading Loading @@ -983,7 +1154,8 @@ public final class Choreographer { try { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#onVsync " + vsyncEventData.id); "Choreographer#onVsync " + vsyncEventData.preferredFrameTimeline().vsyncId); } // Post the vsync event to the Handler. // The idea is to prevent incoming vsync events from completely starving Loading Loading @@ -1026,7 +1198,9 @@ public final class Choreographer { private static final class CallbackRecord { public CallbackRecord next; public long dueTime; public Object action; // Runnable or FrameCallback /** Runnable or FrameCallback or ExtendedFrameCallback object. */ public Object action; /** Denotes the action type. */ public Object token; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) Loading @@ -1037,6 +1211,14 @@ public final class Choreographer { ((Runnable)action).run(); } } void run(FrameData frameData) { if (token == EXTENDED_FRAME_CALLBACK_TOKEN) { ((ExtendedFrameCallback) action).onVsync(frameData); } else { run(frameData.getFrameTimeNanos()); } } } private final class CallbackQueue { Loading core/java/android/view/DisplayEventReceiver.java +39 −14 Original line number Diff line number Diff line Loading @@ -138,13 +138,28 @@ public abstract class DisplayEventReceiver { } static final class VsyncEventData { static final FrameTimeline[] INVALID_FRAME_TIMELINES = {new FrameTimeline(FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE)}; public static class FrameTimeline { FrameTimeline(long vsyncId, long expectedPresentTime, long deadline) { this.vsyncId = vsyncId; this.expectedPresentTime = expectedPresentTime; this.deadline = deadline; } // The frame timeline vsync id, used to correlate a frame // produced by HWUI with the timeline data stored in Surface Flinger. public final long id; public final long vsyncId; // The frame timestamp for when the frame is expected to be presented. public final long expectedPresentTime; // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is // allotted for the frame to be completed. public final long frameDeadline; public final long deadline; } /** * The current interval between frames in ns. This will be used to align Loading @@ -153,16 +168,27 @@ public abstract class DisplayEventReceiver { */ public final long frameInterval; VsyncEventData(long id, long frameDeadline, long frameInterval) { this.id = id; this.frameDeadline = frameDeadline; public final FrameTimeline[] frameTimelines; public final int preferredFrameTimelineIndex; // Called from native code. @SuppressWarnings("unused") VsyncEventData(FrameTimeline[] frameTimelines, int preferredFrameTimelineIndex, long frameInterval) { this.frameTimelines = frameTimelines; this.preferredFrameTimelineIndex = preferredFrameTimelineIndex; this.frameInterval = frameInterval; } VsyncEventData() { this.id = FrameInfo.INVALID_VSYNC_ID; this.frameDeadline = Long.MAX_VALUE; this.frameInterval = -1; this.frameTimelines = INVALID_FRAME_TIMELINES; this.preferredFrameTimelineIndex = 0; } public FrameTimeline preferredFrameTimeline() { return frameTimelines[preferredFrameTimelineIndex]; } } Loading Loading @@ -256,9 +282,8 @@ public abstract class DisplayEventReceiver { // Called from native code. @SuppressWarnings("unused") private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame, long frameTimelineVsyncId, long frameDeadline, long frameInterval) { onVsync(timestampNanos, physicalDisplayId, frame, new VsyncEventData(frameTimelineVsyncId, frameDeadline, frameInterval)); VsyncEventData vsyncEventData) { onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData); } // Called from native code. Loading core/jni/android_view_DisplayEventReceiver.cpp +60 −3 Original line number Diff line number Diff line Loading @@ -48,6 +48,16 @@ static struct { jmethodID init; } frameRateOverrideClassInfo; struct { jclass clazz; jmethodID init; } frameTimelineClassInfo; struct { jclass clazz; jmethodID init; } vsyncEventDataClassInfo; } gDisplayEventReceiverClassInfo; Loading Loading @@ -105,9 +115,38 @@ void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDispla ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); if (receiverObj.get()) { ALOGV("receiver %p ~ Invoking vsync handler.", this); ScopedLocalRef<jobjectArray> frameTimelineObjs(env, env->NewObjectArray(vsyncEventData.frameTimelines.size(), gDisplayEventReceiverClassInfo .frameTimelineClassInfo.clazz, /*initial element*/ NULL)); for (int i = 0; i < vsyncEventData.frameTimelines.size(); i++) { VsyncEventData::FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i]; ScopedLocalRef<jobject> frameTimelineObj(env, env->NewObject(gDisplayEventReceiverClassInfo .frameTimelineClassInfo.clazz, gDisplayEventReceiverClassInfo .frameTimelineClassInfo.init, frameTimeline.id, frameTimeline.expectedPresentTime, frameTimeline.deadlineTimestamp)); env->SetObjectArrayElement(frameTimelineObjs.get(), i, frameTimelineObj.get()); } ScopedLocalRef<jobject> vsyncEventDataJava(env, env->NewObject(gDisplayEventReceiverClassInfo .vsyncEventDataClassInfo.clazz, gDisplayEventReceiverClassInfo .vsyncEventDataClassInfo.init, frameTimelineObjs.get(), vsyncEventData.preferredFrameTimelineIndex, vsyncEventData.frameInterval)); env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId.value, count, vsyncEventData.id, vsyncEventData.deadlineTimestamp, vsyncEventData.frameInterval); timestamp, displayId.value, count, vsyncEventDataJava.get()); ALOGV("receiver %p ~ Returned from vsync handler.", this); } Loading Loading @@ -239,7 +278,7 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJIJJJ)V"); "(JJILandroid/view/DisplayEventReceiver$VsyncEventData;)V"); gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V"); gDisplayEventReceiverClassInfo.dispatchModeChanged = Loading @@ -258,6 +297,24 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz, "<init>", "(IF)V"); jclass frameTimelineClazz = FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData$FrameTimeline"); gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz = MakeGlobalRefOrDie(env, frameTimelineClazz); gDisplayEventReceiverClassInfo.frameTimelineClassInfo.init = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz, "<init>", "(JJJ)V"); jclass vsyncEventDataClazz = FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData"); gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz = MakeGlobalRefOrDie(env, vsyncEventDataClazz); gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.init = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz, "<init>", "([Landroid/view/" "DisplayEventReceiver$VsyncEventData$FrameTimeline;IJ)V"); return res; } Loading Loading
core/api/current.txt +18 −0 Original line number Diff line number Diff line Loading @@ -47595,15 +47595,33 @@ package android.view { public final class Choreographer { method public static android.view.Choreographer getInstance(); method public void postExtendedFrameCallback(@NonNull android.view.Choreographer.ExtendedFrameCallback); method public void postFrameCallback(android.view.Choreographer.FrameCallback); method public void postFrameCallbackDelayed(android.view.Choreographer.FrameCallback, long); method public void removeExtendedFrameCallback(@Nullable android.view.Choreographer.ExtendedFrameCallback); method public void removeFrameCallback(android.view.Choreographer.FrameCallback); } public static interface Choreographer.ExtendedFrameCallback { method public void onVsync(@NonNull android.view.Choreographer.FrameData); } public static interface Choreographer.FrameCallback { method public void doFrame(long); } public static class Choreographer.FrameData { method public long getFrameTimeNanos(); method @NonNull public android.view.Choreographer.FrameTimeline[] getFrameTimelines(); method @NonNull public android.view.Choreographer.FrameTimeline getPreferredFrameTimeline(); } public static class Choreographer.FrameTimeline { method public long getDeadlineNanos(); method public long getExpectedPresentTimeNanos(); method public long getVsyncId(); } public interface CollapsibleActionView { method public void onActionViewCollapsed(); method public void onActionViewExpanded();
core/java/android/view/Choreographer.java +197 −15 Original line number Diff line number Diff line Loading @@ -19,6 +19,9 @@ package android.view; import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP; import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.FrameInfo; Loading Loading @@ -151,10 +154,15 @@ public final class Choreographer { private static final int MSG_DO_SCHEDULE_VSYNC = 1; private static final int MSG_DO_SCHEDULE_CALLBACK = 2; // All frame callbacks posted by applications have this token. // All frame callbacks posted by applications have this token or EXTENDED_FRAME_CALLBACK_TOKEN. private static final Object FRAME_CALLBACK_TOKEN = new Object() { public String toString() { return "FRAME_CALLBACK_TOKEN"; } }; private static final Object EXTENDED_FRAME_CALLBACK_TOKEN = new Object() { public String toString() { return "EXTENDED_FRAME_CALLBACK_TOKEN"; } }; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final Object mLock = new Object(); Loading Loading @@ -483,6 +491,24 @@ public final class Choreographer { } } /** * Posts an extended frame callback to run on the next frame. * <p> * The callback runs once then is automatically removed. * </p> * * @param callback The extended frame callback to run during the next frame. * * @see #removeExtendedFrameCallback */ public void postExtendedFrameCallback(@NonNull ExtendedFrameCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } postCallbackDelayedInternal(CALLBACK_ANIMATION, callback, EXTENDED_FRAME_CALLBACK_TOKEN, 0); } /** * Removes callbacks that have the specified action and token. * Loading Loading @@ -572,6 +598,21 @@ public final class Choreographer { removeCallbacksInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN); } /** * Removes a previously posted extended frame callback. * * @param callback The extended frame callback to remove. * * @see #postExtendedFrameCallback */ public void removeExtendedFrameCallback(@Nullable ExtendedFrameCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } removeCallbacksInternal(CALLBACK_ANIMATION, callback, EXTENDED_FRAME_CALLBACK_TOKEN); } /** * Gets the time when the current frame started. * <p> Loading Loading @@ -673,7 +714,7 @@ public final class Choreographer { * @hide */ public long getVsyncId() { return mLastVsyncEventData.id; return mLastVsyncEventData.preferredFrameTimeline().vsyncId; } /** Loading @@ -684,7 +725,7 @@ public final class Choreographer { * @hide */ public long getFrameDeadline() { return mLastVsyncEventData.frameDeadline; return mLastVsyncEventData.preferredFrameTimeline().deadline; } void setFPSDivisor(int divisor) { Loading @@ -705,8 +746,9 @@ public final class Choreographer { try { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame " + vsyncEventData.id); "Choreographer#doFrame " + vsyncEventData.preferredFrameTimeline().vsyncId); } FrameData frameData = new FrameData(frameTimeNanos, vsyncEventData); synchronized (mLock) { if (!mFrameScheduled) { traceMessage("Frame not scheduled"); Loading Loading @@ -737,6 +779,7 @@ public final class Choreographer { + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past."); } frameTimeNanos = startNanos - lastFrameOffset; frameData.setFrameTimeNanos(-lastFrameOffset); } if (frameTimeNanos < mLastFrameTimeNanos) { Loading @@ -758,8 +801,10 @@ public final class Choreographer { } } mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id, vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval); mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.preferredFrameTimeline().vsyncId, vsyncEventData.preferredFrameTimeline().deadline, startNanos, vsyncEventData.frameInterval); mFrameScheduled = false; mLastFrameTimeNanos = frameTimeNanos; mLastFrameIntervalNanos = frameIntervalNanos; Loading @@ -769,17 +814,17 @@ public final class Choreographer { AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); mFrameInfo.markInputHandlingStart(); doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_INPUT, frameData, frameIntervalNanos); mFrameInfo.markAnimationsStart(); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos, doCallbacks(Choreographer.CALLBACK_ANIMATION, frameData, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameData, frameIntervalNanos); mFrameInfo.markPerformTraversalsStart(); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameData, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameData, frameIntervalNanos); } finally { AnimationUtils.unlockAnimationClock(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); Loading @@ -793,8 +838,9 @@ public final class Choreographer { } } void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) { void doCallbacks(int callbackType, FrameData frameData, long frameIntervalNanos) { CallbackRecord callbacks; long frameTimeNanos = frameData.mFrameTimeNanos; synchronized (mLock) { // We use "now" to determine when callbacks become due because it's possible // for earlier processing phases in a frame to post callbacks that should run Loading Loading @@ -831,6 +877,7 @@ public final class Choreographer { } frameTimeNanos = now - lastFrameOffset; mLastFrameTimeNanos = frameTimeNanos; frameData.setFrameTimeNanos(frameTimeNanos); } } } Loading @@ -842,7 +889,7 @@ public final class Choreographer { + ", action=" + c.action + ", token=" + c.token + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime)); } c.run(frameTimeNanos); c.run(frameData); } } finally { synchronized (mLock) { Loading Loading @@ -942,6 +989,130 @@ public final class Choreographer { public void doFrame(long frameTimeNanos); } /** Holds data that describes one possible VSync frame event to render at. */ public static class FrameTimeline { static final FrameTimeline INVALID_FRAME_TIMELINE = new FrameTimeline( FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE); FrameTimeline(long vsyncId, long expectedPresentTimeNanos, long deadlineNanos) { this.mVsyncId = vsyncId; this.mExpectedPresentTimeNanos = expectedPresentTimeNanos; this.mDeadlineNanos = deadlineNanos; } private long mVsyncId; private long mExpectedPresentTimeNanos; private long mDeadlineNanos; /** * The id that corresponds to this frame timeline, used to correlate a frame * produced by HWUI with the timeline data stored in Surface Flinger. */ public long getVsyncId() { return mVsyncId; } /** Sets the vsync ID. */ void resetVsyncId() { mVsyncId = FrameInfo.INVALID_VSYNC_ID; } /** * The time in {@link System#nanoTime()} timebase which this frame is expected to be * presented. */ public long getExpectedPresentTimeNanos() { return mExpectedPresentTimeNanos; } /** * The time in {@link System#nanoTime()} timebase which this frame needs to be ready by. */ public long getDeadlineNanos() { return mDeadlineNanos; } } /** * The payload for {@link ExtendedFrameCallback} which includes frame information such as when * the frame started being rendered, and multiple possible frame timelines and their * information including deadline and expected present time. */ public static class FrameData { static final FrameTimeline[] INVALID_FRAME_TIMELINES = new FrameTimeline[0]; FrameData() { this.mFrameTimelines = INVALID_FRAME_TIMELINES; this.mPreferredFrameTimeline = FrameTimeline.INVALID_FRAME_TIMELINE; } FrameData(long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) { FrameTimeline[] frameTimelines = new FrameTimeline[vsyncEventData.frameTimelines.length]; for (int i = 0; i < vsyncEventData.frameTimelines.length; i++) { DisplayEventReceiver.VsyncEventData.FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i]; frameTimelines[i] = new FrameTimeline(frameTimeline.vsyncId, frameTimeline.expectedPresentTime, frameTimeline.deadline); } this.mFrameTimeNanos = frameTimeNanos; this.mFrameTimelines = frameTimelines; this.mPreferredFrameTimeline = frameTimelines[vsyncEventData.preferredFrameTimelineIndex]; } private long mFrameTimeNanos; private final FrameTimeline[] mFrameTimelines; private final FrameTimeline mPreferredFrameTimeline; void setFrameTimeNanos(long frameTimeNanos) { mFrameTimeNanos = frameTimeNanos; for (FrameTimeline ft : mFrameTimelines) { // The ID is no longer valid because the frame time that was registered with the ID // no longer matches. // TODO(b/205721584): Ask SF for valid vsync information. ft.resetVsyncId(); } } /** The time in nanoseconds when the frame started being rendered. */ public long getFrameTimeNanos() { return mFrameTimeNanos; } /** The possible frame timelines, sorted chronologically. */ @NonNull @SuppressLint("ArrayReturn") // For API consistency and speed. public FrameTimeline[] getFrameTimelines() { return mFrameTimelines; } /** The platform-preferred frame timeline. */ @NonNull public FrameTimeline getPreferredFrameTimeline() { return mPreferredFrameTimeline; } } /** * Implement this interface to receive a callback to start the next frame. The callback is * invoked on the {@link Looper} thread to which the {@link Choreographer} is attached. The * callback payload contains information about multiple possible frames, allowing choice of * the appropriate frame based on latency requirements. * * @see FrameCallback */ public interface ExtendedFrameCallback { /** * Called when a new display frame is being rendered. * * @param data The payload which includes frame information. Divide nanosecond values by * {@code 1000000} to convert it to the {@link SystemClock#uptimeMillis()} * time base. * @see FrameCallback#doFrame **/ void onVsync(@NonNull FrameData data); } private final class FrameHandler extends Handler { public FrameHandler(Looper looper) { super(looper); Loading Loading @@ -983,7 +1154,8 @@ public final class Choreographer { try { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#onVsync " + vsyncEventData.id); "Choreographer#onVsync " + vsyncEventData.preferredFrameTimeline().vsyncId); } // Post the vsync event to the Handler. // The idea is to prevent incoming vsync events from completely starving Loading Loading @@ -1026,7 +1198,9 @@ public final class Choreographer { private static final class CallbackRecord { public CallbackRecord next; public long dueTime; public Object action; // Runnable or FrameCallback /** Runnable or FrameCallback or ExtendedFrameCallback object. */ public Object action; /** Denotes the action type. */ public Object token; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) Loading @@ -1037,6 +1211,14 @@ public final class Choreographer { ((Runnable)action).run(); } } void run(FrameData frameData) { if (token == EXTENDED_FRAME_CALLBACK_TOKEN) { ((ExtendedFrameCallback) action).onVsync(frameData); } else { run(frameData.getFrameTimeNanos()); } } } private final class CallbackQueue { Loading
core/java/android/view/DisplayEventReceiver.java +39 −14 Original line number Diff line number Diff line Loading @@ -138,13 +138,28 @@ public abstract class DisplayEventReceiver { } static final class VsyncEventData { static final FrameTimeline[] INVALID_FRAME_TIMELINES = {new FrameTimeline(FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE)}; public static class FrameTimeline { FrameTimeline(long vsyncId, long expectedPresentTime, long deadline) { this.vsyncId = vsyncId; this.expectedPresentTime = expectedPresentTime; this.deadline = deadline; } // The frame timeline vsync id, used to correlate a frame // produced by HWUI with the timeline data stored in Surface Flinger. public final long id; public final long vsyncId; // The frame timestamp for when the frame is expected to be presented. public final long expectedPresentTime; // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is // allotted for the frame to be completed. public final long frameDeadline; public final long deadline; } /** * The current interval between frames in ns. This will be used to align Loading @@ -153,16 +168,27 @@ public abstract class DisplayEventReceiver { */ public final long frameInterval; VsyncEventData(long id, long frameDeadline, long frameInterval) { this.id = id; this.frameDeadline = frameDeadline; public final FrameTimeline[] frameTimelines; public final int preferredFrameTimelineIndex; // Called from native code. @SuppressWarnings("unused") VsyncEventData(FrameTimeline[] frameTimelines, int preferredFrameTimelineIndex, long frameInterval) { this.frameTimelines = frameTimelines; this.preferredFrameTimelineIndex = preferredFrameTimelineIndex; this.frameInterval = frameInterval; } VsyncEventData() { this.id = FrameInfo.INVALID_VSYNC_ID; this.frameDeadline = Long.MAX_VALUE; this.frameInterval = -1; this.frameTimelines = INVALID_FRAME_TIMELINES; this.preferredFrameTimelineIndex = 0; } public FrameTimeline preferredFrameTimeline() { return frameTimelines[preferredFrameTimelineIndex]; } } Loading Loading @@ -256,9 +282,8 @@ public abstract class DisplayEventReceiver { // Called from native code. @SuppressWarnings("unused") private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame, long frameTimelineVsyncId, long frameDeadline, long frameInterval) { onVsync(timestampNanos, physicalDisplayId, frame, new VsyncEventData(frameTimelineVsyncId, frameDeadline, frameInterval)); VsyncEventData vsyncEventData) { onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData); } // Called from native code. Loading
core/jni/android_view_DisplayEventReceiver.cpp +60 −3 Original line number Diff line number Diff line Loading @@ -48,6 +48,16 @@ static struct { jmethodID init; } frameRateOverrideClassInfo; struct { jclass clazz; jmethodID init; } frameTimelineClassInfo; struct { jclass clazz; jmethodID init; } vsyncEventDataClassInfo; } gDisplayEventReceiverClassInfo; Loading Loading @@ -105,9 +115,38 @@ void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDispla ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); if (receiverObj.get()) { ALOGV("receiver %p ~ Invoking vsync handler.", this); ScopedLocalRef<jobjectArray> frameTimelineObjs(env, env->NewObjectArray(vsyncEventData.frameTimelines.size(), gDisplayEventReceiverClassInfo .frameTimelineClassInfo.clazz, /*initial element*/ NULL)); for (int i = 0; i < vsyncEventData.frameTimelines.size(); i++) { VsyncEventData::FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i]; ScopedLocalRef<jobject> frameTimelineObj(env, env->NewObject(gDisplayEventReceiverClassInfo .frameTimelineClassInfo.clazz, gDisplayEventReceiverClassInfo .frameTimelineClassInfo.init, frameTimeline.id, frameTimeline.expectedPresentTime, frameTimeline.deadlineTimestamp)); env->SetObjectArrayElement(frameTimelineObjs.get(), i, frameTimelineObj.get()); } ScopedLocalRef<jobject> vsyncEventDataJava(env, env->NewObject(gDisplayEventReceiverClassInfo .vsyncEventDataClassInfo.clazz, gDisplayEventReceiverClassInfo .vsyncEventDataClassInfo.init, frameTimelineObjs.get(), vsyncEventData.preferredFrameTimelineIndex, vsyncEventData.frameInterval)); env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId.value, count, vsyncEventData.id, vsyncEventData.deadlineTimestamp, vsyncEventData.frameInterval); timestamp, displayId.value, count, vsyncEventDataJava.get()); ALOGV("receiver %p ~ Returned from vsync handler.", this); } Loading Loading @@ -239,7 +278,7 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJIJJJ)V"); "(JJILandroid/view/DisplayEventReceiver$VsyncEventData;)V"); gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V"); gDisplayEventReceiverClassInfo.dispatchModeChanged = Loading @@ -258,6 +297,24 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz, "<init>", "(IF)V"); jclass frameTimelineClazz = FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData$FrameTimeline"); gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz = MakeGlobalRefOrDie(env, frameTimelineClazz); gDisplayEventReceiverClassInfo.frameTimelineClassInfo.init = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz, "<init>", "(JJJ)V"); jclass vsyncEventDataClazz = FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData"); gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz = MakeGlobalRefOrDie(env, vsyncEventDataClazz); gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.init = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz, "<init>", "([Landroid/view/" "DisplayEventReceiver$VsyncEventData$FrameTimeline;IJ)V"); return res; } Loading