The open source OpenXR runtime
at main 422 lines 13 kB view raw
1// Copyright 2023, Collabora, Ltd. 2// Copyright 2025, NVIDIA CORPORATION. 3// SPDX-License-Identifier: BSL-1.0 4/*! 5 * @file 6 * @brief IPC Client space overseer. 7 * @author Jakob Bornecrantz <jakob@collabora.com> 8 * @ingroup ipc_client 9 */ 10 11#include "xrt/xrt_defines.h" 12#include "xrt/xrt_space.h" 13 14#include "shared/ipc_message_channel.h" 15 16#include "client/ipc_client.h" 17#include "client/ipc_client_xdev.h" 18#include "client/ipc_client_connection.h" 19 20#include "ipc_client_generated.h" 21 22 23struct ipc_client_space 24{ 25 struct xrt_space base; 26 27 struct ipc_connection *ipc_c; 28 29 uint32_t id; 30}; 31 32struct ipc_client_space_overseer 33{ 34 struct xrt_space_overseer base; 35 36 struct ipc_connection *ipc_c; 37 38 struct xrt_reference ref_space_use[XRT_SPACE_REFERENCE_TYPE_COUNT]; 39}; 40 41 42/* 43 * 44 * Helpers 45 * 46 */ 47 48static inline struct ipc_client_space * 49ipc_client_space(struct xrt_space *xs) 50{ 51 return (struct ipc_client_space *)xs; 52} 53 54static inline struct ipc_client_space_overseer * 55ipc_client_space_overseer(struct xrt_space_overseer *xso) 56{ 57 return (struct ipc_client_space_overseer *)xso; 58} 59 60 61/* 62 * 63 * Space member functions. 64 * 65 */ 66 67static void 68space_destroy(struct xrt_space *xs) 69{ 70 struct ipc_client_space *icsp = ipc_client_space(xs); 71 72 ipc_call_space_destroy(icsp->ipc_c, icsp->id); 73 74 free(xs); 75} 76 77static void 78alloc_space_with_id(struct ipc_client_space_overseer *icspo, uint32_t id, struct xrt_space **out_space) 79{ 80 struct ipc_client_space *icsp = U_TYPED_CALLOC(struct ipc_client_space); 81 icsp->base.reference.count = 1; 82 icsp->base.destroy = space_destroy; 83 icsp->ipc_c = icspo->ipc_c; 84 icsp->id = id; 85 86 *out_space = &icsp->base; 87} 88 89 90/* 91 * 92 * Overseer member functions. 93 * 94 */ 95 96static xrt_result_t 97create_offset_space(struct xrt_space_overseer *xso, 98 struct xrt_space *parent, 99 const struct xrt_pose *offset, 100 struct xrt_space **out_space) 101{ 102 struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); 103 xrt_result_t xret; 104 uint32_t parent_id = ipc_client_space(parent)->id; 105 uint32_t id = 0; 106 107 xret = ipc_call_space_create_offset(icspo->ipc_c, parent_id, offset, &id); 108 IPC_CHK_AND_RET(icspo->ipc_c, xret, "ipc_call_space_create_offset"); 109 110 alloc_space_with_id(icspo, id, out_space); 111 112 return XRT_SUCCESS; 113} 114 115static xrt_result_t 116create_pose_space(struct xrt_space_overseer *xso, 117 struct xrt_device *xdev, 118 enum xrt_input_name name, 119 struct xrt_space **out_space) 120{ 121 struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); 122 xrt_result_t xret; 123 uint32_t xdev_id = ipc_client_xdev(xdev)->device_id; 124 uint32_t id = 0; 125 126 xret = ipc_call_space_create_pose(icspo->ipc_c, xdev_id, name, &id); 127 IPC_CHK_AND_RET(icspo->ipc_c, xret, "ipc_call_space_create_pose"); 128 129 alloc_space_with_id(icspo, id, out_space); 130 131 return XRT_SUCCESS; 132} 133 134static xrt_result_t 135locate_space(struct xrt_space_overseer *xso, 136 struct xrt_space *base_space, 137 const struct xrt_pose *base_offset, 138 int64_t at_timestamp_ns, 139 struct xrt_space *space, 140 const struct xrt_pose *offset, 141 struct xrt_space_relation *out_relation) 142{ 143 struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); 144 xrt_result_t xret; 145 146 struct ipc_client_space *icsp_base_space = ipc_client_space(base_space); 147 struct ipc_client_space *icsp_space = ipc_client_space(space); 148 149 xret = ipc_call_space_locate_space( // 150 icspo->ipc_c, // 151 icsp_base_space->id, // 152 base_offset, // 153 at_timestamp_ns, // 154 icsp_space->id, // 155 offset, // 156 out_relation); // 157 IPC_CHK_ALWAYS_RET(icspo->ipc_c, xret, "ipc_call_space_locate_space"); 158} 159 160static xrt_result_t 161locate_spaces(struct xrt_space_overseer *xso, 162 struct xrt_space *base_space, 163 const struct xrt_pose *base_offset, 164 int64_t at_timestamp_ns, 165 struct xrt_space **spaces, 166 uint32_t space_count, 167 const struct xrt_pose *offsets, 168 struct xrt_space_relation *out_relations) 169{ 170 struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); 171 struct ipc_connection *ipc_c = icspo->ipc_c; 172 173 xrt_result_t xret; 174 175 struct ipc_client_space *icsp_base_space = ipc_client_space(base_space); 176 177 uint32_t *space_ids = U_TYPED_ARRAY_CALLOC(uint32_t, space_count); 178 if (space_ids == NULL) { 179 IPC_ERROR(ipc_c, "Failed to allocate space_ids"); 180 return XRT_ERROR_ALLOCATION; 181 } 182 183 ipc_client_connection_lock(ipc_c); 184 185 xret = 186 ipc_send_space_locate_spaces_locked(ipc_c, icsp_base_space->id, base_offset, space_count, at_timestamp_ns); 187 IPC_CHK_WITH_GOTO(ipc_c, xret, "ipc_send_space_locate_spaces_locked", locate_spaces_out); 188 189 enum xrt_result received_result = XRT_SUCCESS; 190 xret = ipc_receive(&ipc_c->imc, &received_result, sizeof(enum xrt_result)); 191 IPC_CHK_WITH_GOTO(ipc_c, xret, "ipc_receive: Receive spaces allocation result", locate_spaces_out); 192 193 // now check if the service sent a success code or an error code about allocating memory for spaces. 194 xret = received_result; 195 IPC_CHK_WITH_GOTO(ipc_c, xret, "ipc_receive: service side spaces allocation failed", locate_spaces_out); 196 197 for (uint32_t i = 0; i < space_count; i++) { 198 if (spaces[i] == NULL) { 199 space_ids[i] = UINT32_MAX; 200 } else { 201 struct ipc_client_space *icsp_space = ipc_client_space(spaces[i]); 202 space_ids[i] = icsp_space->id; 203 } 204 } 205 206 xret = ipc_send(&ipc_c->imc, space_ids, sizeof(uint32_t) * space_count); 207 IPC_CHK_WITH_GOTO(ipc_c, xret, "ipc_send: Send spaces ids", locate_spaces_out); 208 209 xret = ipc_send(&ipc_c->imc, offsets, sizeof(struct xrt_pose) * space_count); 210 IPC_CHK_WITH_GOTO(ipc_c, xret, "ipc_send: Send spaces offsets", locate_spaces_out); 211 212 xret = ipc_receive(&ipc_c->imc, out_relations, sizeof(struct xrt_space_relation) * space_count); 213 IPC_CHK_WITH_GOTO(ipc_c, xret, "ipc_receive: Receive spaces relations", locate_spaces_out); 214 215locate_spaces_out: 216 free(space_ids); 217 ipc_client_connection_unlock(ipc_c); 218 return xret; 219} 220 221static xrt_result_t 222locate_device(struct xrt_space_overseer *xso, 223 struct xrt_space *base_space, 224 const struct xrt_pose *base_offset, 225 int64_t at_timestamp_ns, 226 struct xrt_device *xdev, 227 struct xrt_space_relation *out_relation) 228{ 229 struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); 230 xrt_result_t xret; 231 232 struct ipc_client_space *icsp_base_space = ipc_client_space(base_space); 233 uint32_t xdev_id = ipc_client_xdev(xdev)->device_id; 234 235 xret = ipc_call_space_locate_device( // 236 icspo->ipc_c, // 237 icsp_base_space->id, // 238 base_offset, // 239 at_timestamp_ns, // 240 xdev_id, // 241 out_relation); // 242 IPC_CHK_ALWAYS_RET(icspo->ipc_c, xret, "ipc_call_space_locate_device"); 243} 244 245static xrt_result_t 246ref_space_inc(struct xrt_space_overseer *xso, enum xrt_reference_space_type type) 247{ 248 struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); 249 xrt_result_t xret; 250 251 // No more checking then this. 252 assert(type < XRT_SPACE_REFERENCE_TYPE_COUNT); 253 254 // If it wasn't zero nothing to do. 255 if (!xrt_reference_inc_and_was_zero(&icspo->ref_space_use[type])) { 256 return XRT_SUCCESS; 257 } 258 259 xret = ipc_call_space_mark_ref_space_in_use(icspo->ipc_c, type); 260 IPC_CHK_ALWAYS_RET(icspo->ipc_c, xret, "ipc_call_space_mark_ref_space_in_use"); 261} 262 263static xrt_result_t 264ref_space_dec(struct xrt_space_overseer *xso, enum xrt_reference_space_type type) 265{ 266 struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); 267 xrt_result_t xret; 268 269 // No more checking then this. 270 assert(type < XRT_SPACE_REFERENCE_TYPE_COUNT); 271 272 // If it is not zero we are done. 273 if (!xrt_reference_dec_and_is_zero(&icspo->ref_space_use[type])) { 274 return XRT_SUCCESS; 275 } 276 277 xret = ipc_call_space_unmark_ref_space_in_use(icspo->ipc_c, type); 278 IPC_CHK_ALWAYS_RET(icspo->ipc_c, xret, "ipc_call_space_unmark_ref_space_in_use"); 279} 280 281static xrt_result_t 282recenter_local_spaces(struct xrt_space_overseer *xso) 283{ 284 struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); 285 286 return ipc_call_space_recenter_local_spaces(icspo->ipc_c); 287} 288 289static xrt_result_t 290get_tracking_origin_offset(struct xrt_space_overseer *xso, struct xrt_tracking_origin *xto, struct xrt_pose *out_offset) 291{ 292 return XRT_ERROR_NOT_IMPLEMENTED; 293} 294 295static xrt_result_t 296set_tracking_origin_offset(struct xrt_space_overseer *xso, 297 struct xrt_tracking_origin *xto, 298 const struct xrt_pose *offset) 299{ 300 return XRT_ERROR_NOT_IMPLEMENTED; 301} 302 303static xrt_result_t 304get_reference_space_offset(struct xrt_space_overseer *xso, 305 enum xrt_reference_space_type type, 306 struct xrt_pose *out_offset) 307{ 308 struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); 309 return ipc_call_space_get_reference_space_offset(icspo->ipc_c, type, out_offset); 310} 311 312static xrt_result_t 313set_reference_space_offset(struct xrt_space_overseer *xso, 314 enum xrt_reference_space_type type, 315 const struct xrt_pose *offset) 316{ 317 struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); 318 return ipc_call_space_set_reference_space_offset(icspo->ipc_c, type, offset); 319} 320 321static xrt_result_t 322add_device(struct xrt_space_overseer *xso, struct xrt_device *xdev) 323{ 324 return XRT_ERROR_NOT_IMPLEMENTED; 325} 326 327static xrt_result_t 328attach_device(struct xrt_space_overseer *xso, struct xrt_device *xdev, struct xrt_space *space) 329{ 330 // For IPC client, attachable devices are handled on the server side. 331 // This should not be called from the client in the typical use case. 332 return XRT_ERROR_NOT_IMPLEMENTED; 333} 334 335static void 336destroy(struct xrt_space_overseer *xso) 337{ 338 struct ipc_client_space_overseer *icspo = ipc_client_space_overseer(xso); 339 340 xrt_space_reference(&icspo->base.semantic.root, NULL); 341 xrt_space_reference(&icspo->base.semantic.view, NULL); 342 xrt_space_reference(&icspo->base.semantic.local, NULL); 343 xrt_space_reference(&icspo->base.semantic.local_floor, NULL); 344 xrt_space_reference(&icspo->base.semantic.stage, NULL); 345 xrt_space_reference(&icspo->base.semantic.unbounded, NULL); 346 347 free(icspo); 348} 349 350 351/* 352 * 353 * 'Exported' functions. 354 * 355 */ 356 357struct xrt_space_overseer * 358ipc_client_space_overseer_create(struct ipc_connection *ipc_c) 359{ 360 uint32_t root_id = UINT32_MAX; 361 uint32_t view_id = UINT32_MAX; 362 uint32_t local_id = UINT32_MAX; 363 uint32_t local_floor_id = UINT32_MAX; 364 uint32_t stage_id = UINT32_MAX; 365 uint32_t unbounded_id = UINT32_MAX; 366 xrt_result_t xret; 367 368 xret = ipc_call_space_create_semantic_ids( // 369 ipc_c, // 370 &root_id, // 371 &view_id, // 372 &local_id, // 373 &local_floor_id, // 374 &stage_id, // 375 &unbounded_id); // 376 IPC_CHK_WITH_RET(ipc_c, xret, "ipc_call_space_create_semantic_ids", NULL); 377 378 struct ipc_client_space_overseer *icspo = U_TYPED_CALLOC(struct ipc_client_space_overseer); 379 icspo->base.create_offset_space = create_offset_space; 380 icspo->base.create_pose_space = create_pose_space; 381 icspo->base.locate_space = locate_space; 382 icspo->base.locate_spaces = locate_spaces; 383 icspo->base.locate_device = locate_device; 384 icspo->base.ref_space_inc = ref_space_inc; 385 icspo->base.ref_space_dec = ref_space_dec; 386 icspo->base.recenter_local_spaces = recenter_local_spaces; 387 icspo->base.get_tracking_origin_offset = get_tracking_origin_offset; 388 icspo->base.set_tracking_origin_offset = set_tracking_origin_offset; 389 icspo->base.get_reference_space_offset = get_reference_space_offset; 390 icspo->base.set_reference_space_offset = set_reference_space_offset; 391 icspo->base.add_device = add_device; 392 icspo->base.attach_device = attach_device; 393 icspo->base.destroy = destroy; 394 icspo->ipc_c = ipc_c; 395 396#define CREATE(NAME) \ 397 do { \ 398 if (NAME##_id == UINT32_MAX) { \ 399 break; \ 400 } \ 401 alloc_space_with_id(icspo, NAME##_id, &icspo->base.semantic.NAME); \ 402 } while (false) 403 404 CREATE(root); 405 CREATE(view); 406 CREATE(local); 407 CREATE(local_floor); 408 CREATE(stage); 409 CREATE(unbounded); 410 411#undef CREATE 412 413 return &icspo->base; 414} 415 416uint32_t 417ipc_client_space_get_id(struct xrt_space *space) 418{ 419 assert(space != NULL); 420 421 return ipc_client_space(space)->id; 422}