glDebugMessageCallback doesn't get called despite of error

Viewed 2881

I am using GLFW and GLEW in a C++ program to work with OpenGL. I want to be able to output OpenGL errors to the console. To test this, I made a little program:

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

// Test error function
void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
    std::cout << "ERROR";
}

// Main function
int main(void)
{
    // Just some initialization
    GLFWwindow* window;

    if (!glfwInit()) {
        return -1;
    }

    window = glfwCreateWindow(640, 480, "Debugtest", NULL, NULL);
    if (!window) {
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glewInit();

    // Output the opengl version
    std::cout << glGetString(GL_VERSION) << std::endl;

    // Enable debug output
    glEnable(GL_DEBUG_OUTPUT);
    glDebugMessageCallback(MessageCallback, 0);

    unsigned int buffer;
    glGenBuffers(-1, &buffer); // -1 throws an error according to http://docs.gl/gl4/glGenBuffers

    // Loop until the user closes the window
    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
    };

    glfwTerminate();
    return 0;
}

The code for the callback is from the khronos documentation. glGenBuffers should throw an error acccording to the docs. However, the OpenGL window stays white, the terminal just displays the OpenGL version (4.5.13474 Copatibility Profile Context 22.19.162.4).

What is the best way to handle errors? How do I fix my code?

2 Answers

Thanks to @OutOfBound I found the answer. Before glfwCreateWindow, you need to call glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE).

Similar to his answer, you can do something like this to break when an error occurs (At least using the MSVC compiler):

#define call(x) x;\
    if (error) __debugbreak();

bool error = false;

void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
    error = true;
    std::cout << "[OpenGL Error](" << type << ") " << message << std::endl;
}

In my OpenGL projects I wrap every call into the OpenGL API with error checking. My approach goes something like this:

std::string error_description(GLenum err) {
    switch(err) {
        case GL_NO_ERROR:
            return "GL_NO_ERROR: No error has been recorded. The value of this symbolic constant is guaranteed to be 0. ";
        case GL_INVALID_ENUM:
            return "GL_INVALID_ENUM: An unacceptable value is specified for an enumerated argument. The offending command is ignored and has no other side effect than to set the error flag.  ";
        case GL_INVALID_VALUE:
            return "GL_INVALID_VALUE: A numeric argument is out of range. The offending command is ignored and has no other side effect than to set the error flag.  ";
        case GL_INVALID_OPERATION:
            return "GL_INVALID_OPERATION: The specified operation is not allowed in the current state. The offending command is ignored and has no other side effect than to set the error flag.  ";
        case GL_INVALID_FRAMEBUFFER_OPERATION:
            return
            "GL_INVALID_FRAMEBUFFER_OPERATION: The framebuffer object is not complete."
            "The offending command is ignored and has no other side effect than to set the error flag.";
        case GL_OUT_OF_MEMORY:
            return "GL_OUT_OF_MEMORY: There is not enough memory left to execute the command. The state of the GL is undefined, except for the state of the error flags, after this error is recorded. . ";
        case GL_STACK_UNDERFLOW:
            return "GL_STACK_UNDERFLOW: An attempt has been made to perform an operation that would cause an internal stack to underflow. ";
        case GL_STACK_OVERFLOW:
            return "GL_STACK_OVERFLOW: An attempt has been made to perform an operation that would cause an internal stack to overflow. ";
        default:
            return "No Description";
    }
}

namespace detail {
    void check() {
        const auto err = glGetError();
        if(err != GL_NO_ERROR) {
            throw std::runtime_error(error_description(err));
        }
    }

}


template <class result_t, class... gl_args_t, class... args_t>
result_t call(result_t (*fun)(gl_args_t...), args_t... args) {
    if constexpr(!std::is_same_v<result_t, void>) {
        auto result = fun(std::forward<args_t>(args)...);
        #ifndef NDEBUG
            detail::check();
        #endif
        return result;
    } else {
        fun(std::forward<args_t>(args)...);
        #ifndef NDEBUG
            detail::check();
        #endif
    }
}

In the application I call into the API like this:

call(<api_call>, <arguments...>>);
call(glGenBuffers, 1, &buffer);

The reasons, why I prefer the error checking over the error callback:

  • Unhandled exceptions will terminate your program and make it easy to identify the exact line of code that is broken
  • If you are consequent and wrap all calls to OpenGL, then you know that your state machine is always in a defined state
Related