mirror of
https://github.com/korenkonder/ReDIVA.git
synced 2026-09-30 18:58:08 +03:00
1593 lines
65 KiB
C++
1593 lines
65 KiB
C++
/*
|
|
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/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 <shlobj_core.h>
|
|
|
|
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<Vulkan::ShaderModule> 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<int32_t, std::string>& samplers,
|
|
prj::vector_pair<int32_t, std::string>& 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* 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, "vec4 apply_skinning(in const vec3 a_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 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"
|
|
"vec3 apply_skinning_rotation(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";
|
|
const size_t replacement_len = sizeof(replacement) - 1;
|
|
|
|
size_t data_len = utf8_length(data);
|
|
char* p = force_malloc<char>(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;
|
|
|
|
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, 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<char*>(count);
|
|
size_t* temp_len = force_malloc<size_t>(count);
|
|
char** temp_ptr0 = force_malloc<char*>(count);
|
|
char** temp_ptr1 = force_malloc<char*>(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<char>(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<char>(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<char>(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(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<uint64_t, prj::shared_ptr<Vulkan::ShaderModule>> vec_shader_module;
|
|
shader* shaders = force_malloc<shader>(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_sub>(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) & 0xF;
|
|
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) & 0xF;
|
|
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<GLuint>(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<Vulkan::ShaderModule> 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<Vulkan::ShaderModule> 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<GLuint>();
|
|
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<Vulkan::ShaderModule> 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<Vulkan::ShaderModule> 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<int32_t> vec_vert;
|
|
std::vector<int32_t> vec_frag;
|
|
std::vector<program_binary> program_data_binary;
|
|
shader* shaders = force_malloc<shader>(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_sub>(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<char>(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<char>(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) & 0xF;
|
|
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) & 0xF;
|
|
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);
|
|
|
|
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<int32_t, std::string> samplers;
|
|
prj::vector_pair<int32_t, std::string> 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<GLuint>(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<GLuint>();
|
|
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<GLchar>(length);
|
|
glGetShaderInfoLog(shader, length, 0, info_log);
|
|
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_utf8_string(info_log);
|
|
s.write_utf8_string("*/\n");
|
|
}
|
|
|
|
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<GLchar>(length);
|
|
glGetProgramInfoLog(program, length, 0, info_log);
|
|
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_utf8_string(info_log);
|
|
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_utf8_string(info_log);
|
|
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<GLchar>(length);
|
|
glGetProgramInfoLog(program, length, 0, info_log);
|
|
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_utf8_string(info_log);
|
|
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_utf8_string(info_log);
|
|
s.write_utf8_string("*/\n");
|
|
s.close();
|
|
}
|
|
|
|
free_def(info_log);
|
|
glDeleteProgram(program);
|
|
return 0;
|
|
}
|
|
else {
|
|
gl_get_error_all_print();
|
|
|
|
GLenum binary_format = 0x0;
|
|
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<Vulkan::ShaderModule> 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<Vulkan::ShaderModule>(new Vulkan::ShaderModule(
|
|
Vulkan::current_device, (const void*)((size_t)spv + spv->spv), spv->size));
|
|
}
|