diff --git a/src/xrt/auxiliary/util/u_pretty_print.c b/src/xrt/auxiliary/util/u_pretty_print.c index 7fef52067..8061419a3 100644 --- a/src/xrt/auxiliary/util/u_pretty_print.c +++ b/src/xrt/auxiliary/util/u_pretty_print.c @@ -267,6 +267,7 @@ u_pp_xrt_result(struct u_pp_delegate dg, xrt_result_t xret) case XRT_OPERATION_CANCELLED: DG("XRT_OPERATION_CANCELLED"); return; case XRT_ERROR_FUTURE_RESULT_NOT_READY: DG("XRT_ERROR_FUTURE_RESULT_NOT_READY"); return; case XRT_ERROR_FUTURE_ALREADY_COMPLETE: DG("XRT_ERROR_FUTURE_ALREADY_COMPLETE"); return; + case XRT_ERROR_DEVICE_NOT_ATTACHABLE: DG("XRT_ERROR_DEVICE_NOT_ATTACHABLE"); return; } // clang-format on diff --git a/src/xrt/auxiliary/util/u_space_overseer.c b/src/xrt/auxiliary/util/u_space_overseer.c index c55dda8a2..8198e75a6 100644 --- a/src/xrt/auxiliary/util/u_space_overseer.c +++ b/src/xrt/auxiliary/util/u_space_overseer.c @@ -43,6 +43,13 @@ enum u_space_type U_SPACE_TYPE_POSE, U_SPACE_TYPE_OFFSET, U_SPACE_TYPE_ROOT, + + /*! + * Space designed to be attachable to others, most importantly it is + * re-attachable, and in order to move all of the spaces that has this + * space as it's parent/next we need a node that can be updated. + */ + U_SPACE_TYPE_ATTACHABLE, }; /*! @@ -331,7 +338,8 @@ push_then_traverse(struct xrt_relation_chain *xrc, struct u_space *space, int64_ m_relation_chain_push_relation(xrc, &xsr); } break; case U_SPACE_TYPE_OFFSET: m_relation_chain_push_pose_if_not_identity(xrc, &space->offset.pose); break; - case U_SPACE_TYPE_ROOT: return; // Stops the traversing. + case U_SPACE_TYPE_ROOT: return; // Stops the traversing. + case U_SPACE_TYPE_ATTACHABLE: break; // No-op } // Please tail-call optimise this miss compiler. @@ -353,7 +361,8 @@ traverse_then_push_inverse(struct xrt_relation_chain *xrc, struct u_space *space case U_SPACE_TYPE_NULL: break; case U_SPACE_TYPE_POSE: break; case U_SPACE_TYPE_OFFSET: break; - case U_SPACE_TYPE_ROOT: return; // Stops the traversing. + case U_SPACE_TYPE_ROOT: return; // Stops the traversing. + case U_SPACE_TYPE_ATTACHABLE: break; // No-op } // Can't tail-call optimise this one :( @@ -372,6 +381,7 @@ traverse_then_push_inverse(struct xrt_relation_chain *xrc, struct u_space *space } break; case U_SPACE_TYPE_OFFSET: m_relation_chain_push_inverted_pose_if_not_identity(xrc, &space->offset.pose); break; case U_SPACE_TYPE_ROOT: assert(false); // Should not get here. + case U_SPACE_TYPE_ATTACHABLE: break; // No-op } } @@ -503,9 +513,17 @@ add_device_helper(struct u_space_overseer *uso, struct xrt_device *xdev) if (ptr != NULL) { xs = (struct xrt_space *)ptr; + } else if (torig->type == XRT_TRACKING_TYPE_ATTACHABLE) { + /* + * If we ever make u_space_overseer sub-classable make sure + * this calls the right function, can't call interface function + * as the lock is held here. + */ + xs = (struct xrt_space *)create_space(U_SPACE_TYPE_ATTACHABLE, u_space(root)); + u_hashmap_int_insert(uso->xto_map, key, xs); } else { /* - * If we ever make u_space_overseer sub-classable maek sure + * If we ever make u_space_overseer sub-classable make sure * this calls the right function, can't call interface function * as the lock is held here. */ @@ -1093,6 +1111,52 @@ add_device(struct xrt_space_overseer *xso, struct xrt_device *xdev) return add_device_helper(uso, xdev); } +static xrt_result_t +attach_device(struct xrt_space_overseer *xso, struct xrt_device *xdev, struct xrt_space *space) +{ + struct u_space_overseer *uso = u_space_overseer(xso); + + // Check that the device has the correct tracking origin type. + if (xdev->tracking_origin == NULL || xdev->tracking_origin->type != XRT_TRACKING_TYPE_ATTACHABLE) { + U_LOG_E("Device '%s' does not have XRT_TRACKING_TYPE_ATTACHABLE tracking origin type", xdev->str); + return XRT_ERROR_DEVICE_NOT_ATTACHABLE; + } + + // If no space is provided, use the root space. + struct xrt_space *target_space = space; + if (target_space == NULL) { + target_space = uso->base.semantic.root; + } + + xrt_result_t xret = XRT_SUCCESS; + pthread_rwlock_wrlock(&uso->lock); + + + void *ptr = NULL; + uint64_t key = (uint64_t)(intptr_t)xdev->tracking_origin; + u_hashmap_int_find(uso->xto_map, key, &ptr); + if (ptr == NULL) { + U_LOG_E("Device doesn't have space associated with it!"); + xret = XRT_ERROR_DEVICE_NOT_ATTACHABLE; + goto err_unlock; + } + + struct u_space *us = (struct u_space *)ptr; + if (us->type != U_SPACE_TYPE_ATTACHABLE) { + U_LOG_E("Device doesn't have a attachable space!"); + xret = XRT_ERROR_DEVICE_NOT_ATTACHABLE; + goto err_unlock; + } + + // Update the link. + u_space_reference(&us->next, u_space(target_space)); + +err_unlock: + pthread_rwlock_unlock(&uso->lock); + + return xret; +} + static void destroy(struct xrt_space_overseer *xso) { @@ -1150,6 +1214,7 @@ u_space_overseer_create(struct xrt_session_event_sink *broadcast) uso->base.get_reference_space_offset = get_reference_space_offset; uso->base.set_reference_space_offset = set_reference_space_offset; uso->base.add_device = add_device; + uso->base.attach_device = attach_device; uso->base.destroy = destroy; uso->broadcast = broadcast; diff --git a/src/xrt/include/xrt/xrt_results.h b/src/xrt/include/xrt/xrt_results.h index c39085733..140f4ac25 100644 --- a/src/xrt/include/xrt/xrt_results.h +++ b/src/xrt/include/xrt/xrt_results.h @@ -251,4 +251,10 @@ typedef enum xrt_result * Invoking complete on an already completed future */ XRT_ERROR_FUTURE_ALREADY_COMPLETE = -41, + + /*! + * The device's tracking origin is not of type + * XRT_TRACKING_TYPE_ATTACHABLE. + */ + XRT_ERROR_DEVICE_NOT_ATTACHABLE = -42, } xrt_result_t; diff --git a/src/xrt/include/xrt/xrt_space.h b/src/xrt/include/xrt/xrt_space.h index 45cc85cff..d3d43d547 100644 --- a/src/xrt/include/xrt/xrt_space.h +++ b/src/xrt/include/xrt/xrt_space.h @@ -330,6 +330,26 @@ struct xrt_space_overseer */ xrt_result_t (*add_device)(struct xrt_space_overseer *xso, struct xrt_device *xdev); + /*! + * Attach a device to a different space then it was associated with + * originally, the space overseer might not support this operation. + * + * For some space overseer implementations this operation requires + * that the device has the tracking origin type of + * @ref XRT_TRACKING_TYPE_ATTACHABLE. Which space that becomes the + * parent space of the device when @p space is NULL is undefined, + * and the device might become un-trackable. + * + * @param[in] xso Owning space overseer. + * @param[in] xdev Device to attach. + * @param[in] space Space to attach the device to, may be NULL. + * + * @return XRT_SUCCESS on success. + * @return XRT_ERROR_DEVICE_NOT_ATTACHABLE if the device does not have + * the XRT_TRACKING_TYPE_ATTACHABLE tracking origin type. + */ + xrt_result_t (*attach_device)(struct xrt_space_overseer *xso, struct xrt_device *xdev, struct xrt_space *space); + /* * @@ -562,6 +582,19 @@ xrt_space_overseer_add_device(struct xrt_space_overseer *xso, struct xrt_device return xso->add_device(xso, xdev); } +/*! + * @copydoc xrt_space_overseer::attach_device + * + * Helper for calling through the function pointer. + * + * @public @memberof xrt_space_overseer + */ +static inline xrt_result_t +xrt_space_overseer_attach_device(struct xrt_space_overseer *xso, struct xrt_device *xdev, struct xrt_space *space) +{ + return xso->attach_device(xso, xdev, space); +} + /*! * Helper for calling through the function pointer: does a null check and sets * xc_ptr to null if freed. diff --git a/src/xrt/include/xrt/xrt_tracking.h b/src/xrt/include/xrt/xrt_tracking.h index b6346e61c..747b32276 100644 --- a/src/xrt/include/xrt/xrt_tracking.h +++ b/src/xrt/include/xrt/xrt_tracking.h @@ -1,4 +1,5 @@ // Copyright 2019, Collabora, Ltd. +// Copyright 2025, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -60,6 +61,9 @@ enum xrt_tracking_type // The device(s) are tracked by other methods. XRT_TRACKING_TYPE_OTHER, + + // The device(s) are (re)attachable. + XRT_TRACKING_TYPE_ATTACHABLE, }; /*! diff --git a/src/xrt/ipc/client/ipc_client_space_overseer.c b/src/xrt/ipc/client/ipc_client_space_overseer.c index 665687430..553de0719 100644 --- a/src/xrt/ipc/client/ipc_client_space_overseer.c +++ b/src/xrt/ipc/client/ipc_client_space_overseer.c @@ -324,6 +324,14 @@ add_device(struct xrt_space_overseer *xso, struct xrt_device *xdev) return XRT_ERROR_NOT_IMPLEMENTED; } +static xrt_result_t +attach_device(struct xrt_space_overseer *xso, struct xrt_device *xdev, struct xrt_space *space) +{ + // For IPC client, attachable devices are handled on the server side. + // This should not be called from the client in the typical use case. + return XRT_ERROR_NOT_IMPLEMENTED; +} + static void destroy(struct xrt_space_overseer *xso) { @@ -381,6 +389,7 @@ ipc_client_space_overseer_create(struct ipc_connection *ipc_c) icspo->base.get_reference_space_offset = get_reference_space_offset; icspo->base.set_reference_space_offset = set_reference_space_offset; icspo->base.add_device = add_device; + icspo->base.attach_device = attach_device; icspo->base.destroy = destroy; icspo->ipc_c = ipc_c;