Monday, February 9, 2015

Why does my OpenGL game only support 2.1 on MacOSX 10.7+?

tl;dr: Lion and Mountain Lion (specifically 10.7.5 and 10.8.5) can support 3.2 and Mavericks (10.9) on up can support at least 3.3. To get anything better than 2.1 though, you need to specify:
  • That the context should use an OpenGL Core Profile
  • Set the "forward compatibility" bit to true
  • The major version should be 3, and minor version should be 2 (or use whatever version you can support).
Now your app should support OpenGL versions higher than 2.1 on MacOSX. Note that, because you've set forward compatibility to true, you can't use immediate mode (well, you can... it just won't do anything. Not that you were using immediate mode anyway, right? ;-) )

The Long Version

This particular issue caught me off-guard when I first came across it. We recently got our very alpha game (an overstatement—it's a slightly-fancier hello-world at this point) running on Ubuntu 14.10 and Windows 7. I wanted to get it working on my laptop so I could do some couch coding. This is an aging Late-2009 13-inch MacBook, upgraded to Yosemite 10.10.2. It's rocking an NVIDIA GeForce 9400M (256MB). Pretty boss, I know.

How does one find out what OpenGL version their graphics chip supports on MacOSX? It turns out Apple has an OS X OpenGL Compatibilities Tables for it, and that not only is it hardware dependent, but also OS dependent as well. The takeaway I got from the page was that, if you want to target 10.7 users, don't exceed version OpenGL 3.2. If you want 10.8, you can go to 3.3. Anything above that, you might be able to hit 4.1, but it depends on hardware at that point.

We're programming with golang. All we've got at this point for the graphics library is glfw3. Getting this set up with MacOSX deserves another post entirely, as it requires cgo and was more than a minor annoyance (but not much more). Anyway, I noticed that, upon start up, the game log indicated it was only running OpenGL 2.1, even though I knew for a fact that even this aging GeForce 9400M could go higher than that.

It turns out it has nothing to do with the library we're using and everything to do with the quirks that Apple baked into MacOSX. I will show how to fix this in golang with glfw3, but I'm betting the same concept works in c++ or other languages and even if you're using other libraries like SDL2 or GLU or SFML or...

Here it is:

    if !glfw.Init() {
        panic("Can't init glfw!")
    }
    defer glfw.Terminate()

    glfw.WindowHint(glfw.OpenglProfile, glfw.OpenglCoreProfile)
    glfw.WindowHint(glfw.OpenglForwardCompatible, glfw.True)
    glfw.WindowHint(glfw.ContextVersionMajor, 3)
    glfw.WindowHint(glfw.ContextVersionMinor, 2)
    window, err := glfw.CreateWindow(640, 480, "Awesome Game", nil, nil)
    if err != nil {
        panic(err)
    }

How can we apply this knowledge to the other wrapper libs, like SDL2? All of these hinting values likely line up with GL/glext.h's constants for GL_CONTEXT_PROFILE_MASK, GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT, GL_MAJOR_VERSION, and GL_MINOR_VERSION.

The possible values for glfw.OpenglCoreProfile, glfw.OpenglCompatProfile, and glfw.OpenglAnyProfile, I would also guess, line up with GL_CONTEXT_CORE_PROFILE_BIT, GL_CONTEXT_COMPATIBILITY_PROFILE_BIT, and finally both values OR'ed together.

Those values come directly from OpenGL itself, so if you use a library that wraps OpenGL, chances are good that it will use those too.

But what in the world are OpenGL profiles, anyway? After reading the official page on profiles, I still have no idea, other than it seems to be a way for the process to indicate that it wants to use new the newer features (core), or that it still needs to use the older ones (compatibility).

The profile page talks about the forward compatibility bit as well. This is the part that caught my eye (emphasis theirs):

Recommendation: You should never use the forward compatibility bit. It had a use for GL 3.0, but once 3.1 removed most of the stuff, it stopped having a use.
- OpenGL's Core And Compatibility in Contexts page
So, why should we still have to set the forward compatibility bit in MacOSX, if it doesn't even matter for 3.2 anyway, and the author of the sentence thought this point was so important that he had to go to the trouble of using italics?!

Seriously, why? I have no idea. But I no longer care much, now that the window appears without error.