Implement Java Choreographer multi frame timeline.
Bug: 198191703 Test: atest ChoreographerTest Change-Id: I822782874bc7167be9ad016681ce31b91a38b7ed
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -138,13 +138,28 @@ public abstract class DisplayEventReceiver {
|
||||
}
|
||||
|
||||
static final class VsyncEventData {
|
||||
// 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;
|
||||
|
||||
// The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
|
||||
// allotted for the frame to be completed.
|
||||
public final long frameDeadline;
|
||||
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 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 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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user