From 468a06d73ed9e00a7195d99c32424522e96dd5a7 Mon Sep 17 00:00:00 2001 From: Jakob Bornecrantz Date: Fri, 5 Jun 2026 23:14:05 +0100 Subject: [PATCH] xrt: Add unique ID field to xrt_device Assign each device a unique 64-bit identifier for hash maps and caches. Synchronize the ID to IPC clients via device_get_info while keeping the existing uint32_t slot index for IPC lookups. Key the space overseer device map by the stable ID instead of the device pointer. Signed-off-by: Jakob Bornecrantz Part-of: --- src/xrt/auxiliary/glue/g_device.hpp | 3 ++ src/xrt/auxiliary/util/CMakeLists.txt | 2 ++ src/xrt/auxiliary/util/u_device.c | 8 +++++ src/xrt/auxiliary/util/u_device_id.c | 34 +++++++++++++++++++++ src/xrt/auxiliary/util/u_device_id.h | 39 +++++++++++++++++++++++++ src/xrt/drivers/multi_wrapper/multi.c | 5 ++++ src/xrt/drivers/steamvr_lh/device.cpp | 4 +++ src/xrt/include/xrt/xrt_device.h | 25 ++++++++++++++++ src/xrt/ipc/client/ipc_client_xdev.c | 3 ++ src/xrt/ipc/client/ipc_client_xdev.h | 6 ++++ src/xrt/ipc/server/ipc_server_handler.c | 3 ++ src/xrt/ipc/server/ipc_server_objects.h | 23 +++++++++++---- src/xrt/ipc/server/ipc_server_process.c | 2 +- src/xrt/ipc/shared/ipc_protocol.h | 10 ++++++- src/xrt/targets/sdl_test/sdl_device.c | 4 +++ tests/tests_cxx_wrappers.cpp | 4 +++ 16 files changed, 167 insertions(+), 8 deletions(-) create mode 100644 src/xrt/auxiliary/util/u_device_id.c create mode 100644 src/xrt/auxiliary/util/u_device_id.h diff --git a/src/xrt/auxiliary/glue/g_device.hpp b/src/xrt/auxiliary/glue/g_device.hpp index 7aa72297e..57d59acad 100644 --- a/src/xrt/auxiliary/glue/g_device.hpp +++ b/src/xrt/auxiliary/glue/g_device.hpp @@ -12,6 +12,7 @@ #include "xrt/xrt_device.h" #include "util/u_device.h" +#include "util/u_device_id.h" #include "g_catch_guard.hpp" #include "g_traits.hpp" @@ -123,6 +124,8 @@ public: // Members // Setup function for the device. auto &xdev = *getXDev(); + u_device_id_assign(&xdev); + // Inits all functions, some are replaced below. u_device_populate_function_pointers(&xdev, getTrackedPoseWrap, destroyDeviceWrap); diff --git a/src/xrt/auxiliary/util/CMakeLists.txt b/src/xrt/auxiliary/util/CMakeLists.txt index 1960ad4da..0ec4e95ee 100644 --- a/src/xrt/auxiliary/util/CMakeLists.txt +++ b/src/xrt/auxiliary/util/CMakeLists.txt @@ -33,6 +33,8 @@ add_library( u_deque.h u_device.c u_device.h + u_device_id.c + u_device_id.h u_device_ni.c u_device_ni.h u_distortion.c diff --git a/src/xrt/auxiliary/util/u_device.c b/src/xrt/auxiliary/util/u_device.c index 9da6c51d7..0ca6ad2e5 100644 --- a/src/xrt/auxiliary/util/u_device.c +++ b/src/xrt/auxiliary/util/u_device.c @@ -1,4 +1,5 @@ // Copyright 2019-2025, Collabora, Ltd. +// Copyright 2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -12,6 +13,7 @@ #include "util/u_device.h" #include "util/u_debug.h" +#include "util/u_device_id.h" #include "util/u_device_ni.h" #include "util/u_logging.h" #include "util/u_misc.h" @@ -311,6 +313,8 @@ u_device_allocate(enum u_device_alloc_flags flags, size_t size, size_t input_cou char *ptr = U_TYPED_ARRAY_CALLOC(char, total_size); struct xrt_device *xdev = (struct xrt_device *)ptr; + u_device_id_assign(xdev); + if (input_count > 0) { xdev->input_count = input_count; xdev->inputs = (struct xrt_input *)(ptr + offset_inputs); @@ -596,6 +600,10 @@ u_device_populate_function_pointers(struct xrt_device *xdev, u_device_get_tracked_pose_function_t get_tracked_pose_fn, u_device_destroy_function_t destroy_fn) { + if (xdev->id.val == 0) { + u_device_id_assign(xdev); + } + if (get_tracked_pose_fn == NULL) { U_LOG_E("Got get_tracked_pose_fn == NULL!"); assert(get_tracked_pose_fn != NULL); diff --git a/src/xrt/auxiliary/util/u_device_id.c b/src/xrt/auxiliary/util/u_device_id.c new file mode 100644 index 000000000..c5eaee5a9 --- /dev/null +++ b/src/xrt/auxiliary/util/u_device_id.c @@ -0,0 +1,34 @@ +// Copyright 2026, NVIDIA CORPORATION. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Generator for per-process unique @ref xrt_device_id values. + * @author Jakob Bornecrantz + * @ingroup aux_util + */ + +#include "util/u_device_id.h" +#include "util/u_limited_unique_id.h" + + +struct xrt_device_id +u_device_id_generate(void) +{ + /* + * The xrt_limited_unique_id only guartantees that it is unique within + * one process, that's its limitation. Device IDs are only generated + * in one process, either the service for out-of-processs or the app + * for in-process mode. That's the rule for xrt_device_id which makes + * this is safe to use here. And also makes device IDs unique with any + * other uses of limited unique id, or using it to generate an ID. + */ + return XRT_C11_COMPOUND(struct xrt_device_id){ + .val = u_limited_unique_id_get().data, + }; +} + +void +u_device_id_assign(struct xrt_device *xdev) +{ + xdev->id = u_device_id_generate(); +} diff --git a/src/xrt/auxiliary/util/u_device_id.h b/src/xrt/auxiliary/util/u_device_id.h new file mode 100644 index 000000000..cccba6b7a --- /dev/null +++ b/src/xrt/auxiliary/util/u_device_id.h @@ -0,0 +1,39 @@ +// Copyright 2026, NVIDIA CORPORATION. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Generator for per-process unique @ref xrt_device_id values. + * @author Jakob Bornecrantz + * @ingroup aux_util + */ + +#pragma once + +#include "xrt/xrt_device.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*! + * Generate a new per-process unique device ID. + * + * @ingroup aux_util + */ +struct xrt_device_id +u_device_id_generate(void); + +/*! + * Assign a newly generated ID to the given device. + * + * @ingroup aux_util + */ +void +u_device_id_assign(struct xrt_device *xdev); + + +#ifdef __cplusplus +} +#endif diff --git a/src/xrt/drivers/multi_wrapper/multi.c b/src/xrt/drivers/multi_wrapper/multi.c index 2ec50ab97..e0e7ecac5 100644 --- a/src/xrt/drivers/multi_wrapper/multi.c +++ b/src/xrt/drivers/multi_wrapper/multi.c @@ -1,4 +1,5 @@ // Copyright 2021, Collabora, Ltd. +// Copyright 2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -13,6 +14,7 @@ #include "util/u_misc.h" #include "util/u_debug.h" #include "util/u_device.h" +#include "util/u_device_id.h" #include "util/u_var.h" #include "multi.h" @@ -272,6 +274,9 @@ multi_create_tracking_override(enum xrt_tracking_override_type override_type, // mimic the tracking override target d->base = *tracking_override_target; + // The wrapper is a new device and needs its own per-process unique ID. + u_device_id_assign(&d->base); + // but take orientation and position tracking capabilities from tracker d->base.supported.orientation_tracking = tracking_override_tracker->supported.orientation_tracking; d->base.supported.position_tracking = tracking_override_tracker->supported.position_tracking; diff --git a/src/xrt/drivers/steamvr_lh/device.cpp b/src/xrt/drivers/steamvr_lh/device.cpp index 5dac28a72..6781a7c79 100644 --- a/src/xrt/drivers/steamvr_lh/device.cpp +++ b/src/xrt/drivers/steamvr_lh/device.cpp @@ -1,4 +1,5 @@ // Copyright 2023, Shawn Wallace +// Copyright 2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -17,6 +18,7 @@ #include "util/u_debug.h" #include "util/u_device.h" +#include "util/u_device_id.h" #include "util/u_hand_tracking.h" #include "util/u_logging.h" #include "util/u_json.hpp" @@ -304,6 +306,8 @@ Device::~Device() Device::Device(const DeviceBuilder &builder) : xrt_device({}), ctx(builder.ctx), driver(builder.driver) { + u_device_id_assign(this); + m_relation_history_create(&relation_hist); std::strncpy(this->serial, builder.serial, XRT_DEVICE_NAME_LEN - 1); this->serial[XRT_DEVICE_NAME_LEN - 1] = 0; diff --git a/src/xrt/include/xrt/xrt_device.h b/src/xrt/include/xrt/xrt_device.h index 95fa206b2..562505467 100644 --- a/src/xrt/include/xrt/xrt_device.h +++ b/src/xrt/include/xrt/xrt_device.h @@ -26,6 +26,28 @@ struct xrt_tracking; #define XRT_DEVICE_NAME_LEN 256 +/*! + * A unique identifier for an @ref xrt_device instance. + * + * Each device is assigned a 64-bit ID when it is created in the Monado service + * process, or in the application process for in-process setups. For + * out-of-process clients the ID is synchronized over the IPC layer, so every + * client connected to the same service refers to a given device by the same + * value. + * + * The ID is intended for hash maps, caches, and similar lookups. Unlike a + * memory pointer it is never reused after the device is destroyed. + * + * A value of zero is invalid and means the device ID has not been assigned, + * matching the zero-initialized state of the struct. + * + * @ingroup xrt_iface + */ +struct xrt_device_id +{ + uint64_t val; +}; + /*! * A per-lens/display view information. * @@ -316,6 +338,9 @@ struct xrt_device_supported */ struct xrt_device { + //! Instance identifier, see @ref xrt_device_id. + struct xrt_device_id id; + //! Enum identifier of the device. enum xrt_device_name name; enum xrt_device_type device_type; diff --git a/src/xrt/ipc/client/ipc_client_xdev.c b/src/xrt/ipc/client/ipc_client_xdev.c index 16206b85f..642d01b4a 100644 --- a/src/xrt/ipc/client/ipc_client_xdev.c +++ b/src/xrt/ipc/client/ipc_client_xdev.c @@ -471,6 +471,8 @@ ipc_client_xdev_init(struct ipc_client_xdev *icx, // Important fields. icx->ipc_c = ipc_c; + + // Slot index for IPC server lookups; not the same as xrt_device::id. icx->device_id = device_id; /* @@ -513,6 +515,7 @@ ipc_client_xdev_init(struct ipc_client_xdev *icx, IPC_CHK_WITH_GOTO(ipc_c, xret, "ipc_receive_device_get_info_locked", out_free_and_unlock); // Copying the information from the info. + icx->base.id.val = info.xrt_device_id_val; icx->base.device_type = info.device_type; icx->base.supported = info.supported; icx->base.name = info.name; diff --git a/src/xrt/ipc/client/ipc_client_xdev.h b/src/xrt/ipc/client/ipc_client_xdev.h index 2062d7f81..4a1946937 100644 --- a/src/xrt/ipc/client/ipc_client_xdev.h +++ b/src/xrt/ipc/client/ipc_client_xdev.h @@ -36,6 +36,12 @@ struct ipc_client_xdev struct ipc_connection *ipc_c; + /*! + * Per-client-connection slot index for looking up this device on the IPC + * server. Passed in IPC message calls such as @c device_get_tracked_pose. + * This is not the same as @ref xrt_device::id, which is synchronized from + * the service via @ref ipc_device_info. + */ uint32_t device_id; struct xrt_binding_input_pair *all_input_pairs; diff --git a/src/xrt/ipc/server/ipc_server_handler.c b/src/xrt/ipc/server/ipc_server_handler.c index e0e9dca2f..7231ab489 100644 --- a/src/xrt/ipc/server/ipc_server_handler.c +++ b/src/xrt/ipc/server/ipc_server_handler.c @@ -1774,6 +1774,9 @@ ipc_handle_tracking_origin_get_info(volatile struct ipc_client_state *ics, static void fill_device_info(struct xrt_device *xdev, uint32_t tracking_origin_id, struct ipc_device_info *out_info) { + // Synchronize the stable device ID to IPC clients. + out_info->xrt_device_id_val = xdev->id.val; + // Fill in basic device info out_info->name = xdev->name; out_info->device_type = xdev->device_type; diff --git a/src/xrt/ipc/server/ipc_server_objects.h b/src/xrt/ipc/server/ipc_server_objects.h index afc96f255..54b16b4e2 100644 --- a/src/xrt/ipc/server/ipc_server_objects.h +++ b/src/xrt/ipc/server/ipc_server_objects.h @@ -23,11 +23,16 @@ struct ipc_client_state; */ /*! - * Get a device by ID, must only be called from the per client - * thread as this function accesses the client state's memory. + * Get a device by its per-client-connection slot index. + * + * The @p id is a @c uint32_t index into @c ics->objects.xdevs used for IPC + * message lookups. It is not @ref xrt_device::id. + * + * Must only be called from the per client thread as this function accesses the + * client state's memory. * * @param ics The client state instance. - * @param id The device ID. + * @param id The per-client slot index. * @param out_xdev Will be filled with the device object on success. * @return XRT_SUCCESS on success, some other result on failure. * @@ -39,12 +44,18 @@ ipc_server_objects_get_xdev_and_validate(volatile struct ipc_client_state *ics, struct xrt_device **out_xdev); /*! - * Get a device ID for a given device object, must only be called from the per - * client thread as this function accesses the client state's memory. + * Get or allocate a per-client-connection slot index for a device object. + * + * The returned @c uint32_t is used in IPC messages to look up @p xdev on the + * server. It is not @ref xrt_device::id, which is assigned at device creation + * and synchronized to clients via @ref ipc_device_info. + * + * Must only be called from the per client thread as this function accesses the + * client state's memory. * * @param ics The client state instance. * @param xdev The device object. - * @param out_id Will be filled with the device ID on success. + * @param out_id Will be filled with the slot index on success. * @return XRT_SUCCESS on success, some other result on failure. * * @ingroup ipc_server diff --git a/src/xrt/ipc/server/ipc_server_process.c b/src/xrt/ipc/server/ipc_server_process.c index caa82d05d..4f9a6e2a4 100644 --- a/src/xrt/ipc/server/ipc_server_process.c +++ b/src/xrt/ipc/server/ipc_server_process.c @@ -239,7 +239,7 @@ init_system_shm_state(struct ipc_server *s, volatile struct ipc_client_state *ic continue; } - // Populate the device. + // Assign a per-client slot index for IPC lookups (not xrt_device::id). uint32_t device_id = 0; xret = ipc_server_objects_get_xdev_id_or_add(ics, xdev, &device_id); if (xret != XRT_SUCCESS) { diff --git a/src/xrt/ipc/shared/ipc_protocol.h b/src/xrt/ipc/shared/ipc_protocol.h index d2adfcac8..fd06c5c01 100644 --- a/src/xrt/ipc/shared/ipc_protocol.h +++ b/src/xrt/ipc/shared/ipc_protocol.h @@ -153,8 +153,16 @@ struct ipc_device_info enum xrt_device_name name; enum xrt_device_type device_type; + /*! + * The service-side @ref xrt_device_id, synchronized to IPC clients so that + * @ref xrt_device::id matches across all clients connected to the same + * service. This is distinct from the per-client-connection @c uint32_t + * slot index used to look up devices in IPC messages. + */ + uint64_t xrt_device_id_val; + //! Which tracking system origin is this device attached to. - uint32_t tracking_origin_id; + XRT_ALIGNAS(8) uint32_t tracking_origin_id; //! A string describing the device. char str[XRT_DEVICE_NAME_LEN]; diff --git a/src/xrt/targets/sdl_test/sdl_device.c b/src/xrt/targets/sdl_test/sdl_device.c index b1d94fe4c..f54d21786 100644 --- a/src/xrt/targets/sdl_test/sdl_device.c +++ b/src/xrt/targets/sdl_test/sdl_device.c @@ -1,4 +1,5 @@ // Copyright 2020-2023, Collabora, Ltd. +// Copyright 2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -11,6 +12,7 @@ #include "math/m_mathinclude.h" #include "util/u_device.h" +#include "util/u_device_id.h" #include "util/u_distortion_mesh.h" #include @@ -72,6 +74,8 @@ sdl_device_init(struct sdl_program *sp) { struct xrt_device *xdev = &sp->xdev_base; + u_device_id_assign(xdev); + // Setup pointers. xdev->inputs = sp->inputs; xdev->input_count = ARRAY_SIZE(sp->inputs); diff --git a/tests/tests_cxx_wrappers.cpp b/tests/tests_cxx_wrappers.cpp index 7dd36b6af..20fa9a572 100644 --- a/tests/tests_cxx_wrappers.cpp +++ b/tests/tests_cxx_wrappers.cpp @@ -1,4 +1,5 @@ // Copyright 2021, Collabora, Ltd. +// Copyright 2026, NVIDIA CORPORATION. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -9,6 +10,8 @@ #include #include +#include "util/u_device_id.h" + #include "catch_amalgamated.hpp" @@ -20,6 +23,7 @@ struct silly_device silly_device(bool &destroyed_) : destroyed(&destroyed_) { + u_device_id_assign(&base); base.destroy = [](xrt_device *xdev) { delete reinterpret_cast(xdev); }; } ~silly_device() -- 2.51.2