From 8215af5e5c0aabeb78d4249689cd0e2d983947f6 Mon Sep 17 00:00:00 2001
From: Jakob Bornecrantz <jakob@collabora.com>
Date: Fri, 2 Apr 2021 19:43:01 +0100
Subject: [PATCH] u/rt: Refactor render timing

---
 src/xrt/auxiliary/CMakeLists.txt              |   1 -
 src/xrt/auxiliary/meson.build                 |   1 -
 src/xrt/auxiliary/util/u_timing.h             | 208 ++++++++++++-
 src/xrt/auxiliary/util/u_timing_render.c      | 284 ++++++++++++------
 src/xrt/auxiliary/util/u_timing_render.h      | 165 ----------
 .../compositor/multi/comp_multi_compositor.c  |  22 +-
 src/xrt/compositor/multi/comp_multi_private.h |   4 +-
 src/xrt/compositor/multi/comp_multi_system.c  |   4 +-
 src/xrt/ipc/server/ipc_server.h               |   1 -
 9 files changed, 404 insertions(+), 286 deletions(-)
 delete mode 100644 src/xrt/auxiliary/util/u_timing_render.h

diff --git a/src/xrt/auxiliary/CMakeLists.txt b/src/xrt/auxiliary/CMakeLists.txt
index 455945fb9..0827faac9 100644
--- a/src/xrt/auxiliary/CMakeLists.txt
+++ b/src/xrt/auxiliary/CMakeLists.txt
@@ -152,7 +152,6 @@ set(UTIL_SOURCE_FILES
 	util/u_timing_fake.c
 	util/u_timing_frame.c
 	util/u_timing_render.c
-	util/u_timing_render.h
 	util/u_trace_marker.c
 	util/u_trace_marker.h
 	util/u_var.cpp
diff --git a/src/xrt/auxiliary/meson.build b/src/xrt/auxiliary/meson.build
index 42ad0a246..aac2fc21a 100644
--- a/src/xrt/auxiliary/meson.build
+++ b/src/xrt/auxiliary/meson.build
@@ -58,7 +58,6 @@ lib_aux_util = static_library(
 		'util/u_timing_fake.c',
 		'util/u_timing_frame.c',
 		'util/u_timing_render.c',
-		'util/u_timing_render.h',
 		'util/u_trace_marker.c',
 		'util/u_trace_marker.h',
 		'util/u_var.cpp',
diff --git a/src/xrt/auxiliary/util/u_timing.h b/src/xrt/auxiliary/util/u_timing.h
index 09709e18d..d0f704f04 100644
--- a/src/xrt/auxiliary/util/u_timing.h
+++ b/src/xrt/auxiliary/util/u_timing.h
@@ -25,12 +25,6 @@ extern "C" {
  */
 
 
-/*
- *
- * Frame timing helper.
- *
- */
-
 /*!
  * For marking timepoints on a frame's lifetime, not a async event.
  *
@@ -43,6 +37,13 @@ enum u_timing_point
 	U_TIMING_POINT_SUBMIT,  //!< Submitted work to the GPU.
 };
 
+
+/*
+ *
+ * Frame timing helper.
+ *
+ */
+
 /*!
  * Frame timing helper struct, used for the compositors own frame timing.
  *
@@ -103,13 +104,6 @@ struct u_frame_timing
 	void (*destroy)(struct u_frame_timing *uft);
 };
 
-
-/*
- *
- * Helper functions.
- *
- */
-
 /*!
  * @copydoc u_frame_timing::predict
  *
@@ -194,6 +188,186 @@ u_ft_destroy(struct u_frame_timing **uft_ptr)
 }
 
 
+/*
+ *
+ * Render timing helper.
+ *
+ */
+
+/*!
+ * This render timing helper is designed to schedule the rendering time of
+ * clients that submit frames to a compositor, which runs its own render loop
+ * that picks latest completed frames for that client.
+ *
+ * @ingroup aux_timing
+ */
+struct u_render_timing
+{
+	/*!
+	 * Predict when the client's next: rendered frame will be display; when the
+	 * client should be woken up from sleeping; and its display period.
+	 *
+	 * This is called from `xrWaitFrame`, but it does not do any waiting, the caller
+	 * should wait till `out_wake_up_time`.
+	 *
+	 * @param      urth                         Render timing helper.
+	 * @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_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);
+
+	/*!
+	 * Mark a point on the frame's lifetime.
+	 *
+	 * @see @ref frame-timing.
+	 */
+	void (*mark_point)(struct u_render_timing *urt, int64_t frame_id, enum u_timing_point point, uint64_t when_ns);
+
+	/*!
+	 * When a frame has been discarded.
+	 */
+	void (*mark_discarded)(struct u_render_timing *urt, int64_t frame_id);
+
+	/*!
+	 * A frame has been delivered from the client, see `xrEndFrame`. The GPU might
+	 * still be rendering the work.
+	 */
+	void (*mark_delivered)(struct u_render_timing *urt, int64_t frame_id);
+
+	/*!
+	 * Add a new sample point from the main render loop.
+	 *
+	 * This is called in the main renderer loop that tightly submits frames to the
+	 * real compositor for displaying. This is only used to inform the render helper
+	 * when the frame will be shown, not any timing information about the client.
+	 *
+	 * When this is called doesn't matter that much, as the render timing will need
+	 * to be able to predict one or more frames into the future anyways. But
+	 * preferably as soon as the main loop wakes up from wait frame.
+	 *
+	 * @param urth                        Self pointer
+	 * @param predicted_display_time_ns   Predicted display time for this sample.
+	 * @param predicted_display_period_ns Predicted display period for this sample.
+	 * @param extra_ns                    Time between display and when this sample
+	 *                                    was created, that is when the main loop
+	 *                                    was woken up by the main compositor.
+	 */
+	void (*info)(struct u_render_timing *urt,
+	             uint64_t predicted_display_time_ns,
+	             uint64_t predicted_display_period_ns,
+	             uint64_t extra_ns);
+
+	/*!
+	 * Destroy this u_render_timing.
+	 */
+	void (*destroy)(struct u_render_timing *urt);
+};
+
+/*!
+ * @copydoc u_frame_timing::predict
+ *
+ * Helper for calling through the function pointer.
+ *
+ * @public @memberof u_render_timing
+ * @ingroup aux_timing
+ */
+static inline void
+u_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)
+{
+	urt->predict(urt, out_frame_id, out_wake_up_time, out_predicted_display_time, out_predicted_display_period);
+}
+
+/*!
+ * @copydoc u_frame_timing::mark_point
+ *
+ * Helper for calling through the function pointer.
+ *
+ * @public @memberof u_render_timing
+ * @ingroup aux_timing
+ */
+static inline void
+u_rt_mark_point(struct u_render_timing *urt, int64_t frame_id, enum u_timing_point point, uint64_t when_ns)
+{
+	urt->mark_point(urt, frame_id, point, when_ns);
+}
+
+/*!
+ * @copydoc u_frame_timing::mark_discarded
+ *
+ * Helper for calling through the function pointer.
+ *
+ * @public @memberof u_render_timing
+ * @ingroup aux_timing
+ */
+static inline void
+u_rt_mark_discarded(struct u_render_timing *urt, int64_t frame_id)
+{
+	urt->mark_discarded(urt, frame_id);
+}
+
+/*!
+ * @copydoc u_frame_timing::mark_delivered
+ *
+ * Helper for calling through the function pointer.
+ *
+ * @public @memberof u_render_timing
+ * @ingroup aux_timing
+ */
+static inline void
+u_rt_mark_delivered(struct u_render_timing *urt, int64_t frame_id)
+{
+	urt->mark_delivered(urt, frame_id);
+}
+
+/*!
+ * @copydoc u_frame_timing::info
+ *
+ * Helper for calling through the function pointer.
+ *
+ * @public @memberof u_render_timing
+ * @ingroup aux_timing
+ */
+static inline void
+u_rt_info(struct u_render_timing *urt,
+          uint64_t predicted_display_time_ns,
+          uint64_t predicted_display_period_ns,
+          uint64_t extra_ns)
+{
+	urt->info(urt, predicted_display_time_ns, predicted_display_period_ns, extra_ns);
+}
+
+/*!
+ * @copydoc u_render_timing::destroy
+ *
+ * Helper for calling through the function pointer: does a null check and sets
+ * urt_ptr to null if freed.
+ *
+ * @public @memberof u_frame_timing
+ * @ingroup aux_timing
+ */
+static inline void
+u_rt_destroy(struct u_render_timing **urt_ptr)
+{
+	struct u_render_timing *urt = *urt_ptr;
+	if (urt == NULL) {
+		return;
+	}
+
+	urt->destroy(urt);
+	*urt_ptr = NULL;
+}
+
+
 /*
  *
  * Implementations.
@@ -216,6 +390,14 @@ u_ft_display_timing_create(uint64_t estimated_frame_period_ns, struct u_frame_ti
 xrt_result_t
 u_ft_fake_create(uint64_t estimated_frame_period_ns, struct u_frame_timing **out_uft);
 
+/*!
+ * Creates a new render timing.
+ *
+ * @ingroup aux_timing
+ */
+xrt_result_t
+u_rt_create(struct u_render_timing **out_urt);
+
 
 #ifdef __cplusplus
 }
diff --git a/src/xrt/auxiliary/util/u_timing_render.c b/src/xrt/auxiliary/util/u_timing_render.c
index f6824b1cd..68e729d56 100644
--- a/src/xrt/auxiliary/util/u_timing_render.c
+++ b/src/xrt/auxiliary/util/u_timing_render.c
@@ -7,23 +7,98 @@
  * @ingroup aux_util
  */
 
+#include "os/os_time.h"
+
 #include "util/u_time.h"
 #include "util/u_misc.h"
 #include "util/u_debug.h"
 #include "util/u_logging.h"
-#include "util/u_timing_render.h"
+#include "util/u_timing.h"
 
 #include <stdio.h>
 #include <assert.h>
 #include <inttypes.h>
 
 
+/*
+ *
+ * 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;
+};
+
+
 /*
  *
  * Helpers
  *
  */
 
+static inline struct render_timing *
+render_timing(struct u_render_timing *urt)
+{
+	return (struct render_timing *)urt;
+}
+
 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__)
