Debug Callback
The debug callback is a piece of code called by OpenGL in case it disagrees with the values you passed to its functions.
Let's hook it up.
A free floating function above main
.
Explain the following blob a bit more
void GLAPIENTRY DebugMessageCallback(
GLenum source,
GLenum type,
GLuint id,
GLenum severity,
[[maybe_unused]] GLsizei length,
const GLchar* message,
[[maybe_unused]] const void* userParam)
{
// Ignore certain verbose info messages (particularly ones on Nvidia).
if (id == 131169 ||
id == 131185 || // NV: Buffer will use video memory
id == 131218 ||
id == 131204 || // Texture cannot be used for texture mapping
id == 131222 ||
id == 131154 || // NV: pixel transfer is synchronized with 3D rendering
id == 0 // gl{Push, Pop}DebugGroup
)
return;
std::stringstream debugMessageStream;
debugMessageStream << message << '\n';
switch (source)
{
case GL_DEBUG_SOURCE_API: debugMessageStream << "Source: API"; break;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: debugMessageStream << "Source: Window Manager"; break;
case GL_DEBUG_SOURCE_SHADER_COMPILER: debugMessageStream << "Source: Shader Compiler"; break;
case GL_DEBUG_SOURCE_THIRD_PARTY: debugMessageStream << "Source: Third Party"; break;
case GL_DEBUG_SOURCE_APPLICATION: debugMessageStream << "Source: Application"; break;
case GL_DEBUG_SOURCE_OTHER: debugMessageStream << "Source: Other"; break;
}
debugMessageStream << '\n';
switch (type)
{
case GL_DEBUG_TYPE_ERROR: debugMessageStream << "Type: Error"; break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: debugMessageStream << "Type: Deprecated Behaviour"; break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: debugMessageStream << "Type: Undefined Behaviour"; break;
case GL_DEBUG_TYPE_PORTABILITY: debugMessageStream << "Type: Portability"; break;
case GL_DEBUG_TYPE_PERFORMANCE: debugMessageStream << "Type: Performance"; break;
case GL_DEBUG_TYPE_MARKER: debugMessageStream << "Type: Marker"; break;
case GL_DEBUG_TYPE_PUSH_GROUP: debugMessageStream << "Type: Push Group"; break;
case GL_DEBUG_TYPE_POP_GROUP: debugMessageStream << "Type: Pop Group"; break;
case GL_DEBUG_TYPE_OTHER: debugMessageStream << "Type: Other"; break;
}
debugMessageStream << '\n';
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH: debugMessageStream << "Severity: high"; break;
case GL_DEBUG_SEVERITY_MEDIUM: debugMessageStream << "Severity: medium"; break;
case GL_DEBUG_SEVERITY_LOW: debugMessageStream << "Severity: low"; break;
case GL_DEBUG_SEVERITY_NOTIFICATION: debugMessageStream << "Severity: notification"; break;
}
spdlog::error("OpenGL Message: {} {}", type, debugMessageStream.str());
}
That alone doesn't do anything. We need to tell OpenGL
about it too. After gladLoadGLLoader
we add those lines.
glDebugMessageCallback(DebugMessageCallback, nullptr);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback
hands over our callback to OpenGL, and the next glEnable
call enables the debug callback in general.
The next line glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS)
is the neat part.
If we place a break point in our debug callback, let's say at this line spdlog::error("OpenGL Message: ...
then we will naturally get a call stack, but we will also get the right callstack and can follow it to the offending glXXX function which OpenGL was not happy about.
The output also contains somewhat meaningful output which can tell us what exactly we did wrong, well, its not always perfect in telling us whats the problem, but its 10000% better than what was used before... the dreaded GLCHECK(...)
(or GLCALL
or whatever else people used to call it) macro, which in most cases called glGetError
which didnt provide any meaningful hint about what the problem is.
Let's try it out.
I have prepared 2 exercises.