The open source OpenXR runtime
1// Copyright 2020-2024, Collabora, Ltd.
2// Copyright 2025, NVIDIA CORPORATION.
3// SPDX-License-Identifier: BSL-1.0
4/*!
5 * @file
6 * @brief Client side wrapper of instance.
7 * @author Jakob Bornecrantz <jakob@collabora.com>
8 * @author Korcan Hussein <korcan.hussein@collabora.com>
9 * @ingroup ipc_client
10 */
11
12#include "xrt/xrt_results.h"
13#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
14#define _CRT_SECURE_NO_WARNINGS
15#endif
16
17#include "xrt/xrt_instance.h"
18#include "xrt/xrt_handles.h"
19#include "xrt/xrt_config_os.h"
20#include "xrt/xrt_config_android.h"
21
22#include "util/u_var.h"
23#include "util/u_misc.h"
24#include "util/u_file.h"
25#include "util/u_debug.h"
26#include "util/u_git_tag.h"
27#include "util/u_system_helpers.h"
28
29#include "shared/ipc_protocol.h"
30#include "shared/ipc_shmem.h"
31#include "client/ipc_client.h"
32#include "client/ipc_client_interface.h"
33#include "client/ipc_client_connection.h"
34
35#include "ipc_client_generated.h"
36
37
38#include <stdio.h>
39#if defined(XRT_OS_WINDOWS)
40#include <timeapi.h>
41#else
42#include <sys/socket.h>
43#include <sys/un.h>
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <sys/mman.h>
47#include <errno.h>
48#include <fcntl.h>
49#include <unistd.h>
50#endif
51#include <limits.h>
52
53#ifdef XRT_GRAPHICS_BUFFER_HANDLE_IS_AHARDWAREBUFFER
54#include "android/android_ahardwarebuffer_allocator.h"
55#endif
56
57#ifdef XRT_OS_ANDROID
58#include "xrt/xrt_android.h"
59#include "android/ipc_client_android.h"
60#include "android/android_instance_base.h"
61#endif // XRT_OS_ANDROID
62
63DEBUG_GET_ONCE_LOG_OPTION(ipc_log, "IPC_LOG", U_LOGGING_WARN)
64
65
66/*
67 *
68 * Struct and helpers.
69 *
70 */
71
72/*!
73 * @implements xrt_instance
74 */
75struct ipc_client_instance
76{
77 //! @public Base
78 struct xrt_instance base;
79
80 struct ipc_connection ipc_c;
81
82#ifdef XRT_OS_ANDROID
83 struct android_instance_base android;
84#endif
85};
86
87static inline struct ipc_client_instance *
88ipc_client_instance(struct xrt_instance *xinst)
89{
90 return (struct ipc_client_instance *)xinst;
91}
92
93static xrt_result_t
94create_system_compositor(struct ipc_client_instance *ii,
95 struct xrt_device *xdev,
96 struct xrt_system_compositor **out_xsysc)
97{
98 struct xrt_system_compositor *xsysc = NULL;
99 struct xrt_image_native_allocator *xina = NULL;
100 xrt_result_t xret;
101
102#ifdef XRT_GRAPHICS_BUFFER_HANDLE_IS_AHARDWAREBUFFER
103 // On Android, we allocate images natively on the client side.
104 xina = android_ahardwarebuffer_allocator_create();
105#endif // XRT_GRAPHICS_BUFFER_HANDLE_IS_AHARDWAREBUFFER
106
107 xret = ipc_client_create_system_compositor(&ii->ipc_c, xina, xdev, &xsysc);
108 IPC_CHK_WITH_GOTO(&ii->ipc_c, xret, "ipc_client_create_system_compositor", err_xina);
109
110 // Paranoia.
111 if (xsysc == NULL) {
112 xret = XRT_ERROR_IPC_FAILURE;
113 IPC_ERROR(&ii->ipc_c, "Variable xsysc NULL!");
114 goto err_xina;
115 }
116
117 *out_xsysc = xsysc;
118
119 return XRT_SUCCESS;
120
121err_xina:
122 xrt_images_destroy(&xina);
123 return xret;
124}
125
126
127/*
128 *
129 * Member functions.
130 *
131 */
132
133static xrt_result_t
134ipc_client_instance_is_system_available(struct xrt_instance *xinst, bool *out_available)
135{
136 struct ipc_client_instance *ii = ipc_client_instance(xinst);
137 xrt_result_t xret = ipc_call_instance_is_system_available(&ii->ipc_c, out_available);
138 IPC_CHK_ALWAYS_RET(&ii->ipc_c, xret, "ipc_call_instance_is_system_available");
139}
140
141static xrt_result_t
142ipc_client_instance_create_system(struct xrt_instance *xinst,
143 struct xrt_system **out_xsys,
144 struct xrt_system_devices **out_xsysd,
145 struct xrt_space_overseer **out_xso,
146 struct xrt_system_compositor **out_xsysc)
147{
148 struct ipc_client_instance *ii = ipc_client_instance(xinst);
149 xrt_result_t xret = XRT_SUCCESS;
150
151 assert(out_xsys != NULL);
152 assert(*out_xsys == NULL);
153 assert(out_xsysd != NULL);
154 assert(*out_xsysd == NULL);
155 assert(out_xsysc == NULL || *out_xsysc == NULL);
156
157 struct xrt_system_compositor *xsysc = NULL;
158
159 // Allocate a helper xrt_system_devices struct.
160 struct ipc_client_system_devices *icsd = ipc_client_system_devices_create(&ii->ipc_c);
161 struct xrt_system_devices *xsysd = &icsd->base.base;
162
163 uint32_t count = 0;
164 struct xrt_tracking_origin *xtrack = NULL;
165 struct ipc_shared_memory *ism = ii->ipc_c.ism;
166
167 // Query the server for how many tracking origins it has.
168 count = 0;
169 for (uint32_t i = 0; i < ism->itrack_count; i++) {
170 xtrack = U_TYPED_CALLOC(struct xrt_tracking_origin);
171
172 memcpy(xtrack->name, ism->itracks[i].name, sizeof(xtrack->name));
173
174 xtrack->type = ism->itracks[i].type;
175 xtrack->initial_offset = ism->itracks[i].offset;
176 icsd->xtracks[count++] = xtrack;
177
178 u_var_add_root(xtrack, "Tracking origin", true);
179 u_var_add_ro_text(xtrack, xtrack->name, "name");
180 u_var_add_pose(xtrack, &xtrack->initial_offset, "offset");
181 }
182 icsd->xtrack_count = count;
183
184 // Query the server for how many devices it has.
185 count = 0;
186 for (uint32_t i = 0; i < ism->isdev_count; i++) {
187 struct ipc_shared_device *isdev = &ism->isdevs[i];
188 xtrack = icsd->xtracks[isdev->tracking_origin_index];
189
190 if (isdev->device_type == XRT_DEVICE_TYPE_HMD) {
191 xsysd->xdevs[count++] = ipc_client_hmd_create(&ii->ipc_c, xtrack, i);
192 } else {
193 xsysd->xdevs[count++] = ipc_client_device_create(&ii->ipc_c, xtrack, i);
194 }
195 }
196 xsysd->xdev_count = count;
197
198#define SET_ROLE(ROLE) \
199 do { \
200 int32_t index = ii->ipc_c.ism->roles.ROLE; \
201 xsysd->static_roles.ROLE = index >= 0 ? xsysd->xdevs[index] : NULL; \
202 } while (false)
203
204 SET_ROLE(head);
205 SET_ROLE(eyes);
206 SET_ROLE(face);
207 SET_ROLE(body);
208 SET_ROLE(hand_tracking.unobstructed.left);
209 SET_ROLE(hand_tracking.unobstructed.right);
210 SET_ROLE(hand_tracking.conforming.left);
211 SET_ROLE(hand_tracking.conforming.right);
212
213#undef SET_ROLE
214
215 // Done here now.
216 if (out_xsysc == NULL) {
217 goto out;
218 }
219
220 if (xsysd->static_roles.head == NULL) {
221 IPC_ERROR((&ii->ipc_c), "No head device found but asking for system compositor!");
222 xret = XRT_ERROR_IPC_FAILURE;
223 goto err_destroy;
224 }
225
226 xret = create_system_compositor(ii, xsysd->static_roles.head, &xsysc);
227 if (xret != XRT_SUCCESS) {
228 goto err_destroy;
229 }
230
231out:
232 *out_xsys = ipc_client_system_create(&ii->ipc_c, xsysc);
233 *out_xsysd = xsysd;
234 *out_xso = ipc_client_space_overseer_create(&ii->ipc_c);
235
236 if (xsysc != NULL) {
237 assert(out_xsysc != NULL);
238 *out_xsysc = xsysc;
239 }
240
241 return XRT_SUCCESS;
242
243err_destroy:
244 xrt_system_devices_destroy(&xsysd);
245
246 return xret;
247}
248
249static xrt_result_t
250ipc_client_instance_get_prober(struct xrt_instance *xinst, struct xrt_prober **out_xp)
251{
252 *out_xp = NULL;
253
254 return XRT_ERROR_PROBER_NOT_SUPPORTED;
255}
256
257static void
258ipc_client_instance_destroy(struct xrt_instance *xinst)
259{
260 struct ipc_client_instance *ii = ipc_client_instance(xinst);
261
262 // service considers us to be connected until fd is closed
263 ipc_client_connection_fini(&ii->ipc_c);
264
265#ifdef XRT_OS_ANDROID
266 android_instance_base_cleanup(&(ii->android), xinst);
267 ipc_client_android_destroy(&(ii->ipc_c.ica));
268#endif // XRT_OS_ANDROID
269
270#ifdef XRT_OS_WINDOWS
271 timeEndPeriod(1);
272#endif
273
274 ipc_shmem_destroy(&ii->ipc_c.ism_handle, (void **)&ii->ipc_c.ism, sizeof(struct ipc_shared_memory));
275
276 free(ii);
277}
278
279
280/*
281 *
282 * Exported function(s).
283 *
284 */
285
286/*!
287 * Constructor for xrt_instance IPC client proxy.
288 *
289 * @public @memberof ipc_instance
290 */
291xrt_result_t
292ipc_instance_create(const struct xrt_instance_info *i_info, struct xrt_instance **out_xinst)
293{
294 struct ipc_client_instance *ii = U_TYPED_CALLOC(struct ipc_client_instance);
295 ii->base.is_system_available = ipc_client_instance_is_system_available;
296 ii->base.create_system = ipc_client_instance_create_system;
297 ii->base.get_prober = ipc_client_instance_get_prober;
298 ii->base.destroy = ipc_client_instance_destroy;
299
300#ifdef XRT_OS_WINDOWS
301 timeBeginPeriod(1);
302#endif
303
304 xrt_result_t xret;
305#ifdef XRT_OS_ANDROID
306 xret = android_instance_base_init(&ii->android, &ii->base, i_info);
307 if (xret != XRT_SUCCESS) {
308 free(ii);
309 return xret;
310 }
311#endif
312
313 xret = ipc_client_connection_init(&ii->ipc_c, debug_get_log_option_ipc_log(), i_info);
314 if (xret != XRT_SUCCESS) {
315#ifdef XRT_OS_ANDROID
316 android_instance_base_cleanup(&(ii->android), &(ii->base));
317#endif
318 free(ii);
319 return xret;
320 }
321
322 ii->base.startup_timestamp = ii->ipc_c.ism->startup_timestamp;
323
324 *out_xinst = &ii->base;
325
326 return XRT_SUCCESS;
327}