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;
|
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)
|