/* by korenkonder GitHub/GitLab: korenkonder */ #include "shader.hpp" #include "../KKdLib/io/file_stream.hpp" #include "../KKdLib/io/path.hpp" #include "../KKdLib/prj/shared_ptr.hpp" #include "../KKdLib/prj/vector_pair.hpp" #include "../KKdLib/farc.hpp" #include "../KKdLib/hash.hpp" #include "../KKdLib/str_utils.hpp" #include "Vulkan/gl_wrap.hpp" #include "Vulkan/Manager.hpp" #include "Vulkan/ShaderModule.hpp" #include "gl_rend_state.hpp" #include "render_context.hpp" #include "shared.hpp" #include struct program_binary { GLsizei length; GLenum binary_format; size_t binary; uint64_t hash; }; struct program_spv { size_t size; size_t spv; uint64_t hash; }; #ifdef USE_OPENGL static GLuint shader_compile_shader(GLenum type, const char* data, const char* file); static GLuint shader_compile(const char* vert, const char* frag, const char* vp, const char* fp); static GLuint shader_compile_binary(const char* vert, const char* frag, const char* vp, const char* fp, program_binary* bin, GLsizei* buffer_size, void** binary); #endif static bool shader_load_binary_shader(program_binary* bin, GLuint* program, const char* vp, const char* fp); static prj::shared_ptr shader_load_spv_shader(program_spv* spv, const char* shader); int32_t shader::bind(p_gl_rend_state& p_gl_rend_st, const uniform_value& shader_flags, shader_set_data* set, uint32_t sub_index) { if (num_sub < 1) return -1; int32_t sub_shader_index = 0; for (shader_sub* i = sub; i->sub_index != sub_index; i++) if (++sub_shader_index >= num_sub) return -1; shader_sub* sub_shader = &sub[sub_shader_index]; int32_t unival_shad_curr = 1; int32_t unival_shad = 0; if (num_uniform > 0) { const int32_t* vp_unival_max = sub_shader->vp_unival_max; const int32_t* fp_unival_max = sub_shader->fp_unival_max; for (int32_t i = 0; i < num_uniform; i++) { const int32_t unival = shader_flags.arr[use_uniform[i]]; const int32_t unival_max = max_def(vp_unival_max[i], fp_unival_max[i]); unival_shad += unival_shad_curr * min_def(unival, unival_max); unival_shad_curr *= unival_max + 1; } } GLuint program = sub_shader->programs[unival_shad]; p_gl_rend_st.use_program(program); return 0; } static char* get_uniform_location(char* data, prj::vector_pair& samplers, prj::vector_pair& uniforms, bool apple_fix) { std::string temp(data); free_def(data); size_t version_pos = temp.find("#version 430 core"); if (version_pos != -1) if (GLAD_GL_VERSION_4_2) temp.replace(version_pos, 17, "#version 420 core", 17); else if (GLAD_GL_VERSION_4_1) temp.replace(version_pos, 17, "#version 410 core", 17); if (apple_fix) { size_t result_pos = temp.size() - 1; while ((result_pos = temp.rfind("result_", result_pos)) != -1) temp.replace(result_pos, 7, "frg_", 4); } size_t binding_pos = 0; size_t binding_end_pos = 0; while ((binding_pos = temp.find("layout(set = 0, binding = ", binding_pos)) != -1 && (binding_end_pos = temp.find(") uniform sampler", binding_pos)) != -1) { std::string binding_str = temp.substr(binding_pos + 26, binding_end_pos - (binding_pos + 26)); int32_t binding = atoi(binding_str.c_str()); bool sampler_2d = !temp.compare(binding_end_pos + 10, 9, "sampler2D"); bool sampler_cube = !temp.compare(binding_end_pos + 10, 11, "samplerCube"); if (GLAD_GL_VERSION_4_2) { if (sampler_2d) { char buf[0x40]; sprintf_s(buf, sizeof(buf), "layout(binding = %d) uniform sampler2D", binding); temp.replace(binding_pos, binding_end_pos + 19 - binding_pos, buf); } else if (sampler_cube) { char buf[0x40]; sprintf_s(buf, sizeof(buf), "layout(binding = %d) uniform samplerCube", binding); temp.replace(binding_pos, binding_end_pos + 21 - binding_pos, buf); } } else { if (sampler_2d) { size_t name_pos = binding_end_pos + 19; while (isspace(temp.data()[name_pos])) name_pos++; size_t name_end_pos = temp.find(';', name_pos); if (name_end_pos != -1) { while (isspace(temp.data()[name_end_pos - 1])) name_end_pos--; std::string name_str = temp.substr(name_pos, name_end_pos - name_pos); bool found = false; for (auto& i : samplers) if (i.first == binding && i.second == name_str) { found = true; break; } if (!found) samplers.push_back(binding, name_str); } } else if (sampler_cube) { size_t name_pos = binding_end_pos + 21; while (isspace(temp.data()[name_pos])) name_pos++; size_t name_end_pos = temp.find(';', name_pos); if (name_end_pos != -1) { while (isspace(temp.data()[name_end_pos - 1])) name_end_pos--; std::string name_str = temp.substr(name_pos, name_end_pos - name_pos); bool found = false; for (auto& i : samplers) if (i.first == binding && i.second == name_str) { found = true; break; } if (!found) samplers.push_back(binding, name_str); } } temp.erase(binding_pos, binding_end_pos + 2 - binding_pos); } } binding_pos = 0; binding_end_pos = 0; while ((binding_pos = temp.find("layout(set = 1, binding = ", binding_pos)) != -1 && (binding_end_pos = temp.find(") uniform", binding_pos)) != -1) { std::string binding_str = temp.substr(binding_pos + 26, binding_end_pos - (binding_pos + 26)); int32_t binding = atoi(binding_str.c_str()); if (GLAD_GL_VERSION_4_2) { char buf[0x40]; sprintf_s(buf, sizeof(buf), "layout(binding = %d) uniform", binding); temp.replace(binding_pos, binding_end_pos + 9 - binding_pos, buf); } else { size_t name_pos = binding_end_pos + 9; while (isspace(temp.data()[name_pos])) name_pos++; size_t name_end_pos = temp.find('{', name_pos); if (name_end_pos != -1) { while (isspace(temp.data()[name_end_pos - 1])) name_end_pos--; std::string name_str = temp.substr(name_pos, name_end_pos - name_pos); bool found = false; for (auto& i : uniforms) if (i.first == binding && i.second == name_str) { found = true; break; } if (!found) uniforms.push_back(binding, name_str); } temp.erase(binding_pos, binding_end_pos + 2 - binding_pos); } } binding_pos = 0; binding_end_pos = 0; while ((binding_pos = temp.find("layout(std140, set = 1, binding = ", binding_pos)) != -1 && (binding_end_pos = temp.find(") uniform", binding_pos)) != -1) { std::string binding_str = temp.substr(binding_pos + 34, binding_end_pos - (binding_pos + 34)); int32_t binding = atoi(binding_str.c_str()); if (GLAD_GL_VERSION_4_2) { char buf[0x40]; sprintf_s(buf, sizeof(buf), "layout(binding = %d) uniform", binding); temp.replace(binding_pos, binding_end_pos + 9 - binding_pos, buf); } else { size_t name_pos = binding_end_pos + 9; while (isspace(temp.data()[name_pos])) name_pos++; size_t name_end_pos = temp.find('{', name_pos); if (name_end_pos != -1) { while (isspace(temp.data()[name_end_pos - 1])) name_end_pos--; std::string name_str = temp.substr(name_pos, name_end_pos - name_pos); bool found = false; for (auto& i : uniforms) if (i.first == binding && i.second == name_str) { found = true; break; } if (!found) uniforms.push_back(binding, name_str); } temp.erase(binding_pos, binding_end_pos + 2 - binding_pos); } } binding_pos = 0; binding_end_pos = 0; while ((binding_pos = temp.find("layout(std430, set = 2, binding = ", binding_pos)) != -1 && (binding_end_pos = temp.find(") readonly buffer", binding_pos)) != -1) { std::string binding_str = temp.substr(binding_pos + 34, binding_end_pos - (binding_pos + 34)); int32_t binding = atoi(binding_str.c_str()) + 6; if (GLAD_GL_VERSION_4_2) { char buf[0x40]; sprintf_s(buf, sizeof(buf), "layout(binding = %d) uniform", binding); temp.replace(binding_pos, binding_end_pos + 17 - binding_pos, buf); } else { size_t name_pos = binding_end_pos + 17; while (isspace(temp.data()[name_pos])) name_pos++; size_t name_end_pos = temp.find('{', name_pos); if (name_end_pos != -1) { while (isspace(temp.data()[name_end_pos - 1])) name_end_pos--; std::string name_str = temp.substr(name_pos, name_end_pos - name_pos); bool found = false; for (auto& i : uniforms) if (i.first == binding && i.second == name_str) { found = true; break; } if (!found) uniforms.push_back(binding, name_str); } temp.replace(binding_pos, binding_end_pos + 17 - binding_pos, "uniform", 7); } } size_t in_pos = temp.size() - 1; while ((in_pos = temp.rfind("in const", in_pos)) != -1) temp.replace(in_pos, 8, "const", 5); size_t const_pos = temp.size() - 1; size_t offsets_pos = temp.size() - 1; while ((const_pos = temp.rfind("const ", const_pos)) != -1) { offsets_pos = temp.find("offsets", const_pos); if (offsets_pos == -1 || offsets_pos - const_pos > 20) temp.erase(const_pos, 6); else const_pos--; } size_t use_vertex_attrib_pos = temp.size() - 1; while ((use_vertex_attrib_pos = temp.rfind("USE_VERTEX_ATTRIB", use_vertex_attrib_pos)) != -1) temp.replace(use_vertex_attrib_pos, 17, "(1)", 3); return str_utils_copy(temp.c_str()); } static char* remove_control_flow_attributes(char* data) { char* ext_control_flow_attributes_ptr = strstr(data, "#extension GL_EXT_control_flow_attributes : require"); if (!ext_control_flow_attributes_ptr) return data; size_t ext_control_flow_attributes_pos = ext_control_flow_attributes_ptr - data; std::string temp(data); free_def(data); size_t ext_control_flow_attributes_end_pos = temp.find('\n', ext_control_flow_attributes_pos); if (ext_control_flow_attributes_end_pos != -1) { ext_control_flow_attributes_end_pos++; temp.replace(ext_control_flow_attributes_pos, ext_control_flow_attributes_end_pos - ext_control_flow_attributes_pos, "#pragma optionNV(unroll all)"); } size_t attribute_pos = 0; while ((attribute_pos = temp.find("[[unroll]]", attribute_pos)) != -1) { size_t attribute_end_pos = attribute_pos + 10; while (isspace(temp.data()[attribute_end_pos])) attribute_end_pos++; temp.erase(attribute_pos, attribute_end_pos - attribute_pos); } return str_utils_copy(temp.c_str()); } static char* replace_skinning_with_g_skinning(char* data) { char* buffer_skinning_ptr = strstr(data, "layout(std430, set = 2, binding = 0) readonly buffer Skinning"); if (!buffer_skinning_ptr) return data; char* apply_skinning_ptr = strstr(buffer_skinning_ptr, "vec3 apply_skinning(in const vec3 data," " in const ivec4 mtxidx, in const vec4 weight)"); if (!apply_skinning_ptr) return data; size_t buffer_skinning_pos = buffer_skinning_ptr - data; size_t apply_skinning_pos = apply_skinning_ptr - data; const char replacement[] = "#define skinning_offset ivec2(g_bump_depth.zw)\n" "layout(binding = 21) uniform sampler2D g_skinning;\n" "\n" "vec3 apply_skinning(in const vec3 data, in const int mtxidx_comp) {\n" " const ivec3 mtxidx_row = ivec3(mtxidx_comp * 3) + ivec3(0, 1, 2);\n" "\n" " return vec3(\n" " dot(data, texelFetch(g_skinning, ivec2(mtxidx_row.x, 0) + skinning_offset, 0).xyz),\n" " dot(data, texelFetch(g_skinning, ivec2(mtxidx_row.y, 0) + skinning_offset, 0).xyz),\n" " dot(data, texelFetch(g_skinning, ivec2(mtxidx_row.z, 0) + skinning_offset, 0).xyz)\n" " );\n" "}\n" "\n" "vec3 apply_skinning(in const vec4 data, in const int mtxidx_comp) {\n" " const ivec3 mtxidx_row = ivec3(mtxidx_comp * 3) + ivec3(0, 1, 2);\n" "\n" " return vec3(\n" " dot(data, texelFetch(g_skinning, ivec2(mtxidx_row.x, 0) + skinning_offset, 0)),\n" " dot(data, texelFetch(g_skinning, ivec2(mtxidx_row.y, 0) + skinning_offset, 0)),\n" " dot(data, texelFetch(g_skinning, ivec2(mtxidx_row.z, 0) + skinning_offset, 0))\n" " );\n" "}\n" "\n"; const size_t replacement_len = sizeof(replacement) - 1; size_t data_len = utf8_length(data); char* p = force_malloc(data_len - (apply_skinning_pos - buffer_skinning_pos) + replacement_len + 1); memcpy(p, data, buffer_skinning_pos); memcpy(p + buffer_skinning_pos, replacement, replacement_len); memcpy(p + buffer_skinning_pos + replacement_len, data + apply_skinning_pos, data_len - apply_skinning_pos); free_def(data); return p; } static void parse_define_inner(std::string& temp, bool vulkan) { if (!vulkan) { size_t off = 0; while (true) { size_t pos_set = temp.find("set = ", off); if (pos_set == -1) break; if (pos_set > 0 && !(temp.data()[pos_set - 1] == '(' || isspace(temp.data()[pos_set - 1]))) { off = pos_set + 6; continue; } size_t pos_binding = temp.find("binding = ", pos_set); if (pos_binding == -1) break; if (pos_binding - pos_set > 10) { off = pos_binding + 10; continue; } temp.erase(pos_set, pos_binding - pos_set); off = pos_set + 10; } } else { size_t off = 0; while (true) { size_t pos_result_position = temp.find("result_position = ", off); if (pos_result_position == -1) break; size_t pos_end = temp.find(";", pos_result_position + 18); if (pos_end == -1) break; temp.insert(pos_end + 1, " result_position.z = (result_position.z + result_position.w) * 0.5;"); off = pos_end + 67; } off = 0; while (true) { size_t pos = temp.find("gl_VertexID", off); if (pos == -1) break; temp.replace(pos, 11, "gl_VertexIndex"); off = pos + 14; } off = 0; while (true) { size_t pos = temp.find("layout(set = 1, binding = 0) uniform Shader", off); if (pos == -1) break; temp.replace(pos, 43, "layout(push_constant) uniform Shader"); off = pos + 36; } } } bool shader::parse_define(const char* data, std::string& temp, bool vulkan) { if (!data) return false; const char* def = strstr(data, "//DEF\n"); if (def) { temp.clear(); size_t len_a = def - data; def += 5; if (*def == '\n') def++; size_t len_b = utf8_length(def); size_t pos = 0; temp.insert(pos, data, len_a); pos += len_a; temp.insert(pos, def, len_b); pos += len_b; } else temp.assign(data); if (GLAD_GL_VERSION_4_3) parse_define_inner(temp, vulkan); return true; } bool shader::parse_define(const char* data, int32_t num_uniform, int32_t* uniform_value, std::string& temp, bool vulkan) { if (!data) return false; const char* def = strstr(data, "//DEF\n"); if (def) { temp.clear(); size_t len_a = def - data; def += 5; if (*def == '\n') def++; size_t len_b = utf8_length(def); size_t pos = 0; temp.insert(pos, data, len_a); pos += len_a; for (int32_t i = 0; i < num_uniform; i++) { char t[0x100]; int32_t len = sprintf_s(t, sizeof(t), "#define _%d %d\n", i, uniform_value[i]); temp.insert(pos, t, len); pos += len; } temp.insert(pos, def, len_b); pos += len_b; } else temp.assign(data); parse_define_inner(temp, vulkan); return true; } char* shader::parse_include(char* data, struct farc* f) { if (!data || !f) return data; char* data_end = data + utf8_length(data); char* i0 = strstr(data, "#include \""); char* i1 = i0 ? strstr(i0, "\"\n") : 0; if (!i0 || !i1) return data; size_t count = 1; while (i1 && (i0 = strstr(i1, "#include \""))) { i0 += 10; i1 = strstr(i0, "\"\n"); if (i1) i1 += 1; count++; } char** temp = force_malloc(count); size_t* temp_len = force_malloc(count); char** temp_ptr0 = force_malloc(count); char** temp_ptr1 = force_malloc(count); if (!temp || !temp_len || !temp_ptr0 || !temp_ptr1) { free_def(temp); free_def(temp_len); free_def(temp_ptr0); free_def(temp_ptr1); return data; } i1 = data; for (size_t i = 0; i < count; i++) { temp[i] = 0; i0 = i1 ? strstr(i1, "#include \"") : 0; i1 = i0 ? strstr(i0, "\"\n") : 0; if (!i0 || !i1) continue; temp_ptr0[i] = i0; temp_ptr1[i] = i1 + 1; i0 += 10; size_t s = i1 - i0; i1 += 2; char* t = force_malloc(s + 1); if (!t) continue; memcpy(t, i0, s); t[s] = 0; farc_file* ff = f->read_file(t); free_def(t); if (!ff) continue; t = force_malloc(ff->size + 1); if (t) { memcpy(t, ff->data, ff->size); t[ff->size] = 0; } temp[i] = t; temp_len[i] = ff->size; } size_t len = data_end - data; i1 = data; for (size_t i = 0; i < count; i++) { i0 = temp_ptr0[i]; i1 = temp_ptr1[i]; if (!i0 || !i1) continue; len -= i1 - i0; len += temp_len[i]; } char* temp_data = force_malloc(len + 1); size_t pos = 0; memcpy(temp_data + pos, data, temp_ptr0[0] - data); pos += temp_ptr0[0] - data; for (int32_t i = 0; i < count; i++) { if (temp[i]) { size_t s = temp_len[i]; memcpy(temp_data + pos, temp[i], s); pos += s; } if (i < count - 1 && temp_ptr1[i]) { size_t s = temp_ptr0[i + 1] - temp_ptr1[i]; memcpy(temp_data + pos, temp_ptr1[i], s); pos += s; } else if (temp_ptr1[i]) { size_t s = data_end - temp_ptr1[i]; memcpy(temp_data + pos, temp_ptr1[i], s); pos += s; } } temp_data[pos] = 0; free_def(data); for (size_t i = 0; i < count; i++) free_def(temp[i]); free_def(temp); free_def(temp_len); free_def(temp_ptr0); free_def(temp_ptr1); return temp_data; } void shader::unbind(p_gl_rend_state& p_gl_rend_st) { p_gl_rend_st.use_program(0); } shader_set_data::shader_set_data() : size(), shaders(), get_index_by_name_func(), get_name_by_index_func() { } int32_t shader_set_data::get_index_by_name(const char* name) { if (get_index_by_name_func) { int32_t index = get_index_by_name_func(name); if (index != -1) return index; } for (size_t i = 0; i < size; i++) if (!str_utils_compare(shaders[i].name, name)) return (int32_t)shaders[i].name_index; return -1; } const char* shader_set_data::get_name_by_index(int32_t index) { if (get_name_by_index_func) { const char* name = get_name_by_index_func(index); if (name) return name; } if (index >= 0 && index < size) return shaders[index].name; return 0; } void shader_set_data::load(struct farc* f, bool ignore_cache, const char* name, const shader_table* shaders_table, const size_t size, const shader_bind_func* bind_func_table, const size_t bind_func_table_size, PFNSHADERGETINDEXFUNCPROC get_index_by_name, PFNSHADERGETNAMEFUNCPROC get_name_by_index) { if (!this || !f || !shaders_table || !size) return; if (Vulkan::use) { char vert_buf[MAX_PATH]; char frag_buf[MAX_PATH]; char vert_file_buf[MAX_PATH]; char frag_file_buf[MAX_PATH]; char vert_bin_buf[MAX_PATH]; char frag_bin_buf[MAX_PATH]; prj::vector_pair> vec_shader_module; shader* shaders = force_malloc(size); this->shaders = shaders; this->size = size; for (size_t i = 0; i < size; i++) { shader* shader = &shaders[i]; shader->name = shaders_table[i].name; shader->name_index = shaders_table[i].name_index; shader->num_sub = shaders_table[i].num_sub; shader->sub = force_malloc(shader->num_sub); shader->num_uniform = shaders_table[i].num_uniform; shader->use_uniform = shaders_table[i].use_uniform; int32_t num_sub = shader->num_sub; const shader_sub_table* sub_table = shaders_table[i].sub; shader_sub* sub = shader->sub; const uniform_name* use_uniform = shader->use_uniform; for (int32_t j = 0; j < num_sub; j++, sub++, sub_table++) { sub->sub_index = sub_table->sub_index; sub->vp_unival_max = sub_table->vp_unival_max; sub->fp_unival_max = sub_table->fp_unival_max; strcpy_s(vert_file_buf, sizeof(vert_file_buf), sub_table->vp); strcat_s(vert_file_buf, sizeof(vert_file_buf), ".vert"); strcpy_s(frag_file_buf, sizeof(frag_file_buf), sub_table->fp); strcat_s(frag_file_buf, sizeof(frag_file_buf), ".frag"); sub->vp_desc = sub_table->vp_desc; sub->fp_desc = sub_table->fp_desc; uint32_t uniform_vert_flags = 0; uint32_t uniform_frag_flags = 0; for (int32_t k = 0; k < shader->num_uniform; k++) { if (sub_table->vp_unival_max[k] > 0) uniform_vert_flags |= 1 << k; if (sub_table->fp_unival_max[k] > 0) uniform_frag_flags |= 1 << k; } char uniform_vert_flags_buf[9]; char uniform_frag_flags_buf[9]; for (int32_t k = 0, l = 7; k < 32; k += 4, l--) { int32_t vert_flags_digit = (uniform_vert_flags >> k) & 0x0F; if (vert_flags_digit >= 0x00 && vert_flags_digit <= 0x09) uniform_vert_flags_buf[l] = (char)('0' + vert_flags_digit); else uniform_vert_flags_buf[l] = (char)('A' + (vert_flags_digit - 0x0A)); int32_t frag_flags_digit = (uniform_frag_flags >> k) & 0x0F; if (frag_flags_digit >= 0x00 && frag_flags_digit <= 0x09) uniform_frag_flags_buf[l] = (char)('0' + frag_flags_digit); else uniform_frag_flags_buf[l] = (char)('A' + (frag_flags_digit - 0x0A)); } uniform_vert_flags_buf[8] = 0; uniform_frag_flags_buf[8] = 0; strcpy_s(vert_buf, sizeof(vert_buf), vert_file_buf); strcpy_s(frag_buf, sizeof(frag_buf), frag_file_buf); strcat_s(vert_buf, sizeof(vert_buf), "."); strcat_s(frag_buf, sizeof(frag_buf), "."); strcat_s(vert_buf, sizeof(vert_buf), uniform_vert_flags_buf); strcat_s(frag_buf, sizeof(frag_buf), uniform_frag_flags_buf); uint64_t vert_file_name_hash = hash_utf8_xxh3_64bits(vert_buf); uint64_t frag_file_name_hash = hash_utf8_xxh3_64bits(frag_buf); strcpy_s(vert_bin_buf, sizeof(vert_bin_buf), sub_table->vp); strcat_s(vert_bin_buf, sizeof(vert_bin_buf), ".vert.bin"); strcpy_s(frag_bin_buf, sizeof(frag_bin_buf), sub_table->fp); strcat_s(frag_bin_buf, sizeof(frag_bin_buf), ".frag.bin"); farc_file* shader_vert_file = f->read_file(vert_bin_buf); program_spv* vert_spv = 0; if (!shader_vert_file || !shader_vert_file->data) printf_debug("Vertex shader not found: %s\n", vert_bin_buf); else if (vert_file_name_hash != ((uint64_t*)shader_vert_file->data)[0]) printf_debug("Vertex shader flags not equal: %s\n", vert_file_buf); else vert_spv = (program_spv*)&((uint64_t*)shader_vert_file->data)[1]; farc_file* shader_frag_file = f->read_file(frag_bin_buf); program_spv* frag_spv = 0; if (!shader_frag_file || !shader_frag_file->data) printf_debug("Fragment shader not found: %s\n", frag_bin_buf); else if (frag_file_name_hash != ((uint64_t*)shader_frag_file->data)[0]) printf_debug("Fragment shader flags not equal: %s\n", frag_file_buf); else frag_spv = (program_spv*)&((uint64_t*)shader_frag_file->data)[1]; if (shader->num_uniform > 0 && (sub_table->vp_unival_max[0] != -1 || sub_table->fp_unival_max[0] != -1)) { int32_t num_uniform = shader->num_uniform; int32_t unival_shad_curr = 1; int32_t unival_vp_curr = 1; int32_t unival_fp_curr = 1; int32_t unival_shad_count = 1; int32_t unival_vp_count = 1; int32_t unival_fp_count = 1; const int32_t* vp_unival_max = sub_table->vp_unival_max; const int32_t* fp_unival_max = sub_table->fp_unival_max; for (int32_t k = 0; k < num_uniform; k++) { const int32_t unival_max = max_def(vp_unival_max[k], fp_unival_max[k]); const int32_t unival_vp_max = vp_unival_max[k]; const int32_t unival_fp_max = fp_unival_max[k]; unival_shad_count += unival_shad_curr * unival_max; unival_vp_count += unival_vp_curr * unival_vp_max; unival_fp_count += unival_fp_curr * unival_fp_max; unival_shad_curr *= unival_max + 1; unival_vp_curr *= unival_vp_max + 1; unival_fp_curr *= unival_fp_max + 1; } GLuint* programs = force_malloc(unival_shad_count); sub->programs = programs; if (programs) { strcpy_s(vert_buf, sizeof(vert_buf), sub_table->vp); size_t vert_buf_pos = utf8_length(vert_buf); vert_buf[vert_buf_pos++] = '.'; vert_buf[vert_buf_pos] = 0; memset(&vert_buf[vert_buf_pos], '0', num_uniform); vert_buf[vert_buf_pos + num_uniform] = 0; strcat_s(vert_buf, sizeof(vert_buf), ".vert"); strcpy_s(frag_buf, sizeof(frag_buf), sub_table->fp); size_t frag_buf_pos = utf8_length(frag_buf); frag_buf[frag_buf_pos++] = '.'; frag_buf[frag_buf_pos] = 0; memset(&frag_buf[frag_buf_pos], '0', num_uniform); frag_buf[frag_buf_pos + num_uniform] = 0; strcat_s(frag_buf, sizeof(frag_buf), ".frag"); vec_shader_module.reserve((size_t)unival_vp_count + unival_fp_count); for (int32_t k = 0; k < unival_vp_count; k++) { uint64_t vp_hash = 0x00; for (int32_t l = 0, m = k; l < num_uniform; l++) { int32_t unival_max = vp_unival_max[l] + 1; int32_t vp_digit = m % unival_max; vp_hash = (vp_hash << 3) | (uint32_t)vp_digit; m /= unival_max; vert_buf[vert_buf_pos + l] = (char)('0' + vp_digit); } prj::shared_ptr vp_shader_module; if (vert_spv) { vp_shader_module = shader_load_spv_shader(&vert_spv[k], vert_buf); if (!vp_shader_module) vp_shader_module.reset(); } vec_shader_module.push_back(vp_hash, vp_shader_module); } for (int32_t k = 0; k < unival_fp_count; k++) { uint64_t fp_hash = 0x01; for (int32_t l = 0, m = k; l < num_uniform; l++) { int32_t unival_max = fp_unival_max[l] + 1; int32_t fp_digit = m % unival_max; fp_hash = (fp_hash << 3) | (uint32_t)fp_digit; m /= unival_max; frag_buf[frag_buf_pos + l] = (char)('0' + fp_digit); } prj::shared_ptr fp_shader_module; if (frag_spv) { fp_shader_module = shader_load_spv_shader(&frag_spv[k], frag_buf); if (!fp_shader_module) fp_shader_module.reset(); } vec_shader_module.push_back(fp_hash, fp_shader_module); } vec_shader_module.sort(); for (int32_t k = 0; k < unival_shad_count; k++) { uint64_t vp_hash = 0x00; uint64_t fp_hash = 0x01; uint32_t unival_arr[0x20] = {}; for (int32_t l = 0, m = k; l < num_uniform; l++) { const int32_t unival_max = max_def(vp_unival_max[l], fp_unival_max[l]) + 1; const int32_t unival = m % unival_max; vp_hash = (vp_hash << 3) | (uint32_t)min_def(unival, vp_unival_max[l]); fp_hash = (fp_hash << 3) | (uint32_t)min_def(unival, fp_unival_max[l]); unival_arr[l] = unival; m /= unival_max; } auto elem_vp = vec_shader_module.find(vp_hash); auto elem_fp = vec_shader_module.find(fp_hash); if (elem_vp != vec_shader_module.end() && elem_fp != vec_shader_module.end() && elem_vp->second && elem_fp->second) { programs[k] = glCreateProgram(); Vulkan::gl_program* vk_program = Vulkan::gl_program::get(programs[k]); vk_program->vertex_shader_module = elem_vp->second; vk_program->fragment_shader_module = elem_fp->second; vk_program->shader = &shaders_table[i]; vk_program->sub_shader = sub_table; static_assert(sizeof(vk_program->unival_arr) == sizeof(unival_arr), "\"unival_arr\" field should be 0x80"); memcpy(vk_program->unival_arr, unival_arr, sizeof(vk_program->unival_arr)); vk_program->init(); } } vec_shader_module.clear(); } } else { GLuint* programs = force_malloc(); sub->programs = programs; if (programs) { strcpy_s(vert_buf, sizeof(vert_buf), sub_table->vp); strcpy_s(frag_buf, sizeof(vert_buf), sub_table->fp); strcat_s(vert_buf, sizeof(vert_buf), "..vert"); strcat_s(frag_buf, sizeof(vert_buf), "..frag"); prj::shared_ptr vp_shader_module; if (vert_spv) { vp_shader_module = shader_load_spv_shader(&vert_spv[0], vert_buf); if (!vp_shader_module) vp_shader_module.reset(); } prj::shared_ptr fp_shader_module; if (frag_spv) { fp_shader_module = shader_load_spv_shader(&frag_spv[0], frag_buf); if (!fp_shader_module) fp_shader_module.reset(); } if (vp_shader_module && fp_shader_module) { programs[0] = glCreateProgram(); Vulkan::gl_program* vk_program = Vulkan::gl_program::get(programs[0]); vk_program->vertex_shader_module = vp_shader_module; vk_program->fragment_shader_module = fp_shader_module; vk_program->shader = &shaders_table[i]; vk_program->sub_shader = sub_table; memset(vk_program->unival_arr, 0, sizeof(vk_program->unival_arr)); vk_program->init(); } } } } for (size_t j = 0; j < bind_func_table_size; j++) if (shader->name_index == bind_func_table[j].name_index) { shader->bind_func = bind_func_table[j].bind_func; break; } } } #ifdef USE_OPENGL else { wchar_t temp_buf[MAX_PATH]; if (FAILED(SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, 0, temp_buf))) return; const bool apple = sv_gpu_vendor == GPU_VENDOR_APPLE; if (strstr((const char*)glGetString(GL_VERSION), "Mesa")) ignore_cache = true; wcscat_s(temp_buf, sizeof(temp_buf) / sizeof(wchar_t), L"\\ReDIVA"); path_create_directory(temp_buf); wchar_t buf[MAX_PATH]; swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"\\%hs_shader_cache", name); wcscat_s(temp_buf, sizeof(temp_buf) / sizeof(wchar_t), buf); bool shader_cache_changed = false; farc shader_cache_farc; swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"%ls.farc", temp_buf); if (!ignore_cache && path_check_file_exists(buf)) shader_cache_farc.read(buf, true, false); char vert_buf[MAX_PATH]; char frag_buf[MAX_PATH]; char vert_file_buf[MAX_PATH]; char frag_file_buf[MAX_PATH]; char shader_cache_file_name[MAX_PATH]; GLsizei buffer_size = 0x100000; void* binary = force_malloc(buffer_size); std::string temp_vert; std::string temp_frag; std::vector vec_vert; std::vector vec_frag; std::vector program_data_binary; shader* shaders = force_malloc(size); this->shaders = shaders; this->size = size; for (size_t i = 0; i < size; i++) { shader* shader = &shaders[i]; shader->name = shaders_table[i].name; shader->name_index = shaders_table[i].name_index; shader->num_sub = shaders_table[i].num_sub; shader->sub = force_malloc(shader->num_sub); shader->num_uniform = shaders_table[i].num_uniform; shader->use_uniform = shaders_table[i].use_uniform; int32_t num_sub = shader->num_sub; const shader_sub_table* sub_table = shaders_table[i].sub; shader_sub* sub = shader->sub; vec_vert.resize(shader->num_uniform); vec_frag.resize(shader->num_uniform); int32_t* vec_vert_data = vec_vert.data(); int32_t* vec_frag_data = vec_frag.data(); for (int32_t j = 0; j < num_sub; j++, sub++, sub_table++) { sub->sub_index = sub_table->sub_index; sub->vp_unival_max = sub_table->vp_unival_max; sub->fp_unival_max = sub_table->fp_unival_max; strcpy_s(vert_file_buf, sizeof(vert_file_buf), sub_table->vp); strcat_s(vert_file_buf, sizeof(vert_file_buf), ".vert"); farc_file* vert_ff = f->read_file(vert_file_buf); char* vert_data = 0; if (vert_ff && vert_ff->data) { vert_data = force_malloc(vert_ff->size + 1); if (vert_data) { memcpy(vert_data, vert_ff->data, vert_ff->size); vert_data[vert_ff->size] = 0; } } strcpy_s(frag_file_buf, sizeof(frag_file_buf), sub_table->fp); strcat_s(frag_file_buf, sizeof(frag_file_buf), ".frag"); farc_file* frag_ff = f->read_file(frag_file_buf); char* frag_data = 0; if (frag_ff && frag_ff->data) { frag_data = force_malloc(frag_ff->size + 1); if (frag_data) { memcpy(frag_data, frag_ff->data, frag_ff->size); frag_data[frag_ff->size] = 0; } } if (!vert_data || !frag_data) { free_def(vert_data); free_def(frag_data); continue; } uint32_t uniform_vert_flags = 0; uint32_t uniform_frag_flags = 0; for (int32_t k = 0; k < shader->num_uniform; k++) { if (sub_table->vp_unival_max[k] > 0) uniform_vert_flags |= 1 << k; if (sub_table->fp_unival_max[k] > 0) uniform_frag_flags |= 1 << k; } char uniform_vert_flags_buf[9]; char uniform_frag_flags_buf[9]; for (int32_t k = 0, l = 7; k < 32; k += 4, l--) { int32_t vert_flags_digit = (uniform_vert_flags >> k) & 0x0F; if (vert_flags_digit >= 0x00 && vert_flags_digit <= 0x09) uniform_vert_flags_buf[l] = (char)('0' + vert_flags_digit); else uniform_vert_flags_buf[l] = (char)('A' + (vert_flags_digit - 0x0A)); int32_t frag_flags_digit = (uniform_frag_flags >> k) & 0x0F; if (frag_flags_digit >= 0x00 && frag_flags_digit <= 0x09) uniform_frag_flags_buf[l] = (char)('0' + frag_flags_digit); else uniform_frag_flags_buf[l] = (char)('A' + (frag_flags_digit - 0x0A)); } uniform_vert_flags_buf[8] = 0; uniform_frag_flags_buf[8] = 0; strcpy_s(vert_buf, sizeof(vert_buf), vert_file_buf); strcpy_s(frag_buf, sizeof(frag_buf), frag_file_buf); strcat_s(vert_buf, sizeof(vert_buf), "."); strcat_s(frag_buf, sizeof(frag_buf), "."); strcat_s(vert_buf, sizeof(vert_buf), uniform_vert_flags_buf); strcat_s(frag_buf, sizeof(frag_buf), uniform_frag_flags_buf); uint64_t vert_file_name_hash = hash_utf8_xxh3_64bits(vert_buf); uint64_t frag_file_name_hash = hash_utf8_xxh3_64bits(frag_buf); strcpy_s(shader_cache_file_name, sizeof(shader_cache_file_name), sub_table->vp); if (str_utils_compare(sub_table->vp, sub_table->fp)) { strcat_s(shader_cache_file_name, sizeof(shader_cache_file_name), "."); strcat_s(shader_cache_file_name, sizeof(shader_cache_file_name), sub_table->fp); } strcat_s(shader_cache_file_name, sizeof(shader_cache_file_name), ".bin"); vert_data = shader::parse_include(vert_data, f); frag_data = shader::parse_include(frag_data, f); vert_data = remove_control_flow_attributes(vert_data); frag_data = remove_control_flow_attributes(frag_data); if (sv_texture_skinning_buffer) { vert_data = replace_skinning_with_g_skinning(vert_data); frag_data = replace_skinning_with_g_skinning(frag_data); } prj::vector_pair samplers; prj::vector_pair uniforms; if (!GLAD_GL_VERSION_4_3) { vert_data = get_uniform_location(vert_data, samplers, uniforms, apple); frag_data = get_uniform_location(frag_data, samplers, uniforms, apple); } uint64_t vert_data_hash = hash_utf8_xxh3_64bits(vert_data); uint64_t frag_data_hash = hash_utf8_xxh3_64bits(frag_data); farc_file* shader_cache_file = shader_cache_farc.read_file(shader_cache_file_name); program_binary* bin = 0; if (!ignore_cache) { if (!shader_cache_file || !shader_cache_file->data) printf_debug("Shader not compiled: %s %s\n", vert_file_buf, frag_file_buf); else if (vert_file_name_hash != ((uint64_t*)shader_cache_file->data)[0] || frag_file_name_hash != ((uint64_t*)shader_cache_file->data)[1]) printf_debug("Shader flags not equal: %s %s\n", vert_file_buf, frag_file_buf); else if (vert_data_hash != ((uint64_t*)shader_cache_file->data)[2] || frag_data_hash != ((uint64_t*)shader_cache_file->data)[3]) printf_debug("Shader hash not equal: %s %s\n", vert_file_buf, frag_file_buf); else bin = (program_binary*)&((uint64_t*)shader_cache_file->data)[4]; } if (shader->num_uniform > 0 && (sub_table->vp_unival_max[0] != -1 || sub_table->fp_unival_max[0] != -1)) { int32_t num_uniform = shader->num_uniform; int32_t unival_shad_curr = 1; int32_t unival_shad_count = 1; const int32_t* vp_unival_max = sub_table->vp_unival_max; const int32_t* fp_unival_max = sub_table->fp_unival_max; for (int32_t k = 0; k < num_uniform; k++) { const int32_t unival_max = max_def(vp_unival_max[k], fp_unival_max[k]); unival_shad_count += unival_shad_curr * unival_max; unival_shad_curr *= unival_max + 1; } if (!ignore_cache) program_data_binary.reserve(unival_shad_count); GLuint* programs = force_malloc(unival_shad_count); sub->programs = programs; if (programs) { strcpy_s(vert_buf, sizeof(vert_buf), sub_table->vp); size_t vert_buf_pos = utf8_length(vert_buf); vert_buf[vert_buf_pos++] = '.'; vert_buf[vert_buf_pos] = 0; memset(&vert_buf[vert_buf_pos], '0', num_uniform); vert_buf[vert_buf_pos + num_uniform] = 0; strcat_s(vert_buf, sizeof(vert_buf), ".vert"); strcpy_s(frag_buf, sizeof(frag_buf), sub_table->fp); size_t frag_buf_pos = utf8_length(frag_buf); frag_buf[frag_buf_pos++] = '.'; frag_buf[frag_buf_pos] = 0; memset(&frag_buf[frag_buf_pos], '0', num_uniform); frag_buf[frag_buf_pos + num_uniform] = 0; strcat_s(frag_buf, sizeof(frag_buf), ".frag"); for (int32_t k = 0; k < unival_shad_count; k++) { for (int32_t l = 0, m = k; l < num_uniform; l++) { int32_t unival_max = max_def(vp_unival_max[l], fp_unival_max[l]) + 1; vec_vert_data[l] = min_def(m % unival_max, vp_unival_max[l]); m /= unival_max; vert_buf[vert_buf_pos + l] = (char)('0' + vec_vert_data[l]); } for (int32_t l = 0, m = k; l < num_uniform; l++) { int32_t unival_max = max_def(vp_unival_max[l], fp_unival_max[l]) + 1; vec_frag_data[l] = min_def(m % unival_max, fp_unival_max[l]); m /= unival_max; frag_buf[frag_buf_pos + l] = (char)('0' + vec_frag_data[l]); } if (!bin || !bin->binary_format || !bin->length || !shader_load_binary_shader(bin, &programs[k], vert_buf, frag_buf)) { bool vert_succ = shader::parse_define(vert_data, num_uniform, vec_vert_data, temp_vert); bool frag_succ = shader::parse_define(frag_data, num_uniform, vec_frag_data, temp_frag); if (ignore_cache) programs[k] = shader_compile(vert_succ ? temp_vert.c_str() : 0, frag_succ ? temp_frag.c_str() : 0, vert_buf, frag_buf); else { program_data_binary.push_back({}); programs[k] = shader_compile_binary(vert_succ ? temp_vert.c_str() : 0, frag_succ ? temp_frag.c_str() : 0, vert_buf, frag_buf, &program_data_binary.back(), &buffer_size, &binary); shader_cache_changed |= !!programs[k]; } } else { program_data_binary.push_back({}); program_binary* b = &program_data_binary.back(); b->length = bin->length; b->binary_format = bin->binary_format; b->binary = (size_t)force_malloc(bin->length); memcpy((void*)b->binary, (void*)((size_t)bin + bin->binary), bin->length); } if (!GLAD_GL_VERSION_4_3) { if (programs[k] && samplers.size()) { GLuint program = programs[k]; glUseProgram(program); for (auto& i : samplers) { GLint loc = glGetUniformLocation(program, i.second.c_str()); if (loc != -1) glUniform1i(loc, i.first); } glUseProgram(0); } if (programs[k] && uniforms.size()) { GLuint program = programs[k]; for (auto& i : uniforms) { GLuint loc = glGetUniformBlockIndex(program, i.second.c_str()); if (loc != -1) glUniformBlockBinding(program, loc, i.first); } } } if (!ignore_cache && bin) bin++; } } } else { program_data_binary.reserve(1); GLuint* programs = force_malloc(); sub->programs = programs; if (programs) { strcpy_s(vert_buf, sizeof(vert_buf), sub_table->vp); strcpy_s(frag_buf, sizeof(vert_buf), sub_table->fp); strcat_s(vert_buf, sizeof(vert_buf), "..vert"); strcat_s(frag_buf, sizeof(vert_buf), "..frag"); if (!bin || !bin->binary_format || !bin->length || !shader_load_binary_shader(bin, &programs[0], vert_buf, frag_buf)) { bool vert_succ = shader::parse_define(vert_data, temp_vert); bool frag_succ = shader::parse_define(frag_data, temp_frag); if (ignore_cache) programs[0] = shader_compile(vert_succ ? temp_vert.c_str() : 0, frag_succ ? temp_frag.c_str() : 0, vert_buf, frag_buf); else { program_data_binary.push_back({}); programs[0] = shader_compile_binary(vert_succ ? temp_vert.c_str() : 0, frag_succ ? temp_frag.c_str() : 0, vert_buf, frag_buf, &program_data_binary.back(), &buffer_size, &binary); shader_cache_changed |= !!programs[0]; } } else { program_data_binary.push_back({}); program_binary* b = &program_data_binary.back(); b->length = bin->length; b->binary_format = bin->binary_format; b->binary = (size_t)force_malloc(bin->length); memcpy((void*)b->binary, (void*)((size_t)bin + bin->binary), bin->length); } if (!GLAD_GL_VERSION_4_3) { if (programs[0] && samplers.size()) { GLuint program = programs[0]; glUseProgram(program); for (auto& i : samplers) { GLint loc = glGetUniformLocation(program, i.second.c_str()); if (loc != -1) glUniform1i(loc, i.first); } glUseProgram(0); } if (programs[0] && uniforms.size()) { GLuint program = programs[0]; for (auto& i : uniforms) { GLint loc = glGetUniformBlockIndex(program, i.second.c_str()); if (loc != -1) glUniformBlockBinding(program, loc, i.first); } } } if (!ignore_cache && bin) bin++; } } if (!ignore_cache) { if (!shader_cache_file) shader_cache_file = shader_cache_farc.add_file(shader_cache_file_name); else free_def(shader_cache_file->data); size_t bin_count = program_data_binary.size(); size_t bin_size = sizeof(uint64_t) * 4 + bin_count * sizeof(program_binary); for (program_binary& k : program_data_binary) bin_size += align_val(k.length, 0x04); shader_cache_file->data = force_malloc(bin_size); shader_cache_file->size = bin_size; shader_cache_file->compressed = true; shader_cache_file->data_changed = true; ((uint64_t*)shader_cache_file->data)[0] = vert_file_name_hash; ((uint64_t*)shader_cache_file->data)[1] = frag_file_name_hash; ((uint64_t*)shader_cache_file->data)[2] = vert_data_hash; ((uint64_t*)shader_cache_file->data)[3] = frag_data_hash; bin = (program_binary*)&((uint64_t*)shader_cache_file->data)[4]; size_t bin_data_base = (size_t)shader_cache_file->data + sizeof(uint64_t) * 4; size_t bin_data = bin_data_base + bin_count * sizeof(program_binary); for (program_binary& k : program_data_binary) { bin->length = k.length; bin->binary_format = k.binary_format; bin->binary = bin_data - bin_data_base; bin->hash = hash_xxh3_64bits((void*)k.binary, k.length); memcpy((void*)bin_data, (void*)k.binary, k.length); bin_data_base += sizeof(program_binary); bin_data += align_val(k.length, 0x04); void* binary = (void*)k.binary; free_def(binary); k.binary = 0; bin++; } } free_def(vert_data); free_def(frag_data); program_data_binary.clear(); } vec_vert.clear(); vec_frag.clear(); for (size_t j = 0; j < bind_func_table_size; j++) if (shader->name_index == bind_func_table[j].name_index) { shader->bind_func = bind_func_table[j].bind_func; break; } } free_def(binary); if (!ignore_cache && shader_cache_changed) shader_cache_farc.write(temp_buf, FARC_FArC, FARC_NONE, true, false); } #endif this->get_index_by_name_func = get_index_by_name; this->get_name_by_index_func = get_name_by_index; } void shader_set_data::set(p_gl_rend_state& p_gl_rend_st, uniform_value& shader_flags, uint32_t index) { if (this && index && index != -1) { shader* shader = &shaders[index]; if (shader->bind_func) shader->bind_func(p_gl_rend_st, shader_flags, this, shader); else shader->bind(p_gl_rend_st, shader_flags, this, shader->sub[0].sub_index); } else shader::unbind(p_gl_rend_st); } void shader_set_data::unload() { size_t size = this->size; shader* shaders = this->shaders; for (size_t i = 0; i < size; i++) { shader* shader = &shaders[i]; if (!shader->sub) continue; int32_t num_sub = shader->num_sub; shader_sub* sub = shader->sub; for (int32_t j = 0; j < num_sub; j++, sub++) { int32_t unival_shad_count = 1; if (shader->num_uniform > 0) { int32_t num_uniform = shader->num_uniform; int32_t unival_shad_curr = 1; const int32_t* vp_unival_max = sub->vp_unival_max; const int32_t* fp_unival_max = sub->fp_unival_max; for (int32_t k = 0; k < num_uniform; k++) { const int32_t unival_max = max_def(vp_unival_max[k], fp_unival_max[k]); unival_shad_count += unival_shad_curr * unival_max; unival_shad_curr *= unival_max + 1; } } if (sub->programs) { GLuint* programs = sub->programs; for (int32_t k = 0; k < unival_shad_count; k++) glDeleteProgram(programs[k]); free(programs); sub->programs = 0; } } free(shader->sub); shader->sub = 0; } free_def(shaders); this->shaders = 0; get_index_by_name_func = 0; get_name_by_index_func = 0; } #ifdef USE_OPENGL static GLuint shader_compile_shader(GLenum type, const char* data, const char* file) { if (!data) return 0; GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &data, 0); glCompileShader(shader); GLint success = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { GLint length = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); GLchar* info_log = force_malloc(length + 1LL); glGetShaderInfoLog(shader, length, 0, info_log); info_log[length] = 0; const char* type_str = "Unknown"; switch (type) { case GL_FRAGMENT_SHADER: type_str = "Fragment"; break; case GL_VERTEX_SHADER: type_str = "Vertex"; break; } printf_debug("%s shader compile error:\nfile: %s\n%s\n", type_str, file, info_log); wchar_t temp_buf[MAX_PATH]; if (SUCCEEDED(SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, 0, temp_buf))) { wcscat_s(temp_buf, sizeof(temp_buf) / sizeof(wchar_t), L"\\ReDIVA"); temp_buf[sizeof(temp_buf) / sizeof(wchar_t) - 1] = 0; path_create_directory(temp_buf); wchar_t buf[MAX_PATH]; swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"%ls\\shader_error", temp_buf); buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; path_create_directory(buf); swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"%ls\\shader_error\\%hs", temp_buf, file); buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; file_stream s; s.open(buf, L"wb"); s.write_utf8_string(data); s.write_utf8_string("\n/*\n"); s.write(info_log, length); s.write_utf8_string("*/\n"); s.close(); } free_def(info_log); glDeleteShader(shader); return 0; } return shader; } static GLuint shader_compile(const char* vert, const char* frag, const char* vp, const char* fp) { GLuint vert_shad = shader_compile_shader(GL_VERTEX_SHADER, vert, vp); GLuint frag_shad = shader_compile_shader(GL_FRAGMENT_SHADER, frag, fp); GLuint program = glCreateProgram(); if (vert_shad) glAttachShader(program, vert_shad); if (frag_shad) glAttachShader(program, frag_shad); glLinkProgram(program); if (vert_shad) glDeleteShader(vert_shad); if (frag_shad) glDeleteShader(frag_shad); GLint success = 0; glGetProgramiv(program, GL_LINK_STATUS, &success); if (!success) { GLint length = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length); GLchar* info_log = force_malloc(length + 1LL); glGetProgramInfoLog(program, length, 0, info_log); info_log[length] = 0; printf_debug("Program Shader Permut linking error:\nvp: %s; fp: %s\n%s\n", vp, fp, info_log); wchar_t temp_buf[MAX_PATH]; if (SUCCEEDED(SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, 0, temp_buf))) { wcscat_s(temp_buf, sizeof(temp_buf) / sizeof(wchar_t), L"\\ReDIVA"); temp_buf[sizeof(temp_buf) / sizeof(wchar_t) - 1] = 0; path_create_directory(temp_buf); wchar_t buf[MAX_PATH]; swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"%ls\\shader_error", temp_buf); buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; path_create_directory(buf); swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"%ls\\shader_error\\%hs", temp_buf, vp); buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; file_stream s; s.open(buf, L"wb"); s.write_utf8_string(vert); s.write_utf8_string("\n/*\n"); s.write(info_log, length); s.write_utf8_string("*/\n"); s.close(); swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"%ls\\shader_error\\%hs", temp_buf, fp); buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; s.open(buf, L"wb"); s.write_utf8_string(frag); s.write_utf8_string("\n/*\n"); s.write(info_log, length); s.write_utf8_string("*/\n"); s.close(); } free_def(info_log); glDeleteProgram(program); return 0; } else { gl_get_error_all_print(); return program; } } static GLuint shader_compile_binary(const char* vert, const char* frag, const char* vp, const char* fp, program_binary* bin, GLsizei* buffer_size, void** binary) { memset(bin, 0, sizeof(*bin)); GLuint vert_shad = shader_compile_shader(GL_VERTEX_SHADER, vert, vp); GLuint frag_shad = shader_compile_shader(GL_FRAGMENT_SHADER, frag, fp); GLuint program = glCreateProgram(); if (vert_shad) glAttachShader(program, vert_shad); if (frag_shad) glAttachShader(program, frag_shad); glLinkProgram(program); if (vert_shad) glDeleteShader(vert_shad); if (frag_shad) glDeleteShader(frag_shad); GLint success = 0; glGetProgramiv(program, GL_LINK_STATUS, &success); if (!success) { GLint length = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length); GLchar* info_log = force_malloc(length + 1LL); glGetProgramInfoLog(program, length, 0, info_log); info_log[length] = 0; printf_debug("Program Shader Permut linking error:\nvp: %s; fp: %s\n%s\n", vp, fp, info_log); wchar_t temp_buf[MAX_PATH]; if (SUCCEEDED(SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, 0, temp_buf))) { wcscat_s(temp_buf, sizeof(temp_buf) / sizeof(wchar_t), L"\\ReDIVA"); temp_buf[sizeof(temp_buf) / sizeof(wchar_t) - 1] = 0; path_create_directory(temp_buf); wchar_t buf[MAX_PATH]; swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"%ls\\shader_error", temp_buf); buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; path_create_directory(buf); swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"%ls\\shader_error\\%hs", temp_buf, vp); buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; file_stream s; s.open(buf, L"wb"); s.write_utf8_string(vert); s.write_utf8_string("\n/*\n"); s.write(info_log, length); s.write_utf8_string("*/\n"); s.close(); swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"%ls\\shader_error\\%hs", temp_buf, fp); buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; s.open(buf, L"wb"); s.write_utf8_string(frag); s.write_utf8_string("\n/*\n"); s.write(info_log, length); s.write_utf8_string("*/\n"); s.close(); } free_def(info_log); glDeleteProgram(program); return 0; } else { gl_get_error_all_print(); GLenum binary_format = 0x00; GLsizei length = 0; while (*buffer_size < 0x7FFFFFF) { glGetProgramBinary(program, *buffer_size, &length, &binary_format, *binary); if (!gl_get_error_print()) break; free_def(*binary); *buffer_size <<= 1; *binary = force_malloc(*buffer_size); } bin->length = length; bin->binary_format = binary_format; bin->binary = (size_t)force_malloc(length); if (*binary) memcpy((void*)bin->binary, *binary, length); return program; } } static bool shader_load_binary_shader(program_binary* bin, GLuint* program, const char* vp, const char* fp) { if (bin->hash != hash_xxh3_64bits((void*)((size_t)bin + bin->binary), bin->length)) { printf_debug("Compiled binary hash could not be validated: %s %s\n", vp, fp); return false; } *program = glCreateProgram(); glProgramBinary(*program, bin->binary_format, (void*)((size_t)bin + bin->binary), bin->length); GLint success = 0; glGetProgramiv(*program, GL_LINK_STATUS, &success); if (!success) { glDeleteProgram(*program); *program = 0; return false; } return true; } #endif static prj::shared_ptr shader_load_spv_shader(program_spv* spv, const char* shader) { if (spv->hash != hash_xxh3_64bits((void*)((size_t)spv + spv->spv), spv->size)) { printf_debug("Compiled binary hash could not be validated: %s\n", shader); return {}; } return prj::shared_ptr(new Vulkan::ShaderModule( Vulkan::current_device, (const void*)((size_t)spv + spv->spv), spv->size)); }