src/globject.cpp
4f14c1c2
 #include <globject.hpp>
 
 #include <algorithm>
 #include <array>
 #include <cerrno>
 #include <cstdio>
 #include <cstring>
 #include <fstream>
 #include <iostream>
 #include <iterator>
 #include <sstream>
 #include <string>
 #include <unordered_set>
 
 // NOLINTNEXTLINE
 #define STR_EXCEPTION GLObject::Exception
 #include <str.hpp>
 
 
 /// Special member functions
 
 
 GLObject::GLObject(
     GLGenObjects    gl_gen_objects,
     GLDeleteObjects gl_delete_objects,
     GLenum          object_type,
     // cppcheck-suppress passedByValue
     std::string     object_label
 )
 :
     gl_delete_objects_{gl_delete_objects},
     object_type_      {object_type},
     object_label_     {std::move(object_label)},
     object_           {0}
 {
     try
     {
         if (gl_gen_objects)
             gl_gen_objects(1, &object_);
         else
             object_ = object_pseudo_;
         check_error_(glGetError());
         GLOBJECT_DEBUG_IF(1)
         {
             debug_label_();
             debug_objects_push_back_(this);
             debug_callback()("Create " + debug_name());
         }
     }
     catch (...)
     {
         if (gl_delete_objects_)
             gl_delete_objects_(1, &object_);
         fail_action_("create");
     }
 }
 
 
 GLObject::GLObject(GLObject && other) noexcept
 :
     gl_delete_objects_{other.gl_delete_objects_},
     object_type_      {other.object_type_},
     object_label_     {std::move(other.object_label_)},
     object_           {other.object_}
 {
     other.object_ = 0;
     GLOBJECT_DEBUG_IF(1)
     {
         debug_objects_erase_(&other);
         debug_objects_push_back_(this);
         debug_callback()("Move " + debug_name());
     }
 }
 
 
 GLObject::~GLObject()
 {
     if (gl_delete_objects_)
         gl_delete_objects_(1, &object_);
     GLOBJECT_DEBUG_IF(1)
     {
         debug_objects_erase_(this);
         debug_callback()("Destroy " + debug_name());
     }
 }
 
 
 /// Core
 
 
 bool GLObject::supported(
     Version version_min,
     std::string const & extension
 )
 {
     auto static version    = Version{0, 0};
     auto static extensions = std::unordered_set<std::string>{};
     if (extension.size() && extension.rfind("GL_", 0) == extension.npos)
         STR_THROW("Failed to parse extension \"" << extension << "\".");
     if (version == Version{0, 0})
     {
         auto const * version_str = (char const *)glGetString(GL_VERSION);
         if (!version_str)
             return false;
         // NOLINTNEXTLINE
         if (2 != std::sscanf(version_str, "%d.%d", &version[0], &version[1]))
             STR_THROW("Failed to parse version \"" << version_str << "\".");
         if (version[0] >= 3)
         {
             auto extension_count = get_integer(GL_NUM_EXTENSIONS);
             for (auto i = 0; i < extension_count; ++i)
                 extensions.insert(
                     (char const *)glGetStringi(GL_EXTENSIONS, (GLuint)i)
                 );
         }
         else
         {
             auto istream = std::istringstream(
                 (char const *)glGetString(GL_EXTENSIONS)
             );
             std::copy(
                 std::istream_iterator<std::string>(istream),
                 std::istream_iterator<std::string>(),
                 std::inserter(extensions, extensions.end())
             );
         }
     }
     if
     (
         (version_min != Version{0, 0}) &&
         (
             (version[0] > version_min[0]) ||
             (
                 version[0] == version_min[0] &&
                 version[1] >= version_min[1]
             )
         )
     )
         return true;
     if
     (
         extension.size() &&
         extensions.find(extension) != extensions.end()
     )
         return true;
     return false;
 }
 
 
 GLint GLObject::get_integer(GLenum name)
 try
 {
     auto value = GLint{};
     glGetIntegerv(name, &value);
     GLOBJECT_DEBUG_IF(1)
         check_error_(glGetError());
     return value;
 }
 catch (...)
 {
     STR_THROW_NESTED("Failed to get integer " << str_enum_(name) << ":");
 }
 
 
 /// Path
 
 
 GLObject::Path GLObject::path_prefix_(
     Path const & path,
     Path const & prefix
 )
 {
     if (prefix.empty() || path[0] == '/')
         return path;
     return STR(prefix << "/" << path);
 }
 
 
 /// TGA
 
 
 GLObject::TGA::TGA(Size size, Data data)
 try
 :
     header_{size},
     data_{std::move(data)}
 {
     check_data_();
 }
 catch (...)
 {
     STR_THROW_NESTED("Failed to create TGA " << str_size_(size) << ":");
 }
 
 
 GLObject::TGA GLObject::TGA::read(Path const & path)
 try
 {
     auto tga = TGA({}, {});
     auto istream = std::ifstream(path, std::ios::binary);
     istream.read(
         (char *)         tga.header_.data(),
         (std::streamsize)tga.header_.size()
     );
     tga.check_header_();
     tga.data_.resize((std::size_t)(4 * tga.size()[0] * tga.size()[1]));
     istream.read(
         (char *)         tga.data_.data(),
         (std::streamsize)tga.data_.size()
     );
     istream.close();
     if (!istream)
         // NOLINTNEXTLINE
         STR_THROW(std::strerror(errno));
     return tga;
 }
 catch (...)
 {
     STR_THROW_NESTED("Failed to read TGA " << str_path_(path) << ":");
 }
 
 
 void GLObject::TGA::write(Path const & path) const
 try
 {
     check_data_();
     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());
     ostream.close();
     if (!ostream)
         // NOLINTNEXTLINE
         STR_THROW(std::strerror(errno));
 }
 catch (...)
 {
     STR_THROW_NESTED("Failed to write TGA " << str_path_(path) << ":");
 }
 
 
 GLObject::TGA::Data const & GLObject::TGA::data()
 {
     return data_;
 }
 
 
 GLObject::TGA::Size GLObject::TGA::size() const
 {
     return {
         (GLsizei)(header_[12]) << 0 | // NOLINT
         (GLsizei)(header_[13]) << 8,  // NOLINT
         (GLsizei)(header_[14]) << 0 | // NOLINT
         (GLsizei)(header_[15]) << 8,  // NOLINT
     };
 }
 
 
 GLObject::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, 8,                              // NOLINT
     }
 {
 }
 
 
 std::string GLObject::TGA::str_size_(Size size)
 {
     return STR("{" << STR_JOIN(", ", it, it, size) << "}");
 }
 
 
 void GLObject::TGA::check_header_() const
 {
     auto header = Header(size());
     if (header_ != header)
         STR_THROW(
             "Expected TGA header"                             << " "  <<
             "[" << STR_JOIN(", ", byte, byte, header)  << "]" << ", " <<
             "got"                                             << " "  <<
             "[" << STR_JOIN(", ", byte, byte, header_) << "]" << "."
         );
 }
 
 
 void GLObject::TGA::check_data_() const
 {
     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
 
 
 GLOBJECT_THREAD(GLObject::debug_,          {1})
 GLOBJECT_THREAD(GLObject::debug_objects_,  {})
 GLOBJECT_THREAD(GLObject::debug_callback_, {[](std::string const & debug)
 {
     std::cerr << debug << std::endl;
 }})
 
 
 std::string GLObject::debug_name() const
 {
     return STR_JOIN(" ", it, it, {
         object_type_  == 0              ? "<GLObject>" :
                                           str_object_type_(object_type_),
         object_label_,
         object_       == 0              ? "<INVALID>" :
         object_       == object_pseudo_ ? "<PSEUDO>"  :
                                           STR(object_),
     });
 }
 
 
 void GLObject::debug_label_() const
 {
     if
     (
         object_type_ &&
         object_label_.size() &&
         object_
     )
     {
         if (supported({4, 3}, "GL_KHR_debug"))
         {
             auto static object_label_length_max = get_integer(
                 GL_MAX_LABEL_LENGTH
             );
             auto object_label_length = std::min(
                 (GLsizei)object_label_length_max,
                 (GLsizei)object_label_.length()
             );
             glObjectLabel(
                 object_type_,
                 object_,
                 object_label_length,
                 object_label_.c_str()
             );
         }
     }
 }
 
 
 std::string static debug_objects_data_(
     std::string (GLObject::*debug_data)() const
 )
 {
     auto const & objects = GLObject::debug_objects();
     auto header = STR("GLObjects: " << objects.size());
     auto data   = STR_JOIN(
         "\n",
         object,
         "  " << (object->*debug_data)(),
         objects
     );
     return STR_JOIN("\n", it, it, {header, data});
 }
 
 
 std::string GLObject::debug_objects_name()
 {
     return debug_objects_data_(&GLObject::debug_name);
 }
 
 
 std::string GLObject::debug_objects_info()
 {
     return debug_objects_data_(&GLObject::debug_info);
 }
 
 
 std::string GLObject::debug_info() const
 {
     return debug_name();
 }
 
 
 void GLObject::debug_objects_push_back_(GLObject * debug_object)
 {
     debug_objects_.push_back(debug_object);
 }
 
 
 void GLObject::debug_objects_erase_(GLObject * debug_object)
 {
     debug_objects_.erase(
         std::remove(
             debug_objects_.begin(),
             debug_objects_.end(),
             debug_object
         ),
         debug_objects_.end()
     );
 }
 
 
 /// Check
 
 
 void GLObject::check_path_(std::string const & path)
 {
     if (!path.size())
         STR_THROW(
             "Expected " << "non-empty path"     << ", " <<
             "got "      << "\"" << path << "\"" << "."
         );
 }
 
 
 void GLObject::check_error_(GLenum error)
 {
     if (error != GL_NO_ERROR)
         STR_THROW(
             "Expected " << "no error"        << ", " <<
             "got "      << str_error_(error) << "."
         );
 }
 
 
 void GLObject::check_supported_(
     Version version_min,
     std::string const & extension
 )
 {
     if (!supported(version_min, extension))
         STR_THROW(
             "Expected GL version " <<
             STR_JOIN(".", it, it, version_min) <<
             (extension.size() ? STR(" or extension " << extension) : "") << " "
             "got " << glGetString(GL_VERSION)                            << "."
         );
 }
 
 
 void GLObject::check_format_(
     GLenum format,
     GLenum format_expected
 )
 {
     if (format != format_expected)
         STR_THROW(
             "Expected format " << str_format_(format_expected) << ", " <<
             "got "             << str_format_(format)          << "."
         );
 }
 
 
 void GLObject::check_type_(
     GLenum type,
     GLenum type_expected
 )
 {
     if (type != type_expected)
         STR_THROW(
             "Expected type " << str_type_(type_expected) << ", " <<
             "got "           << str_type_(type)          << "."
         );
 }
 
 
 void GLObject::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:
         case GL_R3_G3_B2:
             check_supported_({1, 0});
             return;
         case GL_RGB4:
         case GL_RGB5:
         case GL_RGB8:
         case GL_RGB10:
         case GL_RGB12:
         case GL_RGB16:
         case GL_RGBA2:
         case GL_RGBA4:
         case GL_RGBA8:
         case GL_RGBA12:
         case GL_RGBA16:
         case GL_RGB5_A1:
         case GL_RGB10_A2:
             check_supported_({1, 1}, "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;
1b833e48
         // TODO(rcrnstn): Update GLEW headers.
         // case GL_SR8_EXT:
         //     check_supported_({}, "GL_EXT_texture_sRGB_R8");
         //     return;
         // case GL_SRG8_EXT:
         //     check_supported_({}, "GL_EXT_texture_sRGB_RG8");
         //     return;
4f14c1c2
         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_SIGNED_RED_RGTC1:
         case GL_COMPRESSED_RG_RGTC2:
         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_RGB16I:
         case GL_RGB32I:
         case GL_RGB8UI:
         case GL_RGB16UI:
         case GL_RGB32UI:
         case GL_RGBA8I:
         case GL_RGBA16I:
         case GL_RGBA32I:
         case GL_RGBA8UI:
         case GL_RGBA16UI:
         case GL_RGBA32UI:
             check_supported_({3, 0}, "GL_EXT_texture_integer");
             return;
         case GL_R16F:
         case GL_R32F:
         case GL_RG16F:
         case GL_RG32F:
         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_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:
             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_INDEX8:
             check_supported_({3, 0}, "GL_ARB_texture_stencil8");
             return;
         case GL_STENCIL_INDEX1:
         case GL_STENCIL_INDEX4:
         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_SIGNED_FLOAT:
         case GL_COMPRESSED_RGB_BPTC_UNSIGNED_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_R11_EAC:
         case GL_COMPRESSED_RG11_EAC:
         case GL_COMPRESSED_SIGNED_R11_EAC:
         case GL_COMPRESSED_SIGNED_RG11_EAC:
         case GL_COMPRESSED_RGB8_ETC2:
         case GL_COMPRESSED_SRGB8_ETC2:
         case GL_COMPRESSED_RGBA8_ETC2_EAC:
         case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
         case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
         case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
             check_supported_({4, 2}, "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_({4, 2}, "GL_KHR_texture_compression_astc_ldr");
             return;
         default:
             STR_THROW(
                 "Expected " << "supported internal format"           << ", "
                 "got "      << str_internal_format_(internal_format) << "."
             );
     }
 }
 
 
 /// Fail
 
 
 void GLObject::fail_action_(std::string const & action) const
 {
     STR_THROW_NESTED("Failed to " << action << " " << debug_name() << ":");
 }
 
 
 /// String
 
 
 std::string GLObject::str_path_(Path const & path)
 {
     return STR("\"" << path << "\"");
 }
 
 
 std::string GLObject::str_paths_(Paths const & paths)
 {
     return STR_JOIN(", ", path, str_path_(path), paths);
 }
 
 
 std::string GLObject::str_enum_(GLenum name)
 {
     return STR(std::hex << std::showbase << name);
 }
 
 
 std::string GLObject::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_UNDERFLOW)
         STR_CASE(GL_STACK_OVERFLOW)
         default:
             return str_enum_(error);
     }
 }
 
 
 std::string GLObject::str_object_type_(GLenum object_type)
 {
     switch (object_type)
     {
         STR_CASE(GL_BUFFER)
         STR_CASE(GL_SHADER)
         STR_CASE(GL_PROGRAM)
         STR_CASE(GL_VERTEX_ARRAY)
         STR_CASE(GL_QUERY)
         STR_CASE(GL_PROGRAM_PIPELINE)
         STR_CASE(GL_TRANSFORM_FEEDBACK)
         STR_CASE(GL_SAMPLER)
         STR_CASE(GL_TEXTURE)
         STR_CASE(GL_RENDERBUFFER)
         STR_CASE(GL_FRAMEBUFFER)
         default:
             return str_enum_(object_type);
     }
 }
 
 
 std::string GLObject::str_format_(GLenum format)
 {
     switch (format)
     {
         STR_CASE(GL_RED)
         STR_CASE(GL_R)
         STR_CASE(GL_RG)
         STR_CASE(GL_RGB)
         STR_CASE(GL_RGBA)
         STR_CASE(GL_BGR)
         STR_CASE(GL_BGRA)
         STR_CASE(GL_DEPTH_COMPONENT)
         STR_CASE(GL_STENCIL_INDEX)
         default:
             return str_enum_(format);
     }
 }
 
 
 std::string GLObject::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)
         default:
             return str_enum_(type);
     }
 }
 
 
 std::string GLObject::str_internal_format_(GLenum internal_format)
 {
     switch (internal_format)
     {
         STR_CASE(GL_RED)
         STR_CASE(GL_RGB)
         STR_CASE(GL_RGBA)
         STR_CASE(GL_DEPTH_COMPONENT)
         STR_CASE(GL_STENCIL_INDEX)
         STR_CASE(GL_R3_G3_B2)
         STR_CASE(GL_RGB4)
         STR_CASE(GL_RGB5)
         STR_CASE(GL_RGB8)
         STR_CASE(GL_RGB10)
         STR_CASE(GL_RGB12)
         STR_CASE(GL_RGB16)
         STR_CASE(GL_RGBA2)
         STR_CASE(GL_RGBA4)
         STR_CASE(GL_RGBA8)
         STR_CASE(GL_RGBA12)
         STR_CASE(GL_RGBA16)
         STR_CASE(GL_RGB5_A1)
         STR_CASE(GL_RGB10_A2)
         STR_CASE(GL_COMPRESSED_RGB)
         STR_CASE(GL_COMPRESSED_RGBA)
         STR_CASE(GL_DEPTH_COMPONENT16)
         STR_CASE(GL_DEPTH_COMPONENT24)
         STR_CASE(GL_DEPTH_COMPONENT32)
         STR_CASE(GL_SRGB)
         STR_CASE(GL_SRGB8)
         STR_CASE(GL_SRGB_ALPHA)
         STR_CASE(GL_SRGB8_ALPHA8)
         STR_CASE(GL_COMPRESSED_SRGB)
         STR_CASE(GL_COMPRESSED_SRGB_ALPHA)
1b833e48
         // TODO(rcrnstn): Update GLEW headers.
         // STR_CASE(GL_SR8_EXT)
         // STR_CASE(GL_SRG8_EXT)
4f14c1c2
         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)
         STR_CASE(GL_COMPRESSED_RG)
         STR_CASE(GL_COMPRESSED_RED_RGTC1)
         STR_CASE(GL_COMPRESSED_SIGNED_RED_RGTC1)
         STR_CASE(GL_COMPRESSED_RG_RGTC2)
         STR_CASE(GL_COMPRESSED_SIGNED_RG_RGTC2)
         STR_CASE(GL_RGB16F)
         STR_CASE(GL_RGB32F)
         STR_CASE(GL_RGBA16F)
         STR_CASE(GL_RGBA32F)
         STR_CASE(GL_RGB8I)
         STR_CASE(GL_RGB16I)
         STR_CASE(GL_RGB32I)
         STR_CASE(GL_RGB8UI)
         STR_CASE(GL_RGB16UI)
         STR_CASE(GL_RGB32UI)
         STR_CASE(GL_RGBA8I)
         STR_CASE(GL_RGBA16I)
         STR_CASE(GL_RGBA32I)
         STR_CASE(GL_RGBA8UI)
         STR_CASE(GL_RGBA16UI)
         STR_CASE(GL_RGBA32UI)
         STR_CASE(GL_R16F)
         STR_CASE(GL_R32F)
         STR_CASE(GL_RG16F)
         STR_CASE(GL_RG32F)
         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_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_R11F_G11F_B10F)
         STR_CASE(GL_RGB9_E5)
         STR_CASE(GL_DEPTH_STENCIL)
         STR_CASE(GL_DEPTH24_STENCIL8)
         STR_CASE(GL_DEPTH32F_STENCIL8)
         STR_CASE(GL_DEPTH_COMPONENT32F)
         STR_CASE(GL_STENCIL_INDEX8)
         STR_CASE(GL_STENCIL_INDEX1)
         STR_CASE(GL_STENCIL_INDEX4)
         STR_CASE(GL_STENCIL_INDEX16)
         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_RGB10_A2UI)
         STR_CASE(GL_RGB565)
         STR_CASE(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT)
         STR_CASE(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT)
         STR_CASE(GL_COMPRESSED_RGBA_BPTC_UNORM)
         STR_CASE(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM)
         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_RGB8_ETC2)
         STR_CASE(GL_COMPRESSED_SRGB8_ETC2)
         STR_CASE(GL_COMPRESSED_RGBA8_ETC2_EAC)
         STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC)
         STR_CASE(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2)
         STR_CASE(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2)
         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);
     }
 }