class GLTraits
{

  public:
    template <typename> struct Value;
};

template <> struct GLTraits::Value<GLfloat>
{
    auto static constexpr name                 = "GLfloat";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{1};
    auto static constexpr glsl                 = GLenum{GL_FLOAT};
    auto static constexpr format               = GLenum{GL_RED};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_R32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SR8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RED};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, GLfloat const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniform1f(location, (value));
    }
    void static vertex_attrib(GLint location, GLfloat const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib1f((GLuint)location + column, (value));
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(GLfloat))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(GLfloat) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<bool>
{
    auto static constexpr name                 = "bool";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{1};
    auto static constexpr glsl                 = GLenum{GL_BOOL};
    auto static constexpr format               = GLenum{GL_RED_INTEGER};
    auto static constexpr type                 = GLenum{GL_BYTE};
    auto static constexpr internal_format      = GLenum{GL_R8I};
    auto static constexpr internal_format_srgb = GLenum{GL_SR8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RED};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, bool const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniform1i(location, (value));
    }
    void static vertex_attrib(GLint location, bool const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI1i((GLuint)location + column, (value));
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(bool))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(bool) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<GLbyte>
{
    auto static constexpr name                 = "GLbyte";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{1};
    auto static constexpr glsl                 = GLenum{GL_INT};
    auto static constexpr format               = GLenum{GL_RED_INTEGER};
    auto static constexpr type                 = GLenum{GL_BYTE};
    auto static constexpr internal_format      = GLenum{GL_R8I};
    auto static constexpr internal_format_srgb = GLenum{GL_SR8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RED};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, GLbyte const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniform1i(location, (value));
    }
    void static vertex_attrib(GLint location, GLbyte const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI1i((GLuint)location + column, (value));
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(GLbyte))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(GLbyte) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<GLshort>
{
    auto static constexpr name                 = "GLshort";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{1};
    auto static constexpr glsl                 = GLenum{GL_INT};
    auto static constexpr format               = GLenum{GL_RED_INTEGER};
    auto static constexpr type                 = GLenum{GL_SHORT};
    auto static constexpr internal_format      = GLenum{GL_R16I};
    auto static constexpr internal_format_srgb = GLenum{GL_SR8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RED};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, GLshort const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniform1i(location, (value));
    }
    void static vertex_attrib(GLint location, GLshort const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI1i((GLuint)location + column, (value));
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(GLshort))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(GLshort) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<GLint>
{
    auto static constexpr name                 = "GLint";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{1};
    auto static constexpr glsl                 = GLenum{GL_INT};
    auto static constexpr format               = GLenum{GL_RED_INTEGER};
    auto static constexpr type                 = GLenum{GL_INT};
    auto static constexpr internal_format      = GLenum{GL_R32I};
    auto static constexpr internal_format_srgb = GLenum{GL_SR8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RED};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, GLint const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniform1i(location, (value));
    }
    void static vertex_attrib(GLint location, GLint const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI1i((GLuint)location + column, (value));
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(GLint))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(GLint) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<GLubyte>
{
    auto static constexpr name                 = "GLubyte";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{1};
    auto static constexpr glsl                 = GLenum{GL_UNSIGNED_INT};
    auto static constexpr format               = GLenum{GL_RED_INTEGER};
    auto static constexpr type                 = GLenum{GL_UNSIGNED_BYTE};
    auto static constexpr internal_format      = GLenum{GL_R8UI};
    auto static constexpr internal_format_srgb = GLenum{GL_SR8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RED};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, GLubyte const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        glUniform1ui(location, (value));
    }
    void static vertex_attrib(GLint location, GLubyte const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI1ui((GLuint)location + column, (value));
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(GLubyte))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(GLubyte) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<GLushort>
{
    auto static constexpr name                 = "GLushort";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{1};
    auto static constexpr glsl                 = GLenum{GL_UNSIGNED_INT};
    auto static constexpr format               = GLenum{GL_RED_INTEGER};
    auto static constexpr type                 = GLenum{GL_UNSIGNED_SHORT};
    auto static constexpr internal_format      = GLenum{GL_R16UI};
    auto static constexpr internal_format_srgb = GLenum{GL_SR8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RED};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, GLushort const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        glUniform1ui(location, (value));
    }
    void static vertex_attrib(GLint location, GLushort const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI1ui((GLuint)location + column, (value));
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(GLushort))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(GLushort) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<GLuint>
{
    auto static constexpr name                 = "GLuint";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{1};
    auto static constexpr glsl                 = GLenum{GL_UNSIGNED_INT};
    auto static constexpr format               = GLenum{GL_RED_INTEGER};
    auto static constexpr type                 = GLenum{GL_UNSIGNED_INT};
    auto static constexpr internal_format      = GLenum{GL_R32UI};
    auto static constexpr internal_format_srgb = GLenum{GL_SR8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RED};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, GLuint const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        glUniform1ui(location, (value));
    }
    void static vertex_attrib(GLint location, GLuint const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI1ui((GLuint)location + column, (value));
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(GLuint))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(GLuint) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<GLdouble>
{
    auto static constexpr name                 = "GLdouble";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{1};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE};
    auto static constexpr format               = GLenum{GL_RED};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RED};
    auto static constexpr internal_format_srgb = GLenum{GL_SR8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RED};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, GLdouble const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniform1d(location, (value));
    }
    void static vertex_attrib(GLint location, GLdouble const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL1d((GLuint)location + column, (value));
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(GLdouble))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(GLdouble) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};


