a/util: Have all timestamps be injected into pacing.

No more calling os_monotonic_get_ns() inside the pacing helpers.
Much better testability.
This commit is contained in:
Ryan Pavlik 2022-01-18 14:48:43 -06:00
parent 40caa24d4b
commit a2e1eb7e75
6 changed files with 74 additions and 50 deletions

View file

@ -62,6 +62,8 @@ struct u_pacing_compositor
* Predict the next frame.
*
* @param[in] upc The compositor pacing helper.
* @param[in] now_ns The current timestamp in nanoseconds, nominally from @ref
* os_monotonic_get_ns
* @param[out] out_frame_id Id used to refer to this frame again.
* @param[out] out_wake_up_time_ns When should the compositor wake up.
* @param[out] out_desired_present_time_ns The GPU should start scanning out at this time.
@ -73,6 +75,7 @@ struct u_pacing_compositor
* @see @ref frame-pacing.
*/
void (*predict)(struct u_pacing_compositor *upc,
uint64_t now_ns,
int64_t *out_frame_id,
uint64_t *out_wake_up_time_ns,
uint64_t *out_desired_present_time_ns,
@ -118,6 +121,8 @@ struct u_pacing_compositor
* actual_present_time_ns if a @p desired_present_time_ns was passed.
* @param[in] present_margin_ns How "early" present happened compared to when it needed to happen in
* order to hit @p earliestPresentTime.
* @param[in] when_ns The time when we got the info, nominally from @ref
* os_monotonic_get_ns
*
* @see @ref frame-pacing.
*/
@ -126,7 +131,8 @@ struct u_pacing_compositor
uint64_t desired_present_time_ns,
uint64_t actual_present_time_ns,
uint64_t earliest_present_time_ns,
uint64_t present_margin_ns);
uint64_t present_margin_ns,
uint64_t when_ns);
/*!
* Destroy this u_pacing_compositor.
@ -144,6 +150,7 @@ struct u_pacing_compositor
*/
static inline void
u_pc_predict(struct u_pacing_compositor *upc,
uint64_t now_ns,
int64_t *out_frame_id,
uint64_t *out_wake_up_time_ns,
uint64_t *out_desired_present_time_ns,
@ -153,6 +160,7 @@ u_pc_predict(struct u_pacing_compositor *upc,
uint64_t *out_min_display_period_ns)
{
upc->predict(upc, //
now_ns, //
out_frame_id, //
out_wake_up_time_ns, //
out_desired_present_time_ns, //
@ -190,10 +198,11 @@ u_pc_info(struct u_pacing_compositor *upc,
uint64_t desired_present_time_ns,
uint64_t actual_present_time_ns,
uint64_t earliest_present_time_ns,
uint64_t present_margin_ns)
uint64_t present_margin_ns,
uint64_t when_ns)
{
upc->info(upc, frame_id, desired_present_time_ns, actual_present_time_ns, earliest_present_time_ns,
present_margin_ns);
present_margin_ns, when_ns);
}
/*!
@ -244,12 +253,15 @@ struct u_pacing_app
* should wait till `out_wake_up_time`.
*
* @param upa Render timing helper.
* @param[in] now_ns The current timestamp in nanoseconds, nominally from @ref
* os_monotonic_get_ns
* @param[out] out_frame_id Frame ID of this predicted frame.
* @param[out] out_wake_up_time When the client should be woken up.
* @param[out] out_predicted_display_time Predicted display time.
* @param[out] out_predicted_display_period Predicted display period.
*/
void (*predict)(struct u_pacing_app *upa,
uint64_t now_ns,
int64_t *out_frame_id,
uint64_t *out_wake_up_time,
uint64_t *out_predicted_display_time,
@ -272,8 +284,9 @@ struct u_pacing_app
*
* @param upa Render timing helper.
* @param[in] frame_id The frame ID to mark as discarded.
* @param[in] when_ns The time when it was discarded, nominally from @ref os_monotonic_get_ns
*/
void (*mark_discarded)(struct u_pacing_app *upa, int64_t frame_id);
void (*mark_discarded)(struct u_pacing_app *upa, int64_t frame_id, uint64_t when_ns);
/*!
* A frame has been delivered from the client, see `xrEndFrame`. The GPU might
@ -281,8 +294,9 @@ struct u_pacing_app
*
* @param upa Render timing helper.
* @param[in] frame_id The frame ID to mark as delivered.
* @param[in] when_ns The time when it was delivered, nominally from @ref os_monotonic_get_ns
*/
void (*mark_delivered)(struct u_pacing_app *upa, int64_t frame_id);
void (*mark_delivered)(struct u_pacing_app *upa, int64_t frame_id, uint64_t when_ns);
/*!
* Add a new sample point from the main render loop.
@ -323,12 +337,14 @@ struct u_pacing_app
*/
static inline void
u_pa_predict(struct u_pacing_app *upa,
uint64_t now_ns,
int64_t *out_frame_id,
uint64_t *out_wake_up_time,
uint64_t *out_predicted_display_time,
uint64_t *out_predicted_display_period)
{
upa->predict(upa, out_frame_id, out_wake_up_time, out_predicted_display_time, out_predicted_display_period);
upa->predict(upa, now_ns, out_frame_id, out_wake_up_time, out_predicted_display_time,
out_predicted_display_period);
}
/*!
@ -354,9 +370,9 @@ u_pa_mark_point(struct u_pacing_app *upa, int64_t frame_id, enum u_timing_point
* @ingroup aux_pacing
*/
static inline void
u_pa_mark_discarded(struct u_pacing_app *upa, int64_t frame_id)
u_pa_mark_discarded(struct u_pacing_app *upa, int64_t frame_id, uint64_t when_ns)
{
upa->mark_discarded(upa, frame_id);
upa->mark_discarded(upa, frame_id, when_ns);
}
/*!
@ -368,9 +384,9 @@ u_pa_mark_discarded(struct u_pacing_app *upa, int64_t frame_id)
* @ingroup aux_pacing
*/
static inline void
u_pa_mark_delivered(struct u_pacing_app *upa, int64_t frame_id)
u_pa_mark_delivered(struct u_pacing_app *upa, int64_t frame_id, uint64_t when_ns)
{
upa->mark_delivered(upa, frame_id);
upa->mark_delivered(upa, frame_id, when_ns);
}
/*!
@ -477,11 +493,15 @@ u_pc_display_timing_create(uint64_t estimated_frame_period_ns,
*
* When you cannot get display timing information, use this.
*
* @param[in] estimated_frame_period_ns The estimated duration/period of a frame in nanoseconds.
* @param[in] now_ns The current timestamp in nanoseconds, nominally from @ref os_monotonic_get_ns
* @param[out] out_upc The pointer to populate with the created compositor pacing helper
*
* @ingroup aux_pacing
* @see u_pacing_compositor
*/
xrt_result_t
u_pc_fake_create(uint64_t estimated_frame_period_ns, struct u_pacing_compositor **out_upc);
u_pc_fake_create(uint64_t estimated_frame_period_ns, uint64_t now_ns, struct u_pacing_compositor **out_upc);
/*!
* Creates a new application pacing helper.

View file

@ -188,11 +188,8 @@ calc_period(const struct pacing_app *pa)
}
static uint64_t
predict_display_time(const struct pacing_app *pa, uint64_t period_ns)
predict_display_time(const struct pacing_app *pa, uint64_t now_ns, uint64_t period_ns)
{
// Now
uint64_t now_ns = os_monotonic_get_ns();
// Total app and compositor time to produce a frame
uint64_t app_and_compositor_time_ns = total_app_and_compositor_time_ns(pa);
@ -222,6 +219,7 @@ predict_display_time(const struct pacing_app *pa, uint64_t period_ns)
static void
pa_predict(struct u_pacing_app *upa,
uint64_t now_ns,
int64_t *out_frame_id,
uint64_t *out_wake_up_time,
uint64_t *out_predicted_display_time,
@ -235,7 +233,7 @@ pa_predict(struct u_pacing_app *upa,
DEBUG_PRINT_FRAME_ID();
uint64_t period_ns = calc_period(pa);
uint64_t predict_ns = predict_display_time(pa, period_ns);
uint64_t predict_ns = predict_display_time(pa, now_ns, period_ns);
// When should the client wake up.
uint64_t wake_up_time_ns = predict_ns - total_app_and_compositor_time_ns(pa);
// When the client should deliver the frame to us.
@ -255,7 +253,7 @@ pa_predict(struct u_pacing_app *upa,
pa->frames[index].frame_id = frame_id;
pa->frames[index].predicted_delivery_time_ns = delivery_time_ns;
pa->frames[index].predicted_display_period_ns = period_ns;
pa->frames[index].when.predicted_ns = os_monotonic_get_ns();
pa->frames[index].when.predicted_ns = now_ns;
}
static void
@ -278,7 +276,7 @@ pa_mark_point(struct u_pacing_app *upa, int64_t frame_id, enum u_timing_point po
case U_TIMING_POINT_BEGIN:
assert(pa->frames[index].state == U_RT_WAIT_LEFT);
pa->frames[index].when.begin_ns = os_monotonic_get_ns();
pa->frames[index].when.begin_ns = when_ns;
pa->frames[index].state = U_RT_BEGUN;
break;
case U_TIMING_POINT_SUBMIT:
@ -287,7 +285,7 @@ pa_mark_point(struct u_pacing_app *upa, int64_t frame_id, enum u_timing_point po
}
static void
pa_mark_discarded(struct u_pacing_app *upa, int64_t frame_id)
pa_mark_discarded(struct u_pacing_app *upa, int64_t frame_id, uint64_t when_ns)
{
struct pacing_app *pa = pacing_app(upa);
@ -297,13 +295,13 @@ pa_mark_discarded(struct u_pacing_app *upa, int64_t frame_id)
assert(pa->frames[index].frame_id == frame_id);
assert(pa->frames[index].state == U_RT_WAIT_LEFT || pa->frames[index].state == U_RT_BEGUN);
pa->frames[index].when.delivered_ns = os_monotonic_get_ns();
pa->frames[index].when.delivered_ns = when_ns;
pa->frames[index].state = U_PA_READY;
pa->frames[index].frame_id = -1;
}
static void
pa_mark_delivered(struct u_pacing_app *upa, int64_t frame_id)
pa_mark_delivered(struct u_pacing_app *upa, int64_t frame_id, uint64_t when_ns)
{
struct pacing_app *pa = pacing_app(upa);
@ -314,17 +312,15 @@ pa_mark_delivered(struct u_pacing_app *upa, int64_t frame_id)
assert(f->frame_id == frame_id);
assert(f->state == U_RT_BEGUN);
uint64_t now_ns = os_monotonic_get_ns();
// Update all data.
f->when.delivered_ns = now_ns;
f->when.delivered_ns = when_ns;
/*
* Process data.
*/
int64_t diff_ns = f->predicted_delivery_time_ns - now_ns;
int64_t diff_ns = f->predicted_delivery_time_ns - when_ns;
bool late = false;
if (diff_ns < 0) {
diff_ns = -diff_ns;

View file

@ -259,13 +259,12 @@ get_latest_frame_with_state_at_least(struct display_timing *dt, enum frame_state
* frame::desired_present_time_ns (with a crude estimate) and frame::when_predict_ns.
*/
static struct frame *
do_clean_slate_frame(struct display_timing *dt)
do_clean_slate_frame(struct display_timing *dt, uint64_t now_ns)
{
struct frame *f = create_frame(dt, STATE_PREDICTED);
uint64_t now_ns = os_monotonic_get_ns();
// Wild shot in the dark.
uint64_t the_time_ns = os_monotonic_get_ns() + dt->frame_period_ns * 10;
uint64_t the_time_ns = now_ns + dt->frame_period_ns * 10;
f->when_predict_ns = now_ns;
f->desired_present_time_ns = the_time_ns;
@ -277,9 +276,8 @@ do_clean_slate_frame(struct display_timing *dt)
* prediction in it.
*/
static struct frame *
walk_forward_through_frames(struct display_timing *dt, uint64_t last_present_time_ns)
walk_forward_through_frames(struct display_timing *dt, uint64_t last_present_time_ns, uint64_t now_ns)
{
uint64_t now_ns = os_monotonic_get_ns();
// This is the earliest possible time we could present, assuming rendering still must take place.
uint64_t from_time_ns = now_ns + calc_total_app_time(dt);
uint64_t desired_present_time_ns = last_present_time_ns + dt->frame_period_ns;
@ -306,17 +304,17 @@ walk_forward_through_frames(struct display_timing *dt, uint64_t last_present_tim
}
static struct frame *
predict_next_frame(struct display_timing *dt)
predict_next_frame(struct display_timing *dt, uint64_t now_ns)
{
struct frame *f = NULL;
// Last earliest display time, can be zero.
struct frame *last_predicted = get_latest_frame_with_state_at_least(dt, STATE_PREDICTED);
struct frame *last_completed = get_latest_frame_with_state_at_least(dt, STATE_INFO);
if (last_predicted == NULL && last_completed == NULL) {
f = do_clean_slate_frame(dt);
f = do_clean_slate_frame(dt, now_ns);
} else if (last_completed == last_predicted) {
// Very high propability that we missed a frame.
f = walk_forward_through_frames(dt, last_completed->earliest_present_time_ns);
f = walk_forward_through_frames(dt, last_completed->earliest_present_time_ns, now_ns);
} else if (last_completed != NULL) {
assert(last_predicted != NULL);
assert(last_predicted->frame_id > last_completed->frame_id);
@ -342,11 +340,11 @@ predict_next_frame(struct display_timing *dt)
diff_id = 1;
}
f = walk_forward_through_frames(dt, adjusted_last_present_time_ns);
f = walk_forward_through_frames(dt, adjusted_last_present_time_ns, now_ns);
} else {
assert(last_predicted != NULL);
f = walk_forward_through_frames(dt, last_predicted->predicted_display_time_ns);
f = walk_forward_through_frames(dt, last_predicted->predicted_display_time_ns, now_ns);
}
f->predicted_display_time_ns = calc_display_time_from_present_time(dt, f->desired_present_time_ns);
@ -404,6 +402,7 @@ adjust_app_time(struct display_timing *dt, struct frame *f)
static void
dt_predict(struct u_pacing_compositor *upc,
uint64_t now_ns,
int64_t *out_frame_id,
uint64_t *out_wake_up_time_ns,
uint64_t *out_desired_present_time_ns,
@ -414,7 +413,7 @@ dt_predict(struct u_pacing_compositor *upc,
{
struct display_timing *dt = display_timing(upc);
struct frame *f = predict_next_frame(dt);
struct frame *f = predict_next_frame(dt, now_ns);
uint64_t wake_up_time_ns = f->wake_up_time_ns;
uint64_t desired_present_time_ns = f->desired_present_time_ns;
@ -464,7 +463,8 @@ dt_info(struct u_pacing_compositor *upc,
uint64_t desired_present_time_ns,
uint64_t actual_present_time_ns,
uint64_t earliest_present_time_ns,
uint64_t present_margin_ns)
uint64_t present_margin_ns,
uint64_t when_ns)
{
struct display_timing *dt = display_timing(upc);
(void)dt;
@ -481,7 +481,7 @@ dt_info(struct u_pacing_compositor *upc,
assert(f->state == STATE_SUBMITTED);
assert(f->desired_present_time_ns == desired_present_time_ns);
f->when_infoed_ns = os_monotonic_get_ns();
f->when_infoed_ns = when_ns;
f->actual_present_time_ns = actual_present_time_ns;
f->earliest_present_time_ns = earliest_present_time_ns;
f->present_margin_ns = present_margin_ns;

View file

@ -69,10 +69,9 @@ fake_timing(struct u_pacing_compositor *upc)
}
static uint64_t
predict_next_frame(struct fake_timing *ft)
predict_next_frame(struct fake_timing *ft, uint64_t now_ns)
{
uint64_t time_needed_ns = ft->present_offset_ns + ft->app_time_ns;
uint64_t now_ns = os_monotonic_get_ns();
uint64_t predicted_display_time_ns = ft->last_display_time_ns + ft->frame_period_ns;
while (now_ns + time_needed_ns > predicted_display_time_ns) {
@ -98,6 +97,7 @@ get_percent_of_time(uint64_t time_ns, uint32_t fraction_percent)
static void
pc_predict(struct u_pacing_compositor *upc,
uint64_t now_ns,
int64_t *out_frame_id,
uint64_t *out_wake_up_time_ns,
uint64_t *out_desired_present_time_ns,
@ -109,7 +109,7 @@ pc_predict(struct u_pacing_compositor *upc,
struct fake_timing *ft = fake_timing(upc);
int64_t frame_id = ft->frame_id_generator++;
uint64_t predicted_display_time_ns = predict_next_frame(ft);
uint64_t predicted_display_time_ns = predict_next_frame(ft, now_ns);
uint64_t desired_present_time_ns = predicted_display_time_ns - ft->present_offset_ns;
uint64_t wake_up_time_ns = desired_present_time_ns - ft->app_time_ns;
uint64_t present_slop_ns = U_TIME_HALF_MS_IN_NS;
@ -143,7 +143,8 @@ pc_info(struct u_pacing_compositor *upc,
uint64_t desired_present_time_ns,
uint64_t actual_present_time_ns,
uint64_t earliest_present_time_ns,
uint64_t present_margin_ns)
uint64_t present_margin_ns,
uint64_t when_ns)
{
/*
* The compositor might call this function because it selected the
@ -166,7 +167,7 @@ pc_destroy(struct u_pacing_compositor *upc)
*/
xrt_result_t
u_pc_fake_create(uint64_t estimated_frame_period_ns, struct u_pacing_compositor **out_uft)
u_pc_fake_create(uint64_t estimated_frame_period_ns, uint64_t now_ns, struct u_pacing_compositor **out_uft)
{
struct fake_timing *ft = U_TYPED_CALLOC(struct fake_timing);
ft->base.predict = pc_predict;
@ -185,7 +186,7 @@ u_pc_fake_create(uint64_t estimated_frame_period_ns, struct u_pacing_compositor
ft->app_time_ns = get_percent_of_time(estimated_frame_period_ns, 20);
// Make the next display time be in the future.
ft->last_display_time_ns = os_monotonic_get_ns() + U_TIME_1MS_IN_NS * 50.0;
ft->last_display_time_ns = now_ns + U_TIME_1MS_IN_NS * 50.0;
// Return value.
*out_uft = &ft->base;

View file

@ -123,13 +123,14 @@ comp_target_swapchain_create_images(struct comp_target *ct,
VkBool32 supported;
VkResult ret;
uint64_t now_ns = os_monotonic_get_ns();
// Some platforms really don't like the display_timing code.
bool use_display_timing_if_available = cts->timing_usage == COMP_TARGET_USE_DISPLAY_IF_AVAILABLE;
if (cts->upc == NULL && use_display_timing_if_available && vk->has_GOOGLE_display_timing) {
u_pc_display_timing_create(ct->c->settings.nominal_frame_interval_ns,
&U_PC_DISPLAY_TIMING_CONFIG_DEFAULT, &cts->upc);
} else if (cts->upc == NULL) {
u_pc_fake_create(ct->c->settings.nominal_frame_interval_ns, &cts->upc);
u_pc_fake_create(ct->c->settings.nominal_frame_interval_ns, now_ns, &cts->upc);
}
// Free old image views.
@ -598,8 +599,10 @@ comp_target_swapchain_calc_frame_pacing(struct comp_target *ct,
uint64_t predicted_display_time_ns = 0;
uint64_t predicted_display_period_ns = 0;
uint64_t min_display_period_ns = 0;
uint64_t now_ns = os_monotonic_get_ns();
u_pc_predict(cts->upc, //
now_ns, //
&frame_id, //
&wake_up_time_ns, //
&desired_present_time_ns, //
@ -670,14 +673,15 @@ comp_target_swapchain_update_timings(struct comp_target *ct)
cts->swapchain.handle, //
&count, //
timings); //
uint64_t now_ns = os_monotonic_get_ns();
for (uint32_t i = 0; i < count; i++) {
u_pc_info(cts->upc, //
timings[i].presentID, //
timings[i].desiredPresentTime, //
timings[i].actualPresentTime, //
timings[i].earliestPresentTime, //
timings[i].presentMargin); //
timings[i].presentMargin, //
now_ns); //
}
free(timings);
return VK_SUCCESS;

View file

@ -194,11 +194,12 @@ multi_compositor_predict_frame(struct xrt_compositor *xc,
COMP_TRACE_MARKER();
struct multi_compositor *mc = multi_compositor(xc);
uint64_t now_ns = os_monotonic_get_ns();
os_mutex_lock(&mc->msc->list_and_timing_lock);
u_pa_predict( //
mc->upa, //
now_ns, //
out_frame_id, //
out_wake_time_ns, //
out_predicted_display_time_ns, //
@ -290,9 +291,10 @@ multi_compositor_discard_frame(struct xrt_compositor *xc, int64_t frame_id)
COMP_TRACE_MARKER();
struct multi_compositor *mc = multi_compositor(xc);
uint64_t now_ns = os_monotonic_get_ns();
os_mutex_lock(&mc->msc->list_and_timing_lock);
u_pa_mark_discarded(mc->upa, frame_id);
u_pa_mark_discarded(mc->upa, frame_id, now_ns);
os_mutex_unlock(&mc->msc->list_and_timing_lock);
return XRT_SUCCESS;
@ -507,9 +509,10 @@ multi_compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_g
}
wait_for_scheduled_free(mc);
uint64_t now_ns = os_monotonic_get_ns();
os_mutex_lock(&mc->msc->list_and_timing_lock);
u_pa_mark_delivered(mc->upa, frame_id);
u_pa_mark_delivered(mc->upa, frame_id, now_ns);
os_mutex_unlock(&mc->msc->list_and_timing_lock);
return XRT_SUCCESS;