(Simulating) KHR_debug on MacOS X
KHR_debug makes finding errors in OpenGL applications much simpler than the old glGetError method. Sadly, Apple does not yet support KHR_debug (or any other debug extension) natively in OS X 10.8 and it doesn’t look too well for 10.9 either. On most other systems the latest drivers will provide KHR_debug either as an extension or as part of OpenGL 4.3 itself.
As I didn’t want to lose the option to debug my applications on MacOS X and at the same time didn’t want to support debugging via glGetError and KHR_debug, I only had two options: Keep using glGetError until Apple finally supports OpenGL 4.3 or find a way of hacking KHR_debug into (my programs on) MacOS. As waiting for Apple gets boring after a while, I decided to start some hacking.
My goal was to expose KHR_debug to my applications without having to change them (too much). It should be fully compatible with a native support of the extension as I don’t want ifdefs all over my code. And it should be trivial to add to other peoples applications (as I’m also teaching, I want something to help also my students to debug there code on systems that don’t support KHR_debug natively for any reason). The added code should not slow down the application in case debugging is not needed.
Luckily, to implement a simple KHR_debug simulator, no deep access to the GL or even the hardware is needed. All that is needed is a glGetError call after each GL call and a call of the user provided callback in case there was an error. In addition a few new functions have to be provided and the behaviour of a few others need to be changed a bit. (The details are a bit more tricky, but not too hard.)
To sum up, we need to:
- Implement glDebugMessageControl, glDebugMessageInsert, glDebugMessageCallback, glGetDebugMessageLog, glPushDebugGroup, glPopDebugGroup, glObjectLabel, glGetObjectLabel, glObjectPtrLabel and glGetObjectPtrLabel.
- Change the behaviour of glGetPointerv, glEnable, glIsEnabled, glDisable, glGetError and glGetIntegerv.
- Wrap all other GL functions in a new function that tests for errors and can generate a debug message.
Then we have two option of how to deliver this new functionality to our application:
- Write a library that gets preloaded by “set env DYLD_INSERT_LIBRARIES” (or “LD_PRELOAD” on Linux). This means that the application does not know what is happening and does not need to get recompiled. For a normal application run where full speed is desired the preloading just gets omitted. The downside of this is, that without the preload trick even the functions of KHR_debug would not be present and the code would need a lot of runtime checks for the presence of the extension – needed in production code anyway but maybe too much trouble for beginners and basic student projects.
- Hack the new functionality into the library used to load the OpenGL function pointers. We use one anyway to be platform independent so this looked like the obvious choice. The disadvantage is that we need to recompile.
Note that while I have implemented the second solution for now, building the library solution is also possible on top of what I did.
I already use glLoadGen for a couple of reasons:
- It works cross platform and also works for OpenGL 3.2 core contexts on MacOS X (GLEW has some problems with that for ages…).
- I can provide GL headers and loaders that simple don’t provide any compatibility functions anymore to my students – they don’t hand in code using glBegin anymore since that gives them compile errors :-D
- I can create GL headers and function loaders for all GL versions and never accidentally use functions of a GL version too high for my target system (in case I code on an other machine): I can for example use GL 3.2 headers and code on a GL 4.4 capable Linux to later port to MacOS X without worrying that I might have used a too recent GL function (even if I can’t limit the context version for any reason).
glLoadGen itself is not an OpenGL function loader – it’s a generator for function loaders. Basically it comes with the specs in machine readable form and a bunch of Lua scripts that can create the loader code for C or C++. By adding new Lua scripts you can create loaders for other languages or in different styles if you like. If a new GL version comes out, just update the spec definitions and re-run your scripts. It’s the perfect tool for my task of writing a couple of hundred wrapper functions (I haven’t even counted).
The documentation to edit styles in glLoadGen is – well – let’s say I learned more from tinkering with existing styles than reading the documentation but in the end that part of the project was quickly done. The resulting Lua code might not be the best (and I’m open for any input about how to build this scripts better) but it does what I want (also it’s my first work in Lua, so consider this my “hello world” ;-). The resulting loader style is compatible with the provided “C Pointer” style but also adds one new function to init the loading and enforce a (maybe emulated) debug context (see below).
Next I wrote some C code so simulate the new functions of the extension. I tried to write it in plain C as I know that some people might want to add this code to C projects and don’t want to mess with C++. Some code would have turned out smaller, faster and more reliable when using the STL instead of my own minimally tested data structures. Don’t use this work in production code – you have been warned!
Also note that my main goal was to get the callback functionality and not the object markers, debug groups or efficient message filtering. All of those functions should work as advertised in the specs (minor exceptions are listed below) but they are neither efficient nor well tested ;-)
For anyone already using the C function loader from glLoadGen this will be a (nearly) drop-in replacement, for anyone else it works like this:
- You include the header and build the two C files (glLoadGen normally only creates one, for me it was simpler to split it up). Don’t include any other GL headers, you won’t need them. If you include anything that might include GL headers (like glfw3.h), include the glLoadGen header first.
- Call ogl_LoadFunctions(); to load the GL function pointers.
- Call any GL function you like, to test if an extension is present, check if ogl_ext_EXT_name is true (e.g. ogl_ext_KHR_debug).
That’s it. Now my loader will behave nearly like the original one with one exception: If the debug extension is not present, it will add the missing functions. So far this will not impact the performance (as most GL functions are still exactly the same as before) but it will also not create a debug output. It will in fact behave like if you don’t run a debug context – unless of course you do run a debug context and have the extension natively, in which case the loader will not overshadow the real KHR_debug functions!
If however you want to also emulate a debug context (in case you don’t run one already), you can call ogl_LoadFunctionsForDebug( GL_TRUE, GL_TRUE ); The first true will activate the extension simulation if needed, the second the debug context simulation – and now every GL call will get followed by a glGetError and slow everything down. Use this for debugging only! With the same function you can disable all kinds of simulation by calling ogl_LoadFunctionsForDebug( GL_FALSE, GL_FALSE ); which will effectively do what the old C pointer loader did and not change the GL in any way.
This KHR_debug emulation is not perfect:
- It does not support multiple OpenGL contexts.
- glObjectLabel and glObjectPtrLabel do not check if the object to label exists and thus will not generate a GL_INVALID_VALUE as they would have to.
- glObjectLabel can label GL_DISPLAY_LIST even in core profiles.
Everything else should work ok-ish enough for debugging while no native support for this extension is available. I’m open for bug-reports and -fixes.
The code itself can be used under the terms of the MIT license, if that however does not work for you, drop me a mail and we can figure something out.
A OpenGL 3.2 loader to play around on MacOS X can be downloaded here (should also work on Linux and Windows).
The Lua scripts to create loaders for any GL version (and extensions) can be found here (place them in the modules folder of glLoadGen and add an entry to the style registry in modules/Styles.lua ( pointer_debug = require(“khrdebugStyle”), ).
Finally, we have no reason anymore not to build our OpenGL debugging around this extension :-D