Каков наилучший способ отладки OpenGL?
Без учета дополнительных и внешних инструментов (что уже делают другие ответы).
Тогда общий способ - экстенсивно позвонить glGetError()
. Однако лучшей альтернативой является использование Вывод отладки ( KHR_debug , ARB_debug_output ). Это дает вам возможность настроить обратный вызов для сообщений различной степени серьезности.
Чтобы использовать выходные данные отладки, контекст должен быть создан с флагом WGL/GLX_DEBUG_CONTEXT_BIT
. Для GLFW это можно установить с помощью подсказки GLFW_OPENGL_DEBUG_CONTEXT
.
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
Обратите внимание, что если контекст не является контекстом отладки, то получение всех или даже любых сообщений не гарантируется.
Независимо от того, есть ли у вас отладочный контекст или нет, его можно определить, установив GL_CONTEXT_FLAGS
:
GLint flags;
glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
// It's a debug context
Затем вы можете указать обратный вызов:
void debugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
const GLchar *message, const void *userParam)
{
// Print, log, whatever based on the enums and message
}
Каждое возможное значение для перечислений можно увидеть здесь . Особенно не забудьте проверить серьезность, так как некоторые сообщения могут быть просто уведомлениями, а не ошибками.
Теперь вы можете сделать заранее и зарегистрировать обратный вызов.
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);
Вы даже можете вводить свои собственные сообщения, используя glDebugMessageInsert()
.
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0,
GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Vary dangerous error");
Когда дело доходит до шейдеров и программ, вы всегда хотите проверять GL_COMPILE_STATUS
, GL_LINK_STATUS
и GL_VALIDATE_STATUS
. Если кто-то из них обнаружит, что что-то не так, то дополнительно всегда проверяйте glGetShaderInfoLog()
/ glGetProgramInfoLog()
.
GLint linkStatus;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (!linkStatus)
{
GLchar *infoLog = new GLchar[infoLogLength + 1];
glGetProgramInfoLog(program, infoLogLength * sizeof(GLchar), NULL, infoLog);
...
delete[] infoLog;
}
Строка, возвращаемая glGetProgramInfoLog()
, будет оканчиваться нулем.
Вы также можете пойти немного дальше и использовать несколько макросов отладки в сборке отладки. Таким образом, используя функции glIs*()
, можно проверить, является ли ожидаемый тип фактическим.
assert(glIsProgram(program) == GL_TRUE);
glUseProgram(program);
Если выходные данные отладки недоступны, и вы просто хотите использовать glGetError()
, тогда вы, конечно, можете это сделать.
GLenum err;
while ((err = glGetError()) != GL_NO_ERROR)
printf("OpenGL Error: %u\n", err);
Поскольку числовой код ошибки не очень полезен, мы можем сделать его более понятным для человека, сопоставив числовые коды ошибок с сообщением.
const char* glGetErrorString(GLenum error)
{
switch (error)
{
case GL_NO_ERROR: return "No Error";
case GL_INVALID_ENUM: return "Invalid Enum";
case GL_INVALID_VALUE: return "Invalid Value";
case GL_INVALID_OPERATION: return "Invalid Operation";
case GL_INVALID_FRAMEBUFFER_OPERATION: return "Invalid Framebuffer Operation";
case GL_OUT_OF_MEMORY: return "Out of Memory";
case GL_STACK_UNDERFLOW: return "Stack Underflow";
case GL_STACK_OVERFLOW: return "Stack Overflow";
case GL_CONTEXT_LOST: return "Context Lost";
default: return "Unknown Error";
}
}
Затем проверяем это так:
printf("OpenGL Error: [%u] %s\n", err, glGetErrorString(err));
Это все еще не очень полезно или, лучше сказать, интуитивно, как если бы вы высыпали несколько glGetError()
здесь и там. Тогда поиск того, кто из них зарегистрировал ошибку, может быть проблематичным.
Снова на помощь приходят макросы.
void _glCheckErrors(const char *filename, int line)
{
GLenum err;
while ((err = glGetError()) != GL_NO_ERROR)
printf("OpenGL Error: %s (%d) [%u] %s\n", filename, line, err, glGetErrorString(err));
}
Теперь просто определите макрос следующим образом:
#define glCheckErrors() _glCheckErrors(__FILE__, __LINE__)
и вуаля теперь вы можете звонить glCheckErrors()
после всего, что вы хотите, и в случае ошибок он сообщит вам точный файл и строку, в которой он был обнаружен.