The open source OpenXR runtime
1// Copyright 2021, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief System compositor capable of supporting multiple clients: internal structs.
6 * @author Jakob Bornecrantz <jakob@collabora.com>
7 * @ingroup comp_multi
8 */
9
10#pragma once
11
12#include "xrt/xrt_compiler.h"
13#include "xrt/xrt_defines.h"
14#include "xrt/xrt_limits.h"
15#include "xrt/xrt_compositor.h"
16
17#include "os/os_time.h"
18#include "os/os_threading.h"
19
20#include "util/u_pacing.h"
21
22#ifdef __cplusplus
23extern "C" {
24#endif
25
26
27/*!
28 * Number of max active clients.
29 *
30 * @todo Move to `xrt_limits.h`, or make dynamic to remove limit.
31 * @ingroup comp_multi
32 */
33#define MULTI_MAX_CLIENTS 64
34
35/*!
36 * Number of max active layers per @ref multi_compositor.
37 *
38 * @todo Move to `xrt_limits.h` and share.
39 * @ingroup comp_multi
40 */
41#define MULTI_MAX_LAYERS XRT_MAX_LAYERS
42
43
44/*
45 *
46 * Native compositor.
47 *
48 */
49
50/*!
51 * Data for a single composition layer.
52 *
53 * Similar in function to @ref comp_layer
54 *
55 * @ingroup comp_multi
56 */
57struct multi_layer_entry
58{
59 /*!
60 * Device to get pose from.
61 */
62 struct xrt_device *xdev;
63
64 /*!
65 * Pointers to swapchains.
66 *
67 * How many are actually used depends on the value of @p data.type
68 */
69 struct xrt_swapchain *xscs[2 * XRT_MAX_VIEWS];
70
71 /*!
72 * All basic (trivially-serializable) data associated with a layer,
73 * aside from which swapchain(s) are used.
74 */
75 struct xrt_layer_data data;
76};
77
78/*!
79 * Render state for a single client, including all layers.
80 *
81 * @ingroup comp_multi
82 */
83struct multi_layer_slot
84{
85 struct xrt_layer_frame_data data;
86 uint32_t layer_count;
87 struct multi_layer_entry layers[MULTI_MAX_LAYERS];
88 bool active;
89};
90
91/*!
92 * A single compositor for feeding the layers from one session/app into
93 * the multi-client-capable system compositor.
94 *
95 * An instance (usually an IPC server instance) might have several of
96 * these at once, feeding layers to a single multi-client-capable system
97 * compositor.
98 *
99 * @ingroup comp_multi
100 * @implements xrt_compositor_native
101 */
102struct multi_compositor
103{
104 struct xrt_compositor_native base;
105
106 // Client info.
107 struct xrt_session_info xsi;
108
109 //! Where events for this compositor should go.
110 struct xrt_session_event_sink *xses;
111
112 //! Owning system compositor.
113 struct multi_system_compositor *msc;
114
115 //! Used to implement wait frame, only used for in process.
116 struct os_precise_sleeper frame_sleeper;
117
118 //! Used when waiting for the scheduled frame to complete.
119 struct os_precise_sleeper scheduled_sleeper;
120
121 struct
122 {
123 bool visible;
124 bool focused;
125
126 int64_t z_order;
127
128 bool session_active;
129 } state;
130
131 struct
132 {
133 //! Fence to wait for.
134 struct xrt_compositor_fence *xcf;
135
136 //! Timeline semaphore to wait for.
137 struct xrt_compositor_semaphore *xcsem;
138
139 //! Timeline semaphore value to wait for.
140 uint64_t value;
141
142 //! Frame id of frame being waited on.
143 int64_t frame_id;
144
145 //! The wait thread itself
146 struct os_thread_helper oth;
147
148 //! Have we gotten to the loop?
149 bool alive;
150
151 //! Is the thread waiting, if so the client should block.
152 bool waiting;
153
154 /*!
155 * Is the client thread blocked?
156 *
157 * Set to true by the client thread,
158 * cleared by the wait thread to release the client thread.
159 */
160 bool blocked;
161 } wait_thread;
162
163 //! Lock for all of the slots.
164 struct os_mutex slot_lock;
165
166 /*!
167 * The next which the next frames to be picked up will be displayed.
168 */
169 int64_t slot_next_frame_display;
170
171 /*!
172 * Currently being transferred or waited on.
173 * Not protected by the slot lock as it is only touched by the client thread.
174 */
175 struct multi_layer_slot progress;
176
177 //! Scheduled frames for a future timepoint.
178 struct multi_layer_slot scheduled;
179
180 /*!
181 * Fully ready to be used.
182 * Not protected by the slot lock as it is only touched by the main render loop thread.
183 */
184 struct multi_layer_slot delivered;
185
186 struct u_pacing_app *upa;
187
188 float current_refresh_rate_hz;
189};
190
191/*!
192 * Small helper go from @ref xrt_compositor to @ref multi_compositor.
193 *
194 * @ingroup comp_multi
195 */
196static inline struct multi_compositor *
197multi_compositor(struct xrt_compositor *xc)
198{
199 return (struct multi_compositor *)xc;
200}
201
202/*!
203 * Create a multi client wrapper compositor.
204 *
205 * @ingroup comp_multi
206 */
207xrt_result_t
208multi_compositor_create(struct multi_system_compositor *msc,
209 const struct xrt_session_info *xsi,
210 struct xrt_session_event_sink *xses,
211 struct xrt_compositor_native **out_xcn);
212
213/*!
214 * Push a event to be delivered to the session that corresponds
215 * to the given @ref multi_compositor.
216 *
217 * @ingroup comp_multi
218 * @private @memberof multi_compositor
219 */
220XRT_CHECK_RESULT xrt_result_t
221multi_compositor_push_event(struct multi_compositor *mc, const union xrt_session_event *xse);
222
223/*!
224 * Deliver any scheduled frames at that is to be display at or after the given @p display_time_ns. Called by the render
225 * thread and copies data from multi_compositor::scheduled to multi_compositor::delivered while holding the slot_lock.
226 *
227 * @ingroup comp_multi
228 * @private @memberof multi_compositor
229 */
230void
231multi_compositor_deliver_any_frames(struct multi_compositor *mc, int64_t display_time_ns);
232
233/*!
234 * Makes the current delivered frame as latched, called by the render thread.
235 * The list_and_timing_lock is held when this function is called.
236 *
237 * @ingroup comp_multi
238 * @private @memberof multi_compositor
239 */
240void
241multi_compositor_latch_frame_locked(struct multi_compositor *mc, int64_t when_ns, int64_t system_frame_id);
242
243/*!
244 * Clears and retires the delivered frame, called by the render thread.
245 * The list_and_timing_lock is held when this function is called.
246 *
247 * @ingroup comp_multi
248 * @private @memberof multi_compositor
249 */
250void
251multi_compositor_retire_delivered_locked(struct multi_compositor *mc, int64_t when_ns);
252
253
254/*
255 *
256 * Multi-client-capable system compositor
257 *
258 */
259
260/*!
261 * State of the multi-client system compositor. Use to track the calling of native
262 * compositor methods @ref xrt_comp_begin_session and @ref xrt_comp_end_session.
263 *
264 * It is driven by the number of active app sessions.
265 *
266 * @ingroup comp_multi
267 */
268enum multi_system_state
269{
270 /*!
271 * Invalid state, never used.
272 */
273 MULTI_SYSTEM_STATE_INVALID,
274
275 /*!
276 * One of the initial states, the multi-client system compositor will
277 * make sure that its @ref xrt_compositor_native submits one frame.
278 *
279 * The session hasn't been started yet.
280 */
281 MULTI_SYSTEM_STATE_INIT_WARM_START,
282
283 /*!
284 * One of the initial state and post stopping state.
285 *
286 * The multi-client system compositor has called @ref xrt_comp_end_session
287 * on its @ref xrt_compositor_native.
288 */
289 MULTI_SYSTEM_STATE_STOPPED,
290
291 /*!
292 * The main session is running.
293 *
294 * The multi-client system compositor has called @ref xrt_comp_begin_session
295 * on its @ref xrt_compositor_native.
296 */
297 MULTI_SYSTEM_STATE_RUNNING,
298
299 /*!
300 * There are no active sessions and the multi-client system compositor is
301 * instructing the native compositor to draw one or more clear frames.
302 *
303 * The multi-client system compositor has not yet called @ref xrt_comp_begin_session
304 * on its @ref xrt_compositor_native.
305 */
306 MULTI_SYSTEM_STATE_STOPPING,
307};
308
309/*!
310 * The multi-client module (aka multi compositor) is system compositor that
311 * multiplexes access to a single @ref xrt_compositor_native, merging layers
312 * from one or more client apps/sessions. This object implements the
313 * @ref xrt_system_compositor, and gives each session a @ref multi_compositor,
314 * which implements @ref xrt_compositor_native.
315 *
316 * @ingroup comp_multi
317 * @implements xrt_system_compositor
318 */
319struct multi_system_compositor
320{
321 //! Base interface.
322 struct xrt_system_compositor base;
323
324 //! Extra functions to handle multi client.
325 struct xrt_multi_compositor_control xmcc;
326
327 /*!
328 * Real native compositor, which this multi client module submits the
329 * combined layers of active @ref multi_compositor objects.
330 */
331 struct xrt_compositor_native *xcn;
332
333 /*!
334 * App pacer factory, when a new @ref multi_compositor is created a
335 * pacer is created from this factory.
336 */
337 struct u_pacing_app_factory *upaf;
338
339 //! Render loop thread.
340 struct os_thread_helper oth;
341
342 struct
343 {
344 /*!
345 * The state of the multi-client system compositor.
346 * This is updated on the multi_system_compositor::oth
347 * thread, aka multi-client system compositor main thread.
348 * It is driven by the active_count field.
349 */
350 enum multi_system_state state;
351
352 //! Number of active sessions, protected by oth.
353 uint64_t active_count;
354 } sessions;
355
356 /*!
357 * This mutex protects the list of client compositor
358 * and the rendering timings on it.
359 */
360 struct os_mutex list_and_timing_lock;
361
362 struct
363 {
364 int64_t predicted_display_time_ns;
365 int64_t predicted_display_period_ns;
366 int64_t diff_ns;
367 } last_timings;
368
369 //! List of active clients.
370 struct multi_compositor *clients[MULTI_MAX_CLIENTS];
371};
372
373/*!
374 * Cast helper
375 *
376 * @ingroup comp_multi
377 * @private @memberof multi_system_compositor
378 */
379static inline struct multi_system_compositor *
380multi_system_compositor(struct xrt_system_compositor *xsc)
381{
382 return (struct multi_system_compositor *)xsc;
383}
384
385/*!
386 * The client compositor calls this function to update when its session is
387 * started or stopped.
388 *
389 * @ingroup comp_multi
390 * @private @memberof multi_system_compositor
391 */
392void
393multi_system_compositor_update_session_status(struct multi_system_compositor *msc, bool active);
394
395
396#ifdef __cplusplus
397}
398#endif