583abb86 |
/// Includes
#include <glbackend.hpp>
#include <algorithm>
#include <cstddef>
#include <exception>
#include <iomanip>
#include <regex>
#include <sstream>
#include <string>
#include <glbase.hpp>
// NOLINTNEXTLINE
#define STR_EXCEPTION GLBase::Exception
#include <str.hpp>
/// Special member functions
GLBackend::GLBackend()
:
callback_update_{},
callback_render_{},
scroll_{},
position_{},
move_{},
size_{},
callback_key_{},
callback_button_{},
callback_scroll_{},
callback_position_{},
callback_move_{},
callback_size_{}
{
}
void GLBackend::init_()
{
#ifdef __GLEW_H__
if (auto error = glewInit())
STR_THROW(
"Failed to initialize GLEW:" << "\n" <<
glewGetErrorString(error)
);
#endif
if (debug() >= 1)
{
if (supported({4, 3}, "GL_KHR_debug"))
{
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageControl(
GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE,
0, nullptr,
GL_TRUE
);
glDebugMessageControl(
GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION,
0, nullptr,
GL_FALSE
);
glDebugMessageCallback(debug_gl_message_callback_, nullptr);
}
}
}
/// Render loop
void GLBackend::run(float dt_fixed)
{
auto t = time(0);
running(true);
while (events(), running())
{
if (callback_update_)
{
auto t_next = time();
auto dt = t_next - t;
if (dt_fixed != 0.0F)
dt = dt_fixed;
while (t + dt <= t_next)
{
callback_update_(t, dt, !(t + 2 * dt <= t_next));
t += dt;
}
}
if (callback_render_)
callback_render_();
swap();
}
}
/// Path
GLBASE_GLOBAL(GLBackend::prefix_, {})
/// TGA
void GLBackend::tga_write(
Path const & path
) const
{
tga_().write(path_prefix_(path, prefix()));
}
bool GLBackend::tga_compare(
Path const & path,
bool write_on_failed_read
) const
{
auto path_prefix = path_prefix_(path, prefix());
auto tga = tga_();
try
{
auto tga_read = TGA_::read(path_prefix);
return tga_read.data() == tga.data();
}
catch (std::exception const & exception)
{
if (!write_on_failed_read)
throw;
if (debug() >= 1)
debug_callback()(exception.what());
debug_callback()(STR("Writing TGA \"" << path_prefix << "\"."));
tga.write(path_prefix);
return false;
}
}
GLBackend::TGA_ GLBackend::tga_() const
{
glFlush();
auto size = this->size();
auto data = std::vector<GLubyte>((4 * (size_t)size[0] * (size_t)size[1]));
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glReadPixels(
0, 0,
size[0], size[1],
GL_BGRA, GL_UNSIGNED_BYTE,
data.data()
);
return TGA_(size, std::move(data));
}
/// Debug
std::string GLBackend::debug_info() const
{
auto ostream = std::ostringstream{};
#ifdef __GLEW_H__
// NOLINTNEXTLINE
#define GLBACKEND_INFO_GLEW_(NAME) \
{#NAME, (char const *)glewGetString(GLEW_##NAME)}
debug_info_(ostream, "GLEW", {
GLBACKEND_INFO_GLEW_(VERSION),
});
#endif
// NOLINTNEXTLINE
#define GLBACKEND_INFO_GL_(NAME) \
{#NAME, (char const *)glGetString(GL_##NAME)}
// NOLINTNEXTLINE
#define GLBACKEND_INFO_GL_FLAG_(NAME) \
{ \
#NAME, \
(flags & (GLuint)GL_CONTEXT_FLAG_##NAME##_BIT) \
? "TRUE" \
: "FALSE" \
}
// NOLINTNEXTLINE
#define GLBACKEND_INFO_GL_INTEGER_(NAME) \
{#NAME, std::to_string(integer(GL_##NAME)) }
auto const flags = (GLuint)integer(GL_CONTEXT_FLAGS);
debug_info_(ostream, "OpenGL", {
GLBACKEND_INFO_GL_(VENDOR),
GLBACKEND_INFO_GL_(RENDERER),
GLBACKEND_INFO_GL_(VERSION),
GLBACKEND_INFO_GL_(SHADING_LANGUAGE_VERSION),
GLBACKEND_INFO_GL_FLAG_(FORWARD_COMPATIBLE),
GLBACKEND_INFO_GL_FLAG_(DEBUG),
GLBACKEND_INFO_GL_FLAG_(ROBUST_ACCESS),
GLBACKEND_INFO_GL_FLAG_(NO_ERROR),
GLBACKEND_INFO_GL_INTEGER_(SAMPLE_BUFFERS),
GLBACKEND_INFO_GL_INTEGER_(SAMPLES),
});
return ostream.str();
}
void GLBackend::debug_info_(
std::ostream & ostream,
std::string const & category,
std::vector<std::pair<std::string, std::string>> const & values,
char fill
)
{
auto length_max = int{0};
for (auto const & value : values)
length_max = std::max(length_max, (int)value.first.length());
ostream << category << "\n";
for (auto const & value : values)
ostream
<< " "
<< std::left << std::setw(length_max + 1 + 2) << std::setfill(fill)
<< (value.first + " ")
<< (" " + value.second)
<< "\n";
}
void GLAPIENTRY GLBackend::debug_gl_message_callback_(
GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
GLchar const * message,
void const * user_param
)
{
(void)length;
(void)user_param;
auto ostream = std::ostringstream{};
ostream << std::hex << std::showbase;
// https://www.khronos.org/opengl/wiki/Debug_Output#Message_Components
ostream << "GL debug message ";
// NOLINTNEXTLINE
#define GLBACKEND_CALLBACK_CASE_(CATEGORY, VALUE) \
case GL_DEBUG_##CATEGORY##_##VALUE: \
ostream << #VALUE; \
break;
switch(source)
{
GLBACKEND_CALLBACK_CASE_(SOURCE, API)
GLBACKEND_CALLBACK_CASE_(SOURCE, WINDOW_SYSTEM)
GLBACKEND_CALLBACK_CASE_(SOURCE, SHADER_COMPILER)
GLBACKEND_CALLBACK_CASE_(SOURCE, THIRD_PARTY)
GLBACKEND_CALLBACK_CASE_(SOURCE, APPLICATION)
GLBACKEND_CALLBACK_CASE_(SOURCE, OTHER)
default:
ostream << source;
}
ostream << " ";
switch(type)
{
GLBACKEND_CALLBACK_CASE_(TYPE, ERROR)
GLBACKEND_CALLBACK_CASE_(TYPE, DEPRECATED_BEHAVIOR)
GLBACKEND_CALLBACK_CASE_(TYPE, UNDEFINED_BEHAVIOR)
GLBACKEND_CALLBACK_CASE_(TYPE, PORTABILITY)
GLBACKEND_CALLBACK_CASE_(TYPE, PERFORMANCE)
GLBACKEND_CALLBACK_CASE_(TYPE, MARKER)
GLBACKEND_CALLBACK_CASE_(TYPE, PUSH_GROUP)
GLBACKEND_CALLBACK_CASE_(TYPE, POP_GROUP)
GLBACKEND_CALLBACK_CASE_(TYPE, OTHER)
default:
ostream << type;
}
ostream << " ";
switch(severity)
{
GLBACKEND_CALLBACK_CASE_(SEVERITY, HIGH)
GLBACKEND_CALLBACK_CASE_(SEVERITY, MEDIUM)
GLBACKEND_CALLBACK_CASE_(SEVERITY, LOW)
GLBACKEND_CALLBACK_CASE_(SEVERITY, NOTIFICATION)
default:
ostream << severity;
}
ostream << " ";
ostream << id;
ostream << ":\n";
ostream << debug_gl_message_(message);
debug_callback()(ostream.str());
}
std::string GLBackend::debug_gl_message_(std::string message)
{
message.erase(message.find_last_not_of(" \n") + 1);
auto static const file_line = std::regex{R""(^"([^"]+)"(:[0-9]+.*))""};
auto match = std::smatch{};
if (std::regex_match(message, match, file_line))
message = match.str(1) + match.str(2);
return message;
}
|