openglsdlglfwglxwgl

Request the most recent version of OpenGL context


I'm developing an application that can use any OpenGL version from 4.6 down to 2.0 by gradually disabling some features and optimizations. This means that it can live with 2.0 but prefers the latest supported version to be able to use all the available features from OpenGL 3.x-4.x. Also, it handles all the differences between core and compatibility contexts, so it should work with any profile.

It seems that on Windows there won't be a problem, because I can just omit the version and the profile and automatically get a compatibility context with the latest supported version.

But things work differently on macOS and with Mesa. There I have to request a core forward compatible context of some specific version, even though I don't want a specific version, I want the latest one.

How do I handle this problem? Do I have to try all the versions 4.6, 4.5, 4.4, 4.3, 4.2, 4.1, 4.0, 3.3, 3.2, 3.1, 3.0, 2.1, 2.0 in a loop until the context is successfully created? Or is there a better solution?

If there is no better general solution, I would like to know how it works in practice with different drivers on different platforms.


Solution

  • I did some quick tests.

    On Windows AMD Radeon (Ryzen 7):

    1. Requesting any context version up to 2.1 results in a 4.6 Compatibility context. This is exactly what I want.
    2. Requesting any context version above 2.1 results in the context of the requested version.
    3. I assume it works the same on Linux proprietary drivers.
    4. It probably works the same on Intel and NVidia but I can't test it now.

    On Mesa for Windows 20.3.2

    1. Requesting any context version up to 3.1 results in a 3.1 context.
    2. Requesting any context version above 3.1 results in a 4.5 Core context. This is exactly what I want.
    3. I assume it works the same on Linux open-source drivers
    4. Requesting any OpenGL ES version between 2.0-3.2 results in a 3.2 context. This is exactly what I want.

    On Android (Adreno 640)

    1. Requesting any OpenGL ES version between 2.0-3.2 results in a 3.2 context.
    2. I assume that it works the same with other vendors on Android.

    It seems like only the first context creation is slow. In both cases, an additional attempt to create a context adds about 4 ms to the application's startup time on my system, whereas the whole context + window creation is about 300 ms with a native driver or 70 ms with Mesa.

    I don't have a macOS to test so I'm going to use a conservative approach by trying forward-compatible 4.1, 3.3, 3.2, then 2.1. Anyway, most Macs support exactly 4.1, so for them, the context will be created with the first attempt.

    This is what the documentation for iOS OpenGL ES recommends to do:

    To support multiple versions of OpenGL ES as rendering options in your app, you should first attempt to initialize a rendering context of the newest version you want to target. If the returned object is nil, initialize a context of an older version instead.

    So in GLFW pseudocode, my strategy for OpenGL looks like this:

    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
    GLFWwindow* wnd;
    #ifdef __APPLE__ // macOS
    const std::array<char, 2> versions[] = {{4, 1}, {3, 3}, {3, 2}, {2, 1}};
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
    for(auto ver: versions)
    {
        if(ver[0] < 3) glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, false);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, ver[0]);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, ver[1]);
        wnd = glfwCreateWindow(...);
        if(wnd) break;
    }
    glfwMakeContextCurrent(wnd);
    #else // Windows, Linux and other GLFW supported OSes
    glfwWindowHint(GLFW_VISIBLE, false);
    wnd = glfwCreateWindow(...);
    glfwMakeContextCurrent(wnd);
    std::string_view versionStr = glGetString(GL_VERSION);
    if(versionStr[0] < '4' && versionStr.contains("Mesa"))
    {
        glfwDestroyWindow(wnd);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
        wnd = glfwCreateWindow(...);
        glfwMakeContextCurrent(wnd);
    }
    glfwShowWindow(wnd);
    #endif
    

    The code for OpenGL ES would look similar but simpler. Mobile platforms will use a different library instead of GLFW (GLFM/SDL or native EGL). For iOS, I have to try ES 3.0 then ES 2.0. For Mesa and Android, I just request a 2.0 context and get the latest one (3.2). However, for Android, I assume that Mali and other vendors work the same.

    Please, let me know in the comments if you can test my assumptions to confirm or deny them.