#include "shader.hpp" #include "glad/glad.h" #include namespace paradiso { struct Shader::impl { std::reference_wrapper _shader; GLuint _shader_program; std::vector _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(code.c_str()); GLint size = static_cast(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 log_buffer(static_cast(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 info_log(static_cast(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(*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; // TODO query the std::variant of uniform_t if constexpr ((std::is_same_v) || (std::is_same_v) || (std::is_same_v) ) { set_uniform_at_location( loc, std::forward(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); } }