OpenGL调试技巧汇总
前言:由于在GLSL代码中不能设置断点,也没有方法检测GPU的运行状态。当发生视觉错误时也没有一个可以用来输出文本的控制台,从而调试OpenGL就变得十分的困难。我这里整理一些常用调试技巧,方便大家更加舒爽的使用OpenGL,而不至于因为错误而沮丧。
使用glGetError:当我们不正常使用OpenGL接口时,会在幕后生成一个或多个错误标记,此时可以使用glGetError函数查询这些错误标记。glGetError会清除之前所有的错误标记(在分布式系统上面只会清除一个错误标记)并返回一个新的错误标记。
其中glGetError函数原型如下:
GLenum glGetError(void)
其中错误标记类型如下:
由于错误标记语义晦涩难懂,此时我们可以自己封装一个glCheckError接口,内部调用glGetError并对每个错误标记进行文字描述。封装的代码如下所示:
GLenum glCheckError_(const char *file, int line)
{
GLenum errorCode;
while ((errorCode = glGetError()) != GL_NO_ERROR)
{
std::string error;
switch (errorCode)
{
case GL_INVALID_ENUM: error = "INVALID_ENUM"; break;
case GL_INVALID_VALUE: error = "INVALID_VALUE"; break;
case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break;
case GL_STACK_OVERFLOW: error = "STACK_OVERFLOW"; break;
case GL_STACK_UNDERFLOW: error = "STACK_UNDERFLOW"; break;
case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break;
case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break;
}
std::cout << error << " | " << file << " (" << line << ")" << std::endl;
}
return errorCode;
}
#define glCheckError() glCheckError_(__FILE__, __LINE__)
调试输出:由于篇幅较多,建议参考https://blog.****.net/zjz520yy/article/details/83047042。
调试着色器输出:这是一种比较原始的调试方式。主要是将着色器程序中所有相关的变量直接发送到片段着色器的输出通道,通过观察视觉结果来获取有用的信息。
GLSL参考编译器:就是使用OpenGL官方提供的编译器来检测着色器代码是否符合OpenGL规范。
检测代码如下所示:
glsllangvalidator shaderFile.vert
不符合规范时对应的错误提示如图所示:
帧缓冲输出:由于帧缓冲的魔法通常在幕后进行,有时候想要知道出什么问题会非常困难。这个时候就可以在OpenGL程序中一块特定区域显示帧缓冲的内容,从而提前知道输出的内容,根据输出内容推测出现的问题。但是这种方法只能用在纹理附件上,此时可以写一个助手函数快速在屏幕右上角显示任何纹理。核心代码如下所示:
/ 顶点着色器
#version 330 core
layout (location = 0) in vec2 position;
layout (location = 1) in vec2 texCoords;
out vec2 TexCoords;
void main()
{
gl_Position = vec4(position, 0.0f, 1.0f);
TexCoords = texCoords;
}
// 片段着色器
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D fboAttachment;
void main()
{
FragColor = texture(fboAttachment, TexCoords);
}
void DisplayFramebufferTexture(GLuint textureID)
{
if(!notInitialized)
{
// 在屏幕右上角,使用NDC顶点坐标初始化着色器和VAO
[...]
}
glActiveTexture(GL_TEXTURE0);
glUseProgram(shaderDisplayFBOOutput);
glBindTexture(GL_TEXTURE_2D, textureID);
glBindVertexArray(vaoDebugTexturedRect);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
glUseProgram(0);
}
int main()
{
[...]
while (!glfwWindowShouldClose(window))
{
[...]
DisplayFramebufferTexture(fboAttachment0);
glfwSwapBuffers(window);
}
}
调试窗口大概如图所示:
外部调试软件:第三方应用通常将它们自己注入到OpenGL驱动中,并且能够拦截各种OpenGL调用,给你大量有用的数据。常见的应用如下所示:
1.NVIDIA Nsight:由于内容篇幅较多,参见https://blog.****.net/zjz520yy/article/details/83095483。
2.AMD CodeXL:由于内容篇幅较多,参见https://blog.****.net/zjz520yy/article/details/83095498。
3.gDebugger:由于内容篇幅较多,参见https://blog.****.net/zjz520yy/article/details/83095507。