/// Guards #ifndef GLTRAITS_HPP_ #define GLTRAITS_HPP_ /// Includes #include <array> #include <cstddef> #include <string> #include <glbase.hpp> /// GLTraits class GLTraits { public: //// Value template<typename> struct Value; template<GLenum id> struct ValueID; //// Object template<GLenum object_type> struct Object; //// Texture template<std::size_t N> struct Texture; //// Member #if __cplusplus >= 201402L template<typename T, typename... Value> 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 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_; //// Member // Stripped down version of Boost::PFR. #if __cplusplus >= 201402L struct MemberInfo { GLenum id; }; template<std::size_t N> struct MemberInfos { MemberInfo infos[N]; }; struct MemberAny { MemberInfo * info; template<typename Value> constexpr operator Value() { return ( info->id = GLTraits::Value<Value>::id, 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> static constexpr auto member_infos(MemberInfos<sizeof...(Is)> infos = {}) { return ((void)T{MemberAny{&infos.infos[Is]}...}, infos); } template<typename T, std::size_t... Is> static constexpr auto members_(Indices<Is...>) { return Members< T, typename ValueID<member_infos<T, Is...>().infos[Is].id>::Value... >{}; } #endif }; //// 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); \ auto static constexpr id = \ (glsl == GL_INT || glsl == GL_UNSIGNED_INT) && \ sizeof(VALUE) < sizeof(GLint) \ ? type \ : glsl; \ 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) \ ); \ } \ }; \ template<> \ struct GLTraits::ValueID<GLTraits::Value<VALUE>::id> \ { \ using Value = VALUE; \ }; #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 //// 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") //// 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) /// Guards #endif