612 lines
18 KiB
C++
612 lines
18 KiB
C++
#include "pch.h"
|
|
#include "log.h"
|
|
#include "shader.h"
|
|
#include "asset.h"
|
|
#include "app.h"
|
|
#include "renderer_gl/opengl_capabilities.h"
|
|
#include "renderer_gl/shader_bindings.h"
|
|
|
|
#include <cstdint>
|
|
|
|
namespace {
|
|
|
|
[[nodiscard]] GLenum vertex_shader_stage() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::vertex_shader_stage());
|
|
}
|
|
|
|
[[nodiscard]] GLenum fragment_shader_stage() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::fragment_shader_stage());
|
|
}
|
|
|
|
[[nodiscard]] GLenum shader_compile_status_query() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::shader_compile_status_query());
|
|
}
|
|
|
|
[[nodiscard]] GLenum program_link_status_query() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::program_link_status_query());
|
|
}
|
|
|
|
[[nodiscard]] GLenum active_uniform_count_query() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::active_uniform_count_query());
|
|
}
|
|
|
|
void use_opengl_program(std::uint32_t program) noexcept
|
|
{
|
|
glUseProgram(static_cast<GLuint>(program));
|
|
}
|
|
|
|
void delete_opengl_program(std::uint32_t program) noexcept
|
|
{
|
|
glDeleteProgram(static_cast<GLuint>(program));
|
|
}
|
|
|
|
void set_opengl_uniform_4fv(std::int32_t location, std::int32_t count, const float* values) noexcept
|
|
{
|
|
glUniform4fv(static_cast<GLint>(location), static_cast<GLsizei>(count), values);
|
|
}
|
|
|
|
void set_opengl_uniform_3fv(std::int32_t location, std::int32_t count, const float* values) noexcept
|
|
{
|
|
glUniform3fv(static_cast<GLint>(location), static_cast<GLsizei>(count), values);
|
|
}
|
|
|
|
void set_opengl_uniform_2fv(std::int32_t location, std::int32_t count, const float* values) noexcept
|
|
{
|
|
glUniform2fv(static_cast<GLint>(location), static_cast<GLsizei>(count), values);
|
|
}
|
|
|
|
void set_opengl_uniform_matrix_4fv(
|
|
std::int32_t location,
|
|
std::int32_t count,
|
|
std::uint8_t transpose,
|
|
const float* values) noexcept
|
|
{
|
|
glUniformMatrix4fv(
|
|
static_cast<GLint>(location),
|
|
static_cast<GLsizei>(count),
|
|
static_cast<GLboolean>(transpose),
|
|
values);
|
|
}
|
|
|
|
void set_opengl_uniform_1i(std::int32_t location, std::int32_t value) noexcept
|
|
{
|
|
glUniform1i(static_cast<GLint>(location), static_cast<GLint>(value));
|
|
}
|
|
|
|
void set_opengl_uniform_1f(std::int32_t location, float value) noexcept
|
|
{
|
|
glUniform1f(static_cast<GLint>(location), value);
|
|
}
|
|
|
|
std::int32_t get_opengl_attrib_location(std::uint32_t program, const char* name) noexcept
|
|
{
|
|
return static_cast<std::int32_t>(glGetAttribLocation(static_cast<GLuint>(program), name));
|
|
}
|
|
|
|
}
|
|
|
|
std::map<kShader, Shader> ShaderManager::m_shaders;
|
|
Shader* ShaderManager::m_current;
|
|
bool ShaderManager::ext_framebuffer_fetch = false;
|
|
bool ShaderManager::ext_float32 = false;
|
|
bool ShaderManager::ext_float32_linear = false;
|
|
bool ShaderManager::ext_float16 = false;
|
|
bool ShaderManager::ext_map_aligned = false;
|
|
|
|
std::string Shader::read(const std::string& path)
|
|
{
|
|
Asset a;
|
|
std::string ret;
|
|
if (a.open(path.c_str()))
|
|
{
|
|
struct stat tmp_info;
|
|
std::string abs_path = Asset::absolute(path);
|
|
if (stat(abs_path.c_str(), &tmp_info) == 0)
|
|
m_deps[path] = tmp_info;
|
|
|
|
std::regex reg_include(R"!(#include "([^"]+)")!");
|
|
std::string data((char*)a.read_all(), a.m_len);
|
|
|
|
// split path
|
|
std::string base;
|
|
std::regex reg_path(R"((.*)[\\/]([^\\/]+)\.(\w+)$)");
|
|
std::smatch m;
|
|
if (std::regex_search(path, m, reg_path))
|
|
{
|
|
base = m[1].str();
|
|
}
|
|
|
|
for (const auto& l : split(data, '\n'))
|
|
{
|
|
std::smatch include_match;
|
|
if (std::regex_search(l, include_match, reg_include))
|
|
{
|
|
std::string inc = base + "/" + include_match[1].str();
|
|
if (Asset::exist(inc.c_str()))
|
|
{
|
|
std::string subdata = read(inc);
|
|
ret.append(subdata + "\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret.append(l + "\n");
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void Shader::parse_error(const std::string& msg, const std::string& code)
|
|
{
|
|
auto code_lines = split(code, '\n');
|
|
auto error_lines = split(msg, '\n');
|
|
std::smatch m;
|
|
std::regex r(R"(\((\d+)\))");
|
|
for (const auto& line : error_lines)
|
|
{
|
|
LOG("%s", line.c_str());
|
|
if (std::regex_search(line, m, r))
|
|
{
|
|
int ln = std::stoi(m[1].str());
|
|
if (ln < code_lines.size())
|
|
{
|
|
int n = 2;
|
|
int s = std::max(ln - n, 1);
|
|
int e = std::min((int)code_lines.size() - 1, ln + n);
|
|
for (int i = s; i < e; i++)
|
|
{
|
|
LOG("- line %02d: %s", i, code_lines[i - 1].c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Shader::load(const std::string& path)
|
|
{
|
|
std::string data = read(path);
|
|
if (data.empty()) return false;
|
|
|
|
std::map<std::string, std::shared_ptr<std::string>> sections;
|
|
std::shared_ptr<std::string> current_section = nullptr;
|
|
std::regex reg_section(R"!(\[\[(.*)\]\])!");
|
|
for (const auto& l : split(data, '\n'))
|
|
{
|
|
std::smatch m;
|
|
if (std::regex_search(l, m, reg_section))
|
|
{
|
|
std::string section_name = m[1].str();
|
|
if (!sections[section_name])
|
|
sections[section_name] = std::make_shared<std::string>();
|
|
current_section = sections[section_name];
|
|
}
|
|
else
|
|
{
|
|
// create an un-named section
|
|
if (!current_section)
|
|
current_section = sections[""] = std::make_shared<std::string>();
|
|
current_section->append(l + "\n");
|
|
}
|
|
}
|
|
|
|
if (sections.find("vertex") != sections.end() && sections.find("fragment") != sections.end())
|
|
{
|
|
*sections["vertex"] = SHADER_VERSION + *sections["vertex"];
|
|
*sections["fragment"] = SHADER_VERSION + *sections["fragment"];
|
|
if (create(*sections["vertex"], *sections["fragment"]))
|
|
{
|
|
m_path = path;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG("could not find [[vertex]] and [[fragment]] sections on %s", path.c_str());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Shader::reload()
|
|
{
|
|
if (m_path.empty())
|
|
return false;
|
|
|
|
bool dirty = false;
|
|
struct stat tmp_info;
|
|
for (auto& d : m_deps)
|
|
{
|
|
std::string abs_path = Asset::absolute(d.first);
|
|
if (stat(abs_path.c_str(), &tmp_info) != 0)
|
|
continue;
|
|
if (tmp_info.st_mtime > d.second.st_mtime)
|
|
{
|
|
d.second = tmp_info;
|
|
dirty = true;
|
|
}
|
|
}
|
|
|
|
if (dirty)
|
|
{
|
|
LOG("reload shader %s", m_path.c_str());
|
|
destroy();
|
|
return load(m_path);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Shader::create(const std::string& vertex, const std::string& fragment)
|
|
{
|
|
bool ret = true;
|
|
App::I->render_task([this, &ret, vertex, fragment]
|
|
{
|
|
GLint status;
|
|
static char infolog[4096];
|
|
int infolen;
|
|
const GLchar* source;
|
|
|
|
auto vs = glCreateShader(vertex_shader_stage());
|
|
if (!vs)
|
|
{
|
|
ret = false;
|
|
return;
|
|
}
|
|
source = vertex.c_str();
|
|
glShaderSource(vs, 1, &source, nullptr);
|
|
glCompileShader(vs);
|
|
glGetShaderiv(vs, shader_compile_status_query(), &status);
|
|
glGetShaderInfoLog(vs, sizeof(infolog), &infolen, infolog);
|
|
if (infolen > 0)
|
|
{
|
|
LOG("\nVERTEX SHADER: %s", m_path.c_str());
|
|
parse_error(infolog, vertex);
|
|
}
|
|
if (status == 0)
|
|
{
|
|
glDeleteShader(vs);
|
|
ret = false;
|
|
return;
|
|
}
|
|
|
|
auto fs = glCreateShader(fragment_shader_stage());
|
|
if (!fs)
|
|
{
|
|
glDeleteShader(vs);
|
|
ret = false;
|
|
return;
|
|
}
|
|
source = fragment.c_str();
|
|
glShaderSource(fs, 1, &source, nullptr);
|
|
glCompileShader(fs);
|
|
glGetShaderiv(fs, shader_compile_status_query(), &status);
|
|
glGetShaderInfoLog(fs, sizeof(infolog), &infolen, infolog);
|
|
if (infolen > 0)
|
|
{
|
|
LOG("\nFRAGMENT SHADER: %s", m_path.c_str());
|
|
parse_error(infolog, fragment);
|
|
}
|
|
if (status == 0)
|
|
{
|
|
glDeleteShader(vs);
|
|
glDeleteShader(fs);
|
|
ret = false;
|
|
return;
|
|
}
|
|
|
|
auto ps = glCreateProgram();
|
|
if (!ps)
|
|
{
|
|
glDeleteShader(vs);
|
|
glDeleteShader(fs);
|
|
ret = false;
|
|
return;
|
|
}
|
|
glAttachShader(ps, vs);
|
|
glAttachShader(ps, fs);
|
|
glDeleteShader(vs);
|
|
glDeleteShader(fs);
|
|
|
|
glLinkProgram(ps);
|
|
for (const auto& binding : pp::renderer::gl::panopainter_shader_attribute_bindings())
|
|
{
|
|
if (glGetAttribLocation(ps, binding.name) != -1)
|
|
glBindAttribLocation(ps, static_cast<GLuint>(binding.location), binding.name);
|
|
}
|
|
|
|
glLinkProgram(ps);
|
|
glGetProgramiv(ps, program_link_status_query(), &status);
|
|
glGetProgramInfoLog(ps, sizeof(infolog), &infolen, infolog);
|
|
if (infolen > 0)
|
|
LOG("LINK SHADER: %s\n%s", m_path.c_str(), infolog);
|
|
if (status == 0)
|
|
{
|
|
glDeleteProgram(ps);
|
|
ret = false;
|
|
return;
|
|
}
|
|
|
|
// Parse shader uniforms
|
|
{
|
|
GLint count;
|
|
GLint size; // size of the variable
|
|
GLenum type; // type of the variable (float, vec3 or mat4, etc)
|
|
const GLsizei bufSize = 64; // maximum name length
|
|
GLchar name[bufSize]; // variable name in GLSL
|
|
GLsizei length; // name length
|
|
glGetProgramiv(ps, active_uniform_count_query(), &count);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
glGetActiveUniform(ps, (GLuint)i, bufSize, &length, &size, &type, name);
|
|
name[length] = 0;
|
|
kShaderUniform id = static_cast<kShaderUniform>(pp::renderer::gl::shader_uniform_id(name));
|
|
if (m_umap.find(id) != m_umap.end())
|
|
LOG("UNIFORM ALREADY DEFINED: %s", name);
|
|
m_umap[id] = glGetUniformLocation(ps, name);
|
|
//printf("Uniform #%d Type: %u Name: %s Loc: %d\n", i, type, name, glGetUniformLocation(ps, name));
|
|
}
|
|
}
|
|
|
|
prog = ps;
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
|
|
void Shader::destroy()
|
|
{
|
|
if (prog)
|
|
{
|
|
App::I->render_task_async([prog=prog]
|
|
{
|
|
const auto status = pp::renderer::gl::delete_opengl_program(
|
|
static_cast<std::uint32_t>(prog),
|
|
pp::renderer::gl::OpenGlProgramDeleteDispatch {
|
|
.use_program = use_opengl_program,
|
|
.delete_program = delete_opengl_program,
|
|
});
|
|
if (!status.ok())
|
|
LOG("Shader::destroy() failed because: %s", status.message);
|
|
});
|
|
prog = 0;
|
|
}
|
|
m_umap.clear();
|
|
}
|
|
|
|
void Shader::use()
|
|
{
|
|
const auto status = pp::renderer::gl::use_opengl_program(
|
|
static_cast<std::uint32_t>(prog),
|
|
pp::renderer::gl::OpenGlProgramUseDispatch {
|
|
.use_program = use_opengl_program,
|
|
});
|
|
if (!status.ok())
|
|
LOG("Shader::use() failed because: %s", status.message);
|
|
}
|
|
void Shader::u_vec4(kShaderUniform id, const glm::vec4& v)
|
|
{
|
|
if (m_umap.count(id) == 0)
|
|
LOG("UNIFORM vec4 %d NOT FOUND in shader %d", (int)id, (int)name)
|
|
else
|
|
{
|
|
const auto status = pp::renderer::gl::set_opengl_uniform_vec4(
|
|
static_cast<std::int32_t>(m_umap[id]),
|
|
glm::value_ptr(v),
|
|
pp::renderer::gl::OpenGlUniformVec4Dispatch {
|
|
.uniform_4fv = set_opengl_uniform_4fv,
|
|
});
|
|
if (!status.ok())
|
|
LOG("Shader::u_vec4() failed because: %s", status.message);
|
|
}
|
|
}
|
|
void Shader::u_vec3(kShaderUniform id, const glm::vec3& v)
|
|
{
|
|
if (m_umap.count(id) == 0)
|
|
LOG("UNIFORM vec3 %d NOT FOUND in shader %d", (int)id, (int)name)
|
|
else
|
|
{
|
|
const auto status = pp::renderer::gl::set_opengl_uniform_vec3(
|
|
static_cast<std::int32_t>(m_umap[id]),
|
|
glm::value_ptr(v),
|
|
pp::renderer::gl::OpenGlUniformVec3Dispatch {
|
|
.uniform_3fv = set_opengl_uniform_3fv,
|
|
});
|
|
if (!status.ok())
|
|
LOG("Shader::u_vec3() failed because: %s", status.message);
|
|
}
|
|
}
|
|
|
|
void Shader::u_vec2(kShaderUniform id, const glm::vec2& v)
|
|
{
|
|
if (m_umap.count(id) == 0)
|
|
LOG("UNIFORM vec2 %d NOT FOUND in shader %d", (int)id, (int)name)
|
|
else
|
|
{
|
|
const auto status = pp::renderer::gl::set_opengl_uniform_vec2(
|
|
static_cast<std::int32_t>(m_umap[id]),
|
|
glm::value_ptr(v),
|
|
pp::renderer::gl::OpenGlUniformVec2Dispatch {
|
|
.uniform_2fv = set_opengl_uniform_2fv,
|
|
});
|
|
if (!status.ok())
|
|
LOG("Shader::u_vec2() failed because: %s", status.message);
|
|
}
|
|
}
|
|
|
|
void Shader::u_mat4(kShaderUniform id, const glm::mat4& m)
|
|
{
|
|
if (m_umap.count(id) == 0)
|
|
LOG("UNIFORM mat4 %d NOT FOUND in shader %d", (int)id, (int)name)
|
|
else
|
|
{
|
|
const auto status = pp::renderer::gl::set_opengl_uniform_mat4(
|
|
static_cast<std::int32_t>(m_umap[id]),
|
|
glm::value_ptr(m),
|
|
pp::renderer::gl::OpenGlUniformMat4Dispatch {
|
|
.uniform_matrix_4fv = set_opengl_uniform_matrix_4fv,
|
|
});
|
|
if (!status.ok())
|
|
LOG("Shader::u_mat4() failed because: %s", status.message);
|
|
}
|
|
}
|
|
void Shader::u_int(kShaderUniform id, int i)
|
|
{
|
|
if (m_umap.count(id) == 0)
|
|
LOG("UNIFORM int %d NOT FOUND in shader %d", (int)id, (int)name)
|
|
else
|
|
{
|
|
const auto status = pp::renderer::gl::set_opengl_uniform_int(
|
|
static_cast<std::int32_t>(m_umap[id]),
|
|
static_cast<std::int32_t>(i),
|
|
pp::renderer::gl::OpenGlUniformIntDispatch {
|
|
.uniform_1i = set_opengl_uniform_1i,
|
|
});
|
|
if (!status.ok())
|
|
LOG("Shader::u_int() failed because: %s", status.message);
|
|
}
|
|
}
|
|
void Shader::u_int(const char* uniform_name, int i)
|
|
{
|
|
const auto location = pp::renderer::gl::get_opengl_attribute_location(
|
|
static_cast<std::uint32_t>(prog),
|
|
uniform_name,
|
|
pp::renderer::gl::OpenGlAttributeLocationDispatch {
|
|
.get_attrib_location = get_opengl_attrib_location,
|
|
});
|
|
if (!location.ok())
|
|
{
|
|
LOG("Shader::u_int(name) lookup failed because: %s", location.status().message);
|
|
return;
|
|
}
|
|
const auto status = pp::renderer::gl::set_opengl_uniform_int(
|
|
location.value(),
|
|
static_cast<std::int32_t>(i),
|
|
pp::renderer::gl::OpenGlUniformIntDispatch {
|
|
.uniform_1i = set_opengl_uniform_1i,
|
|
});
|
|
if (!status.ok())
|
|
LOG("Shader::u_int(name) failed because: %s", status.message);
|
|
}
|
|
void Shader::u_float(kShaderUniform id, float f)
|
|
{
|
|
if (m_umap.count(id) == 0)
|
|
LOG("UNIFORM float %d NOT FOUND in shader %d", (int)id, (int)name)
|
|
else
|
|
{
|
|
const auto status = pp::renderer::gl::set_opengl_uniform_float(
|
|
static_cast<std::int32_t>(m_umap[id]),
|
|
f,
|
|
pp::renderer::gl::OpenGlUniformFloatDispatch {
|
|
.uniform_1f = set_opengl_uniform_1f,
|
|
});
|
|
if (!status.ok())
|
|
LOG("Shader::u_float() failed because: %s", status.message);
|
|
}
|
|
}
|
|
GLint Shader::GetAttribLocation(const char* attribute_name)
|
|
{
|
|
const auto location = pp::renderer::gl::get_opengl_attribute_location(
|
|
static_cast<std::uint32_t>(prog),
|
|
attribute_name,
|
|
pp::renderer::gl::OpenGlAttributeLocationDispatch {
|
|
.get_attrib_location = get_opengl_attrib_location,
|
|
});
|
|
if (!location.ok())
|
|
{
|
|
LOG("Shader::GetAttribLocation() failed because: %s", location.status().message);
|
|
return -1;
|
|
}
|
|
return static_cast<GLint>(location.value());
|
|
}
|
|
|
|
bool ShaderManager::load(kShader id, const std::string& path)
|
|
{
|
|
m_shaders[id].name = id;
|
|
return m_shaders[id].load(path);
|
|
}
|
|
|
|
bool ShaderManager::reload()
|
|
{
|
|
bool success = false;
|
|
for (auto& s : m_shaders)
|
|
success |= s.second.reload();
|
|
return success;
|
|
}
|
|
|
|
bool ShaderManager::create(kShader id, const std::string& vertex, const std::string& fragment)
|
|
{
|
|
m_shaders[id].name = id;
|
|
return m_shaders[id].create(vertex, fragment);
|
|
}
|
|
|
|
void ShaderManager::use(kShader id)
|
|
{
|
|
m_current = &m_shaders[id];
|
|
m_current->use();
|
|
}
|
|
|
|
void ShaderManager::use(const char* name)
|
|
{
|
|
m_current = &m_shaders[(kShader)const_hash(name)];
|
|
m_current->use();
|
|
}
|
|
|
|
void ShaderManager::u_vec4(kShaderUniform id, const glm::vec4& v)
|
|
{
|
|
m_current->u_vec4(id, v);
|
|
}
|
|
|
|
void ShaderManager::u_vec3(kShaderUniform id, const glm::vec3& v)
|
|
{
|
|
m_current->u_vec3(id, v);
|
|
}
|
|
|
|
void ShaderManager::u_vec2(kShaderUniform id, const glm::vec2& v)
|
|
{
|
|
m_current->u_vec2(id, v);
|
|
}
|
|
|
|
void ShaderManager::u_mat4(kShaderUniform id, const glm::mat4& m)
|
|
{
|
|
m_current->u_mat4(id, m);
|
|
}
|
|
|
|
void ShaderManager::u_int(kShaderUniform id, int i)
|
|
{
|
|
m_current->u_int(id, i);
|
|
}
|
|
|
|
void ShaderManager::u_int(const char* name, int i)
|
|
{
|
|
m_current->u_int(name, i);
|
|
}
|
|
|
|
Shader* ShaderManager::get(kShader id)
|
|
{
|
|
auto it = m_shaders.find(id);
|
|
return (it == m_shaders.end()) ? nullptr : &it->second;
|
|
}
|
|
|
|
void ShaderManager::u_float(kShaderUniform id, float f)
|
|
{
|
|
m_current->u_float(id, f);
|
|
}
|
|
|
|
void ShaderManager::invalidate()
|
|
{
|
|
m_shaders.clear();
|
|
}
|
|
|
|
bool check_uniform_uniqueness()
|
|
{
|
|
return pp::renderer::gl::validate_shader_uniform_names(
|
|
pp::renderer::gl::panopainter_shader_uniform_names())
|
|
.ok();
|
|
}
|