I've been thinking about error handling in OpenGL lately, as I'm slowly rewriting my hobby project, a C++ OpenGL wrapper. Error handling definitely one of those less exciting things, but still worth talking about in my opinion. I don't have any specific question here, other than maybe the very generic "how to do error handling?" or "how do you do it?" so would be happy to hear your thoughts and what approaches have you taken in your projects. Of course, comments on the text below are very welcome also.
So, here's my current thoughts on the subject. These are what I see to be the four overall options how to do the error handling:
1. No error handling in the application. Possibly the option to use in release mode, but with external tools to intercept calls this could work when developing too. I'm not knowledgeable about these at all, which probably explains why these options are not centered around what tool to use but how to handle errors within the code.
2. The "old-style" glGetError after every call to OpenGL. Use of glGet* functions is apparently not very good idea performance-wise, so probably will not be used in release configuration. Here I should add that I don't like having builds that differ from each other much, but in this case it seems justified. Maybe this could be done behind an if instead of #ifdef?
3. Synchronous use of the newer debug callback functionality, when using a debug context. (See http://www.opengl.org/registry/specs/ARB/debug_output.txt) I guess this kind of equates to the driver doing the glGetError internally, except that it can give much more in depth error messages.
4. Asynchronous use of the debug callback functionality. Set up similarly than the previous option, but has some implications which is why I separated it into its own point. More about this below.
There's also the more concrete level, that could be split into setup phase and then the handling of each actual OpenGL call. The setup phase is not very relevant to options 1 and 2, but obviously the 3 and 4 need to have the debug callback set up. Now, finally the part that really makes me think - let's go through the error handling options 2 to 4:
2. glGetError: Error handling can be done just by adding a check after every call. However, just outputting GL_INVALID_VALUE is hardly useful, so some context needs to be added. In C/C++ there are some macros like __FILE__ and __LINE__ that can be used. Going this way ends up with code where after every GL call there is something like "CHECK_GL_ERROR();" there. There could be more than that (in some cases also throw an exception etc.), but mostly there's going to be just the macro.
3. Synchronous debug callback: This does not require any special code where the OpenGL calls are made (at least not when nothing needs to happen in case of an error), but the context will be missing. I'm unsure if the vendor-specific debug messages will contain the function that was called - though that would make very much sense. Avoiding calling the same function from many places is not always possible (see glEnable).
Anyway, to add context to the callback, I could store somewhere the __FILE__ and __LINE__ before doing the call, and from there the callback can pull the information and add it to the output. So now the macro should be situated before the GL call. At this point I started to think about wrapping the call entirely into a macro call, because then the same same macro could then expand to either option 2 or 3. It could be done as something like "CHECKED_GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, bufferId));" Variadic macros, as far as I know, would make it possible to do something like "CALL_GL(glBindBuffer, GL_ARRAY_BUFFER, bufferId);" For some reason the second option appeals to me bit more, but the first one is probably easier to reason about when first time seeing the code.
4. Asynchronous debug callback: This is the preferred way to use the debug callback according to the debug output extension text The downside is that setting up the file and line information somewhere doesn't seem to really work because of the asynchronicity. They'd be nice to have, but maybe that's just the price that has to be paid for the performance? All the macro trickery is therefore unnecessary, but I'm thinking about if it would still be worth doing the wrapping calls in macros thing to allow the other options.