2021-01-21 14:44:52 +00:00
|
|
|
// Copyright 2020-2021, Collabora, Ltd.
|
2020-06-23 20:26:05 +00:00
|
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
|
|
/*!
|
|
|
|
* @file
|
|
|
|
* @brief Shared frame timing code.
|
|
|
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
|
|
|
* @ingroup aux_util
|
|
|
|
*/
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
#include "os/os_time.h"
|
|
|
|
|
2021-01-21 14:44:52 +00:00
|
|
|
#include "util/u_time.h"
|
2020-06-23 20:26:05 +00:00
|
|
|
#include "util/u_misc.h"
|
2021-01-21 14:44:52 +00:00
|
|
|
#include "util/u_debug.h"
|
2020-07-03 13:28:55 +00:00
|
|
|
#include "util/u_logging.h"
|
2021-04-02 18:43:01 +00:00
|
|
|
#include "util/u_timing.h"
|
2020-06-23 20:26:05 +00:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Structs enums, and defines.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
enum u_rt_state
|
|
|
|
{
|
|
|
|
U_RT_READY,
|
|
|
|
U_RT_WAIT_LEFT,
|
|
|
|
U_RT_PREDICTED,
|
|
|
|
U_RT_BEGUN,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct u_rt_frame
|
|
|
|
{
|
|
|
|
//! When we predicted this frame to be shown.
|
|
|
|
uint64_t predicted_display_time_ns;
|
|
|
|
//! When the client should have delivered the frame.
|
|
|
|
uint64_t predicted_delivery_time_ns;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
uint64_t predicted_ns;
|
|
|
|
uint64_t wait_woke_ns;
|
|
|
|
uint64_t begin_ns;
|
|
|
|
uint64_t delivered_ns;
|
|
|
|
} when; //!< When something happened.
|
|
|
|
|
|
|
|
int64_t frame_id;
|
|
|
|
enum u_rt_state state;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct render_timing
|
|
|
|
{
|
|
|
|
struct u_render_timing base;
|
|
|
|
|
|
|
|
struct u_rt_frame frames[2];
|
|
|
|
uint32_t current_frame;
|
|
|
|
uint32_t next_frame;
|
|
|
|
|
|
|
|
int64_t frame_counter;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
//! App time between wait returning and begin being called.
|
|
|
|
uint64_t cpu_time_ns;
|
|
|
|
//! Time between begin and frame rendering completeing.
|
|
|
|
uint64_t draw_time_ns;
|
|
|
|
//! Exrta time between end of draw time and when the compositor wakes up.
|
|
|
|
uint64_t margin_ns;
|
|
|
|
} app; //!< App statistics.
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
//! The last display time that the thing driving this helper got.
|
|
|
|
uint64_t predicted_display_time_ns;
|
|
|
|
//! The last display period the hardware is running at.
|
|
|
|
uint64_t predicted_display_period_ns;
|
|
|
|
//! The extra time needed by the thing driving this helper.
|
|
|
|
uint64_t extra_ns;
|
|
|
|
} last_input;
|
|
|
|
|
|
|
|
uint64_t last_returned_ns;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2020-06-23 20:26:05 +00:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Helpers
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
static inline struct render_timing *
|
|
|
|
render_timing(struct u_render_timing *urt)
|
|
|
|
{
|
|
|
|
return (struct render_timing *)urt;
|
|
|
|
}
|
|
|
|
|
2021-01-21 14:57:14 +00:00
|
|
|
DEBUG_GET_ONCE_LOG_OPTION(ll, "U_TIMING_RENDER_LOG", U_LOGGING_WARN)
|
2021-01-21 14:44:52 +00:00
|
|
|
|
|
|
|
#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)
|
2021-04-02 18:43:01 +00:00
|
|
|
#define GET_INDEX_FROM_ID(RT, ID) ((uint64_t)(ID) % ARRAY_SIZE((RT)->frames))
|
2020-06-23 20:26:05 +00:00
|
|
|
|
2021-01-21 00:32:36 +00:00
|
|
|
static uint64_t
|
2021-04-02 18:43:01 +00:00
|
|
|
min_period(const struct render_timing *rt)
|
2021-01-21 00:32:36 +00:00
|
|
|
{
|
2021-04-02 18:43:01 +00:00
|
|
|
return rt->last_input.predicted_display_period_ns;
|
2021-01-21 00:32:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t
|
2021-04-02 18:43:01 +00:00
|
|
|
last_sample_displayed(const struct render_timing *rt)
|
2021-01-21 00:32:36 +00:00
|
|
|
{
|
2021-04-02 18:43:01 +00:00
|
|
|
return rt->last_input.predicted_display_time_ns;
|
2021-01-21 00:32:36 +00:00
|
|
|
}
|
|
|
|
|
2020-06-23 20:26:05 +00:00
|
|
|
static uint64_t
|
2021-04-02 18:43:01 +00:00
|
|
|
last_return_predicted_display(const struct render_timing *rt)
|
2020-06-23 20:26:05 +00:00
|
|
|
{
|
2021-04-02 18:43:01 +00:00
|
|
|
return rt->last_returned_ns;
|
2021-04-02 16:54:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t
|
2021-04-02 18:43:01 +00:00
|
|
|
total_app_time_ns(const struct render_timing *rt)
|
2021-04-02 16:54:22 +00:00
|
|
|
{
|
2021-04-02 18:43:01 +00:00
|
|
|
return rt->app.cpu_time_ns + rt->app.draw_time_ns;
|
2021-04-02 16:54:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t
|
2021-04-02 18:43:01 +00:00
|
|
|
total_app_and_compositor_time_ns(const struct render_timing *rt)
|
2021-04-02 16:54:22 +00:00
|
|
|
{
|
2021-04-02 18:43:01 +00:00
|
|
|
return total_app_time_ns(rt) + rt->app.margin_ns + rt->last_input.extra_ns;
|
2021-04-02 16:54:22 +00:00
|
|
|
}
|
2020-06-23 20:26:05 +00:00
|
|
|
|
2021-04-02 16:54:22 +00:00
|
|
|
static uint64_t
|
2021-04-02 18:43:01 +00:00
|
|
|
predict_display_time(const struct render_timing *rt)
|
2021-04-02 16:54:22 +00:00
|
|
|
{
|
|
|
|
// Now
|
|
|
|
uint64_t now_ns = os_monotonic_get_ns();
|
|
|
|
|
|
|
|
// Error checking.
|
2021-04-02 18:43:01 +00:00
|
|
|
uint64_t period_ns = min_period(rt);
|
2021-04-02 16:54:22 +00:00
|
|
|
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
|
2021-04-02 18:43:01 +00:00
|
|
|
uint64_t app_and_compositor_time_ns = total_app_and_compositor_time_ns(rt);
|
2021-04-02 16:54:22 +00:00
|
|
|
|
|
|
|
// Start from the last time that the driver displayed something.
|
2021-04-02 18:43:01 +00:00
|
|
|
uint64_t val = last_sample_displayed(rt);
|
2021-04-02 16:54:22 +00:00
|
|
|
|
|
|
|
// Return a time after the last returned display time.
|
2021-04-02 18:43:01 +00:00
|
|
|
while (val < last_return_predicted_display(rt)) {
|
2021-04-02 16:54:22 +00:00
|
|
|
val += period_ns;
|
2020-06-23 20:26:05 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2020-06-23 20:26:05 +00:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
2021-04-02 18:43:01 +00:00
|
|
|
* Member functions.
|
2020-06-23 20:26:05 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
static void
|
|
|
|
rt_predict(struct u_render_timing *urt,
|
|
|
|
int64_t *out_frame_id,
|
|
|
|
uint64_t *out_wake_up_time,
|
|
|
|
uint64_t *out_predicted_display_time,
|
|
|
|
uint64_t *out_predicted_display_period)
|
2020-06-23 20:26:05 +00:00
|
|
|
{
|
2021-04-02 18:43:01 +00:00
|
|
|
struct render_timing *rt = render_timing(urt);
|
2020-06-23 20:26:05 +00:00
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
int64_t frame_id = ++rt->frame_counter;
|
2020-06-23 20:26:05 +00:00
|
|
|
*out_frame_id = frame_id;
|
|
|
|
|
|
|
|
DEBUG_PRINT_FRAME_ID();
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
uint64_t predict_ns = predict_display_time(rt);
|
2020-06-23 20:26:05 +00:00
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
rt->last_returned_ns = predict_ns;
|
2020-06-23 20:26:05 +00:00
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
*out_wake_up_time = predict_ns - total_app_and_compositor_time_ns(rt);
|
2021-04-02 16:54:22 +00:00
|
|
|
*out_predicted_display_time = predict_ns;
|
2021-04-02 18:43:01 +00:00
|
|
|
*out_predicted_display_period = min_period(rt);
|
2020-06-23 20:26:05 +00:00
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
size_t index = GET_INDEX_FROM_ID(rt, frame_id);
|
|
|
|
assert(rt->frames[index].frame_id == -1);
|
|
|
|
assert(rt->frames[index].state == U_RT_READY);
|
2020-06-23 20:26:05 +00:00
|
|
|
|
2021-01-21 14:44:52 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2021-04-02 18:43:01 +00:00
|
|
|
uint64_t delivery_time_ns = predict_ns - rt->last_input.extra_ns - U_TIME_HALF_MS_IN_NS;
|
2021-01-21 14:44:52 +00:00
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
rt->frames[index].when.predicted_ns = os_monotonic_get_ns();
|
|
|
|
rt->frames[index].state = U_RT_PREDICTED;
|
|
|
|
rt->frames[index].frame_id = frame_id;
|
|
|
|
rt->frames[index].predicted_delivery_time_ns = delivery_time_ns;
|
2020-06-23 20:26:05 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
static void
|
|
|
|
rt_mark_point(struct u_render_timing *urt, int64_t frame_id, enum u_timing_point point, uint64_t when_ns)
|
2020-06-23 20:26:05 +00:00
|
|
|
{
|
2021-04-02 18:43:01 +00:00
|
|
|
struct render_timing *rt = render_timing(urt);
|
|
|
|
|
2020-06-23 20:26:05 +00:00
|
|
|
DEBUG_PRINT_FRAME_ID();
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
size_t index = GET_INDEX_FROM_ID(rt, frame_id);
|
|
|
|
assert(rt->frames[index].frame_id == frame_id);
|
2020-06-23 20:26:05 +00:00
|
|
|
|
2021-04-02 16:54:22 +00:00
|
|
|
switch (point) {
|
|
|
|
case U_TIMING_POINT_WAKE_UP:
|
2021-04-02 18:43:01 +00:00
|
|
|
assert(rt->frames[index].state == U_RT_PREDICTED);
|
2021-04-02 16:54:22 +00:00
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
rt->frames[index].when.wait_woke_ns = when_ns;
|
|
|
|
rt->frames[index].state = U_RT_WAIT_LEFT;
|
2021-04-02 16:54:22 +00:00
|
|
|
break;
|
|
|
|
case U_TIMING_POINT_BEGIN:
|
2021-04-02 18:43:01 +00:00
|
|
|
assert(rt->frames[index].state == U_RT_WAIT_LEFT);
|
2021-04-02 16:54:22 +00:00
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
rt->frames[index].when.begin_ns = os_monotonic_get_ns();
|
|
|
|
rt->frames[index].state = U_RT_BEGUN;
|
2021-04-02 16:54:22 +00:00
|
|
|
break;
|
|
|
|
case U_TIMING_POINT_SUBMIT:
|
|
|
|
default: assert(false);
|
|
|
|
}
|
2020-06-23 20:26:05 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
static void
|
|
|
|
rt_mark_discarded(struct u_render_timing *urt, int64_t frame_id)
|
2020-06-23 20:26:05 +00:00
|
|
|
{
|
2021-04-02 18:43:01 +00:00
|
|
|
struct render_timing *rt = render_timing(urt);
|
|
|
|
|
2020-06-23 20:26:05 +00:00
|
|
|
DEBUG_PRINT_FRAME_ID();
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
size_t index = GET_INDEX_FROM_ID(rt, frame_id);
|
|
|
|
assert(rt->frames[index].frame_id == frame_id);
|
|
|
|
assert(rt->frames[index].state == U_RT_WAIT_LEFT || rt->frames[index].state == U_RT_BEGUN);
|
2020-06-23 20:26:05 +00:00
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
rt->frames[index].when.delivered_ns = os_monotonic_get_ns();
|
|
|
|
rt->frames[index].state = U_RT_READY;
|
|
|
|
rt->frames[index].frame_id = -1;
|
2020-06-23 20:26:05 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
static void
|
|
|
|
rt_mark_delivered(struct u_render_timing *urt, int64_t frame_id)
|
2020-06-23 20:26:05 +00:00
|
|
|
{
|
2021-04-02 18:43:01 +00:00
|
|
|
struct render_timing *rt = render_timing(urt);
|
|
|
|
|
2020-06-23 20:26:05 +00:00
|
|
|
DEBUG_PRINT_FRAME_ID();
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
size_t index = GET_INDEX_FROM_ID(rt, frame_id);
|
|
|
|
assert(rt->frames[index].frame_id == frame_id);
|
|
|
|
assert(rt->frames[index].state == U_RT_BEGUN);
|
2020-06-23 20:26:05 +00:00
|
|
|
|
|
|
|
uint64_t now_ns = os_monotonic_get_ns();
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
rt->frames[index].when.delivered_ns = now_ns;
|
|
|
|
rt->frames[index].state = U_RT_READY;
|
|
|
|
rt->frames[index].frame_id = -1;
|
2020-06-23 20:26:05 +00:00
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
int64_t diff_ns = rt->frames[index].predicted_delivery_time_ns - now_ns;
|
2021-01-21 14:44:52 +00:00
|
|
|
bool late = false;
|
|
|
|
if (diff_ns < 0) {
|
|
|
|
diff_ns = -diff_ns;
|
|
|
|
late = true;
|
|
|
|
}
|
2020-06-23 20:26:05 +00:00
|
|
|
|
2021-01-21 14:44:52 +00:00
|
|
|
int64_t ms100 = diff_ns / (1000 * 10);
|
|
|
|
RT_LOG_D("Delivered frame %i.%02ims %s.", (int)ms100 / 100, (int)ms100 % 100, late ? "late" : "early");
|
2020-06-23 20:26:05 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 18:43:01 +00:00
|
|
|
static void
|
|
|
|
rt_info(struct u_render_timing *urt,
|
|
|
|
uint64_t predicted_display_time_ns,
|
|
|
|
uint64_t predicted_display_period_ns,
|
|
|
|
uint64_t extra_ns)
|
2020-06-23 20:26:05 +00:00
|
|
|
{
|
2021-04-02 18:43:01 +00:00
|
|
|
struct render_timing *rt = render_timing(urt);
|
|
|
|
|
|
|
|
rt->last_input.predicted_display_time_ns = predicted_display_time_ns;
|
|
|
|
rt->last_input.predicted_display_period_ns = predicted_display_period_ns;
|
|
|
|
rt->last_input.extra_ns = extra_ns;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
rt_destroy(struct u_render_timing *urt)
|
|
|
|
{
|
|
|
|
free(urt);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* 'Exported' functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
xrt_result_t
|
|
|
|
u_rt_create(struct u_render_timing **out_urt)
|
|
|
|
{
|
|
|
|
struct render_timing *rt = U_TYPED_CALLOC(struct render_timing);
|
|
|
|
rt->base.predict = rt_predict;
|
|
|
|
rt->base.mark_point = rt_mark_point;
|
|
|
|
rt->base.mark_discarded = rt_mark_discarded;
|
|
|
|
rt->base.mark_delivered = rt_mark_delivered;
|
|
|
|
rt->base.info = rt_info;
|
|
|
|
rt->base.destroy = rt_destroy;
|
|
|
|
rt->app.cpu_time_ns = U_TIME_1MS_IN_NS * 2;
|
|
|
|
rt->app.draw_time_ns = U_TIME_1MS_IN_NS * 2;
|
|
|
|
rt->app.margin_ns = U_TIME_1MS_IN_NS / 2;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(rt->frames); i++) {
|
|
|
|
rt->frames[i].state = U_RT_READY;
|
|
|
|
rt->frames[i].frame_id = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out_urt = &rt->base;
|
|
|
|
|
|
|
|
return XRT_SUCCESS;
|
2020-06-23 20:26:05 +00:00
|
|
|
}
|