What is the best way to debug OpenGL?

前端 未结 10 1614
半阙折子戏
半阙折子戏 2020-12-12 17:51

I find that a lot of the time, OpenGL will show you it failed by not drawing anything. I\'m trying to find ways to debug OpenGL programs, by inspecting the transformation ma

相关标签:
10条回答
  • 2020-12-12 18:31

    What is the best way to debug OpenGL?

    Without considering additional and external tools (which other answers already do).

    Then the general way is to extensively call glGetError(). However a better alternative is to use Debug Output (KHR_debug, ARB_debug_output). This provides you with the functionality of setting a callback for messages of varying severity level.

    In order to use debug output, the context must be created with the WGL/GLX_DEBUG_CONTEXT_BIT flag. With GLFW this can be set with the GLFW_OPENGL_DEBUG_CONTEXT window hint.

    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
    

    Note that if the context isn't a debug context, then receiving all or even any messages aren't guaranteed.

    Whether you have a debug context or not can be detected by checking GL_CONTEXT_FLAGS:

    GLint flags;
    glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
    
    if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
        // It's a debug context
    

    You would then go ahead and specify a callback:

    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
    }
    

    Each possible value for the enums can be seen on here. Especially remember to check the severity, as some messages might just be notifications and not errors.

    You can now do ahead and register the callback.

    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);
    

    You can even inject your own messages using glDebugMessageInsert().

    glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0,
                         GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Vary dangerous error");
    

    When it comes to shaders and programs you always want to be checking GL_COMPILE_STATUS, GL_LINK_STATUS and GL_VALIDATE_STATUS. If any of them reflects that something is wrong, then additionally always check 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;
    }
    

    The string returned by glGetProgramInfoLog() will be null terminated.


    You can also go a bit more extreme and utilize a few debug macros in a debug build. Thus using glIs*() functions to check if the expected type is the actual type as well.

    assert(glIsProgram(program) == GL_TRUE);
    glUseProgram(program);
    

    If debug output isn't available and you just want to use glGetError(), then you're of course free to do so.

    GLenum err;
    while ((err = glGetError()) != GL_NO_ERROR)
        printf("OpenGL Error: %u\n", err);
    

    Since a numeric error code isn't that helpful, we could make it a bit more human readable by mapping the numeric error codes to a 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";
        }
    }
    

    Then checking it like this:

    printf("OpenGL Error: [%u] %s\n", err, glGetErrorString(err));
    

    That still isn't very helpful or better said intuitive, as if you have sprinkled a few glGetError() here and there. Then locating which one logged an error can be troublesome.

    Again macros come to the rescue.

    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));
    }
    

    Now simply define a macro like this:

    #define glCheckErrors() _glCheckErrors(__FILE__, __LINE__)
    

    and voila now you can call glCheckErrors() after everything you want, and in case of errors it will tell you the exact file and line it was detected at.

    0 讨论(0)
  • 2020-12-12 18:32

    For those on Mac, the buit in OpenGL debugger is great as well. It lets you inspect buffers, states, and helps in finding performance issues.

    0 讨论(0)
  • 2020-12-12 18:36

    Updating the window title dynamically is convenient for me.

    Example (use GLFW, C++11):

    glfwSetWindowTitle(window, ("Now Time is " + to_string(glfwGetTime())).c_str());
    
    0 讨论(0)
  • 2020-12-12 18:39

    Nsight is good debugging tool if you have an NVidia card.

    0 讨论(0)
提交回复
热议问题