psemek/libs/gfx/source/program.cpp

420 lines
9.8 KiB
C++

#include <psemek/gfx/program.hpp>
#include <psemek/util/to_string.hpp>
#include <psemek/util/at_scope_exit.hpp>
#include <psemek/util/exception.hpp>
#include <memory>
#include <vector>
#include <sstream>
namespace psemek::gfx
{
void program::uniform_proxy::operator = (bool b)
{
gl::Uniform1i(location_, b ? 1 : 0);
}
void program::uniform_proxy::operator = (int i)
{
gl::Uniform1i(location_, i);
}
void program::uniform_proxy::operator = (unsigned int u)
{
gl::Uniform1ui(location_, u);
}
void program::uniform_proxy::operator = (float f)
{
gl::Uniform1f(location_, f);
}
void program::uniform_proxy::operator = (math::vector<int, 1> const & v)
{
gl::Uniform1i(location_, v[0]);
}
void program::uniform_proxy::operator = (math::vector<int, 2> const & v)
{
gl::Uniform2i(location_, v[0], v[1]);
}
void program::uniform_proxy::operator = (math::vector<int, 3> const & v)
{
gl::Uniform3i(location_, v[0], v[1], v[2]);
}
void program::uniform_proxy::operator = (math::vector<int, 4> const & v)
{
gl::Uniform4i(location_, v[0], v[1], v[2], v[3]);
}
void program::uniform_proxy::operator = (math::vector<unsigned int, 1> const & v)
{
gl::Uniform1ui(location_, v[0]);
}
void program::uniform_proxy::operator = (math::vector<unsigned int, 2> const & v)
{
gl::Uniform2ui(location_, v[0], v[1]);
}
void program::uniform_proxy::operator = (math::vector<unsigned int, 3> const & v)
{
gl::Uniform3ui(location_, v[0], v[1], v[2]);
}
void program::uniform_proxy::operator = (math::vector<unsigned int, 4> const & v)
{
gl::Uniform4ui(location_, v[0], v[1], v[2], v[3]);
}
void program::uniform_proxy::operator = (math::vector<float, 1> const & v)
{
gl::Uniform1f(location_, v[0]);
}
void program::uniform_proxy::operator = (math::vector<float, 2> const & v)
{
gl::Uniform2f(location_, v[0], v[1]);
}
void program::uniform_proxy::operator = (math::vector<float, 3> const & v)
{
gl::Uniform3f(location_, v[0], v[1], v[2]);
}
void program::uniform_proxy::operator = (math::vector<float, 4> const & v)
{
gl::Uniform4f(location_, v[0], v[1], v[2], v[3]);
}
void program::uniform_proxy::operator = (math::point<int, 1> const & v)
{
gl::Uniform1i(location_, v[0]);
}
void program::uniform_proxy::operator = (math::point<int, 2> const & v)
{
gl::Uniform2i(location_, v[0], v[1]);
}
void program::uniform_proxy::operator = (math::point<int, 3> const & v)
{
gl::Uniform3i(location_, v[0], v[1], v[2]);
}
void program::uniform_proxy::operator = (math::point<int, 4> const & v)
{
gl::Uniform4i(location_, v[0], v[1], v[2], v[3]);
}
void program::uniform_proxy::operator = (math::point<unsigned int, 1> const & v)
{
gl::Uniform1ui(location_, v[0]);
}
void program::uniform_proxy::operator = (math::point<unsigned int, 2> const & v)
{
gl::Uniform2ui(location_, v[0], v[1]);
}
void program::uniform_proxy::operator = (math::point<unsigned int, 3> const & v)
{
gl::Uniform3ui(location_, v[0], v[1], v[2]);
}
void program::uniform_proxy::operator = (math::point<unsigned int, 4> const & v)
{
gl::Uniform4ui(location_, v[0], v[1], v[2], v[3]);
}
void program::uniform_proxy::operator = (math::point<float, 1> const & v)
{
gl::Uniform1f(location_, v[0]);
}
void program::uniform_proxy::operator = (math::point<float, 2> const & v)
{
gl::Uniform2f(location_, v[0], v[1]);
}
void program::uniform_proxy::operator = (math::point<float, 3> const & v)
{
gl::Uniform3f(location_, v[0], v[1], v[2]);
}
void program::uniform_proxy::operator = (math::point<float, 4> const & v)
{
gl::Uniform4f(location_, v[0], v[1], v[2], v[3]);
}
void program::uniform_proxy::operator = (math::matrix<float, 2, 2> const & m)
{
gl::UniformMatrix2fv(location_, 1, gl::TRUE, m.coords);
}
void program::uniform_proxy::operator = (math::matrix<float, 2, 3> const & m)
{
gl::UniformMatrix3x2fv(location_, 1, gl::TRUE, m.coords);
}
void program::uniform_proxy::operator = (math::matrix<float, 2, 4> const & m)
{
gl::UniformMatrix4x2fv(location_, 1, gl::TRUE, m.coords);
}
void program::uniform_proxy::operator = (math::matrix<float, 3, 2> const & m)
{
gl::UniformMatrix2x3fv(location_, 1, gl::TRUE, m.coords);
}
void program::uniform_proxy::operator = (math::matrix<float, 3, 3> const & m)
{
gl::UniformMatrix3fv(location_, 1, gl::TRUE, m.coords);
}
void program::uniform_proxy::operator = (math::matrix<float, 3, 4> const & m)
{
gl::UniformMatrix4x3fv(location_, 1, gl::TRUE, m.coords);
}
void program::uniform_proxy::operator = (math::matrix<float, 4, 2> const & m)
{
gl::UniformMatrix2x4fv(location_, 1, gl::TRUE, m.coords);
}
void program::uniform_proxy::operator = (math::matrix<float, 4, 3> const & m)
{
gl::UniformMatrix3x4fv(location_, 1, gl::TRUE, m.coords);
}
void program::uniform_proxy::operator = (math::matrix<float, 4, 4> const & m)
{
gl::UniformMatrix4fv(location_, 1, gl::TRUE, m.coords);
}
void program::uniform_proxy::operator = (math::interval<int> const & i)
{
gl::Uniform2i(location_, i.min, i.max);
}
void program::uniform_proxy::operator = (math::interval<unsigned int> const & i)
{
gl::Uniform2ui(location_, i.min, i.max);
}
void program::uniform_proxy::operator = (math::interval<float> const & i)
{
gl::Uniform2f(location_, i.min, i.max);
}
void program::uniform_proxy::operator = (math::quaternion<float> const & q)
{
(*this) = q.coords;
}
static std::string annotated_source(std::string_view source)
{
std::ostringstream os;
bool first = true;
int line = 1;
for (char c : source)
{
if (first)
{
os << line << ": ";
first = false;
}
os.put(c);
if (c == '\n')
{
++line;
first = true;
}
}
return os.str();
}
static void load_shader(GLuint shader, std::string_view source)
{
char const * vert_sources[1] { source.data() };
GLint vert_sources_len[1] { static_cast<GLint>(source.size()) };
gl::ShaderSource(shader, 1, vert_sources, vert_sources_len);
gl::CompileShader(shader);
GLint status;
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &status);
if (status != gl::TRUE)
{
GLint log_len;
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &log_len);
std::unique_ptr<char[]> log(new char [log_len]);
gl::GetShaderInfoLog(shader, log_len, nullptr, log.get());
throw util::exception(util::to_string("Shader compilation failed: ", log.get(), "\nShader source: \n", annotated_source(source)));
}
}
static void load_program(GLuint program, std::vector<GLuint> const & shaders)
{
for (auto s : shaders)
gl::AttachShader(program, s);
gl::LinkProgram(program);
GLint status;
gl::GetProgramiv(program, gl::LINK_STATUS, &status);
if (status != gl::TRUE)
{
GLint log_len;
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &log_len);
std::unique_ptr<char[]> log(new char [log_len]);
gl::GetProgramInfoLog(program, log_len, nullptr, log.get());
throw util::exception(util::to_string("Program link failed: ", log.get()));
}
for (auto s : shaders)
gl::DetachShader(program, s);
}
static GLuint create_program(std::vector<std::pair<GLenum, std::string_view>> const & sources)
{
std::vector<GLuint> shaders;
[[maybe_unused]] util::at_scope_exit delete_shaders([&shaders]{
for (auto s : shaders)
gl::DeleteShader(s);
});
for (auto const & p : sources)
{
GLuint sh = gl::CreateShader(p.first);
shaders.push_back(sh);
load_shader(sh, p.second);
}
GLuint program = gl::CreateProgram();
try
{
load_program(program, shaders);
}
catch (...)
{
gl::DeleteProgram(program);
throw;
}
return program;
}
program::program(std::string_view compute_source)
{
program_ = create_program({{gl::COMPUTE_SHADER, compute_source}});
}
program::program(std::string_view vertex_source, std::string_view fragment_source)
{
program_ = create_program({{gl::VERTEX_SHADER, vertex_source}, {gl::FRAGMENT_SHADER, fragment_source}});
}
program::program(std::string_view vertex_source, std::string_view geometry_source, std::string_view fragment_source)
{
program_ = create_program({{gl::VERTEX_SHADER, vertex_source}, {gl::GEOMETRY_SHADER, geometry_source}, {gl::FRAGMENT_SHADER, fragment_source}});
}
program::program(program && other)
: program_(other.program_)
, uniforms_(std::move(other.uniforms_))
{
other.program_ = 0;
}
program & program::operator = (program && other)
{
if (this == &other)
return *this;
reset();
program_ = other.program_;
uniforms_ = std::move(other.uniforms_);
other.program_ = 0;
return *this;
}
program::~program()
{
reset();
}
GLuint program::id() const
{
return program_;
}
void program::bind() const
{
gl::UseProgram(program_);
}
void program::reset()
{
gl::DeleteProgram(program_);
program_ = 0;
}
GLint program::location(std::string_view name) const
{
auto it = uniforms_.find(name);
if (it == uniforms_.end())
{
std::string name_str(name);
auto location = gl::GetUniformLocation(program_, name_str.data());
uniforms_[std::move(name_str)] = location;
return location;
}
return it->second;
}
program::uniform_proxy program::operator[] (std::string_view name) const
{
return {location(name)};
}
program::uniform_proxy program::operator[] (GLint location) const
{
return {location};
}
GLuint program::uniform_block_index(std::string_view name) const
{
auto it = uniform_blocks_.find(name);
if (it == uniform_blocks_.end())
{
std::string name_str(name);
auto index = gl::GetUniformBlockIndex(program_, name.data());
uniform_blocks_[std::move(name_str)] = index;
return index;
}
return it->second;
}
void program::uniform_block_binding(std::string_view name, GLuint binding) const
{
uniform_block_binding(uniform_block_index(name), binding);
}
void program::uniform_block_binding(GLuint index, GLuint binding) const
{
if (index != gl::INVALID_INDEX)
gl::UniformBlockBinding(program_, index, binding);
}
}