将OpenGL调试或详细信息输出到控制台

6

OpenGL是否有一种调试模式,可以将信息输出到控制台?我有一个应用在许多设备上运行良好,但在一个真正重要的设备上却失败了。因为我无法直接访问该设备,所以我正在试图弄清楚它是如何失败的。目前,我在控制台中看不到任何输出信息。请注意,该应用最初是一个没有控制台的Windows应用程序,并且我通过项目属性将其切换到控制台。此外,如果重要的话,一些对象不会显示,另一个对象被卡住而无法动画-这对我来说毫无意义。

2个回答

7
不,不完全是。如果您真的想要一些详细的输出,您需要为OpenGL编写钩子并记录其所有调用(这就是像gDEBugger这样的工具所做的),但这需要相当多的工作(您可以使用类似于GLIntercept的东西来完成)。从个人经验来看:最简单的方法可能是获得有问题的机器访问权限并在那里运行gDEBugger...或者获取一个等效的机器,以便您可以附加图形调试器重现错误。
另外,您可以尝试使用ARB_debug_output扩展,理论上应该为您提供更多输出。实际上,每次OpenGL API调用后您只会得到一个glGetError调用,这对于您的用例(即您只是执行破坏GL状态的操作)可能已足够。为了使用调试输出扩展,您必须修改应用程序以创建调试上下文。

1
ARB_debug_output的实用性取决于OpenGL实现。 NVIDIA基本上采用“glGetError”版本,但AMD的版本更加有用。 它并不简单地将“GL_INVALID_OPERATION”反弹回您; 它通常会告诉您有用的信息,例如导致此错误的函数等。 我已经看到它特别识别核心上下文中不建议使用的函数调用。 在失败时,着色器编译和链接错误将转储适当的日志。 等等。 - Nicol Bolas
ARG_debug_output4.3版本起是核心规范的一部分。 - Bartek Banachewicz

2

以下是使用C++/GLEW/GLFW和Visual Studio 2019在Windows 10上的方法,仅包含相关部分:

/** Necessary includes */

#include <map>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

/** Debug callback prototype */

void GLAPIENTRY GLDebugMessageCallback(GLenum Source,
    GLenum Type,
    GLuint Id,
    GLenum Severity,
    GLsizei Length,
    const GLchar* Message,
    const void* UserParam);

/** Create the window and set the OpenGL context */

int main(void)
{
    if (glfwInit() != GLFW_TRUE)
    {
        printf("Unable to initialize GLFW\n");
        return EXIT_FAILURE;
    }

    /** Set the OpenGL Debug Context hint */

    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);

    GLFWwindow* Window = glfwCreateWindow(800, 600, "OpenGL", nullptr, nullptr);

    if (!Window)
    {
        glfwTerminate();
        printf("Unable to create a GLFW window\n");
        return EXIT_FAILURE;
    }

    glfwMakeContextCurrent(Window);

    if (glewInit() != GLEW_OK)
    {
        printf("Unable to initialize GLEW\n");
        return EXIT_FAILURE;
    }

    /** Check whether debug context is enabled and set the debug callback */

    GLint ContextFlags;
    glGetIntegerv(GL_CONTEXT_FLAGS, &ContextFlags);

    if (ContextFlags & GL_CONTEXT_FLAG_DEBUG_BIT)
    {
        printf("OpenGL : Debug Context Is Enabled");

        glEnable(GL_DEBUG_OUTPUT);
        glDebugMessageCallback(GLDebugMessageCallback, 0);
    }

    while (!glfwWindowShouldClose(Window))
    {
        glfwPollEvents();

        // Edit here...

        glfwSwapBuffers(Window);
    }

    glfwDestroyWindow(Window);
    glfwTerminate();

    return EXIT_SUCCESS;
}

/** Debug callback definition */

