¿Cuál es la mejor manera de depurar OpenGL?
debugging (10)
Encuentro que muchas veces, OpenGL te mostrará que falló al no dibujar nada. Estoy tratando de encontrar formas de depurar programas OpenGL, inspeccionando la pila de matriz de transformación, etc. ¿Cuál es la mejor manera de depurar OpenGL? Si el código se ve y se siente como si los vértices estuvieran en el lugar correcto, ¿cómo puedes estar seguro de que lo están?
¿Cuál es la mejor manera de depurar OpenGL?
Sin considerar herramientas adicionales y externas (que otras respuestas ya tienen).
Entonces, la forma general es llamar extensivamente a glGetError()
. Sin embargo, una mejor alternativa es usar Debug Output ( KHR_debug , ARB_debug_output ). Esto le proporciona la funcionalidad de establecer una devolución de llamada para mensajes de distinto nivel de gravedad.
Para utilizar el resultado de depuración, el contexto debe crearse con el WGL/GLX_DEBUG_CONTEXT_BIT
. Con GLFW esto se puede establecer con la GLFW_OPENGL_DEBUG_CONTEXT
ventana GLFW_OPENGL_DEBUG_CONTEXT
.
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
Tenga en cuenta que si el contexto no es un contexto de depuración, entonces no se garantiza la recepción de todos o incluso ninguno de los mensajes.
Si usted tiene un contexto de depuración o no se puede detectar al marcar GL_CONTEXT_FLAGS
:
GLint flags;
glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
// It''s a debug context
Luego, seguir adelante y especificar una devolución de llamada:
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
}
Cada valor posible para las enumeraciones se puede ver aquí . Especialmente recuerde verificar la gravedad, ya que algunos mensajes pueden ser notificaciones y no errores.
Ahora puede hacer adelante y registrar la devolución de llamada.
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);
Incluso puede inyectar sus propios mensajes usando glDebugMessageInsert()
.
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0,
GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Vary dangerous error");
Cuando se trata de sombreadores y programas, siempre quiere comprobar GL_COMPILE_STATUS
, GL_LINK_STATUS
y GL_VALIDATE_STATUS
. Si alguno de ellos refleja que algo está mal, entonces siempre compruebe 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;
}
La cadena devuelta por glGetProgramInfoLog()
tendrá una terminación nula.
También puede ir un poco más extremo y utilizar algunas macros de depuración en una compilación de depuración. Así, el uso de glIs*()
funciona para verificar si el tipo esperado es también el tipo real.
assert(glIsProgram(program) == GL_TRUE);
glUseProgram(program);
Si la salida de depuración no está disponible y solo quiere usar glGetError()
, entonces puede hacerlo.
GLenum err;
while ((err = glGetError()) != GL_NO_ERROR)
printf("OpenGL Error: %u/n", err);
Dado que un código de error numérico no es tan útil, podríamos hacerlo un poco más legible mediante la asignación de los códigos de error numéricos a un mensaje.
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 "";
case GL_CONTEXT_LOST: return "Context Lost";
default: return "Unknown Error";
}
}
Luego revisándolo así:
printf("OpenGL Error: [%u] %s/n", err, glGetErrorString(err));
Eso todavía no es muy útil o mejor dicho intuitivo, como si hubiera rociado algunos glGetError()
aquí y allá. Luego, ubicar cuál registró un error puede ser problemático.
De nuevo, las macros vienen al rescate.
void _glCheckError(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));
}
Ahora simplemente defina una macro como esta:
#define glCheckError() _glCheckError(__FILE__, __LINE__)
y ahora puedes llamar a glCheckError()
después de todo lo que quieras, y en caso de errores te dirá el archivo exacto y la línea en la que se detectó.
Apitrace es una herramienta relativamente nueva de parte de algunas personas en Valve, ¡pero funciona de maravilla! Pruébalo: https://github.com/apitrace/apitrace
GDebugger es una excelente herramienta gratuita, pero ya no es compatible. Sin embargo, AMD ha recogido su desarrollo, y este depurador ahora se conoce como CodeXL . Está disponible como una aplicación independiente o como un complemento de Visual Studio: funciona tanto para aplicaciones C ++ nativas como para aplicaciones Java / Python que utilizan enlaces OpenGL, tanto en NVidia como en GPU AMD. Es una gran herramienta.
No hay una respuesta directa. Todo depende de lo que intentas entender. Como OpenGL es una máquina de estado, a veces no hace lo que espera, ya que no se establece el estado requerido o cosas por el estilo.
En general, utilice herramientas como glTrace / glIntercept (para ver el seguimiento de llamadas de OpenGL), gDebugger (para visualizar texturas, sombreadores, estado de OGL, etc.) y papel / lápiz :). A veces ayuda entender cómo se ha configurado la cámara y dónde está mirando, qué se está recortando, etc. Personalmente he confiado más en el último que en los dos enfoques anteriores. Pero cuando puedo argumentar que la profundidad es incorrecta, ayuda observar el rastro. gDebugger es también la única herramienta que se puede utilizar con eficacia para crear perfiles y optimizar su aplicación OpenGL.
Además de esta herramienta, la mayoría de las veces es la matemática que las personas se equivocan y no se puede entender con ninguna herramienta. Publica en el grupo de noticias de OpenGL.org para comentarios específicos del código, nunca estarás decepcionado.
Para aquellos en Mac, la carga en el depurador OpenGL también es genial. Le permite inspeccionar búferes, estados y ayuda a encontrar problemas de rendimiento.
También está el glslDevil gratis: http://www.vis.uni-stuttgart.de/glsldevil/
Le permite depurar sombreadores glsl de forma extensa. También muestra llamadas a OpenGL fallidas.
Sin embargo, le faltan funciones para inspeccionar texturas y búferes fuera de la pantalla.
GLIntercept es tu mejor apuesta. Desde su página web:
- Guarde todas las llamadas a funciones de OpenGL en formato de texto o XML con la opción de registrar marcos individuales.
- Cámara libre Vuela alrededor de la geometría enviada a la tarjeta de gráficos y habilita / deshabilita wireframe / backface-culling / view trustee render
- Guarde y rastree las listas de visualización. Almacenamiento de la memoria intermedia de marcos de OpenGL (color / profundidad / plantilla) antes y después de las llamadas al renderizado. La capacidad de guardar la "diferencia" de imágenes previas y posteriores también está disponible.
Nsight es una buena herramienta de depuración si tienes una tarjeta NVidia.
Actualizar el título de la ventana de forma dinámica es conveniente para mí.
Ejemplo (use GLFW, C ++ 11):
glfwSetWindowTitle(window, ("Now Time is " + to_string(glfwGetTime())).c_str());
Descubrí que puedes verificar usando glGetError
después de cada línea de código que tu sospechoso va a estar equivocado, pero después de hacerlo, el código no se ve muy limpio, pero funciona.