The open source OpenXR runtime
1// Copyright 2023, Collabora, Ltd.
2// Copyright 2024-2025, NVIDIA CORPORATION.
3// SPDX-License-Identifier: BSL-1.0
4/*!
5 * @file
6 * @brief Helper to implement @ref xrt_system.
7 * @author Jakob Bornecrantz <jakob@collabora.com>
8 * @ingroup aux_util
9 */
10
11#include "xrt/xrt_compositor.h"
12
13#include "os/os_threading.h"
14
15#include "util/u_system.h"
16#include "util/u_session.h"
17#include "util/u_misc.h"
18#include "util/u_logging.h"
19
20#include <stdio.h>
21
22/*
23 *
24 * Helpers.
25 *
26 */
27
28static inline struct u_system *
29u_system(struct xrt_system *xsys)
30{
31 return (struct u_system *)xsys;
32}
33
34
35/*
36 *
37 * Member functions.
38 *
39 */
40
41static xrt_result_t
42push_event(struct xrt_session_event_sink *xses, const union xrt_session_event *xse)
43{
44 struct u_system *usys = container_of(xses, struct u_system, broadcast);
45
46 u_system_broadcast_event(usys, xse);
47
48 return XRT_SUCCESS;
49}
50
51static xrt_result_t
52create_session(struct xrt_system *xsys,
53 const struct xrt_session_info *xsi,
54 struct xrt_session **out_xs,
55 struct xrt_compositor_native **out_xcn)
56{
57 struct u_system *usys = u_system(xsys);
58 xrt_result_t xret = XRT_SUCCESS;
59
60 if (out_xcn != NULL && usys->xsysc == NULL) {
61 U_LOG_E("No system compositor in system, can't create native compositor.");
62 return XRT_ERROR_COMPOSITOR_NOT_SUPPORTED;
63 }
64
65 struct u_session *us = u_session_create(usys);
66
67 // Skip making a native compositor if not asked for.
68 if (out_xcn == NULL) {
69 goto out_session;
70 }
71
72 xret = xrt_syscomp_create_native_compositor( //
73 usys->xsysc, //
74 xsi, //
75 &us->sink, //
76 out_xcn); //
77 if (xret != XRT_SUCCESS) {
78 goto err;
79 }
80
81out_session:
82 *out_xs = &us->base;
83
84 return XRT_SUCCESS;
85
86err:
87 return xret;
88}
89
90static void
91destroy(struct xrt_system *xsys)
92{
93 struct u_system *usys = u_system(xsys);
94
95 // Use shared fini function.
96 u_system_fini(usys);
97
98 free(usys);
99}
100
101
102/*
103 *
104 * 'Exported' functions.
105 *
106 */
107
108struct u_system *
109u_system_create(void)
110{
111 struct u_system *usys = U_TYPED_CALLOC(struct u_system);
112
113 // Use init function, then add the common destroy function.
114 if (!u_system_init(usys, destroy)) {
115 free(usys);
116 return NULL;
117 }
118
119 return usys;
120}
121
122bool
123u_system_init(struct u_system *usys, void (*destroy_fn)(struct xrt_system *))
124{
125 // xrt_system fields.
126 usys->base.create_session = create_session;
127 usys->base.destroy = destroy_fn;
128
129 // xrt_session_event_sink fields.
130 usys->broadcast.push_event = push_event;
131
132 usys->sessions.capacity = 2;
133 usys->sessions.pairs = U_TYPED_ARRAY_CALLOC(struct u_system_session_pair, usys->sessions.capacity);
134 if (usys->sessions.pairs == NULL) {
135 U_LOG_E("Failed to allocate session array");
136 return false;
137 }
138
139 // u_system fields.
140 XRT_MAYBE_UNUSED int ret = os_mutex_init(&usys->sessions.mutex);
141 assert(ret == 0);
142
143 return true;
144}
145
146void
147u_system_fini(struct u_system *usys)
148{
149 // Just in case, should never happen.
150 if (usys->sessions.count > 0) {
151 U_LOG_E("Number of sessions not zero, things will crash!");
152 }
153
154 free(usys->sessions.pairs);
155 usys->sessions.count = 0;
156
157 // Mutex needs to be destroyed.
158 os_mutex_destroy(&usys->sessions.mutex);
159}
160
161void
162u_system_add_session(struct u_system *usys, struct xrt_session *xs, struct xrt_session_event_sink *xses)
163{
164 assert(xs != NULL);
165 assert(xses != NULL);
166
167 os_mutex_lock(&usys->sessions.mutex);
168
169 const uint32_t new_count = usys->sessions.count + 1;
170
171 if (new_count > usys->sessions.capacity) {
172 usys->sessions.capacity *= 2;
173 const size_t size = usys->sessions.capacity * sizeof(*usys->sessions.pairs);
174 struct u_system_session_pair *tmp = realloc(usys->sessions.pairs, size);
175 if (tmp == NULL) {
176 U_LOG_E("Failed to reallocate session array");
177 goto add_unlock;
178 }
179 usys->sessions.pairs = tmp;
180 }
181
182 usys->sessions.pairs[usys->sessions.count++] = (struct u_system_session_pair){xs, xses};
183
184add_unlock:
185 os_mutex_unlock(&usys->sessions.mutex);
186}
187
188void
189u_system_remove_session(struct u_system *usys, struct xrt_session *xs, struct xrt_session_event_sink *xses)
190{
191 os_mutex_lock(&usys->sessions.mutex);
192
193 uint32_t count = usys->sessions.count;
194 uint32_t dst = 0;
195
196 // Find where the session we are removing is.
197 while (dst < count) {
198 if (usys->sessions.pairs[dst].xs == xs) {
199 break;
200 } else {
201 dst++;
202 }
203 }
204
205 // Guards against empty array as well as not finding the session.
206 if (dst >= count) {
207 U_LOG_E("Could not find session to remove!");
208 goto remove_unlock;
209 }
210
211 // Should not be true with above check.
212 assert(count > 0);
213
214 /*
215 * Do copies if we are not removing the last session,
216 * this also guards against uint32_t::max.
217 */
218 if (dst < count - 1) {
219 // Copy from every follow down one.
220 for (uint32_t src = dst + 1; src < count; src++, dst++) {
221 usys->sessions.pairs[dst] = usys->sessions.pairs[src];
222 }
223 }
224
225 count--;
226 // This ensures that the memory returned in add is always zero initialized.
227 U_ZERO(&usys->sessions.pairs[count]);
228
229 usys->sessions.count = count;
230
231remove_unlock:
232 os_mutex_unlock(&usys->sessions.mutex);
233}
234
235void
236u_system_broadcast_event(struct u_system *usys, const union xrt_session_event *xse)
237{
238 xrt_result_t xret;
239
240 os_mutex_lock(&usys->sessions.mutex);
241
242 for (uint32_t i = 0; i < usys->sessions.count; i++) {
243 xret = xrt_session_event_sink_push(usys->sessions.pairs[i].xses, xse);
244 if (xret != XRT_SUCCESS) {
245 U_LOG_W("Failed to push event to session, dropping.");
246 }
247 }
248
249 os_mutex_unlock(&usys->sessions.mutex);
250}
251
252void
253u_system_set_system_compositor(struct u_system *usys, struct xrt_system_compositor *xsysc)
254{
255 assert(usys->xsysc == NULL);
256
257 usys->xsysc = xsysc;
258}
259
260void
261u_system_fill_properties(struct u_system *usys, const char *name)
262{
263 usys->base.properties.vendor_id = 42;
264 // The magical 247 number, is to silence warnings.
265 snprintf(usys->base.properties.name, XRT_MAX_SYSTEM_NAME_SIZE, "Monado: %.*s", 247, name);
266 usys->base.properties.form_factor = XRT_FORM_FACTOR_HMD;
267}