void GLAPIENTRY GLDebugMessageCallback(GLenum Source,
    GLenum Type,
    GLuint Id,
    GLenum Severity,
    GLsizei Length,
    const GLchar* Message,
    const void* UserParam)
{
    static std::map<GLenum, const GLchar*> Sources =
    {
        {GL_DEBUG_SOURCE_API, "API"},
        {GL_DEBUG_SOURCE_WINDOW_SYSTEM, "WINDOW_SYSTEM"},
        {GL_DEBUG_SOURCE_SHADER_COMPILER, "SHADER_COMPILER"},
        {GL_DEBUG_SOURCE_THIRD_PARTY, "THIRD_PARTY"},
        {GL_DEBUG_SOURCE_APPLICATION, "APPLICATION"},
        {GL_DEBUG_SOURCE_OTHER, "OTHER"}
    };

    static std::map<GLenum, const GLchar*> Severities =
    {
        {GL_DEBUG_SEVERITY_HIGH, "HIGH"},
        {GL_DEBUG_SEVERITY_MEDIUM, "MEDIUM"},
        {GL_DEBUG_SEVERITY_LOW, "LOW"},
        {GL_DEBUG_SEVERITY_NOTIFICATION, "NOTIFICATION"}
    };

    static std::map<GLenum, const GLchar*> Types =
    {
        {GL_DEBUG_TYPE_ERROR, "ERROR"},
        {GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, "DEPRECATED_BEHAVIOR"},
        {GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, "UNDEFINED_BEHAVIOR"},
        {GL_DEBUG_TYPE_PORTABILITY, "PORTABILITY"},
        {GL_DEBUG_TYPE_PERFORMANCE, "PERFORMANCE"},
        {GL_DEBUG_TYPE_MARKER, "MARKER"},
        {GL_DEBUG_TYPE_PUSH_GROUP, "PUSH_GROUP"},
        {GL_DEBUG_TYPE_POP_GROUP, "POP_GROUP"},
        {GL_DEBUG_TYPE_OTHER, "OTHER"}
    };

    printf("[OpenGL %s] - SEVERITY: %s, SOURCE: %s, ID: %d: %s\n", Types[Type], Severities[Severity], Sources[Source], Id, Message);
}

样例输出

[OpenGL ERROR] - SEVERITY: HIGH, SOURCE: API, ID: 1282: GL_INVALID_OPERATION error generated. Function glColor4fv is deprecated and not available in preview contexts.
[OpenGL ERROR] - SEVERITY: HIGH, SOURCE: API, ID: 1282: GL_INVALID_OPERATION error generated. Array object is not active.
[OpenGL OTHER] - SEVERITY: NOTIFICATION, SOURCE: API, ID: 131185: Buffer detailed info: Buffer object 1 (bound to GL_ARRAY_BUFFER_ARB, usage hint is GL_DYNAMIC_DRAW) will use SYSTEM HEAP memory as the source for buffer object operations.
[OpenGL OTHER] - SEVERITY: NOTIFICATION, SOURCE: API, ID: 131185: Buffer detailed info: Buffer object 1 (bound to GL_ARRAY_BUFFER_ARB, usage hint is GL_DYNAMIC_DRAW) has been mapped WRITE_ONLY in SYSTEM HEAP memory (fast).

测试环境

OpenGL 4.6
GLSL 4.60 NVIDIA
渲染器:GeForce RTX 2070 with Max-Q Design/PCIe/SSE2
供应商:NVIDIA Corporation

如何将内容输出到Visual Studio的输出窗口

#include <windows.h> // For OutputDebugStringA 

将调试回调函数定义末尾的printf替换为以下内容:

char ErrorString[512];
sprintf_s(ErrorString, "[OpenGL %s] - SEVERITY: %s, SOURCE: %s, ID: %d: %s\n", Types[Type], Severities[Severity], Sources[Source], Id, Message);
OutputDebugStringA(ErrorString);

关于调试上下文的注意事项

简而言之,在创建OpenGL上下文时,若启用调试上下文模式,则会在应用程序、驱动程序和GPU之间的路径中激活附加层。这些额外的层执行错误检查、参数分析等操作,并通过调试消息回调以良好的形式报告。

启用OpenGL调试上下文是平台特定的,因此GLFW在这方面很有帮助,它提供了一种通过glfwWindowHint配置OpenGL上下文的方式,应在创建GLFW窗口之前进行配置。

来自OpenGL Wiki - 启用调试输出

除非启用调试输出,否则不会生成、检索或记录任何消息。可以使用GL_DEBUG_OUTPUT枚举值使用glEnable启用调试输出。

在调试上下文中,调试输出默认启用。在非调试上下文中,即使启用了调试输出,OpenGL实现也可能不生成消息。

我验证了在Windows 10上,仅启用调试输出并设置回调函数,即使没有激活调试上下文,也会产生一些输出。

有关glDebugMessageCallback的详细信息,请参见docs.GLOpenGL Wiki和《OpenGL超级宝典第7版》。


你的代码缺少一个关键部分:在上下文创建时实际请求“调试”上下文。此外,使用main的示例很令人困惑,因为它暗示您可以在创建上下文之前进行GL调用。 - derhass
那段代码仍然没有请求调试上下文。虽然有些驱动程序即使在正常情况下仍会生成一些调试消息,但是在调试上下文中通常会获得更多的消息。 - derhass
同意,我添加了调试上下文提示。再次感谢。 - rbento

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接