1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4 Copyright (C) 2021 NVIDIA Corporation
5
6 This software is provided 'as-is', without any express or implied
7 warranty. In no event will the authors be held liable for any damages
8 arising from the use of this software.
9
10 Permission is granted to anyone to use this software for any purpose,
11 including commercial applications, and to alter it and redistribute it
12 freely, subject to the following restrictions:
13
14 1. The origin of this software must not be misrepresented; you must not
15 claim that you wrote the original software. If you use this software
16 in a product, an acknowledgment in the product documentation would be
17 appreciated but is not required.
18 2. Altered source versions must be plainly marked as such, and must not be
19 misrepresented as being the original software.
20 3. This notice may not be removed or altered from any source distribution.
21*/
22#include "SDL_internal.h"
23
24#ifdef SDL_VIDEO_DRIVER_X11
25
26#include "SDL_x11video.h"
27#include "SDL_x11xsync.h"
28
29// GLX implementation of SDL OpenGL support
30
31#ifdef SDL_VIDEO_OPENGL_GLX
32#include "SDL_x11opengles.h"
33
34#if defined(SDL_PLATFORM_IRIX) || defined(SDL_PLATFORM_NETBSD) || defined(SDL_PLATFORM_OPENBSD)
35/*
36 * IRIX doesn't have a GL library versioning system.
37 * NetBSD and OpenBSD have different GL library versions depending on how
38 * the library was installed.
39 */
40#define DEFAULT_OPENGL "libGL.so"
41#elif defined(SDL_PLATFORM_MACOS)
42#define DEFAULT_OPENGL "/opt/X11/lib/libGL.1.dylib"
43#else
44#define DEFAULT_OPENGL "libGL.so.1"
45#endif
46
47#ifndef GLX_NONE_EXT
48#define GLX_NONE_EXT 0x8000
49#endif
50
51#ifndef GLX_ARB_multisample
52#define GLX_ARB_multisample
53#define GLX_SAMPLE_BUFFERS_ARB 100000
54#define GLX_SAMPLES_ARB 100001
55#endif
56
57#ifndef GLX_EXT_visual_rating
58#define GLX_EXT_visual_rating
59#define GLX_VISUAL_CAVEAT_EXT 0x20
60#define GLX_NONE_EXT 0x8000
61#define GLX_SLOW_VISUAL_EXT 0x8001
62#define GLX_NON_CONFORMANT_VISUAL_EXT 0x800D
63#endif
64
65#ifndef GLX_EXT_visual_info
66#define GLX_EXT_visual_info
67#define GLX_X_VISUAL_TYPE_EXT 0x22
68#define GLX_DIRECT_COLOR_EXT 0x8003
69#endif
70
71#ifndef GLX_ARB_create_context
72#define GLX_ARB_create_context
73#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
74#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
75#define GLX_CONTEXT_FLAGS_ARB 0x2094
76#define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001
77#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
78
79// Typedef for the GL 3.0 context creation function
80typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display *dpy,
81 GLXFBConfig config,
82 GLXContext
83 share_context,
84 Bool direct,
85 const int
86 *attrib_list);
87#endif
88
89#ifndef GLX_ARB_create_context_profile
90#define GLX_ARB_create_context_profile
91#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
92#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
93#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
94#endif
95
96#ifndef GLX_ARB_create_context_robustness
97#define GLX_ARB_create_context_robustness
98#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
99#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
100#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261
101#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252
102#endif
103
104#ifndef GLX_EXT_create_context_es2_profile
105#define GLX_EXT_create_context_es2_profile
106#ifndef GLX_CONTEXT_ES2_PROFILE_BIT_EXT
107#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000002
108#endif
109#endif
110
111#ifndef GLX_ARB_framebuffer_sRGB
112#define GLX_ARB_framebuffer_sRGB
113#ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
114#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2
115#endif
116#endif
117
118#ifndef GLX_ARB_fbconfig_float
119#define GLX_ARB_fbconfig_float
120#ifndef GLX_RGBA_FLOAT_TYPE_ARB
121#define GLX_RGBA_FLOAT_TYPE_ARB 0x20B9
122#endif
123#ifndef GLX_RGBA_FLOAT_BIT_ARB
124#define GLX_RGBA_FLOAT_BIT_ARB 0x00000004
125#endif
126#endif
127
128#ifndef GLX_ARB_create_context_no_error
129#define GLX_ARB_create_context_no_error
130#ifndef GLX_CONTEXT_OPENGL_NO_ERROR_ARB
131#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3
132#endif
133#endif
134
135#ifndef GLX_EXT_swap_control
136#define GLX_SWAP_INTERVAL_EXT 0x20F1
137#define GLX_MAX_SWAP_INTERVAL_EXT 0x20F2
138#endif
139
140#ifndef GLX_EXT_swap_control_tear
141#define GLX_LATE_SWAPS_TEAR_EXT 0x20F3
142#endif
143
144#ifndef GLX_ARB_context_flush_control
145#define GLX_ARB_context_flush_control
146#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
147#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0x0000
148#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
149#endif
150
151#define OPENGL_REQUIRES_DLOPEN
152#if defined(OPENGL_REQUIRES_DLOPEN) && defined(HAVE_DLOPEN)
153#include <dlfcn.h>
154#define GL_LoadObject(X) dlopen(X, (RTLD_NOW | RTLD_GLOBAL))
155#define GL_LoadFunction dlsym
156#define GL_UnloadObject dlclose
157#else
158#define GL_LoadObject SDL_LoadObject
159#define GL_LoadFunction SDL_LoadFunction
160#define GL_UnloadObject SDL_UnloadObject
161#endif
162
163static void X11_GL_InitExtensions(SDL_VideoDevice *_this);
164
165bool X11_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path)
166{
167 Display *display;
168 SDL_SharedObject *handle;
169
170 if (_this->gl_data) {
171 return SDL_SetError("OpenGL context already created");
172 }
173
174 // Load the OpenGL library
175 if (path == NULL) {
176 path = SDL_GetHint(SDL_HINT_OPENGL_LIBRARY);
177 }
178 if (path == NULL) {
179 path = DEFAULT_OPENGL;
180 }
181 _this->gl_config.dll_handle = GL_LoadObject(path);
182 if (!_this->gl_config.dll_handle) {
183#if defined(OPENGL_REQUIRES_DLOPEN) && defined(HAVE_DLOPEN)
184 SDL_SetError("Failed loading %s: %s", path, dlerror());
185#endif
186 return false;
187 }
188 SDL_strlcpy(_this->gl_config.driver_path, path,
189 SDL_arraysize(_this->gl_config.driver_path));
190
191 // Allocate OpenGL memory
192 _this->gl_data =
193 (struct SDL_GLDriverData *)SDL_calloc(1,
194 sizeof(struct
195 SDL_GLDriverData));
196 if (!_this->gl_data) {
197 return false;
198 }
199
200 // Load function pointers
201 handle = _this->gl_config.dll_handle;
202 _this->gl_data->glXQueryExtension =
203 (Bool(*)(Display *, int *, int *))
204 GL_LoadFunction(handle, "glXQueryExtension");
205 _this->gl_data->glXGetProcAddress =
206 (__GLXextFuncPtr (*)(const GLubyte *))
207 GL_LoadFunction(handle, "glXGetProcAddressARB");
208 _this->gl_data->glXChooseVisual =
209 (XVisualInfo * (*)(Display *, int, int *))
210 X11_GL_GetProcAddress(_this, "glXChooseVisual");
211 _this->gl_data->glXCreateContext =
212 (GLXContext(*)(Display *, XVisualInfo *, GLXContext, int))
213 X11_GL_GetProcAddress(_this, "glXCreateContext");
214 _this->gl_data->glXDestroyContext =
215 (void (*)(Display *, GLXContext))
216 X11_GL_GetProcAddress(_this, "glXDestroyContext");
217 _this->gl_data->glXMakeCurrent =
218 (int (*)(Display *, GLXDrawable, GLXContext))
219 X11_GL_GetProcAddress(_this, "glXMakeCurrent");
220 _this->gl_data->glXSwapBuffers =
221 (void (*)(Display *, GLXDrawable))
222 X11_GL_GetProcAddress(_this, "glXSwapBuffers");
223 _this->gl_data->glXQueryDrawable =
224 (void (*)(Display *, GLXDrawable, int, unsigned int *))
225 X11_GL_GetProcAddress(_this, "glXQueryDrawable");
226
227 if (!_this->gl_data->glXQueryExtension ||
228 !_this->gl_data->glXChooseVisual ||
229 !_this->gl_data->glXCreateContext ||
230 !_this->gl_data->glXDestroyContext ||
231 !_this->gl_data->glXMakeCurrent ||
232 !_this->gl_data->glXSwapBuffers) {
233 return SDL_SetError("Could not retrieve OpenGL functions");
234 }
235
236 display = _this->internal->display;
237 if (!_this->gl_data->glXQueryExtension(display, &_this->gl_data->errorBase, &_this->gl_data->eventBase)) {
238 return SDL_SetError("GLX is not supported");
239 }
240
241 _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNTESTED;
242
243 // Initialize extensions
244 /* See lengthy comment about the inc/dec in
245 ../windows/SDL_windowsopengl.c. */
246 ++_this->gl_config.driver_loaded;
247 X11_GL_InitExtensions(_this);
248 --_this->gl_config.driver_loaded;
249
250 /* If we need a GL ES context and there's no
251 * GLX_EXT_create_context_es2_profile extension, switch over to X11_GLES functions
252 */
253 if (((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) ||
254 SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) &&
255 X11_GL_UseEGL(_this)) {
256#ifdef SDL_VIDEO_OPENGL_EGL
257 X11_GL_UnloadLibrary(_this);
258 _this->GL_LoadLibrary = X11_GLES_LoadLibrary;
259 _this->GL_GetProcAddress = X11_GLES_GetProcAddress;
260 _this->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
261 _this->GL_CreateContext = X11_GLES_CreateContext;
262 _this->GL_MakeCurrent = X11_GLES_MakeCurrent;
263 _this->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
264 _this->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
265 _this->GL_SwapWindow = X11_GLES_SwapWindow;
266 _this->GL_DestroyContext = X11_GLES_DestroyContext;
267 return X11_GLES_LoadLibrary(_this, NULL);
268#else
269 return SDL_SetError("SDL not configured with EGL support");
270#endif
271 }
272
273 return true;
274}
275
276SDL_FunctionPointer X11_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc)
277{
278 if (_this->gl_data->glXGetProcAddress) {
279 return _this->gl_data->glXGetProcAddress((const GLubyte *)proc);
280 }
281 return GL_LoadFunction(_this->gl_config.dll_handle, proc);
282}
283
284void X11_GL_UnloadLibrary(SDL_VideoDevice *_this)
285{
286 /* Don't actually unload the library, since it may have registered
287 * X11 shutdown hooks, per the notes at:
288 * http://dri.sourceforge.net/doc/DRIuserguide.html
289 */
290#if 0
291 GL_UnloadObject(_this->gl_config.dll_handle);
292 _this->gl_config.dll_handle = NULL;
293#endif
294
295 // Free OpenGL memory
296 SDL_free(_this->gl_data);
297 _this->gl_data = NULL;
298}
299
300static bool HasExtension(const char *extension, const char *extensions)
301{
302 const char *start;
303 const char *where, *terminator;
304
305 if (!extensions) {
306 return false;
307 }
308
309 // Extension names should not have spaces.
310 where = SDL_strchr(extension, ' ');
311 if (where || *extension == '\0') {
312 return false;
313 }
314
315 /* It takes a bit of care to be fool-proof about parsing the
316 * OpenGL extensions string. Don't be fooled by sub-strings,
317 * etc. */
318
319 start = extensions;
320
321 for (;;) {
322 where = SDL_strstr(start, extension);
323 if (!where) {
324 break;
325 }
326
327 terminator = where + SDL_strlen(extension);
328 if (where == start || *(where - 1) == ' ') {
329 if (*terminator == ' ' || *terminator == '\0') {
330 return true;
331 }
332 }
333
334 start = terminator;
335 }
336 return false;
337}
338
339static void X11_GL_InitExtensions(SDL_VideoDevice *_this)
340{
341 Display *display = _this->internal->display;
342 const int screen = DefaultScreen(display);
343 XVisualInfo *vinfo = NULL;
344 Window w = 0;
345 GLXContext prev_ctx = 0;
346 GLXDrawable prev_drawable = 0;
347 GLXContext context = 0;
348 const char *(*glXQueryExtensionsStringFunc)(Display *, int);
349 const char *extensions;
350
351 vinfo = X11_GL_GetVisual(_this, display, screen, false);
352 if (vinfo) {
353 GLXContext (*glXGetCurrentContextFunc)(void) =
354 (GLXContext(*)(void))
355 X11_GL_GetProcAddress(_this, "glXGetCurrentContext");
356
357 GLXDrawable (*glXGetCurrentDrawableFunc)(void) =
358 (GLXDrawable(*)(void))
359 X11_GL_GetProcAddress(_this, "glXGetCurrentDrawable");
360
361 if (glXGetCurrentContextFunc && glXGetCurrentDrawableFunc) {
362 XSetWindowAttributes xattr;
363 prev_ctx = glXGetCurrentContextFunc();
364 prev_drawable = glXGetCurrentDrawableFunc();
365
366 xattr.background_pixel = 0;
367 xattr.border_pixel = 0;
368 xattr.colormap =
369 X11_XCreateColormap(display, RootWindow(display, screen),
370 vinfo->visual, AllocNone);
371 w = X11_XCreateWindow(display, RootWindow(display, screen), 0, 0,
372 32, 32, 0, vinfo->depth, InputOutput, vinfo->visual,
373 (CWBackPixel | CWBorderPixel | CWColormap), &xattr);
374
375 context = _this->gl_data->glXCreateContext(display, vinfo,
376 NULL, True);
377 if (context) {
378 _this->gl_data->glXMakeCurrent(display, w, context);
379 }
380 }
381
382 X11_XFree(vinfo);
383 }
384
385 glXQueryExtensionsStringFunc =
386 (const char *(*)(Display *, int))X11_GL_GetProcAddress(_this,
387 "glXQueryExtensionsString");
388 if (glXQueryExtensionsStringFunc) {
389 extensions = glXQueryExtensionsStringFunc(display, screen);
390 } else {
391 extensions = NULL;
392 }
393
394 // Check for GLX_EXT_swap_control(_tear)
395 _this->gl_data->HAS_GLX_EXT_swap_control_tear = false;
396 if (HasExtension("GLX_EXT_swap_control", extensions)) {
397 _this->gl_data->glXSwapIntervalEXT =
398 (void (*)(Display *, GLXDrawable, int))
399 X11_GL_GetProcAddress(_this, "glXSwapIntervalEXT");
400 if (HasExtension("GLX_EXT_swap_control_tear", extensions)) {
401 _this->gl_data->HAS_GLX_EXT_swap_control_tear = true;
402 }
403 }
404
405 // Check for GLX_MESA_swap_control
406 if (HasExtension("GLX_MESA_swap_control", extensions)) {
407 _this->gl_data->glXSwapIntervalMESA =
408 (int (*)(int))X11_GL_GetProcAddress(_this, "glXSwapIntervalMESA");
409 _this->gl_data->glXGetSwapIntervalMESA =
410 (int (*)(void))X11_GL_GetProcAddress(_this,
411 "glXGetSwapIntervalMESA");
412 }
413
414 // Check for GLX_SGI_swap_control
415 if (HasExtension("GLX_SGI_swap_control", extensions)) {
416 _this->gl_data->glXSwapIntervalSGI =
417 (int (*)(int))X11_GL_GetProcAddress(_this, "glXSwapIntervalSGI");
418 }
419
420 // Check for GLX_ARB_create_context
421 if (HasExtension("GLX_ARB_create_context", extensions)) {
422 _this->gl_data->glXCreateContextAttribsARB =
423 (GLXContext(*)(Display *, GLXFBConfig, GLXContext, Bool, const int *))
424 X11_GL_GetProcAddress(_this, "glXCreateContextAttribsARB");
425 _this->gl_data->glXChooseFBConfig =
426 (GLXFBConfig * (*)(Display *, int, const int *, int *))
427 X11_GL_GetProcAddress(_this, "glXChooseFBConfig");
428 _this->gl_data->glXGetVisualFromFBConfig =
429 (XVisualInfo * (*)(Display *, GLXFBConfig))
430 X11_GL_GetProcAddress(_this, "glXGetVisualFromFBConfig");
431 }
432
433 // Check for GLX_EXT_visual_rating
434 if (HasExtension("GLX_EXT_visual_rating", extensions)) {
435 _this->gl_data->HAS_GLX_EXT_visual_rating = true;
436 }
437
438 // Check for GLX_EXT_visual_info
439 if (HasExtension("GLX_EXT_visual_info", extensions)) {
440 _this->gl_data->HAS_GLX_EXT_visual_info = true;
441 }
442
443 // Check for GLX_EXT_create_context_es2_profile
444 if (HasExtension("GLX_EXT_create_context_es2_profile", extensions)) {
445 // this wants to call glGetString(), so it needs a context.
446 // !!! FIXME: it would be nice not to make a context here though!
447 if (context) {
448 SDL_GL_DeduceMaxSupportedESProfile(
449 &_this->gl_data->es_profile_max_supported_version.major,
450 &_this->gl_data->es_profile_max_supported_version.minor);
451 }
452 }
453
454 // Check for GLX_ARB_context_flush_control
455 if (HasExtension("GLX_ARB_context_flush_control", extensions)) {
456 _this->gl_data->HAS_GLX_ARB_context_flush_control = true;
457 }
458
459 // Check for GLX_ARB_create_context_robustness
460 if (HasExtension("GLX_ARB_create_context_robustness", extensions)) {
461 _this->gl_data->HAS_GLX_ARB_create_context_robustness = true;
462 }
463
464 // Check for GLX_ARB_create_context_no_error
465 if (HasExtension("GLX_ARB_create_context_no_error", extensions)) {
466 _this->gl_data->HAS_GLX_ARB_create_context_no_error = true;
467 }
468
469 if (context) {
470 _this->gl_data->glXMakeCurrent(display, None, NULL);
471 _this->gl_data->glXDestroyContext(display, context);
472 if (prev_ctx && prev_drawable) {
473 _this->gl_data->glXMakeCurrent(display, prev_drawable, prev_ctx);
474 }
475 }
476
477 if (w) {
478 X11_XDestroyWindow(display, w);
479 }
480 X11_PumpEvents(_this);
481}
482
483/* glXChooseVisual and glXChooseFBConfig have some small differences in
484 * the attribute encoding, it can be chosen with the for_FBConfig parameter.
485 * Some targets fail if you use GLX_X_VISUAL_TYPE_EXT/GLX_DIRECT_COLOR_EXT,
486 * so it gets specified last if used and is pointed to by *_pvistypeattr.
487 * In case of failure, if that pointer is not NULL, set that pointer to None
488 * and try again.
489 */
490static int X11_GL_GetAttributes(SDL_VideoDevice *_this, Display *display, int screen, int *attribs, int size, Bool for_FBConfig, int **_pvistypeattr, bool transparent)
491{
492 int i = 0;
493 const int MAX_ATTRIBUTES = 64;
494 int *pvistypeattr = NULL;
495
496 // assert buffer is large enough to hold all SDL attributes.
497 SDL_assert(size >= MAX_ATTRIBUTES);
498
499 // Setup our GLX attributes according to the gl_config.
500 if (for_FBConfig) {
501 attribs[i++] = GLX_RENDER_TYPE;
502 if (_this->gl_config.floatbuffers) {
503 attribs[i++] = GLX_RGBA_FLOAT_BIT_ARB;
504 } else {
505 attribs[i++] = GLX_RGBA_BIT;
506 }
507 } else {
508 attribs[i++] = GLX_RGBA;
509 }
510 attribs[i++] = GLX_RED_SIZE;
511 attribs[i++] = _this->gl_config.red_size;
512 attribs[i++] = GLX_GREEN_SIZE;
513 attribs[i++] = _this->gl_config.green_size;
514 attribs[i++] = GLX_BLUE_SIZE;
515 attribs[i++] = _this->gl_config.blue_size;
516
517 if (_this->gl_config.alpha_size) {
518 attribs[i++] = GLX_ALPHA_SIZE;
519 attribs[i++] = _this->gl_config.alpha_size;
520 }
521
522 if (_this->gl_config.double_buffer) {
523 attribs[i++] = GLX_DOUBLEBUFFER;
524 if (for_FBConfig) {
525 attribs[i++] = True;
526 }
527 }
528
529 attribs[i++] = GLX_DEPTH_SIZE;
530 attribs[i++] = _this->gl_config.depth_size;
531
532 if (_this->gl_config.stencil_size) {
533 attribs[i++] = GLX_STENCIL_SIZE;
534 attribs[i++] = _this->gl_config.stencil_size;
535 }
536
537 if (_this->gl_config.accum_red_size) {
538 attribs[i++] = GLX_ACCUM_RED_SIZE;
539 attribs[i++] = _this->gl_config.accum_red_size;
540 }
541
542 if (_this->gl_config.accum_green_size) {
543 attribs[i++] = GLX_ACCUM_GREEN_SIZE;
544 attribs[i++] = _this->gl_config.accum_green_size;
545 }
546
547 if (_this->gl_config.accum_blue_size) {
548 attribs[i++] = GLX_ACCUM_BLUE_SIZE;
549 attribs[i++] = _this->gl_config.accum_blue_size;
550 }
551
552 if (_this->gl_config.accum_alpha_size) {
553 attribs[i++] = GLX_ACCUM_ALPHA_SIZE;
554 attribs[i++] = _this->gl_config.accum_alpha_size;
555 }
556
557 if (_this->gl_config.stereo) {
558 attribs[i++] = GLX_STEREO;
559 if (for_FBConfig) {
560 attribs[i++] = True;
561 }
562 }
563
564 if (_this->gl_config.multisamplebuffers) {
565 attribs[i++] = GLX_SAMPLE_BUFFERS_ARB;
566 attribs[i++] = _this->gl_config.multisamplebuffers;
567 }
568
569 if (_this->gl_config.multisamplesamples) {
570 attribs[i++] = GLX_SAMPLES_ARB;
571 attribs[i++] = _this->gl_config.multisamplesamples;
572 }
573
574 if (_this->gl_config.floatbuffers) {
575 attribs[i++] = GLX_RENDER_TYPE;
576 attribs[i++] = GLX_RGBA_FLOAT_TYPE_ARB;
577 }
578
579 if (_this->gl_config.framebuffer_srgb_capable) {
580 attribs[i++] = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
581 attribs[i++] = True; // always needed, for_FBConfig or not!
582 }
583
584 if (_this->gl_config.accelerated >= 0 &&
585 _this->gl_data->HAS_GLX_EXT_visual_rating) {
586 attribs[i++] = GLX_VISUAL_CAVEAT_EXT;
587 attribs[i++] = _this->gl_config.accelerated ? GLX_NONE_EXT : GLX_SLOW_VISUAL_EXT;
588 }
589
590 // Un-wanted when we request a transparent buffer
591 if (!transparent) {
592 /* If we're supposed to use DirectColor visuals, and we've got the
593 EXT_visual_info extension, then add GLX_X_VISUAL_TYPE_EXT. */
594 if (X11_UseDirectColorVisuals() && _this->gl_data->HAS_GLX_EXT_visual_info) {
595 pvistypeattr = &attribs[i];
596 attribs[i++] = GLX_X_VISUAL_TYPE_EXT;
597 attribs[i++] = GLX_DIRECT_COLOR_EXT;
598 }
599 }
600
601 attribs[i++] = None;
602
603 SDL_assert(i <= MAX_ATTRIBUTES);
604
605 if (_pvistypeattr) {
606 *_pvistypeattr = pvistypeattr;
607 }
608
609 return i;
610}
611
612//get the first transparent Visual
613static XVisualInfo* X11_GL_GetTransparentVisualInfo(Display *display, int screen)
614{
615 XVisualInfo* visualinfo = NULL;
616 XVisualInfo vi_in;
617 int out_count = 0;
618
619 vi_in.screen = screen;
620 visualinfo = X11_XGetVisualInfo(display, VisualScreenMask, &vi_in, &out_count);
621 if (visualinfo != NULL) {
622 int i = 0;
623 for (i = 0; i < out_count; i++) {
624 XVisualInfo* v = &visualinfo[i];
625 Uint32 format = X11_GetPixelFormatFromVisualInfo(display, v);
626 if (SDL_ISPIXELFORMAT_ALPHA(format)) {
627 vi_in.screen = screen;
628 vi_in.visualid = v->visualid;
629 X11_XFree(visualinfo);
630 visualinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &vi_in, &out_count);
631 break;
632 }
633 }
634 }
635 return visualinfo;
636}
637
638XVisualInfo *X11_GL_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent)
639{
640 // 64 seems nice.
641 int attribs[64];
642 XVisualInfo *vinfo = NULL;
643 int *pvistypeattr = NULL;
644
645 if (!_this->gl_data) {
646 // The OpenGL library wasn't loaded, SDL_GetError() should have info
647 return NULL;
648 }
649
650 if (_this->gl_data->glXChooseFBConfig &&
651 _this->gl_data->glXGetVisualFromFBConfig) {
652 GLXFBConfig *framebuffer_config = NULL;
653 int fbcount = 0;
654
655 X11_GL_GetAttributes(_this, display, screen, attribs, 64, true, &pvistypeattr, transparent);
656 framebuffer_config = _this->gl_data->glXChooseFBConfig(display, screen, attribs, &fbcount);
657 if (!framebuffer_config && (pvistypeattr != NULL)) {
658 *pvistypeattr = None;
659 framebuffer_config = _this->gl_data->glXChooseFBConfig(display, screen, attribs, &fbcount);
660 }
661
662 if (transparent) {
663 // Return the first transparent Visual
664 int i;
665 for (i = 0; i < fbcount; i++) {
666 Uint32 format;
667 vinfo = _this->gl_data->glXGetVisualFromFBConfig(display, framebuffer_config[i]);
668 format = X11_GetPixelFormatFromVisualInfo(display, vinfo);
669 if (SDL_ISPIXELFORMAT_ALPHA(format)) { // found!
670 X11_XFree(framebuffer_config);
671 framebuffer_config = NULL;
672 break;
673 }
674 X11_XFree(vinfo);
675 vinfo = NULL;
676 }
677 }
678
679 if (framebuffer_config) {
680 vinfo = _this->gl_data->glXGetVisualFromFBConfig(display, framebuffer_config[0]);
681 }
682
683 X11_XFree(framebuffer_config);
684 }
685
686 if (!vinfo) {
687 X11_GL_GetAttributes(_this, display, screen, attribs, 64, false, &pvistypeattr, transparent);
688 vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
689
690 if (!vinfo && (pvistypeattr != NULL)) {
691 *pvistypeattr = None;
692 vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
693 }
694 }
695
696 if (transparent && vinfo) {
697 Uint32 format = X11_GetPixelFormatFromVisualInfo(display, vinfo);
698 if (!SDL_ISPIXELFORMAT_ALPHA(format)) {
699 // not transparent!
700 XVisualInfo* visualinfo = X11_GL_GetTransparentVisualInfo(display, screen);
701 if (visualinfo != NULL) {
702 X11_XFree(vinfo);
703 vinfo = visualinfo;
704 }
705 }
706 }
707
708 if (!vinfo) {
709 SDL_SetError("Couldn't find matching GLX visual");
710 }
711 return vinfo;
712}
713
714static int (*handler)(Display *, XErrorEvent *) = NULL;
715static const char *errorHandlerOperation = NULL;
716static int errorBase = 0;
717static int errorCode = 0;
718static int X11_GL_ErrorHandler(Display *d, XErrorEvent *e)
719{
720 char *x11_error = NULL;
721 char x11_error_locale[256];
722
723 errorCode = e->error_code;
724 if (X11_XGetErrorText(d, errorCode, x11_error_locale, sizeof(x11_error_locale)) == Success) {
725 x11_error = SDL_iconv_string("UTF-8", "", x11_error_locale, SDL_strlen(x11_error_locale) + 1);
726 }
727
728 if (x11_error) {
729 SDL_SetError("Could not %s: %s", errorHandlerOperation, x11_error);
730 SDL_free(x11_error);
731 } else {
732 SDL_SetError("Could not %s: %i (Base %i)", errorHandlerOperation, errorCode, errorBase);
733 }
734
735 return 0;
736}
737
738bool X11_GL_UseEGL(SDL_VideoDevice *_this)
739{
740 SDL_assert(_this->gl_data != NULL);
741 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) {
742 // use of EGL has been requested, even for desktop GL
743 return true;
744 }
745
746 SDL_assert(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES);
747 return (SDL_GetHintBoolean(SDL_HINT_OPENGL_ES_DRIVER, false) || _this->gl_config.major_version == 1 // No GLX extension for OpenGL ES 1.x profiles.
748 || _this->gl_config.major_version > _this->gl_data->es_profile_max_supported_version.major || (_this->gl_config.major_version == _this->gl_data->es_profile_max_supported_version.major && _this->gl_config.minor_version > _this->gl_data->es_profile_max_supported_version.minor));
749}
750
751SDL_GLContext X11_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
752{
753 SDL_WindowData *data = window->internal;
754 Display *display = data->videodata->display;
755 int screen = SDL_GetDisplayDriverDataForWindow(window)->screen;
756 XWindowAttributes xattr;
757 XVisualInfo v, *vinfo;
758 int n;
759 SDL_GLContext context = NULL;
760 GLXContext share_context;
761 const int transparent = (window->flags & SDL_WINDOW_TRANSPARENT) ? true : false;
762
763 if (_this->gl_config.share_with_current_context) {
764 share_context = (GLXContext)SDL_GL_GetCurrentContext();
765 } else {
766 share_context = NULL;
767 }
768
769 // We do this to create a clean separation between X and GLX errors.
770 X11_XSync(display, False);
771 errorHandlerOperation = "create GL context";
772 errorBase = _this->gl_data->errorBase;
773 errorCode = Success;
774 handler = X11_XSetErrorHandler(X11_GL_ErrorHandler);
775 X11_XGetWindowAttributes(display, data->xwindow, &xattr);
776 v.screen = screen;
777 v.visualid = X11_XVisualIDFromVisual(xattr.visual);
778 vinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &v, &n);
779 if (vinfo) {
780 if (_this->gl_config.major_version < 3 &&
781 _this->gl_config.profile_mask == 0 &&
782 _this->gl_config.flags == 0 && !transparent) {
783 // Create legacy context
784 context =
785 (SDL_GLContext)_this->gl_data->glXCreateContext(display, vinfo, share_context, True);
786 } else {
787 // max 14 attributes plus terminator
788 int attribs[15] = {
789 GLX_CONTEXT_MAJOR_VERSION_ARB,
790 _this->gl_config.major_version,
791 GLX_CONTEXT_MINOR_VERSION_ARB,
792 _this->gl_config.minor_version,
793 0
794 };
795 int iattr = 4;
796
797 // SDL profile bits match GLX profile bits
798 if (_this->gl_config.profile_mask != 0) {
799 attribs[iattr++] = GLX_CONTEXT_PROFILE_MASK_ARB;
800 attribs[iattr++] = _this->gl_config.profile_mask;
801 }
802
803 // SDL flags match GLX flags
804 if (_this->gl_config.flags != 0) {
805 attribs[iattr++] = GLX_CONTEXT_FLAGS_ARB;
806 attribs[iattr++] = _this->gl_config.flags;
807 }
808
809 // only set if glx extension is available and not the default setting
810 if ((_this->gl_data->HAS_GLX_ARB_context_flush_control) && (_this->gl_config.release_behavior == 0)) {
811 attribs[iattr++] = GLX_CONTEXT_RELEASE_BEHAVIOR_ARB;
812 attribs[iattr++] =
813 _this->gl_config.release_behavior ? GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB : GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB;
814 }
815
816 // only set if glx extension is available and not the default setting
817 if ((_this->gl_data->HAS_GLX_ARB_create_context_robustness) && (_this->gl_config.reset_notification != 0)) {
818 attribs[iattr++] = GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB;
819 attribs[iattr++] =
820 _this->gl_config.reset_notification ? GLX_LOSE_CONTEXT_ON_RESET_ARB : GLX_NO_RESET_NOTIFICATION_ARB;
821 }
822
823 // only set if glx extension is available and not the default setting
824 if ((_this->gl_data->HAS_GLX_ARB_create_context_no_error) && (_this->gl_config.no_error != 0)) {
825 attribs[iattr++] = GLX_CONTEXT_OPENGL_NO_ERROR_ARB;
826 attribs[iattr++] = _this->gl_config.no_error;
827 }
828
829 attribs[iattr++] = 0;
830
831 // Get a pointer to the context creation function for GL 3.0
832 if (!_this->gl_data->glXCreateContextAttribsARB) {
833 SDL_SetError("OpenGL 3.0 and later are not supported by this system");
834 } else {
835 int glxAttribs[64];
836
837 // Create a GL 3.x context
838 GLXFBConfig *framebuffer_config = NULL;
839 int fbcount = 0;
840 int *pvistypeattr = NULL;
841
842 X11_GL_GetAttributes(_this, display, screen, glxAttribs, 64, true, &pvistypeattr, transparent);
843
844 if (_this->gl_data->glXChooseFBConfig) {
845 framebuffer_config = _this->gl_data->glXChooseFBConfig(display,
846 DefaultScreen(display), glxAttribs,
847 &fbcount);
848
849 if (!framebuffer_config && (pvistypeattr != NULL)) {
850 *pvistypeattr = None;
851 framebuffer_config = _this->gl_data->glXChooseFBConfig(display,
852 DefaultScreen(display), glxAttribs,
853 &fbcount);
854 }
855
856 if (transparent && (framebuffer_config != NULL)) {
857 int i;
858 for (i = 0; i < fbcount; i++) {
859 XVisualInfo* vinfo_temp = _this->gl_data->glXGetVisualFromFBConfig(display, framebuffer_config[i]);
860 if ( vinfo_temp != NULL) {
861 Uint32 format = X11_GetPixelFormatFromVisualInfo(display, vinfo_temp);
862 if (SDL_ISPIXELFORMAT_ALPHA(format)) {
863 // found!
864 context = (SDL_GLContext)_this->gl_data->glXCreateContextAttribsARB(display,
865 framebuffer_config[i],
866 share_context, True, attribs);
867 X11_XFree(framebuffer_config);
868 framebuffer_config = NULL;
869 X11_XFree(vinfo_temp);
870 break;
871 }
872 X11_XFree(vinfo_temp);
873 }
874 }
875 }
876 if (framebuffer_config) {
877 context = (SDL_GLContext)_this->gl_data->glXCreateContextAttribsARB(display,
878 framebuffer_config[0],
879 share_context, True, attribs);
880 X11_XFree(framebuffer_config);
881 }
882 }
883 }
884 }
885 X11_XFree(vinfo);
886 }
887 X11_XSync(display, False);
888 X11_XSetErrorHandler(handler);
889
890 if (!context) {
891 if (errorCode == Success) {
892 SDL_SetError("Could not create GL context");
893 }
894 return NULL;
895 }
896
897 if (!X11_GL_MakeCurrent(_this, window, context)) {
898 X11_GL_DestroyContext(_this, context);
899 return NULL;
900 }
901
902 return context;
903}
904
905bool X11_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
906{
907 Display *display = _this->internal->display;
908 Window drawable =
909 (context ? window->internal->xwindow : None);
910 GLXContext glx_context = (GLXContext)context;
911 int rc;
912
913 if (!_this->gl_data) {
914 return SDL_SetError("OpenGL not initialized");
915 }
916
917 // We do this to create a clean separation between X and GLX errors.
918 X11_XSync(display, False);
919 errorHandlerOperation = "make GL context current";
920 errorBase = _this->gl_data->errorBase;
921 errorCode = Success;
922 handler = X11_XSetErrorHandler(X11_GL_ErrorHandler);
923 rc = _this->gl_data->glXMakeCurrent(display, drawable, glx_context);
924 X11_XSetErrorHandler(handler);
925
926 if (errorCode != Success) { // uhoh, an X error was thrown!
927 return false; // the error handler called SDL_SetError() already.
928 } else if (!rc) { // glXMakeCurrent() failed without throwing an X error
929 return SDL_SetError("Unable to make GL context current");
930 }
931
932 return true;
933}
934
935/*
936 0 is a valid argument to glXSwapInterval(MESA|EXT) and setting it to 0
937 will undo the effect of a previous call with a value that is greater
938 than zero (or at least that is what the docs say). OTOH, 0 is an invalid
939 argument to glXSwapIntervalSGI and it returns an error if you call it
940 with 0 as an argument.
941*/
942
943static int swapinterval = 0;
944bool X11_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval)
945{
946 bool result = false;
947
948 if ((interval < 0) && (!_this->gl_data->HAS_GLX_EXT_swap_control_tear)) {
949 return SDL_SetError("Negative swap interval unsupported in this GL");
950 } else if (_this->gl_data->glXSwapIntervalEXT) {
951 Display *display = _this->internal->display;
952 const SDL_WindowData *windowdata = SDL_GL_GetCurrentWindow()->internal;
953
954 Window drawable = windowdata->xwindow;
955
956 /*
957 * This is a workaround for a bug in NVIDIA drivers. Bug has been reported
958 * and will be fixed in a future release (probably 319.xx).
959 *
960 * There's a bug where glXSetSwapIntervalEXT ignores updates because
961 * it has the wrong value cached. To work around it, we just run a no-op
962 * update to the current value.
963 */
964 int currentInterval = 0;
965 X11_GL_GetSwapInterval(_this, &currentInterval);
966 _this->gl_data->glXSwapIntervalEXT(display, drawable, currentInterval);
967 _this->gl_data->glXSwapIntervalEXT(display, drawable, interval);
968 result = true;
969 swapinterval = interval;
970 } else if (_this->gl_data->glXSwapIntervalMESA) {
971 const int rc = _this->gl_data->glXSwapIntervalMESA(interval);
972 if (rc == 0) {
973 swapinterval = interval;
974 result = true;
975 } else {
976 result = SDL_SetError("glXSwapIntervalMESA failed");
977 }
978 } else if (_this->gl_data->glXSwapIntervalSGI) {
979 const int rc = _this->gl_data->glXSwapIntervalSGI(interval);
980 if (rc == 0) {
981 swapinterval = interval;
982 result = true;
983 } else {
984 result = SDL_SetError("glXSwapIntervalSGI failed");
985 }
986 } else {
987 return SDL_Unsupported();
988 }
989 return result;
990}
991
992static SDL_GLSwapIntervalTearBehavior CheckSwapIntervalTearBehavior(SDL_VideoDevice *_this, Window drawable, unsigned int current_val, unsigned int current_allow_late)
993{
994 /* Mesa and Nvidia interpret GLX_EXT_swap_control_tear differently, as of this writing, so
995 figure out which behavior we have.
996 Technical details: https://github.com/libsdl-org/SDL/issues/8004#issuecomment-1819603282 */
997 if (_this->gl_data->swap_interval_tear_behavior == SDL_SWAPINTERVALTEAR_UNTESTED) {
998 if (!_this->gl_data->HAS_GLX_EXT_swap_control_tear) {
999 _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNKNOWN;
1000 } else {
1001 Display *display = _this->internal->display;
1002 unsigned int allow_late_swap_tearing = 22;
1003 int original_val = (int) current_val;
1004
1005 /*
1006 * This is a workaround for a bug in NVIDIA drivers. Bug has been reported
1007 * and will be fixed in a future release (probably 319.xx).
1008 *
1009 * There's a bug where glXSetSwapIntervalEXT ignores updates because
1010 * it has the wrong value cached. To work around it, we just run a no-op
1011 * update to the current value.
1012 */
1013 _this->gl_data->glXSwapIntervalEXT(display, drawable, current_val);
1014
1015 // set it to no swap interval and see how it affects GLX_LATE_SWAPS_TEAR_EXT...
1016 _this->gl_data->glXSwapIntervalEXT(display, drawable, 0);
1017 _this->gl_data->glXQueryDrawable(display, drawable, GLX_LATE_SWAPS_TEAR_EXT, &allow_late_swap_tearing);
1018
1019 if (allow_late_swap_tearing == 0) { // GLX_LATE_SWAPS_TEAR_EXT says whether late swapping is currently in use
1020 _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_NVIDIA;
1021 if (current_allow_late) {
1022 original_val = -original_val;
1023 }
1024 } else if (allow_late_swap_tearing == 1) { // GLX_LATE_SWAPS_TEAR_EXT says whether the Drawable can use late swapping at all
1025 _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_MESA;
1026 } else { // unexpected outcome!
1027 _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNKNOWN;
1028 }
1029
1030 // set us back to what it was originally...
1031 _this->gl_data->glXSwapIntervalEXT(display, drawable, original_val);
1032 }
1033 }
1034
1035 return _this->gl_data->swap_interval_tear_behavior;
1036}
1037
1038
1039bool X11_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval)
1040{
1041 if (_this->gl_data->glXSwapIntervalEXT) {
1042 Display *display = _this->internal->display;
1043 const SDL_WindowData *windowdata = SDL_GL_GetCurrentWindow()->internal;
1044 Window drawable = windowdata->xwindow;
1045 unsigned int allow_late_swap_tearing = 0;
1046 unsigned int val = 0;
1047
1048 if (_this->gl_data->HAS_GLX_EXT_swap_control_tear) {
1049 allow_late_swap_tearing = 22; // set this to nonsense.
1050 _this->gl_data->glXQueryDrawable(display, drawable,
1051 GLX_LATE_SWAPS_TEAR_EXT,
1052 &allow_late_swap_tearing);
1053 }
1054
1055 _this->gl_data->glXQueryDrawable(display, drawable,
1056 GLX_SWAP_INTERVAL_EXT, &val);
1057
1058 *interval = (int)val;
1059
1060 switch (CheckSwapIntervalTearBehavior(_this, drawable, val, allow_late_swap_tearing)) {
1061 case SDL_SWAPINTERVALTEAR_MESA:
1062 *interval = (int)val; // unsigned int cast to signed that generates negative value if necessary.
1063 break;
1064
1065 case SDL_SWAPINTERVALTEAR_NVIDIA:
1066 default:
1067 if ((allow_late_swap_tearing) && (val > 0)) {
1068 *interval = -((int)val);
1069 }
1070 break;
1071 }
1072
1073 return true;
1074 } else if (_this->gl_data->glXGetSwapIntervalMESA) {
1075 int val = _this->gl_data->glXGetSwapIntervalMESA();
1076 if (val == GLX_BAD_CONTEXT) {
1077 return SDL_SetError("GLX_BAD_CONTEXT");
1078 }
1079 *interval = val;
1080 return true;
1081 } else {
1082 *interval = swapinterval;
1083 return true;
1084 }
1085}
1086
1087bool X11_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
1088{
1089 SDL_WindowData *data = window->internal;
1090 Display *display = data->videodata->display;
1091
1092 _this->gl_data->glXSwapBuffers(display, data->xwindow);
1093
1094#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
1095 X11_HandlePresent(data->window);
1096#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
1097
1098 return true;
1099}
1100
1101bool X11_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context)
1102{
1103 Display *display = _this->internal->display;
1104 GLXContext glx_context = (GLXContext)context;
1105
1106 if (!_this->gl_data) {
1107 return true;
1108 }
1109 _this->gl_data->glXDestroyContext(display, glx_context);
1110 X11_XSync(display, False);
1111 return true;
1112}
1113
1114#endif // SDL_VIDEO_OPENGL_GLX
1115
1116#endif // SDL_VIDEO_DRIVER_X11
1117