我发现很多时候,OpenGL 会通过不绘制任何东西来告诉你它失败了。我试图通过检查转换矩阵堆栈等找到调试 OpenGL 程序的方法。调试 OpenGL 的最佳方法是什么?如果代码看起来和感觉顶点都在正确的位置,你怎么能确定它们是正确的?
10 回答
没有直接的答案。这一切都取决于你试图理解什么。由于 OpenGL 是一个状态机,有时它不会做你期望的事情,因为没有设置所需的状态或类似的事情。
通常,使用 glTrace / glIntercept(查看 OpenGL 调用跟踪)、gDebugger(可视化纹理、着色器、OGL 状态等)和纸/铅笔 :) 之类的工具。有时它有助于了解您如何设置相机以及它正在寻找的位置,正在剪辑的内容等。我个人比前两种方法更依赖于最后。但是,当我可以争辩说深度是错误的时,查看轨迹会有所帮助。gDebugger 也是唯一可以有效用于分析和优化 OpenGL 应用程序的工具。
除了这个工具之外,大多数时候人们会出错并且使用任何工具都无法理解的数学。在 OpenGL.org 新闻组上发布代码特定评论,您将永远不会失望。
调试 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()
所需的所有内容,如果出现错误,它会告诉您检测到的确切文件和行。
GLIntercept是您最好的选择。从他们的网页:
- 使用记录单个帧的选项将所有 OpenGL 函数调用保存为文本或 XML 格式。
- 免费相机。环绕发送到显卡的几何图形并启用/禁用线框/背面剔除/查看平截头体渲染
- 保存和跟踪显示列表。在渲染调用前后保存 OpenGL 帧缓冲区(颜色/深度/模板)。还可以保存前后图像的“差异”。
Apitrace 是 Valve 的一些人提供的一个相对较新的工具,但它非常好用!试一试:https ://github.com/apitrace/apitrace
我发现你可以glGetError
在每一行代码之后检查你的嫌疑人是否会出错,但是在这样做之后,代码看起来不是很干净,但它可以工作。
对于 Mac 上的用户,OpenGL 调试器中的 buit 也很棒。它使您可以检查缓冲区、状态并帮助发现性能问题。
gDebugger 是一款出色的免费工具,但不再受支持。然而,AMD 已经开始了它的开发,这个调试器现在被称为CodeXL。它既可作为独立应用程序使用,也可作为 Visual Studio 插件使用 - 适用于 NVidia 和 AMD GPU 上的原生 C++ 应用程序或使用 OpenGL 绑定的 Java/Python 应用程序。这是一个地狱般的工具。
还有免费的 glslDevil: http: //www.vis.uni-stuttgart.de/glsldevil/
它允许您广泛地调试 glsl 着色器。它还显示失败的 OpenGL 调用。
但是,它缺少检查纹理和屏幕外缓冲区的功能。
如果您有 NVidia 卡,Nsight是一个很好的调试工具。
动态更新窗口标题对我来说很方便。
示例(使用 GLFW,C++11):
glfwSetWindowTitle(window, ("Now Time is " + to_string(glfwGetTime())).c_str());