Debug Output

From OpenGL.org
Jump to: navigation, search
Debug Output
Core in version 4.4
Core since version 4.3
Core ARB extension KHR_debug
ARB extension ARB_debug_output
Vendor extension AMD_debug_output

Debug Output is an OpenGL feature that makes Error Checking from functions easier.

Note that this feature, which is core in 4.3, has functionality that is generally exposed by two different extensions. The KHR extension is the core feature, which is always advertised when available. The other two are generally only advertised when the OpenGL Context was created with the WGL/GLX_DEBUG_CONTEXT_BIT​.

This page describes the KHR/core functionality. The ARB/AMD version contains only the message log part, without the object names or the scoped messages. They also have some slight differences in the callback function parameters and the types of messages they work with.

Message

The heart of debug output is a message. Messages are generated when certain things happen in the OpenGL implementation; the user can even generate message of their own.

In order to have messages generated at all, debug output must first be enabled. If the context is a debug context (ie: GL_CONTEXT_FLAG_DEBUG_BIT​ is part of the GL_CONTEXT_FLAGS​ state), then debug output is already enabled. Otherwise it starts disabled and must be enabled with an explicit glEnable(GL_DEBUG_OUTPUT).

If the context isn't a debug context, then the implementation is free to provide no messages at all, even user-generated ones.

Messages can come from a variety of sources, but they each contain the following data:

  • The source that produced the message.
  • The type of message.
  • The message severity (how important it is).
  • An ID, as a GLuint​.
  • A null-terminated string describing the message.

Implementations may create messages at their discretion. In general, you can expect to see messages for OpenGL Errors and GLSL compiling and linking failures. From there, it becomes a quality-of-implementation question.

Message Sources
Source enum Generated by
DEBUG_SOURCE_API​ Calls to the OpenGL API
DEBUG_SOURCE_WINDOW_SYSTEM​ Calls to a window-system API
DEBUG_SOURCE_SHADER_COMPILER​ A compiler for a shading language
DEBUG_SOURCE_THIRD_PARTY​ An application associated with OpenGL
DEBUG_SOURCE_APPLICATION​ Generated by the user of this application
DEBUG_SOURCE_OTHER​ Some source that isn't one of these
Message Types
Type enum Meaning
DEBUG_TYPE_ERROR​ An error, typically from the API
DEBUG_TYPE_DEPRECATED_BEHAVIOR​ Some behavior marked deprecated has been used
DEBUG_TYPE_UNDEFINED_BEHAVIOR​ Something has invoked undefined behavior
DEBUG_TYPE_PORTABILITY​ Some functionality the user relies upon is not portable
DEBUG_TYPE_PERFORMANCE​ Code has triggered possible performance issues
DEBUG_TYPE_MARKER​ Command stream annotation
DEBUG_TYPE_PUSH_GROUP​ Group pushing
DEBUG_TYPE_POP_GROUP​ foo
DEBUG_TYPE_OTHER​ Some type that isn't one of these
Message Severity
Severity enum Meaning
DEBUG_SEVERITY_HIGH​ All OpenGL Errors, shader compilation/linking errors, or highly-dangerous undefined behavior
DEBUG_SEVERITY_MEDIUM​ Major performance warnings, shader compilation/linking warnings, or the use of deprecated functionality
DEBUG_SEVERITY_LOW​ Redundant state change performance warning, or unimportant undefined behavior
DEBUG_SEVERITY_NOTIFICATION​ Anything that isn't an error or performance issue.

User messages

Getting messages

Messages, once generated, can either be stored in a log or passed directly to the application via a callback function. If a callback is registered, then the messages are not stored in a log.

Messages can be routed to a callback via this function:

void glDebugMessageCallback(DEBUGPROC callback​, void* userParam​);

The type of callback​ is a function of the form:

typedef void APIENTRY funcname(GLenum source​, GLenum type​, GLuint id​,
   GLenum severity​, GLsizei length​, const GLchar* message​, void* userParam​);

