include/gltraits.hpp
e38947dc
 /// Guards
 
 #ifndef GLTRAITS_HPP_
 #define GLTRAITS_HPP_
 
 
 /// Includes
 
3378873e
 #include <array>
e38947dc
 #include <cstddef>
799e1ae5
 #include <string>
e38947dc
 
 #include <glbase.hpp>
 
 
 /// GLTraits
 
 class GLTraits
 {
 
 public:
 
     //// Value
 
     template<typename>
     struct Value;
 
a6304722
     template<GLenum id>
     struct ValueID;
 
799e1ae5
     //// Object
 
     template<GLenum object_type>
     struct Object;
 
3378873e
     //// Texture
 
     template<std::size_t N>
     struct Texture;
 
27d69715
     //// Member
 
     #if __cplusplus >= 201402L
 
23a16d29
     template<typename Value, std::size_t offset>
     struct Member
     {};
 
     template<typename T, typename... Member>
27d69715
     struct Members
     {};
 
     template<typename T>
     static constexpr auto members()
     {
         static_assert(
             !std::is_empty<T>::value &&
             std::is_trivially_copyable<T>::value &&
             std::is_standard_layout<T>::value,
             "T must be a non-empty trivially copyable standard-layout type"
         );
         return members_<T>(MakeIndices<member_count<T>()>{});
     }
 
     #endif
 
3378873e
 private:
 
     //// Helpers
 
     // Stripped down version of C++14's `std::{,make_}index_sequence`.
 
     template<std::size_t... Is>
     struct Indices
     {};
 
     template<std::size_t N, std::size_t... Is>
     struct MakeIndices_ : MakeIndices_<N - 1, N - 1, Is...>
     {};
 
     template<std::size_t... Is>
     struct MakeIndices_<0, Is...>
     {
         using Indices_ = Indices<Is...>;
     };
 
     template<std::size_t N>
     using MakeIndices = typename MakeIndices_<N>::Indices_;
 
     //// Texture
 
     template <
         std::size_t N,
         typename    Indices,
         typename    CopySizeIndices,
         typename    CopyOffsetIndices
     >
     struct Texture_;
 
27d69715
     //// Member
 
     // Stripped down version of Boost::PFR.
 
     #if __cplusplus >= 201402L
 
     struct MemberInfo
     {
23a16d29
         GLenum      id;
         std::size_t offset;
27d69715
     };
 
     template<std::size_t N>
     struct MemberInfos
     {
         MemberInfo infos[N];
     };
 
     struct MemberAny
     {
         MemberInfo * info;
 
23a16d29
         constexpr static auto align(std::size_t align, std::size_t offset)
         {
             return ((offset - 1) / align + 1) * align;
         }
 
27d69715
         template<typename Value>
         constexpr operator Value()
         {
             return (
23a16d29
                 info[0].id     = GLTraits::Value<Value>::id,
                 info[0].offset = align(alignof(Value), info[0].offset),
                 info[1].offset = info[0].offset + sizeof(Value),
27d69715
                 Value{}
             );
         }
     };
 
     template<typename T, std::size_t I0, std::size_t... Is>
     static constexpr auto member_count_(Indices<I0, Is...>)
         -> decltype(T{(I0, MemberAny{}), (Is, MemberAny{})...}, std::size_t{})
     {
         return 1 + sizeof...(Is);
     }
 
     template<typename T, std::size_t... Is>
     static constexpr auto member_count_(Indices<Is...>)
     {
         return member_count_<T>(MakeIndices<sizeof...(Is) - 1>{});
     }
 
     template<typename T>
     static constexpr auto member_count()
     {
         return member_count_<T>(MakeIndices<sizeof(T)>{});
     }
 
     template<typename T, std::size_t... Is>
23a16d29
     static constexpr auto member_infos(MemberInfos<sizeof...(Is)+1> infos = {})
27d69715
     {
         return ((void)T{MemberAny{&infos.infos[Is]}...}, infos);
     }
 
     template<typename T, std::size_t... Is>
     static constexpr auto members_(Indices<Is...>)
     {
         return Members<
5ee00b59
             T,
23a16d29
             Member<
                 typename ValueID<member_infos<T, Is...>().infos[Is].id>::Value,
                 member_infos<T, Is...>().infos[Is].offset
             >...
27d69715
         >{};
     }
 
     #endif
 
e38947dc
 };
 
 //// Helpers
 
 #define GLTRAITS_KEEP(...) __VA_ARGS__
 #define GLTRAITS_OMIT(...)
 
 //// Value
 
 #define GLTRAITS_VALUE_SRED  GL_SR8_EXT
 #define GLTRAITS_VALUE_SRG   GL_SRG8_EXT
 #define GLTRAITS_VALUE_SRGB  GL_SRGB8
 #define GLTRAITS_VALUE_SRGBA GL_SRGB8_ALPHA8
 
 #define GLTRAITS_VALUE_COMPRESSED_SRED  GL_COMPRESSED_SRGB
 #define GLTRAITS_VALUE_COMPRESSED_SRG   GL_COMPRESSED_SRGB
 #define GLTRAITS_VALUE_COMPRESSED_SRGB  GL_COMPRESSED_SRGB
 #define GLTRAITS_VALUE_COMPRESSED_SRGBA GL_COMPRESSED_SRGB_ALPHA
 
 #define GLTRAITS_VALUE( \
     VALUE, \
     COLUMNS, ROWS, \
     GLSL, FORMAT, TYPE, INTERNAL_FORMAT, \
     OFFSET_ARGS, PTR, \
     V, UNIFORM, SUFFIX, ATTRIB, \
     INTEGER, \
     NORMALIZE_ARGS, \
     V1U, V2U, \
     V1A, V2A, \
     EXTU, EXTA, \
     ... \
 ) \
     template<> \
     struct GLTraits::Value<VALUE> \
     { \
         auto static constexpr name                            = #VALUE; \
         auto static constexpr columns                         = GLint{COLUMNS}; \
         auto static constexpr rows                            = GLint{ROWS}; \
         auto static constexpr glsl                            = GLenum{GL_##GLSL}; \
         auto static constexpr format                          = GLenum{GL_##FORMAT##INTEGER}; \
         auto static constexpr type                            = GLenum{GL_##TYPE}; \
         auto static constexpr internal_format                 = GLenum{GL_##INTERNAL_FORMAT}; \
         auto static constexpr internal_format_srgb            = GLenum{GLTRAITS_VALUE_S##FORMAT}; \
         auto static constexpr internal_format_compressed      = GLenum{GL_COMPRESSED_##FORMAT}; \
         auto static constexpr internal_format_compressed_srgb = GLenum{GLTRAITS_VALUE_COMPRESSED_S##FORMAT}; \
         auto static constexpr integer                         = bool(*#INTEGER); \
a6304722
         auto static constexpr id = \
             (glsl == GL_INT || glsl == GL_UNSIGNED_INT) && \
             sizeof(VALUE) < sizeof(GLint) \
                 ? type \
                 : glsl; \
e38947dc
         void static uniform(GLint location, VALUE const & value) \
         { \
             if (GLBase::debug() >= 1) \
                 GLBase::check_supported({V1U, V2U}, EXTU); \
             glUniform##UNIFORM##ROWS##SUFFIX##V( \
                 location, \
                 __VA_ARGS__ \
                 PTR(value) \
             ); \
         } \
         void static vertex_attrib(GLint location, VALUE const & value) \
         { \
             if (GLBase::debug() >= 1) \
                 GLBase::check_supported({V1A, V2A}, EXTA); \
             if (location == -1) \
                 return; \
             for (auto column = GLuint{0}; column < columns; ++column) \
                 glVertexAttrib##ATTRIB##ROWS##SUFFIX##V( \
                     (GLuint)location + column, \
                     PTR(value) GLTRAITS_##OFFSET_ARGS(+ rows * column) \
                 ); \
         } \
         void static vertex_attrib_pointer( \
             GLint       location, \
             std::size_t offset = 0, \
             std::size_t stride = sizeof(VALUE) \
         ) \
         { \
             if (GLBase::debug() >= 1) \
                 GLBase::check_supported({V1A, V2A}, EXTA); \
             if (location == -1) \
                 return; \
             auto constexpr sizeof_column = sizeof(VALUE) / columns; \
             for (auto column = GLuint{0}; column < columns; ++column) \
                 glVertexAttrib##ATTRIB##Pointer( \
                     (GLuint)location + column, \
                     rows, \
                     type, \
                     GLTRAITS_##NORMALIZE_ARGS(GL_FALSE,) \
                     (GLsizei)stride, \
                     (void const *)(offset + sizeof_column * column) \
                 ); \
         } \
a6304722
     }; \
     template<> \
     struct GLTraits::ValueID<GLTraits::Value<VALUE>::id> \
     { \
         using Value = VALUE; \
e38947dc
     };
 
 #define GLTRAITS_VALUE_SCALAR(                  VALUE, TYPE, GLSL,   INTERNAL, ...) GLTRAITS_VALUE(VALUE,          1, 1, GLSL,                RED,    TYPE, R     ##INTERNAL, OMIT, ,    ,  ,             __VA_ARGS__,)
 #define GLTRAITS_VALUE_VECTOR_N(  N,    FORMAT, VALUE, TYPE, PTR,    INTERNAL, ...) GLTRAITS_VALUE(VALUE##N,       1, N, TYPE##_VEC##N,       FORMAT, TYPE, FORMAT##INTERNAL, KEEP, PTR, v, ,             __VA_ARGS__, 1,)
 #define GLTRAITS_VALUE_MATRIX_N(  N,    FORMAT, VALUE, TYPE, PTR, T, INTERNAL, ...) GLTRAITS_VALUE(VALUE##N,       N, N, TYPE##_MAT##N,       FORMAT, TYPE, FORMAT##INTERNAL, KEEP, PTR, v, Matrix,       __VA_ARGS__, 1, T,)
 #define GLTRAITS_VALUE_MATRIX_N_M(N, M, FORMAT, VALUE, TYPE, PTR, T, INTERNAL, ...) GLTRAITS_VALUE(VALUE##N##x##M, N, M, TYPE##_MAT##N##x##M, FORMAT, TYPE, FORMAT##INTERNAL, KEEP, PTR, v, Matrix##N##x, __VA_ARGS__, 1, T,)
 
 #define GLTRAITS_VALUE_VECTOR(...) \
     GLTRAITS_VALUE_VECTOR_N(  2,    RG,   __VA_ARGS__) \
     GLTRAITS_VALUE_VECTOR_N(  3,    RGB,  __VA_ARGS__) \
     GLTRAITS_VALUE_VECTOR_N(  4,    RGBA, __VA_ARGS__)
 
 #define GLTRAITS_VALUE_MATRIX_(SUPPORT_ARGS, ...) \
     GLTRAITS_VALUE_MATRIX_N(  2,    RG,   __VA_ARGS__ GLTRAITS_##SUPPORT_ARGS(, 2, 0, 2, 0, {}, {})) \
     GLTRAITS_VALUE_MATRIX_N_M(2, 3, RGB,  __VA_ARGS__ GLTRAITS_##SUPPORT_ARGS(, 2, 1, 2, 0, {}, {})) \
     GLTRAITS_VALUE_MATRIX_N_M(2, 4, RGBA, __VA_ARGS__ GLTRAITS_##SUPPORT_ARGS(, 2, 1, 2, 0, {}, {})) \
     GLTRAITS_VALUE_MATRIX_N_M(3, 2, RG,   __VA_ARGS__ GLTRAITS_##SUPPORT_ARGS(, 2, 1, 2, 0, {}, {})) \
     GLTRAITS_VALUE_MATRIX_N(  3,    RGB,  __VA_ARGS__ GLTRAITS_##SUPPORT_ARGS(, 2, 0, 2, 0, {}, {})) \
     GLTRAITS_VALUE_MATRIX_N_M(3, 4, RGBA, __VA_ARGS__ GLTRAITS_##SUPPORT_ARGS(, 2, 1, 2, 0, {}, {})) \
     GLTRAITS_VALUE_MATRIX_N_M(4, 2, RG,   __VA_ARGS__ GLTRAITS_##SUPPORT_ARGS(, 2, 1, 2, 0, {}, {})) \
     GLTRAITS_VALUE_MATRIX_N_M(4, 3, RGB,  __VA_ARGS__ GLTRAITS_##SUPPORT_ARGS(, 2, 1, 2, 0, {}, {})) \
     GLTRAITS_VALUE_MATRIX_N(  4,    RGBA, __VA_ARGS__ GLTRAITS_##SUPPORT_ARGS(, 2, 0, 2, 0, {}, {}))
 
 #define GLTRAITS_VALUE_MATRIXS(...) GLTRAITS_VALUE_MATRIX_(KEEP, __VA_ARGS__)
 #define GLTRAITS_VALUE_MATRIX(...)  GLTRAITS_VALUE_MATRIX_(OMIT, __VA_ARGS__)
 
 GLTRAITS_VALUE_SCALAR( GLfloat,   FLOAT,          FLOAT,                    32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
 GLTRAITS_VALUE_SCALAR( bool,      BYTE,           BOOL,                     8I,   i,  I, _INTEGER, OMIT, 2, 0, 3, 0, {}, {})
 GLTRAITS_VALUE_SCALAR( GLbyte,    BYTE,           INT,                      8I,   i,  I, _INTEGER, OMIT, 2, 0, 3, 0, {}, {})
 GLTRAITS_VALUE_SCALAR( GLshort,   SHORT,          INT,                      16I,  i,  I, _INTEGER, OMIT, 2, 0, 3, 0, {}, {})
 GLTRAITS_VALUE_SCALAR( GLint,     INT,            INT,                      32I,  i,  I, _INTEGER, OMIT, 2, 0, 3, 0, {}, {})
 GLTRAITS_VALUE_SCALAR( GLubyte,   UNSIGNED_BYTE,  UNSIGNED_INT,             8UI,  ui, I, _INTEGER, OMIT, 3, 0, 3, 0, {}, {})
 GLTRAITS_VALUE_SCALAR( GLushort,  UNSIGNED_SHORT, UNSIGNED_INT,             16UI, ui, I, _INTEGER, OMIT, 3, 0, 3, 0, {}, {})
 GLTRAITS_VALUE_SCALAR( GLuint,    UNSIGNED_INT,   UNSIGNED_INT,             32UI, ui, I, _INTEGER, OMIT, 3, 0, 3, 0, {}, {})
 GLTRAITS_VALUE_SCALAR( GLdouble,  DOUBLE,         DOUBLE,                   ED,   d,  L, ,         OMIT, 4, 0, 4, 1, "GL_ARB_gpu_shader_fp64", "GL_ARB_vertex_attrib_64bit")
 
 #ifdef GLM_VERSION
 #include <glm/gtc/type_ptr.hpp>
 GLTRAITS_VALUE_VECTOR( glm::vec,  FLOAT,          glm::value_ptr,           32F,  f,  ,  ,         KEEP, 2, 0, 2, 0, {}, {})
 GLTRAITS_VALUE_MATRIXS(glm::mat,  FLOAT,          glm::value_ptr, GL_FALSE, 32F,  f,  ,  ,         KEEP)
 GLTRAITS_VALUE_VECTOR( glm::ivec, INT,            glm::value_ptr,           32I,  i,  I, _INTEGER, OMIT, 2, 0, 3, 0, {}, {})
 GLTRAITS_VALUE_VECTOR( glm::uvec, UNSIGNED_INT,   glm::value_ptr,           32UI, ui, I, _INTEGER, OMIT, 3, 0, 3, 0, {}, {})
 GLTRAITS_VALUE_VECTOR( glm::dvec, DOUBLE,         glm::value_ptr,           ,     d,  L, ,         OMIT, 4, 0, 4, 1, "GL_ARB_gpu_shader_fp64", "GL_ARB_vertex_attrib_64bit")
 GLTRAITS_VALUE_MATRIX( glm::dmat, DOUBLE,         glm::value_ptr, GL_FALSE, ,     d,  L, ,         OMIT, 4, 0, 4, 1, "GL_ARB_gpu_shader_fp64", "GL_ARB_vertex_attrib_64bit")
 #endif
 
799e1ae5
 //// Object
 
 #define GLTRAITS_OBJECT_( \
     OBJECT_TYPE, OBJECT, \
     GEN_OBJECTS, DELETE_OBJECTS, \
     INFO_LOG_ARGS, \
     V1, V2, EXT \
 ) \
     template<> \
     struct GLTraits::Object<GL_##OBJECT_TYPE> \
     { \
         auto static constexpr name = "GL_" #OBJECT_TYPE; \
         template<typename... Args> \
         void static gen_objects(GLsizei n, GLuint * objects, Args... args) \
         { \
             if (GLBase::debug() >= 1) \
                 GLBase::check_supported({V1, V2}, EXT); \
             GEN_OBJECTS; \
         } \
         void static delete_objects(GLsizei n, GLuint const * objects) \
         { \
             if (GLBase::debug() >= 1) \
                 GLBase::check_supported({V1, V2}, EXT); \
             DELETE_OBJECTS; \
         } \
         GLTRAITS_##INFO_LOG_ARGS( \
         std::string static info_log(GLuint object) \
         { \
             if (GLBase::debug() >= 1) \
                 GLBase::check_supported({V1, V2}, EXT); \
             auto length = GLint{}; \
             glGet##OBJECT##iv(object, GL_INFO_LOG_LENGTH, &length); \
             if (length != 0) \
                 --length; \
             auto info_log = std::string((std::size_t)length, char{}); \
             glGet##OBJECT##InfoLog( \
                 object, \
                 length+1, \
                 nullptr, \
                 &info_log[0] \
             ); \
             return info_log; \
         } \
         ) \
     };
 
 #define GLTRAITS_OBJECT(OBJECT_TYPE, OBJECT, OBJECTS, ...) \
     GLTRAITS_OBJECT_( \
         OBJECT_TYPE, OBJECT, \
         glGen##OBJECTS(n, objects, args...), \
         glDelete##OBJECTS(n, objects), \
         __VA_ARGS__ \
     )
 
 #define GLTRAITS_OBJECT_GLSL(OBJECT_TYPE, OBJECT, OBJECTS, ...) \
     GLTRAITS_OBJECT_( \
         OBJECT_TYPE, OBJECT, \
         while (n--) objects[n] = glCreate##OBJECT(args...), \
         while (n--) glDelete##OBJECT(objects[n]), \
         __VA_ARGS__ \
     )
 
 GLTRAITS_OBJECT(     TEXTURE,            Texture,           Textures,           OMIT, 1, 1, {})
 GLTRAITS_OBJECT(     BUFFER,             Buffer,            Buffers,            OMIT, 1, 5, {})
 GLTRAITS_OBJECT(     QUERY,              Query,             Queries,            OMIT, 1, 5, {})
 GLTRAITS_OBJECT_GLSL(PROGRAM,            Program,           Programs,           KEEP, 2, 0, {})
 GLTRAITS_OBJECT_GLSL(SHADER,             Shader,            Shaders,            KEEP, 2, 0, {})
 GLTRAITS_OBJECT(     VERTEX_ARRAY,       VertexArray,       VertexArrays,       OMIT, 3, 0, "GL_ARB_vertex_array_object")
 GLTRAITS_OBJECT(     FRAMEBUFFER,        Framebuffer,       Framebuffers,       OMIT, 3, 0, "GL_ARB_framebuffer_object")
 GLTRAITS_OBJECT(     RENDERBUFFER,       Renderbuffer,      Renderbuffers,      OMIT, 3, 0, "GL_ARB_framebuffer_object")
 GLTRAITS_OBJECT(     SAMPLER,            Sampler,           Samplers,           OMIT, 3, 3, "GL_ARB_sampler_objects")
 GLTRAITS_OBJECT(     TRANSFORM_FEEDBACK, TransformFeedback, TransformFeedbacks, OMIT, 4, 0, "GL_ARB_transform_feedback2")
 GLTRAITS_OBJECT(     PROGRAM_PIPELINE,   ProgramPipeline,   ProgramPipelines,   KEEP, 4, 1, "GL_ARB_separate_shader_objects")
 
3378873e
 //// Texture
 
 template<>
 struct GLTraits::Texture<0>
 {
 protected:
     void static defaults_(
         GLenum & internal_format,
         GLenum & format,
         GLenum & type,
         GLenum   default_internal_format,
         GLenum   default_format,
         GLenum   default_type
     )
     {
         if (!internal_format) internal_format = default_internal_format;
         switch (internal_format)
         {
             case GL_DEPTH_STENCIL:
             case GL_DEPTH24_STENCIL8:
             case GL_DEPTH32F_STENCIL8:
                 if (!format) format = GL_DEPTH_STENCIL;
                 if (!type)   type   = GL_UNSIGNED_INT_24_8;
                 break;
             case GL_DEPTH_COMPONENT:
             case GL_DEPTH_COMPONENT16:
             case GL_DEPTH_COMPONENT24:
             case GL_DEPTH_COMPONENT32:
             case GL_DEPTH_COMPONENT32F:
                 if (!format) format = GL_DEPTH_COMPONENT;
                 if (!type)   type   = GL_UNSIGNED_INT;
                 break;
             case GL_STENCIL_INDEX:
             case GL_STENCIL_INDEX1:
             case GL_STENCIL_INDEX4:
             case GL_STENCIL_INDEX8:
             case GL_STENCIL_INDEX16:
                 if (!format) format = GL_STENCIL_INDEX;
                 if (!type)   type   = GL_UNSIGNED_BYTE;
                 break;
             default:
                 if (!format) format = default_format;
                 if (!type)   type   = default_type;
         }
     }
 };
 
 #define GLTRAITS_TEXTURE_IMAGE( \
     N, \
     TEXTURE, TEXTURE_LOWER, \
     TYPE, NAME, \
     V1, V2, EXT \
 ) \
     template<typename Value = GLubyte> \
     void static TEXTURE_LOWER##_image( \
         TYPE          NAME, \
         Size          size, \
         GLenum        internal_format = 0, \
         Value const * data            = nullptr, \
         GLenum        format          = 0, \
         GLenum        type            = 0, \
         GLint         level           = 0, \
         GLint         border          = 0 \
     ) \
     { \
         if (GLBase::debug() >= 1) \
             GLBase::check_supported({V1, V2}, EXT); \
         defaults_( \
             internal_format, \
             format, \
             type, \
             GLTraits::Value<Value>::internal_format, \
             GLTraits::Value<Value>::format, \
             GLTraits::Value<Value>::type \
         ); \
         gl##TEXTURE##Image##N##D( \
             NAME, \
             level, \
             (GLint)internal_format, \
             std::get<Is>(size)..., \
             border, \
             format, \
             type, \
             data \
         ); \
     }
 
 #define GLTRAITS_TEXTURE_STORAGE( \
     N, \
     TEXTURE, TEXTURE_LOWER, \
     TYPE, NAME, \
     V1, V2, EXT \
 ) \
     template<typename Value = GLubyte> \
     void static TEXTURE_LOWER##_storage( \
         TYPE    NAME, \
         Size    size, \
         GLenum  internal_format = 0, \
         GLsizei levels          = 1 \
     ) \
     { \
         if (GLBase::debug() >= 1) \
             GLBase::check_supported({V1, V2}, EXT); \
         if (!internal_format) \
             internal_format = GLTraits::Value<Value>::internal_format; \
         gl##TEXTURE##Storage##N##D( \
             NAME, \
             levels, \
             internal_format, \
             std::get<Is>(size)... \
         ); \
     }
 
 #define GLTRAITS_TEXTURE_SUB_IMAGE( \
     N, \
     TEXTURE, TEXTURE_LOWER, \
     TYPE, NAME, \
     V1, V2, EXT \
 ) \
     template<typename Value = GLubyte> \
     void static TEXTURE_LOWER##_sub_image( \
         TYPE          NAME, \
         Size          size, \
         Value const * data, \
         GLenum        format = 0, \
         GLenum        type   = 0, \
         Offset        offset = {}, \
         GLint         level  = 0 \
     ) \
     { \
         if (GLBase::debug() >= 1) \
             GLBase::check_supported({V1, V2}, EXT); \
         if (!format) format = GLTraits::Value<Value>::format; \
         if (!type)   type   = GLTraits::Value<Value>::type; \
         gl##TEXTURE##SubImage##N##D( \
             NAME, \
             level, \
             std::get<Is>(offset)..., \
             std::get<Is>(size)  ..., \
             format, \
             type, \
             data \
         ); \
     }
 
 #define GLTRAITS_TEXTURE_COPY_SUB_IMAGE( \
     N, \
     TEXTURE, TEXTURE_LOWER, \
     TYPE, NAME, \
     V1, V2, EXT \
 ) \
     void static copy_##TEXTURE_LOWER##_sub_image( \
         TYPE       NAME, \
         CopySize   copy_size, \
         CopyOffset copy_offset = {}, \
         Offset     offset      = {}, \
         GLint      level       = 0 \
     ) \
     { \
         if (GLBase::debug() >= 1) \
             GLBase::check_supported({V1, V2}, EXT); \
         glCopy##TEXTURE##SubImage##N##D( \
             NAME, \
             level, \
             std::get<Is>          (offset)     ..., \
             std::get<CopyOffsetIs>(copy_offset)..., \
             std::get<CopySizeIs>  (copy_size)  ... \
         ); \
     }
 
 #define GLTRAITS_TEXTURE_COMPRESSED_IMAGE( \
     N, \
     TEXTURE, TEXTURE_LOWER, \
     TYPE, NAME, \
     V1, V2, EXT \
 ) \
     void static compressed_##TEXTURE_LOWER##_image( \
         TYPE         NAME, \
         Size         size, \
         GLenum       internal_format, \
         GLsizei      data_size = 0, \
         void const * data      = nullptr, \
         GLint        level     = 0, \
         GLint        border    = 0 \
     ) \
     { \
         if (GLBase::debug() >= 1) \
             GLBase::check_supported({V1, V2}, EXT); \
         glCompressed##TEXTURE##Image##N##D( \
             NAME, \
             level, \
             internal_format, \
             std::get<Is>(size)..., \
             border, \
             data_size, \
             data \
         ); \
     }
 
 #define GLTRAITS_TEXTURE_COMPRESSED_SUB_IMAGE( \
     N, \
     TEXTURE, TEXTURE_LOWER, \
     TYPE, NAME, \
     V1, V2, EXT \
 ) \
     void static compressed_##TEXTURE_LOWER##_sub_image( \
         TYPE         NAME, \
         Size         size, \
         GLsizei      data_size, \
         void const * data, \
         GLenum       internal_format, \
         Offset       offset = {}, \
         GLint        level  = 0 \
     ) \
     { \
         if (GLBase::debug() >= 1) \
             GLBase::check_supported({V1, V2}, EXT); \
         glCompressed##TEXTURE##SubImage##N##D( \
             NAME, \
             level, \
             std::get<Is>(offset)..., \
             std::get<Is>(size)  ..., \
             internal_format, \
             data_size, \
             data \
         ); \
     }
 
 #define GLTRAITS_TEXTURE( \
     N, \
     V1_1, V2_1, \
     V1_2, V2_2 \
 ) \
     template< \
         std::size_t... Is, \
         std::size_t... CopySizeIs, \
         std::size_t... CopyOffsetIs \
     > \
     struct GLTraits::Texture_< \
         N, \
         GLTraits::Indices<Is...>, \
         GLTraits::Indices<CopySizeIs...>, \
         GLTraits::Indices<CopyOffsetIs...> \
     > \
     : \
         GLTraits::Texture<0> \
     { \
         using Size       = std::array<GLsizei, sizeof...(Is)>; \
         using Offset     = std::array<GLsizei, sizeof...(Is)>; \
         using CopySize   = std::array<GLsizei, sizeof...(CopySizeIs)>; \
         using CopyOffset = std::array<GLsizei, sizeof...(CopyOffsetIs)>; \
         auto static constexpr default_target  = GL_TEXTURE_##N##D; \
         auto static constexpr default_binding = GL_TEXTURE_BINDING_##N##D; \
         GLTRAITS_TEXTURE_IMAGE(               N, Tex,     tex,     GLenum, target,  V1_1, V2_1, {}) \
         GLTRAITS_TEXTURE_SUB_IMAGE(           N, Tex,     tex,     GLenum, target,  V1_2, V2_2, {}) \
         GLTRAITS_TEXTURE_COPY_SUB_IMAGE(      N, Tex,     tex,     GLenum, target,  V1_2, V2_2, {}) \
         GLTRAITS_TEXTURE_COMPRESSED_IMAGE(    N, Tex,     tex,     GLenum, target,  1,    3,    {}) \
         GLTRAITS_TEXTURE_COMPRESSED_SUB_IMAGE(N, Tex,     tex,     GLenum, target,  1,    3,    {}) \
         GLTRAITS_TEXTURE_STORAGE(             N, Tex,     tex,     GLenum, target,  4,    2,    "GL_ARB_texture_storage") \
         GLTRAITS_TEXTURE_STORAGE(             N, Texture, texture, GLuint, texture, 4,    5,    "GL_ARB_direct_state_access") \
         GLTRAITS_TEXTURE_SUB_IMAGE(           N, Texture, texture, GLuint, texture, 4,    5,    "GL_ARB_direct_state_access") \
         GLTRAITS_TEXTURE_COPY_SUB_IMAGE(      N, Texture, texture, GLuint, texture, 4,    5,    "GL_ARB_direct_state_access") \
         GLTRAITS_TEXTURE_COMPRESSED_SUB_IMAGE(N, Texture, texture, GLuint, texture, 4,    5,    "GL_ARB_direct_state_access") \
     }; \
     template<> \
     struct GLTraits::Texture<N> \
     : \
         Texture_< \
             N, \
             MakeIndices<N>, \
             MakeIndices<N < 2 ? N : 2>, \
             MakeIndices<2> \
         > \
     {};
 
 GLTRAITS_TEXTURE(1, 1, 0, 1, 1)
 GLTRAITS_TEXTURE(2, 1, 0, 1, 1)
 GLTRAITS_TEXTURE(3, 1, 2, 1, 2)
 
e38947dc
 
 /// Guards
 
 #endif