src/video/x11/SDL_x11opengl.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Aug 2009 18:39:57 +0000
changeset 3227 458e53d8662c
parent 3139 7f684f249ec9
child 3241 08c5964f2a34
permissions -rw-r--r--
Clarified API documentation
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with _this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #include "SDL_x11video.h"
    25 
    26 /* GLX implementation of SDL OpenGL support */
    27 
    28 #if SDL_VIDEO_OPENGL_GLX
    29 #include "SDL_loadso.h"
    30 
    31 #if defined(__IRIX__)
    32 /* IRIX doesn't have a GL library versioning system */
    33 #define DEFAULT_OPENGL	"libGL.so"
    34 #elif defined(__MACOSX__)
    35 #define DEFAULT_OPENGL	"/usr/X11R6/lib/libGL.1.dylib"
    36 #elif defined(__QNXNTO__)
    37 #define DEFAULT_OPENGL	"libGL.so.3"
    38 #else
    39 #define DEFAULT_OPENGL	"libGL.so.1"
    40 #endif
    41 
    42 #ifndef GLX_ARB_multisample
    43 #define GLX_ARB_multisample
    44 #define GLX_SAMPLE_BUFFERS_ARB             100000
    45 #define GLX_SAMPLES_ARB                    100001
    46 #endif
    47 
    48 #ifndef GLX_EXT_visual_rating
    49 #define GLX_EXT_visual_rating
    50 #define GLX_VISUAL_CAVEAT_EXT              0x20
    51 #define GLX_NONE_EXT                       0x8000
    52 #define GLX_SLOW_VISUAL_EXT                0x8001
    53 #define GLX_NON_CONFORMANT_VISUAL_EXT      0x800D
    54 #endif
    55 
    56 #ifndef GLX_ARB_create_context
    57 #define GLX_ARB_create_context
    58 #define GLX_CONTEXT_MAJOR_VERSION_ARB      0x2091
    59 #define GLX_CONTEXT_MINOR_VERSION_ARB      0x2092
    60 #define GLX_CONTEXT_FLAGS_ARB              0x2094
    61 #define GLX_CONTEXT_DEBUG_BIT_ARB          0x0001
    62 #define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
    63 #endif
    64 
    65 #define OPENGL_REQUIRS_DLOPEN
    66 #if defined(OPENGL_REQUIRS_DLOPEN) && defined(SDL_LOADSO_DLOPEN)
    67 #include <dlfcn.h>
    68 #define GL_LoadObject(X)	dlopen(X, (RTLD_NOW|RTLD_GLOBAL))
    69 #define GL_LoadFunction		dlsym
    70 #define GL_UnloadObject		dlclose
    71 #else
    72 #define GL_LoadObject	SDL_LoadObject
    73 #define GL_LoadFunction	SDL_LoadFunction
    74 #define GL_UnloadObject	SDL_UnloadObject
    75 #endif
    76 
    77 static void X11_GL_InitExtensions(_THIS);
    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 
    88 int
    89 X11_GL_LoadLibrary(_THIS, const char *path)
    90 {
    91     void *handle;
    92 
    93     /* Load the OpenGL library */
    94     if (path == NULL) {
    95         path = SDL_getenv("SDL_OPENGL_LIBRARY");
    96     }
    97     if (path == NULL) {
    98         path = DEFAULT_OPENGL;
    99     }
   100     _this->gl_config.dll_handle = SDL_LoadObject(path);
   101     if (!_this->gl_config.dll_handle) {
   102         return -1;
   103     }
   104     SDL_strlcpy(_this->gl_config.driver_path, path,
   105                 SDL_arraysize(_this->gl_config.driver_path));
   106 
   107     /* Allocate OpenGL memory */
   108     _this->gl_data =
   109         (struct SDL_GLDriverData *) SDL_calloc(1,
   110                                                sizeof(struct
   111                                                       SDL_GLDriverData));
   112     if (!_this->gl_data) {
   113         SDL_OutOfMemory();
   114         return -1;
   115     }
   116 
   117     /* Load function pointers */
   118     handle = _this->gl_config.dll_handle;
   119     _this->gl_data->glXGetProcAddress =
   120         (void *(*)(const GLubyte *)) GL_LoadFunction(handle,
   121                                                      "glXGetProcAddressARB");
   122     _this->gl_data->glXChooseVisual =
   123         (XVisualInfo * (*)(Display *, int, int *)) GL_LoadFunction(handle,
   124                                                                    "glXChooseVisual");
   125     _this->gl_data->glXCreateContext =
   126         (GLXContext(*)(Display *, XVisualInfo *, GLXContext, int))
   127         GL_LoadFunction(handle, "glXCreateContext");
   128     _this->gl_data->glXDestroyContext =
   129         (void (*)(Display *, GLXContext)) GL_LoadFunction(handle,
   130                                                           "glXDestroyContext");
   131     _this->gl_data->glXMakeCurrent =
   132         (int (*)(Display *, GLXDrawable, GLXContext)) GL_LoadFunction(handle,
   133                                                                       "glXMakeCurrent");
   134     _this->gl_data->glXSwapBuffers =
   135         (void (*)(Display *, GLXDrawable)) GL_LoadFunction(handle,
   136                                                            "glXSwapBuffers");
   137 
   138     if (!_this->gl_data->glXChooseVisual ||
   139         !_this->gl_data->glXCreateContext ||
   140         !_this->gl_data->glXDestroyContext ||
   141         !_this->gl_data->glXMakeCurrent || !_this->gl_data->glXSwapBuffers) {
   142         SDL_SetError("Could not retrieve OpenGL functions");
   143         return -1;
   144     }
   145 
   146     /* Initialize extensions */
   147     X11_GL_InitExtensions(_this);
   148 
   149     return 0;
   150 }
   151 
   152 void *
   153 X11_GL_GetProcAddress(_THIS, const char *proc)
   154 {
   155     void *handle;
   156 
   157     handle = _this->gl_config.dll_handle;
   158     if (_this->gl_data->glXGetProcAddress) {
   159         return _this->gl_data->glXGetProcAddress((const GLubyte *) proc);
   160     }
   161     return GL_LoadFunction(handle, proc);
   162 }
   163 
   164 void
   165 X11_GL_UnloadLibrary(_THIS)
   166 {
   167     /* Don't actually unload the library, since it may have registered
   168      * X11 shutdown hooks, per the notes at:
   169      * http://dri.sourceforge.net/doc/DRIuserguide.html
   170      */
   171 #if 0
   172     GL_UnloadObject(_this->gl_config.dll_handle);
   173     _this->gl_config.dll_handle = NULL;
   174 #endif
   175 
   176     /* Free OpenGL memory */
   177     SDL_free(_this->gl_data);
   178     _this->gl_data = NULL;
   179 }
   180 
   181 static SDL_bool
   182 HasExtension(const char *extension, const char *extensions)
   183 {
   184     const char *start;
   185     const char *where, *terminator;
   186 
   187     /* Extension names should not have spaces. */
   188     where = SDL_strchr(extension, ' ');
   189     if (where || *extension == '\0')
   190         return SDL_FALSE;
   191 
   192     if (!extensions)
   193         return SDL_FALSE;
   194 
   195     /* It takes a bit of care to be fool-proof about parsing the
   196      * OpenGL extensions string. Don't be fooled by sub-strings,
   197      * etc. */
   198 
   199     start = extensions;
   200 
   201     for (;;) {
   202         where = SDL_strstr(start, extension);
   203         if (!where)
   204             break;
   205 
   206         terminator = where + SDL_strlen(extension);
   207         if (where == start || *(where - 1) == ' ')
   208             if (*terminator == ' ' || *terminator == '\0')
   209                 return SDL_TRUE;
   210 
   211         start = terminator;
   212     }
   213     return SDL_FALSE;
   214 }
   215 
   216 static void
   217 X11_GL_InitExtensions(_THIS)
   218 {
   219     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   220     int screen = ((SDL_DisplayData *) SDL_CurrentDisplay.driverdata)->screen;
   221     XVisualInfo *vinfo;
   222     XSetWindowAttributes xattr;
   223     Window w;
   224     GLXContext context;
   225     const char *(*glXQueryExtensionsStringFunc) (Display *, int);
   226     const char *extensions;
   227 
   228     vinfo = X11_GL_GetVisual(_this, display, screen);
   229     if (!vinfo) {
   230         return;
   231     }
   232     xattr.background_pixel = 0;
   233     xattr.border_pixel = 0;
   234     xattr.colormap =
   235         XCreateColormap(display, RootWindow(display, screen), vinfo->visual,
   236                         AllocNone);
   237     w = XCreateWindow(display, RootWindow(display, screen), 0, 0, 32, 32, 0,
   238                       vinfo->depth, InputOutput, vinfo->visual,
   239                       (CWBackPixel | CWBorderPixel | CWColormap), &xattr);
   240     context = _this->gl_data->glXCreateContext(display, vinfo, NULL, True);
   241     if (context) {
   242         _this->gl_data->glXMakeCurrent(display, w, context);
   243     }
   244     XFree(vinfo);
   245 
   246     glXQueryExtensionsStringFunc =
   247         (const char *(*)(Display *, int)) X11_GL_GetProcAddress(_this,
   248                                                                 "glXQueryExtensionsString");
   249     if (glXQueryExtensionsStringFunc) {
   250         extensions = glXQueryExtensionsStringFunc(display, screen);
   251     } else {
   252         extensions = NULL;
   253     }
   254 
   255     /* Check for SGI_swap_control */
   256     if (HasExtension("GLX_SGI_swap_control", extensions)) {
   257         _this->gl_data->glXSwapIntervalSGI =
   258             (int (*)(int)) X11_GL_GetProcAddress(_this, "glXSwapIntervalSGI");
   259     }
   260 
   261     /* Check for GLX_MESA_swap_control */
   262     if (HasExtension("GLX_MESA_swap_control", extensions)) {
   263         _this->gl_data->glXSwapIntervalMESA =
   264             (GLint(*)(unsigned)) X11_GL_GetProcAddress(_this,
   265                                                        "glXSwapIntervalMESA");
   266         _this->gl_data->glXGetSwapIntervalMESA =
   267             (GLint(*)(void)) X11_GL_GetProcAddress(_this,
   268                                                    "glXGetSwapIntervalMESA");
   269     }
   270 
   271     /* Check for GLX_EXT_visual_rating */
   272     if (HasExtension("GLX_EXT_visual_rating", extensions)) {
   273         _this->gl_data->HAS_GLX_EXT_visual_rating = SDL_TRUE;
   274     }
   275 
   276     if (context) {
   277         _this->gl_data->glXMakeCurrent(display, None, NULL);
   278         _this->gl_data->glXDestroyContext(display, context);
   279     }
   280     XDestroyWindow(display, w);
   281     X11_PumpEvents(_this);
   282 }
   283 
   284 XVisualInfo *
   285 X11_GL_GetVisual(_THIS, Display * display, int screen)
   286 {
   287     XVisualInfo *vinfo;
   288 
   289     /* 64 seems nice. */
   290     int attribs[64];
   291     int i = 0;
   292 
   293     /* Setup our GLX attributes according to the gl_config. */
   294     attribs[i++] = GLX_RGBA;
   295     attribs[i++] = GLX_RED_SIZE;
   296     attribs[i++] = _this->gl_config.red_size;
   297     attribs[i++] = GLX_GREEN_SIZE;
   298     attribs[i++] = _this->gl_config.green_size;
   299     attribs[i++] = GLX_BLUE_SIZE;
   300     attribs[i++] = _this->gl_config.blue_size;
   301 
   302     if (_this->gl_config.alpha_size) {
   303         attribs[i++] = GLX_ALPHA_SIZE;
   304         attribs[i++] = _this->gl_config.alpha_size;
   305     }
   306 
   307     if (_this->gl_config.buffer_size) {
   308         attribs[i++] = GLX_BUFFER_SIZE;
   309         attribs[i++] = _this->gl_config.buffer_size;
   310     }
   311 
   312     if (_this->gl_config.double_buffer) {
   313         attribs[i++] = GLX_DOUBLEBUFFER;
   314     }
   315 
   316     attribs[i++] = GLX_DEPTH_SIZE;
   317     attribs[i++] = _this->gl_config.depth_size;
   318 
   319     if (_this->gl_config.stencil_size) {
   320         attribs[i++] = GLX_STENCIL_SIZE;
   321         attribs[i++] = _this->gl_config.stencil_size;
   322     }
   323 
   324     if (_this->gl_config.accum_red_size) {
   325         attribs[i++] = GLX_ACCUM_RED_SIZE;
   326         attribs[i++] = _this->gl_config.accum_red_size;
   327     }
   328 
   329     if (_this->gl_config.accum_green_size) {
   330         attribs[i++] = GLX_ACCUM_GREEN_SIZE;
   331         attribs[i++] = _this->gl_config.accum_green_size;
   332     }
   333 
   334     if (_this->gl_config.accum_blue_size) {
   335         attribs[i++] = GLX_ACCUM_BLUE_SIZE;
   336         attribs[i++] = _this->gl_config.accum_blue_size;
   337     }
   338 
   339     if (_this->gl_config.accum_alpha_size) {
   340         attribs[i++] = GLX_ACCUM_ALPHA_SIZE;
   341         attribs[i++] = _this->gl_config.accum_alpha_size;
   342     }
   343 
   344     if (_this->gl_config.stereo) {
   345         attribs[i++] = GLX_STEREO;
   346     }
   347 
   348     if (_this->gl_config.multisamplebuffers) {
   349         attribs[i++] = GLX_SAMPLE_BUFFERS_ARB;
   350         attribs[i++] = _this->gl_config.multisamplebuffers;
   351     }
   352 
   353     if (_this->gl_config.multisamplesamples) {
   354         attribs[i++] = GLX_SAMPLES_ARB;
   355         attribs[i++] = _this->gl_config.multisamplesamples;
   356     }
   357 
   358     if (_this->gl_config.accelerated >= 0
   359         && _this->gl_data->HAS_GLX_EXT_visual_rating) {
   360         attribs[i++] = GLX_VISUAL_CAVEAT_EXT;
   361         attribs[i++] = GLX_NONE_EXT;
   362     }
   363 #ifdef GLX_DIRECT_COLOR         /* Try for a DirectColor visual for gamma support */
   364     if (X11_UseDirectColorVisuals()) {
   365         attribs[i++] = GLX_X_VISUAL_TYPE;
   366         attribs[i++] = GLX_DIRECT_COLOR;
   367     }
   368 #endif
   369     attribs[i++] = None;
   370 
   371     vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
   372 #ifdef GLX_DIRECT_COLOR
   373     if (!vinfo && X11_UseDirectColorVisuals()) {        /* No DirectColor visual?  Try again.. */
   374         attribs[i - 3] = None;
   375         vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
   376     }
   377 #endif
   378     if (!vinfo) {
   379         SDL_SetError("Couldn't find matching GLX visual");
   380     }
   381     return vinfo;
   382 }
   383 
   384 SDL_GLContext
   385 X11_GL_CreateContext(_THIS, SDL_Window * window)
   386 {
   387     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   388     Display *display = data->videodata->display;
   389     int screen =
   390         ((SDL_DisplayData *) SDL_GetDisplayFromWindow(window)->
   391          driverdata)->screen;
   392     XWindowAttributes xattr;
   393     XVisualInfo v, *vinfo;
   394     int n;
   395     GLXContext context = NULL;
   396 
   397     /* We do this to create a clean separation between X and GLX errors. */
   398     XSync(display, False);
   399     XGetWindowAttributes(display, data->window, &xattr);
   400     v.screen = screen;
   401     v.visualid = XVisualIDFromVisual(xattr.visual);
   402     vinfo = XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &v, &n);
   403     if (vinfo) {
   404         if (_this->gl_config.major_version < 3) {
   405             context =
   406                 _this->gl_data->glXCreateContext(display, vinfo, NULL, True);
   407         } else {
   408             /* If we want a GL 3.0 context or later we need to get a temporary
   409                context to grab the new context creation function */
   410             GLXContext temp_context =
   411                 _this->gl_data->glXCreateContext(display, vinfo, NULL, True);
   412             if (!temp_context) {
   413                 SDL_SetError("Could not create GL context");
   414                 return NULL;
   415             } else {
   416                 int attribs[] = {
   417                     GLX_CONTEXT_MAJOR_VERSION_ARB,
   418                     _this->gl_config.major_version,
   419                     GLX_CONTEXT_MINOR_VERSION_ARB,
   420                     _this->gl_config.minor_version,
   421                     0
   422                 };
   423 
   424                 /* Get a pointer to the context creation function for GL 3.0 */
   425                 PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs =
   426                     (PFNGLXCREATECONTEXTATTRIBSARBPROC) _this->gl_data->
   427                     glXGetProcAddress((GLubyte *)
   428                                       "glXCreateContextAttribsARB");
   429                 if (!glXCreateContextAttribs) {
   430                     SDL_SetError("GL 3.x is not supported");
   431                     context = temp_context;
   432                 } else {
   433                     /* Create a GL 3.x context */
   434                     GLXFBConfig *framebuffer_config = NULL;
   435                     int fbcount = 0;
   436                     GLXFBConfig *(*glXChooseFBConfig) (Display * disp,
   437                                                        int screen,
   438                                                        const int *attrib_list,
   439                                                        int *nelements);
   440 
   441                     glXChooseFBConfig =
   442                         (GLXFBConfig *
   443                          (*)(Display *, int, const int *,
   444                              int *)) _this->gl_data->
   445                         glXGetProcAddress((GLubyte *) "glXChooseFBConfig");
   446 
   447                     if (!glXChooseFBConfig
   448                         || !(framebuffer_config =
   449                              glXChooseFBConfig(display,
   450                                                DefaultScreen(display), NULL,
   451                                                &fbcount))) {
   452                         SDL_SetError
   453                             ("No good framebuffers found. GL 3.x disabled");
   454                         context = temp_context;
   455                     } else {
   456                         context =
   457                             glXCreateContextAttribs(display,
   458                                                     framebuffer_config[0],
   459                                                     NULL, True, attribs);
   460                         _this->gl_data->glXDestroyContext(display,
   461                                                           temp_context);
   462                     }
   463                 }
   464             }
   465         }
   466         XFree(vinfo);
   467     }
   468     XSync(display, False);
   469 
   470     if (!context) {
   471         SDL_SetError("Could not create GL context");
   472         return NULL;
   473     }
   474 
   475     if (X11_GL_MakeCurrent(_this, window, context) < 0) {
   476         X11_GL_DeleteContext(_this, context);
   477         return NULL;
   478     }
   479 
   480     return context;
   481 }
   482 
   483 int
   484 X11_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
   485 {
   486     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   487     Window drawable =
   488         (window ? ((SDL_WindowData *) window->driverdata)->window : None);
   489     GLXContext glx_context = (GLXContext) context;
   490     int status;
   491 
   492     status = 0;
   493     if (!_this->gl_data->glXMakeCurrent(display, drawable, glx_context)) {
   494         SDL_SetError("Unable to make GL context current");
   495         status = -1;
   496     }
   497     XSync(display, False);
   498 
   499     return (status);
   500 }
   501 
   502 /* 
   503    0 is a valid argument to glxSwapIntervalMESA and setting it to 0
   504    with the MESA version of the extension will undo the effect of a
   505    previous call with a value that is greater than zero (or at least
   506    that is what the FM says. OTOH, 0 is an invalid argument to
   507    glxSwapIntervalSGI and it returns an error if you call it with 0 as
   508    an argument.
   509 */
   510 
   511 static int swapinterval = -1;
   512 int
   513 X11_GL_SetSwapInterval(_THIS, int interval)
   514 {
   515     int status;
   516 
   517     if (_this->gl_data->glXSwapIntervalMESA) {
   518         status = _this->gl_data->glXSwapIntervalMESA(interval);
   519         if (status != 0) {
   520             SDL_SetError("glxSwapIntervalMESA failed");
   521             status = -1;
   522         } else {
   523             swapinterval = interval;
   524         }
   525     } else if (_this->gl_data->glXSwapIntervalSGI) {
   526         status = _this->gl_data->glXSwapIntervalSGI(interval);
   527         if (status != 0) {
   528             SDL_SetError("glxSwapIntervalSGI failed");
   529             status = -1;
   530         } else {
   531             swapinterval = interval;
   532         }
   533     } else {
   534         SDL_Unsupported();
   535         status = -1;
   536     }
   537     return status;
   538 }
   539 
   540 int
   541 X11_GL_GetSwapInterval(_THIS)
   542 {
   543     if (_this->gl_data->glXGetSwapIntervalMESA) {
   544         return _this->gl_data->glXGetSwapIntervalMESA();
   545     } else {
   546         return swapinterval;
   547     }
   548 }
   549 
   550 void
   551 X11_GL_SwapWindow(_THIS, SDL_Window * window)
   552 {
   553     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   554     Display *display = data->videodata->display;
   555 
   556     _this->gl_data->glXSwapBuffers(display, data->window);
   557 }
   558 
   559 void
   560 X11_GL_DeleteContext(_THIS, SDL_GLContext context)
   561 {
   562     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   563     GLXContext glx_context = (GLXContext) context;
   564 
   565     _this->gl_data->glXDestroyContext(display, glx_context);
   566     XSync(display, False);
   567 }
   568 
   569 #endif /* SDL_VIDEO_OPENGL_GLX */
   570 
   571 /* vi: set ts=4 sw=4 expandtab: */