#include "pch.h" #include "log.h" #include "shader.h" #include "asset.h" #include "app.h" std::map ShaderManager::m_shaders; Shader* ShaderManager::m_current; bool ShaderManager::ext_framebuffer_fetch = 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 name, base, ext; std::regex reg_path(R"((.*)[\\/]([^\\/]+)\.(\w+)$)"); std::smatch m; if (std::regex_search(path, m, reg_path)) { base = m[1].str(); name = m[2].str(); ext = m[3].str(); } for (const auto& l : split(data, '\n')) { std::smatch m; if (std::regex_search(l, m, reg_include)) { std::string inc = base + "/" + m[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> sections; std::shared_ptr 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(); current_section = sections[section_name]; } else { // create an un-named section if (!current_section) current_section = sections[""] = std::make_shared(); 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(GL_VERTEX_SHADER); if (!vs) { ret = false; return; } source = vertex.c_str(); glShaderSource(vs, 1, &source, nullptr); glCompileShader(vs); glGetShaderiv(vs, GL_COMPILE_STATUS, &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(GL_FRAGMENT_SHADER); if (!fs) { glDeleteShader(vs); ret = false; return; } source = fragment.c_str(); glShaderSource(fs, 1, &source, nullptr); glCompileShader(fs); glGetShaderiv(fs, GL_COMPILE_STATUS, &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); if (glGetAttribLocation(ps, "pos") != -1) glBindAttribLocation(ps, 0, "pos"); if (glGetAttribLocation(ps, "uvs") != -1) glBindAttribLocation(ps, 1, "uvs"); if (glGetAttribLocation(ps, "uvs2") != -1) glBindAttribLocation(ps, 2, "uvs2"); if (glGetAttribLocation(ps, "col") != -1) glBindAttribLocation(ps, 3, "col"); if (glGetAttribLocation(ps, "nor") != -1) glBindAttribLocation(ps, 3, "nor"); glLinkProgram(ps); glGetProgramiv(ps, GL_LINK_STATUS, &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, GL_ACTIVE_UNIFORMS, &count); for (int i = 0; i < count; i++) { glGetActiveUniform(ps, (GLuint)i, bufSize, &length, &size, &type, name); name[length] = 0; kShaderUniform id = (kShaderUniform)const_hash(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] { glUseProgram(0); glDeleteProgram(prog); }); prog = 0; } m_umap.clear(); } void Shader::use() { glUseProgram(prog); } 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 glUniform4fv(m_umap[id], 1, glm::value_ptr(v)); } 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 glUniform3fv(m_umap[id], 1, glm::value_ptr(v)); } 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 glUniform2fv(m_umap[id], 1, glm::value_ptr(v)); } 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 glUniformMatrix4fv(m_umap[id], 1, GL_FALSE, glm::value_ptr(m)); } 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 glUniform1i(m_umap[id], i); } void Shader::u_int(const char* name, int i) { glUniform1i(glGetAttribLocation(prog, name), i); } 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 glUniform1f(m_umap[id], f); } GLint Shader::GetAttribLocation(const char* name) { return glGetAttribLocation(prog, name); } 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() { std::vector v = { const_hash("mvp"), const_hash("tex"), const_hash("tex_alpha"), const_hash("tex_fg"), const_hash("tex_bg"), const_hash("tex_mix"), const_hash("tex_mix_alpha"), const_hash("tex_mask"), const_hash("tex_dual"), const_hash("tex_stroke"), const_hash("tex_pattern"), const_hash("pattern_offset"), const_hash("pattern_alpha"), const_hash("mix_alpha"), const_hash("opacity"), const_hash("wet"), const_hash("lock"), const_hash("col"), const_hash("tof"), const_hash("tsz"), const_hash("alpha"), const_hash("mask"), const_hash("resolution"), const_hash("highlight"), const_hash("blend_mode"), const_hash("dual_blend_mode"), const_hash("noise"), const_hash("dir"), const_hash("use_dual"), const_hash("use_pattern"), const_hash("light_dir"), const_hash("mode"), const_hash("ambient"), const_hash("pattern_invert"), const_hash("pattern_scale"), const_hash("pattern_bright"), const_hash("pattern_contr"), const_hash("pattern_depth"), const_hash("patt_blend_mode"), const_hash("colorize"), const_hash("dual_alpha"), const_hash("use_fragcoord"), const_hash("draw_outline"), }; std::sort(v.begin(), v.end()); int last = 0; for (auto o : v) { if (o == last) return false; last = o; } return true; }