template <> struct GLTraits::Value<glm::vec2>
{
    auto static constexpr name                 = "glm::vec2";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{2};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_VEC2};
    auto static constexpr format               = GLenum{GL_RG};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RG32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRG8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RG};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::vec2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniform2fv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::vec2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib2fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::vec2))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::vec2) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::vec3>
{
    auto static constexpr name                 = "glm::vec3";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{3};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_VEC3};
    auto static constexpr format               = GLenum{GL_RGB};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RGB32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGB};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::vec3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniform3fv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::vec3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib3fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::vec3))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::vec3) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::vec4>
{
    auto static constexpr name                 = "glm::vec4";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{4};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_VEC4};
    auto static constexpr format               = GLenum{GL_RGBA};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RGBA32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8_ALPHA8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGBA};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB_ALPHA};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::vec4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniform4fv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::vec4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib4fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::vec4))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::vec4) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::mat2>
{
    auto static constexpr name                 = "glm::mat2";
    auto static constexpr columns              = GLint{2};
    auto static constexpr rows                 = GLint{2};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_MAT2};
    auto static constexpr format               = GLenum{GL_RG};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RG32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRG8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RG};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::mat2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniformMatrix2fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::mat2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib2fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::mat2))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::mat2) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::mat2x3>
{
    auto static constexpr name                 = "glm::mat2x3";
    auto static constexpr columns              = GLint{2};
    auto static constexpr rows                 = GLint{3};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_MAT2x3};
    auto static constexpr format               = GLenum{GL_RGB};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RGB32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGB};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::mat2x3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 1}, {});
        glUniformMatrix2x3fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::mat2x3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib3fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::mat2x3))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::mat2x3) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::mat2x4>
{
    auto static constexpr name                 = "glm::mat2x4";
    auto static constexpr columns              = GLint{2};
    auto static constexpr rows                 = GLint{4};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_MAT2x4};
    auto static constexpr format               = GLenum{GL_RGBA};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RGBA32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8_ALPHA8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGBA};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB_ALPHA};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::mat2x4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 1}, {});
        glUniformMatrix2x4fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::mat2x4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib4fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::mat2x4))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::mat2x4) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::mat3x2>
{
    auto static constexpr name                 = "glm::mat3x2";
    auto static constexpr columns              = GLint{3};
    auto static constexpr rows                 = GLint{2};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_MAT3x2};
    auto static constexpr format               = GLenum{GL_RG};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RG32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRG8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RG};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::mat3x2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 1}, {});
        glUniformMatrix3x2fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::mat3x2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib2fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::mat3x2))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::mat3x2) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::mat3>
{
    auto static constexpr name                 = "glm::mat3";
    auto static constexpr columns              = GLint{3};
    auto static constexpr rows                 = GLint{3};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_MAT3};
    auto static constexpr format               = GLenum{GL_RGB};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RGB32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGB};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::mat3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::mat3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib3fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::mat3))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::mat3) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::mat3x4>
{
    auto static constexpr name                 = "glm::mat3x4";
    auto static constexpr columns              = GLint{3};
    auto static constexpr rows                 = GLint{4};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_MAT3x4};
    auto static constexpr format               = GLenum{GL_RGBA};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RGBA32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8_ALPHA8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGBA};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB_ALPHA};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::mat3x4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 1}, {});
        glUniformMatrix3x4fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::mat3x4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib4fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::mat3x4))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::mat3x4) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::mat4x2>
{
    auto static constexpr name                 = "glm::mat4x2";
    auto static constexpr columns              = GLint{4};
    auto static constexpr rows                 = GLint{2};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_MAT4x2};
    auto static constexpr format               = GLenum{GL_RG};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RG32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRG8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RG};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::mat4x2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 1}, {});
        glUniformMatrix4x2fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::mat4x2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib2fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::mat4x2))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::mat4x2) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::mat4x3>
{
    auto static constexpr name                 = "glm::mat4x3";
    auto static constexpr columns              = GLint{4};
    auto static constexpr rows                 = GLint{3};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_MAT4x3};
    auto static constexpr format               = GLenum{GL_RGB};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RGB32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGB};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::mat4x3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 1}, {});
        glUniformMatrix4x3fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::mat4x3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib3fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::mat4x3))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::mat4x3) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::mat4>
{
    auto static constexpr name                 = "glm::mat4";
    auto static constexpr columns              = GLint{4};
    auto static constexpr rows                 = GLint{4};
    auto static constexpr glsl                 = GLenum{GL_FLOAT_MAT4};
    auto static constexpr format               = GLenum{GL_RGBA};
    auto static constexpr type                 = GLenum{GL_FLOAT};
    auto static constexpr internal_format      = GLenum{GL_RGBA32F};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8_ALPHA8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGBA};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB_ALPHA};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::mat4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::mat4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttrib4fv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::mat4))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::mat4) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribPointer(
                (GLuint)location + column,
                rows,
                type,
                GL_FALSE,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::ivec2>
{
    auto static constexpr name                 = "glm::ivec2";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{2};
    auto static constexpr glsl                 = GLenum{GL_INT_VEC2};
    auto static constexpr format               = GLenum{GL_RG_INTEGER};
    auto static constexpr type                 = GLenum{GL_INT};
    auto static constexpr internal_format      = GLenum{GL_RG32I};
    auto static constexpr internal_format_srgb = GLenum{GL_SRG8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RG};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, glm::ivec2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniform2iv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::ivec2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI2iv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::ivec2))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::ivec2) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::ivec3>
{
    auto static constexpr name                 = "glm::ivec3";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{3};
    auto static constexpr glsl                 = GLenum{GL_INT_VEC3};
    auto static constexpr format               = GLenum{GL_RGB_INTEGER};
    auto static constexpr type                 = GLenum{GL_INT};
    auto static constexpr internal_format      = GLenum{GL_RGB32I};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGB};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, glm::ivec3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniform3iv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::ivec3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI3iv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::ivec3))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::ivec3) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::ivec4>
{
    auto static constexpr name                 = "glm::ivec4";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{4};
    auto static constexpr glsl                 = GLenum{GL_INT_VEC4};
    auto static constexpr format               = GLenum{GL_RGBA_INTEGER};
    auto static constexpr type                 = GLenum{GL_INT};
    auto static constexpr internal_format      = GLenum{GL_RGBA32I};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8_ALPHA8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGBA};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB_ALPHA};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, glm::ivec4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({2, 0}, {});
        glUniform4iv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::ivec4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI4iv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::ivec4))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::ivec4) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::uvec2>
{
    auto static constexpr name                 = "glm::uvec2";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{2};
    auto static constexpr glsl                 = GLenum{GL_UNSIGNED_INT_VEC2};
    auto static constexpr format               = GLenum{GL_RG_INTEGER};
    auto static constexpr type                 = GLenum{GL_UNSIGNED_INT};
    auto static constexpr internal_format      = GLenum{GL_RG32UI};
    auto static constexpr internal_format_srgb = GLenum{GL_SRG8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RG};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, glm::uvec2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        glUniform2uiv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::uvec2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI2uiv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::uvec2))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::uvec2) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::uvec3>
{
    auto static constexpr name                 = "glm::uvec3";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{3};
    auto static constexpr glsl                 = GLenum{GL_UNSIGNED_INT_VEC3};
    auto static constexpr format               = GLenum{GL_RGB_INTEGER};
    auto static constexpr type                 = GLenum{GL_UNSIGNED_INT};
    auto static constexpr internal_format      = GLenum{GL_RGB32UI};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGB};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, glm::uvec3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        glUniform3uiv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::uvec3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI3uiv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::uvec3))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::uvec3) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::uvec4>
{
    auto static constexpr name                 = "glm::uvec4";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{4};
    auto static constexpr glsl                 = GLenum{GL_UNSIGNED_INT_VEC4};
    auto static constexpr format               = GLenum{GL_RGBA_INTEGER};
    auto static constexpr type                 = GLenum{GL_UNSIGNED_INT};
    auto static constexpr internal_format      = GLenum{GL_RGBA32UI};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8_ALPHA8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGBA};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB_ALPHA};
    auto static constexpr integer = bool(*"_INTEGER");
    void static uniform(GLint location, glm::uvec4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        glUniform4uiv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::uvec4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribI4uiv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::uvec4))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({3, 0}, {});
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::uvec4) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribIPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dvec2>
{
    auto static constexpr name                 = "glm::dvec2";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{2};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_VEC2};
    auto static constexpr format               = GLenum{GL_RG};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RG};
    auto static constexpr internal_format_srgb = GLenum{GL_SRG8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RG};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dvec2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniform2dv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dvec2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL2dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dvec2))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dvec2) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dvec3>
{
    auto static constexpr name                 = "glm::dvec3";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{3};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_VEC3};
    auto static constexpr format               = GLenum{GL_RGB};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RGB};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGB};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dvec3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniform3dv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dvec3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL3dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dvec3))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dvec3) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dvec4>
{
    auto static constexpr name                 = "glm::dvec4";
    auto static constexpr columns              = GLint{1};
    auto static constexpr rows                 = GLint{4};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_VEC4};
    auto static constexpr format               = GLenum{GL_RGBA};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RGBA};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8_ALPHA8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGBA};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB_ALPHA};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dvec4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniform4dv(location, 1, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dvec4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL4dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dvec4))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dvec4) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dmat2>
{
    auto static constexpr name                 = "glm::dmat2";
    auto static constexpr columns              = GLint{2};
    auto static constexpr rows                 = GLint{2};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_MAT2};
    auto static constexpr format               = GLenum{GL_RG};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RG};
    auto static constexpr internal_format_srgb = GLenum{GL_SRG8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RG};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dmat2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniformMatrix2dv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dmat2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL2dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dmat2))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dmat2) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dmat2x3>
{
    auto static constexpr name                 = "glm::dmat2x3";
    auto static constexpr columns              = GLint{2};
    auto static constexpr rows                 = GLint{3};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_MAT2x3};
    auto static constexpr format               = GLenum{GL_RGB};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RGB};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGB};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dmat2x3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniformMatrix2x3dv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dmat2x3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL3dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dmat2x3))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dmat2x3) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dmat2x4>
{
    auto static constexpr name                 = "glm::dmat2x4";
    auto static constexpr columns              = GLint{2};
    auto static constexpr rows                 = GLint{4};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_MAT2x4};
    auto static constexpr format               = GLenum{GL_RGBA};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RGBA};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8_ALPHA8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGBA};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB_ALPHA};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dmat2x4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniformMatrix2x4dv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dmat2x4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL4dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dmat2x4))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dmat2x4) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dmat3x2>
{
    auto static constexpr name                 = "glm::dmat3x2";
    auto static constexpr columns              = GLint{3};
    auto static constexpr rows                 = GLint{2};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_MAT3x2};
    auto static constexpr format               = GLenum{GL_RG};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RG};
    auto static constexpr internal_format_srgb = GLenum{GL_SRG8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RG};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dmat3x2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniformMatrix3x2dv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dmat3x2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL2dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dmat3x2))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dmat3x2) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dmat3>
{
    auto static constexpr name                 = "glm::dmat3";
    auto static constexpr columns              = GLint{3};
    auto static constexpr rows                 = GLint{3};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_MAT3};
    auto static constexpr format               = GLenum{GL_RGB};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RGB};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGB};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dmat3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniformMatrix3dv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dmat3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL3dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dmat3))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dmat3) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dmat3x4>
{
    auto static constexpr name                 = "glm::dmat3x4";
    auto static constexpr columns              = GLint{3};
    auto static constexpr rows                 = GLint{4};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_MAT3x4};
    auto static constexpr format               = GLenum{GL_RGBA};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RGBA};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8_ALPHA8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGBA};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB_ALPHA};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dmat3x4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniformMatrix3x4dv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dmat3x4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL4dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dmat3x4))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dmat3x4) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dmat4x2>
{
    auto static constexpr name                 = "glm::dmat4x2";
    auto static constexpr columns              = GLint{4};
    auto static constexpr rows                 = GLint{2};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_MAT4x2};
    auto static constexpr format               = GLenum{GL_RG};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RG};
    auto static constexpr internal_format_srgb = GLenum{GL_SRG8_EXT};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RG};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dmat4x2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniformMatrix4x2dv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dmat4x2 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL2dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dmat4x2))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dmat4x2) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dmat4x3>
{
    auto static constexpr name                 = "glm::dmat4x3";
    auto static constexpr columns              = GLint{4};
    auto static constexpr rows                 = GLint{3};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_MAT4x3};
    auto static constexpr format               = GLenum{GL_RGB};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RGB};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGB};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dmat4x3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniformMatrix4x3dv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dmat4x3 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL3dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dmat4x3))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dmat4x3) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};