source​, type​, and severity​ are the messages source, type, and severity enumerators. id​ is the message's identifier integer. length​ is the length of the message​ string, excluding the null-terminator.

The userParam​ that is registered with glDebugMessageCallback will be passed to the given callback function with each message.

The callback can be called synchronously or asynchronously. This is controlled by the glEnable flag GL_DEBUG_OUTPUT_SYNCHRONOUS​. If this flag is enabled, then OpenGL guarantees that your callback will be called:

  • In the same thread as the context.
  • In the scope of the OpenGL function call that fired the message.

If the flag is disabled, then none of these are guaranteed. So if you want asynchronous debug output (for performance reasons), you must take into account the following possibilities:

  • The thread the callback is called on is not the context's thread.
  • Multiple instances of the same callback function may be called at the same time.
  • Messages may be issued out of the order of their occurrence.

Logging

If no callback is registered, then messages are stored in a log. This log has a fixed, implementation defined length of GL_MAX_DEBUG_LOGGED_MESSAGES​ message entries. If the log is full and more messages are generated, then the new messages will be discarded.

Messages from the log can be fetched with this function:

GLuint glGetDebugMessageLog(GLuint count​, GLsizei bufSize​,
   GLenum *sources​, Glenum *types​, GLuint *ids​, GLenum *severities​,
   GLsizei *lengths​, GLchar *messageLog​);

count​ is the number of messages that you want to try to fetch. The return value is the number of messages actually fetched. Any messages successfully fetched will be removed from the message log.

sources​, types​, ids​, severities​, lengths​ are arrays that must be at least count​ in size. For each successfully extracted message, an entry in these arrays will be filled in with the appropriate message data. The lengths​ are the lengths of the string for that index's corresponding messages.

The behavior of the character data is more difficult to deal with. messageLog​ is a single array of characters, of at least bufSize​ in size. The function will copy into this string the message strings for each message extracted, separated by null characters (and terminated by one). However, if it is unable to copy a string due to lack of space remaining in bufSize​, then this message, and all subsequent messages, will remain in the log.

Therefore, if you do not provide enough space in messageLog​, you cannot get any messages from the log. You can query the length of the string in the first message of the log with GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH​. This includes the null-terminator, so you can use that directly to get at least the first message. If you want to be able to query any number of messages, you can use the implementation-defined GL_MAX_DEBUG_MESSAGE_LENGTH​ value, which is the maximum length (including null-terminator) of message strings.

Here is an example of code that can get the first N messages:

V · E

This example code shows how to get the first X messages from the debug output log.

void GetFirstNMessages(GLuint numMsgs)
{
        GLint maxMsgLen = 0;
        glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH, &maxMsgLen);
 
        std::vector<GLchar> msgData(numMsgs * maxMsgLen);
        std::vector<GLenum> sources(numMsgs);
        std::vector<GLenum> types(numMsgs);
        std::vector<GLenum> severities(numMsgs);
        std::vector<GLuint> ids(numMsgs);
        std::vector<GLsizei> lengths(numMsgs);
 
        GLuint numFound = glGetDebugMessageLog(numMsgs, msgs.size(), &sources[0], &types[0], &ids[0], &severities[0], &lengths[0], &msgData[0]);
 
        sources.resize(numFound);
        types.resize(numFound);
        severities.resize(numFound);
        ids.resize(numFound);
        lengths.resize(numFound);
 
        std::vector<std::string> messages;
        messages.reserve(numFound);
 
        std::vector<GLchar>::iterator currPos = msgData.begin();
        for(size_t msg = 0; msg < lengths.size(); ++msg)
        {
                messages.push_back(std::string(currPos, currPos + lengths[msg] - 1));
                currPos = currPos + lengths[msg];
        }
}


Message filtering

Implementations can emit a lot of messages. As such, it is often useful to be able to filter out certain messages.

Scoping messages

Object names