| | |
| | | /* |
| | | Simple DirectMedia Layer |
| | | Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> |
| | | Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> |
| | | |
| | | This software is provided 'as-is', without any express or implied |
| | | warranty. In no event will the authors be held liable for any damages |
| | |
| | | |
| | | #include "SDL_x11video.h" |
| | | #include "SDL_assert.h" |
| | | #include "SDL_hints.h" |
| | | |
| | | /* GLX implementation of SDL OpenGL support */ |
| | | |
| | |
| | | #endif |
| | | #endif |
| | | |
| | | #ifndef GLX_ARB_create_context_no_error |
| | | #define GLX_ARB_create_context_no_error |
| | | #ifndef GLX_CONTEXT_OPENGL_NO_ERROR_ARB |
| | | #define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3 |
| | | #endif |
| | | #endif |
| | | |
| | | #ifndef GLX_EXT_swap_control |
| | | #define GLX_SWAP_INTERVAL_EXT 0x20F1 |
| | | #define GLX_MAX_SWAP_INTERVAL_EXT 0x20F2 |
| | |
| | | #endif |
| | | |
| | | static void X11_GL_InitExtensions(_THIS); |
| | | |
| | | |
| | | int |
| | | X11_GL_LoadLibrary(_THIS, const char *path) |
| | |
| | | } |
| | | |
| | | /* Initialize extensions */ |
| | | /* See lengthy comment about the inc/dec in |
| | | ../windows/SDL_windowsopengl.c. */ |
| | | ++_this->gl_config.driver_loaded; |
| | | X11_GL_InitExtensions(_this); |
| | | --_this->gl_config.driver_loaded; |
| | | |
| | | /* If we need a GL ES context and there's no |
| | | * GLX_EXT_create_context_es2_profile extension, switch over to X11_GLES functions |
| | | */ |
| | | if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES && |
| | | ! _this->gl_data->HAS_GLX_EXT_create_context_es2_profile ) { |
| | | X11_GL_UseEGL(_this) ) { |
| | | #if SDL_VIDEO_OPENGL_EGL |
| | | X11_GL_UnloadLibrary(_this); |
| | | /* Better avoid conflicts! */ |
| | |
| | | { |
| | | Display *display = ((SDL_VideoData *) _this->driverdata)->display; |
| | | const int screen = DefaultScreen(display); |
| | | XVisualInfo *vinfo = NULL; |
| | | Window w = 0; |
| | | GLXContext prev_ctx = 0; |
| | | GLXDrawable prev_drawable = 0; |
| | | GLXContext context = 0; |
| | | const char *(*glXQueryExtensionsStringFunc) (Display *, int); |
| | | const char *extensions; |
| | | |
| | | vinfo = X11_GL_GetVisual(_this, display, screen); |
| | | if (vinfo) { |
| | | GLXContext (*glXGetCurrentContextFunc) (void) = |
| | | (GLXContext(*)(void)) |
| | | X11_GL_GetProcAddress(_this, "glXGetCurrentContext"); |
| | | |
| | | GLXDrawable (*glXGetCurrentDrawableFunc) (void) = |
| | | (GLXDrawable(*)(void)) |
| | | X11_GL_GetProcAddress(_this, "glXGetCurrentDrawable"); |
| | | |
| | | if (glXGetCurrentContextFunc && glXGetCurrentDrawableFunc) { |
| | | XSetWindowAttributes xattr; |
| | | prev_ctx = glXGetCurrentContextFunc(); |
| | | prev_drawable = glXGetCurrentDrawableFunc(); |
| | | |
| | | xattr.background_pixel = 0; |
| | | xattr.border_pixel = 0; |
| | | xattr.colormap = |
| | | X11_XCreateColormap(display, RootWindow(display, screen), |
| | | vinfo->visual, AllocNone); |
| | | w = X11_XCreateWindow(display, RootWindow(display, screen), 0, 0, |
| | | 32, 32, 0, vinfo->depth, InputOutput, vinfo->visual, |
| | | (CWBackPixel | CWBorderPixel | CWColormap), &xattr); |
| | | |
| | | context = _this->gl_data->glXCreateContext(display, vinfo, |
| | | NULL, True); |
| | | if (context) { |
| | | _this->gl_data->glXMakeCurrent(display, w, context); |
| | | } |
| | | } |
| | | |
| | | X11_XFree(vinfo); |
| | | } |
| | | |
| | | glXQueryExtensionsStringFunc = |
| | | (const char *(*)(Display *, int)) X11_GL_GetProcAddress(_this, |
| | |
| | | |
| | | /* Check for GLX_EXT_create_context_es2_profile */ |
| | | if (HasExtension("GLX_EXT_create_context_es2_profile", extensions)) { |
| | | _this->gl_data->HAS_GLX_EXT_create_context_es2_profile = SDL_TRUE; |
| | | /* this wants to call glGetString(), so it needs a context. */ |
| | | /* !!! FIXME: it would be nice not to make a context here though! */ |
| | | if (context) { |
| | | SDL_GL_DeduceMaxSupportedESProfile( |
| | | &_this->gl_data->es_profile_max_supported_version.major, |
| | | &_this->gl_data->es_profile_max_supported_version.minor |
| | | ); |
| | | } |
| | | } |
| | | |
| | | /* Check for GLX_ARB_context_flush_control */ |
| | | if (HasExtension("GLX_ARB_context_flush_control", extensions)) { |
| | | _this->gl_data->HAS_GLX_ARB_context_flush_control = SDL_TRUE; |
| | | } |
| | | |
| | | /* Check for GLX_ARB_create_context_robustness */ |
| | | if (HasExtension("GLX_ARB_create_context_robustness", extensions)) { |
| | | _this->gl_data->HAS_GLX_ARB_create_context_robustness = SDL_TRUE; |
| | | } |
| | | |
| | | /* Check for GLX_ARB_create_context_no_error */ |
| | | if (HasExtension("GLX_ARB_create_context_no_error", extensions)) { |
| | | _this->gl_data->HAS_GLX_ARB_create_context_no_error = SDL_TRUE; |
| | | } |
| | | |
| | | if (context) { |
| | | _this->gl_data->glXMakeCurrent(display, None, NULL); |
| | | _this->gl_data->glXDestroyContext(display, context); |
| | | if (prev_ctx && prev_drawable) { |
| | | _this->gl_data->glXMakeCurrent(display, prev_drawable, prev_ctx); |
| | | } |
| | | } |
| | | |
| | | if (w) { |
| | | X11_XDestroyWindow(display, w); |
| | | } |
| | | X11_PumpEvents(_this); |
| | | } |
| | | |
| | | /* glXChooseVisual and glXChooseFBConfig have some small differences in |
| | | * the attribute encoding, it can be chosen with the for_FBConfig parameter. |
| | | * Some targets fail if you use GLX_X_VISUAL_TYPE_EXT/GLX_DIRECT_COLOR_EXT, |
| | | * so it gets specified last if used and is pointed to by *_pvistypeattr. |
| | | * In case of failure, if that pointer is not NULL, set that pointer to None |
| | | * and try again. |
| | | */ |
| | | static int |
| | | X11_GL_GetAttributes(_THIS, Display * display, int screen, int * attribs, int size, Bool for_FBConfig) |
| | | X11_GL_GetAttributes(_THIS, Display * display, int screen, int * attribs, int size, Bool for_FBConfig, int **_pvistypeattr) |
| | | { |
| | | int i = 0; |
| | | const int MAX_ATTRIBUTES = 64; |
| | | int *pvistypeattr = NULL; |
| | | |
| | | /* assert buffer is large enough to hold all SDL attributes. */ |
| | | SDL_assert(size >= MAX_ATTRIBUTES); |
| | |
| | | EXT_visual_info extension, then add GLX_X_VISUAL_TYPE_EXT. */ |
| | | if (X11_UseDirectColorVisuals() && |
| | | _this->gl_data->HAS_GLX_EXT_visual_info) { |
| | | pvistypeattr = &attribs[i]; |
| | | attribs[i++] = GLX_X_VISUAL_TYPE_EXT; |
| | | attribs[i++] = GLX_DIRECT_COLOR_EXT; |
| | | } |
| | |
| | | attribs[i++] = None; |
| | | |
| | | SDL_assert(i <= MAX_ATTRIBUTES); |
| | | |
| | | if (_pvistypeattr) { |
| | | *_pvistypeattr = pvistypeattr; |
| | | } |
| | | |
| | | return i; |
| | | } |
| | |
| | | /* 64 seems nice. */ |
| | | int attribs[64]; |
| | | XVisualInfo *vinfo; |
| | | int *pvistypeattr = NULL; |
| | | |
| | | if (!_this->gl_data) { |
| | | /* The OpenGL library wasn't loaded, SDL_GetError() should have info */ |
| | | return NULL; |
| | | } |
| | | |
| | | X11_GL_GetAttributes(_this, display, screen, attribs, 64, SDL_FALSE); |
| | | X11_GL_GetAttributes(_this, display, screen, attribs, 64, SDL_FALSE, &pvistypeattr); |
| | | vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs); |
| | | |
| | | if (!vinfo && (pvistypeattr != NULL)) { |
| | | *pvistypeattr = None; |
| | | vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs); |
| | | } |
| | | |
| | | if (!vinfo) { |
| | | SDL_SetError("Couldn't find matching GLX visual"); |
| | | } |
| | | return vinfo; |
| | | } |
| | | |
| | | #ifndef GLXBadContext |
| | | #define GLXBadContext 0 |
| | | #endif |
| | | #ifndef GLXBadFBConfig |
| | | #define GLXBadFBConfig 9 |
| | | #endif |
| | | #ifndef GLXBadProfileARB |
| | | #define GLXBadProfileARB 13 |
| | | #endif |
| | | static int (*handler) (Display *, XErrorEvent *) = NULL; |
| | | static const char *errorHandlerOperation = NULL; |
| | | static int errorBase = 0; |
| | |
| | | } |
| | | else |
| | | { |
| | | SDL_SetError("Could not %s: %i (Base %i)\n", errorHandlerOperation, errorCode, errorBase); |
| | | SDL_SetError("Could not %s: %i (Base %i)", errorHandlerOperation, errorCode, errorBase); |
| | | } |
| | | |
| | | return (0); |
| | | } |
| | | |
| | | SDL_bool |
| | | X11_GL_UseEGL(_THIS) |
| | | { |
| | | SDL_assert(_this->gl_data != NULL); |
| | | SDL_assert(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES); |
| | | |
| | | return (SDL_GetHintBoolean(SDL_HINT_OPENGL_ES_DRIVER, SDL_FALSE) |
| | | || _this->gl_config.major_version == 1 /* No GLX extension for OpenGL ES 1.x profiles. */ |
| | | || _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)); |
| | | } |
| | | |
| | | SDL_GLContext |
| | |
| | | context = |
| | | _this->gl_data->glXCreateContext(display, vinfo, share_context, True); |
| | | } else { |
| | | /* max 10 attributes plus terminator */ |
| | | int attribs[11] = { |
| | | /* max 14 attributes plus terminator */ |
| | | int attribs[15] = { |
| | | GLX_CONTEXT_MAJOR_VERSION_ARB, |
| | | _this->gl_config.major_version, |
| | | GLX_CONTEXT_MINOR_VERSION_ARB, |
| | |
| | | GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB; |
| | | } |
| | | |
| | | /* only set if glx extension is available */ |
| | | if( _this->gl_data->HAS_GLX_ARB_create_context_robustness ) { |
| | | attribs[iattr++] = GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB; |
| | | attribs[iattr++] = |
| | | _this->gl_config.reset_notification ? |
| | | GLX_LOSE_CONTEXT_ON_RESET_ARB : |
| | | GLX_NO_RESET_NOTIFICATION_ARB; |
| | | } |
| | | |
| | | /* only set if glx extension is available */ |
| | | if( _this->gl_data->HAS_GLX_ARB_create_context_no_error ) { |
| | | attribs[iattr++] = GLX_CONTEXT_OPENGL_NO_ERROR_ARB; |
| | | attribs[iattr++] = _this->gl_config.no_error; |
| | | } |
| | | |
| | | attribs[iattr++] = 0; |
| | | |
| | | /* Get a pointer to the context creation function for GL 3.0 */ |
| | |
| | | /* Create a GL 3.x context */ |
| | | GLXFBConfig *framebuffer_config = NULL; |
| | | int fbcount = 0; |
| | | int *pvistypeattr = NULL; |
| | | |
| | | X11_GL_GetAttributes(_this,display,screen,glxAttribs,64,SDL_TRUE); |
| | | X11_GL_GetAttributes(_this,display,screen,glxAttribs,64,SDL_TRUE,&pvistypeattr); |
| | | |
| | | if (!_this->gl_data->glXChooseFBConfig |
| | | || !(framebuffer_config = |
| | | _this->gl_data->glXChooseFBConfig(display, |
| | | if (_this->gl_data->glXChooseFBConfig) { |
| | | framebuffer_config = _this->gl_data->glXChooseFBConfig(display, |
| | | DefaultScreen(display), glxAttribs, |
| | | &fbcount))) { |
| | | SDL_SetError("No good framebuffers found. OpenGL 3.0 and later unavailable"); |
| | | } else { |
| | | context = _this->gl_data->glXCreateContextAttribsARB(display, |
| | | framebuffer_config[0], |
| | | share_context, True, attribs); |
| | | &fbcount); |
| | | |
| | | if (!framebuffer_config && (pvistypeattr != NULL)) { |
| | | *pvistypeattr = None; |
| | | framebuffer_config = _this->gl_data->glXChooseFBConfig(display, |
| | | DefaultScreen(display), glxAttribs, |
| | | &fbcount); |
| | | } |
| | | |
| | | if (framebuffer_config) { |
| | | context = _this->gl_data->glXCreateContextAttribsARB(display, |
| | | framebuffer_config[0], |
| | | share_context, True, attribs); |
| | | X11_XFree(framebuffer_config); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | void |
| | | int |
| | | X11_GL_SwapWindow(_THIS, SDL_Window * window) |
| | | { |
| | | SDL_WindowData *data = (SDL_WindowData *) window->driverdata; |
| | | Display *display = data->videodata->display; |
| | | |
| | | _this->gl_data->glXSwapBuffers(display, data->xwindow); |
| | | return 0; |
| | | } |
| | | |
| | | void |