Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 34f56bf7 authored by Rachel Lee's avatar Rachel Lee Committed by Android (Google) Code Review
Browse files

Merge "Implement Java Choreographer multi frame timeline."

parents 87c13a54 5af0462a
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -48079,15 +48079,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();
+197 −15
Original line number Diff line number Diff line
@@ -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;
@@ -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();
@@ -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.
     *
@@ -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>
@@ -673,7 +714,7 @@ public final class Choreographer {
     * @hide
     */
    public long getVsyncId() {
        return mLastVsyncEventData.id;
        return mLastVsyncEventData.preferredFrameTimeline().vsyncId;
    }

    /**
@@ -684,7 +725,7 @@ public final class Choreographer {
     * @hide
     */
    public long getFrameDeadline() {
        return mLastVsyncEventData.frameDeadline;
        return mLastVsyncEventData.preferredFrameTimeline().deadline;
    }

    void setFPSDivisor(int divisor) {
@@ -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");
@@ -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) {
@@ -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;
@@ -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);
@@ -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
@@ -831,6 +877,7 @@ public final class Choreographer {
                    }
                    frameTimeNanos = now - lastFrameOffset;
                    mLastFrameTimeNanos = frameTimeNanos;
                    frameData.setFrameTimeNanos(frameTimeNanos);
                }
            }
        }
@@ -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) {
@@ -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);
@@ -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
@@ -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)
@@ -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 {
+39 −14
Original line number Diff line number Diff line
@@ -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
@@ -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];
        }
    }

@@ -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.
+60 −3
Original line number Diff line number Diff line
@@ -48,6 +48,16 @@ static struct {
        jmethodID init;
    } frameRateOverrideClassInfo;

    struct {
        jclass clazz;
        jmethodID init;
    } frameTimelineClassInfo;

    struct {
        jclass clazz;
        jmethodID init;
    } vsyncEventDataClassInfo;

} gDisplayEventReceiverClassInfo;


@@ -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);
    }

@@ -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 =
@@ -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;
}