/// 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 Value, std::size_t offset>
struct Member
{};
template<typename T, typename... Member>
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;
std::size_t offset;
};
template<std::size_t N>
struct MemberInfos
{
MemberInfo infos[N];
};
struct MemberAny
{
MemberInfo * info;
constexpr static auto align(std::size_t align, std::size_t offset)
{
return ((offset - 1) / align + 1) * align;
}
template<typename Value>
constexpr operator Value()
{
return (
info[0].id = GLTraits::Value<Value>::id,
info[0].offset = align(alignof(Value), info[0].offset),
info[1].offset = info[0].offset + sizeof(Value),
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)+1> 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,
Member<
typename ValueID<member_infos<T, Is...>().infos[Is].id>::Value,
member_infos<T, Is...>().infos[Is].offset
>...
>{};
}
#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