From 83081f9cc179882c9ff89a3780c5fdd7900b989f Mon Sep 17 00:00:00 2001 From: Jakob Bornecrantz Date: Tue, 23 Jun 2020 21:26:05 +0100 Subject: [PATCH] u/render_timing: Add helper code for frame timing --- src/xrt/auxiliary/CMakeLists.txt | 2 + src/xrt/auxiliary/meson.build | 2 + src/xrt/auxiliary/util/u_render_timing.c | 188 +++++++++++++++++++++++ src/xrt/auxiliary/util/u_render_timing.h | 87 +++++++++++ 4 files changed, 279 insertions(+) create mode 100644 src/xrt/auxiliary/util/u_render_timing.c create mode 100644 src/xrt/auxiliary/util/u_render_timing.h diff --git a/src/xrt/auxiliary/CMakeLists.txt b/src/xrt/auxiliary/CMakeLists.txt index c3636d99b..724977223 100644 --- a/src/xrt/auxiliary/CMakeLists.txt +++ b/src/xrt/auxiliary/CMakeLists.txt @@ -89,6 +89,8 @@ set(UTIL_SOURCE_FILES util/u_hashset.h util/u_json.c util/u_json.h + util/u_render_timing.c + util/u_render_timing.h util/u_sink.h util/u_sink_converter.c util/u_sink_deinterleaver.c diff --git a/src/xrt/auxiliary/meson.build b/src/xrt/auxiliary/meson.build index 8712ae8b3..782f4403f 100644 --- a/src/xrt/auxiliary/meson.build +++ b/src/xrt/auxiliary/meson.build @@ -29,6 +29,8 @@ lib_aux_util = static_library( 'util/u_json.h', 'util/u_misc.c', 'util/u_misc.h', + 'util/u_render_timing.c', + 'util/u_render_timing.h', 'util/u_sink.h', 'util/u_sink_converter.c', 'util/u_sink_deinterleaver.c', diff --git a/src/xrt/auxiliary/util/u_render_timing.c b/src/xrt/auxiliary/util/u_render_timing.c new file mode 100644 index 000000000..9e630b796 --- /dev/null +++ b/src/xrt/auxiliary/util/u_render_timing.c @@ -0,0 +1,188 @@ +// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Shared frame timing code. + * @author Jakob Bornecrantz + * @ingroup aux_util + */ + +#include "util/u_misc.h" + +#include "u_render_timing.h" + +#include +#include +#include + + +/* + * + * Helpers + * + */ + +#if 0 +#define DEBUG_PRINT_FRAME_ID() + fprintf(stderr, "%s %" PRIi64 "\n", __func__, frame_id) +#else +#define DEBUG_PRINT_FRAME_ID() \ + do { \ + } while (false) +#endif + +static uint64_t +get_last_input_plus_period_at_least_greater_then(struct u_rt_helper *urth, + uint64_t then_ns) +{ + uint64_t val = urth->last_input; + + while (val <= then_ns) { + val += urth->period; + } + + return val; +} + + +/* + * + * 'Exported' functions. + * + */ + +void +u_rt_helper_clear(struct u_rt_helper *urth) +{ + U_ZERO(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; + } +} + +void +u_rt_helper_init(struct u_rt_helper *urth) +{ + u_rt_helper_clear(urth); +} + +void +u_rt_helper_predict(struct u_rt_helper *urth, + int64_t *out_frame_id, + uint64_t *predicted_display_time, + uint64_t *wake_up_time, + uint64_t *predicted_display_period, + uint64_t *min_display_period) +{ + int64_t frame_id = ++urth->frame_counter; + *out_frame_id = frame_id; + + DEBUG_PRINT_FRAME_ID(); + + uint64_t at_least_ns = os_monotonic_get_ns(); + + // Don't return a time before the last returned type. + if (at_least_ns < urth->last_returned) { + at_least_ns = urth->last_returned; + } + + uint64_t predict_ns = + get_last_input_plus_period_at_least_greater_then(urth, at_least_ns); + + urth->last_returned = predict_ns; + + *wake_up_time = predict_ns - urth->period; + *predicted_display_time = predict_ns; + *predicted_display_period = urth->period; + *min_display_period = urth->period; + + size_t index = (uint64_t)frame_id % ARRAY_SIZE(urth->frames); + assert(urth->frames[index].frame_id == -1); + assert(urth->frames[index].state == U_RT_READY); + + urth->frames[index].predicted = os_monotonic_get_ns(); + urth->frames[index].state = U_RT_PREDICTED; + urth->frames[index].frame_id = frame_id; +} + +void +u_rt_helper_mark_wait_woke(struct u_rt_helper *urth, int64_t frame_id) +{ + size_t index = (uint64_t)frame_id % ARRAY_SIZE(urth->frames); + assert(urth->frames[index].frame_id == frame_id); + assert(urth->frames[index].state == U_RT_PREDICTED); + + urth->frames[index].wait_woke = os_monotonic_get_ns(); + urth->frames[index].state = U_RT_WAIT_LEFT; +} + +void +u_rt_helper_mark_begin(struct u_rt_helper *urth, int64_t frame_id) +{ + DEBUG_PRINT_FRAME_ID(); + DEBUG_PRINT_FRAME_ID(); + + size_t index = (uint64_t)frame_id % ARRAY_SIZE(urth->frames); + assert(urth->frames[index].frame_id == frame_id); + assert(urth->frames[index].state == U_RT_WAIT_LEFT); + + urth->frames[index].begin = os_monotonic_get_ns(); + urth->frames[index].state = U_RT_BEGUN; +} + +void +u_rt_helper_mark_discarded(struct u_rt_helper *urth, int64_t frame_id) +{ + DEBUG_PRINT_FRAME_ID(); + + size_t index = (uint64_t)frame_id % ARRAY_SIZE(urth->frames); + assert(urth->frames[index].frame_id == frame_id); + assert(urth->frames[index].state == U_RT_WAIT_LEFT || + urth->frames[index].state == U_RT_BEGUN); + + urth->frames[index].end_frame = 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 = (uint64_t)frame_id % ARRAY_SIZE(urth->frames); + 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].end_frame = now_ns; + urth->frames[index].state = U_RT_READY; + urth->frames[index].frame_id = -1; + +#if 0 + uint64_t then_ns = urth->frames[index].wait_woke; + uint64_t diff_ns = now_ns - then_ns; + uint64_t ms100 = diff_ns / (1000 * 10); + + fprintf(stderr, "%s: Diff %i.%02ims\n", __func__, (int)ms100 / 100, + (int)ms100 % 100); +#endif +} + +void +u_rt_helper_new_sample(struct u_rt_helper *urth, + uint64_t predict, + uint64_t extra, + uint64_t min_period) +{ + urth->last_input = predict; + urth->extra = extra; + urth->period = min_period; + + if (urth->last_returned == 0) { + return; + } +} diff --git a/src/xrt/auxiliary/util/u_render_timing.h b/src/xrt/auxiliary/util/u_render_timing.h new file mode 100644 index 000000000..b9b654b2a --- /dev/null +++ b/src/xrt/auxiliary/util/u_render_timing.h @@ -0,0 +1,87 @@ +// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Shared frame timing code. + * @author Jakob Bornecrantz + * @ingroup aux_util + */ + +#pragma once + +#include "xrt/xrt_compiler.h" +#include "os/os_time.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +enum u_rt_state +{ + U_RT_READY, + U_RT_WAIT_LEFT, + U_RT_PREDICTED, + U_RT_BEGUN, +}; + +struct u_rt_frame +{ + uint64_t predicted; + uint64_t wait_woke; + uint64_t begin; + uint64_t end_frame; + int64_t frame_id; + enum u_rt_state state; +}; + +struct u_rt_helper +{ + struct u_rt_frame frames[2]; + uint32_t current_frame; + uint32_t next_frame; + + int64_t frame_counter; + + uint64_t extra; + uint64_t period; + uint64_t last_input; + uint64_t last_returned; +}; + +void +u_rt_helper_init(struct u_rt_helper *urth); + +void +u_rt_helper_clear(struct u_rt_helper *urth); + +void +u_rt_helper_predict(struct u_rt_helper *urth, + int64_t *out_frame_id, + uint64_t *out_predicted_display_time, + uint64_t *out_wake_up_time, + uint64_t *out_predicted_display_period, + uint64_t *out_min_display_period); + +void +u_rt_helper_mark_wait_woke(struct u_rt_helper *urth, int64_t frame_id); + +void +u_rt_helper_mark_begin(struct u_rt_helper *urth, int64_t frame_id); + +void +u_rt_helper_mark_discarded(struct u_rt_helper *urth, int64_t frame_id); + +void +u_rt_helper_mark_delivered(struct u_rt_helper *urth, int64_t frame_id); + +void +u_rt_helper_new_sample(struct u_rt_helper *urth, + uint64_t predict, + uint64_t extra, + uint64_t min_period); + + +#ifdef __cplusplus +} +#endif