Wednesday, October 22, 2014

Simple OpenGL error checking

For everyone who worked with OpenGL the error checking pattern is quite annoying - after each OpenGL call that can generate some internal error glGetError() should fallow to check if everything was ok.

The common pattern is something like this:

    glFunction(...);
    auto error = glGetError();

    if(error != GL_NO_ERROR)
    {
        //your code here

    }

It can be simplified if everything we want is just to report the errors instead of doing something about them after the failed call:

    void CheckErrors()
    {
        auto error = glGetError();
        if(error != GL_NO_ERROR)
        {
            //your code here
        }
    }

    ...

        glFunction(...);
        CheckErrors();

This looks better, but is still annoying to have to make 2 function calls instead of one for each OpenGL call. 

My proposal is to make a wrapper for OpenGL calls. The wrapper should accept any OpenGL function and its parameters, should call the function and return what the OpenGL function returned. Beside this it should also check for eventual OpenGL errors with minimal overhead and remove the error checking in release builds. 

To be able to satisfy all previous requirements using variadic templates with trailing return type approach was obvious:

    template<typename glFunction, typename... Params>
    auto glCall(glFunction function, Params... params)->decltype(function(params...))

The only concern at this point it how to separate OpenGL function with void return type from the others because for void return type the implementation of the wrapper is just the OpenGL function call followed by error checkin and for non void function the result has to be stored somehow, error checking has to be done and afterwards the previously stored result should be returned. Good ol' SFINAE comes to the rescue here to separate the void functions from anything else:

    template<typename glFunction, typename... Params>
    auto glCall(glFunction function, Params... params)->typename std::enable_if<std::is_same<void, decltype(function(params...))>::value, decltype(function(params...))>::type
    {
        function(std::forward<Params>(params)...);
        CheckErrors();
    }

    template<typename glFunction, typename... Params>
    auto glCall(glFunction function, Params... params)->typename std::enable_if<!std::is_same<void, decltype(function(params...))>::value, decltype(function(params...))>::type
    {
        auto ret = function(std::forward<Params>(params)...);
        CheckErrors();
        return ret;
    }

I know the return type looks ugly, but actually is pretty simple: if decltype(function(params...)) is void first implementation should be used, else the second implementation should be used. Using the wrapper all OpenGL calls become something like:

glCall(glViewport, 0, 0, 1000, 1000);
glCall(glTexParameteri, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myFilter);
myHandle = glCall(glCreateShaderGL_VERTEX_SHADER);

Removing the error checking on release builds can be done easily with some #ifdefs inside the wrapper. With any decent compiler the call should be inlined and any overhead from indirect function calls should be removed.