The open source OpenXR runtime
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}