@@ -33,59 +108,59 @@ DEBUG_GET_ONCE_LOG_OPTION(ll, "U_TIMING_RENDER_LOG", U_LOGGING_WARN)
 #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))
+#define GET_INDEX_FROM_ID(RT, ID) ((uint64_t)(ID) % ARRAY_SIZE((RT)->frames))
 
 static uint64_t
-min_period(const struct u_rt_helper *urth)
+min_period(const struct render_timing *rt)
 {
-	return urth->last_input.predicted_display_period_ns;
+	return rt->last_input.predicted_display_period_ns;
 }
 
 static uint64_t
-last_sample_displayed(const struct u_rt_helper *urth)
+last_sample_displayed(const struct render_timing *rt)
 {
-	return urth->last_input.predicted_display_time_ns;
+	return rt->last_input.predicted_display_time_ns;
 }
 
 static uint64_t
-last_return_predicted_display(const struct u_rt_helper *urth)
+last_return_predicted_display(const struct render_timing *rt)
 {
-	return urth->last_returned_ns;
+	return rt->last_returned_ns;
 }
 
 static uint64_t
-total_app_time_ns(const struct u_rt_helper *urth)
+total_app_time_ns(const struct render_timing *rt)
 {
-	return urth->app.cpu_time_ns + urth->app.draw_time_ns;
+	return rt->app.cpu_time_ns + rt->app.draw_time_ns;
 }
 
 static uint64_t