template <> struct GLTraits::Value<glm::dmat4>
{
    auto static constexpr name                 = "glm::dmat4";
    auto static constexpr columns              = GLint{4};
    auto static constexpr rows                 = GLint{4};
    auto static constexpr glsl                 = GLenum{GL_DOUBLE_MAT4};
    auto static constexpr format               = GLenum{GL_RGBA};
    auto static constexpr type                 = GLenum{GL_DOUBLE};
    auto static constexpr internal_format      = GLenum{GL_RGBA};
    auto static constexpr internal_format_srgb = GLenum{GL_SRGB8_ALPHA8};
    auto static constexpr internal_format_compressed =
        GLenum{GL_COMPRESSED_RGBA};
    auto static constexpr internal_format_compressed_srgb =
        GLenum{GL_COMPRESSED_SRGB_ALPHA};
    auto static constexpr integer = bool(*"");
    void static uniform(GLint location, glm::dmat4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 0}, "GL_ARB_gpu_shader_fp64");
        glUniformMatrix4dv(location, 1, GL_FALSE, glm::value_ptr(value));
    }
    void static vertex_attrib(GLint location, glm::dmat4 const & value)
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribL4dv(
                (GLuint)location + column,
                glm::value_ptr(value) + rows * column);
    }
    void static vertex_attrib_pointer(
        GLint       location,
        std::size_t offset = 0,
        std::size_t stride = sizeof(glm::dmat4))
    {
        if (GLBase::debug() >= 1)
            GLBase::check_supported({4, 1}, "GL_ARB_vertex_attrib_64bit");
        if (location == -1)
            return;
        auto constexpr sizeof_column = sizeof(glm::dmat4) / columns;
        for (auto column = GLuint{0}; column < columns; ++column)
            glVertexAttribLPointer(
                (GLuint)location + column,
                rows,
                type,
                (GLsizei)stride,
                (void const *)(offset + sizeof_column * column));
    }
};