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 |
80 | typedef 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 | |
163 | static void X11_GL_InitExtensions(SDL_VideoDevice *_this); |
164 | |
165 | bool 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 | |
276 | SDL_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 | |
284 | void 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 | |
300 | static 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 | |
339 | static 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 | */ |
490 | static 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 |
613 | static 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 | |
638 | XVisualInfo *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 | |
714 | static int (*handler)(Display *, XErrorEvent *) = NULL; |
715 | static const char *errorHandlerOperation = NULL; |
716 | static int errorBase = 0; |
717 | static int errorCode = 0; |
718 | static 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 | |
738 | bool 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 | |
751 | SDL_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 | |
905 | bool 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 | |
943 | static int swapinterval = 0; |
944 | bool 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, ¤tInterval); |
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 | |
992 | static 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 | |
1039 | bool 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 | |
1087 | bool 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 | |
1101 | bool 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 | |