The open source OpenXR runtime
1// Copyright 2019-2024, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Multi client wrapper compositor.
6 * @author Pete Black <pblack@collabora.com>
7 * @author Jakob Bornecrantz <jakob@collabora.com>
8 * @author Korcan Hussein <korcan.hussein@collabora.com>
9 * @ingroup comp_multi
10 */
11
12#include "xrt/xrt_config_os.h"
13#include "xrt/xrt_session.h"
14
15#include "os/os_time.h"
16#include "os/os_threading.h"
17
18#include "util/u_var.h"
19#include "util/u_misc.h"
20#include "util/u_time.h"
21#include "util/u_wait.h"
22#include "util/u_debug.h"
23#include "util/u_trace_marker.h"
24#include "util/u_distortion_mesh.h"
25
26#ifdef XRT_OS_LINUX
27#include "util/u_linux.h"
28#endif
29
30#include "multi/comp_multi_private.h"
31#include "multi/comp_multi_interface.h"
32
33#include <math.h>
34#include <stdio.h>
35#include <assert.h>
36#include <stdarg.h>
37#include <stdlib.h>
38#include <string.h>
39
40#ifdef XRT_GRAPHICS_SYNC_HANDLE_IS_FD
41#include <unistd.h>
42#endif
43
44
45/*
46 *
47 * Render thread.
48 *
49 */
50
51static void
52do_projection_layer(struct xrt_compositor *xc, struct multi_compositor *mc, struct multi_layer_entry *layer, uint32_t i)
53{
54 struct xrt_device *xdev = layer->xdev;
55
56 // Cast away
57 struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
58
59 // Do not need to copy the reference, but should verify the pointers for consistency
60 for (uint32_t j = 0; j < data->view_count; j++) {
61 if (layer->xscs[j] == NULL) {
62 U_LOG_E("Invalid swap chain for projection layer #%u!", i);
63 return;
64 }
65 }
66
67 if (xdev == NULL) {
68 U_LOG_E("Invalid xdev for projection layer #%u!", i);
69 return;
70 }
71
72 xrt_comp_layer_projection(xc, xdev, layer->xscs, data);
73}
74
75static void
76do_projection_layer_depth(struct xrt_compositor *xc,
77 struct multi_compositor *mc,
78 struct multi_layer_entry *layer,
79 uint32_t i)
80{
81 struct xrt_device *xdev = layer->xdev;
82
83 struct xrt_swapchain *xsc[XRT_MAX_VIEWS];
84 struct xrt_swapchain *d_xsc[XRT_MAX_VIEWS];
85 // Cast away
86 struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
87
88 for (uint32_t j = 0; j < data->view_count; j++) {
89 xsc[j] = layer->xscs[j];
90 d_xsc[j] = layer->xscs[j + data->view_count];
91
92 if (xsc[j] == NULL || d_xsc[j] == NULL) {
93 U_LOG_E("Invalid swap chain for projection layer #%u!", i);
94 return;
95 }
96 }
97
98 if (xdev == NULL) {
99 U_LOG_E("Invalid xdev for projection layer #%u!", i);
100 return;
101 }
102
103
104 xrt_comp_layer_projection_depth(xc, xdev, xsc, d_xsc, data);
105}
106
107static bool
108do_single(struct xrt_compositor *xc,
109 struct multi_compositor *mc,
110 struct multi_layer_entry *layer,
111 uint32_t i,
112 const char *name,
113 struct xrt_device **out_xdev,
114 struct xrt_swapchain **out_xcs,
115 struct xrt_layer_data **out_data)
116{
117 struct xrt_device *xdev = layer->xdev;
118 struct xrt_swapchain *xcs = layer->xscs[0];
119
120 if (xcs == NULL) {
121 U_LOG_E("Invalid swapchain for layer #%u '%s'!", i, name);
122 return false;
123 }
124
125 if (xdev == NULL) {
126 U_LOG_E("Invalid xdev for layer #%u '%s'!", i, name);
127 return false;
128 }
129
130 // Cast away
131 struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data;
132
133 *out_xdev = xdev;
134 *out_xcs = xcs;
135 *out_data = data;
136
137 return true;
138}
139
140static void
141do_quad_layer(struct xrt_compositor *xc, struct multi_compositor *mc, struct multi_layer_entry *layer, uint32_t i)
142{
143 struct xrt_device *xdev = NULL;
144 struct xrt_swapchain *xcs = NULL;
145 struct xrt_layer_data *data = NULL;
146
147 if (!do_single(xc, mc, layer, i, "quad", &xdev, &xcs, &data)) {
148 return;
149 }
150
151 xrt_comp_layer_quad(xc, xdev, xcs, data);
152}
153
154static void
155do_cube_layer(struct xrt_compositor *xc, struct multi_compositor *mc, struct multi_layer_entry *layer, uint32_t i)
156{
157 struct xrt_device *xdev = NULL;
158 struct xrt_swapchain *xcs = NULL;
159 struct xrt_layer_data *data = NULL;
160
161 if (!do_single(xc, mc, layer, i, "cube", &xdev, &xcs, &data)) {
162 return;
163 }
164
165 xrt_comp_layer_cube(xc, xdev, xcs, data);
166}
167
168static void
169do_cylinder_layer(struct xrt_compositor *xc, struct multi_compositor *mc, struct multi_layer_entry *layer, uint32_t i)
170{
171 struct xrt_device *xdev = NULL;
172 struct xrt_swapchain *xcs = NULL;
173 struct xrt_layer_data *data = NULL;
174
175 if (!do_single(xc, mc, layer, i, "cylinder", &xdev, &xcs, &data)) {
176 return;
177 }
178
179 xrt_comp_layer_cylinder(xc, xdev, xcs, data);
180}
181
182static void
183do_equirect1_layer(struct xrt_compositor *xc, struct multi_compositor *mc, struct multi_layer_entry *layer, uint32_t i)
184{
185 struct xrt_device *xdev = NULL;
186 struct xrt_swapchain *xcs = NULL;
187 struct xrt_layer_data *data = NULL;
188
189 if (!do_single(xc, mc, layer, i, "equirect1", &xdev, &xcs, &data)) {
190 return;
191 }
192
193 xrt_comp_layer_equirect1(xc, xdev, xcs, data);
194}
195
196static void
197do_equirect2_layer(struct xrt_compositor *xc, struct multi_compositor *mc, struct multi_layer_entry *layer, uint32_t i)
198{
199 struct xrt_device *xdev = NULL;
200 struct xrt_swapchain *xcs = NULL;
201 struct xrt_layer_data *data = NULL;
202
203 if (!do_single(xc, mc, layer, i, "equirect2", &xdev, &xcs, &data)) {
204 return;
205 }
206
207 xrt_comp_layer_equirect2(xc, xdev, xcs, data);
208}
209
210static int
211overlay_sort_func(const void *a, const void *b)
212{
213 struct multi_compositor *mc_a = *(struct multi_compositor **)a;
214 struct multi_compositor *mc_b = *(struct multi_compositor **)b;
215
216 if (mc_a->state.z_order < mc_b->state.z_order) {
217 return -1;
218 }
219
220 if (mc_a->state.z_order > mc_b->state.z_order) {
221 return 1;
222 }
223
224 return 0;
225}
226
227static enum xrt_blend_mode
228find_active_blend_mode(struct multi_compositor **overlay_sorted_clients, size_t size)
229{
230 if (overlay_sorted_clients == NULL)
231 return XRT_BLEND_MODE_OPAQUE;
232
233 const struct multi_compositor *first_visible = NULL;
234 for (size_t k = 0; k < size; ++k) {
235 const struct multi_compositor *mc = overlay_sorted_clients[k];
236 assert(mc != NULL);
237
238 // if a focused client is found just return, "first_visible" has lower priority and can be ignored.
239 if (mc->state.focused) {
240 assert(mc->state.visible);
241 return mc->delivered.data.env_blend_mode;
242 }
243
244 if (first_visible == NULL && mc->state.visible) {
245 first_visible = mc;
246 }
247 }
248 if (first_visible != NULL)
249 return first_visible->delivered.data.env_blend_mode;
250 return XRT_BLEND_MODE_OPAQUE;
251}
252
253static void
254transfer_layers_locked(struct multi_system_compositor *msc, int64_t display_time_ns, int64_t system_frame_id)
255{
256 COMP_TRACE_MARKER();
257
258 struct xrt_compositor *xc = &msc->xcn->base;
259
260 struct multi_compositor *array[MULTI_MAX_CLIENTS] = {0};
261
262 // To mark latching.
263 int64_t now_ns = os_monotonic_get_ns();
264
265 size_t count = 0;
266 for (size_t k = 0; k < ARRAY_SIZE(array); k++) {
267 struct multi_compositor *mc = msc->clients[k];
268
269 // Array can be empty
270 if (mc == NULL) {
271 continue;
272 }
273
274 // Even if it's not shown, make sure that frames are delivered.
275 multi_compositor_deliver_any_frames(mc, display_time_ns);
276
277 // None of the data in this slot is valid, don't check access it.
278 if (!mc->delivered.active) {
279 continue;
280 }
281
282 // The client isn't visible, do not submit it's layers.
283 if (!mc->state.visible) {
284 // Need to drop delivered frame as it shouldn't be reused.
285 multi_compositor_retire_delivered_locked(mc, now_ns);
286 continue;
287 }
288
289 // Just in case.
290 if (!mc->state.session_active) {
291 U_LOG_W("Session is visible but not active.");
292
293 // Need to drop delivered frame as it shouldn't be reused.
294 multi_compositor_retire_delivered_locked(mc, now_ns);
295 continue;
296 }
297
298 // The list_and_timing_lock is held when callign this function.
299 multi_compositor_latch_frame_locked(mc, now_ns, system_frame_id);
300
301 array[count++] = msc->clients[k];
302 }
303
304 // Sort the stack array
305 qsort(array, count, sizeof(struct multi_compositor *), overlay_sort_func);
306
307 // find first (ordered by bottom to top) active client to retrieve xrt_layer_frame_data
308 const enum xrt_blend_mode blend_mode = find_active_blend_mode(array, count);
309
310 const struct xrt_layer_frame_data data = {
311 .frame_id = system_frame_id,
312 .display_time_ns = display_time_ns,
313 .env_blend_mode = blend_mode,
314 };
315 xrt_comp_layer_begin(xc, &data);
316
317 // Copy all active layers.
318 for (size_t k = 0; k < count; k++) {
319 struct multi_compositor *mc = array[k];
320 assert(mc != NULL);
321
322 for (uint32_t i = 0; i < mc->delivered.layer_count; i++) {
323 struct multi_layer_entry *layer = &mc->delivered.layers[i];
324
325 switch (layer->data.type) {
326 case XRT_LAYER_PROJECTION: do_projection_layer(xc, mc, layer, i); break;
327 case XRT_LAYER_PROJECTION_DEPTH: do_projection_layer_depth(xc, mc, layer, i); break;
328 case XRT_LAYER_QUAD: do_quad_layer(xc, mc, layer, i); break;
329 case XRT_LAYER_CUBE: do_cube_layer(xc, mc, layer, i); break;
330 case XRT_LAYER_CYLINDER: do_cylinder_layer(xc, mc, layer, i); break;
331 case XRT_LAYER_EQUIRECT1: do_equirect1_layer(xc, mc, layer, i); break;
332 case XRT_LAYER_EQUIRECT2: do_equirect2_layer(xc, mc, layer, i); break;
333 default: U_LOG_E("Unhandled layer type '%i'!", layer->data.type); break;
334 }
335 }
336 }
337}
338
339static void
340broadcast_timings_to_clients(struct multi_system_compositor *msc, int64_t predicted_display_time_ns)
341{
342 COMP_TRACE_MARKER();
343
344 os_mutex_lock(&msc->list_and_timing_lock);
345
346 for (size_t i = 0; i < ARRAY_SIZE(msc->clients); i++) {
347 struct multi_compositor *mc = msc->clients[i];
348 if (mc == NULL) {
349 continue;
350 }
351
352 os_mutex_lock(&mc->slot_lock);
353 mc->slot_next_frame_display = predicted_display_time_ns;
354 os_mutex_unlock(&mc->slot_lock);
355 }
356
357 os_mutex_unlock(&msc->list_and_timing_lock);
358}
359
360static void
361broadcast_timings_to_pacers(struct multi_system_compositor *msc,
362 int64_t predicted_display_time_ns,
363 int64_t predicted_display_period_ns,
364 int64_t diff_ns)
365{
366 COMP_TRACE_MARKER();
367
368 os_mutex_lock(&msc->list_and_timing_lock);
369
370 for (size_t i = 0; i < ARRAY_SIZE(msc->clients); i++) {
371 struct multi_compositor *mc = msc->clients[i];
372 if (mc == NULL) {
373 continue;
374 }
375
376 u_pa_info( //
377 mc->upa, //
378 predicted_display_time_ns, //
379 predicted_display_period_ns, //
380 diff_ns); //
381
382 os_mutex_lock(&mc->slot_lock);
383 mc->slot_next_frame_display = predicted_display_time_ns;
384 os_mutex_unlock(&mc->slot_lock);
385 }
386
387 msc->last_timings.predicted_display_time_ns = predicted_display_time_ns;
388 msc->last_timings.predicted_display_period_ns = predicted_display_period_ns;
389 msc->last_timings.diff_ns = diff_ns;
390
391 os_mutex_unlock(&msc->list_and_timing_lock);
392}
393
394static void
395wait_frame(struct os_precise_sleeper *sleeper, struct xrt_compositor *xc, int64_t frame_id, int64_t wake_up_time_ns)
396{
397 COMP_TRACE_MARKER();
398
399 // Wait until the given wake up time.
400 u_wait_until(sleeper, wake_up_time_ns);
401
402 int64_t now_ns = os_monotonic_get_ns();
403
404 // Signal that we woke up.
405 xrt_comp_mark_frame(xc, frame_id, XRT_COMPOSITOR_FRAME_POINT_WOKE, now_ns);
406}
407
408static void
409update_session_state_locked(struct multi_system_compositor *msc)
410{
411 struct xrt_compositor *xc = &msc->xcn->base;
412
413 //! @todo Make this not be hardcoded.
414 const struct xrt_begin_session_info begin_session_info = {
415 .view_type = XRT_VIEW_TYPE_STEREO,
416 .ext_hand_tracking_enabled = false,
417 .ext_hand_tracking_data_source_enabled = false,
418 .ext_eye_gaze_interaction_enabled = false,
419 .ext_hand_interaction_enabled = false,
420 .htc_facial_tracking_enabled = false,
421 .fb_body_tracking_enabled = false,
422 .fb_face_tracking2_enabled = false,
423 .meta_body_tracking_full_body_enabled = false,
424 .meta_body_tracking_calibration_enabled = false,
425 };
426
427 switch (msc->sessions.state) {
428 case MULTI_SYSTEM_STATE_INIT_WARM_START:
429 // Produce at least one frame on init.
430 msc->sessions.state = MULTI_SYSTEM_STATE_STOPPING;
431 xrt_comp_begin_session(xc, &begin_session_info);
432 U_LOG_I("Doing warm start, %u active app session(s).", (uint32_t)msc->sessions.active_count);
433 break;
434
435 case MULTI_SYSTEM_STATE_STOPPED:
436 if (msc->sessions.active_count == 0) {
437 break;
438 }
439
440 msc->sessions.state = MULTI_SYSTEM_STATE_RUNNING;
441 xrt_comp_begin_session(xc, &begin_session_info);
442 U_LOG_I("Started native session, %u active app session(s).", (uint32_t)msc->sessions.active_count);
443 break;
444
445 case MULTI_SYSTEM_STATE_RUNNING:
446 if (msc->sessions.active_count > 0) {
447 break;
448 }
449
450 msc->sessions.state = MULTI_SYSTEM_STATE_STOPPING;
451 U_LOG_D("Stopping native session, %u active app session(s).", (uint32_t)msc->sessions.active_count);
452 break;
453
454 case MULTI_SYSTEM_STATE_STOPPING:
455 // Just in case
456 if (msc->sessions.active_count > 0) {
457 msc->sessions.state = MULTI_SYSTEM_STATE_RUNNING;
458 U_LOG_D("Restarting native session, %u active app session(s).",
459 (uint32_t)msc->sessions.active_count);
460 break;
461 }
462
463 msc->sessions.state = MULTI_SYSTEM_STATE_STOPPED;
464 xrt_comp_end_session(xc);
465 U_LOG_I("Stopped native session, %u active app session(s).", (uint32_t)msc->sessions.active_count);
466 break;
467
468 case MULTI_SYSTEM_STATE_INVALID:
469 default:
470 U_LOG_E("Got invalid state %u", msc->sessions.state);
471 msc->sessions.state = MULTI_SYSTEM_STATE_STOPPING;
472 assert(false);
473 }
474}
475
476static int
477multi_main_loop(struct multi_system_compositor *msc)
478{
479 U_TRACE_SET_THREAD_NAME("Multi Client Module");
480 os_thread_helper_name(&msc->oth, "Multi Client Module");
481
482#ifdef XRT_OS_LINUX
483 // Try to raise priority of this thread.
484 u_linux_try_to_set_realtime_priority_on_thread(U_LOGGING_INFO, "Multi Client Module");
485#endif
486
487 struct xrt_compositor *xc = &msc->xcn->base;
488
489 // For wait frame.
490 struct os_precise_sleeper sleeper = {0};
491 os_precise_sleeper_init(&sleeper);
492
493 // Protect the thread state and the sessions state.
494 os_thread_helper_lock(&msc->oth);
495
496 while (os_thread_helper_is_running_locked(&msc->oth)) {
497
498 // Updates msc->sessions.active depending on active client sessions.
499 update_session_state_locked(msc);
500
501 if (msc->sessions.state == MULTI_SYSTEM_STATE_STOPPED) {
502 // Sleep and wait to be signaled.
503 os_thread_helper_wait_locked(&msc->oth);
504
505 // Loop back to running and session check.
506 continue;
507 }
508
509 // Unlock the thread after the checks has been done.
510 os_thread_helper_unlock(&msc->oth);
511
512 int64_t frame_id = -1;
513 int64_t wake_up_time_ns = 0;
514 int64_t predicted_gpu_time_ns = 0;
515 int64_t predicted_display_time_ns = 0;
516 int64_t predicted_display_period_ns = 0;
517
518 // Get the information for the next frame.
519 xrt_comp_predict_frame( //
520 xc, //
521 &frame_id, //
522 &wake_up_time_ns, //
523 &predicted_gpu_time_ns, //
524 &predicted_display_time_ns, //
525 &predicted_display_period_ns); //
526
527 // Do this as soon as we have the new display time.
528 broadcast_timings_to_clients(msc, predicted_display_time_ns);
529
530 // Now we can wait.
531 wait_frame(&sleeper, xc, frame_id, wake_up_time_ns);
532
533 int64_t now_ns = os_monotonic_get_ns();
534 int64_t diff_ns = predicted_display_time_ns - now_ns;
535
536 // Now we know the diff, broadcast to pacers.
537 broadcast_timings_to_pacers(msc, predicted_display_time_ns, predicted_display_period_ns, diff_ns);
538
539 xrt_comp_begin_frame(xc, frame_id);
540
541 // Make sure that the clients doesn't go away while we transfer layers.
542 os_mutex_lock(&msc->list_and_timing_lock);
543 transfer_layers_locked(msc, predicted_display_time_ns, frame_id);
544 os_mutex_unlock(&msc->list_and_timing_lock);
545
546 xrt_comp_layer_commit(xc, XRT_GRAPHICS_SYNC_HANDLE_INVALID);
547
548 // Re-lock the thread for check in while statement.
549 os_thread_helper_lock(&msc->oth);
550 }
551
552 // Clean up the sessions state.
553 switch (msc->sessions.state) {
554 case MULTI_SYSTEM_STATE_RUNNING:
555 case MULTI_SYSTEM_STATE_STOPPING:
556 U_LOG_I("Stopped native session, shutting down.");
557 xrt_comp_end_session(xc);
558 break;
559 case MULTI_SYSTEM_STATE_STOPPED: break;
560 default: assert(false);
561 }
562
563 os_thread_helper_unlock(&msc->oth);
564
565 os_precise_sleeper_deinit(&sleeper);
566
567 return 0;
568}
569
570static void *
571thread_func(void *ptr)
572{
573 return (void *)(intptr_t)multi_main_loop((struct multi_system_compositor *)ptr);
574}
575
576
577/*
578 *
579 * System multi compositor functions.
580 *
581 */
582
583static xrt_result_t
584system_compositor_set_state(struct xrt_system_compositor *xsc, struct xrt_compositor *xc, bool visible, bool focused)
585{
586 struct multi_system_compositor *msc = multi_system_compositor(xsc);
587 struct multi_compositor *mc = multi_compositor(xc);
588 (void)msc;
589
590 //! @todo Locking?
591 if (mc->state.visible != visible || mc->state.focused != focused) {
592 mc->state.visible = visible;
593 mc->state.focused = focused;
594
595 union xrt_session_event xse = XRT_STRUCT_INIT;
596 xse.type = XRT_SESSION_EVENT_STATE_CHANGE;
597 xse.state.visible = visible;
598 xse.state.focused = focused;
599
600 return multi_compositor_push_event(mc, &xse);
601 }
602
603 return XRT_SUCCESS;
604}
605
606static xrt_result_t
607system_compositor_set_z_order(struct xrt_system_compositor *xsc, struct xrt_compositor *xc, int64_t z_order)
608{
609 struct multi_system_compositor *msc = multi_system_compositor(xsc);
610 struct multi_compositor *mc = multi_compositor(xc);
611 (void)msc;
612
613 //! @todo Locking?
614 mc->state.z_order = z_order;
615
616 return XRT_SUCCESS;
617}
618
619static xrt_result_t
620system_compositor_set_main_app_visibility(struct xrt_system_compositor *xsc, struct xrt_compositor *xc, bool visible)
621{
622 struct multi_system_compositor *msc = multi_system_compositor(xsc);
623 struct multi_compositor *mc = multi_compositor(xc);
624 (void)msc;
625
626 union xrt_session_event xse = XRT_STRUCT_INIT;
627 xse.type = XRT_SESSION_EVENT_OVERLAY_CHANGE;
628 xse.overlay.visible = visible;
629
630 return multi_compositor_push_event(mc, &xse);
631}
632
633static xrt_result_t
634system_compositor_notify_loss_pending(struct xrt_system_compositor *xsc,
635 struct xrt_compositor *xc,
636 int64_t loss_time_ns)
637{
638 struct multi_system_compositor *msc = multi_system_compositor(xsc);
639 struct multi_compositor *mc = multi_compositor(xc);
640 (void)msc;
641
642 union xrt_session_event xse = XRT_STRUCT_INIT;
643 xse.type = XRT_SESSION_EVENT_LOSS_PENDING;
644 xse.loss_pending.loss_time_ns = loss_time_ns;
645
646 return multi_compositor_push_event(mc, &xse);
647}
648
649static xrt_result_t
650system_compositor_notify_lost(struct xrt_system_compositor *xsc, struct xrt_compositor *xc)
651{
652 struct multi_system_compositor *msc = multi_system_compositor(xsc);
653 struct multi_compositor *mc = multi_compositor(xc);
654 (void)msc;
655
656 union xrt_session_event xse = XRT_STRUCT_INIT;
657 xse.type = XRT_SESSION_EVENT_LOST;
658
659 return multi_compositor_push_event(mc, &xse);
660}
661
662static xrt_result_t
663system_compositor_notify_display_refresh_changed(struct xrt_system_compositor *xsc,
664 struct xrt_compositor *xc,
665 float from_display_refresh_rate_hz,
666 float to_display_refresh_rate_hz)
667{
668 struct multi_system_compositor *msc = multi_system_compositor(xsc);
669 struct multi_compositor *mc = multi_compositor(xc);
670 (void)msc;
671
672 union xrt_session_event xse = XRT_STRUCT_INIT;
673 xse.type = XRT_SESSION_EVENT_DISPLAY_REFRESH_RATE_CHANGE;
674 xse.display.from_display_refresh_rate_hz = from_display_refresh_rate_hz;
675 xse.display.to_display_refresh_rate_hz = to_display_refresh_rate_hz;
676
677 return multi_compositor_push_event(mc, &xse);
678}
679
680
681/*
682 *
683 * System compositor functions.
684 *
685 */
686
687static xrt_result_t
688system_compositor_create_native_compositor(struct xrt_system_compositor *xsc,
689 const struct xrt_session_info *xsi,
690 struct xrt_session_event_sink *xses,
691 struct xrt_compositor_native **out_xcn)
692{
693 struct multi_system_compositor *msc = multi_system_compositor(xsc);
694
695 return multi_compositor_create(msc, xsi, xses, out_xcn);
696}
697
698static void
699system_compositor_destroy(struct xrt_system_compositor *xsc)
700{
701 struct multi_system_compositor *msc = multi_system_compositor(xsc);
702
703 // Destroy the render thread first, destroy also stops the thread.
704 os_thread_helper_destroy(&msc->oth);
705
706 u_paf_destroy(&msc->upaf);
707
708 xrt_comp_native_destroy(&msc->xcn);
709
710 os_mutex_destroy(&msc->list_and_timing_lock);
711
712 free(msc);
713}
714
715
716/*
717 *
718 * 'Exported' functions.
719 *
720 */
721
722void
723multi_system_compositor_update_session_status(struct multi_system_compositor *msc, bool active)
724{
725 os_thread_helper_lock(&msc->oth);
726
727 if (active) {
728 assert(msc->sessions.active_count < UINT32_MAX);
729 msc->sessions.active_count++;
730
731 // If the thread is sleeping wake it up.
732 os_thread_helper_signal_locked(&msc->oth);
733 } else {
734 assert(msc->sessions.active_count > 0);
735 msc->sessions.active_count--;
736 }
737
738 os_thread_helper_unlock(&msc->oth);
739}
740
741xrt_result_t
742comp_multi_create_system_compositor(struct xrt_compositor_native *xcn,
743 struct u_pacing_app_factory *upaf,
744 const struct xrt_system_compositor_info *xsci,
745 bool do_warm_start,
746 struct xrt_system_compositor **out_xsysc)
747{
748 struct multi_system_compositor *msc = U_TYPED_CALLOC(struct multi_system_compositor);
749 msc->base.create_native_compositor = system_compositor_create_native_compositor;
750 msc->base.destroy = system_compositor_destroy;
751 msc->xmcc.set_state = system_compositor_set_state;
752 msc->xmcc.set_z_order = system_compositor_set_z_order;
753 msc->xmcc.set_main_app_visibility = system_compositor_set_main_app_visibility;
754 msc->xmcc.notify_loss_pending = system_compositor_notify_loss_pending;
755 msc->xmcc.notify_lost = system_compositor_notify_lost;
756 msc->xmcc.notify_display_refresh_changed = system_compositor_notify_display_refresh_changed;
757 msc->base.xmcc = &msc->xmcc;
758 msc->base.info = *xsci;
759 msc->upaf = upaf;
760 msc->xcn = xcn;
761 msc->sessions.active_count = 0;
762 msc->sessions.state = do_warm_start ? MULTI_SYSTEM_STATE_INIT_WARM_START : MULTI_SYSTEM_STATE_STOPPED;
763
764 os_mutex_init(&msc->list_and_timing_lock);
765
766 //! @todo Make the clients not go from IDLE to READY before we have completed a first frame.
767 // Make sure there is at least some sort of valid frame data here.
768 msc->last_timings.predicted_display_time_ns = os_monotonic_get_ns(); // As good as any time.
769 msc->last_timings.predicted_display_period_ns = U_TIME_1MS_IN_NS * 16; // Just a wild guess.
770 msc->last_timings.diff_ns = U_TIME_1MS_IN_NS * 5; // Make sure it's not zero at least.
771
772 int ret = os_thread_helper_init(&msc->oth);
773 if (ret < 0) {
774 return XRT_ERROR_THREADING_INIT_FAILURE;
775 }
776
777 os_thread_helper_start(&msc->oth, thread_func, msc);
778
779 *out_xsysc = &msc->base;
780
781 return XRT_SUCCESS;
782}