The traditional way to debug OpenGL is to insert glGetError() after each OpenGL function call as described in the previous blog post Debugging OpenGL part 1 – using glGetError(). Using glGetError is both cumbersome and time consuming. One of the new features in OpenGL 4.3 is the ability to get a callback whenever an OpenGL-related error occur. In this blog I’ll give a short introduction to how to use this new functionality using FreeGLUT and GLEW.
Prerequisites
First of all, you need a graphics card and a graphics card driver, which either supports OpenGL 4.3 fully or have support for glDebugMessageCallback.
To use the glDebugMessageCallback, you need to create a OpenGL context in debug mode. In FreeGlut you create a debug context by passing the GLUT_DEBUG enum to the glutInitContextFlags function. Usually you only want a debug context in a debug build. The parameter should therefor be wrapped with the following preprocessor directive.
glutInitContextFlags(GLUT_FORWARD_COMPATIBLE #if _DEBUG | GLUT_DEBUG #endif );
The callback function
The following shows a simple example how the actual callback function could look like:
void APIENTRY openglCallbackFunction(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam){ cout << "---------------------opengl-callback-start------------" << endl; cout << "message: "<< message << endl; cout << "type: "; switch (type) { case GL_DEBUG_TYPE_ERROR: cout << "ERROR"; break; case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: cout << "DEPRECATED_BEHAVIOR"; break; case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: cout << "UNDEFINED_BEHAVIOR"; break; case GL_DEBUG_TYPE_PORTABILITY: cout << "PORTABILITY"; break; case GL_DEBUG_TYPE_PERFORMANCE: cout << "PERFORMANCE"; break; case GL_DEBUG_TYPE_OTHER: cout << "OTHER"; break; } cout << endl; cout << "id: " << id << endl; cout << "severity: "; switch (severity){ case GL_DEBUG_SEVERITY_LOW: cout << "LOW"; break; case GL_DEBUG_SEVERITY_MEDIUM: cout << "MEDIUM"; break; case GL_DEBUG_SEVERITY_HIGH: cout << "HIGH"; break; } cout << endl; cout << "---------------------opengl-callback-end--------------" << endl; }
An important thing to notice: The function must be a C method with the same calling convention as the GL API functions; here this is done using APIENTRY function prefix.
Subscribing for callbacks
The only missing piece is to configure OpenGL to use the callback function after the OpenGL Context has been created. This works like many other logging libraries, where you can subscribe for certain events. In our case we want to get all possible events and therefor use GL_DONT_CARE as filter. An important detail is to enable GL_DEBUG_OUTPUT_SYNCHRONOUS, which allows us to see the origin of each error, simply by setting a breakpoint inside the callback function.
#if _DEBUG if(glDebugMessageCallback){ cout << "Register OpenGL debug callback " << endl; glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback(openglCallbackFunction, nullptr); GLuint unusedIds = 0; glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, &unusedIds, true); } else cout << "glDebugMessageCallback not available" << endl; #endif
Source code
A full running example of this is available at GitHub: https://github.com/mortennobel/OpenGL_4.3_VS_2010_freeglut
API documentation:
Hey, very helpful post. Just wanted to note two things.
1. The last argument in openglCallbackFunction should be const void * userParam, not void * userParam.
For some reason VS2012 is really picky about the difference between const void * and void *. When I used VS2010 I never had a problem with this.
2. Small typo in the debug function, line 34. I guess it should be cout << "id: " << id << endl;
cout << "severity: ";
By: Martin Leo Erik Lundgren on April 16, 2015
at 17:19
Thanks for the corrections and the kind words 🙂 I have updated the post 🙂
By: Morten Nobel-Jørgensen on April 16, 2015
at 17:32
[…] if you’re lucky. Morten Nobel’s blog here shows the usage of glDebugMessageCallback which can do the same sorts of things, but without the […]
By: Legacy GL Debugging: The GLCALL macro – Codesump on June 18, 2017
at 16:41