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