The open source OpenXR runtime
1// Copyright 2019-2022, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief OpenGL client side glue to compositor header.
6 * @author Jakob Bornecrantz <jakob@collabora.com>
7 * @ingroup comp_client
8 */
9
10#pragma once
11
12#include "xrt/xrt_compositor.h"
13#include "os/os_threading.h"
14
15#ifdef __cplusplus
16extern "C" {
17#endif
18
19/*
20 *
21 * Structs
22 *
23 */
24
25struct client_gl_compositor;
26
27/*!
28 * @class client_gl_swapchain
29 *
30 * Wraps the real compositor swapchain providing a OpenGL based interface.
31 *
32 * Almost a one to one mapping to a OpenXR swapchain.
33 *
34 * @ingroup comp_client
35 * @implements xrt_swapchain_gl
36 */
37struct client_gl_swapchain
38{
39 //! Implements @ref xrt_swapchain_gl
40 struct xrt_swapchain_gl base;
41
42 struct xrt_swapchain_native *xscn;
43
44 //! The texture target of images in this swapchain.
45 uint32_t tex_target;
46
47 /*!
48 * The compositor this swapchain was created on. Used when swapchain functions need to use the GL context.
49 *
50 * Not an owning pointer.
51 */
52 struct client_gl_compositor *gl_compositor;
53};
54
55/*!
56 * What's the reason to make the context current, this is needed currently for
57 * EGL where we have to create a shared context in some cases. But when we
58 * want to synchronize (insert a fence or call glFinish) we can not use the
59 * shared context and must use the context that the app provided on creation.
60 */
61enum client_gl_context_reason
62{
63 /*!
64 * Used when the compositor needs to insert a fence in the command
65 * stream of the apps context, this needs to be done in the given
66 * context and not the shared one that may be created.
67 */
68 CLIENT_GL_CONTEXT_REASON_SYNCHRONIZE,
69 /*!
70 * Any other reason to make the context current,
71 * the shared may be used by now.
72 */
73 CLIENT_GL_CONTEXT_REASON_OTHER,
74};
75
76/*!
77 * Fetches the OpenGL context that is current on this thread and makes the
78 * OpenGL context given in the graphics binding current instead. Only one thread
79 * at a time can operate on the sections between
80 * @ref client_gl_context_begin_locked_func_t and
81 * @ref client_gl_context_end_locked_func_t,
82 * therefore @ref client_gl_context_end_locked_func_t MUST be called to avoid
83 * blocking the next thread calling @ref client_gl_context_begin_locked_func_t.
84 *
85 * This function must be called with the context_mutex locked held, that is
86 * handled by the helper function @ref client_gl_compositor_context_begin.
87 *
88 * If the return value is not XRT_SUCCESS,
89 * @ref client_gl_context_end_locked_func_t should not be called.
90 */
91typedef xrt_result_t (*client_gl_context_begin_locked_func_t)(struct xrt_compositor *xc,
92 enum client_gl_context_reason reason);
93
94/*!
95 * Makes the OpenGL context current that was current before
96 * @ref client_gl_context_begin_locked_func_t was called.
97 *
98 * This function must be called with the context_mutex locked held, successful
99 * call to @ref client_gl_compositor_context_begin will ensure that. The lock is
100 * not released by this function, but @ref client_gl_compositor_context_end does
101 * release it.
102 */
103typedef void (*client_gl_context_end_locked_func_t)(struct xrt_compositor *xc, enum client_gl_context_reason reason);
104
105/*!
106 * The type of a swapchain create constructor.
107 *
108 * Because our swapchain creation varies depending on available extensions and
109 * application choices, the swapchain constructor parameter to
110 * client_gl_compositor is parameterized.
111 *
112 * Note that the "common" swapchain creation function does some setup before
113 * invoking this, and some cleanup after.
114 *
115 * - Must populate `destroy`
116 * - Does not need to save/restore texture binding
117 */
118typedef struct xrt_swapchain *(*client_gl_swapchain_create_func_t)(struct xrt_compositor *xc,
119 const struct xrt_swapchain_create_info *info,
120 struct xrt_swapchain_native *xscn,
121 struct client_gl_swapchain **out_sc);
122
123/*!
124 * The type of a fence insertion function.
125 *
126 * This function is called in xrt_compositor::layer_commit.
127 *
128 * The returned graphics sync handle is given to xrt_compositor::layer_commit.
129 */
130typedef xrt_result_t (*client_gl_insert_fence_func_t)(struct xrt_compositor *xc,
131 xrt_graphics_sync_handle_t *out_handle);
132
133/*!
134 * @class client_gl_compositor
135 *
136 * Wraps the real compositor providing a OpenGL based interface.
137 *
138 * @ingroup comp_client
139 * @implements xrt_compositor_gl
140 */
141struct client_gl_compositor
142{
143 struct xrt_compositor_gl base;
144
145 struct xrt_compositor_native *xcn;
146
147 /*!
148 * Function pointer for making the OpenGL context current.
149 */
150 client_gl_context_begin_locked_func_t context_begin_locked;
151
152 /*!
153 * Function pointer for restoring prior OpenGL context.
154 */
155 client_gl_context_end_locked_func_t context_end_locked;
156
157 /*!
158 * Function pointer for creating the client swapchain.
159 */
160 client_gl_swapchain_create_func_t create_swapchain;
161
162 /*!
163 * Function pointer for inserting fences on
164 * xrt_compositor::layer_commit.
165 */
166 client_gl_insert_fence_func_t insert_fence;
167
168 /*!
169 * @ref client_gl_xlib_compositor::app_context can only be current on one thread; block other threads while we
170 * know it is bound to a thread.
171 */
172 struct os_mutex context_mutex;
173
174 bool renderdoc_enabled;
175};
176
177
178/*
179 *
180 * Functions and helpers.
181 *
182 */
183
184/*!
185 * Down-cast helper.
186 * @protected @memberof client_gl_compositor
187 */
188static inline struct client_gl_compositor *
189client_gl_compositor(struct xrt_compositor *xc)
190{
191 return (struct client_gl_compositor *)xc;
192}
193
194/*!
195 * Fill in a client_gl_compositor and do common OpenGL readiness checking.
196 *
197 * OpenGL can have multiple backing window systems we have to interact with, so
198 * there isn't just one unified OpenGL client constructor.
199 *
200 * Moves ownership of provided xcn to the client_gl_compositor.
201 *
202 * Be sure to load your GL loader/wrapper (GLAD) before calling into here, it
203 * won't be called for you.
204 *
205 * @public @memberof client_gl_compositor
206 * @see xrt_compositor_native, client_gl_compositor_fini
207 */
208bool
209client_gl_compositor_init(struct client_gl_compositor *c,
210 struct xrt_compositor_native *xcn,
211 client_gl_context_begin_locked_func_t context_begin,
212 client_gl_context_end_locked_func_t context_end,
213 client_gl_swapchain_create_func_t create_swapchain,
214 client_gl_insert_fence_func_t insert_fence);
215
216/*!
217 * Free all resources from the client_gl_compositor, does not free the
218 * @ref client_gl_compositor itself. Nor does it free the
219 * @ref xrt_compositor_native given at init as that is not owned by us.
220 *
221 * @public @memberof client_gl_compositor
222 */
223void
224client_gl_compositor_fini(struct client_gl_compositor *c);
225
226/*!
227 * @copydoc client_gl_context_begin_locked_func_t
228 *
229 * Helper for calling through the function pointer.
230 *
231 * @public @memberof client_gl_compositor
232 */
233static inline xrt_result_t
234client_gl_compositor_context_begin(struct xrt_compositor *xc, enum client_gl_context_reason reason)
235{
236 struct client_gl_compositor *cgc = client_gl_compositor(xc);
237
238 os_mutex_lock(&cgc->context_mutex);
239
240 xrt_result_t xret = cgc->context_begin_locked(xc, reason);
241 if (xret != XRT_SUCCESS) {
242 os_mutex_unlock(&cgc->context_mutex);
243 }
244
245 return xret;
246}
247
248/*!
249 * @copydoc client_gl_context_end_locked_func_t
250 *
251 * Helper for calling through the function pointer.
252 *
253 * @public @memberof client_gl_compositor
254 */
255static inline void
256client_gl_compositor_context_end(struct xrt_compositor *xc, enum client_gl_context_reason reason)
257{
258 struct client_gl_compositor *cgc = client_gl_compositor(xc);
259
260 cgc->context_end_locked(xc, reason);
261
262 os_mutex_unlock(&cgc->context_mutex);
263}
264
265
266#ifdef __cplusplus
267}
268#endif