Remove some per-frame allocations in Choreographer

This removes per-frame allocations of
* Choreographer$FrameData
* Choreographer$FrameTimeline[]
* Choreographer$FrameTimeline

Test: hwui_unit_tests using instructions
Test: test app w/ invalidate, android studio profiler
Bug: 258694738
Change-Id: If24e6d884fda67526063828053980fcc17cb6853
This commit is contained in:
Rachel Lee
2023-02-15 15:04:48 -08:00
parent 77ec4fab67
commit c76126e3d0
2 changed files with 84 additions and 71 deletions

View File

@@ -197,6 +197,7 @@ public final class Choreographer {
private int mFPSDivisor = 1;
private DisplayEventReceiver.VsyncEventData mLastVsyncEventData =
new DisplayEventReceiver.VsyncEventData();
private final FrameData mFrameData = new FrameData();
/**
* Contains information about the current frame for jank-tracking,
@@ -789,7 +790,7 @@ public final class Choreographer {
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
"Choreographer#doFrame " + vsyncEventData.preferredFrameTimeline().vsyncId);
}
FrameData frameData = new FrameData(frameTimeNanos, vsyncEventData);
mFrameData.update(frameTimeNanos, vsyncEventData);
synchronized (mLock) {
if (!mFrameScheduled) {
traceMessage("Frame not scheduled");
@@ -827,7 +828,7 @@ public final class Choreographer {
+ " ms in the past.");
}
}
frameData = getUpdatedFrameData(frameTimeNanos, frameData, jitterNanos);
mFrameData.update(frameTimeNanos, mDisplayEventReceiver, jitterNanos);
}
if (frameTimeNanos < mLastFrameTimeNanos) {
@@ -862,17 +863,16 @@ public final class Choreographer {
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameData, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_INPUT, frameIntervalNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameData, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameData,
frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameIntervalNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameData, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameData, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameIntervalNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
@@ -886,9 +886,9 @@ public final class Choreographer {
}
}
void doCallbacks(int callbackType, FrameData frameData, long frameIntervalNanos) {
void doCallbacks(int callbackType, long frameIntervalNanos) {
CallbackRecord callbacks;
long frameTimeNanos = frameData.mFrameTimeNanos;
long frameTimeNanos = mFrameData.getFrameTimeNanos();
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
@@ -925,7 +925,7 @@ public final class Choreographer {
}
frameTimeNanos = now - lastFrameOffset;
mLastFrameTimeNanos = frameTimeNanos;
frameData = getUpdatedFrameData(frameTimeNanos, frameData, jitterNanos);
mFrameData.update(frameTimeNanos, mDisplayEventReceiver, jitterNanos);
}
}
}
@@ -937,7 +937,7 @@ public final class Choreographer {
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
}
c.run(frameData);
c.run(mFrameData);
}
} finally {
synchronized (mLock) {
@@ -1039,15 +1039,19 @@ public final class Choreographer {
/** Holds data that describes one possible VSync frame event to render at. */
public static class FrameTimeline {
FrameTimeline(long vsyncId, long expectedPresentTimeNanos, long deadlineNanos) {
this.mVsyncId = vsyncId;
this.mExpectedPresentTimeNanos = expectedPresentTimeNanos;
this.mDeadlineNanos = deadlineNanos;
private long mVsyncId = FrameInfo.INVALID_VSYNC_ID;
private long mExpectedPresentationTimeNanos = -1;
private long mDeadlineNanos = -1;
FrameTimeline() {
// Intentionally empty; defined so that it is not API/public by default.
}
private long mVsyncId;
private long mExpectedPresentTimeNanos;
private long mDeadlineNanos;
void update(long vsyncId, long expectedPresentationTimeNanos, long deadlineNanos) {
mVsyncId = vsyncId;
mExpectedPresentationTimeNanos = expectedPresentationTimeNanos;
mDeadlineNanos = deadlineNanos;
}
/**
* The id that corresponds to this frame timeline, used to correlate a frame
@@ -1062,7 +1066,7 @@ public final class Choreographer {
* presented.
*/
public long getExpectedPresentationTimeNanos() {
return mExpectedPresentTimeNanos;
return mExpectedPresentationTimeNanos;
}
/**
@@ -1079,20 +1083,15 @@ public final class Choreographer {
* information including deadline and expected present time.
*/
public static class FrameData {
FrameData(long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) {
this.mFrameTimeNanos = frameTimeNanos;
this.mFrameTimelines = convertFrameTimelines(vsyncEventData);
this.mPreferredFrameTimelineIndex =
vsyncEventData.preferredFrameTimelineIndex;
}
private long mFrameTimeNanos;
private final FrameTimeline[] mFrameTimelines;
private final FrameTimeline[] mFrameTimelines =
new FrameTimeline[DisplayEventReceiver.VsyncEventData.FRAME_TIMELINES_LENGTH];
private int mPreferredFrameTimelineIndex;
void updateFrameData(long frameTimeNanos, int newPreferredFrameTimelineIndex) {
mFrameTimeNanos = frameTimeNanos;
mPreferredFrameTimelineIndex = newPreferredFrameTimelineIndex;
FrameData() {
for (int i = 0; i < mFrameTimelines.length; i++) {
mFrameTimelines[i] = new FrameTimeline();
}
}
/** The time in nanoseconds when the frame started being rendered. */
@@ -1113,49 +1112,58 @@ public final class Choreographer {
return mFrameTimelines[mPreferredFrameTimelineIndex];
}
private FrameTimeline[] convertFrameTimelines(
DisplayEventReceiver.VsyncEventData vsyncEventData) {
FrameTimeline[] frameTimelines =
new FrameTimeline[vsyncEventData.frameTimelines.length];
/**
* Update the frame data with a {@code DisplayEventReceiver.VsyncEventData} received from
* native.
*/
void update(long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) {
if (vsyncEventData.frameTimelines.length != mFrameTimelines.length) {
throw new IllegalStateException(
"Length of native frame timelines received does not match Java. Did "
+ "FRAME_TIMELINES_LENGTH or kFrameTimelinesLength (native) "
+ "change?");
}
mFrameTimeNanos = frameTimeNanos;
mPreferredFrameTimelineIndex = vsyncEventData.preferredFrameTimelineIndex;
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);
mFrameTimelines[i].update(frameTimeline.vsyncId,
frameTimeline.expectedPresentationTime, frameTimeline.deadline);
}
return frameTimelines;
}
}
/**
* Update the frame data when the frame is late.
*
* @param jitterNanos currentTime - frameTime
*/
private FrameData getUpdatedFrameData(long frameTimeNanos, FrameData frameData,
long jitterNanos) {
int newPreferredIndex = 0;
FrameTimeline[] frameTimelines = frameData.getFrameTimelines();
final long minimumDeadline =
frameData.getPreferredFrameTimeline().getDeadlineNanos() + jitterNanos;
// Look for a non-past deadline timestamp in the existing frame data. Otherwise, binder
// query for new frame data. Note that binder is relatively slow, O(ms), so it is
// only called when the existing frame data does not hold a valid frame.
while (newPreferredIndex < frameTimelines.length - 1
&& frameTimelines[newPreferredIndex].getDeadlineNanos()
< minimumDeadline) {
newPreferredIndex++;
}
long newPreferredDeadline =
frameData.getFrameTimelines()[newPreferredIndex].getDeadlineNanos();
if (newPreferredDeadline < minimumDeadline) {
DisplayEventReceiver.VsyncEventData latestVsyncEventData =
mDisplayEventReceiver.getLatestVsyncEventData();
return new FrameData(frameTimeNanos, latestVsyncEventData);
} else {
frameData.updateFrameData(frameTimeNanos, newPreferredIndex);
return frameData;
/**
* Update the frame data when the frame is late.
*
* @param jitterNanos currentTime - frameTime
*/
void update(
long frameTimeNanos, DisplayEventReceiver displayEventReceiver, long jitterNanos) {
int newPreferredIndex = 0;
final long minimumDeadline =
getPreferredFrameTimeline().getDeadlineNanos() + jitterNanos;
// Look for a non-past deadline timestamp in the existing frame data. Otherwise, binder
// query for new frame data. Note that binder is relatively slow, O(ms), so it is
// only called when the existing frame data does not hold a valid frame.
while (newPreferredIndex < mFrameTimelines.length - 1
&& mFrameTimelines[newPreferredIndex].getDeadlineNanos() < minimumDeadline) {
newPreferredIndex++;
}
long newPreferredDeadline = mFrameTimelines[newPreferredIndex].getDeadlineNanos();
if (newPreferredDeadline < minimumDeadline) {
DisplayEventReceiver.VsyncEventData latestVsyncEventData =
displayEventReceiver.getLatestVsyncEventData();
update(frameTimeNanos, latestVsyncEventData);
} else {
update(frameTimeNanos, newPreferredIndex);
}
}
void update(long frameTimeNanos, int newPreferredFrameTimelineIndex) {
mFrameTimeNanos = frameTimeNanos;
mPreferredFrameTimelineIndex = newPreferredFrameTimelineIndex;
}
}

View File

@@ -145,11 +145,16 @@ public abstract class DisplayEventReceiver {
static final FrameTimeline[] INVALID_FRAME_TIMELINES =
{new FrameTimeline(FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE)};
// The amount of frame timeline choices.
// Must be in sync with VsyncEventData::kFrameTimelinesLength in
// frameworks/native/libs/gui/include/gui/VsyncEventData.h. If they do not match, a runtime
// assertion is thrown when Choreographer is processing VsyncEventData.
static final int FRAME_TIMELINES_LENGTH = 7;
public static class FrameTimeline {
FrameTimeline(long vsyncId, long expectedPresentTime, long deadline) {
FrameTimeline(long vsyncId, long expectedPresentationTime, long deadline) {
this.vsyncId = vsyncId;
this.expectedPresentTime = expectedPresentTime;
this.expectedPresentationTime = expectedPresentationTime;
this.deadline = deadline;
}
@@ -158,7 +163,7 @@ public abstract class DisplayEventReceiver {
public final long vsyncId;
// The frame timestamp for when the frame is expected to be presented.
public final long expectedPresentTime;
public final long expectedPresentationTime;
// The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
// allotted for the frame to be completed.