Question

I'm using SDL2 and C++11 to build a game engine (just as a personal project, for fun and for practice), and one of the things I want to do is try and have the graphics driver use the latest supported version of OpenGL, and vary how the graphics portion of the engine renders based on the version. This way I can use the newest features of OpenGL where they are relevant and useful, but also support older hardware. There are two methods I can think of:

  1. Detecting the latest supported version of OpenGL and using that, but I can't think of any way to do that. Yes, I have tried Google.

  2. Using trial and error, where I start from the newest version (4.3, but my GTX 460 only supports up to 4.2, even though I updated the drivers), and if that fails (which I detect by checking for SDL to return a NULL context), I lower the version number and try again.

The method I use (#2) fails immediately upon creation of the 4.3 context. I know my graphics drivers only support 4.2 right now, but I had thought that GLX was designed to throw an error, give a NULL context, and let your program keep going, but instead it can't get past the failed creation of the context. Is my assumption about how GLX behaves wrong? Is there a way to detect the latest supported version without creating a context?

Because I know some people prefer to see complete and minimal source that demonstrates the error, here it is:

#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
int main() {
    if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) {
        return -1;
    }
    SDL_Window* window = SDL_CreateWindow( "SDL Window", 0, 0, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN );
    if( window == nullptr ) {
        return -1;
    }
    unsigned int major = 4;
    unsigned int minor = 3;
    SDL_GLContext context = nullptr;
    while( context == nullptr ) {
        SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, major );
        SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, minor );
        context = SDL_GL_CreateContext( window );
        if( context == nullptr ) {
            if( minor == 0 && major > 1 ) {
                --major;
                minor = 9;
            }
            else if( minor > 0 ) {
                --minor;
            }
            else {
                SDL_DestroyWindow( window );
                return -1;
            }
        }
    }
    SDL_Delay( 5000 );
    SDL_GL_DeleteContext( context );
    SDL_DestroyWindow( window );
    return 0;
}

Also, if I change that code to start with a 4.2 context instead of a 4.3, it works just fine. So the error is in creating a 4.3 context specifically.

Was it helpful?

Solution

This might likely indicate a bug in SDL's X11_GL_CreateContext() (on the web it says that SDL2 is still experimental). Your strategy to find out the supported OpenGL version is valid, but in order for SDL to give you forward-compatible context, it first needs to create a temporary context, call glXGetProcAddress() to get glCreateContextAttribsARB (it can't do that without having an active OpenGL context), and then it attempts to initialize OpenGL version that you requested. Now if it fails, it disposes of the temporary context and returns NULL.

My guess is the second time it fails in creating the temporary context, I'd suggest building SDL with debug info and trying to find out where exactly in X11_GL_CreateContext() it fails (or at least insering a couple of printf).

On the other hand, you already get maximum OpenGL version if you leave SDL_GL_CONTEXT_MAJOR_VERSION and SDL_GL_CONTEXT_MINOR_VERSION in their default values. You just don't get a forward-compatible context, meaning it will let some behavior that is considered "errorneous" in future versions to slip by.

Also, in general, it is not a good idea to create a forward-compatible context with the maximum version possible. E.g. in OpenGL 3.2, the forward-compatible profile enforces the use of vertex array objects, which makes older code that did not use VAOs broken. So if the application written for OpenGL 3.1 (or any other version) tries to get the newest forward-compatible profile possible, it might in fact stop working in the future. This is not a good idea.

One option is to decide on use of one forward-compatible profile, which is mostly very helpful as it reports errors about deprecated features. If you really want to use more versions, you need more code paths (= more versions of your game engine) to do so. To detect OpenGL version, i'd suggest creating a dummy SDL window, initializing a simple OpenGL profile, getting version and shutting everything down, then initializing again, with the version for which you decide. That should work.

The other option is to use the forward-compatible profile only for development and using a simple legacy profile for the distribution. It is the most compatible and involves least effort (you want to ignore most of OpenGL errors in release version anyway - error reporting might even be disabled by the driver).

OTHER TIPS

This is a bit late, but I struggled with this problem recently (Still crashes when trying to create a opengl context thats not supported in SDL2)

I simply created a dummy context and then use that context to that to get the GL Version. Then destroy that context (Unless it's exactly what I wanted) and make another.

//Create Dummy Context
context = SDL_GL_CreateContext(window);

//Get OpenGL Version
const GLubyte* sGLVersion =  glGetString(GL_VERSION);

version = (sGLVersion[0]-'0')*10;
version += (sGLVersion[2]-'0');

//Now Check that it can support the version that you want to make and stop if you can't
//or in your case set it to that version

//If you need to make another context destroy the old one
SDL_GL_DeleteContext(context);

//and make a new one
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, majorVersion );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, minorVersion );

context = SDL_GL_CreateContext(window);

This is also faster than brute-forcing through version numbers until you happen to land on one that works. (Your solution 1 that you couldn't find by googling :) )

'the swine' was mistaken about the default version being the maximum supported version (at least in the version I have it default to 2.1 where I have 4.3 available)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top