The open source OpenXR runtime
1// Copyright 2019-2022, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Win32 client side glue to compositor implementation.
6 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
7 * @author Milan Jaros <milan.jaros@vsb.cz>
8 * @author Jakob Bornecrantz <jakob@collabora.com>
9 * @ingroup comp_client
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14
15#include "client/comp_gl_client.h"
16#include "util/u_misc.h"
17#include "util/u_logging.h"
18
19#include "xrt/xrt_gfx_win32.h"
20
21#include "client/comp_gl_win32_client.h"
22#include "client/comp_gl_memobj_swapchain.h"
23
24#include "ogl/ogl_api.h"
25#include "ogl/wgl_api.h"
26
27/*
28 *
29 * OpenGL context helper.
30 *
31 */
32
33static inline bool
34context_matches(const struct client_gl_context *a, const struct client_gl_context *b)
35{
36 return a->hDC == b->hDC && a->hGLRC == b->hGLRC;
37}
38
39static inline void
40context_save_current(struct client_gl_context *current_ctx)
41{
42 current_ctx->hDC = wglGetCurrentDC();
43 current_ctx->hGLRC = wglGetCurrentContext();
44}
45
46static inline bool
47context_make_current(const struct client_gl_context *ctx_to_make_current)
48{
49 if (wglMakeCurrent(ctx_to_make_current->hDC, ctx_to_make_current->hGLRC)) {
50 return true;
51 }
52 return false;
53}
54
55/*!
56 * Down-cast helper.
57 *
58 * @private @memberof client_gl_win32_compositor
59 */
60static inline struct client_gl_win32_compositor *
61client_gl_win32_compositor(struct xrt_compositor *xc)
62{
63 return (struct client_gl_win32_compositor *)xc;
64}
65
66static void
67client_gl_win32_compositor_destroy(struct xrt_compositor *xc)
68{
69 struct client_gl_win32_compositor *c = client_gl_win32_compositor(xc);
70
71 client_gl_compositor_fini(&c->base);
72
73 FreeLibrary(c->opengl);
74 c->opengl = NULL;
75
76 free(c);
77}
78
79static xrt_result_t
80client_gl_context_begin_locked(struct xrt_compositor *xc, enum client_gl_context_reason reason)
81{
82 struct client_gl_win32_compositor *c = client_gl_win32_compositor(xc);
83
84 struct client_gl_context *app_ctx = &c->app_context;
85
86 context_save_current(&c->temp_context);
87
88 bool need_make_current = !context_matches(&c->temp_context, app_ctx);
89
90 U_LOG_T("GL Context begin: need makeCurrent: %d (current %p -> app %p)", need_make_current,
91 (void *)c->temp_context.hGLRC, (void *)app_ctx->hGLRC);
92
93 if (need_make_current && !context_make_current(app_ctx)) {
94 U_LOG_E("Failed to make WGL context current");
95 // No need to restore on failure.
96 return XRT_ERROR_OPENGL;
97 }
98
99 return XRT_SUCCESS;
100}
101
102static void
103client_gl_context_end_locked(struct xrt_compositor *xc, enum client_gl_context_reason reason)
104{
105 struct client_gl_win32_compositor *c = client_gl_win32_compositor(xc);
106
107 struct client_gl_context *app_ctx = &c->app_context;
108
109 struct client_gl_context *current_wgl_context = &c->temp_context;
110
111 bool need_make_current = !context_matches(&c->temp_context, app_ctx);
112
113 U_LOG_T("GL Context end: need makeCurrent: %d (app %p -> current %p)", need_make_current,
114 (void *)app_ctx->hGLRC, (void *)c->temp_context.hGLRC);
115
116 if (need_make_current && !context_make_current(current_wgl_context)) {
117 U_LOG_E("Failed to make old WGL context current!");
118 // fall through to os_mutex_unlock even if we didn't succeed in restoring the context
119 }
120}
121
122static GLADapiproc
123client_gl_get_proc_addr(void *userptr, const char *name)
124{
125 GLADapiproc ret = (GLADapiproc)wglGetProcAddress(name);
126 if (ret == NULL) {
127 ret = (GLADapiproc)GetProcAddress((HMODULE)userptr, name);
128 }
129 return ret;
130}
131
132struct client_gl_win32_compositor *
133client_gl_win32_compositor_create(struct xrt_compositor_native *xcn, void *hDC, void *hGLRC)
134{
135 // Save old GLX context.
136 struct client_gl_context current_ctx;
137 context_save_current(¤t_ctx);
138
139 // The context and drawables given from the app.
140 struct client_gl_context app_ctx = {
141 .hDC = hDC,
142 .hGLRC = hGLRC,
143 };
144
145
146 /*
147 * Make given context current if needed.
148 */
149
150 bool need_make_current = !context_matches(¤t_ctx, &app_ctx);
151
152 if (need_make_current && !context_make_current(&app_ctx)) {
153 U_LOG_E("Failed to make WGL context current");
154 // No need to restore on failure.
155 return NULL;
156 }
157
158
159 /*
160 * Load functions.
161 */
162
163 HMODULE opengl = LoadLibraryW(L"opengl32.dll");
164
165 int wgl_result = gladLoadWGLUserPtr(hDC, client_gl_get_proc_addr, opengl);
166 int gl_result = gladLoadGLUserPtr(client_gl_get_proc_addr, opengl);
167
168 if (glGetString != NULL) {
169 U_LOG_D( //
170 "OpenGL context:" //
171 "\n\tGL_VERSION: %s" //
172 "\n\tGL_RENDERER: %s" //
173 "\n\tGL_VENDOR: %s", //
174 glGetString(GL_VERSION), //
175 glGetString(GL_RENDERER), //
176 glGetString(GL_VENDOR)); //
177 }
178
179
180 /*
181 * Return to app context.
182 */
183
184 if (need_make_current && !context_make_current(¤t_ctx)) {
185 U_LOG_E("Failed to make old WGL context current!");
186 }
187
188
189 /*
190 * Checking of context.
191 */
192
193 // Only do error checking here.
194 if (wgl_result == 0 || gl_result == 0) {
195 U_LOG_E("Failed to load GLAD functions gladLoadWGL: 0x%08x, gladLoadGL: 0x%08x", wgl_result, gl_result);
196 FreeLibrary(opengl);
197 return NULL;
198 }
199
200#define CHECK_REQUIRED_EXTENSION(EXT) \
201 do { \
202 if (!GLAD_GL_##EXT) { \
203 U_LOG_E("%s - Required OpenGL extension GL_" #EXT " not available", __func__); \
204 FreeLibrary(opengl); \
205 return NULL; \
206 } \
207 } while (0)
208
209 CHECK_REQUIRED_EXTENSION(EXT_memory_object); // why is this failing? the gpuinfo.org tool says I have it.
210 CHECK_REQUIRED_EXTENSION(EXT_memory_object_win32);
211
212#undef CHECK_REQUIRED_EXTENSION
213
214
215 /*
216 * Checking complete, create client compositor here.
217 */
218
219 struct client_gl_win32_compositor *c = U_TYPED_CALLOC(struct client_gl_win32_compositor);
220
221 // Move the app context to the struct.
222 c->app_context = app_ctx;
223 // Same for the opengl library handle
224 c->opengl = opengl;
225
226 if (!client_gl_compositor_init( //
227 &c->base, //
228 xcn, //
229 client_gl_context_begin_locked, //
230 client_gl_context_end_locked, //
231 client_gl_memobj_swapchain_create, //
232 NULL)) { //
233 U_LOG_E("Failed to init parent GL client compositor!");
234 FreeLibrary(opengl);
235 free(c);
236 return NULL;
237 }
238
239 c->base.base.base.destroy = client_gl_win32_compositor_destroy;
240
241 return c;
242}