The open source OpenXR runtime

xrt: adds support for XR_EXT_future

* Adds xrt interface, basic impl, and IPC for futures
* Adds state tracker plumbing for XR_EXT_future

Part-of: <https://gitlab.freedesktop.org/monado/monado/-/merge_requests/2554>

authored by

Korcan Hussein and committed by
Marge Bot
d580fcdf 05fc05ca

+1223 -1
+2
CMakeLists.txt
··· 369 369 option(XRT_FEATURE_OPENXR_FACE_TRACKING2_FB "Enable XR_FB_face_tracking2" OFF) 370 370 option(XRT_FEATURE_OPENXR_FACIAL_TRACKING_HTC "Enable XR_HTC_facial_tracking" OFF) 371 371 option(XRT_FEATURE_OPENXR_FORCE_FEEDBACK_CURL "Enable XR_MNDX_force_feedback_curl" ON) 372 + option(XRT_FEATURE_OPENXR_FUTURE_EXT "Enable XR_EXT_future" ON) 372 373 option(XRT_FEATURE_OPENXR_HAND_TRACKING_EXT "Enable XR_EXT_hand_tracking" ON) 373 374 option_with_deps(XRT_FEATURE_OPENXR_HAND_TRACKING_DATA_SOURCE_EXT DEPENDS XRT_FEATURE_OPENXR_HAND_TRACKING_EXT) 374 375 option(XRT_FEATURE_OPENXR_HAPTIC_PCM "Enable XR_FB_haptic_pcm" OFF) ··· 617 618 message(STATUS "# FEATURE_OPENXR_DISPLAY_REFRESH_RATE: ${XRT_FEATURE_OPENXR_DISPLAY_REFRESH_RATE}") 618 619 message(STATUS "# FEATURE_OPENXR_FACIAL_TRACKING_HTC: ${XRT_FEATURE_OPENXR_FACIAL_TRACKING_HTC}") 619 620 message(STATUS "# FEATURE_OPENXR_FORCE_FEEDBACK_CURL: ${XRT_FEATURE_OPENXR_FORCE_FEEDBACK_CURL}") 621 + message(STATUS "# FEATURE_OPENXR_FUTURE_EXT: ${XRT_FEATURE_OPENXR_FUTURE_EXT}") 620 622 message(STATUS "# FEATURE_OPENXR_HAND_TRACKING_EXT: ${XRT_FEATURE_OPENXR_HAND_TRACKING_EXT}") 621 623 message(STATUS "# FEATURE_OPENXR_HAND_TRACKING_DATA_SOURCE_EXT: ${XRT_FEATURE_OPENXR_HAND_TRACKING_DATA_SOURCE_EXT}") 622 624 message(STATUS "# FEATURE_OPENXR_HAPTIC_PCM: ${XRT_FEATURE_OPENXR_HAPTIC_PCM}")
+1
scripts/generate_oxr_ext_support.py
··· 64 64 ['XR_EXT_debug_utils', 'XRT_FEATURE_OPENXR_DEBUG_UTILS'], 65 65 ['XR_EXT_dpad_binding'], 66 66 ['XR_EXT_eye_gaze_interaction', 'XRT_FEATURE_OPENXR_INTERACTION_EXT_EYE_GAZE'], 67 + ['XR_EXT_future', 'XRT_FEATURE_OPENXR_FUTURE_EXT'], 67 68 ['XR_EXT_hand_interaction', 'XRT_FEATURE_OPENXR_INTERACTION_EXT_HAND'], 68 69 ['XR_EXT_hand_tracking', 'XRT_FEATURE_OPENXR_HAND_TRACKING_EXT'], 69 70 ['XR_EXT_hand_tracking_data_source', 'XRT_FEATURE_OPENXR_HAND_TRACKING_DATA_SOURCE_EXT'],
+2
scripts/mapping.imp
··· 43 43 { symbol: ["XRT_FEATURE_OPENXR_FACE_TRACKING2_FB", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 44 44 { symbol: ["XRT_FEATURE_OPENXR_FACIAL_TRACKING_HTC", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 45 45 { symbol: ["XRT_FEATURE_OPENXR_FORCE_FEEDBACK_CURL", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 46 + { symbol: ["XRT_FEATURE_OPENXR_FUTURE_EXT", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 46 47 { symbol: ["XRT_FEATURE_OPENXR_HAND_TRACKING_EXT", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 47 48 { symbol: ["XRT_FEATURE_OPENXR_HAND_TRACKING_DATA_SOURCE_EXT", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 48 49 { symbol: ["XRT_FEATURE_OPENXR_HAPTIC_PCM", "public", "\"xrt/xrt_config_build.h\"", "public"] }, ··· 74 75 { symbol: ["XRT_FEATURE_OPENXR_LAYER_FB_PASSTHROUGH", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 75 76 { symbol: ["XRT_FEATURE_OPENXR_OVERLAY", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 76 77 { symbol: ["XRT_FEATURE_OPENXR_PLANE_DETECTION", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 78 + { symbol: ["XRT_FEATURE_OPENXR_USER_PRESENCE", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 77 79 { symbol: ["XRT_FEATURE_OPENXR_SPACE_LOCAL_FLOOR", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 78 80 { symbol: ["XRT_FEATURE_OPENXR_SPACE_UNBOUNDED", "public", "\"xrt/xrt_config_build.h\"", "public"] }, 79 81 { symbol: ["XRT_FEATURE_OPENXR_VISIBILITY_MASK", "public", "\"xrt/xrt_config_build.h\"", "public"] },
+2
src/xrt/auxiliary/util/CMakeLists.txt
··· 41 41 u_format.h 42 42 u_frame.c 43 43 u_frame.h 44 + u_future.h 45 + u_future.cpp 44 46 u_generic_callbacks.hpp 45 47 u_git_tag.h 46 48 u_hand_tracking.c
+265
src/xrt/auxiliary/util/u_future.cpp
··· 1 + // Copyright 2025, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Helper to implement @ref xrt_future, 6 + * A basic CPU based future implementation. 7 + * @author Korcan Hussein <korcan.hussein@collabora.com> 8 + * @ingroup aux_util 9 + */ 10 + #include "u_future.h" 11 + 12 + #include "util/u_debug.h" 13 + #include "util/u_logging.h" 14 + 15 + #include "os/os_threading.h" 16 + 17 + #include <errno.h> 18 + #include <atomic> 19 + #include <memory> 20 + 21 + DEBUG_GET_ONCE_LOG_OPTION(log_level_future, "U_FUTURE_LOG", U_LOGGING_WARN) 22 + 23 + #define UFT_LOG_T(...) U_LOG_IFL_T(debug_get_log_option_log_level_future(), __VA_ARGS__) 24 + #define UFT_LOG_D(...) U_LOG_IFL_D(debug_get_log_option_log_level_future(), __VA_ARGS__) 25 + #define UFT_LOG_I(...) U_LOG_IFL_I(debug_get_log_option_log_level_future(), __VA_ARGS__) 26 + #define UFT_LOG_W(...) U_LOG_IFL_W(debug_get_log_option_log_level_future(), __VA_ARGS__) 27 + #define UFT_LOG_E(...) U_LOG_IFL_E(debug_get_log_option_log_level_future(), __VA_ARGS__) 28 + 29 + #define U_FUTURE_CLEANUP_TIMEOUT_NS (3000000000LL) // 3 seconds 30 + 31 + /*! 32 + * A helper to implement a @ref xrt_future, 33 + * a basic CPU based future implementation 34 + * 35 + * @ingroup aux_util 36 + * @implements xrt_future 37 + */ 38 + struct u_future 39 + { 40 + struct xrt_future base; 41 + struct os_mutex mtx; 42 + struct os_cond cv; 43 + std::atomic<xrt_future_state_t> state{XRT_FUTURE_STATE_PENDING}; 44 + std::atomic<xrt_result_t> result{XRT_SUCCESS}; 45 + struct xrt_future_value value = XRT_NULL_FUTURE_VALUE; 46 + }; 47 + 48 + static inline xrt_result_t 49 + u_future_get_xrt_result(const struct u_future *uft) 50 + { 51 + assert(uft != NULL); 52 + return uft->result.load(std::memory_order::acquire); 53 + } 54 + 55 + static inline xrt_future_state_t 56 + u_future_get_state_priv(const struct u_future *uft) 57 + { 58 + assert(uft != NULL); 59 + return uft->state.load(std::memory_order::acquire); 60 + } 61 + 62 + static inline void 63 + u_future_set_xrt_result(struct u_future *uft, const xrt_result_t result) 64 + { 65 + assert(uft != NULL); 66 + uft->result.store(result, std::memory_order::release); 67 + } 68 + 69 + static inline void 70 + u_future_set_state(struct u_future *uft, const xrt_future_state_t new_state) 71 + { 72 + assert(uft != NULL); 73 + uft->state.store(new_state, std::memory_order::release); 74 + } 75 + 76 + //! internal helper only, does not atomically set both. 77 + static inline void 78 + u_future_set_state_and_xrt_result(struct u_future *uft, const xrt_future_state_t new_state, const xrt_result_t result) 79 + { 80 + u_future_set_xrt_result(uft, result); 81 + u_future_set_state(uft, new_state); 82 + } 83 + 84 + static inline xrt_result_t 85 + u_future_get_state(const struct xrt_future *xft, enum xrt_future_state *out_state) 86 + { 87 + const struct u_future *uft = (const struct u_future *)xft; 88 + if (uft == NULL || out_state == NULL) { 89 + return XRT_ERROR_INVALID_ARGUMENT; 90 + } 91 + *out_state = u_future_get_state_priv(uft); 92 + return XRT_SUCCESS; 93 + } 94 + 95 + static xrt_result_t 96 + u_future_get_result(const struct xrt_future *xft, struct xrt_future_result *out_result) 97 + { 98 + const struct u_future *uft = (const struct u_future *)xft; 99 + if (uft == NULL || out_result == NULL) { 100 + return XRT_ERROR_INVALID_ARGUMENT; 101 + } 102 + 103 + struct os_mutex *mtx = (struct os_mutex *)&uft->mtx; 104 + os_mutex_lock(mtx); 105 + 106 + const xrt_future_state_t curr_state = u_future_get_state_priv(uft); 107 + if (curr_state == XRT_FUTURE_STATE_PENDING) { 108 + os_mutex_unlock(mtx); 109 + return XRT_ERROR_FUTURE_RESULT_NOT_READY; 110 + } 111 + 112 + out_result->result = u_future_get_xrt_result(uft); 113 + if (out_result->result == XRT_SUCCESS && // 114 + curr_state == XRT_FUTURE_STATE_READY) { 115 + out_result->value = uft->value; 116 + } 117 + 118 + os_mutex_unlock(mtx); 119 + return XRT_SUCCESS; 120 + } 121 + 122 + static xrt_result_t 123 + u_future_cancel(struct xrt_future *xft) 124 + { 125 + struct u_future *uft = (struct u_future *)xft; 126 + if (uft == NULL) { 127 + return XRT_ERROR_INVALID_ARGUMENT; 128 + } 129 + 130 + os_mutex_lock(&uft->mtx); 131 + if (u_future_get_state_priv(uft) == XRT_FUTURE_STATE_PENDING) { 132 + u_future_set_state_and_xrt_result(uft, XRT_FUTURE_STATE_CANCELLED, XRT_OPERATION_CANCELLED); 133 + os_cond_broadcast(&uft->cv); 134 + } 135 + os_mutex_unlock(&uft->mtx); 136 + return XRT_SUCCESS; 137 + } 138 + 139 + static xrt_result_t 140 + u_future_wait(struct xrt_future *xft, int64_t timeout_ns) 141 + { 142 + struct u_future *uft = (struct u_future *)xft; 143 + if (uft == NULL) { 144 + return XRT_ERROR_INVALID_ARGUMENT; 145 + } 146 + 147 + if (timeout_ns < 0) { 148 + timeout_ns = INT64_MAX; 149 + } 150 + 151 + // on windows pthread_cond_timedwait can not be used with monotonic time 152 + const int64_t start_wait_rt = os_realtime_get_ns(); 153 + const int64_t end_wait_rt = 154 + (start_wait_rt > (INT64_MAX - timeout_ns)) ? INT64_MAX : (start_wait_rt + timeout_ns); 155 + 156 + struct timespec ts = {}; 157 + os_ns_to_timespec(end_wait_rt, &ts); 158 + 159 + xrt_future_state_t curr_state = XRT_FUTURE_STATE_PENDING; 160 + 161 + os_mutex_lock(&uft->mtx); 162 + 163 + while ((curr_state = u_future_get_state_priv(uft)) == XRT_FUTURE_STATE_PENDING) { 164 + const int wait_res = pthread_cond_timedwait(&uft->cv.cond, &uft->mtx.mutex, &ts); 165 + if (wait_res == ETIMEDOUT) { 166 + if (os_realtime_get_ns() >= end_wait_rt) { 167 + // final state check - might have completed during timeout handling 168 + curr_state = u_future_get_state_priv(uft); 169 + break; 170 + } 171 + } else if (wait_res != 0) { 172 + 173 + break; 174 + } 175 + } 176 + 177 + os_mutex_unlock(&uft->mtx); 178 + 179 + if (curr_state == XRT_FUTURE_STATE_PENDING) { 180 + return XRT_TIMEOUT; 181 + } 182 + return u_future_get_xrt_result(uft); 183 + } 184 + 185 + static inline xrt_result_t 186 + u_future_is_cancel_requested(const struct xrt_future *xft, bool *out_request_cancel) 187 + { 188 + const struct u_future *uft = (const struct u_future *)xft; 189 + if (uft == NULL || out_request_cancel == NULL) { 190 + return XRT_ERROR_INVALID_ARGUMENT; 191 + } 192 + const xrt_future_state_t curr_state = u_future_get_state_priv(uft); 193 + *out_request_cancel = curr_state == XRT_FUTURE_STATE_CANCELLED; 194 + return XRT_SUCCESS; 195 + } 196 + 197 + static xrt_result_t 198 + u_future_complete(struct xrt_future *xft, const struct xrt_future_result *ft_result) 199 + { 200 + struct u_future *uft = (struct u_future *)xft; 201 + if (uft == NULL || ft_result == NULL) { 202 + return XRT_ERROR_INVALID_ARGUMENT; 203 + } 204 + 205 + os_mutex_lock(&uft->mtx); 206 + const xrt_future_state_t curr_state = u_future_get_state_priv(uft); 207 + if (curr_state != XRT_FUTURE_STATE_PENDING) { 208 + os_mutex_unlock(&uft->mtx); 209 + switch (curr_state) { 210 + case XRT_FUTURE_STATE_READY: return XRT_ERROR_FUTURE_ALREADY_COMPLETE; 211 + case XRT_FUTURE_STATE_CANCELLED: 212 + default: return XRT_OPERATION_CANCELLED; 213 + } 214 + } 215 + 216 + if (ft_result->result == XRT_SUCCESS) { 217 + uft->value = ft_result->value; 218 + } 219 + u_future_set_state_and_xrt_result(uft, XRT_FUTURE_STATE_READY, ft_result->result); 220 + 221 + os_cond_broadcast(&uft->cv); 222 + os_mutex_unlock(&uft->mtx); 223 + return XRT_SUCCESS; 224 + } 225 + 226 + static void 227 + u_future_destroy(struct xrt_future *xft) 228 + { 229 + struct u_future *uft = (struct u_future *)xft; 230 + if (uft == NULL) { 231 + return; 232 + } 233 + 234 + UFT_LOG_T("destroying u_future:%p", (void *)uft); 235 + 236 + u_future_cancel(&uft->base); 237 + u_future_wait(&uft->base, U_FUTURE_CLEANUP_TIMEOUT_NS); 238 + os_cond_destroy(&uft->cv); 239 + os_mutex_destroy(&uft->mtx); 240 + 241 + UFT_LOG_T("u_future:%p destroyed", (void *)uft); 242 + 243 + delete uft; 244 + } 245 + 246 + struct xrt_future * 247 + u_future_create(void) 248 + { 249 + std::unique_ptr<struct u_future> uft{new struct u_future()}; 250 + os_mutex_init(&uft->mtx); 251 + os_cond_init(&uft->cv); 252 + 253 + struct xrt_future *xft = &uft->base; 254 + xft->reference.count = 1; 255 + xft->get_state = u_future_get_state; 256 + xft->get_result = u_future_get_result; 257 + xft->cancel = u_future_cancel; 258 + xft->wait = u_future_wait; 259 + xft->is_cancel_requested = u_future_is_cancel_requested; 260 + xft->complete = u_future_complete; 261 + xft->destroy = u_future_destroy; 262 + 263 + UFT_LOG_T("created u_future:%p", (void *)xft); 264 + return &uft.release()->base; 265 + }
+23
src/xrt/auxiliary/util/u_future.h
··· 1 + // Copyright 2025, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Helper to implement @ref xrt_future, 6 + * A basic CPU based future implementation. 7 + * @author Korcan Hussein <korcan.hussein@collabora.com> 8 + * @ingroup aux_util 9 + */ 10 + #pragma once 11 + 12 + #include "xrt/xrt_future.h" 13 + 14 + #ifdef __cplusplus 15 + extern "C" { 16 + #endif 17 + 18 + struct xrt_future * 19 + u_future_create(void); 20 + 21 + #ifdef __cplusplus 22 + } 23 + #endif
+1
src/xrt/include/xrt/xrt_compositor.h
··· 967 967 bool ext_hand_tracking_enabled; 968 968 bool ext_hand_tracking_data_source_enabled; 969 969 bool ext_eye_gaze_interaction_enabled; 970 + bool ext_future_enabled; 970 971 bool ext_hand_interaction_enabled; 971 972 bool htc_facial_tracking_enabled; 972 973 bool fb_body_tracking_enabled;
+1
src/xrt/include/xrt/xrt_config_build.h.cmake_in
··· 40 40 #cmakedefine XRT_FEATURE_OPENXR_FACE_TRACKING2_FB 41 41 #cmakedefine XRT_FEATURE_OPENXR_FACIAL_TRACKING_HTC 42 42 #cmakedefine XRT_FEATURE_OPENXR_FORCE_FEEDBACK_CURL 43 + #cmakedefine XRT_FEATURE_OPENXR_FUTURE_EXT 43 44 #cmakedefine XRT_FEATURE_OPENXR_HAND_TRACKING_EXT 44 45 #cmakedefine XRT_FEATURE_OPENXR_HAND_TRACKING_DATA_SOURCE_EXT 45 46 #cmakedefine XRT_FEATURE_OPENXR_HAPTIC_PCM
+304
src/xrt/include/xrt/xrt_future.h
··· 1 + // Copyright 2025, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Interface for creating futures. 6 + * @author Korcan Hussein <korcan.hussein@collabora.com> 7 + * @ingroup xrt_iface 8 + */ 9 + #pragma once 10 + 11 + #include "xrt/xrt_defines.h" 12 + #include "xrt/xrt_future_value.h" 13 + 14 + #include <assert.h> 15 + 16 + #ifdef __cplusplus 17 + extern "C" { 18 + #endif 19 + 20 + /*! 21 + * The (future) status of an asynchronous operation 22 + * 23 + * @see xrt_future::get_state 24 + */ 25 + typedef enum xrt_future_state 26 + { 27 + XRT_FUTURE_STATE_PENDING, 28 + XRT_FUTURE_STATE_READY, 29 + XRT_FUTURE_STATE_CANCELLED, 30 + } xrt_future_state_t; 31 + 32 + /*! 33 + * The (future) result of an asynchronous operation 34 + * 35 + * @see xrt_future::get_result, xrt_future::complete 36 + */ 37 + struct xrt_future_result 38 + { 39 + /*! 40 + * The result value of a successfully completed asynchronous operation 41 + * @see xrt_future_value_make 42 + */ 43 + struct xrt_future_value value; 44 + 45 + //! The error/ok status of a completed asynchronous operation 46 + XRT_ALIGNAS(8) xrt_result_t result; 47 + }; 48 + 49 + #define XRT_FUTURE_RESULT(TYPED_VALUE, ERR_CODE) \ 50 + XRT_C11_COMPOUND(struct xrt_future_result) \ 51 + { \ 52 + .value = xrt_future_value_make(TYPED_VALUE), .result = ERR_CODE, \ 53 + } 54 + 55 + /*! 56 + * @interface xrt_future 57 + * @ingroup xrt_iface 58 + * 59 + * @brief A future is a concurrency primitive that provides a mechanism to access results of asynchronous operations. 60 + * 61 + * The xrt_future interface shares similarities with OpenXR futures but is not identical. In comparison to C++ 62 + * std::future, xrt_future combines concepts from both std::shared_future and std::promise with built-in 63 + * cancellation support in a single interface. The interface provides separate method sets for producers 64 + * (result generators) and consumers (result pollers/waiters). 65 + * 66 + * Thread Safety and Reference Counting: 67 + * Each thread that references an xrt_future must properly manage the reference count using 68 + * @ref xrt_future_reference when entering and exiting the thread's scope. 69 + * 70 + * @see "Server-side / driver — implementing async callbacks" in @ref 71 + * [async-functions-and-futures](../../../../doc/async-functions-and-futures.md) for producer example code 72 + */ 73 + struct xrt_future 74 + { 75 + /*! 76 + * Reference helper. 77 + */ 78 + struct xrt_reference reference; 79 + 80 + /*! 81 + * Destroys the future. 82 + */ 83 + void (*destroy)(struct xrt_future *xft); 84 + 85 + /*! 86 + * @brief Gets the current state of the future 87 + * 88 + * @param[in] xft The future. 89 + * @param[out] out_state The current state of @ref xft 90 + * 91 + * @note Consumer Interface 92 + * 93 + * @note Blocking behavior - non-blocking 94 + * 95 + * @see xrt_future_state 96 + */ 97 + xrt_result_t (*get_state)(const struct xrt_future *xft, enum xrt_future_state *out_state); 98 + 99 + /*! 100 + * @brief Gets the future results (after async operation has finished) 101 + * 102 + * @param[in] xft The future. 103 + * @param[out] out_result The future result of @ref xft 104 + * 105 + * @note Consumer Interface 106 + * 107 + * @note Blocking behavior - Non-blocking w.r.t. result: returns immediately, 108 + * may briefly block acquiring an internal mutex to check/consume state 109 + * 110 + * @note differs from std::future::get in that std::future will block & wait the calling thread 111 + * until the result is ready where as xrt_future::get_result is non-blocking (w.r.t. result) 112 + * to achieve the equivalent without using polling interface would be: 113 + * 114 + * // std::future::get == 115 + * xrt_future_wait(xft, INT64_MAX); 116 + * xrt_get_result(xft, &my_result); 117 + * 118 + * @note Similar to or used by OpenXR future/async complete functions 119 + * 120 + * @see xrt_future::complete, xrt_future_result, xrt_future_value 121 + */ 122 + xrt_result_t (*get_result)(const struct xrt_future *xft, struct xrt_future_result *out_result); 123 + 124 + /*! 125 + * @brief Signals an asynchronous operation associated with the future to cancel. 126 + * 127 + * @param[in] xft The future. 128 + * 129 + * @note Consumer Interface 130 + * 131 + * @note Blocking behavior - Non-blocking, may briefly block acquiring an internal mutex to check/consume state 132 + */ 133 + xrt_result_t (*cancel)(struct xrt_future *xft); 134 + 135 + /*! 136 + * @brief Waits on a pending/cancelled future 137 + * 138 + * @param[in] xft The future. 139 + * @param[in] timeout_ns Timeout in nanoseconds or INT64_MAX for infinite duration 140 + * 141 + * @note Consumer Interface 142 + * 143 + * @note Blocking behavior - Blocking 144 + * 145 + * @see xrt_future::cancel, xrt_future::complete 146 + */ 147 + xrt_result_t (*wait)(struct xrt_future *xft, int64_t timeout_ns); 148 + 149 + /*! 150 + * @brief Waits on a cancelled future 151 + * 152 + * @param[in] xft The future. 153 + * @param[out] out_request_cancel Has the consumer requested to cancel the async operation? 154 + * 155 + * @note Producer interface 156 + * 157 + * @note Blocking behavior - non-blocking 158 + * 159 + * @see xrt_future::cancel 160 + */ 161 + xrt_result_t (*is_cancel_requested)(const struct xrt_future *xft, bool *out_request_cancel); 162 + 163 + /*! 164 + * @brief Signals that the asynchronous operation has completed and sets the future’s result. 165 + * 166 + * @param[in] xft The future. 167 + * @param[in] ft_result the result of an async operation associated with @ref xft. 168 + * 169 + * @note Producer interface 170 + * 171 + * @note Blocking behavior - Non-blocking, may briefly block acquiring an internal mutex to check/consume state 172 + * 173 + * @note Differs from OpenXR future/async complete functions as those are used to only get the results of 174 + * a future once the async-operation has finished where as this callback is used by async operation 175 + * to mark/signal completion and return a result 176 + * 177 + * @note Similar to std::promise::set_value 178 + * 179 + * @see xrt_future::get_result, xrt_future_result, xrt_future_value 180 + */ 181 + xrt_result_t (*complete)(struct xrt_future *xft, const struct xrt_future_result *ft_result); 182 + }; 183 + 184 + /*! 185 + * Update the reference counts on xrt_future(s). 186 + * 187 + * @param[in,out] dst Pointer to a object reference: if the object reference is 188 + * non-null will decrement its counter. The reference that 189 + * @p dst points to will be set to @p src. 190 + * @param[in] src New object for @p dst to refer to (may be null). 191 + * If non-null, will have its refcount increased. 192 + * @ingroup xrt_iface 193 + * @relates xrt_future 194 + */ 195 + static inline void 196 + xrt_future_reference(struct xrt_future **dst, struct xrt_future *src) 197 + { 198 + struct xrt_future *old_dst = *dst; 199 + 200 + if (old_dst == src) { 201 + return; 202 + } 203 + 204 + if (src) { 205 + xrt_reference_inc(&src->reference); 206 + } 207 + 208 + *dst = src; 209 + 210 + if (old_dst) { 211 + if (xrt_reference_dec_and_is_zero(&old_dst->reference)) { 212 + assert(old_dst->destroy); 213 + old_dst->destroy(old_dst); 214 + } 215 + } 216 + } 217 + 218 + /*! 219 + * Helper function for @ref xrt_future::get_result. 220 + * 221 + * @copydoc xrt_future::get_result 222 + * 223 + * @public @memberof xrt_future 224 + */ 225 + static inline xrt_result_t 226 + xrt_future_get_result(const struct xrt_future *xft, struct xrt_future_result *out_result) 227 + { 228 + assert(xft && xft->get_result); 229 + return xft->get_result(xft, out_result); 230 + } 231 + 232 + /*! 233 + * Helper function for @ref xrt_future::get_state. 234 + * 235 + * @copydoc xrt_future::get_state 236 + * 237 + * @public @memberof xrt_future 238 + */ 239 + static inline xrt_result_t 240 + xrt_future_get_state(const struct xrt_future *xft, enum xrt_future_state *out_state) 241 + { 242 + assert(xft && xft->get_state); 243 + return xft->get_state(xft, out_state); 244 + } 245 + 246 + /*! 247 + * Helper function for @ref xrt_future::cancel. 248 + * 249 + * @copydoc xrt_future::cancel 250 + * 251 + * @public @memberof xrt_future 252 + */ 253 + static inline xrt_result_t 254 + xrt_future_cancel(struct xrt_future *xft) 255 + { 256 + assert(xft && xft->cancel); 257 + return xft->cancel(xft); 258 + } 259 + 260 + /*! 261 + * Helper function for @ref xrt_future::wait. 262 + * 263 + * @copydoc xrt_future::wait 264 + * 265 + * @public @memberof xrt_future 266 + */ 267 + static inline xrt_result_t 268 + xrt_future_wait(struct xrt_future *xft, int64_t timeout) 269 + { 270 + assert(xft && xft->wait); 271 + return xft->wait(xft, timeout); 272 + } 273 + 274 + /*! 275 + * Helper function for @ref xrt_future::is_cancel_requested. 276 + * 277 + * @copydoc xrt_future::is_cancel_requested 278 + * 279 + * @public @memberof xrt_future 280 + */ 281 + static inline xrt_result_t 282 + xrt_future_is_cancel_requested(const struct xrt_future *xft, bool *out_request_cancel) 283 + { 284 + assert(xft && xft->is_cancel_requested); 285 + return xft->is_cancel_requested(xft, out_request_cancel); 286 + } 287 + 288 + /*! 289 + * Helper function for @ref xrt_future::complete. 290 + * 291 + * @copydoc xrt_future::complete 292 + * 293 + * @public @memberof xrt_future 294 + */ 295 + static inline xrt_result_t 296 + xrt_future_complete(struct xrt_future *xft, const struct xrt_future_result *ft_result) 297 + { 298 + assert(xft && xft->complete); 299 + return xft->complete(xft, ft_result); 300 + } 301 + 302 + #ifdef __cplusplus 303 + } 304 + #endif
+116
src/xrt/include/xrt/xrt_future_value.h
··· 1 + // Copyright 2025, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Variant/algebraic data-type for holding the values of xrt_futures 6 + * @author Korcan Hussein <korcan.hussein@collabora.com> 7 + * 8 + * @see xrt_future, xrt_future_result 9 + * 10 + * @ingroup xrt_iface 11 + */ 12 + #pragma once 13 + 14 + #include "xrt/xrt_compiler.h" 15 + #include "xrt/xrt_defines.h" 16 + 17 + #include <assert.h> 18 + 19 + #ifdef __cplusplus 20 + extern "C" { 21 + #endif 22 + 23 + // X-Macro type definitions: typename/prefix, type, member, user-data 24 + #define XRT_FUTURE_VALUE_TYPES_WITH(_, P) \ 25 + _(UINT64, uint64_t, uint64_, P) \ 26 + _(INT64, int64_t, int64_, P) 27 + 28 + #define XRT_FUTURE_VALUE_WRAP_MACRO(N, T, M, X) X(N, T, M) 29 + 30 + #define XRT_FUTURE_VALUE_TYPES(X) XRT_FUTURE_VALUE_TYPES_WITH(XRT_FUTURE_VALUE_WRAP_MACRO, X) 31 + 32 + typedef enum xrt_future_value_type 33 + { 34 + XRT_FUTURE_VALUE_TYPE_NONE, 35 + 36 + #define X_ENUM_ENTRY(TYPE_NAME, T, M) XRT_FUTURE_VALUE_TYPE_##TYPE_NAME, 37 + XRT_FUTURE_VALUE_TYPES(X_ENUM_ENTRY) 38 + #undef X_ENUM_ENTRY 39 + 40 + // clang-format off 41 + XRT_FUTURE_VALUE_TYPE_LIST_END, 42 + // clang-format on 43 + XRT_FUTURE_VALUE_TYPE_COUNT = XRT_FUTURE_VALUE_TYPE_LIST_END - 1, 44 + } xrt_future_value_type_t; 45 + 46 + struct xrt_future_value 47 + { 48 + union { 49 + #define X_MEMBER_ENTRY(N, TYPE, MEMBER) TYPE MEMBER; 50 + XRT_FUTURE_VALUE_TYPES(X_MEMBER_ENTRY) 51 + #undef X_MEMBER_ENTRY 52 + }; 53 + XRT_ALIGNAS(8) xrt_future_value_type_t type; 54 + }; 55 + 56 + static inline bool 57 + xrt_future_value_is_valid(const struct xrt_future_value *xfv) 58 + { 59 + return xfv && xfv->type != XRT_FUTURE_VALUE_TYPE_NONE; 60 + } 61 + 62 + #define XRT_FUTURE_VALUE_MAKE(TYPE_NAME, MEMBER, VALUE) \ 63 + XRT_C11_COMPOUND(struct xrt_future_value) \ 64 + { \ 65 + .MEMBER = VALUE, .type = XRT_FUTURE_VALUE_TYPE_##TYPE_NAME, \ 66 + } 67 + 68 + #define XRT_NULL_FUTURE_VALUE XRT_FUTURE_VALUE_MAKE(NONE, uint64_, 0) 69 + 70 + static inline struct xrt_future_value 71 + xrt_future_value_make_none(const void *ignore) 72 + { 73 + (void)ignore; 74 + return XRT_NULL_FUTURE_VALUE; 75 + } 76 + 77 + #define X_MAKE_CONS_FN(TYPE_NAME, TYPE, MEMBER) \ 78 + static inline struct xrt_future_value xrt_future_value_make_##MEMBER(TYPE value) \ 79 + { \ 80 + return XRT_FUTURE_VALUE_MAKE(TYPE_NAME, MEMBER, value); \ 81 + } \ 82 + \ 83 + static inline struct xrt_future_value xrt_future_value_make_##MEMBER##_ptr(const TYPE *value) \ 84 + { \ 85 + assert(value != NULL); \ 86 + return XRT_FUTURE_VALUE_MAKE(TYPE_NAME, MEMBER, (*value)); \ 87 + } 88 + XRT_FUTURE_VALUE_TYPES(X_MAKE_CONS_FN) 89 + #undef X_MAKE_CONS_FN 90 + #ifdef __cplusplus 91 + } 92 + #endif 93 + 94 + #ifdef __cplusplus 95 + extern "C++" { 96 + #define X_MAKE_CONS_FN(TYPE_NAME, TYPE, MEMBER) \ 97 + inline struct xrt_future_value xrt_future_value_make(const TYPE &value) \ 98 + { \ 99 + return XRT_FUTURE_VALUE_MAKE(TYPE_NAME, MEMBER, value); \ 100 + } 101 + XRT_FUTURE_VALUE_TYPES(X_MAKE_CONS_FN) 102 + #undef X_MAKE_CONS_FN 103 + } 104 + #else 105 + // clang-format off 106 + #define XRT_FUTURE_VALUE_TYPECASE(TYPE_NAME, TYPE, MEMBER, P) \ 107 + TYPE: xrt_future_value_make_##MEMBER, \ 108 + const TYPE*: xrt_future_value_make_##MEMBER##_ptr, 109 + 110 + #define xrt_future_value_make(VALUE) \ 111 + _Generic((VALUE), \ 112 + XRT_FUTURE_VALUE_TYPES_WITH(XRT_FUTURE_VALUE_TYPECASE, _) \ 113 + default: xrt_future_value_make_none \ 114 + )(VALUE) 115 + // clang-format on 116 + #endif
+1
src/xrt/include/xrt/xrt_instance.h
··· 74 74 bool ext_hand_tracking_enabled; 75 75 bool ext_hand_tracking_data_source_enabled; 76 76 bool ext_eye_gaze_interaction_enabled; 77 + bool ext_future_enabled; 77 78 bool ext_hand_interaction_enabled; 78 79 bool htc_facial_tracking_enabled; 79 80 bool fb_body_tracking_enabled;
+1
src/xrt/ipc/CMakeLists.txt
··· 64 64 client/ipc_client_compositor.c 65 65 client/ipc_client_connection.c 66 66 client/ipc_client_device.c 67 + client/ipc_client_future.c 67 68 client/ipc_client_hmd.c 68 69 client/ipc_client_instance.c 69 70 client/ipc_client_session.c
+3
src/xrt/ipc/client/ipc_client.h
··· 130 130 131 131 struct xrt_session * 132 132 ipc_client_session_create(struct ipc_connection *ipc_c); 133 + 134 + struct xrt_future * 135 + ipc_client_future_create(struct ipc_connection *ipc_c, uint32_t future_id);
+117
src/xrt/ipc/client/ipc_client_future.c
··· 1 + // Copyright 2025, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief IPC Client futures. 6 + * @author Korcan Hussein <korcan@hussein.com> 7 + * @ingroup ipc_client 8 + */ 9 + 10 + #include "xrt/xrt_defines.h" 11 + 12 + #include "shared/ipc_message_channel.h" 13 + 14 + #include "client/ipc_client.h" 15 + #include "client/ipc_client_connection.h" 16 + 17 + #include "ipc_client_generated.h" 18 + 19 + struct ipc_client_future 20 + { 21 + struct xrt_future base; 22 + struct ipc_connection *ipc_c; 23 + uint32_t id; 24 + }; 25 + 26 + static inline struct ipc_client_future * 27 + ipc_client_future(struct xrt_future *xft) 28 + { 29 + return (struct ipc_client_future *)xft; 30 + } 31 + 32 + static inline const struct ipc_client_future * 33 + const_ipc_client_future(const struct xrt_future *xft) 34 + { 35 + return (const struct ipc_client_future *)xft; 36 + } 37 + 38 + static xrt_result_t 39 + get_state(const struct xrt_future *xft, enum xrt_future_state *out_state) 40 + { 41 + if (out_state == NULL) { 42 + return XRT_ERROR_INVALID_ARGUMENT; 43 + } 44 + const struct ipc_client_future *ipc_xft = const_ipc_client_future(xft); 45 + const xrt_result_t xret = ipc_call_future_get_state(ipc_xft->ipc_c, ipc_xft->id, out_state); 46 + IPC_CHK_ALWAYS_RET(ipc_xft->ipc_c, xret, "ipc_call_future_get_state"); 47 + } 48 + 49 + static xrt_result_t 50 + get_result(const struct xrt_future *xft, struct xrt_future_result *out_ft_result) 51 + { 52 + if (out_ft_result == NULL) { 53 + return XRT_ERROR_INVALID_ARGUMENT; 54 + } 55 + const struct ipc_client_future *ipc_xft = const_ipc_client_future(xft); 56 + const xrt_result_t xret = ipc_call_future_get_result(ipc_xft->ipc_c, ipc_xft->id, out_ft_result); 57 + IPC_CHK_ALWAYS_RET(ipc_xft->ipc_c, xret, "ipc_call_future_get_result"); 58 + } 59 + 60 + static xrt_result_t 61 + cancel(struct xrt_future *xft) 62 + { 63 + struct ipc_client_future *ipc_xft = ipc_client_future(xft); 64 + const xrt_result_t xret = ipc_call_future_cancel(ipc_xft->ipc_c, ipc_xft->id); 65 + IPC_CHK_ALWAYS_RET(ipc_xft->ipc_c, xret, "ipc_call_future_cancel"); 66 + } 67 + 68 + static xrt_result_t 69 + wait(struct xrt_future *xft, int64_t timeout_ns) 70 + { 71 + return XRT_ERROR_NOT_IMPLEMENTED; 72 + } 73 + 74 + static xrt_result_t 75 + is_cancel_requested(const struct xrt_future *xft, bool *out_request_cancel) 76 + { 77 + return XRT_ERROR_NOT_IMPLEMENTED; 78 + } 79 + 80 + static xrt_result_t 81 + complete(struct xrt_future *xft, const struct xrt_future_result *ft_result) 82 + { 83 + return XRT_ERROR_NOT_IMPLEMENTED; 84 + } 85 + 86 + static void 87 + destroy(struct xrt_future *xft) 88 + { 89 + struct ipc_client_future *ipc_xft = ipc_client_future(xft); 90 + if (ipc_xft == NULL) { 91 + return; 92 + } 93 + const xrt_result_t xret = ipc_call_future_destroy(ipc_xft->ipc_c, ipc_xft->id); 94 + IPC_CHK_ONLY_PRINT(ipc_xft->ipc_c, xret, "ipc_call_future_destroy"); 95 + 96 + free(ipc_xft); 97 + } 98 + 99 + struct xrt_future * 100 + ipc_client_future_create(struct ipc_connection *ipc_c, uint32_t future_id) 101 + { 102 + assert(ipc_c != NULL); 103 + struct ipc_client_future *icft = U_TYPED_CALLOC(struct ipc_client_future); 104 + struct xrt_future *xft = &icft->base; 105 + xft->destroy = destroy; 106 + xft->get_state = get_state; 107 + xft->get_result = get_result; 108 + xft->cancel = cancel; 109 + xft->wait = wait; 110 + xft->is_cancel_requested = is_cancel_requested; 111 + xft->complete = complete; 112 + xft->reference.count = 1; 113 + icft->id = future_id; 114 + icft->ipc_c = ipc_c; 115 + 116 + return xft; 117 + }
+4
src/xrt/ipc/server/ipc_server.h
··· 58 58 #define IPC_MAX_CLIENT_SEMAPHORES 8 59 59 #define IPC_MAX_CLIENT_SWAPCHAINS (XRT_MAX_LAYERS * 2) 60 60 #define IPC_MAX_CLIENT_SPACES 128 61 + #define IPC_MAX_CLIENT_FUTURES 128 61 62 62 63 struct xrt_instance; 63 64 struct xrt_compositor; ··· 112 113 113 114 //! Ptrs to the semaphores. 114 115 struct xrt_compositor_semaphore *xcsems[IPC_MAX_CLIENT_SEMAPHORES]; 116 + 117 + //! Ptrs to the futures. 118 + struct xrt_future *xfts[IPC_MAX_CLIENT_FUTURES]; 115 119 116 120 struct 117 121 {
+92
src/xrt/ipc/server/ipc_server_handler.c
··· 285 285 return XRT_SUCCESS; 286 286 } 287 287 288 + XRT_MAYBE_UNUSED xrt_result_t 289 + get_new_future_id(volatile struct ipc_client_state *ics, uint32_t *out_id) 290 + { 291 + // Our handle is just the index for now. 292 + uint32_t index = 0; 293 + for (; index < IPC_MAX_CLIENT_FUTURES; ++index) { 294 + if (ics->xfts[index] == NULL) { 295 + break; 296 + } 297 + } 298 + 299 + if (index >= IPC_MAX_CLIENT_FUTURES) { 300 + IPC_ERROR(ics->server, "Too many futures!"); 301 + return XRT_ERROR_IPC_FAILURE; 302 + } 303 + 304 + *out_id = index; 305 + 306 + return XRT_SUCCESS; 307 + } 308 + 309 + static inline xrt_result_t 310 + validate_future_id(volatile struct ipc_client_state *ics, uint32_t future_id, struct xrt_future **out_xft) 311 + { 312 + if (future_id >= IPC_MAX_CLIENT_FUTURES) { 313 + return XRT_ERROR_IPC_FAILURE; 314 + } 315 + 316 + if (ics->xfts[future_id] == NULL) { 317 + return XRT_ERROR_IPC_FAILURE; 318 + } 319 + 320 + *out_xft = (struct xrt_future *)ics->xfts[future_id]; 321 + return (*out_xft != NULL) ? XRT_SUCCESS : XRT_ERROR_ALLOCATION; 322 + } 323 + 324 + static inline xrt_result_t 325 + release_future(volatile struct ipc_client_state *ics, uint32_t future_id) 326 + { 327 + struct xrt_future *xft = NULL; 328 + xrt_result_t xret = validate_future_id(ics, future_id, &xft); 329 + if (xret != XRT_SUCCESS) { 330 + return xret; 331 + } 332 + xrt_future_reference(&xft, NULL); 333 + ics->xfts[future_id] = NULL; 334 + return XRT_SUCCESS; 335 + } 336 + 288 337 /* 289 338 * 290 339 * Handle functions. ··· 331 380 EXT(ext_hand_tracking_enabled); 332 381 EXT(ext_hand_tracking_data_source_enabled); 333 382 EXT(ext_eye_gaze_interaction_enabled); 383 + EXT(ext_future_enabled); 334 384 EXT(ext_hand_interaction_enabled); 335 385 EXT(htc_facial_tracking_enabled); 336 386 EXT(fb_body_tracking_enabled); ··· 428 478 .ext_hand_tracking_enabled = ics->client_state.info.ext_hand_tracking_enabled, 429 479 .ext_hand_tracking_data_source_enabled = ics->client_state.info.ext_hand_tracking_data_source_enabled, 430 480 .ext_eye_gaze_interaction_enabled = ics->client_state.info.ext_eye_gaze_interaction_enabled, 481 + .ext_future_enabled = ics->client_state.info.ext_future_enabled, 431 482 .ext_hand_interaction_enabled = ics->client_state.info.ext_hand_interaction_enabled, 432 483 .htc_facial_tracking_enabled = ics->client_state.info.htc_facial_tracking_enabled, 433 484 .fb_body_tracking_enabled = ics->client_state.info.fb_body_tracking_enabled, ··· 2580 2631 2581 2632 return xrt_device_set_brightness(xdev, brightness, relative); 2582 2633 } 2634 + 2635 + xrt_result_t 2636 + ipc_handle_future_get_state(volatile struct ipc_client_state *ics, uint32_t future_id, enum xrt_future_state *out_state) 2637 + { 2638 + struct xrt_future *xft = NULL; 2639 + xrt_result_t xret = validate_future_id(ics, future_id, &xft); 2640 + if (xret != XRT_SUCCESS) { 2641 + return xret; 2642 + } 2643 + return xrt_future_get_state(xft, out_state); 2644 + } 2645 + 2646 + xrt_result_t 2647 + ipc_handle_future_get_result(volatile struct ipc_client_state *ics, 2648 + uint32_t future_id, 2649 + struct xrt_future_result *out_ft_result) 2650 + { 2651 + struct xrt_future *xft = NULL; 2652 + xrt_result_t xret = validate_future_id(ics, future_id, &xft); 2653 + if (xret != XRT_SUCCESS) { 2654 + return xret; 2655 + } 2656 + return xrt_future_get_result(xft, out_ft_result); 2657 + } 2658 + 2659 + xrt_result_t 2660 + ipc_handle_future_cancel(volatile struct ipc_client_state *ics, uint32_t future_id) 2661 + { 2662 + struct xrt_future *xft = NULL; 2663 + xrt_result_t xret = validate_future_id(ics, future_id, &xft); 2664 + if (xret != XRT_SUCCESS) { 2665 + return xret; 2666 + } 2667 + return xrt_future_cancel(xft); 2668 + } 2669 + 2670 + xrt_result_t 2671 + ipc_handle_future_destroy(volatile struct ipc_client_state *ics, uint32_t future_id) 2672 + { 2673 + return release_future(ics, future_id); 2674 + }
+6
src/xrt/ipc/server/ipc_server_per_client_thread.c
··· 389 389 IPC_TRACE(ics->server, "Destroyed compositor semaphore %d.", j); 390 390 } 391 391 392 + for (uint32_t j = 0; j < IPC_MAX_CLIENT_FUTURES; j++) { 393 + // Drop our reference, does NULL checking. Cast away volatile. 394 + xrt_future_reference((struct xrt_future **)&ics->xfts[j], NULL); 395 + IPC_TRACE(ics->server, "Destroyed future %d.", j); 396 + } 397 + 392 398 os_mutex_unlock(&ics->server->global_state.lock); 393 399 394 400 // Cast away volatile.
+1
src/xrt/ipc/shared/ipc_protocol.h
··· 16 16 #include "xrt/xrt_compositor.h" 17 17 #include "xrt/xrt_results.h" 18 18 #include "xrt/xrt_defines.h" 19 + #include "xrt/xrt_future.h" 19 20 #include "xrt/xrt_system.h" 20 21 #include "xrt/xrt_session.h" 21 22 #include "xrt/xrt_instance.h"
+30
src/xrt/ipc/shared/proto.json
··· 635 635 {"name": "brightness", "type": "float"}, 636 636 {"name": "relative", "type": "bool"} 637 637 ] 638 + }, 639 + 640 + "future_get_state": { 641 + "in": [ 642 + {"name": "future_id", "type": "uint32_t"} 643 + ], 644 + "out": [ 645 + {"name": "out_state", "type": "enum xrt_future_state"} 646 + ] 647 + }, 648 + 649 + "future_cancel": { 650 + "in": [ 651 + {"name": "future_id", "type": "uint32_t"} 652 + ] 653 + }, 654 + 655 + "future_destroy": { 656 + "in": [ 657 + {"name": "future_id", "type": "uint32_t"} 658 + ] 659 + }, 660 + 661 + "future_get_result": { 662 + "in": [ 663 + {"name": "future_id", "type": "uint32_t"} 664 + ], 665 + "out": [ 666 + {"name": "out_ft_result", "type": "struct xrt_future_result"} 667 + ] 638 668 } 639 669 }
+4
src/xrt/state_trackers/oxr/CMakeLists.txt
··· 114 114 target_sources(st_oxr PRIVATE oxr_api_face_tracking2_fb.c oxr_face_tracking2_fb.c) 115 115 endif() 116 116 117 + if(XRT_FEATURE_OPENXR_FUTURE_EXT) 118 + target_sources(st_oxr PRIVATE oxr_api_future.c oxr_future.c) 119 + endif() 120 + 117 121 target_link_libraries( 118 122 st_oxr 119 123 PRIVATE
+14
src/xrt/state_trackers/oxr/oxr_api_funcs.h
··· 765 765 const XrBodyTrackingCalibrationInfoMETA *calibrationInfo); 766 766 #endif 767 767 768 + /* 769 + * 770 + * oxr_api_future.c 771 + * 772 + */ 773 + 774 + #ifdef OXR_HAVE_EXT_future 775 + XRAPI_ATTR XrResult XRAPI_CALL 776 + oxr_xrPollFutureEXT(XrInstance instance, const XrFuturePollInfoEXT *pollInfo, XrFuturePollResultEXT *pollResult); 777 + 778 + XRAPI_ATTR XrResult XRAPI_CALL 779 + oxr_xrCancelFutureEXT(XrInstance instance, const XrFutureCancelInfoEXT *cancelInfo); 780 + #endif 781 + 768 782 /*! 769 783 * @} 770 784 */
+43
src/xrt/state_trackers/oxr/oxr_api_future.c
··· 1 + // Copyright 2025, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief future related API entrypoint functions. 6 + * @author Korcan Hussein <korcan.hussein@collabora.com> 7 + * @ingroup oxr_api 8 + */ 9 + #include "util/u_trace_marker.h" 10 + 11 + #include "oxr_objects.h" 12 + #include "oxr_logger.h" 13 + 14 + #include "oxr_api_funcs.h" 15 + #include "oxr_api_verify.h" 16 + #include "oxr_handle.h" 17 + #include "oxr_defines.h" 18 + 19 + XRAPI_ATTR XrResult XRAPI_CALL 20 + oxr_xrPollFutureEXT(XrInstance instance, const XrFuturePollInfoEXT *pollInfo, XrFuturePollResultEXT *pollResult) 21 + { 22 + struct oxr_logger log; 23 + struct oxr_instance *inst = NULL; 24 + struct oxr_future_ext *oxr_future = NULL; 25 + OXR_VERIFY_INSTANCE_AND_INIT_LOG(&log, instance, inst, "xrPollFutureEXT"); 26 + OXR_VERIFY_EXTENSION(&log, inst, EXT_future); 27 + OXR_VERIFY_ARG_TYPE_AND_NOT_NULL(&log, pollInfo, XR_TYPE_FUTURE_POLL_INFO_EXT); 28 + OXR_VERIFY_ARG_TYPE_AND_NOT_NULL(&log, pollResult, XR_TYPE_FUTURE_POLL_RESULT_EXT); 29 + OXR_VERIFY_FUTURE_AND_INIT_LOG(&log, pollInfo->future, oxr_future, "xrPollFutureEXT"); 30 + return oxr_future_ext_poll(&log, oxr_future, pollResult); 31 + } 32 + 33 + XRAPI_ATTR XrResult XRAPI_CALL 34 + oxr_xrCancelFutureEXT(XrInstance instance, const XrFutureCancelInfoEXT *cancelInfo) 35 + { 36 + struct oxr_logger log; 37 + struct oxr_instance *inst = NULL; 38 + struct oxr_future_ext *oxr_future = NULL; 39 + OXR_VERIFY_INSTANCE_AND_INIT_LOG(&log, instance, inst, "xrCancelFutureEXT"); 40 + OXR_VERIFY_EXTENSION(&log, inst, EXT_future); 41 + OXR_VERIFY_FUTURE_AND_INIT_LOG(&log, cancelInfo->future, oxr_future, "xrCancelFutureEXT"); 42 + return oxr_future_ext_cancel(&log, oxr_future); 43 + }
+6 -1
src/xrt/state_trackers/oxr/oxr_api_negotiate.c
··· 412 412 #ifdef OXR_HAVE_META_body_tracking_calibration 413 413 ENTRY_IF_EXT(xrResetBodyTrackingCalibrationMETA, META_body_tracking_calibration); 414 414 ENTRY_IF_EXT(xrSuggestBodyTrackingCalibrationOverrideMETA, META_body_tracking_calibration); 415 - #endif 415 + #endif // OXR_HAVE_META_body_tracking_calibration 416 + 417 + #ifdef OXR_HAVE_EXT_future 418 + ENTRY_IF_EXT(xrPollFutureEXT, EXT_future); 419 + ENTRY_IF_EXT(xrCancelFutureEXT, EXT_future); 420 + #endif // OXR_HAVE_EXT_future 416 421 417 422 #ifdef OXR_HAVE_KHR_extended_struct_name_lengths 418 423 ENTRY_IF_EXT(xrStructureTypeToString2KHR, KHR_extended_struct_name_lengths);
+11
src/xrt/state_trackers/oxr/oxr_api_verify.h
··· 92 92 OXR_VERIFY_AND_SET_AND_INIT(log, thing, new_thing, oxr_xdev_list, XDEVLIST, name, new_thing->sess->sys->inst) 93 93 #define OXR_VERIFY_PLANE_DETECTOR_AND_INIT_LOG(log, thing, new_thing, name) \ 94 94 OXR_VERIFY_AND_SET_AND_INIT(log, thing, new_thing, oxr_plane_detector_ext, PLANEDET, name, new_thing->sess->sys->inst) 95 + #define OXR_VERIFY_FUTURE_AND_INIT_LOG(log, thing, new_thing, name) \ 96 + OXR_VERIFY_AND_SET_AND_INIT(log, thing, new_thing, oxr_future_ext, FUTURE, name, new_thing->inst); \ 97 + OXR_VERIFY_FUTURE_VALID(log, new_thing) 95 98 // clang-format on 96 99 97 100 #define OXR_VERIFY_INSTANCE_NOT_NULL(log, arg, new_arg) OXR_VERIFY_SET(log, arg, new_arg, oxr_instance, INSTANCE); ··· 103 106 #define OXR_VERIFY_ACTIONSET_NOT_NULL(log, arg, new_arg) OXR_VERIFY_SET(log, arg, new_arg, oxr_action_set, ACTIONSET); 104 107 #define OXR_VERIFY_XDEVLIST_NOT_NULL(log, arg, new_arg) OXR_VERIFY_SET(log, arg, new_arg, oxr_xdev_list, XDEVLIST); 105 108 109 + #define OXR_VERIFY_FUTURE_NOT_NULL(log, arg, new_arg) OXR_VERIFY_SET(log, arg, new_arg, oxr_future_ext, FUTURE); 106 110 /*! 107 111 * Checks if a required extension is enabled. 108 112 * ··· 322 326 if (verify_ret != XR_SUCCESS) { \ 323 327 return verify_ret; \ 324 328 } \ 329 + } \ 330 + } while (false) 331 + 332 + #define OXR_VERIFY_FUTURE_VALID(LOG, OXR_FT) \ 333 + do { \ 334 + if (OXR_FT->xft == NULL) { \ 335 + return oxr_error(LOG, XR_ERROR_FUTURE_INVALID_EXT, "future is not valid"); \ 325 336 } \ 326 337 } while (false) 327 338
+2
src/xrt/state_trackers/oxr/oxr_defines.h
··· 34 34 #define OXR_XR_DEBUG_XDEVLIST (*(uint64_t *)"oxrxdli\0") 35 35 // plane detection 36 36 #define OXR_XR_DEBUG_PLANEDET (*(uint64_t *)"oxrplan\0") 37 + // futures 38 + #define OXR_XR_DEBUG_FUTURE (*(uint64_t *)"oxrfutr\0") 37 39 // clang-format on 38 40 39 41 /*!
+12
src/xrt/state_trackers/oxr/oxr_extension_support.h
··· 351 351 352 352 353 353 /* 354 + * XR_EXT_future 355 + */ 356 + #if defined(XR_EXT_future) && defined(XRT_FEATURE_OPENXR_FUTURE_EXT) 357 + #define OXR_HAVE_EXT_future 358 + #define OXR_EXTENSION_SUPPORT_EXT_future(_) _(EXT_future, EXT_FUTURE) 359 + #else 360 + #define OXR_EXTENSION_SUPPORT_EXT_future(_) 361 + #endif 362 + 363 + 364 + /* 354 365 * XR_EXT_hand_interaction 355 366 */ 356 367 #if defined(XR_EXT_hand_interaction) && defined(XRT_FEATURE_OPENXR_INTERACTION_EXT_HAND) ··· 903 914 OXR_EXTENSION_SUPPORT_EXT_debug_utils(_) \ 904 915 OXR_EXTENSION_SUPPORT_EXT_dpad_binding(_) \ 905 916 OXR_EXTENSION_SUPPORT_EXT_eye_gaze_interaction(_) \ 917 + OXR_EXTENSION_SUPPORT_EXT_future(_) \ 906 918 OXR_EXTENSION_SUPPORT_EXT_hand_interaction(_) \ 907 919 OXR_EXTENSION_SUPPORT_EXT_hand_tracking(_) \ 908 920 OXR_EXTENSION_SUPPORT_EXT_hand_tracking_data_source(_) \
+94
src/xrt/state_trackers/oxr/oxr_future.c
··· 1 + // Copyright 2025, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief future related functions. 6 + * @author Korcan Hussein <korcan.hussein@collabora.com> 7 + * @ingroup oxr_main 8 + */ 9 + #include "oxr_objects.h" 10 + #include "oxr_logger.h" 11 + #include "oxr_handle.h" 12 + #include "oxr_xret.h" 13 + 14 + static inline XrFutureStateEXT 15 + oxr_to_XrFutureStateEXT(const xrt_future_state_t fts) 16 + { 17 + switch (fts) { 18 + case XRT_FUTURE_STATE_PENDING: return XR_FUTURE_STATE_PENDING_EXT; 19 + case XRT_FUTURE_STATE_READY: return XR_FUTURE_STATE_READY_EXT; 20 + default: return XR_FUTURE_STATE_MAX_ENUM_EXT; 21 + } 22 + } 23 + 24 + XrResult 25 + oxr_future_invalidate(struct oxr_logger *log, struct oxr_future_ext *oxr_future) 26 + { 27 + (void)log; 28 + if (oxr_future && oxr_future->xft) { 29 + xrt_future_reference(&oxr_future->xft, NULL); 30 + assert(oxr_future->xft == NULL); 31 + } 32 + return XR_SUCCESS; 33 + } 34 + 35 + XrResult 36 + oxr_future_ext_poll(struct oxr_logger *log, const struct oxr_future_ext *oxr_future, XrFuturePollResultEXT *pollResult) 37 + { 38 + assert(log && oxr_future && oxr_future->xft && pollResult); 39 + xrt_future_state_t fts; 40 + const xrt_result_t xres = xrt_future_get_state(oxr_future->xft, &fts); 41 + OXR_CHECK_XRET(log, oxr_future->sess, xres, oxr_future_ext_poll); 42 + pollResult->state = oxr_to_XrFutureStateEXT(fts); 43 + return XR_SUCCESS; 44 + } 45 + 46 + XrResult 47 + oxr_future_ext_cancel(struct oxr_logger *log, struct oxr_future_ext *oxr_future) 48 + { 49 + assert(log && oxr_future && oxr_future->xft); 50 + const xrt_result_t xres = xrt_future_cancel(oxr_future->xft); 51 + OXR_CHECK_XRET(log, oxr_future->sess, xres, oxr_future_ext_cancel); 52 + return oxr_future_invalidate(log, oxr_future); 53 + } 54 + 55 + XrResult 56 + oxr_future_ext_complete(struct oxr_logger *log, 57 + struct oxr_future_ext *oxr_future, 58 + struct xrt_future_result *out_ft_result) 59 + { 60 + struct oxr_session *sess = oxr_future->sess; 61 + const xrt_result_t xret = xrt_future_get_result(oxr_future->xft, out_ft_result); 62 + OXR_CHECK_XRET(log, sess, xret, oxr_future_ext_complete); 63 + if (xret == XRT_ERROR_FUTURE_RESULT_NOT_READY) { 64 + return oxr_error(log, XR_ERROR_FUTURE_PENDING_EXT, "Call to oxr_future_ext_complete failed"); 65 + } 66 + return oxr_future_invalidate(log, oxr_future); 67 + } 68 + 69 + static XrResult 70 + oxr_future_ext_destroy(struct oxr_logger *log, struct oxr_handle_base *hb) 71 + { 72 + struct oxr_future_ext *future_ext = (struct oxr_future_ext *)hb; 73 + if (future_ext && future_ext->xft) { 74 + oxr_future_ext_cancel(log, future_ext); 75 + } 76 + free(future_ext); 77 + return XR_SUCCESS; 78 + } 79 + 80 + XrResult 81 + oxr_future_create(struct oxr_logger *log, 82 + struct oxr_session *sess, 83 + struct xrt_future *xft, 84 + struct oxr_handle_base *parent_handle, 85 + struct oxr_future_ext **out_oxr_future_ext) 86 + { 87 + struct oxr_future_ext *new_future = NULL; 88 + OXR_ALLOCATE_HANDLE_OR_RETURN(log, new_future, OXR_XR_DEBUG_FUTURE, oxr_future_ext_destroy, parent_handle); 89 + new_future->sess = sess; 90 + new_future->inst = sess->sys->inst; 91 + new_future->xft = xft; 92 + *out_oxr_future_ext = new_future; 93 + return XR_SUCCESS; 94 + }
+3
src/xrt/state_trackers/oxr/oxr_instance.c
··· 310 310 #ifdef OXR_HAVE_EXT_eye_gaze_interaction 311 311 .ext_eye_gaze_interaction_enabled = extensions->EXT_eye_gaze_interaction, 312 312 #endif 313 + #ifdef OXR_HAVE_EXT_future 314 + .ext_future_enabled = extensions->EXT_future, 315 + #endif 313 316 #ifdef OXR_HAVE_EXT_hand_interaction 314 317 .ext_hand_interaction_enabled = extensions->EXT_hand_interaction, 315 318 #endif
+59
src/xrt/state_trackers/oxr/oxr_objects.h
··· 14 14 #include "xrt/xrt_space.h" 15 15 #include "xrt/xrt_limits.h" 16 16 #include "xrt/xrt_system.h" 17 + #include "xrt/xrt_future.h" 17 18 #include "xrt/xrt_device.h" 18 19 #include "xrt/xrt_tracking.h" 19 20 #include "xrt/xrt_compositor.h" ··· 2959 2960 uint64_t detection_id; 2960 2961 }; 2961 2962 #endif // OXR_HAVE_EXT_plane_detection 2963 + 2964 + #ifdef OXR_HAVE_EXT_future 2965 + /*! 2966 + * EXT futures. 2967 + * 2968 + * Parent type/handle is @ref oxr_instance 2969 + * 2970 + * @obj{XrFutureEXT} 2971 + * @extends oxr_handle_base 2972 + */ 2973 + struct oxr_future_ext 2974 + { 2975 + //! Common structure for things referred to by OpenXR handles. 2976 + struct oxr_handle_base handle; 2977 + 2978 + //! (weak) reference to instance (may or not be a direct parent handle") 2979 + struct oxr_instance *inst; 2980 + 2981 + //! Owning session. 2982 + struct oxr_session *sess; 2983 + 2984 + //! xrt_future backing this future 2985 + struct xrt_future *xft; 2986 + }; 2987 + 2988 + /*! 2989 + * To go back to a OpenXR object. 2990 + * 2991 + * @relates oxr_future_ext 2992 + */ 2993 + static inline XrFutureEXT 2994 + oxr_future_ext_to_openxr(struct oxr_future_ext *future_ext) 2995 + { 2996 + return XRT_CAST_PTR_TO_OXR_HANDLE(XrFutureEXT, future_ext); 2997 + } 2998 + 2999 + XrResult 3000 + oxr_future_create(struct oxr_logger *log, 3001 + struct oxr_session *sess, 3002 + struct xrt_future *xft, 3003 + struct oxr_handle_base *parent_handle, 3004 + struct oxr_future_ext **out_oxr_future_ext); 3005 + 3006 + XrResult 3007 + oxr_future_invalidate(struct oxr_logger *log, struct oxr_future_ext *oxr_future); 3008 + 3009 + XrResult 3010 + oxr_future_ext_poll(struct oxr_logger *log, const struct oxr_future_ext *oxr_future, XrFuturePollResultEXT *pollResult); 3011 + 3012 + XrResult 3013 + oxr_future_ext_cancel(struct oxr_logger *log, struct oxr_future_ext *oxr_future); 3014 + 3015 + XrResult 3016 + oxr_future_ext_complete(struct oxr_logger *log, 3017 + struct oxr_future_ext *oxr_future, 3018 + struct xrt_future_result *out_ft_result); 3019 + 3020 + #endif 2962 3021 2963 3022 #ifdef OXR_HAVE_EXT_user_presence 2964 3023 XrResult
+3
src/xrt/state_trackers/oxr/oxr_session.c
··· 274 274 #ifdef OXR_HAVE_EXT_eye_gaze_interaction 275 275 .ext_eye_gaze_interaction_enabled = extensions->EXT_eye_gaze_interaction, 276 276 #endif 277 + #ifdef OXR_HAVE_EXT_future 278 + .ext_future_enabled = extensions->EXT_future, 279 + #endif 277 280 #ifdef OXR_HAVE_EXT_hand_interaction 278 281 .ext_hand_interaction_enabled = extensions->EXT_hand_interaction, 279 282 #endif