monado/src/xrt/auxiliary/util/u_timing_render.c

239 lines
6.3 KiB
C
Raw Normal View History

// Copyright 2020-2021, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Shared frame timing code.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup aux_util
*/
#include "util/u_time.h"
#include "util/u_misc.h"
#include "util/u_debug.h"
2020-07-03 13:28:55 +00:00
#include "util/u_logging.h"
2021-01-21 14:57:14 +00:00
#include "util/u_timing_render.h"
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>
/*
*
* Helpers
*
*/
2021-01-21 14:57:14 +00:00
DEBUG_GET_ONCE_LOG_OPTION(ll, "U_TIMING_RENDER_LOG", U_LOGGING_WARN)
#define RT_LOG_T(...) U_LOG_IFL_T(debug_get_log_option_ll(), __VA_ARGS__)
#define RT_LOG_D(...) U_LOG_IFL_D(debug_get_log_option_ll(), __VA_ARGS__)
#define RT_LOG_I(...) U_LOG_IFL_I(debug_get_log_option_ll(), __VA_ARGS__)
#define RT_LOG_W(...) U_LOG_IFL_W(debug_get_log_option_ll(), __VA_ARGS__)
#define RT_LOG_E(...) U_LOG_IFL_E(debug_get_log_option_ll(), __VA_ARGS__)
#define DEBUG_PRINT_FRAME_ID() RT_LOG_T("%" PRIi64, frame_id)
#define GET_INDEX_FROM_ID(URTH, ID) ((uint64_t)(ID) % ARRAY_SIZE((URTH)->frames))
static uint64_t
min_period(const struct u_rt_helper *urth)
{
return urth->last_input.predicted_display_period_ns;
}
static uint64_t
2021-04-02 16:54:22 +00:00
last_sample_displayed(const struct u_rt_helper *urth)
{
return urth->last_input.predicted_display_time_ns;
}
static uint64_t
2021-04-02 16:54:22 +00:00
last_return_predicted_display(const struct u_rt_helper *urth)
{
2021-04-02 16:54:22 +00:00
return urth->last_returned_ns;
}
static uint64_t
total_app_time_ns(const struct u_rt_helper *urth)
{
return urth->app.cpu_time_ns + urth->app.draw_time_ns;
}
static uint64_t
total_app_and_compositor_time_ns(const struct u_rt_helper *urth)
{
return total_app_time_ns(urth) + urth->app.margin_ns + urth->last_input.extra_ns;
}
2021-04-02 16:54:22 +00:00
static uint64_t
predict_display_time(struct u_rt_helper *urth)
{
// Now
uint64_t now_ns = os_monotonic_get_ns();
// Error checking.
uint64_t period_ns = min_period(urth);
if (period_ns == 0) {
assert(false && "Have not yet received and samples from timing driver.");
return now_ns;
2020-08-08 08:51:13 +00:00
}
2021-04-02 16:54:22 +00:00
// Total app and compositor time to produce a frame
uint64_t app_and_compositor_time_ns = total_app_and_compositor_time_ns(urth);
// Start from the last time that the driver displayed something.
uint64_t val = last_sample_displayed(urth);
// Return a time after the last returned display time.
while (val < last_return_predicted_display(urth)) {
val += period_ns;
}
2021-04-02 16:54:22 +00:00
// Have to have enough time to perform app work.
while ((val - app_and_compositor_time_ns) <= now_ns) {
val += period_ns;
}
2020-08-08 08:51:13 +00:00
return val;
}
/*
*
* 'Exported' functions.
*
*/
void
u_rt_helper_client_clear(struct u_rt_helper *urth)
{
for (size_t i = 0; i < ARRAY_SIZE(urth->frames); i++) {
urth->frames[i].state = U_RT_READY;
urth->frames[i].frame_id = -1;
}
2021-04-02 16:54:22 +00:00
urth->app.cpu_time_ns = U_TIME_1MS_IN_NS * 2;
urth->app.draw_time_ns = U_TIME_1MS_IN_NS * 2;
urth->app.margin_ns = U_TIME_1MS_IN_NS / 2;
}
void
u_rt_helper_init(struct u_rt_helper *urth)
{
U_ZERO(urth);
u_rt_helper_client_clear(urth);
}
void
u_rt_helper_predict(struct u_rt_helper *urth,
int64_t *out_frame_id,
2021-04-02 16:54:22 +00:00
uint64_t *out_wake_up_time,
uint64_t *out_predicted_display_time,
uint64_t *out_predicted_display_period)
{
int64_t frame_id = ++urth->frame_counter;
*out_frame_id = frame_id;
DEBUG_PRINT_FRAME_ID();
2021-04-02 16:54:22 +00:00
uint64_t predict_ns = predict_display_time(urth);
urth->last_returned_ns = predict_ns;
2021-04-02 16:54:22 +00:00
*out_wake_up_time = predict_ns - total_app_and_compositor_time_ns(urth);
*out_predicted_display_time = predict_ns;
*out_predicted_display_period = min_period(urth);
size_t index = GET_INDEX_FROM_ID(urth, frame_id);
assert(urth->frames[index].frame_id == -1);
assert(urth->frames[index].state == U_RT_READY);
/*
* When the client should deliver the frame to us, take into account the
* extra time needed by the main loop, plus a bit of extra time.
*/
uint64_t delivery_time_ns = predict_ns - urth->last_input.extra_ns - U_TIME_HALF_MS_IN_NS;
urth->frames[index].when.predicted_ns = os_monotonic_get_ns();
urth->frames[index].state = U_RT_PREDICTED;
urth->frames[index].frame_id = frame_id;
urth->frames[index].predicted_delivery_time_ns = delivery_time_ns;
}
void
2021-04-02 16:54:22 +00:00
u_rt_helper_mark(struct u_rt_helper *urth, int64_t frame_id, enum u_timing_point point, uint64_t when_ns)
{
DEBUG_PRINT_FRAME_ID();
size_t index = GET_INDEX_FROM_ID(urth, frame_id);
assert(urth->frames[index].frame_id == frame_id);
2021-04-02 16:54:22 +00:00
switch (point) {
case U_TIMING_POINT_WAKE_UP:
assert(urth->frames[index].state == U_RT_PREDICTED);
urth->frames[index].when.wait_woke_ns = when_ns;
urth->frames[index].state = U_RT_WAIT_LEFT;
break;
case U_TIMING_POINT_BEGIN:
assert(urth->frames[index].state == U_RT_WAIT_LEFT);
urth->frames[index].when.begin_ns = os_monotonic_get_ns();
urth->frames[index].state = U_RT_BEGUN;
break;
case U_TIMING_POINT_SUBMIT:
default: assert(false);
}
}
void
u_rt_helper_mark_discarded(struct u_rt_helper *urth, int64_t frame_id)
{
DEBUG_PRINT_FRAME_ID();
size_t index = GET_INDEX_FROM_ID(urth, frame_id);
assert(urth->frames[index].frame_id == frame_id);
2021-01-14 14:13:48 +00:00
assert(urth->frames[index].state == U_RT_WAIT_LEFT || urth->frames[index].state == U_RT_BEGUN);
urth->frames[index].when.delivered_ns = os_monotonic_get_ns();
urth->frames[index].state = U_RT_READY;
urth->frames[index].frame_id = -1;
}
void
u_rt_helper_mark_delivered(struct u_rt_helper *urth, int64_t frame_id)
{
DEBUG_PRINT_FRAME_ID();
size_t index = GET_INDEX_FROM_ID(urth, frame_id);
assert(urth->frames[index].frame_id == frame_id);
assert(urth->frames[index].state == U_RT_BEGUN);
uint64_t now_ns = os_monotonic_get_ns();
urth->frames[index].when.delivered_ns = now_ns;
urth->frames[index].state = U_RT_READY;
urth->frames[index].frame_id = -1;
int64_t diff_ns = urth->frames[index].predicted_delivery_time_ns - now_ns;
bool late = false;
if (diff_ns < 0) {
diff_ns = -diff_ns;
late = true;
}
int64_t ms100 = diff_ns / (1000 * 10);
RT_LOG_D("Delivered frame %i.%02ims %s.", (int)ms100 / 100, (int)ms100 % 100, late ? "late" : "early");
}
void
u_rt_helper_new_sample(struct u_rt_helper *urth,
uint64_t predicted_display_time_ns,
uint64_t predicted_display_period_ns,
uint64_t extra_ns)
{
urth->last_input.predicted_display_time_ns = predicted_display_time_ns;
urth->last_input.predicted_display_period_ns = predicted_display_period_ns;
urth->last_input.extra_ns = extra_ns;
}