420 lines
9.8 KiB
C++
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 = (geom::vector<int, 1> const & v)
|
|
{
|
|
gl::Uniform1i(location_, v[0]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::vector<int, 2> const & v)
|
|
{
|
|
gl::Uniform2i(location_, v[0], v[1]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::vector<int, 3> const & v)
|
|
{
|
|
gl::Uniform3i(location_, v[0], v[1], v[2]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::vector<int, 4> const & v)
|
|
{
|
|
gl::Uniform4i(location_, v[0], v[1], v[2], v[3]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::vector<unsigned int, 1> const & v)
|
|
{
|
|
gl::Uniform1ui(location_, v[0]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::vector<unsigned int, 2> const & v)
|
|
{
|
|
gl::Uniform2ui(location_, v[0], v[1]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::vector<unsigned int, 3> const & v)
|
|
{
|
|
gl::Uniform3ui(location_, v[0], v[1], v[2]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::vector<unsigned int, 4> const & v)
|
|
{
|
|
gl::Uniform4ui(location_, v[0], v[1], v[2], v[3]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::vector<float, 1> const & v)
|
|
{
|
|
gl::Uniform1f(location_, v[0]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::vector<float, 2> const & v)
|
|
{
|
|
gl::Uniform2f(location_, v[0], v[1]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::vector<float, 3> const & v)
|
|
{
|
|
gl::Uniform3f(location_, v[0], v[1], v[2]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::vector<float, 4> const & v)
|
|
{
|
|
gl::Uniform4f(location_, v[0], v[1], v[2], v[3]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<int, 1> const & v)
|
|
{
|
|
gl::Uniform1i(location_, v[0]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<int, 2> const & v)
|
|
{
|
|
gl::Uniform2i(location_, v[0], v[1]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<int, 3> const & v)
|
|
{
|
|
gl::Uniform3i(location_, v[0], v[1], v[2]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<int, 4> const & v)
|
|
{
|
|
gl::Uniform4i(location_, v[0], v[1], v[2], v[3]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<unsigned int, 1> const & v)
|
|
{
|
|
gl::Uniform1ui(location_, v[0]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<unsigned int, 2> const & v)
|
|
{
|
|
gl::Uniform2ui(location_, v[0], v[1]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<unsigned int, 3> const & v)
|
|
{
|
|
gl::Uniform3ui(location_, v[0], v[1], v[2]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<unsigned int, 4> const & v)
|
|
{
|
|
gl::Uniform4ui(location_, v[0], v[1], v[2], v[3]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<float, 1> const & v)
|
|
{
|
|
gl::Uniform1f(location_, v[0]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<float, 2> const & v)
|
|
{
|
|
gl::Uniform2f(location_, v[0], v[1]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<float, 3> const & v)
|
|
{
|
|
gl::Uniform3f(location_, v[0], v[1], v[2]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::point<float, 4> const & v)
|
|
{
|
|
gl::Uniform4f(location_, v[0], v[1], v[2], v[3]);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::matrix<float, 2, 2> const & m)
|
|
{
|
|
gl::UniformMatrix2fv(location_, 1, gl::TRUE, m.coords);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::matrix<float, 2, 3> const & m)
|
|
{
|
|
gl::UniformMatrix3x2fv(location_, 1, gl::TRUE, m.coords);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::matrix<float, 2, 4> const & m)
|
|
{
|
|
gl::UniformMatrix4x2fv(location_, 1, gl::TRUE, m.coords);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::matrix<float, 3, 2> const & m)
|
|
{
|
|
gl::UniformMatrix2x3fv(location_, 1, gl::TRUE, m.coords);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::matrix<float, 3, 3> const & m)
|
|
{
|
|
gl::UniformMatrix3fv(location_, 1, gl::TRUE, m.coords);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::matrix<float, 3, 4> const & m)
|
|
{
|
|
gl::UniformMatrix4x3fv(location_, 1, gl::TRUE, m.coords);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::matrix<float, 4, 2> const & m)
|
|
{
|
|
gl::UniformMatrix2x4fv(location_, 1, gl::TRUE, m.coords);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::matrix<float, 4, 3> const & m)
|
|
{
|
|
gl::UniformMatrix3x4fv(location_, 1, gl::TRUE, m.coords);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::matrix<float, 4, 4> const & m)
|
|
{
|
|
gl::UniformMatrix4fv(location_, 1, gl::TRUE, m.coords);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::interval<int> const & i)
|
|
{
|
|
gl::Uniform2i(location_, i.min, i.max);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::interval<unsigned int> const & i)
|
|
{
|
|
gl::Uniform2ui(location_, i.min, i.max);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::interval<float> const & i)
|
|
{
|
|
gl::Uniform2f(location_, i.min, i.max);
|
|
}
|
|
|
|
void program::uniform_proxy::operator = (geom::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);
|
|
}
|
|
|
|
}
|