src/glbase.cpp
58cc126e
 /// Includes
 
 
 #include <glbase.hpp>
 
 #include <algorithm>
 #include <array>
 #include <cstdio>
 #include <fstream>
 #include <iostream>
 #include <iterator>
 #include <sstream>
 #include <string>
 #include <unordered_set>
 
 #define STR_EXCEPTION GLBase::Exception
 #include <str.hpp>
 
 
 /// Base
 
 
 GLBASE_GLOBAL(GLBase::version_max_, {})
 
 
 bool GLBase::supported(
     Version             version_min,
     std::string const & extension
 )
 {
     if (!extension.empty() && extension.rfind("GL_", 0) == std::string::npos)
         STR_THROW("Failed to parse extension \"" << extension << "\".");
     auto static thread_local version    = Version{};
     auto static thread_local extensions = std::unordered_set<std::string>{};
     if (version == Version{})
     {
         auto const * version_str = string(GL_VERSION);
         if (!version_str)
             return false;
         // NOLINTNEXTLINE
         if (std::sscanf(version_str, "%d.%d", &version[0], &version[1]) != 2)
             STR_THROW("Failed to parse version \"" << version_str << "\".");
         if (version[0] >= 3)
         {
             auto count = (GLuint)integer(GL_NUM_EXTENSIONS);
             for (auto index = GLuint{0}; index < count; ++index)
             {
                 auto const * extension_str = string(GL_EXTENSIONS, index);
                 if (extension_str)
                     extensions.insert(extension_str);
             }
         }
         else
         {
             auto const * extensions_str = string(GL_EXTENSIONS);
             if (!extensions_str)
                 return false;
             auto istream = std::istringstream(extensions_str);
             using iterator = std::istream_iterator<std::string>;
             std::copy(
                 iterator(istream),
                 iterator(),
                 std::inserter(extensions, extensions.end())
             );
         }
     }
     if (version_max() != Version{})
         if
         (
             version_min[0] > version_max()[0] ||
             (
                 version_min[0] == version_max()[0] &&
                 version_min[1] >  version_max()[1]
             )
         )
             return false;
     if (version_min != Version{})
         if
         (
             version[0] > version_min[0] ||
             (
                 version[0] == version_min[0] &&
                 version[1] >= version_min[1]
             )
         )
             return true;
     if (!extension.empty())
         if (extensions.find(extension) != extensions.end())
             return true;
     return false;
 }
 
 
 char const * GLBase::string(GLenum name)
 try
 {
     auto const * string = (char const *)glGetString(name);
     check_error_(glGetError());
     return string;
 }
 catch (...)
 {
     fail_action_("get string", str_enum_(name));
 }
 
 
 char const * GLBase::string(GLenum name, GLuint index)
 try
 {
     // if (debug() >= 1)
     //     check_supported({3, 0});
     auto const * string = (char const *)glGetStringi(name, index);
     check_error_(glGetError());
     return string;
 }
 catch (...)
 {
     fail_action_("get string", STR(str_enum_(name) << "[" << index << "]"));
 }
 
 
 GLint GLBase::integer(GLenum name)
 try
 {
     auto integer = GLint{};
     glGetIntegerv(name, &integer);
     check_error_(glGetError());
     return integer;
 }
 catch (...)
 {
     fail_action_("get integer", str_enum_(name));
 }
 
 
 /// Path
 
 
 GLBase::Path GLBase::path_prefix_(
     Path const & path,
     Path const & prefix
 )
 {
     check_path_(path);
     if (prefix.empty() || path[0] == '/')
         return path;
     return STR(prefix << "/" << path);
 }
 
 
 /// TGA
 
 
 GLBase::TGA_::TGA_(Size size, Data data, Path const & path)
 try
 :
     size_{size},
     data_{std::move(data)}
 {
     check_data_size_();
 }
 catch (...)
 {
     fail_action_("create", name_(path));
 }
 
 
 GLBase::TGA_ GLBase::TGA_::read(Path const & path)
 try
 {
     auto istream = std::ifstream(path, std::ios::binary);
     auto header  = Header_({});
     istream.read((char *)header.data(), (std::streamsize)header.size());
     check_header_(header);
     auto size = header.tga_size();
     auto data = Data(4 * (std::size_t)size[0] * (std::size_t)size[1]);
     istream.read((char *)data.data(), (std::streamsize)data.size());
     if (!istream)
         STR_THROW_ERRNO();
     if (!istream.eof())
         STR_THROW("Garbage at end of file.");
     return TGA_(size, std::move(data), path);;
 }
 catch (...)
 {
     fail_action_("read", name_(path));
 }
 
 
 void GLBase::TGA_::write(Path const & path) const
 try
 {
     auto header  = Header_(size());
     auto ostream = std::ofstream(path, std::ios::binary);
     ostream.write((char *)header.data(), (std::streamsize)header.size());
     ostream.write((char *)data_ .data(), (std::streamsize)data_ .size());
     if (!ostream)
         STR_THROW_ERRNO();
 }
 catch (...)
 {
     fail_action_("write", name_(path));
 }
 
 
 GLBase::TGA_::Header_::Header_(Size size)
 :
     std::array<GLubyte, 18>{                // NOLINT
         0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, // NOLINT
         (GLubyte)(size[0] >> 0),            // NOLINT
         (GLubyte)(size[0] >> 8),            // NOLINT
         (GLubyte)(size[1] >> 0),            // NOLINT
         (GLubyte)(size[1] >> 8),            // NOLINT
         32, 0,                              // NOLINT
     }
 {
 }
 
 
 GLBase::TGA_::Size GLBase::TGA_::Header_::tga_size() const
 {
     return {
         (GLsizei)((*this)[12]) << 0 | // NOLINT
         (GLsizei)((*this)[13]) << 8,  // NOLINT
         (GLsizei)((*this)[14]) << 0 | // NOLINT
         (GLsizei)((*this)[15]) << 8,  // NOLINT
     };
 }
 
 
 std::string GLBase::TGA_::name_(Path const & path)
 {
     return STR("TGA" << " " << str_path_(path));
 }
 
 
 void GLBase::TGA_::check_header_(Header_ const & header)
 {
     auto header_ = Header_(header.tga_size());
     if (header != header_)
         STR_THROW(
             "Expected TGA header"                      << " " <<
             "[" << STR_JOIN(", ", byte, byte, header_) << "]" << ", " <<
             "got"                                      << " " <<
             "[" << STR_JOIN(", ", byte, byte, header)  << "]" << "."
         );
 }
 
 
 void GLBase::TGA_::check_data_size_() const
 {
     auto size = this->size();
     auto data_size = (std::size_t)(4 * size[0] * size[1]);
     if (data_size != data_.size())
         STR_THROW(
             "Expected TGA data size " << data_size    << ", " <<
             "got "                    << data_.size() << "."
         );
 }
 
 
 //// Debug
 
 
 GLBASE_GLOBAL(GLBase::debug_,          {0})
 GLBASE_GLOBAL(GLBase::debug_callback_, {[](std::string const & message) {
     std::cerr << message << std::endl;
 }})
 
 
 int GLBase::debug(int debug)
 {
     auto debug_old = debug_;
     debug_         = debug;
     if (supported({4, 3}, "GL_KHR_debug"))
     {
         if (debug_old && !debug)
             glDisable(GL_DEBUG_OUTPUT);
         if (!debug_old && debug)
         {
             glEnable(GL_DEBUG_OUTPUT);
             glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
             glDebugMessageControl(
                 GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE,
                 0, nullptr,
                 GL_TRUE
             );
         }
         if (debug_old >= 2 && debug < 2)
             glDebugMessageControl(
                 GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION,
                 0, nullptr,
                 GL_FALSE
             );
         if (debug_old < 2 && debug >= 2)
             glDebugMessageControl(
                 GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION,
                 0, nullptr,
                 GL_TRUE
             );
     }
     return debug_old;
 }
 
 
 void GLBase::debug_action_(
     std::string const & action,
     std::string const & name
 )
 {
     debug_message(STR("Trying to " << action << " " << name << "."));
 }
 
 
 /// Check
 
 
 void GLBase::check_path_(Path const & path)
 {
     if (path.empty())
         STR_THROW(
             "Expected " << "non-empty path" << ", " <<
             "got "      << str_path_(path)  << "."
         );
 }
 
 
 void GLBase::check_error_(GLenum error)
 {
     if (error != GL_NO_ERROR)
         STR_THROW(
             "Expected " << "no error"        << ", " <<
             "got "      << str_error_(error) << "."
         );
 }
 
 
 void GLBase::check_supported(
     Version             version_min,
     std::string const & extension
 )
 {
     if (!supported(version_min, extension))
     {
         auto const * version_str = string(GL_VERSION);
         STR_THROW(
             "Expected OpenGL version >=" <<
                 STR_JOIN(".", it, it, version_min) <<
                 (
                     !extension.empty()
                         ? STR(" or extension " << extension)
                         : ""
                 ) <<
             ", " <<
             "got " <<
                 (
                     version_str
                         ? version_str
                         : "none (no current context?)"
                 ) <<
             "."
         );
     }
 }
 
 
 void GLBase::check_type_(
     GLenum type,
     GLenum type_expected
 )
 {
     if (type != type_expected)
         STR_THROW(
             "Expected type " << str_type_(type_expected) << ", " <<
             "got "           << str_type_(type)          << "."
         );
 }
 
 
 void GLBase::check_format_(
     GLenum format,
     GLenum format_expected
 )
 {
     if (format != format_expected)
         STR_THROW(
             "Expected format " << str_format_(format_expected) << ", " <<
             "got "             << str_format_(format)          << "."
         );
 }
 
 
 void GLBase::check_internal_format_(GLenum internal_format)
 {
     switch (internal_format)
     {
         case GL_RED:
         case GL_RGB:
         case GL_RGBA:
         case GL_DEPTH_COMPONENT:
         case GL_STENCIL_INDEX:
             check_supported({1, 0});
             return;
         case GL_R3_G3_B2:
             check_supported({1, 1});
             return;
         case GL_RGB4:
         case GL_RGB5:
         case GL_RGB8:
         case GL_RGB10:
         case GL_RGB12:
         case GL_RGB16:
         case GL_RGB5_A1:
         case GL_RGB10_A2:
         case GL_RGBA2:
         case GL_RGBA4:
         case GL_RGBA8:
         case GL_RGBA12:
         case GL_RGBA16:
             check_supported({1, 1}, "GL_EXT_texture");
             return;
         case GL_RGB2_EXT:
             check_supported({}, "GL_EXT_texture");
             return;
         case GL_COMPRESSED_RGB:
         case GL_COMPRESSED_RGBA:
             check_supported({1, 3}, "GL_ARB_texture_compression");
             return;
         case GL_DEPTH_COMPONENT16:
         case GL_DEPTH_COMPONENT24:
         case GL_DEPTH_COMPONENT32:
             check_supported({1, 4}, "GL_ARB_depth_texture");
             return;
         case GL_SRGB:
         case GL_SRGB8:
         case GL_SRGB_ALPHA:
         case GL_SRGB8_ALPHA8:
         case GL_COMPRESSED_SRGB:
         case GL_COMPRESSED_SRGB_ALPHA:
             check_supported({2, 1}, "GL_EXT_texture_sRGB");
             return;
         case GL_SR8_EXT:
             check_supported({}, "GL_EXT_texture_sRGB_R8");
             return;
         case GL_SRG8_EXT:
             check_supported({}, "GL_EXT_texture_sRGB_RG8");
             return;
         case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
         case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
         case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
         case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
             check_supported({}, "GL_EXT_texture_compression_s3tc");
             return;
         case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
         case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
         case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
         case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
             check_supported({}, "GL_EXT_texture_sRGB");
             check_supported({}, "GL_EXT_texture_compression_s3tc");
             return;
         case GL_COMPRESSED_RED:
         case GL_COMPRESSED_RG:
             check_supported({3, 0});
             return;
         case GL_COMPRESSED_RED_RGTC1:
         case GL_COMPRESSED_RG_RGTC2:
         case GL_COMPRESSED_SIGNED_RED_RGTC1:
         case GL_COMPRESSED_SIGNED_RG_RGTC2:
             check_supported({3, 0}, "GL_ARB_texture_compression_rgtc");
             return;
         case GL_RGB16F:
         case GL_RGB32F:
         case GL_RGBA16F:
         case GL_RGBA32F:
             check_supported({3, 0}, "GL_ARB_texture_float");
             return;
         case GL_RGB8I:
         case GL_RGB8UI:
         case GL_RGB16I:
         case GL_RGB16UI:
         case GL_RGB32I:
         case GL_RGB32UI:
         case GL_RGBA8I:
         case GL_RGBA8UI:
         case GL_RGBA16I:
         case GL_RGBA16UI:
         case GL_RGBA32I:
         case GL_RGBA32UI:
             check_supported({3, 0}, "GL_EXT_texture_integer");
             return;
         case GL_R8:
         case GL_R8I:
         case GL_R8UI:
         case GL_R16:
         case GL_R16I:
         case GL_R16UI:
         case GL_R32I:
         case GL_R32UI:
         case GL_R16F:
         case GL_R32F:
         case GL_RG:
         case GL_RG8:
         case GL_RG8I:
         case GL_RG8UI:
         case GL_RG16:
         case GL_RG16I:
         case GL_RG16UI:
         case GL_RG32I:
         case GL_RG32UI:
         case GL_RG16F:
         case GL_RG32F:
             check_supported({3, 0}, "GL_ARB_texture_rg");
             return;
         case GL_R11F_G11F_B10F:
             check_supported({3, 0}, "GL_EXT_packed_float");
             return;
         case GL_RGB9_E5:
             check_supported({3, 0}, "GL_EXT_texture_shared_exponent");
             return;
         case GL_DEPTH_STENCIL:
         case GL_DEPTH24_STENCIL8:
             check_supported({3, 0}, "GL_EXT_packed_depth_stencil");
             return;
         case GL_DEPTH32F_STENCIL8:
         case GL_DEPTH_COMPONENT32F:
             check_supported({3, 0}, "GL_ARB_depth_buffer_float");
             return;
         case GL_STENCIL_INDEX1:
         case GL_STENCIL_INDEX4:
         case GL_STENCIL_INDEX8:
         case GL_STENCIL_INDEX16:
             check_supported({3, 0}, "GL_ARB_framebuffer_object");
             return;
         case GL_R8_SNORM:
         case GL_R16_SNORM:
         case GL_RG8_SNORM:
         case GL_RG16_SNORM:
         case GL_RGB8_SNORM:
         case GL_RGB16_SNORM:
         case GL_RGBA8_SNORM:
         case GL_RGBA16_SNORM:
             check_supported({3, 1}, "GL_EXT_texture_snorm");
             return;
         case GL_RGB10_A2UI:
             check_supported({3, 3}, "GL_ARB_texture_rgb10_a2ui");
             return;
         case GL_RGB565:
             check_supported({4, 1}, "GL_ARB_ES2_compatibility");
             return;
         case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
         case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
         case GL_COMPRESSED_RGBA_BPTC_UNORM:
         case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
             check_supported({4, 2}, "GL_ARB_texture_compression_bptc");
             return;
         case GL_COMPRESSED_RGB8_ETC2:
         case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
         case GL_COMPRESSED_SRGB8_ETC2:
         case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
         case GL_COMPRESSED_RGBA8_ETC2_EAC:
         case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
         case GL_COMPRESSED_R11_EAC:
         case GL_COMPRESSED_RG11_EAC:
         case GL_COMPRESSED_SIGNED_R11_EAC:
         case GL_COMPRESSED_SIGNED_RG11_EAC:
             check_supported({4, 3}, "GL_ARB_ES3_compatibility");
             return;
         case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
         case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:
         case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:
         case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:
         case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
         case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:
         case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:
         case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
         case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:
         case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:
         case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:
         case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
         case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:
         case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
         case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
             check_supported({}, "GL_KHR_texture_compression_astc_ldr");
             return;
         default:
             STR_THROW(
                 "Expected " << "internal format"                     << ", " <<
                 "got "      << str_internal_format_(internal_format) << "."
             );
     }
 }
 
 
 /// Fail
 
 
 void GLBase::fail_action_(
     std::string const & action,
     std::string const & name
 )
 {
     STR_RETHROW("Failed to " << action << " " << name << ":\n");
 }
 
 
 /// String
 
 
 std::string GLBase::str_path_(Path const & path)
 {
     return STR("\"" << path << "\"");
 }
 
 
 std::string GLBase::str_paths_(Paths const & paths)
 {
     return STR_JOIN(", ", path, str_path_(path), paths);
 }
 
 
 std::string GLBase::str_enum_(GLenum name)
 {
     return STR(std::hex << std::showbase << std::uppercase << name);
 }
 
 
 std::string GLBase::str_error_(GLenum error)
 {
     switch (error)
     {
         STR_CASE(GL_NO_ERROR)
         STR_CASE(GL_INVALID_ENUM)
         STR_CASE(GL_INVALID_VALUE)
         STR_CASE(GL_INVALID_OPERATION)
         STR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION)
         STR_CASE(GL_OUT_OF_MEMORY)
         STR_CASE(GL_STACK_OVERFLOW)
         STR_CASE(GL_STACK_UNDERFLOW)
         STR_CASE(GL_CONTEXT_LOST) // GL_KHR_robustness
         default:
             return str_enum_(error);
     }
 }
 
 
 std::string GLBase::str_object_type_(GLenum object_type)
 {
     switch (object_type)
     {
         STR_CASE(GL_TEXTURE)
         STR_CASE(GL_BUFFER)
         STR_CASE(GL_SHADER)
         STR_CASE(GL_PROGRAM)
         STR_CASE(GL_PROGRAM_PIPELINE)
         STR_CASE(GL_FRAMEBUFFER)
         STR_CASE(GL_RENDERBUFFER)
         STR_CASE(GL_VERTEX_ARRAY)
         STR_CASE(GL_TRANSFORM_FEEDBACK)
         STR_CASE(GL_SAMPLER)
         STR_CASE(GL_QUERY)
         default:
             return str_enum_(object_type);
     }
 }
 
 
 std::string GLBase::str_glsl_(GLenum glsl)
 {
     switch (glsl)
     {
         STR_CASE(GL_FLOAT)
         STR_CASE(GL_FLOAT_VEC2)
         STR_CASE(GL_FLOAT_VEC3)
         STR_CASE(GL_FLOAT_VEC4)
         STR_CASE(GL_FLOAT_MAT2)
         STR_CASE(GL_FLOAT_MAT2x3)
         STR_CASE(GL_FLOAT_MAT2x4)
         STR_CASE(GL_FLOAT_MAT3x2)
         STR_CASE(GL_FLOAT_MAT3)
         STR_CASE(GL_FLOAT_MAT3x4)
         STR_CASE(GL_FLOAT_MAT4x2)
         STR_CASE(GL_FLOAT_MAT4x3)
         STR_CASE(GL_FLOAT_MAT4)
         STR_CASE(GL_INT)
         STR_CASE(GL_INT_VEC2)
         STR_CASE(GL_INT_VEC3)
         STR_CASE(GL_INT_VEC4)
         STR_CASE(GL_UNSIGNED_INT)
         STR_CASE(GL_UNSIGNED_INT_VEC2)
         STR_CASE(GL_UNSIGNED_INT_VEC3)
         STR_CASE(GL_UNSIGNED_INT_VEC4)
         STR_CASE(GL_DOUBLE)
         STR_CASE(GL_DOUBLE_VEC2)
         STR_CASE(GL_DOUBLE_VEC3)
         STR_CASE(GL_DOUBLE_VEC4)
         STR_CASE(GL_DOUBLE_MAT2)
         STR_CASE(GL_DOUBLE_MAT2x3)
         STR_CASE(GL_DOUBLE_MAT2x4)
         STR_CASE(GL_DOUBLE_MAT3x2)
         STR_CASE(GL_DOUBLE_MAT3)
         STR_CASE(GL_DOUBLE_MAT3x4)
         STR_CASE(GL_DOUBLE_MAT4x2)
         STR_CASE(GL_DOUBLE_MAT4x3)
         STR_CASE(GL_DOUBLE_MAT4)
         STR_CASE(GL_BOOL)
         STR_CASE(GL_BOOL_VEC2)
         STR_CASE(GL_BOOL_VEC3)
         STR_CASE(GL_BOOL_VEC4)
         STR_CASE(GL_SAMPLER_1D)
         STR_CASE(GL_SAMPLER_1D_SHADOW)
         STR_CASE(GL_SAMPLER_1D_ARRAY)
         STR_CASE(GL_SAMPLER_1D_ARRAY_SHADOW)
         STR_CASE(GL_SAMPLER_2D)
         STR_CASE(GL_SAMPLER_2D_SHADOW)
         STR_CASE(GL_SAMPLER_2D_ARRAY)
         STR_CASE(GL_SAMPLER_2D_ARRAY_SHADOW)
         STR_CASE(GL_SAMPLER_2D_RECT)
         STR_CASE(GL_SAMPLER_2D_RECT_SHADOW)
         STR_CASE(GL_SAMPLER_2D_MULTISAMPLE)
         STR_CASE(GL_SAMPLER_2D_MULTISAMPLE_ARRAY)
         STR_CASE(GL_SAMPLER_3D)
         STR_CASE(GL_SAMPLER_CUBE)
         STR_CASE(GL_SAMPLER_CUBE_SHADOW)
         STR_CASE(GL_SAMPLER_CUBE_MAP_ARRAY)
         STR_CASE(GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW)
         STR_CASE(GL_SAMPLER_BUFFER)
         STR_CASE(GL_INT_SAMPLER_1D)
         STR_CASE(GL_INT_SAMPLER_1D_ARRAY)
         STR_CASE(GL_INT_SAMPLER_2D)
         STR_CASE(GL_INT_SAMPLER_2D_ARRAY)
         STR_CASE(GL_INT_SAMPLER_2D_RECT)
         STR_CASE(GL_INT_SAMPLER_2D_MULTISAMPLE)
         STR_CASE(GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY)
         STR_CASE(GL_INT_SAMPLER_3D)
         STR_CASE(GL_INT_SAMPLER_CUBE)
         STR_CASE(GL_INT_SAMPLER_CUBE_MAP_ARRAY)
         STR_CASE(GL_INT_SAMPLER_BUFFER)
         STR_CASE(GL_UNSIGNED_INT_SAMPLER_1D)
         STR_CASE(GL_UNSIGNED_INT_SAMPLER_1D_ARRAY)
         STR_CASE(GL_UNSIGNED_INT_SAMPLER_2D)
         STR_CASE(GL_UNSIGNED_INT_SAMPLER_2D_ARRAY)
         STR_CASE(GL_UNSIGNED_INT_SAMPLER_2D_RECT)
         STR_CASE(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE)
         STR_CASE(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY)
         STR_CASE(GL_UNSIGNED_INT_SAMPLER_3D)
         STR_CASE(GL_UNSIGNED_INT_SAMPLER_CUBE)
         STR_CASE(GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY)
         STR_CASE(GL_UNSIGNED_INT_SAMPLER_BUFFER)
         STR_CASE(GL_IMAGE_1D)
         STR_CASE(GL_IMAGE_1D_ARRAY)
         STR_CASE(GL_IMAGE_2D)
         STR_CASE(GL_IMAGE_2D_ARRAY)
         STR_CASE(GL_IMAGE_2D_RECT)
         STR_CASE(GL_IMAGE_2D_MULTISAMPLE)
         STR_CASE(GL_IMAGE_2D_MULTISAMPLE_ARRAY)
         STR_CASE(GL_IMAGE_3D)
         STR_CASE(GL_IMAGE_CUBE)
         STR_CASE(GL_IMAGE_CUBE_MAP_ARRAY)
         STR_CASE(GL_IMAGE_BUFFER)
         STR_CASE(GL_INT_IMAGE_1D)
         STR_CASE(GL_INT_IMAGE_1D_ARRAY)
         STR_CASE(GL_INT_IMAGE_2D)
         STR_CASE(GL_INT_IMAGE_2D_ARRAY)
         STR_CASE(GL_INT_IMAGE_2D_RECT)
         STR_CASE(GL_INT_IMAGE_2D_MULTISAMPLE)
         STR_CASE(GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY)
         STR_CASE(GL_INT_IMAGE_3D)
         STR_CASE(GL_INT_IMAGE_CUBE)
         STR_CASE(GL_INT_IMAGE_CUBE_MAP_ARRAY)
         STR_CASE(GL_INT_IMAGE_BUFFER)
         STR_CASE(GL_UNSIGNED_INT_IMAGE_1D)
         STR_CASE(GL_UNSIGNED_INT_IMAGE_1D_ARRAY)
         STR_CASE(GL_UNSIGNED_INT_IMAGE_2D)
         STR_CASE(GL_UNSIGNED_INT_IMAGE_2D_ARRAY)
         STR_CASE(GL_UNSIGNED_INT_IMAGE_2D_RECT)
         STR_CASE(GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE)
         STR_CASE(GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY)
         STR_CASE(GL_UNSIGNED_INT_IMAGE_3D)
         STR_CASE(GL_UNSIGNED_INT_IMAGE_CUBE)
         STR_CASE(GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY)
         STR_CASE(GL_UNSIGNED_INT_IMAGE_BUFFER)
         // GL_ARB_gpu_shader_int64
         STR_CASE(GL_INT64_ARB)
         STR_CASE(GL_INT64_VEC2_ARB)
         STR_CASE(GL_INT64_VEC3_ARB)
         STR_CASE(GL_INT64_VEC4_ARB)
         STR_CASE(GL_UNSIGNED_INT64_ARB) // GL_ARB_bindless_texture
         STR_CASE(GL_UNSIGNED_INT64_VEC2_ARB)
         STR_CASE(GL_UNSIGNED_INT64_VEC3_ARB)
         STR_CASE(GL_UNSIGNED_INT64_VEC4_ARB)
         default:
             return str_enum_(glsl);
     }
 }
 
 
 std::string GLBase::str_format_(GLenum format)
 {
     switch (format)
     {
         STR_CASE(GL_RED)
         STR_CASE(GL_RED_INTEGER)
         STR_CASE(GL_GREEN)
         STR_CASE(GL_GREEN_INTEGER)
         STR_CASE(GL_BLUE)
         STR_CASE(GL_BLUE_INTEGER)
         STR_CASE(GL_ALPHA)
         STR_CASE(GL_ALPHA_INTEGER)
         STR_CASE(GL_RG)
         STR_CASE(GL_RG_INTEGER)
         STR_CASE(GL_RGB)
         STR_CASE(GL_RGB_INTEGER)
         STR_CASE(GL_RGBA)
         STR_CASE(GL_RGBA_INTEGER)
         STR_CASE(GL_BGR)
         STR_CASE(GL_BGR_INTEGER)
         STR_CASE(GL_BGRA)
         STR_CASE(GL_BGRA_INTEGER)
         STR_CASE(GL_ABGR_EXT) // GL_EXT_abgr
         STR_CASE(GL_DEPTH_STENCIL)
         STR_CASE(GL_DEPTH_COMPONENT)
         STR_CASE(GL_STENCIL_INDEX)
         default:
             return str_enum_(format);
     }
 }
 
 
 std::string GLBase::str_type_(GLenum type)
 {
     switch (type)
     {
         STR_CASE(GL_FLOAT)
         STR_CASE(GL_BYTE)
         STR_CASE(GL_SHORT)
         STR_CASE(GL_INT)
         STR_CASE(GL_UNSIGNED_BYTE)
         STR_CASE(GL_UNSIGNED_SHORT)
         STR_CASE(GL_UNSIGNED_INT)
         STR_CASE(GL_UNSIGNED_BYTE_3_3_2)
         STR_CASE(GL_UNSIGNED_BYTE_2_3_3_REV)
         STR_CASE(GL_UNSIGNED_SHORT_5_6_5)
         STR_CASE(GL_UNSIGNED_SHORT_5_6_5_REV)
         STR_CASE(GL_UNSIGNED_SHORT_4_4_4_4)
         STR_CASE(GL_UNSIGNED_SHORT_4_4_4_4_REV)
         STR_CASE(GL_UNSIGNED_SHORT_5_5_5_1)
         STR_CASE(GL_UNSIGNED_SHORT_1_5_5_5_REV)
         STR_CASE(GL_UNSIGNED_INT_8_8_8_8)
         STR_CASE(GL_UNSIGNED_INT_8_8_8_8_REV)
         STR_CASE(GL_UNSIGNED_INT_10_10_10_2)
         STR_CASE(GL_UNSIGNED_INT_2_10_10_10_REV)
         STR_CASE(GL_UNSIGNED_INT_10F_11F_11F_REV)
         STR_CASE(GL_UNSIGNED_INT_5_9_9_9_REV)
         STR_CASE(GL_UNSIGNED_INT_24_8)
         STR_CASE(GL_FLOAT_32_UNSIGNED_INT_24_8_REV)
         STR_CASE(GL_DOUBLE)
         STR_CASE(GL_HALF_FLOAT)
         STR_CASE(GL_FIXED)
         // GL_ARB_gpu_shader_int64
         STR_CASE(GL_INT64_ARB)
         STR_CASE(GL_UNSIGNED_INT64_ARB) // GL_ARB_bindless_texture
         default:
             return str_enum_(type);
     }
 }
 
 
 std::string GLBase::str_internal_format_(GLenum internal_format)
 {
     switch (internal_format)
     {
         STR_CASE(GL_RED)
         STR_CASE(GL_R8)
         STR_CASE(GL_R8I)
         STR_CASE(GL_R8UI)
         STR_CASE(GL_R16)
         STR_CASE(GL_R16I)
         STR_CASE(GL_R16UI)
         STR_CASE(GL_R32I)
         STR_CASE(GL_R32UI)
         STR_CASE(GL_R16F)
         STR_CASE(GL_R32F)
         STR_CASE(GL_RG)
         STR_CASE(GL_RG8)
         STR_CASE(GL_RG8I)
         STR_CASE(GL_RG8UI)
         STR_CASE(GL_RG16)
         STR_CASE(GL_RG16I)
         STR_CASE(GL_RG16UI)
         STR_CASE(GL_RG32I)
         STR_CASE(GL_RG32UI)
         STR_CASE(GL_RG16F)
         STR_CASE(GL_RG32F)
         STR_CASE(GL_RGB)
         STR_CASE(GL_RGB2_EXT) // GL_EXT_texture
         STR_CASE(GL_R3_G3_B2)
         STR_CASE(GL_RGB4)
         STR_CASE(GL_RGB5)
         STR_CASE(GL_RGB565)
         STR_CASE(GL_RGB8)
         STR_CASE(GL_RGB8I)
         STR_CASE(GL_RGB8UI)
         STR_CASE(GL_RGB10)
         STR_CASE(GL_RGB12)
         STR_CASE(GL_RGB16)
         STR_CASE(GL_RGB16I)
         STR_CASE(GL_RGB16UI)
         STR_CASE(GL_RGB32I)
         STR_CASE(GL_RGB32UI)
         STR_CASE(GL_RGB16F)
         STR_CASE(GL_RGB32F)
         STR_CASE(GL_R11F_G11F_B10F)
         STR_CASE(GL_RGB9_E5)
         STR_CASE(GL_RGBA)
         STR_CASE(GL_RGBA2)
         STR_CASE(GL_RGBA4)
         STR_CASE(GL_RGBA8)
         STR_CASE(GL_RGBA8I)
         STR_CASE(GL_RGBA8UI)
         STR_CASE(GL_RGBA12)
         STR_CASE(GL_RGBA16)
         STR_CASE(GL_RGBA16I)
         STR_CASE(GL_RGBA16UI)
         STR_CASE(GL_RGBA32I)
         STR_CASE(GL_RGBA32UI)
         STR_CASE(GL_RGB5_A1)
         STR_CASE(GL_RGB10_A2)
         STR_CASE(GL_RGB10_A2UI)
         STR_CASE(GL_RGBA16F)
         STR_CASE(GL_RGBA32F)
         STR_CASE(GL_R8_SNORM)
         STR_CASE(GL_R16_SNORM)
         STR_CASE(GL_RG8_SNORM)
         STR_CASE(GL_RG16_SNORM)
         STR_CASE(GL_RGB8_SNORM)
         STR_CASE(GL_RGB16_SNORM)
         STR_CASE(GL_RGBA8_SNORM)
         STR_CASE(GL_RGBA16_SNORM)
         STR_CASE(GL_SR8_EXT)  // GL_EXT_texture_sRGB_R8
         STR_CASE(GL_SRG8_EXT) // GL_EXT_texture_sRGB_RG8
         STR_CASE(GL_SRGB)
         STR_CASE(GL_SRGB8)
         STR_CASE(GL_SRGB_ALPHA)
         STR_CASE(GL_SRGB8_ALPHA8)
         STR_CASE(GL_DEPTH_STENCIL)
         STR_CASE(GL_DEPTH24_STENCIL8)
         STR_CASE(GL_DEPTH32F_STENCIL8)
         STR_CASE(GL_DEPTH_COMPONENT)
         STR_CASE(GL_DEPTH_COMPONENT16)
         STR_CASE(GL_DEPTH_COMPONENT24)
         STR_CASE(GL_DEPTH_COMPONENT32)
         STR_CASE(GL_DEPTH_COMPONENT32F)
         STR_CASE(GL_STENCIL_INDEX)
         STR_CASE(GL_STENCIL_INDEX1)
         STR_CASE(GL_STENCIL_INDEX4)
         STR_CASE(GL_STENCIL_INDEX8)
         STR_CASE(GL_STENCIL_INDEX16)
         STR_CASE(GL_COMPRESSED_RED)
         STR_CASE(GL_COMPRESSED_RG)
         STR_CASE(GL_COMPRESSED_RGB)
         STR_CASE(GL_COMPRESSED_RGBA)
         STR_CASE(GL_COMPRESSED_SRGB)
         STR_CASE(GL_COMPRESSED_SRGB_ALPHA)
         STR_CASE(GL_COMPRESSED_RGB_S3TC_DXT1_EXT)
         STR_CASE(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
         STR_CASE(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT)
         STR_CASE(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
         STR_CASE(GL_COMPRESSED_SRGB_S3TC_DXT1_EXT)
         STR_CASE(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT)
         STR_CASE(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT)
         STR_CASE(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT)
         STR_CASE(GL_COMPRESSED_RED_RGTC1)
         STR_CASE(GL_COMPRESSED_RG_RGTC2)
         STR_CASE(GL_COMPRESSED_SIGNED_RED_RGTC1)
         STR_CASE(GL_COMPRESSED_SIGNED_RG_RGTC2)
         STR_CASE(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT)
         STR_CASE(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT)
         STR_CASE(GL_COMPRESSED_RGBA_BPTC_UNORM)
         STR_CASE(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM)
         STR_CASE(GL_COMPRESSED_RGB8_ETC2)
         STR_CASE(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2)
         STR_CASE(GL_COMPRESSED_SRGB8_ETC2)
         STR_CASE(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2)
         STR_CASE(GL_COMPRESSED_RGBA8_ETC2_EAC)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC)
         STR_CASE(GL_COMPRESSED_R11_EAC)
         STR_CASE(GL_COMPRESSED_RG11_EAC)
         STR_CASE(GL_COMPRESSED_SIGNED_R11_EAC)
         STR_CASE(GL_COMPRESSED_SIGNED_RG11_EAC)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_4x4_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_5x4_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_5x5_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_6x5_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_6x6_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_8x5_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_8x6_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_8x8_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x5_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x6_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x8_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x10_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_12x10_KHR)
         STR_CASE(GL_COMPRESSED_RGBA_ASTC_12x12_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR)
         default:
             return str_enum_(internal_format);
     }
 }