Je trouve que la plupart du temps, OpenGL vous montrera qu'il a échoué en ne dessinant rien. J'essaie de trouver des moyens de déboguer des programmes OpenGL, en inspectant la pile de matrices de transformation et ainsi de suite. Quelle est la meilleure façon de déboguer OpenGL? Si le code donne l'impression que les sommets sont au bon endroit, comment pouvez-vous en être sûr?
Il n'y a pas de réponse directe. Tout dépend de ce que vous essayez de comprendre. Depuis OpenGL est une machine à états, parfois il ne fait pas ce que vous attendez car l'état requis n'est pas défini ou des choses comme ça.
En général, utilisez des outils comme glTrace/glIntercept (pour regarder la trace d'appel OpenGL), gDebugger (pour visualiser les textures, les shaders, l'état OGL, etc.) et le papier/crayon :). Parfois, cela aide à comprendre comment vous avez configuré la caméra et où elle se trouve, ce qui est coupé, etc. Je me suis personnellement davantage appuyé sur la dernière que sur les deux approches précédentes. Mais quand je peux affirmer que la profondeur est mauvaise, cela aide à regarder la trace. gDebugger est également le seul outil qui peut être utilisé efficacement pour le profilage et l'optimisation de votre application OpenGL.
En dehors de cet outil, la plupart du temps, c'est le calcul que les gens se trompent et cela ne peut être compris en utilisant aucun outil. Publiez sur le groupe de discussion OpenGL.org pour des commentaires spécifiques au code, vous ne serez jamais déçu.
GLIntercept est votre meilleur pari. Depuis leur page Web:
Apitrace est un outil relativement nouveau de certaines personnes chez Valve, mais il fonctionne très bien! Essayez-le: https://github.com/apitrace/apitrace
Quelle est la meilleure façon de déboguer OpenGL?
Sans considérer les outils supplémentaires et externes (ce que font déjà d'autres réponses).
Ensuite, la méthode générale consiste à appeler de manière extensive glGetError()
. Cependant, une meilleure alternative consiste à utiliser Debug Output ( KHR_debug , ARB_debug_output ). Cela vous permet de définir un rappel pour les messages de niveau de gravité variable.
Afin d'utiliser la sortie de débogage, le contexte doit être créé avec l'indicateur WGL/GLX_DEBUG_CONTEXT_BIT
. Avec GLFW, cela peut être défini avec l'indicateur de fenêtre GLFW_OPENGL_DEBUG_CONTEXT
.
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
Notez que si le contexte n'est pas un contexte de débogage, la réception de tous ou même des messages n'est pas garantie.
Que vous ayez un contexte de débogage ou non peut être détecté en vérifiant GL_CONTEXT_FLAGS
:
GLint flags;
glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
// It's a debug context
Vous devriez ensuite aller de l'avant et spécifier un rappel:
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
}
Chaque valeur possible pour les énumérations peut être vue ici . N'oubliez surtout pas de vérifier la gravité, car certains messages peuvent simplement être des notifications et non des erreurs.
Vous pouvez maintenant faire à l'avance et enregistrer le rappel.
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);
Vous pouvez même injecter vos propres messages en utilisant glDebugMessageInsert()
.
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0,
GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Vary dangerous error");
En ce qui concerne les shaders et les programmes, vous devez toujours vérifier GL_COMPILE_STATUS
, GL_LINK_STATUS
Et GL_VALIDATE_STATUS
. Si l'un d'eux indique que quelque chose ne va pas, vérifiez en outre toujours 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 chaîne renvoyée par glGetProgramInfoLog()
sera terminée par null.
Vous pouvez également aller un peu plus loin et utiliser quelques macros de débogage dans une version de débogage. Ainsi, en utilisant les fonctions glIs*()
pour vérifier si le type attendu est également le type réel.
assert(glIsProgram(program) == GL_TRUE);
glUseProgram(program);
Si la sortie de débogage n'est pas disponible et que vous souhaitez simplement utiliser glGetError()
, vous êtes bien sûr libre de le faire.
GLenum err;
while ((err = glGetError()) != GL_NO_ERROR)
printf("OpenGL Error: %u\n", err);
Puisqu'un code d'erreur numérique n'est pas très utile, nous pourrions le rendre un peu plus lisible par l'homme en mappant les codes d'erreur numériques sur un message.
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";
}
}
Ensuite, vérifiez-le comme ceci:
printf("OpenGL Error: [%u] %s\n", err, glGetErrorString(err));
Ce n'est toujours pas très utile ou mieux dit intuitif, comme si vous en aviez saupoudré quelques glGetError()
ici et là. Ensuite, localiser celui qui a enregistré une erreur peut être gênant.
Encore une fois, les macros viennent à la rescousse.
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));
}
Définissez maintenant simplement une macro comme celle-ci:
#define glCheckErrors() _glCheckErrors(__FILE__, __LINE__)
et voila maintenant vous pouvez appeler glCheckErrors()
après tout ce que vous voulez, et en cas d'erreur il vous indiquera le fichier exact et la ligne où il a été détecté.
J'ai trouvé que vous pouvez vérifier en utilisant glGetError
après chaque ligne de code, votre suspect se trompera, mais après le faire, le code n'est pas très propre mais il fonctionne.
Le gDebugger est un excellent outil gratuit, mais n'est plus pris en charge. Cependant, AMD a repris son développement, et ce débogueur est désormais connu sous le nom de CodeXL . Il est disponible à la fois en tant qu'application autonome ou en tant que plug-in Visual Studio - fonctionne à la fois pour les applications C++ natives ou les applications Java/Python utilisant des liaisons OpenGL, à la fois sur les GPU NVidia et AMD. C'est un sacré outil.
Pour ceux sur Mac, le débogueur OpenGL intégré est également très bien. Il vous permet d'inspecter les tampons, les états et aide à trouver les problèmes de performances.
Il y a aussi le glslDevil gratuit: http://www.vis.uni-stuttgart.de/glsldevil/
Il vous permet de déboguer de manière approfondie les shaders glsl. Il montre également les appels OpenGL ayant échoué.
Cependant, il manque des fonctionnalités pour inspecter les textures et les tampons hors écran.
Mettre à jour le titre de la fenêtre dynamiquement me convient.
Exemple (utilisez GLFW, C++ 11):
glfwSetWindowTitle(window, ("Now Time is " + to_string(glfwGetTime())).c_str());
Nsight est un bon outil de débogage si vous avez une carte NVidia.