paradiso/src/lib/src/shader.cpp

284 lines
8.1 KiB
C++
Raw Normal View History

2023-06-30 22:55:59 +02:00
/*
* Copyright 2023 Hartmut Seichter
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#include "paradiso/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() const { 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() const { 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);
}
} // namespace paradiso