Debugging since OpenGL 4.3

In my previous post we set up an OpenGL 4.5 context. However, we didn’t do any OpenGL error checking. In this post I’ll explain how debugging can be performed in OpenGL since version 4.3. If you want to code along with this post, you can either follow the tutorial in the previous post, or download the project files.

The old method

Prior to OpenGL 4.3 error checking had to be performed by calling glGetError() after each api-call. If that returned something else than GL_NO_ERROR something had gone wrong. The value returned by glGetError() can be converted to a human readable string using:

const char * getErrorString(GLenum type) {
  switch(type) {
    case GL_NO_ERROR: return "GL_NO_ERROR";
    case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
    case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
    case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
    case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
    case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
    case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW"; // Until OpenGL 4.2
    case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW"; // Until OpenGL 4.2
    default: return "invalid error number";
  }
}

Note: you could also have used GLU, however GLU should be considered deprecated as the specs haven’t been updated in the past 17 years.

This method is tedious, and provides little information of what is actually going wrong. Though if you’re stuck with an OpenGL version prior to 4.3, you have no choice. Luckily, you can ask Glad to create a ‘C/C++ debug’ loader, which wraps the OpenGL API methods with pre- and post-callbacks. The default post-callback checks glGetError.

The new method

Since OpenGL version 4.3 the GL_ARB_debug_output extension is included in core. It adds a callback mechanism through which the OpenGL API can report error and debug messages.

Creating a debug context

To use this callback mechanism, one must request the debug functionality to be enabled upon context creation. If you don’t, the debug functionality might still work (partially), however there is no guarantee, so some or all debug messages might be missing. With SDL2, the debug context is requested by setting a flag prior to context creation (inserted after the other SDL_GL_SetAttribute calls):

// Request a debug context.
SDL_GL_SetAttribute(
  SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG
);

The callback function

First we need to write a callback function:

static void APIENTRY openglCallbackFunction(
  GLenum source,
  GLenum type,
  GLuint id,
  GLenum severity,
  GLsizei length,
  const GLchar* message,
  const void* userParam
){
  (void)source; (void)type; (void)id; 
  (void)severity; (void)length; (void)userParam;
  fprintf(stderr, "%s\n", message);
  if (severity==GL_DEBUG_SEVERITY_HIGH) {
    fprintf(stderr, "Aborting...\n");
    abort();
  }
}

Our callback handler is minimalistic. It prints the message. If the severity is high, we print that we will abort and then abort. A lot of the callback parameters are unused, because they don’t really provide any additional information. If you’re still interested, the meaning of the parameters are explained on the Debug Output page of the OpenGL wiki.

If we would not check the severity, the OpenGL library probably aborts by itself after the callback returns, however, we would not be sure and unable to distinguish such termination from, for example, a segmentation fault. Furthermore, the call to abort also provides a nice breakpoint in case we run the program in a debugger.

Also, note that if we had omitted APIENTRY in the function signature, the function would likely use the wrong calling convention, which causes all kinds of weird behaviour and crashes.

Enabling the debug output

After the context has been created, the API has been loaded by Glad and we’ve printed the debug information about the created context, we insert the following snippet:

// Enable the debug callback
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(openglCallbackFunction, nullptr);
glDebugMessageControl(
  GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true
);

Enabling GL_DEBUG_OUTPUT is probably superfluous, however, if we didn’t create a debug context, this would enable the debug output (for as far as your OpenGL implementation likes to provide debug output in a non-debug context). It can also be passed to glDisable to disable the debug output.

Enabling GL_DEBUG_OUTPUT_SYNCHRONOUS is important. It guarantees that the callback is called by the same thread as the OpenGL api-call that invoked the callback. Without it, the callback might be called from a different thread and thus you won’t be able to obtain a meaningful stack-trace in case of an error. Enabling synchronous output might affect performance though.

Subsequently, glDebugMessageCallback is called to set the callback, followed by glDebugMessageControl, which in this case is used to tell OpenGL that we want to receive all possible callback messages.

The result

By calling glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); or GLuint x=12; glGetQueryObjectuiv(x, GL_QUERY_RESULT, &x); at inappropriate times, the following errors are reported respectively:

GL_INVALID_OPERATION error generated. Array object is not active.
GL_INVALID_OPERATION error generated. Query object not found.

This is more informative than the GL_INVALID_OPERATION that would be reported by glGetError in both cases.

Again, the resulting project is available in a zip-file.

Advertisements

One thought on “Debugging since OpenGL 4.3

  1. Pingback: Copy a texture to screen | Voxel-Engine

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s