On October 22, 2013, Apple released OS X Mavericks, also known as OS X version 10.9. This version of the operating system included a long awaited update to the supported version of OpenGL. The 6th edition of the OpenGL SuperBible is about OpenGL version 4.3, and unfortunately, Apple’s latest and greatest only supports version 4.1 of the API. As OpenGL 4.1 was released on July 26, 2010, this puts OS X more than three years behind. However, not all of the book’s samples make use of all of the latest features, and it’s possible to run many of them on version 4.1 of the API. I’ve ported what I can.
What Works, What Doesn’t
Now, there are clearly some samples that aren’t going to run on OpenGL 4.1. For instance, anything that uses compute shaders (the depth-of-field and flocking samples, for example), shader storage buffers (the
fragmentlist sample), atomic counters or image loads and stores isn’t going to port. Luckily, although the Mac OpenGL implementation is only version 4.1, it does have a couple of extensions from OpenGL 4.2 —
GL_ARB_texture_storage, the latter of which is used by the
sb6 framework’s texture loader.
To be fair, the book’s preface does say that the samples have been tested on Mac. We’ve received a couple of poor reviews on Amazon because the code isn’t usable on Mac. For my day job, I’m the architect of AMD’s PC OpenGL driver, which we use for Windows and Linux. My cube neighbor is the architect for AMD’s Mac driver. Apple can be pretty secretive about what’s coming up and when, and all I had to go on when I wrote that preface (back in January of 2013) was insider knowledge of what my neighbor was working on. Even he doesn’t know what’s going to be in the next OS version or when it might release. However, it seemed that he was making pretty good progress and that I’d have a shot at building Mac versions of the samples by the time the book shipped. So much for my ability to predict the future.
Unlike Windows and Linux, where the hardware vendor ships drivers which determine the version of OpenGL available on your machine, on the Mac platform, big chunks of the OpenGL stack are part of the operating system and so an OS update is required to bump the OpenGL version. Hence, although AMD, NVIDIA and Intel have all had OpenGL 4.x capable hardware shipping in Mac platforms for some time, we needed to wait until the 10.9 Mavericks update to actually expose it. Step 1, then, is to update the operating system to the latest available. After a quick stop off at the App Store, and a rather large, 5GiB free download (which is excellent), I was installing OS X 10.9 Mavericks on my trusty Mac Book Pro.
Of course, the next step is to install XCode, which includes all the developer tools. What’s more to say about XCode? It seems that people either love it or hate it. I wouldn’t say I love it. I’ve used a lot of development environments in the past. While I’m most comfortable with Visual Studio, I’ve had spells with Borland’s C++ Builder IDE, KDevelop, Anjuta, Eclipse, and Qt Creator. XCode is different and I would imagine would take a lot of getting used to. I was able to coerce CMake into generating XCode project files for the samples. But, once loaded into XCode, getting things to build was close to impossible. It was difficult to navigate the project, difficult to see what was going wrong, and an extremely frustrating experience.
In the end, I opted to generate regular Makefiles at the command prompt. This seemed much more straight forward, and after a short time, I had everything building and linking. Upon running the first example, it crashed almost immediately. Now, this may be an artifact of the version of GLFW that I’ve used for the projects, but it seems that if you ask for
GLFW_OPENGL_VERSION_MAJOR as 4 and
GLFW_OPENGL_VERSION_MINOR as 1 (i.e., ask for OpenGL 4.1), context creation will fail. However, if you set
GLFW_OPENGL_VERSION_MAJOR to 3 and
GLFW_OPENGL_VERSION_MINOR to 2 (ask for OpenGL 3.2), you’ll get a 4.1 context. You also need to ask for a forward compatible context.
Debugging this was a pain. XCode wouldn’t cooperate — I couldn’t get it to debug anything it didn’t build, and I couldn’t get it to build my samples. Time to whip out the trusty command line debugger,
gdb. Except, with Mavericks,
gdb is gone and now we have
lldb seems good. I’m not quite sure why a whole new debugger is necessary. I’m certain that
lldb does some pretty neat things that
gdb can’t do, but for the things that are the same (setting breakpoints, for example), it seems to be different (and therefore unfamiliar) for the sake of it. Learning a new debugger just to step through sample applications I know already work was yet more pain.
For the most part, modifying the
#version declaration in the shaders to request version
410 core rather than 430 was sufficient to get the samples working. Of course, dropping the shading language version meant that I couldn’t make use of some of the convenience functions that have been incorporated recently, such as some forms of implicit conversion and promotion from scalar to vector. I fixed the most trivial of these and was fairly quickly up and running with the first few samples.
The biggest issue was the lack of explicit binding declarations on sampler uniforms in GLSL. These allow you to write, directly in the shader code, the texture unit that a sampler corresponds to. The type of declaration I’m referring to looks like this:
layout (binding = 0) uniform sampler1D grasspalette_texture; layout (binding = 1) uniform sampler2D length_texture; layout (binding = 2) uniform sampler2D orientation_texture; layout (binding = 3) uniform sampler2D grasscolor_texture; layout (binding = 4) uniform sampler2D bend_texture;
Here, I’ve assigned the five sampler uniforms used by the shader to the first five texture units. I could have left gaps or assigned the uniforms in any order. By default, sampler uniforms are associated with texture unit zero. For simpler examples with that use only a single texture, I can remove the
binding layout qualifiers and take that zero default. For more complex examples such as the one above, I’d have to query the locations of the sampler uniforms and set their values with
glUniform1i from the application. The binding qualifiers allow the application code to be more concise and the shader code to be more explicit. Therefore, I won’t be modifying any of the examples that make extensive use of this OpenGL 4.2 feature.
The second feature that most of the samples rely on is part of the
GL_ARB_base_instance extension, which is part of OpenGL 4.2. This extension adds variants of several instanced draw commands which include a
baseinstance parameter. These functions are fairly trivial additions to the API, with the additional parameter not requiring any validation and being passed directly to the GPU as part of the draw command. It’s surprising that Apple wasn’t able to squeeze this one into the Mavericks update. Unfortunately, the
sb6 object loader uses the
glDrawElementsInstancedBaseVertexBaseInstance functions to render objects.
To work around this issue, I’ve modified the object rendering code such that on Mac, it calls the variants of the functions without the
baseinstance parameter. They’ll behave as if the
baseinstance parameter was set to zero, which will be correct in the vast majority of the examples. In addition to the new APIs, this extension defines the meaning of the last parameter of the structures used by
glDrawElementsIndirect functions, which is undefined in
GL_ARB_draw_indirect. However, the only sample that makes use of this parameter is the
asteroids sample which demonstrates
GL_ARB_multi_draw_indirect, which is also unsupported in Mavericks.
Errors and Debugging
The OpenGL SuperBible sample code framework (referred to as
sb6) is pretty simple. It’s not really designed as an experimentation environment or as a full-featured utility library. Rather, it’s a good place to stuff boilerplate code in order to make the actual sample application more concise. As such, it, and the sample applications do little-to-no error checking. This is fine — the examples that shipped all work and don’t need to be debugged. In the dark ages, people would hack together framework code to call
glGetError 1 after every OpenGL function call. We’ll have none of that in our applications. Instead, we rely on the
GL_ARB_debug_output was promoted to core OpenGL in version 4.3, but has been available as an ARB extension since 2010, and as a vendor specific AMD extension prior to that. This extension has even been promoted to KHR status to allow it to be exposed by OpenGL ES implementations consistently with desktop implementations. Strangely, while OS X 10.9 appears to expose
GL_EXT_debug_marker extensions (which are presumably used by some of the developer tools), it does not expose
GL_KHR_debug, which basically means that the framework’s debugging code is disabled.
With the presence of
GL_ARB_debug_output (or version 4.3 of OpenGL, where it is a core feature), the OpenGL implementation will call a user-supplied callback function whenever an error is generated. By turning on synchronous output, one can insert a breakpoint inside this callback function and see the exact line of code and function call that generated the error. The error message passed to the callback function contains detailed information about what caused the error. This is the way to debug OpenGL applications.
Other Notable Omissions
There’s a few more conspicuously absent features in the Mavericks OpenGL implementation. As the ARB has produced new features in OpenGL, we’ve gone to some effort to document them not only in the core specification, but as extensions as well. This allows vendors and implementers to ship a baseline OpenGL version, but cherry-pick a couple of handy features on top of them. As I mentioned, Apple has done this with
GL_ARB_texture_storage, but there’s nothing more beyond the core features of OpenGL 4.1 and a couple of vendor extensions.
One example is the
GL_ARB_multi_draw_indirect extension. A core feature of OpenGL 4.3, it’s actually pretty simple.
GL_ARB_draw_indirect introduced a way to send a draw to OpenGL with its parameters stored in a buffer object. The multi version of this simply adds a loop around it allowing a whole bunch of draws to be sent in one go (with
glMultiDrawElementsIndirect). In their most naïve form, they can be implemented in the very top layers of an OpenGL driver using a simple loop around the underlying non-multi versions of the functions. However, it’s actually pretty trivial to send the iteration count and stride parameters to the GPU and let it handle the loop.
Other interesting omissions include
GL_ARB_map_buffer_alignment, which simply guarantees that pointers returned from
glMapBufferRange will be aligned to at least a 64-byte boundary (which is important if you want to process data with some SIMD implementations),
GL_ARB_invalidate_subdata, which can be implemented as a hint, and
GL_ARB_texture_storage_multisample, which simply extends
GL_ARB_texture_storage (which is supported) to multisample texture targets.
Getting OpenGL 4.3 samples running on an OpenGL implementation that is three years out of date at launch is a pain. It’s not insurmountable, but it certainly can be challenging. If developing an engine from scratch that had to support it, it’s certainly possible to target OpenGL 4.1 as a baseline and either compile-time or run-time detect newer versions of OpenGL to enable more up-to-date features on platforms that support them. However, the 6th edition of the OpenGL SuperBible is a book about OpenGL 4.3, not OpenGL 4.1. Simply not using features introduced to OpenGL in the last three years defeats the purpose of the updated edition. Regardless, I’ve managed to get many of the OpenGL SuperBible samples running on Mac OS X 10.9.
The major part of the fixes for Mac were pushed to the master repository on Github over the weekend. The downloadable package has also been updated and is available from the example code page on this site.
- … and sometimes even leave it on in the shipping application! ↩