// Copyright 2020-2021, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 /*! * @file * @brief Shared timing code. * @author Jakob Bornecrantz * @ingroup aux_util */ #pragma once #include "xrt/xrt_compiler.h" #include "xrt/xrt_defines.h" #ifdef __cplusplus extern "C" { #endif /*! * @defgroup aux_timing Frame and Render timing * * @ingroup aux_util * @see @ref frame-timing. */ /*! * For marking timepoints on a frame's lifetime, not a async event. * * @ingroup aux_timing */ enum u_timing_point { U_TIMING_POINT_WAKE_UP, //!< Woke up after sleeping in wait frame. U_TIMING_POINT_BEGIN, //!< Began CPU side work for GPU. U_TIMING_POINT_SUBMIT, //!< Submitted work to the GPU. }; /* * * Frame timing helper. * */ /*! * Frame timing helper struct, used for the compositors own frame timing. * * @ingroup aux_timing */ struct u_frame_timing { /*! * Predict the next frame. * * @param[in] uft The frame timing struct. * @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. * @param[out] out_present_slop_ns Any looseness to the desired present timing. * @param[out] out_predicted_display_time_ns At what time have we predicted that pixels turns to photons. * @param[out] out_predicted_display_period_ns Display period that we are running on. * @param[out] out_min_display_period_ns The fastest theoretical display period. * * @see @ref frame-timing. */ void (*predict)(struct u_frame_timing *uft, int64_t *out_frame_id, uint64_t *out_wake_up_time_ns, uint64_t *out_desired_present_time_ns, uint64_t *out_present_slop_ns, uint64_t *out_predicted_display_time_ns, uint64_t *out_predicted_display_period_ns, uint64_t *out_min_display_period_ns); /*! * Mark a point on the frame's lifetime. * * @see @ref frame-timing. */ void (*mark_point)(struct u_frame_timing *uft, enum u_timing_point point, int64_t frame_id, uint64_t when_ns); /*! * Provide frame timing information about a delivered frame, this is * usually provided by the display system. These arguments currently * matches 1-to-1 what VK_GOOGLE_display_timing provides. * * Depend on when the information is delivered this can be called at any * point of the following frames. * * @see @ref frame-timing. */ void (*info)(struct u_frame_timing *uft, int64_t frame_id, uint64_t desired_present_time_ns, uint64_t actual_present_time_ns, uint64_t earliest_present_time_ns, uint64_t present_margin_ns); /*! * Destroy this u_frame_timing. */ void (*destroy)(struct u_frame_timing *uft); }; /*! * @copydoc u_frame_timing::predict * * Helper for calling through the function pointer. * * @public @memberof u_frame_timing * @ingroup aux_timing */ static inline void u_ft_predict(struct u_frame_timing *uft, int64_t *out_frame_id, uint64_t *out_wake_up_time_ns, uint64_t *out_desired_present_time_ns, uint64_t *out_present_slop_ns, uint64_t *out_predicted_display_time_ns, uint64_t *out_predicted_display_period_ns, uint64_t *out_min_display_period_ns) { uft->predict(uft, // out_frame_id, // out_wake_up_time_ns, // out_desired_present_time_ns, // out_present_slop_ns, // out_predicted_display_time_ns, // out_predicted_display_period_ns, // out_min_display_period_ns); // } /*! * @copydoc u_frame_timing::mark_point * * Helper for calling through the function pointer. * * @public @memberof u_frame_timing * @ingroup aux_timing */ static inline void u_ft_mark_point(struct u_frame_timing *uft, enum u_timing_point point, int64_t frame_id, uint64_t when_ns) { uft->mark_point(uft, point, frame_id, when_ns); } /*! * @copydoc u_frame_timing::info * * Helper for calling through the function pointer. * * @public @memberof u_frame_timing * @ingroup aux_timing */ static inline void u_ft_info(struct u_frame_timing *uft, int64_t frame_id, uint64_t desired_present_time_ns, uint64_t actual_present_time_ns, uint64_t earliest_present_time_ns, uint64_t present_margin_ns) { uft->info(uft, frame_id, desired_present_time_ns, actual_present_time_ns, earliest_present_time_ns, present_margin_ns); } /*! * @copydoc u_frame_timing::destroy * * Helper for calling through the function pointer: does a null check and sets * uft_ptr to null if freed. * * @public @memberof u_frame_timing * @ingroup aux_timing */ static inline void u_ft_destroy(struct u_frame_timing **uft_ptr) { struct u_frame_timing *uft = *uft_ptr; if (uft == NULL) { return; } uft->destroy(uft); *uft_ptr = NULL; } /* * * 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. * */ /*! * Meant to be used with VK_GOOGLE_display_timing. * * @ingroup aux_timing */ xrt_result_t u_ft_display_timing_create(uint64_t estimated_frame_period_ns, struct u_frame_timing **out_uft); /*! * When you can not get display timing information use this. * * @ingroup aux_timing */ 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 } #endif