Merge "Shared timeline plumbing"

This commit is contained in:
Ady Abraham
2020-09-17 21:16:25 +00:00
committed by Android (Google) Code Review
16 changed files with 104 additions and 54 deletions

View File

@@ -660,8 +660,7 @@ public final class Choreographer {
ThreadedRenderer.setFPSDivisor(divisor);
}
@UnsupportedAppUsage
void doFrame(long frameTimeNanos, int frame) {
void doFrame(long frameTimeNanos, int frame, long frameTimelineVsyncId) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
@@ -711,7 +710,7 @@ public final class Choreographer {
}
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, frameTimelineVsyncId);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
@@ -897,7 +896,7 @@ public final class Choreographer {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
doFrame(System.nanoTime(), 0, FrameInfo.INVALID_VSYNC_ID);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
@@ -914,6 +913,7 @@ public final class Choreographer {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
private long mFrameTimelineVsyncId;
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS);
@@ -923,7 +923,8 @@ public final class Choreographer {
// the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
// for the internal display implicitly.
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
long frameTimelineVsyncId) {
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
@@ -946,6 +947,7 @@ public final class Choreographer {
mTimestampNanos = timestampNanos;
mFrame = frame;
mFrameTimelineVsyncId = frameTimelineVsyncId;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
@@ -954,7 +956,7 @@ public final class Choreographer {
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
doFrame(mTimestampNanos, mFrame, mFrameTimelineVsyncId);
}
}

View File

@@ -154,9 +154,11 @@ public abstract class DisplayEventReceiver {
* timebase.
* @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
* @param frame The frame number. Increases by one for each vertical sync interval.
* @param frameTimelineVsyncId The frame timeline vsync id, used to correlate a frame
* produced by HWUI with the timeline data stored in Surface Flinger.
*/
@UnsupportedAppUsage
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
long frameTimelineVsyncId) {
}
/**
@@ -198,9 +200,9 @@ public abstract class DisplayEventReceiver {
// Called from native code.
@SuppressWarnings("unused")
@UnsupportedAppUsage
private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
onVsync(timestampNanos, physicalDisplayId, frame);
private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
long frameTimelineVsyncId) {
onVsync(timestampNanos, physicalDisplayId, frame, frameTimelineVsyncId);
}
// Called from native code.

View File

@@ -189,6 +189,7 @@ public final class FrameMetrics {
*/
@IntDef ({
Index.FLAGS,
Index.FRAME_TIMELINE_VSYNC_ID,
Index.INTENDED_VSYNC,
Index.VSYNC,
Index.OLDEST_INPUT_EVENT,
@@ -206,21 +207,22 @@ public final class FrameMetrics {
@Retention(RetentionPolicy.SOURCE)
private @interface Index {
int FLAGS = 0;
int INTENDED_VSYNC = 1;
int VSYNC = 2;
int OLDEST_INPUT_EVENT = 3;
int NEWEST_INPUT_EVENT = 4;
int HANDLE_INPUT_START = 5;
int ANIMATION_START = 6;
int PERFORM_TRAVERSALS_START = 7;
int DRAW_START = 8;
int SYNC_QUEUED = 9;
int SYNC_START = 10;
int ISSUE_DRAW_COMMANDS_START = 11;
int SWAP_BUFFERS = 12;
int FRAME_COMPLETED = 13;
int FRAME_TIMELINE_VSYNC_ID = 1;
int INTENDED_VSYNC = 2;
int VSYNC = 3;
int OLDEST_INPUT_EVENT = 4;
int NEWEST_INPUT_EVENT = 5;
int HANDLE_INPUT_START = 6;
int ANIMATION_START = 7;
int PERFORM_TRAVERSALS_START = 8;
int DRAW_START = 9;
int SYNC_QUEUED = 10;
int SYNC_START = 11;
int ISSUE_DRAW_COMMANDS_START = 12;
int SWAP_BUFFERS = 13;
int FRAME_COMPLETED = 14;
int FRAME_STATS_COUNT = 17; // must always be last
int FRAME_STATS_COUNT = 18; // must always be last
}
/*

View File

@@ -59,7 +59,8 @@ private:
jobject mReceiverWeakGlobal;
sp<MessageQueue> mMessageQueue;
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
int64_t sharedTimelineFrameCount) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
int32_t configId, nsecs_t vsyncPeriod) override;
@@ -90,14 +91,14 @@ void NativeDisplayEventReceiver::dispose() {
}
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
uint32_t count) {
uint32_t count, int64_t frameTimelineVsyncId) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
if (receiverObj.get()) {
ALOGV("receiver %p ~ Invoking vsync handler.", this);
env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
timestamp, displayId.value, count);
timestamp, displayId.value, count, frameTimelineVsyncId);
ALOGV("receiver %p ~ Returned from vsync handler.", this);
}
@@ -196,8 +197,8 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) {
jclass clazz = FindClassOrDie(env, "android/view/DisplayEventReceiver");
gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env,
gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
gDisplayEventReceiverClassInfo.dispatchVsync =
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJIJ)V");
gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,

View File

@@ -40,7 +40,7 @@ import java.lang.annotation.RetentionPolicy;
*/
public final class FrameInfo {
public long[] frameInfo = new long[9];
public long[] frameInfo = new long[10];
// Various flags set to provide extra metadata about the current frame
private static final int FLAGS = 0;
@@ -51,38 +51,44 @@ public final class FrameInfo {
// A renderer associated with just a Surface, not with a ViewRootImpl instance.
public static final long FLAG_SURFACE_CANVAS = 1 << 2;
// An invalid vsync id to be used when FRAME_TIMELINE_VSYNC_ID is unknown
public static final long INVALID_VSYNC_ID = -1;
@LongDef(flag = true, value = {
FLAG_WINDOW_LAYOUT_CHANGED, FLAG_SURFACE_CANVAS })
@Retention(RetentionPolicy.SOURCE)
public @interface FrameInfoFlags {}
private static final int FRAME_TIMELINE_VSYNC_ID = 1;
// The intended vsync time, unadjusted by jitter
private static final int INTENDED_VSYNC = 1;
private static final int INTENDED_VSYNC = 2;
// Jitter-adjusted vsync time, this is what was used as input into the
// animation & drawing system
private static final int VSYNC = 2;
private static final int VSYNC = 3;
// The time of the oldest input event
private static final int OLDEST_INPUT_EVENT = 3;
private static final int OLDEST_INPUT_EVENT = 4;
// The time of the newest input event
private static final int NEWEST_INPUT_EVENT = 4;
private static final int NEWEST_INPUT_EVENT = 5;
// When input event handling started
private static final int HANDLE_INPUT_START = 5;
private static final int HANDLE_INPUT_START = 6;
// When animation evaluations started
private static final int ANIMATION_START = 6;
private static final int ANIMATION_START = 7;
// When ViewRootImpl#performTraversals() started
private static final int PERFORM_TRAVERSALS_START = 7;
private static final int PERFORM_TRAVERSALS_START = 8;
// When View:draw() started
private static final int DRAW_START = 8;
private static final int DRAW_START = 9;
/** checkstyle */
public void setVsync(long intendedVsync, long usedVsync) {
public void setVsync(long intendedVsync, long usedVsync, long frameTimelineVsyncId) {
frameInfo[FRAME_TIMELINE_VSYNC_ID] = frameTimelineVsyncId;
frameInfo[INTENDED_VSYNC] = intendedVsync;
frameInfo[VSYNC] = usedVsync;
frameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE;

View File

@@ -354,7 +354,8 @@ public class HardwareRenderer {
* @return this instance
*/
public @NonNull FrameRenderRequest setVsyncTime(long vsyncTime) {
mFrameInfo.setVsync(vsyncTime, vsyncTime);
// TODO(b/168552873): populate vsync Id once available to Choreographer public API
mFrameInfo.setVsync(vsyncTime, vsyncTime, FrameInfo.INVALID_VSYNC_ID);
mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
return this;
}

View File

@@ -22,6 +22,7 @@ namespace uirenderer {
const std::string FrameInfoNames[] = {
"Flags",
"FrameTimelineVsyncId",
"IntendedVsync",
"Vsync",
"OldestInputEvent",
@@ -44,7 +45,7 @@ static_assert((sizeof(FrameInfoNames) / sizeof(FrameInfoNames[0])) ==
static_cast<int>(FrameInfoIndex::NumIndexes),
"size mismatch: FrameInfoNames doesn't match the enum!");
static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 17,
static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 18,
"Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
void FrameInfo::importUiThreadInfo(int64_t* info) {

View File

@@ -27,10 +27,11 @@
namespace android {
namespace uirenderer {
#define UI_THREAD_FRAME_INFO_SIZE 9
#define UI_THREAD_FRAME_INFO_SIZE 10
enum class FrameInfoIndex {
Flags = 0,
FrameTimelineVsyncId,
IntendedVsync,
Vsync,
OldestInputEvent,
@@ -71,11 +72,15 @@ enum {
class UiFrameInfoBuilder {
public:
static constexpr int64_t INVALID_VSYNC_ID = -1;
explicit UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) {
memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
set(FrameInfoIndex::FrameTimelineVsyncId) = INVALID_VSYNC_ID;
}
UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync) {
UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync, int64_t vsyncId) {
set(FrameInfoIndex::FrameTimelineVsyncId) = vsyncId;
set(FrameInfoIndex::Vsync) = vsyncTime;
set(FrameInfoIndex::IntendedVsync) = intendedVsync;
// Pretend the other fields are all at vsync, too, so that naive

View File

@@ -514,7 +514,7 @@ static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(
proxy.setLightGeometry((Vector3){0, 0, 0}, 0);
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
UiFrameInfoBuilder(proxy.frameInfo())
.setVsync(vsync, vsync)
.setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID)
.addFlag(FrameInfoFlags::SurfaceCanvas);
proxy.syncAndDrawFrame();
}

View File

@@ -484,6 +484,14 @@ void CanvasContext::draw() {
waitOnFences();
if (mNativeSurface) {
// TODO(b/165985262): measure performance impact
if (const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
native_window_set_frame_timeline_vsync(mNativeSurface->getNativeWindow(), vsyncId);
}
}
bool requireSwap = false;
int error = OK;
bool didSwap =
@@ -617,8 +625,11 @@ void CanvasContext::prepareAndDraw(RenderNode* node) {
ATRACE_CALL();
nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos();
int64_t vsyncId = mRenderThread.timeLord().lastVsyncId();
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
UiFrameInfoBuilder(frameInfo).addFlag(FrameInfoFlags::RTAnimation).setVsync(vsync, vsync);
UiFrameInfoBuilder(frameInfo)
.addFlag(FrameInfoFlags::RTAnimation)
.setVsync(vsync, vsync, vsyncId);
TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node);

View File

@@ -128,7 +128,9 @@ void DrawFrameTask::run() {
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL();
int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
mRenderThread->timeLord().vsyncReceived(vsync);
int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
mRenderThread->timeLord().vsyncReceived(vsync, intendedVsync, vsyncId);
bool canDraw = mContext->makeCurrent();
mContext->unpinImages();

View File

@@ -51,8 +51,10 @@ static JVMAttachHook gOnStartHook = nullptr;
void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
int64_t vsyncId = AChoreographer_getVsyncId(rt->mChoreographer);
rt->mVsyncRequested = false;
if (rt->timeLord().vsyncReceived(frameTimeNanos) && !rt->mFrameCallbackTaskPending) {
if (rt->timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId) &&
!rt->mFrameCallbackTaskPending) {
ATRACE_NAME("queue mFrameCallbackTask");
rt->mFrameCallbackTaskPending = true;
nsecs_t runAt = (frameTimeNanos + rt->mDispatchFrameDelay);

View File

@@ -19,8 +19,8 @@
#include <GrDirectContext.h>
#include <SkBitmap.h>
#include <apex/choreographer.h>
#include <cutils/compiler.h>
#include <private/android/choreographer.h>
#include <thread/ThreadBase.h>
#include <utils/Looper.h>
#include <utils/Thread.h>

View File

@@ -19,9 +19,17 @@ namespace android {
namespace uirenderer {
namespace renderthread {
TimeLord::TimeLord() : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)), mFrameTimeNanos(0) {}
TimeLord::TimeLord() : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)),
mFrameTimeNanos(0),
mFrameIntendedTimeNanos(0),
mFrameVsyncId(-1) {}
bool TimeLord::vsyncReceived(nsecs_t vsync, nsecs_t intendedVsync, int64_t vsyncId) {
if (intendedVsync > mFrameIntendedTimeNanos) {
mFrameIntendedTimeNanos = intendedVsync;
mFrameVsyncId = vsyncId;
}
bool TimeLord::vsyncReceived(nsecs_t vsync) {
if (vsync > mFrameTimeNanos) {
mFrameTimeNanos = vsync;
return true;
@@ -36,6 +44,8 @@ nsecs_t TimeLord::computeFrameTimeNanos() {
if (jitterNanos >= mFrameIntervalNanos) {
nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos;
mFrameTimeNanos = now - lastFrameOffset;
// mFrameVsyncId is not adjusted here as we still want to send
// the vsync id that started this frame to the Surface Composer
}
return mFrameTimeNanos;
}

View File

@@ -32,9 +32,10 @@ public:
nsecs_t frameIntervalNanos() const { return mFrameIntervalNanos; }
// returns true if the vsync is newer, false if it was rejected for staleness
bool vsyncReceived(nsecs_t vsync);
bool vsyncReceived(nsecs_t vsync, nsecs_t indendedVsync, int64_t vsyncId);
nsecs_t latestVsync() { return mFrameTimeNanos; }
nsecs_t computeFrameTimeNanos();
int64_t lastVsyncId() const { return mFrameVsyncId; }
private:
friend class RenderThread;
@@ -44,6 +45,8 @@ private:
nsecs_t mFrameIntervalNanos;
nsecs_t mFrameTimeNanos;
nsecs_t mFrameIntendedTimeNanos;
int64_t mFrameVsyncId;
};
} /* namespace renderthread */

View File

@@ -145,7 +145,8 @@ void run(const TestScene::Info& info, const TestScene::Options& opts,
for (int i = 0; i < warmupFrameCount; i++) {
testContext.waitForVsync();
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
UiFrameInfoBuilder(proxy->frameInfo())
.setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID);
proxy->syncAndDrawFrame();
}
@@ -165,7 +166,8 @@ void run(const TestScene::Info& info, const TestScene::Options& opts,
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
{
ATRACE_NAME("UI-Draw Frame");
UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
UiFrameInfoBuilder(proxy->frameInfo())
.setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID);
scene->doFrame(i);
proxy->syncAndDrawFrame();
}