forked from Hartmut/paradiso
309 lines
7.9 KiB
C++
309 lines
7.9 KiB
C++
|
#include "shader.hpp"
|
||
|
|
||
|
#include "glad/glad.h"
|
||
|
|
||
|
#include <iostream>
|
||
|
|
||
|
namespace paradiso
|
||
|
{
|
||
|
|
||
|
struct Shader::impl
|
||
|
{
|
||
|
std::reference_wrapper<Shader> _shader;
|
||
|
|
||
|
GLuint _shader_program;
|
||
|
std::vector<GLuint> _shader_stages;
|
||
|
|
||
|
impl(Shader &s)
|
||
|
: _shader(s)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
~impl()
|
||
|
{
|
||
|
destroy();
|
||
|
}
|
||
|
|
||
|
bool is_valid()
|
||
|
{
|
||
|
// we potentially haul in is_valid while no context is given
|
||
|
return glIsProgram != nullptr && glIsProgram(_shader_program);
|
||
|
}
|
||
|
|
||
|
bool build()
|
||
|
{
|
||
|
// if (!is_valid()) return false;
|
||
|
|
||
|
for (const auto &[type, code] : _shader.get().source_)
|
||
|
{
|
||
|
GLuint shader_type = 0;
|
||
|
switch (type)
|
||
|
{
|
||
|
case Shader::Type::Vertex:
|
||
|
shader_type = GL_VERTEX_SHADER;
|
||
|
break;
|
||
|
case Shader::Type::Compute:
|
||
|
shader_type = GL_COMPUTE_SHADER;
|
||
|
break;
|
||
|
case Shader::Type::Geometry:
|
||
|
shader_type = GL_GEOMETRY_SHADER;
|
||
|
break;
|
||
|
case Shader::Type::Fragment:
|
||
|
shader_type = GL_FRAGMENT_SHADER;
|
||
|
break;
|
||
|
default:
|
||
|
std::cerr << " unknown shader type";
|
||
|
}
|
||
|
|
||
|
GLuint shaderId = glCreateShader(shader_type);
|
||
|
|
||
|
char *src = const_cast<char *>(code.c_str());
|
||
|
GLint size = static_cast<GLint>(code.length());
|
||
|
|
||
|
glShaderSource(shaderId, 1, &src, &size);
|
||
|
|
||
|
glCompileShader(shaderId);
|
||
|
|
||
|
GLint is_compiled = GL_FALSE;
|
||
|
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &is_compiled);
|
||
|
if (is_compiled == GL_FALSE)
|
||
|
{
|
||
|
|
||
|
GLint log_length;
|
||
|
|
||
|
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &log_length);
|
||
|
|
||
|
std::vector<char> log_buffer(static_cast<size_t>(log_length));
|
||
|
|
||
|
glGetShaderInfoLog(shaderId, log_length, &log_length, log_buffer.data());
|
||
|
|
||
|
// TODO - handle errors!
|
||
|
std::cerr << log_buffer.data();
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
_shader_stages.push_back(shaderId);
|
||
|
}
|
||
|
|
||
|
_shader_program = glCreateProgram();
|
||
|
|
||
|
for (auto s : _shader_stages)
|
||
|
glAttachShader(_shader_program, s);
|
||
|
|
||
|
// TODO attribute binding ...
|
||
|
|
||
|
/* Bind attribute index 0 (coordinates) to in_Position and attribute index 1 (color) to in_Color */
|
||
|
/* Attribute locations must be setup before calling glLinkProgram. */
|
||
|
// glBindAttribLocation(shaderprogram, 0, "in_Position");
|
||
|
// glBindAttribLocation(shaderprogram, 1, "in_Color");
|
||
|
|
||
|
glLinkProgram(_shader_program);
|
||
|
|
||
|
GLint is_linked = 0;
|
||
|
glGetProgramiv(_shader_program, GL_LINK_STATUS, &is_linked);
|
||
|
if (is_linked == GL_FALSE)
|
||
|
{
|
||
|
|
||
|
GLint log_length;
|
||
|
|
||
|
/* Noticed that glGetProgramiv is used to get the length for a shader program, not glGetShaderiv. */
|
||
|
glGetProgramiv(_shader_program, GL_INFO_LOG_LENGTH, &log_length);
|
||
|
|
||
|
/* The maxLength includes the NULL character */
|
||
|
std::vector<char> info_log(static_cast<size_t>(log_length));
|
||
|
|
||
|
/* Notice that glGetProgramInfoLog, not glGetShaderInfoLog. */
|
||
|
glGetProgramInfoLog(_shader_program, log_length, &log_length, info_log.data());
|
||
|
|
||
|
std::cerr << info_log.data();
|
||
|
|
||
|
/* Handle the error in an appropriate way such as displaying a message or writing to a log file. */
|
||
|
/* In this simple program, we'll just leave */
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void use()
|
||
|
{
|
||
|
glUseProgram(_shader_program);
|
||
|
}
|
||
|
|
||
|
void destroy()
|
||
|
{
|
||
|
// potentially the GL driver hasn't been loaded
|
||
|
if (is_valid())
|
||
|
{
|
||
|
|
||
|
// deleting and detaching should happen much earlier
|
||
|
|
||
|
for (auto s : _shader_stages)
|
||
|
{
|
||
|
glDeleteShader(s);
|
||
|
}
|
||
|
|
||
|
// only program needs to be deleted
|
||
|
|
||
|
glDeleteProgram(_shader_program);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int uniform_location(std::string const &name) const
|
||
|
{
|
||
|
return glGetUniformLocation(_shader_program, name.c_str());
|
||
|
}
|
||
|
|
||
|
// void bind(int location,const matrix3x3f& m)
|
||
|
// {
|
||
|
// glUniformMatrix3fv(location,1,GL_FALSE,m.ptr());
|
||
|
// }
|
||
|
|
||
|
// void bind(int location,const matrix4x4f& m)
|
||
|
// {
|
||
|
// glUniformMatrix4fv(location,1,GL_FALSE,m.ptr());
|
||
|
// }
|
||
|
|
||
|
// void bind(int location,const vector4f& v)
|
||
|
// {
|
||
|
// glUniform4fv(location,1,v.ptr());
|
||
|
// }
|
||
|
|
||
|
void bind(int location, const float &v)
|
||
|
{
|
||
|
glUniform1f(location, v);
|
||
|
}
|
||
|
|
||
|
void bind(int location, const uint32_t &i)
|
||
|
{
|
||
|
glUniform1ui(location, i);
|
||
|
}
|
||
|
|
||
|
void bind(int location, const int32_t &i)
|
||
|
{
|
||
|
glUniform1i(location, i);
|
||
|
}
|
||
|
|
||
|
// void bind(int location,const texture& v)
|
||
|
// {
|
||
|
// this->bind(location,(int)v.native_handle());
|
||
|
|
||
|
// }
|
||
|
};
|
||
|
|
||
|
Shader::Shader()
|
||
|
{
|
||
|
impl_ = std::make_unique<impl>(*this);
|
||
|
}
|
||
|
|
||
|
Shader::~Shader()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
bool Shader::ready() const
|
||
|
{
|
||
|
return impl_->is_valid();
|
||
|
}
|
||
|
|
||
|
Shader &Shader::set_uniform_at_location(int location, float v)
|
||
|
{
|
||
|
impl_->bind(location, v);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Shader &Shader::set_uniform_at_location(int location, uint32_t v)
|
||
|
{
|
||
|
impl_->bind(location, v);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Shader &Shader::set_uniform_at_location(int location, int32_t v)
|
||
|
{
|
||
|
impl_->bind(location, v);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
// Shader &Shader::set_uniform_at_location(int location, vector4f const &v)
|
||
|
// {
|
||
|
// impl_->bind(location, v);
|
||
|
// return *this;
|
||
|
// }
|
||
|
|
||
|
// Shader &Shader::set_uniform_at_location(int location, matrix4x4f const &v)
|
||
|
// {
|
||
|
// impl_->bind(location, v);
|
||
|
// return *this;
|
||
|
// }
|
||
|
|
||
|
// Shader &Shader::set_uniform_at_location(int location, texture const &v)
|
||
|
// {
|
||
|
// impl_->bind(location, v);
|
||
|
// return *this;
|
||
|
// }
|
||
|
|
||
|
bool Shader::build()
|
||
|
{
|
||
|
return impl_->build();
|
||
|
}
|
||
|
|
||
|
void Shader::use()
|
||
|
{
|
||
|
impl_->use();
|
||
|
}
|
||
|
|
||
|
void Shader::set_uniforms(uniform_cache_t c)
|
||
|
{
|
||
|
|
||
|
#if 0
|
||
|
// TODO rewrite in proper C++17
|
||
|
|
||
|
for (auto &u : c)
|
||
|
{
|
||
|
// get name
|
||
|
std::string name = std::get<0>(u);
|
||
|
// get location
|
||
|
GLint loc = std::get<2>(u);
|
||
|
|
||
|
// if lower 0 check for location
|
||
|
if (loc < 0)
|
||
|
{
|
||
|
loc = impl_->uniform_location(name);
|
||
|
std::get<2>(u) = loc; // cache location
|
||
|
}
|
||
|
|
||
|
auto var = std::get<1>(u);
|
||
|
|
||
|
std::visit([this, loc](auto &&arg)
|
||
|
{
|
||
|
|
||
|
using T = std::decay_t<decltype(arg)>;
|
||
|
|
||
|
// TODO query the std::variant of uniform_t
|
||
|
|
||
|
if constexpr ((std::is_same_v<T, vector4f>) ||
|
||
|
(std::is_same_v<T, matrix4x4f>) ||
|
||
|
(std::is_same_v<T, float>) ) {
|
||
|
set_uniform_at_location( loc, std::forward<T>(arg));
|
||
|
} else {
|
||
|
debug::e() << "unknown uniform type";
|
||
|
} },
|
||
|
var);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
uint32_t Shader::native_handle() const
|
||
|
{
|
||
|
return impl_->_shader_program;
|
||
|
}
|
||
|
|
||
|
int Shader::uniform_location(const std::string &name) const
|
||
|
{
|
||
|
return impl_->uniform_location(name);
|
||
|
}
|
||
|
|
||
|
}
|