Debug Output is an amazing OpenGL feature that makes error checking way easier and much more convenient!
Note that this feature has only been a core feature since OpenGL 4.3! Though even if you're developing and supporting prior versions, then you should definitely use version 4.3 just for debugging if possible.
Compared to glGetError
, debug output not only tells you about the errors but also sends a message explaining the cause of the error (or simply the cause of the debug message). The severity level is also send, so that you can see how serious the generated debug message should be taken and the source of it!
Here's the GL_ARB_debug_output specs!
The above output was generated by erroneously calling glBindBuffer(GL_ARRAY_BUFFER, -1)
.
OS X
On Reddit /u/sockstream pointed out that debug output isn't supported on OS X yet! Though Apple have made their own OpenGL Profiler which could be the next best thing!
Context Creation
You need to specify a flag/hint before you create the OpenGL context. To tell OpenGL that you will be using Debug Output. When you release the software remember to disable this by default, as this can impact performance a bit!
WGL (Windows)
Creating an OpenGL Context on Windows, which supports debug output. Requires you to set the WGL_CONTEXT_DEBUG_BIT_ARB
flag in WGL_CONTEXT_FLAGS_ARB
before calling wglCreateContextAttribs
.
GLX (Linux)
Creating an OpenGL Context using GLX, which supports debug output. Requires you to set the GLX_CONTEXT_DEBUG_BIT_ARB
flag in GLX_CONTEXT_FLAGS_ARB
before calling glXCreateContextAttribsARB
.
GLFW
With GLFW you must call the following before calling glfwCreateWindow
.
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
SDL
As with SDL you must specify the following attribute, before calling SDL_SetVideoMode
.
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
QT
With QT you need to specify you want a debug context in the QSurfaceFormat
object, by doing:
QSurfaceFormat format;
...
format.setProfile(QSurfaceFormat::CoreProfile);
format.setOption(QSurfaceFormat::DebugContext);
GLUT & FreeGLUT
You need to do the following, before calling glutMainLoop
.
glutInitContextProfile(GLUT_CORE_PROFILE);
glutInitContextFlags(GLUT_DEBUG);
WebGL
If you're using WebGL then this is already the end of the line for you! This is just too different from the rest, so I'm going to let Khronos explain how that is done. As they've already written a great page on how to setup a debug context with WebGL!
Android (OpenGL ES)
On Android you can't directly create a debug context. But you can enable a few debug flags when creating the GLSurfaceView
object.
GLSurfaceView surface_view;
...
surface_view.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR | GLSurfaceView.DEBUG_LOG_GL_CALLS);
Checking Context
When the context is created you can check if the context actually supports debug output. You do that by doing the following:
GLint flags;
glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) {
// Debug Output available
}
Registering OpenGL Debug Callback
When the context is created and we know it supports debug messages. Then we can enable and initialize it by doing the following:
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(debugMessage, NULL);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
GL_DEBUG_OUTPUT
- If enabled, debug messages are produced by a debug context. When disabled, the debug message log is silenced. Note that in a non-debug context, very few, if any messages might be produced, even whenGL_DEBUG_OUTPUT
is enabled.
GL_DEBUG_OUTPUT_SYNCHRONOUS?
- If enabled, debug messages are produced synchronously by a debug context. If disabled, debug messages may be produced asynchronously. In particular, they may be delayed relative to the execution of GL commands, and the debug callback function may be called from a thread other than that in which the commands are executed. SeeglDebugMessageCallback
.
This means that enabling GL_DEBUG_OUTPUT_SYNCHRONOUS
ensures us that the generated debug messages won't be thrown concurrently!
When
[GL_]DEBUG_OUTPUT_SYNCHRONOUS
is enabled, the driver guarantees synchronous calls to the callback routine by the context. When synchronous callbacks are enabled, all calls to the callback routine will be made by the thread that owns the current context; all such calls will be made serially by the current context; and each call will be made before the GL command that generated the debug message is allowed to return.
Callback Function
The message with id 131185 is an annoying Nvidia driver message. Which is generated when glBufferData
was successful!
void APIENTRY debugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
{
// Some debug messages are just annoying informational messages
switch (id)
{
case 131185: // glBufferData
return;
}
printf("Message: %s\n", message);
printf("Source: ");
switch (source)
{
case GL_DEBUG_SOURCE_API:
printf("API");
break;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
printf("Window System");
break;
case GL_DEBUG_SOURCE_SHADER_COMPILER:
printf("Shader Compiler");
break;
case GL_DEBUG_SOURCE_THIRD_PARTY:
printf("Third Party");
break;
case GL_DEBUG_SOURCE_APPLICATION:
printf("Application");
break;
case GL_DEBUG_SOURCE_OTHER:
printf("Other");
break;
}
printf("\n");
printf("Type: ");
switch (type)
{
case GL_DEBUG_TYPE_ERROR:
printf("Error");
break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
printf("Deprecated Behavior");
break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
printf("Undefined Behavior");
break;
case GL_DEBUG_TYPE_PORTABILITY:
printf("Portability");
break;
case GL_DEBUG_TYPE_PERFORMANCE:
printf("Performance");
break;
case GL_DEBUG_TYPE_MARKER:
printf("Marker");
break;
case GL_DEBUG_TYPE_PUSH_GROUP:
printf("Push Group");
break;
case GL_DEBUG_TYPE_POP_GROUP:
printf("Pop Group");
break;
case GL_DEBUG_TYPE_OTHER:
printf("Other");
break;
}
printf("\n");
printf("ID: %d\n", id);
printf("Severity: ");
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH:
printf("High");
break;
case GL_DEBUG_SEVERITY_MEDIUM:
printf("Medium");
break;
case GL_DEBUG_SEVERITY_LOW:
printf("Low");
break;
case GL_DEBUG_SEVERITY_NOTIFICATION:
printf("Notification");
break;
}
printf("\n\n");
}
Insert Debug Messages
You can also push your own debug messages using glDebugMessageInsert.
glDebugMessageInsert(
GL_DEBUG_SOURCE_APPLICATION,
GL_DEBUG_TYPE_ERROR,
0,
GL_DEBUG_SEVERITY_NOTIFICATION,
-1,
"Very dangerous error");
length
contains a count of the characters in the character array whose address is given in message. Iflength
is negative then message is treated as a null-terminated string. The length of the message, whether specified explicitly or implicitly, must be less than or equal to the implementation defined constantGL_MAX_DEBUG_MESSAGE_LENGTH
.
Edit
Thanks to Daniel in the comments for pointing this out!
Apparently there was a bug in the OpenGL headers which meant that const void *userParam
previously was defined as void *userParam
. So if you're getting any errors defining the function try to add or remove the const
keyword from the parameter.
(v17, 2013-07-08, Jon Leech) - Change type of userParam parameter to DebugMessageCallbackARB and GLDEBUGPROCARB from 'void *' to 'const void *' to match GL core and KHR_debug (Bug 10083).