-total_app_and_compositor_time_ns(const struct u_rt_helper *urth)
+total_app_and_compositor_time_ns(const struct render_timing *rt)
 {
-	return total_app_time_ns(urth) + urth->app.margin_ns + urth->last_input.extra_ns;
+	return total_app_time_ns(rt) + rt->app.margin_ns + rt->last_input.extra_ns;
 }
 
 static uint64_t
-predict_display_time(struct u_rt_helper *urth)
+predict_display_time(const struct render_timing *rt)
 {
 	// Now
 	uint64_t now_ns = os_monotonic_get_ns();
 
 	// Error checking.
-	uint64_t period_ns = min_period(urth);
+	uint64_t period_ns = min_period(rt);
 	if (period_ns == 0) {
 		assert(false && "Have not yet received and samples from timing driver.");
 		return now_ns;
 	}
 
 	// Total app and compositor time to produce a frame
-	uint64_t app_and_compositor_time_ns = total_app_and_compositor_time_ns(urth);
+	uint64_t app_and_compositor_time_ns = total_app_and_compositor_time_ns(rt);
 
 	// Start from the last time that the driver displayed something.
-	uint64_t val = last_sample_displayed(urth);
+	uint64_t val = last_sample_displayed(rt);
 
 	// Return a time after the last returned display time.
-	while (val < last_return_predicted_display(urth)) {
+	while (val < last_return_predicted_display(rt)) {
 		val += period_ns;
 	}
 
@@ -100,122 +175,110 @@ predict_display_time(struct u_rt_helper *urth)
 
 /*
  *
- * 'Exported' functions.
+ * Member functions.
  *
  */
 
-void
-u_rt_helper_client_clear(struct u_rt_helper *urth)
+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)
 {
-	for (size_t i = 0; i < ARRAY_SIZE(urth->frames); i++) {
-		urth->frames[i].state = U_RT_READY;
-		urth->frames[i].frame_id = -1;
-	}
+	struct render_timing *rt = render_timing(urt);
 
-	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,
-                    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;
+	int64_t frame_id = ++rt->frame_counter;
 	*out_frame_id = frame_id;
 
 	DEBUG_PRINT_FRAME_ID();
 
-	uint64_t predict_ns = predict_display_time(urth);
+	uint64_t predict_ns = predict_display_time(rt);
 
-	urth->last_returned_ns = predict_ns;
+	rt->last_returned_ns = predict_ns;
 
-	*out_wake_up_time = predict_ns - total_app_and_compositor_time_ns(urth);
+	*out_wake_up_time = predict_ns - total_app_and_compositor_time_ns(rt);
 	*out_predicted_display_time = predict_ns;
-	*out_predicted_display_period = min_period(urth);
+	*out_predicted_display_period = min_period(rt);
 
-	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);
+	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);
 
 	/*
 	 * 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;
+	uint64_t delivery_time_ns = predict_ns - rt->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;
+	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;
 }
 
-void
-u_rt_helper_mark(struct u_rt_helper *urth, int64_t frame_id, enum u_timing_point point, uint64_t when_ns)
+static void
+rt_mark_point(struct u_render_timing *urt, int64_t frame_id, enum u_timing_point point, uint64_t when_ns)
 {
+	struct render_timing *rt = render_timing(urt);
+
 	DEBUG_PRINT_FRAME_ID();
 
-	size_t index = GET_INDEX_FROM_ID(urth, frame_id);
-	assert(urth->frames[index].frame_id == frame_id);
+	size_t index = GET_INDEX_FROM_ID(rt, frame_id);
+	assert(rt->frames[index].frame_id == frame_id);
 
 	switch (point) {
 	case U_TIMING_POINT_WAKE_UP:
-		assert(urth->frames[index].state == U_RT_PREDICTED);
+		assert(rt->frames[index].state == U_RT_PREDICTED);
 
-		urth->frames[index].when.wait_woke_ns = when_ns;
-		urth->frames[index].state = U_RT_WAIT_LEFT;
+		rt->frames[index].when.wait_woke_ns = when_ns;
+		rt->frames[index].state = U_RT_WAIT_LEFT;
 		break;
 	case U_TIMING_POINT_BEGIN:
-		assert(urth->frames[index].state == U_RT_WAIT_LEFT);
+		assert(rt->frames[index].state == U_RT_WAIT_LEFT);
 
-		urth->frames[index].when.begin_ns = os_monotonic_get_ns();
-		urth->frames[index].state = U_RT_BEGUN;
+		rt->frames[index].when.begin_ns = os_monotonic_get_ns();
+		rt->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)
+static void
+rt_mark_discarded(struct u_render_timing *urt, int64_t frame_id)
 {
+	struct render_timing *rt = render_timing(urt);
+
 	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_WAIT_LEFT || urth->frames[index].state == U_RT_BEGUN);
+	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);
 
-	urth->frames[index].when.delivered_ns = os_monotonic_get_ns();
-	urth->frames[index].state = U_RT_READY;
-	urth->frames[index].frame_id = -1;
+	rt->frames[index].when.delivered_ns = os_monotonic_get_ns();
+	rt->frames[index].state = U_RT_READY;
+	rt->frames[index].frame_id = -1;
 }
 
-void
-u_rt_helper_mark_delivered(struct u_rt_helper *urth, int64_t frame_id)
+static void
+rt_mark_delivered(struct u_render_timing *urt, int64_t frame_id)
 {
+	struct render_timing *rt = render_timing(urt);
+
 	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);
+	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);
 
 	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;
+	rt->frames[index].when.delivered_ns = now_ns;
+	rt->frames[index].state = U_RT_READY;
+	rt->frames[index].frame_id = -1;
 
-	int64_t diff_ns = urth->frames[index].predicted_delivery_time_ns - now_ns;
+	int64_t diff_ns = rt->frames[index].predicted_delivery_time_ns - now_ns;
 	bool late = false;
 	if (diff_ns < 0) {
 		diff_ns = -diff_ns;
@@ -226,13 +289,52 @@ u_rt_helper_mark_delivered(struct u_rt_helper *urth, int64_t frame_id)
 	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)
+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)
 {
-	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;
+	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;
 }
diff --git a/src/xrt/auxiliary/util/u_timing_render.h b/src/xrt/auxiliary/util/u_timing_render.h
deleted file mode 100644
index 1be3bad3c..000000000
--- a/src/xrt/auxiliary/util/u_timing_render.h
+++ /dev/null
@@ -1,165 +0,0 @@
-// 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
- */
-
-#pragma once
-
-#include "xrt/xrt_compiler.h"
-#include "os/os_time.h"
-#include "util/u_timing.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
-{
-	//! 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;
-};
-
-/*!
- * This render timing helper is designed to schedule the rendering time of
- * clients that submit frames to a compositor, which runs its own render loop
- * that picks latest completed frames for that client.
- */
-struct u_rt_helper
-{
-	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;
-};
-
-void
-u_rt_helper_init(struct u_rt_helper *urth);
-
-/*!
- * This function gets the client part of the render timing helper ready to be
- * used. If you use init you will also clear all of the timing information.
- *
- * Call this when resetting a client.
- */
-void
-u_rt_helper_client_clear(struct u_rt_helper *urth);
-
-/*!
- * Predict when the client's next: rendered frame will be display; when the
- * client should be woken up from sleeping; and its display period.
- *
- * This is called from `xrWaitFrame`, but it does not do any waiting, the caller
- * should wait till `out_wake_up_time`.
- *
- * @param      urth                         Render timing helper.
- * @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
-u_rt_helper_predict(struct u_rt_helper *urth,
-                    int64_t *out_frame_id,
-                    uint64_t *out_wake_up_time,
-                    uint64_t *out_predicted_display_time,
-                    uint64_t *out_predicted_display_period);
-
-/*!
- * Mark a point on the frame's lifetime.
- *
- * @see @ref frame-timing.
- */
-void
-u_rt_helper_mark(struct u_rt_helper *urth, int64_t frame_id, enum u_timing_point point, uint64_t when_ns);
-
-/*!
- * When a frame has been discarded.
- */
-void
-u_rt_helper_mark_discarded(struct u_rt_helper *urth, int64_t frame_id);
-
-/*!
- * A frame has been delivered from the client, see `xrEndFrame`. The GPU might
- * still be rendering the work.
- */
-void
-u_rt_helper_mark_delivered(struct u_rt_helper *urth, int64_t frame_id);
-
-/*!
- * Add a new sample point from the main render loop.
- *
- * This is called in the main renderer loop that tightly submits frames to the
- * real compositor for displaying. This is only used to inform the render helper
- * when the frame will be shown, not any timing information about the client.
- *
- * When this is called doesn't matter that much, as the render timing will need
- * to be able to predict one or more frames into the future anyways. But
- * preferably as soon as the main loop wakes up from wait frame.
- *
- * @param urth                        Self pointer
- * @param predicted_display_time_ns   Predicted display time for this sample.
- * @param predicted_display_period_ns Predicted display period for this sample.
- * @param extra_ns                    Time between display and when this sample
- *                                    was created, that is when the main loop
- *                                    was woken up by the main compositor.
- */
-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);
-
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/src/xrt/compositor/multi/comp_multi_compositor.c b/src/xrt/compositor/multi/comp_multi_compositor.c
index facad36d2..9c8ad55ef 100644
--- a/src/xrt/compositor/multi/comp_multi_compositor.c
+++ b/src/xrt/compositor/multi/comp_multi_compositor.c
@@ -197,8 +197,8 @@ multi_compositor_predict_frame(struct xrt_compositor *xc,
 
 	os_mutex_lock(&mc->msc->list_and_timing_lock);
 
-	u_rt_helper_predict(                  //
-	    &mc->urth,                        //
+	u_rt_predict(                         //
+	    mc->urt,                          //
 	    out_frame_id,                     //
 	    out_wake_time_ns,                 //
 	    out_predicted_display_time_ns,    //
@@ -223,7 +223,7 @@ multi_compositor_mark_frame(struct xrt_compositor *xc,
 	case XRT_COMPOSITOR_FRAME_POINT_WOKE:
 		os_mutex_lock(&mc->msc->list_and_timing_lock);
 		uint64_t now_ns = os_monotonic_get_ns();
-		u_rt_helper_mark(&mc->urth, frame_id, U_TIMING_POINT_WAKE_UP, now_ns);
+		u_rt_mark_point(mc->urt, frame_id, U_TIMING_POINT_WAKE_UP, now_ns);
 		os_mutex_unlock(&mc->msc->list_and_timing_lock);
 		break;
 	default: assert(false);
@@ -255,7 +255,7 @@ multi_compositor_begin_frame(struct xrt_compositor *xc, int64_t frame_id)
 
 	os_mutex_lock(&mc->msc->list_and_timing_lock);
 	uint64_t now_ns = os_monotonic_get_ns();
-	u_rt_helper_mark(&mc->urth, frame_id, U_TIMING_POINT_BEGIN, now_ns);
+	u_rt_mark_point(mc->urt, frame_id, U_TIMING_POINT_BEGIN, now_ns);
 	os_mutex_unlock(&mc->msc->list_and_timing_lock);
 
 	return XRT_SUCCESS;
@@ -269,7 +269,7 @@ multi_compositor_discard_frame(struct xrt_compositor *xc, int64_t frame_id)
 	struct multi_compositor *mc = multi_compositor(xc);
 
 	os_mutex_lock(&mc->msc->list_and_timing_lock);
-	u_rt_helper_mark_discarded(&mc->urth, frame_id);
+	u_rt_mark_discarded(mc->urt, frame_id);
 	os_mutex_unlock(&mc->msc->list_and_timing_lock);
 
 	return XRT_SUCCESS;
@@ -451,7 +451,7 @@ multi_compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_g
 
 	slot_move_and_clear(&mc->delivered, &mc->progress);
 
-	u_rt_helper_mark_delivered(&mc->urth, frame_id);
+	u_rt_mark_delivered(mc->urt, frame_id);
 
 	os_mutex_unlock(&mc->msc->list_and_timing_lock);
 
@@ -494,6 +494,9 @@ multi_compositor_destroy(struct xrt_compositor *xc)
 	slot_clear(&mc->progress);
 	slot_clear(&mc->delivered);
 
+	// Does null checking.
+	u_rt_destroy(&mc->urt);
+
 	free(mc);
 }
 
@@ -534,8 +537,7 @@ multi_compositor_create(struct multi_system_compositor *msc,
 	mc->base.base.info = msc->xcn->base.info;
 
 	// This is safe to do without a lock since we are not on the list yet.
-	u_rt_helper_init(&mc->urth);
-	u_rt_helper_client_clear(&mc->urth);
+	u_rt_create(&mc->urt);
 
 	os_mutex_lock(&msc->list_and_timing_lock);
 
@@ -548,8 +550,8 @@ multi_compositor_create(struct multi_system_compositor *msc,
 		break;
 	}
 
-	u_rt_helper_new_sample(                            //
-	    &mc->urth,                                     //
+	u_rt_info(                                         //
+	    mc->urt,                                       //
 	    msc->last_timings.predicted_display_time_ns,   //
 	    msc->last_timings.predicted_display_period_ns, //
 	    msc->last_timings.diff_ns);                    //
diff --git a/src/xrt/compositor/multi/comp_multi_private.h b/src/xrt/compositor/multi/comp_multi_private.h
index b27261d15..64f4cb551 100644
--- a/src/xrt/compositor/multi/comp_multi_private.h
+++ b/src/xrt/compositor/multi/comp_multi_private.h
@@ -16,7 +16,7 @@
 #include "os/os_time.h"
 #include "os/os_threading.h"
 
-#include "util/u_timing_render.h"
+#include "util/u_timing.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -128,7 +128,7 @@ struct multi_compositor
 	//! Fully ready to be used.
 	struct multi_layer_slot delivered;
 
-	struct u_rt_helper urth;
+	struct u_render_timing *urt;
 };
 
 static inline struct multi_compositor *
diff --git a/src/xrt/compositor/multi/comp_multi_system.c b/src/xrt/compositor/multi/comp_multi_system.c
index 20fd21f1a..f2a44a82a 100644
--- a/src/xrt/compositor/multi/comp_multi_system.c
+++ b/src/xrt/compositor/multi/comp_multi_system.c
@@ -271,8 +271,8 @@ broadcast_timings(struct multi_system_compositor *msc,
 			continue;
 		}
 
-		u_rt_helper_new_sample(          //
-		    &mc->urth,                   //
+		u_rt_info(                       //
+		    mc->urt,                     //
 		    predicted_display_time_ns,   //
 		    predicted_display_period_ns, //
 		    diff_ns);                    //
diff --git a/src/xrt/ipc/server/ipc_server.h b/src/xrt/ipc/server/ipc_server.h
index b247d53c2..5abf0bc1b 100644
--- a/src/xrt/ipc/server/ipc_server.h
+++ b/src/xrt/ipc/server/ipc_server.h
@@ -14,7 +14,6 @@
 #include "xrt/xrt_compiler.h"
 
 #include "util/u_logging.h"
-#include "util/u_timing_render.h"
 
 #include "os/os_threading.h"