src/gltexture.cpp
694871da
 #include <gltexture.hpp>
 
 #include <algorithm>
 #include <ostream>
 #include <utility>
 
 #include <globject.hpp>
 
 // NOLINTNEXTLINE
 #define STR_EXCEPTION GLObject<>::Exception
 #include <str.hpp>
 
 
 /// Special member functions
 
 
 GLTexture::GLTexture(
     // NOLINTNEXTLINE
     std::string object_label,
     GLenum      target,
     GLenum      binding,
     GLenum      internal_format,
     GLenum      wrap,
     GLenum      min_filter,
     GLenum      mag_filter
 )
 :
     GLObject(STR_JOIN(" ", it, it, {
         str_target_(target),
         str_internal_format_(internal_format),
         std::move(object_label)
     })),
     target_    {target},
     binding_   {binding},
     min_filter_{min_filter},
     unit_      {0}
 {
     // check_internal_format_(internal_format);
     unit(true);
     if (wrap)
     {
         glTexParameteri(target_, GL_TEXTURE_WRAP_S, (GLint)wrap);
         glTexParameteri(target_, GL_TEXTURE_WRAP_T, (GLint)wrap);
         glTexParameteri(target_, GL_TEXTURE_WRAP_R, (GLint)wrap);
     }
     if (min_filter)
         glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, (GLint)min_filter);
     if (mag_filter)
         glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, (GLint)mag_filter);
     if (min_filter_mipmap_())
         if (!supported({3, 0}, "GL_ARB_framebuffer_object"))
             glTexParameteri(target_, GL_GENERATE_MIPMAP, GL_TRUE);
     if (target_ == GL_TEXTURE_2D)
         // NOLINTNEXTLINE
         if (supported({4, 6}, "GL_ARB_texture_filter_anisotropic"))
             // TODO(rcrnstn): Remove the `_EXT` suffix when the headers are
             // updated.
             glTexParameterf(target_, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy_);
 }
 
 
 /// Core
 
 
 GLOBJECT_THREAD(GLTexture::anisotropy_, {1.0F})
 
 
 GLint GLTexture::unit(bool force_active) const
 try
 {
     auto static const unit_count = (GLuint)integer(
         GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
     );
     auto static units       = std::vector<GLuint>(unit_count);
     auto static textures    = std::vector<GLuint>(unit_count);
     auto static targets     = std::vector<GLenum>(unit_count);
     auto static unit_next   = GLuint{1};
     auto static unit_active = GLuint{0};
 
     if (!unit_ || textures[unit_] != object())
     {
         if (unit_next < unit_count)
             units.back() = unit_ = unit_next++;
         else
             unit_ = units.back();
         std::rotate(units.begin(), units.end() - 1, units.end());
     }
 
     if (force_active || unit_active != unit_)
     {
         glActiveTexture(GL_TEXTURE0 + unit_);
         unit_active = unit_;
     }
 
     if (textures[unit_] != object())
     {
         GLOBJECT_DEBUG_IF(1)
             check_unit_active_();
         GLOBJECT_DEBUG_IF(1)
         {
             glBindTexture(targets[unit_], 0);
             targets[unit_] = target_;
         }
         glBindTexture(target_, object());
         textures[unit_] = object();
     }
     GLOBJECT_DEBUG_IF(1)
         check_unit_texture_();
 
     return (GLint)unit_;
 }
 catch (...)
 {
     fail_action_("allocate texture unit for");
 }
 
 
 bool GLTexture::min_filter_mipmap_() const
 {
     switch (min_filter_)
     {
         case GL_NEAREST_MIPMAP_NEAREST:
         case GL_NEAREST_MIPMAP_LINEAR:
         case GL_LINEAR_MIPMAP_NEAREST:
         case GL_LINEAR_MIPMAP_LINEAR:
             return true;
         default:
             return false;
     }
 }
 
 
 /// Path
 
 
 GLOBJECT_THREAD(GLTexture::prefix_, {"assets/textures"})
 
 
 /// Check
 
 
 void GLTexture::check_unit_active_() const
 {
     auto unit_active = (GLuint)integer(GL_ACTIVE_TEXTURE) - GL_TEXTURE0;
     if (unit_active != unit_)
         STR_THROW(
             "Expected active unit " << unit_        << ", " <<
             "got "                  << unit_active  << "."
         );
 }
 
 
 void GLTexture::check_unit_texture_() const
 {
     auto unit_texture = (GLuint)integer(binding_);
     if (unit_texture != object())
         STR_THROW(
             "Expected unit "  << unit_        << " "   <<
             "to be bound to " << debug_name() << ", "  <<
             "got "            << unit_texture << "."
         );
 }
 
 
 void GLTexture::check_data_size_(std::size_t data_size) const
 {
     if (data_size != data_size_())
         STR_THROW(
             "Expected data size " << data_size_() << ", " <<
             "got "                << data_size    << "."
         );
 }
 
 
 /// String
 
 
 std::string GLTexture::str_target_(GLenum target)
 {
     switch (target)
     {
         STR_CASE(GL_TEXTURE_1D)
         STR_CASE(GL_TEXTURE_2D)
         STR_CASE(GL_TEXTURE_3D)
         STR_CASE(GL_TEXTURE_1D_ARRAY)
         STR_CASE(GL_TEXTURE_2D_ARRAY)
         STR_CASE(GL_TEXTURE_RECTANGLE)
         STR_CASE(GL_TEXTURE_CUBE_MAP)
         STR_CASE(GL_TEXTURE_CUBE_MAP_ARRAY)
         STR_CASE(GL_TEXTURE_BUFFER)
         STR_CASE(GL_TEXTURE_2D_MULTISAMPLE)
         STR_CASE(GL_TEXTURE_2D_MULTISAMPLE_ARRAY)
         default:
             return str_enum_(target);
     }
 }