调试 OpenGL 的最佳方法是啥?

Posted

技术标签:

【中文标题】调试 OpenGL 的最佳方法是啥?【英文标题】:What is the best way to debug OpenGL?调试 OpenGL 的最佳方法是什么? 【发布时间】:2009-02-05 21:36:27 【问题描述】:

我发现很多时候,OpenGL 会通过不绘制任何东西来告诉你它失败了。我试图通过检查转换矩阵堆栈等找到调试 OpenGL 程序的方法。调试 OpenGL 的最佳方法是什么?如果代码看起来和感觉顶点都在正确的位置,你怎么能确定它们是正确的?

【问题讨论】:

【参考方案1】:

没有直接的答案。这一切都取决于你试图理解什么。由于 OpenGL 是一个状态机,因此有时它不会按照您的预期执行,因为未设置所需的状态或类似的事情。

一般来说,使用 glTrace / glIntercept(查看 OpenGL 调用跟踪)、gDebugger(可视化纹理、着色器、OGL 状态等)和纸/铅笔 :) 等工具。有时它有助于了解您如何设置相机以及它正在寻找的位置,正在剪辑的内容等。我个人比前两种方法更依赖于最后。但是,当我可以争辩说深度是错误的时,查看轨迹会有所帮助。 gDebugger 也是唯一的工具,可以有效地用于分析和优化 OpenGL 应用程序。

除了这个工具之外,大多数时候人们会出错并且使用任何工具都无法理解数学。在 OpenGL.org 新闻组上发布代码特定的 cmets,您将永远不会失望。

【讨论】:

还有学术许可证,如果你质量好的话 由于这个问题仍然弹出,也许模组可以编辑它并更正它现在是免费的?【参考方案2】:

调试 OpenGL 的最佳方法是什么?

不考虑其他和外部工具(其他答案已经这样做了)。

那么一般的方法是广泛调用glGetError()。然而,更好的选择是使用Debug Output (KHR_debug, ARB_debug_output)。这为您提供了为不同严重级别的消息设置回调的功能。

为了使用调试输出,必须使用WGL/GLX_DEBUG_CONTEXT_BIT 标志创建上下文。 With GLFW this can be set with the GLFW_OPENGL_DEBUG_CONTEXT window hint.

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

枚举can be seen on here 的每个可能值。尤其要记得检查严重性,因为有些消息可能只是通知而不是错误。

您现在可以提前注册回调。

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_STATUSGL_LINK_STATUSGL_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(),如果出现错误,它会告诉你检测到的确切文件和行。

【讨论】:

调试输出似乎没有记录着色器编译错误:( @WingerSendon 我不记得什么/如果标准保证窗帘调试输出消息。然而,从司机到司机,信息确实有所不同。但是使用glGet(Shader|Program)InfoLog 查询着色器/程序错误。 Debug Output Wiki 说“在调试上下文中,需要实现来生成 OpenGL 错误和 GLSL 编译/链接失败的消息”。显然我的司机不在乎 @WingerSendon 是的,可能是这样,假设您复制了我的示例。我最好的猜测是人们通常会检查信息日志,所以这可能是它没有通过调试输出推送的原因。【参考方案3】:

GLIntercept 是您最好的选择。从他们的网页:

将所有 OpenGL 函数调用保存为文本或 XML 格式,并可选择记录单个帧。 免费相机。环绕发送到显卡的几何图形并启用/禁用线框/背面剔除/视锥体渲染 保存和跟踪显示列表。 在渲染调用前后保存 OpenGL 帧缓冲区(颜色/深度/模板)。还可以保存前后图像的“差异”。

【讨论】:

【参考方案4】:

Apitrace 是 Valve 的一些人提供的一个相对较新的工具,但它非常好用!试试看:https://github.com/apitrace/apitrace

【讨论】:

【参考方案5】:

我发现你可以在每行代码之后使用glGetError检查你的嫌疑人是否会出错,但是在这样做之后,代码看起来不是很干净但它可以工作。

【讨论】:

【参考方案6】:

对于 Mac 上的用户,OpenGL 调试器中的内置功能也很棒。它可以让您检查缓冲区、状态并帮助发现性能问题。

【讨论】:

【参考方案7】:

gDebugger 是一款出色的免费工具,但不再受支持。然而,AMD 已经开始了它的开发,这个调试器现在被称为CodeXL。它既可作为独立应用程序使用,也可作为 Visual Studio 插件使用 - 适用于 NVidia 和 AMD GPU 上的原生 C++ 应用程序或使用 OpenGL 绑定的 Java/Python 应用程序。这是一种非常棒的工具。

【讨论】:

【参考方案8】:

还有免费的 glslDevil:http://www.vis.uni-stuttgart.de/glsldevil/

它允许您广泛地调试 glsl 着色器。它还显示失败的 OpenGL 调用。

但是,它缺少检查纹理和屏幕外缓冲区的功能。

【讨论】:

该链接似乎已损坏。 已开源并重命名为GLSL Debugger,链接:github.com/GLSL-Debugger/GLSL-Debugger【参考方案9】:

Nsight 是一个很好的调试工具,如果你有 NVidia 卡的话。

【讨论】:

【参考方案10】:

动态更新窗口标题对我来说很方便。

示例(使用 GLFW,C++11):

glfwSetWindowTitle(window, ("Now Time is " + to_string(glfwGetTime())).c_str());

【讨论】:

以上是关于调试 OpenGL 的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

用openGL绘制频谱图的最佳方法是啥

绘制分层 OpenGL ES 内容的最佳方式是啥?

在 OpenGL 中绘制形状的标准(常用)方法是啥?

OpenGL:纹理坐标实时变化的地形的最佳渲染方法?

修改OpenGL顶点缓冲区的正确方法是啥?

在OpenGL中动态加载纹理的好方法是啥?