The open source OpenXR runtime
at main 344 lines 7.6 kB view raw
1// Copyright 2019-2022, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Wrapper around OS native time functions. 6 * 7 * These should be preferred over directly using native OS time functions in 8 * potentially-portable code. Additionally, in most cases these are preferred 9 * over timepoints from @ref time_state for general usage in drivers, etc. 10 * 11 * @author Drew DeVault <sir@cmpwn.com> 12 * @author Jakob Bornecrantz <jakob@collabora.com> 13 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 14 * 15 * @ingroup aux_os 16 */ 17 18#pragma once 19 20#include "xrt/xrt_config_os.h" 21#include "xrt/xrt_compiler.h" 22 23#include "util/u_time.h" 24 25#ifdef XRT_OS_LINUX 26#include <time.h> 27#include <sys/time.h> 28#define XRT_HAVE_TIMESPEC 29#define XRT_HAVE_TIMEVAL 30 31#elif defined(XRT_OS_WINDOWS) 32#if defined(XRT_ENV_MINGW) 33// That define is needed before to include windows.h, to avoid a collision 34// between the 'byte' type defined by windows and std::byte defined in cstddef 35// since C++17 36#define byte win_byte_override 37#include <windows.h> 38#undef byte 39#endif 40 41#include <time.h> 42#include <timeapi.h> 43#define XRT_HAVE_TIMESPEC 44 45#elif defined(XRT_DOXYGEN) 46#include <time.h> 47#define XRT_HAVE_TIMESPEC 48#define XRT_HAVE_TIMEVAL 49 50#else 51#error "No time support on non-Linux platforms yet." 52#endif 53 54#ifdef __cplusplus 55extern "C" { 56#endif 57 58/*! 59 * @defgroup aux_os_time Portable Timekeeping 60 * @ingroup aux_os 61 * 62 * @brief Unifying wrapper around system time retrieval functions. 63 */ 64 65 66/*! 67 * @defgroup aux_os_time_extra Extra Timekeeping Utilities 68 * @ingroup aux_os_time 69 * 70 * @brief Less-portable utility functions for manipulating system time, for 71 * interoperation with platform APIs. 72 */ 73 74/*! 75 * Return a monotonic clock in nanoseconds. 76 * @ingroup aux_os_time 77 */ 78static inline int64_t 79os_monotonic_get_ns(void); 80 81/*! 82 * Sleep the given number of nanoseconds. 83 * 84 * Note that on some platforms, this may be somewhat less accurate than you might want. 85 * On all platforms, the system scheduler has the final say. 86 * 87 * @see os_precise_sleeper 88 * 89 * @ingroup aux_os_time 90 */ 91static inline void 92os_nanosleep(int64_t nsec); 93 94/*! 95 * A structure for storing state as needed for more precise sleeping, mostly for compositor use. 96 * @ingroup aux_os_time 97 */ 98struct os_precise_sleeper; 99 100/*! 101 * Initialize members of @ref os_precise_sleeper. 102 * @public @memberof os_precise_sleeper 103 */ 104static inline void 105os_precise_sleeper_init(struct os_precise_sleeper *ops); 106 107/*! 108 * De-initialize members of @ref os_precise_sleeper, and free resources, without actually freeing the given 109 * pointer. 110 * @public @memberof os_precise_sleeper 111 */ 112static inline void 113os_precise_sleeper_deinit(struct os_precise_sleeper *ops); 114 115/*! 116 * Sleep the given number of nanoseconds, trying harder to be precise. 117 * 118 * On some platforms, there is no way to improve sleep precision easily with some OS-specific state, so we forward 119 * to os_nanosleep(). 120 * 121 * Note that on all platforms, the system scheduler has the final say. 122 * 123 * @public @memberof os_precise_sleeper 124 */ 125static inline void 126os_precise_sleeper_nanosleep(struct os_precise_sleeper *ops, int32_t nsec); 127 128#if defined(XRT_HAVE_TIMESPEC) || defined(XRT_DOXYGEN) 129/*! 130 * Convert a timespec struct to nanoseconds. 131 * 132 * Note that this only does the value combining, no adjustment for epochs is performed. 133 * 134 * @ingroup aux_os_time_extra 135 */ 136static inline int64_t 137os_timespec_to_ns(const struct timespec *spec); 138 139/*! 140 * Convert an nanosecond integer to a timespec struct. 141 * 142 * Note that this only does the value splitting, no adjustment for epochs is performed. 143 * @ingroup aux_os_time_extra 144 */ 145static inline void 146os_ns_to_timespec(int64_t ns, struct timespec *spec); 147#endif 148 149#if defined(XRT_HAVE_TIMEVAL) || defined(XRT_DOXYGEN) 150/*! 151 * Convert a timeval struct to nanoseconds. 152 * @ingroup aux_os_time_extra 153 */ 154static inline int64_t 155os_timeval_to_ns(struct timeval *val); 156#endif 157 158#if defined(XRT_OS_LINUX) || defined(XRT_DOXYGEN) 159/*! 160 * Return a realtime clock in nanoseconds (Linux-only) 161 * 162 * @ingroup aux_os_time_extra 163 */ 164static inline int64_t 165os_realtime_get_ns(void); 166#endif 167 168#if defined(XRT_OS_WINDOWS) 169/*! 170 * Return a realtime clock in nanoseconds 171 * 172 * @ingroup aux_os_time_extra 173 */ 174int64_t 175os_realtime_get_ns(void); 176#endif 177 178#if defined(XRT_OS_WINDOWS) || defined(XRT_DOXYGEN) 179/*! 180 * @brief Return a qpc freq in nanoseconds. 181 * @ingroup aux_os_time 182 */ 183static inline int64_t 184os_ns_per_qpc_tick_get(void); 185#endif 186 187 188/* 189 * 190 * implementations follow 191 * 192 */ 193 194static inline void 195os_nanosleep(int64_t nsec) 196{ 197#if defined(XRT_OS_LINUX) 198 struct timespec spec; 199 spec.tv_sec = (nsec / U_1_000_000_000); 200 spec.tv_nsec = (nsec % U_1_000_000_000); 201 nanosleep(&spec, NULL); 202#elif defined(XRT_OS_WINDOWS) 203 Sleep((DWORD)(nsec / U_TIME_1MS_IN_NS)); 204#endif 205} 206 207struct os_precise_sleeper 208{ 209#if defined(XRT_OS_WINDOWS) 210 HANDLE timer; 211#else 212 int unused_; 213#endif 214}; 215 216static inline void 217os_precise_sleeper_init(struct os_precise_sleeper *ops) 218{ 219#if defined(XRT_OS_WINDOWS) 220 ops->timer = CreateWaitableTimer(NULL, TRUE, NULL); 221#endif 222} 223 224static inline void 225os_precise_sleeper_deinit(struct os_precise_sleeper *ops) 226{ 227#if defined(XRT_OS_WINDOWS) 228 if (ops->timer) { 229 CloseHandle(ops->timer); 230 ops->timer = NULL; 231 } 232#endif 233} 234 235static inline void 236os_precise_sleeper_nanosleep(struct os_precise_sleeper *ops, int32_t nsec) 237{ 238#if defined(XRT_OS_WINDOWS) 239 timeBeginPeriod(1); 240 if (ops->timer) { 241 LARGE_INTEGER timeperiod; 242 timeperiod.QuadPart = -(nsec / 100); 243 if (SetWaitableTimer(ops->timer, &timeperiod, 0, NULL, NULL, FALSE)) { 244 // OK we could set up the timer, now let's wait. 245 WaitForSingleObject(ops->timer, INFINITE); 246 timeEndPeriod(1); 247 return; 248 } 249 } 250#endif 251 // If we fall through from an implementation, or there's no implementation needed for a platform, we 252 // delegate to the regular os_nanosleep. 253 os_nanosleep(nsec); 254 255#if defined(XRT_OS_WINDOWS) 256 timeEndPeriod(1); 257#endif 258} 259 260#if defined(XRT_HAVE_TIMESPEC) 261static inline int64_t 262os_timespec_to_ns(const struct timespec *spec) 263{ 264 int64_t ns = 0; 265 ns += (int64_t)spec->tv_sec * U_1_000_000_000; 266 ns += (int64_t)spec->tv_nsec; 267 return ns; 268} 269 270static inline void 271os_ns_to_timespec(int64_t ns, struct timespec *spec) 272{ 273 spec->tv_sec = (ns / U_1_000_000_000); 274 spec->tv_nsec = (ns % U_1_000_000_000); 275} 276#endif // XRT_HAVE_TIMESPEC 277 278#define OS_NS_PER_USEC (1000) 279 280#if defined(XRT_HAVE_TIMEVAL) && defined(XRT_OS_LINUX) 281 282static inline int64_t 283os_timeval_to_ns(struct timeval *val) 284{ 285 int64_t ns = 0; 286 ns += (int64_t)val->tv_sec * U_1_000_000_000; 287 ns += (int64_t)val->tv_usec * OS_NS_PER_USEC; 288 return ns; 289} 290#endif // defined(XRT_HAVE_TIMEVAL) && defined(XRT_OS_LINUX) 291 292#if defined(XRT_OS_WINDOWS) 293static inline int64_t 294os_ns_per_qpc_tick_get(void) 295{ 296 static int64_t ns_per_qpc_tick = 0; 297 if (ns_per_qpc_tick == 0) { 298 // Fixed at startup, so we can cache this. 299 LARGE_INTEGER freq; 300 QueryPerformanceFrequency(&freq); 301 ns_per_qpc_tick = U_1_000_000_000 / freq.QuadPart; 302 } 303 return ns_per_qpc_tick; 304} 305#endif // defined(XRT_OS_WINDOWS) 306 307static inline int64_t 308os_monotonic_get_ns(void) 309{ 310#if defined(XRT_OS_LINUX) 311 struct timespec ts; 312 int ret = clock_gettime(CLOCK_MONOTONIC, &ts); 313 if (ret != 0) { 314 return 0; 315 } 316 317 return os_timespec_to_ns(&ts); 318#elif defined(XRT_OS_WINDOWS) 319 LARGE_INTEGER qpc; 320 QueryPerformanceCounter(&qpc); 321 return qpc.QuadPart * os_ns_per_qpc_tick_get(); 322#else 323#error "need port" 324#endif 325} 326 327#ifdef XRT_OS_LINUX 328static inline int64_t 329os_realtime_get_ns(void) 330{ 331 struct timespec ts; 332 int ret = clock_gettime(CLOCK_REALTIME, &ts); 333 if (ret != 0) { 334 return 0; 335 } 336 337 return os_timespec_to_ns(&ts); 338} 339#endif 340 341 342#ifdef __cplusplus 343} 344#endif