diff --git a/src/external/openxr_includes/openxr/XR_MNDX_xdev_space.h b/src/external/openxr_includes/openxr/XR_MNDX_xdev_space.h index fcfd962b5..a53b681ca 100644 --- a/src/external/openxr_includes/openxr/XR_MNDX_xdev_space.h +++ b/src/external/openxr_includes/openxr/XR_MNDX_xdev_space.h @@ -1,4 +1,5 @@ // Copyright 2023-2024, Collabora, Ltd. +// Copyright 2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -17,7 +18,7 @@ extern "C" { // Extension number 445 (444 prefix) #define XR_MNDX_xdev_space 1 -#define XR_MNDX_xdev_space_SPEC_VERSION 1 +#define XR_MNDX_xdev_space_SPEC_VERSION 3 #define XR_MNDX_XDEV_SPACE_EXTENSION_NAME "XR_MNDX_xdev_space" @@ -55,6 +56,15 @@ typedef struct XrXDevPropertiesMNDX { XrBool32 canCreateSpace; } XrXDevPropertiesMNDX; +XR_STRUCT_ENUM(XR_TYPE_XDEV_HAND_TRACKING_PROPERTIES_MNDX, 1000444007); +// XrXDevHandTrackingPropertiesMNDX extends XrXDevPropertiesMNDX. +typedef struct XrXDevHandTrackingPropertiesMNDX { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 canCreateHandTrackerLeft; + XrBool32 canCreateHandTrackerRight; +} XrXDevHandTrackingPropertiesMNDX; + XR_STRUCT_ENUM(XR_TYPE_CREATE_XDEV_SPACE_INFO_MNDX, 1000444005); typedef struct XrCreateXDevSpaceInfoMNDX { XrStructureType type; @@ -64,6 +74,18 @@ typedef struct XrCreateXDevSpaceInfoMNDX { XrPosef offset; } XrCreateXDevSpaceInfoMNDX; +XR_STRUCT_ENUM(XR_TYPE_CREATE_HAND_TRACKER_XDEV_MNDX, 1000444006); +/*! + * XrCreateHandTrackerXDevMNDX extends XrHandTrackerCreateInfoEXT when calling + * xrCreateHandTrackerEXT. When chained, it requests that the hand tracker uses + * the specified xdev rather than the runtime's default hand-tracking device. + */ +typedef struct XrCreateHandTrackerXDevMNDX { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrXDevListMNDX xdevList; + XrXDevIdMNDX id; +} XrCreateHandTrackerXDevMNDX; typedef XrResult (XRAPI_PTR *PFN_xrCreateXDevListMNDX)(XrSession session, const XrCreateXDevListInfoMNDX *info, XrXDevListMNDX *xdevList); typedef XrResult (XRAPI_PTR *PFN_xrGetXDevListGenerationNumberMNDX)(XrXDevListMNDX xdevList, uint64_t *outGeneration); diff --git a/src/xrt/state_trackers/oxr/oxr_api_hand_tracking.c b/src/xrt/state_trackers/oxr/oxr_api_hand_tracking.c index faad64a50..e1df86d62 100644 --- a/src/xrt/state_trackers/oxr/oxr_api_hand_tracking.c +++ b/src/xrt/state_trackers/oxr/oxr_api_hand_tracking.c @@ -54,6 +54,10 @@ oxr_xrCreateHandTrackerEXT(XrSession session, return oxr_error(&log, XR_ERROR_VALIDATION_FAILURE, "Invalid hand value %d\n", createInfo->hand); } + if (!oxr_system_get_hand_tracking_support(&log, sess->sys->inst)) { + return oxr_error(&log, XR_ERROR_FEATURE_UNSUPPORTED, "System does not support hand tracking"); + } + #ifdef OXR_HAVE_EXT_hand_tracking_data_source const XrHandTrackingDataSourceInfoEXT *data_source_info = NULL; if (sess->sys->inst->extensions.EXT_hand_tracking_data_source) { @@ -63,7 +67,39 @@ oxr_xrCreateHandTrackerEXT(XrSession session, OXR_VERIFY_HAND_TRACKING_DATA_SOURCE_OR_NULL(&log, data_source_info); #endif - ret = oxr_hand_tracker_create(&log, sess, createInfo, &hand_tracker); + // Lookup the external xdev if provided via XrCreateHandTrackerXDevMNDX + struct xrt_device *override_xdev = NULL; +#ifdef OXR_HAVE_MNDX_xdev_space + const XrCreateHandTrackerXDevMNDX *xdev_info = NULL; + if (sess->sys->supports_xdev_space && sess->sys->inst->extensions.MNDX_xdev_space) { + xdev_info = OXR_GET_INPUT_FROM_CHAIN(createInfo, XR_TYPE_CREATE_HAND_TRACKER_XDEV_MNDX, + XrCreateHandTrackerXDevMNDX); + } + + if (xdev_info != NULL) { + struct oxr_xdev_list *xdl = NULL; + OXR_VERIFY_ARG_TYPE_AND_NOT_NULL(&log, xdev_info, XR_TYPE_CREATE_HAND_TRACKER_XDEV_MNDX); + OXR_VERIFY_XDEVLIST_NOT_NULL(&log, xdev_info->xdevList, xdl); + + if (!oxr_xdev_list_get_xdev(&log, xdl, xdev_info->id, &override_xdev)) { + return oxr_error(&log, XR_ERROR_VALIDATION_FAILURE, + "(createInfo->next->id == %" PRIu64 ") not a valid XrXDevIdMNDX", + xdev_info->id); + } + + if (override_xdev == NULL) { + return oxr_error(&log, XR_ERROR_RUNTIME_FAILURE, "Got null xrt_device?"); + } + + if (!override_xdev->supported.hand_tracking) { + return oxr_error(&log, XR_ERROR_FEATURE_UNSUPPORTED, + "(createInfo->next->id == %" PRIu64 ") XDev does not support hand tracking", + xdev_info->id); + } + } +#endif + + ret = oxr_hand_tracker_create(&log, sess, createInfo, override_xdev, &hand_tracker); if (ret != XR_SUCCESS) { return ret; } diff --git a/src/xrt/state_trackers/oxr/oxr_api_instance.c b/src/xrt/state_trackers/oxr/oxr_api_instance.c index 1fa9957e2..6c0d475c8 100644 --- a/src/xrt/state_trackers/oxr/oxr_api_instance.c +++ b/src/xrt/state_trackers/oxr/oxr_api_instance.c @@ -308,7 +308,9 @@ oxr_xrStructureTypeToString(XrInstance instance, XrStructureType value, char buf EXT_TYPE(XR_TYPE_CREATE_XDEV_LIST_INFO_MNDX) EXT_TYPE(XR_TYPE_GET_XDEV_INFO_MNDX) EXT_TYPE(XR_TYPE_XDEV_PROPERTIES_MNDX) + EXT_TYPE(XR_TYPE_XDEV_HAND_TRACKING_PROPERTIES_MNDX) EXT_TYPE(XR_TYPE_CREATE_XDEV_SPACE_INFO_MNDX) + EXT_TYPE(XR_TYPE_CREATE_HAND_TRACKER_XDEV_MNDX) #endif #ifdef OXR_HAVE_MND_query_egl_device @@ -357,7 +359,9 @@ oxr_xrStructureTypeToString2KHR(XrInstance instance, EXT_TYPE(XR_TYPE_CREATE_XDEV_LIST_INFO_MNDX) EXT_TYPE(XR_TYPE_GET_XDEV_INFO_MNDX) EXT_TYPE(XR_TYPE_XDEV_PROPERTIES_MNDX) + EXT_TYPE(XR_TYPE_XDEV_HAND_TRACKING_PROPERTIES_MNDX) EXT_TYPE(XR_TYPE_CREATE_XDEV_SPACE_INFO_MNDX) + EXT_TYPE(XR_TYPE_CREATE_HAND_TRACKER_XDEV_MNDX) #endif #ifdef OXR_HAVE_MND_query_egl_device diff --git a/src/xrt/state_trackers/oxr/oxr_hand_tracking.c b/src/xrt/state_trackers/oxr/oxr_hand_tracking.c index 58ea2dcb6..8fab83db6 100644 --- a/src/xrt/state_trackers/oxr/oxr_hand_tracking.c +++ b/src/xrt/state_trackers/oxr/oxr_hand_tracking.c @@ -80,24 +80,19 @@ XrResult oxr_hand_tracker_create(struct oxr_logger *log, struct oxr_session *sess, const XrHandTrackerCreateInfoEXT *createInfo, + struct xrt_device *override_xdev, struct oxr_hand_tracker **out_hand_tracker) { - if (!oxr_system_get_hand_tracking_support(log, sess->sys->inst)) { - return oxr_error(log, XR_ERROR_FEATURE_UNSUPPORTED, "System does not support hand tracking"); - } - - struct oxr_hand_tracker *hand_tracker = NULL; - OXR_ALLOCATE_HANDLE_OR_RETURN(log, hand_tracker, OXR_XR_DEBUG_HTRACKER, oxr_hand_tracker_destroy_cb, - &sess->handle); - - hand_tracker->sess = sess; - hand_tracker->hand = createInfo->hand; - hand_tracker->hand_joint_set = createInfo->handJointSet; + // Use variables to avoid creating the hand tracker object until we know it's valid. + struct oxr_hand_tracking_data_source unobstructed = XRT_STRUCT_INIT; + struct oxr_hand_tracking_data_source conforming = XRT_STRUCT_INIT; #define OXR_SET_HT_DATA_SOURCE(SRC, SRC_TYPE) \ { \ struct xrt_device *xdev = NULL; \ - if (createInfo->hand == XR_HAND_LEFT_EXT) { \ + if (override_xdev != NULL) { \ + xdev = override_xdev; \ + } else if (createInfo->hand == XR_HAND_LEFT_EXT) { \ xdev = GET_STATIC_XDEV_BY_ROLE(sess->sys, hand_tracking_##SRC##_left); \ } else if (createInfo->hand == XR_HAND_RIGHT_EXT) { \ xdev = GET_STATIC_XDEV_BY_ROLE(sess->sys, hand_tracking_##SRC##_right); \ @@ -109,16 +104,17 @@ oxr_hand_tracker_create(struct oxr_logger *log, : XRT_INPUT_HT_##SRC_TYPE##_RIGHT; \ struct xrt_input *input = NULL; \ if (oxr_xdev_find_input(xdev, ht_input_name, &input) && input != NULL) { \ - hand_tracker->SRC = (struct oxr_hand_tracking_data_source){ \ + SRC = (struct oxr_hand_tracking_data_source){ \ .xdev = xdev, \ .input_name = ht_input_name, \ }; \ } \ } \ \ - if (xdev != NULL && hand_tracker->SRC.xdev == NULL) \ + if (xdev != NULL && SRC.xdev == NULL && override_xdev == NULL) { \ oxr_warn(log, "We got hand tracking xdev (%s) but it didn't have a hand tracking input.", \ #SRC); \ + } \ } // Find the assigned device. @@ -126,6 +122,21 @@ oxr_hand_tracker_create(struct oxr_logger *log, OXR_SET_HT_DATA_SOURCE(conforming, CONFORMING) #undef OXR_SET_HT_DATA_SOURCE + if (unobstructed.xdev == NULL && conforming.xdev == NULL) { + return oxr_error(log, XR_ERROR_FEATURE_UNSUPPORTED, + "Did not find any hand-tracking inputs on XDev that matched the given hand."); + } + + struct oxr_hand_tracker *hand_tracker = NULL; + OXR_ALLOCATE_HANDLE_OR_RETURN(log, hand_tracker, OXR_XR_DEBUG_HTRACKER, oxr_hand_tracker_destroy_cb, + &sess->handle); + + hand_tracker->sess = sess; + hand_tracker->hand = createInfo->hand; + hand_tracker->hand_joint_set = createInfo->handJointSet; + hand_tracker->unobstructed = unobstructed; + hand_tracker->conforming = conforming; + hand_tracker->requested_sources_count = ARRAY_SIZE(hand_tracker->requested_sources); hand_tracker->requested_sources[0] = &hand_tracker->unobstructed; hand_tracker->requested_sources[1] = &hand_tracker->conforming; diff --git a/src/xrt/state_trackers/oxr/oxr_hand_tracking.h b/src/xrt/state_trackers/oxr/oxr_hand_tracking.h index 9a2d58b77..d7e00f915 100644 --- a/src/xrt/state_trackers/oxr/oxr_hand_tracking.h +++ b/src/xrt/state_trackers/oxr/oxr_hand_tracking.h @@ -92,6 +92,7 @@ XrResult oxr_hand_tracker_create(struct oxr_logger *log, struct oxr_session *sess, const XrHandTrackerCreateInfoEXT *createInfo, + struct xrt_device *override_xdev, struct oxr_hand_tracker **out_hand_tracker); XrResult diff --git a/src/xrt/state_trackers/oxr/oxr_objects.h b/src/xrt/state_trackers/oxr/oxr_objects.h index 1df1194f6..7ead14418 100644 --- a/src/xrt/state_trackers/oxr/oxr_objects.h +++ b/src/xrt/state_trackers/oxr/oxr_objects.h @@ -837,6 +837,12 @@ oxr_xdev_list_space_create(struct oxr_logger *log, uint32_t index, struct oxr_space **out_space); +bool +oxr_xdev_list_get_xdev(struct oxr_logger *log, + struct oxr_xdev_list *xdl, + XrXDevIdMNDX id, + struct xrt_device **out_xdev); + #endif // OXR_HAVE_MNDX_xdev_space diff --git a/src/xrt/state_trackers/oxr/oxr_xdev.c b/src/xrt/state_trackers/oxr/oxr_xdev.c index d5febbd57..465afbc5b 100644 --- a/src/xrt/state_trackers/oxr/oxr_xdev.c +++ b/src/xrt/state_trackers/oxr/oxr_xdev.c @@ -1,5 +1,5 @@ // Copyright 2019-2024, Collabora, Ltd. -// Copyright 2026, NVIDIA CORPORATION. +// Copyright 2025-2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -21,6 +21,7 @@ #include "oxr_handle_base.h" #include "oxr_objects.h" #include "oxr_logger.h" +#include "oxr_chain.h" #include @@ -69,6 +70,20 @@ find_suitable_pose_name(struct xrt_device *xdev) return 0; } + +static bool +can_create_hand_tracker(struct xrt_device *xdev, + enum xrt_input_name unobstructed_name, + enum xrt_input_name conforming_name) +{ + if (xdev == NULL || !xdev->supported.hand_tracking) { + return false; + } + + struct xrt_input *input = NULL; + return oxr_xdev_find_input(xdev, unobstructed_name, &input) || + oxr_xdev_find_input(xdev, conforming_name, &input); +} #endif @@ -211,6 +226,15 @@ oxr_xdev_list_get_properties(struct oxr_logger *log, snprintf(properties->serial, ARRAY_SIZE(properties->serial), "%s", xdev->serial); properties->canCreateSpace = can_create_space; + XrXDevHandTrackingPropertiesMNDX *hand_tracking_properties = OXR_GET_OUTPUT_FROM_CHAIN( + properties, XR_TYPE_XDEV_HAND_TRACKING_PROPERTIES_MNDX, XrXDevHandTrackingPropertiesMNDX); + if (hand_tracking_properties != NULL) { + hand_tracking_properties->canCreateHandTrackerLeft = + can_create_hand_tracker(xdev, XRT_INPUT_HT_UNOBSTRUCTED_LEFT, XRT_INPUT_HT_CONFORMING_LEFT); + hand_tracking_properties->canCreateHandTrackerRight = + can_create_hand_tracker(xdev, XRT_INPUT_HT_UNOBSTRUCTED_RIGHT, XRT_INPUT_HT_CONFORMING_RIGHT); + } + return XR_SUCCESS; } @@ -238,4 +262,24 @@ oxr_xdev_list_space_create(struct oxr_logger *log, return oxr_space_xdev_pose_create(log, xdl->sess, xdev, name, pose, out_space); } +bool +oxr_xdev_list_get_xdev(struct oxr_logger *log, struct oxr_xdev_list *xdl, XrXDevIdMNDX id, struct xrt_device **out_xdev) +{ + uint32_t index = 0; + for (; index < xdl->device_count; index++) { + if (id != xdl->ids[index]) { + continue; + } + break; + } + + if (index >= xdl->device_count) { + return false; + } + + *out_xdev = xdl->xdevs[index]; + + return true; +} + #endif // OXR_HAVE_MNDX_xdev_space