diff options
Diffstat (limited to 'servers/rendering/renderer_rd/shader_rd.cpp')
| -rw-r--r-- | servers/rendering/renderer_rd/shader_rd.cpp | 674 |
1 files changed, 404 insertions, 270 deletions
diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index e4a39ff813..6f29ff42bc 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -30,148 +30,102 @@ #include "shader_rd.h" -#include "core/string/string_builder.h" +#include "core/io/compression.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" #include "renderer_compositor_rd.h" #include "servers/rendering/rendering_device.h" - -void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name) { - name = p_name; - //split vertex and shader code (thank you, shader compiler programmers from you know what company). - if (p_vertex_code) { - String defines_tag = "\nVERSION_DEFINES"; - String globals_tag = "\nVERTEX_SHADER_GLOBALS"; - String material_tag = "\nMATERIAL_UNIFORMS"; - String code_tag = "\nVERTEX_SHADER_CODE"; - String code = p_vertex_code; - - int cpos = code.find(defines_tag); - if (cpos != -1) { - vertex_codev = code.substr(0, cpos).ascii(); - code = code.substr(cpos + defines_tag.length(), code.length()); - } - - cpos = code.find(material_tag); - - if (cpos == -1) { - vertex_code0 = code.ascii(); - } else { - vertex_code0 = code.substr(0, cpos).ascii(); - code = code.substr(cpos + material_tag.length(), code.length()); - - cpos = code.find(globals_tag); - - if (cpos == -1) { - vertex_code1 = code.ascii(); - } else { - vertex_code1 = code.substr(0, cpos).ascii(); - String code2 = code.substr(cpos + globals_tag.length(), code.length()); - - cpos = code2.find(code_tag); - if (cpos == -1) { - vertex_code2 = code2.ascii(); - } else { - vertex_code2 = code2.substr(0, cpos).ascii(); - vertex_code3 = code2.substr(cpos + code_tag.length(), code2.length()).ascii(); +#include "thirdparty/misc/smolv.h" + +void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) { + Vector<String> lines = String(p_code).split("\n"); + + String text; + + for (int i = 0; i < lines.size(); i++) { + String l = lines[i]; + bool push_chunk = false; + + StageTemplate::Chunk chunk; + + if (l.begins_with("#VERSION_DEFINES")) { + chunk.type = StageTemplate::Chunk::TYPE_VERSION_DEFINES; + push_chunk = true; + } else if (l.begins_with("#GLOBALS")) { + switch (p_stage_type) { + case STAGE_TYPE_VERTEX: + chunk.type = StageTemplate::Chunk::TYPE_VERTEX_GLOBALS; + break; + case STAGE_TYPE_FRAGMENT: + chunk.type = StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS; + break; + case STAGE_TYPE_COMPUTE: + chunk.type = StageTemplate::Chunk::TYPE_COMPUTE_GLOBALS; + break; + default: { } } - } - } - - if (p_fragment_code) { - String defines_tag = "\nVERSION_DEFINES"; - String globals_tag = "\nFRAGMENT_SHADER_GLOBALS"; - String material_tag = "\nMATERIAL_UNIFORMS"; - String code_tag = "\nFRAGMENT_SHADER_CODE"; - String light_code_tag = "\nLIGHT_SHADER_CODE"; - String code = p_fragment_code; - int cpos = code.find(defines_tag); - if (cpos != -1) { - fragment_codev = code.substr(0, cpos).ascii(); - code = code.substr(cpos + defines_tag.length(), code.length()); - } - - cpos = code.find(material_tag); - if (cpos == -1) { - fragment_code0 = code.ascii(); + push_chunk = true; + } else if (l.begins_with("#MATERIAL_UNIFORMS")) { + chunk.type = StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS; + push_chunk = true; + } else if (l.begins_with("#CODE")) { + chunk.type = StageTemplate::Chunk::TYPE_CODE; + push_chunk = true; + chunk.code = l.replace_first("#CODE", String()).replace(":", "").strip_edges().to_upper(); } else { - fragment_code0 = code.substr(0, cpos).ascii(); - //print_line("CODE0:\n"+String(fragment_code0.get_data())); - code = code.substr(cpos + material_tag.length(), code.length()); - cpos = code.find(globals_tag); - - if (cpos == -1) { - fragment_code1 = code.ascii(); - } else { - fragment_code1 = code.substr(0, cpos).ascii(); - //print_line("CODE1:\n"+String(fragment_code1.get_data())); - - String code2 = code.substr(cpos + globals_tag.length(), code.length()); - cpos = code2.find(light_code_tag); + text += l + "\n"; + } - if (cpos == -1) { - fragment_code2 = code2.ascii(); - } else { - fragment_code2 = code2.substr(0, cpos).ascii(); - //print_line("CODE2:\n"+String(fragment_code2.get_data())); - - String code3 = code2.substr(cpos + light_code_tag.length(), code2.length()); - - cpos = code3.find(code_tag); - if (cpos == -1) { - fragment_code3 = code3.ascii(); - } else { - fragment_code3 = code3.substr(0, cpos).ascii(); - //print_line("CODE3:\n"+String(fragment_code3.get_data())); - fragment_code4 = code3.substr(cpos + code_tag.length(), code3.length()).ascii(); - //print_line("CODE4:\n"+String(fragment_code4.get_data())); - } - } + if (push_chunk) { + if (text != String()) { + StageTemplate::Chunk text_chunk; + text_chunk.type = StageTemplate::Chunk::TYPE_TEXT; + text_chunk.text = text.utf8(); + stage_templates[p_stage_type].chunks.push_back(text_chunk); + text = String(); } + stage_templates[p_stage_type].chunks.push_back(chunk); } } - if (p_compute_code) { - is_compute = true; + if (text != String()) { + StageTemplate::Chunk text_chunk; + text_chunk.type = StageTemplate::Chunk::TYPE_TEXT; + text_chunk.text = text.utf8(); + stage_templates[p_stage_type].chunks.push_back(text_chunk); + text = String(); + } +} - String defines_tag = "\nVERSION_DEFINES"; - String globals_tag = "\nCOMPUTE_SHADER_GLOBALS"; - String material_tag = "\nMATERIAL_UNIFORMS"; - String code_tag = "\nCOMPUTE_SHADER_CODE"; - String code = p_compute_code; +void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name) { + name = p_name; - int cpos = code.find(defines_tag); - if (cpos != -1) { - compute_codev = code.substr(0, cpos).ascii(); - code = code.substr(cpos + defines_tag.length(), code.length()); + if (p_compute_code) { + _add_stage(p_compute_code, STAGE_TYPE_COMPUTE); + is_compute = true; + } else { + is_compute = false; + if (p_vertex_code) { + _add_stage(p_vertex_code, STAGE_TYPE_VERTEX); } - - cpos = code.find(material_tag); - - if (cpos == -1) { - compute_code0 = code.ascii(); - } else { - compute_code0 = code.substr(0, cpos).ascii(); - code = code.substr(cpos + material_tag.length(), code.length()); - - cpos = code.find(globals_tag); - - if (cpos == -1) { - compute_code1 = code.ascii(); - } else { - compute_code1 = code.substr(0, cpos).ascii(); - String code2 = code.substr(cpos + globals_tag.length(), code.length()); - - cpos = code2.find(code_tag); - if (cpos == -1) { - compute_code2 = code2.ascii(); - } else { - compute_code2 = code2.substr(0, cpos).ascii(); - compute_code3 = code2.substr(cpos + code_tag.length(), code2.length()).ascii(); - } - } + if (p_fragment_code) { + _add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT); } } + + StringBuilder tohash; + tohash.append("[VersionKey]"); + tohash.append(RenderingDevice::get_singleton()->shader_get_cache_key()); + tohash.append("[Vertex]"); + tohash.append(p_vertex_code ? p_vertex_code : ""); + tohash.append("[Fragment]"); + tohash.append(p_fragment_code ? p_fragment_code : ""); + tohash.append("[Compute]"); + tohash.append(p_compute_code ? p_compute_code : ""); + + base_sha256 = tohash.as_string().sha256_text(); } RID ShaderRD::version_create() { @@ -194,16 +148,62 @@ void ShaderRD::_clear_version(Version *p_version) { } memdelete_arr(p_version->variants); + if (p_version->variant_stages) { + memdelete_arr(p_version->variant_stages); + } p_version->variants = nullptr; } } +void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template) { + for (uint32_t i = 0; i < p_template.chunks.size(); i++) { + const StageTemplate::Chunk &chunk = p_template.chunks[i]; + switch (chunk.type) { + case StageTemplate::Chunk::TYPE_VERSION_DEFINES: { + builder.append("\n"); //make sure defines begin at newline + builder.append(general_defines.get_data()); + builder.append(variant_defines[p_variant].get_data()); + for (int j = 0; j < p_version->custom_defines.size(); j++) { + builder.append(p_version->custom_defines[j].get_data()); + } + builder.append("\n"); //make sure defines begin at newline + if (p_version->uniforms.size()) { + builder.append("#define MATERIAL_UNIFORMS_USED\n"); + } + for (Map<StringName, CharString>::Element *E = p_version->code_sections.front(); E; E = E->next()) { + builder.append(String("#define ") + String(E->key()) + "_CODE_USED\n"); + } + } break; + case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: { + builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment) + } break; + case StageTemplate::Chunk::TYPE_VERTEX_GLOBALS: { + builder.append(p_version->vertex_globals.get_data()); // vertex globals + } break; + case StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS: { + builder.append(p_version->fragment_globals.get_data()); // fragment globals + } break; + case StageTemplate::Chunk::TYPE_COMPUTE_GLOBALS: { + builder.append(p_version->compute_globals.get_data()); // compute globals + } break; + case StageTemplate::Chunk::TYPE_CODE: { + if (p_version->code_sections.has(chunk.code)) { + builder.append(p_version->code_sections[chunk.code].get_data()); + } + } break; + case StageTemplate::Chunk::TYPE_TEXT: { + builder.append(chunk.text.get_data()); + } break; + } + } +} + void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { if (!variants_enabled[p_variant]) { return; //variant is disabled, return } - Vector<RD::ShaderStageData> stages; + Vector<RD::ShaderStageData> &stages = p_version->variant_stages[p_variant]; String error; String current_source; @@ -214,29 +214,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { //vertex stage StringBuilder builder; - - builder.append(vertex_codev.get_data()); // version info (if exists) - builder.append("\n"); //make sure defines begin at newline - builder.append(general_defines.get_data()); - builder.append(variant_defines[p_variant].get_data()); - - for (int j = 0; j < p_version->custom_defines.size(); j++) { - builder.append(p_version->custom_defines[j].get_data()); - } - - builder.append(vertex_code0.get_data()); //first part of vertex - - builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment) - - builder.append(vertex_code1.get_data()); //second part of vertex - - builder.append(p_version->vertex_globals.get_data()); // vertex globals - - builder.append(vertex_code2.get_data()); //third part of vertex - - builder.append(p_version->vertex_code.get_data()); // code - - builder.append(vertex_code3.get_data()); //fourth of vertex + _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX]); current_source = builder.as_string(); RD::ShaderStageData stage; @@ -254,33 +232,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { current_stage = RD::SHADER_STAGE_FRAGMENT; StringBuilder builder; - - builder.append(fragment_codev.get_data()); // version info (if exists) - builder.append("\n"); //make sure defines begin at newline - - builder.append(general_defines.get_data()); - builder.append(variant_defines[p_variant].get_data()); - for (int j = 0; j < p_version->custom_defines.size(); j++) { - builder.append(p_version->custom_defines[j].get_data()); - } - - builder.append(fragment_code0.get_data()); //first part of fragment - - builder.append(p_version->uniforms.get_data()); //uniforms (same for fragment and fragment) - - builder.append(fragment_code1.get_data()); //first part of fragment - - builder.append(p_version->fragment_globals.get_data()); // fragment globals - - builder.append(fragment_code2.get_data()); //third part of fragment - - builder.append(p_version->fragment_light.get_data()); // fragment light - - builder.append(fragment_code3.get_data()); //fourth part of fragment - - builder.append(p_version->fragment_code.get_data()); // fragment code - - builder.append(fragment_code4.get_data()); //fourth part of fragment + _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT]); current_source = builder.as_string(); RD::ShaderStageData stage; @@ -298,32 +250,10 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { current_stage = RD::SHADER_STAGE_COMPUTE; StringBuilder builder; - - builder.append(compute_codev.get_data()); // version info (if exists) - builder.append("\n"); //make sure defines begin at newline - builder.append(base_compute_defines.get_data()); - builder.append(general_defines.get_data()); - builder.append(variant_defines[p_variant].get_data()); - - for (int j = 0; j < p_version->custom_defines.size(); j++) { - builder.append(p_version->custom_defines[j].get_data()); - } - - builder.append(compute_code0.get_data()); //first part of compute - - builder.append(p_version->uniforms.get_data()); //uniforms (same for compute and fragment) - - builder.append(compute_code1.get_data()); //second part of compute - - builder.append(p_version->compute_globals.get_data()); // compute globals - - builder.append(compute_code2.get_data()); //third part of compute - - builder.append(p_version->compute_code.get_data()); // code - - builder.append(compute_code3.get_data()); //fourth of compute + _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_COMPUTE]); current_source = builder.as_string(); + RD::ShaderStageData stage; stage.spir_v = RD::get_singleton()->shader_compile_from_source(RD::SHADER_STAGE_COMPUTE, current_source, RD::SHADER_LANGUAGE_GLSL, &error); if (stage.spir_v.size() == 0) { @@ -364,113 +294,234 @@ RS::ShaderNativeSourceCode ShaderRD::version_get_native_source_code(RID p_versio //vertex stage StringBuilder builder; + _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX]); - builder.append(vertex_codev.get_data()); // version info (if exists) - builder.append("\n"); //make sure defines begin at newline - builder.append(general_defines.get_data()); - builder.append(variant_defines[i].get_data()); - - for (int j = 0; j < version->custom_defines.size(); j++) { - builder.append(version->custom_defines[j].get_data()); - } + RS::ShaderNativeSourceCode::Version::Stage stage; + stage.name = "vertex"; + stage.code = builder.as_string(); - builder.append(vertex_code0.get_data()); //first part of vertex + source_code.versions.write[i].stages.push_back(stage); + } - builder.append(version->uniforms.get_data()); //uniforms (same for vertex and fragment) + if (!is_compute) { + //fragment stage - builder.append(vertex_code1.get_data()); //second part of vertex + StringBuilder builder; + _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT]); - builder.append(version->vertex_globals.get_data()); // vertex globals + RS::ShaderNativeSourceCode::Version::Stage stage; + stage.name = "fragment"; + stage.code = builder.as_string(); - builder.append(vertex_code2.get_data()); //third part of vertex + source_code.versions.write[i].stages.push_back(stage); + } - builder.append(version->vertex_code.get_data()); // code + if (is_compute) { + //compute stage - builder.append(vertex_code3.get_data()); //fourth of vertex + StringBuilder builder; + _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_COMPUTE]); RS::ShaderNativeSourceCode::Version::Stage stage; - stage.name = "vertex"; + stage.name = "compute"; stage.code = builder.as_string(); source_code.versions.write[i].stages.push_back(stage); } + } - if (!is_compute) { - //fragment stage + return source_code; +} - StringBuilder builder; +String ShaderRD::_version_get_sha1(Version *p_version) const { + StringBuilder hash_build; + + hash_build.append("[uniforms]"); + hash_build.append(p_version->uniforms.get_data()); + hash_build.append("[vertex_globals]"); + hash_build.append(p_version->vertex_globals.get_data()); + hash_build.append("[fragment_globals]"); + hash_build.append(p_version->fragment_globals.get_data()); + hash_build.append("[compute_globals]"); + hash_build.append(p_version->compute_globals.get_data()); + + Vector<StringName> code_sections; + for (Map<StringName, CharString>::Element *E = p_version->code_sections.front(); E; E = E->next()) { + code_sections.push_back(E->key()); + } + code_sections.sort_custom<StringName::AlphCompare>(); - builder.append(fragment_codev.get_data()); // version info (if exists) - builder.append("\n"); //make sure defines begin at newline - builder.append(general_defines.get_data()); - builder.append(variant_defines[i].get_data()); - for (int j = 0; j < version->custom_defines.size(); j++) { - builder.append(version->custom_defines[j].get_data()); - } + for (int i = 0; i < code_sections.size(); i++) { + hash_build.append(String("[code:") + String(code_sections[i]) + "]"); + hash_build.append(p_version->code_sections[code_sections[i]].get_data()); + } + for (int i = 0; i < p_version->custom_defines.size(); i++) { + hash_build.append("[custom_defines:" + itos(i) + "]"); + hash_build.append(p_version->custom_defines[i].get_data()); + } - builder.append(fragment_code0.get_data()); //first part of fragment + return hash_build.as_string().sha1_text(); +} - builder.append(version->uniforms.get_data()); //uniforms (same for fragment and fragment) +static const char *shader_file_header = "GDSC"; +static const uint32_t cache_file_version = 1; - builder.append(fragment_code1.get_data()); //first part of fragment +bool ShaderRD::_load_from_cache(Version *p_version) { + String sha1 = _version_get_sha1(p_version); + String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; - builder.append(version->fragment_globals.get_data()); // fragment globals + uint64_t time_from = OS::get_singleton()->get_ticks_usec(); - builder.append(fragment_code2.get_data()); //third part of fragment + FileAccessRef f = FileAccess::open(path, FileAccess::READ); + if (!f) { + return false; + } - builder.append(version->fragment_light.get_data()); // fragment light + char header[5] = { 0, 0, 0, 0, 0 }; + f->get_buffer((uint8_t *)header, 4); + ERR_FAIL_COND_V(header != String(shader_file_header), false); - builder.append(fragment_code3.get_data()); //fourth part of fragment + uint32_t file_version = f->get_32(); + if (file_version != cache_file_version) { + return false; // wrong version + } - builder.append(version->fragment_code.get_data()); // fragment code + uint32_t variant_count = f->get_32(); - builder.append(fragment_code4.get_data()); //fourth part of fragment + ERR_FAIL_COND_V(variant_count != (uint32_t)variant_defines.size(), false); //should not happen but check - RS::ShaderNativeSourceCode::Version::Stage stage; - stage.name = "fragment"; - stage.code = builder.as_string(); + bool success = true; + for (uint32_t i = 0; i < variant_count; i++) { + uint32_t stage_count = f->get_32(); + p_version->variant_stages[i].resize(stage_count); + for (uint32_t j = 0; j < stage_count; j++) { + p_version->variant_stages[i].write[j].shader_stage = RD::ShaderStage(f->get_32()); - source_code.versions.write[i].stages.push_back(stage); - } + int compression = f->get_32(); + uint32_t length = f->get_32(); - if (is_compute) { - //compute stage + if (compression == 0) { + Vector<uint8_t> data; + data.resize(length); - StringBuilder builder; + f->get_buffer(data.ptrw(), length); - builder.append(compute_codev.get_data()); // version info (if exists) - builder.append("\n"); //make sure defines begin at newline - builder.append(base_compute_defines.get_data()); - builder.append(general_defines.get_data()); - builder.append(variant_defines[i].get_data()); + p_version->variant_stages[i].write[j].spir_v = data; + } else { + Vector<uint8_t> data; - for (int j = 0; j < version->custom_defines.size(); j++) { - builder.append(version->custom_defines[j].get_data()); - } + if (compression == 2) { + //zstd + int smol_length = f->get_32(); + Vector<uint8_t> zstd_data; - builder.append(compute_code0.get_data()); //first part of compute + zstd_data.resize(smol_length); + f->get_buffer(zstd_data.ptrw(), smol_length); - builder.append(version->uniforms.get_data()); //uniforms (same for compute and fragment) + data.resize(length); + Compression::decompress(data.ptrw(), data.size(), zstd_data.ptr(), zstd_data.size(), Compression::MODE_ZSTD); - builder.append(compute_code1.get_data()); //second part of compute + } else { + data.resize(length); + f->get_buffer(data.ptrw(), length); + } - builder.append(version->compute_globals.get_data()); // compute globals + Vector<uint8_t> spirv; + uint32_t spirv_size = smolv::GetDecodedBufferSize(data.ptr(), data.size()); + spirv.resize(spirv_size); + if (!smolv::Decode(data.ptr(), data.size(), spirv.ptrw(), spirv_size)) { + ERR_PRINT("Malformed smolv input uncompressing shader " + name + ", variant #" + itos(i) + " stage :" + itos(j)); + success = false; + break; + } + p_version->variant_stages[i].write[j].spir_v = spirv; + } + } + } - builder.append(compute_code2.get_data()); //third part of compute + if (!success) { + for (uint32_t i = 0; i < variant_count; i++) { + p_version->variant_stages[i].resize(0); + } + return false; + } - builder.append(version->compute_code.get_data()); // code + float time_ms = double(OS::get_singleton()->get_ticks_usec() - time_from) / 1000.0; - builder.append(compute_code3.get_data()); //fourth of compute + print_verbose("Shader cache load success '" + path + "' " + rtos(time_ms) + "ms."); - RS::ShaderNativeSourceCode::Version::Stage stage; - stage.name = "compute"; - stage.code = builder.as_string(); + for (uint32_t i = 0; i < variant_count; i++) { + RID shader = RD::get_singleton()->shader_create(p_version->variant_stages[i]); + { + MutexLock lock(variant_set_mutex); + p_version->variants[i] = shader; + } + } - source_code.versions.write[i].stages.push_back(stage); + memdelete_arr(p_version->variant_stages); //clear stages + p_version->variant_stages = nullptr; + p_version->valid = true; + return true; +} + +void ShaderRD::_save_to_cache(Version *p_version) { + String sha1 = _version_get_sha1(p_version); + String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; + + FileAccessRef f = FileAccess::open(path, FileAccess::WRITE); + ERR_FAIL_COND(!f); + f->store_buffer((const uint8_t *)shader_file_header, 4); + f->store_32(cache_file_version); //file version + uint32_t variant_count = variant_defines.size(); + f->store_32(variant_count); //variant count + + for (uint32_t i = 0; i < variant_count; i++) { + f->store_32(p_version->variant_stages[i].size()); //stage count + for (int j = 0; j < p_version->variant_stages[i].size(); j++) { + f->store_32(p_version->variant_stages[i][j].shader_stage); //stage count + Vector<uint8_t> spirv = p_version->variant_stages[i][j].spir_v; + + bool save_uncompressed = true; + if (shader_cache_save_compressed) { + smolv::ByteArray smolv; + bool strip_debug = !shader_cache_save_debug; + if (!smolv::Encode(spirv.ptr(), spirv.size(), smolv, strip_debug ? smolv::kEncodeFlagStripDebugInfo : 0)) { + ERR_PRINT("Error compressing shader " + name + ", variant #" + itos(i) + " stage :" + itos(i)); + } else { + bool compress_zstd = shader_cache_save_compressed_zstd; + + if (compress_zstd) { + Vector<uint8_t> zstd; + zstd.resize(Compression::get_max_compressed_buffer_size(smolv.size(), Compression::MODE_ZSTD)); + int dst_size = Compression::compress(zstd.ptrw(), &smolv[0], smolv.size(), Compression::MODE_ZSTD); + if (dst_size >= 0 && (uint32_t)dst_size < smolv.size()) { + f->store_32(2); //compressed zstd + f->store_32(smolv.size()); //size of smolv buffer + f->store_32(dst_size); //size of smolv buffer + f->store_buffer(zstd.ptr(), dst_size); //smolv buffer + } else { + compress_zstd = false; + } + } + + if (!compress_zstd) { + f->store_32(1); //compressed + f->store_32(smolv.size()); //size of smolv buffer + f->store_buffer(&smolv[0], smolv.size()); //smolv buffer + } + save_uncompressed = false; + } + } + + if (save_uncompressed) { + f->store_32(0); //uncompressed + f->store_32(spirv.size()); //stage count + f->store_buffer(spirv.ptr(), spirv.size()); //stage count + } } } - return source_code; + f->close(); } void ShaderRD::_compile_version(Version *p_version) { @@ -480,6 +531,15 @@ void ShaderRD::_compile_version(Version *p_version) { p_version->dirty = false; p_version->variants = memnew_arr(RID, variant_defines.size()); + typedef Vector<RD::ShaderStageData> ShaderStageArray; + p_version->variant_stages = memnew_arr(ShaderStageArray, variant_defines.size()); + + if (shader_cache_dir_valid) { + if (_load_from_cache(p_version)) { + return; + } + } + #if 1 RendererThreadPool::singleton->thread_work_pool.do_work(variant_defines.size(), this, &ShaderRD::_compile_variant, p_version); @@ -511,24 +571,35 @@ void ShaderRD::_compile_version(Version *p_version) { } } memdelete_arr(p_version->variants); + if (p_version->variant_stages) { + memdelete_arr(p_version->variant_stages); + } p_version->variants = nullptr; + p_version->variant_stages = nullptr; return; + } else if (shader_cache_dir_valid) { + //save shader cache + _save_to_cache(p_version); } + memdelete_arr(p_version->variant_stages); //clear stages + p_version->variant_stages = nullptr; + p_version->valid = true; } -void ShaderRD::version_set_code(RID p_version, const String &p_uniforms, const String &p_vertex_globals, const String &p_vertex_code, const String &p_fragment_globals, const String &p_fragment_light, const String &p_fragment_code, const Vector<String> &p_custom_defines) { +void ShaderRD::version_set_code(RID p_version, const Map<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines) { ERR_FAIL_COND(is_compute); Version *version = version_owner.getornull(p_version); ERR_FAIL_COND(!version); version->vertex_globals = p_vertex_globals.utf8(); - version->vertex_code = p_vertex_code.utf8(); - version->fragment_light = p_fragment_light.utf8(); version->fragment_globals = p_fragment_globals.utf8(); - version->fragment_code = p_fragment_code.utf8(); version->uniforms = p_uniforms.utf8(); + version->code_sections.clear(); + for (Map<String, String>::Element *E = p_code.front(); E; E = E->next()) { + version->code_sections[StringName(E->key().to_upper())] = E->get().utf8(); + } version->custom_defines.clear(); for (int i = 0; i < p_custom_defines.size(); i++) { @@ -542,15 +613,20 @@ void ShaderRD::version_set_code(RID p_version, const String &p_uniforms, const S } } -void ShaderRD::version_set_compute_code(RID p_version, const String &p_uniforms, const String &p_compute_globals, const String &p_compute_code, const Vector<String> &p_custom_defines) { +void ShaderRD::version_set_compute_code(RID p_version, const Map<String, String> &p_code, const String &p_uniforms, const String &p_compute_globals, const Vector<String> &p_custom_defines) { ERR_FAIL_COND(!is_compute); Version *version = version_owner.getornull(p_version); ERR_FAIL_COND(!version); + version->compute_globals = p_compute_globals.utf8(); - version->compute_code = p_compute_code.utf8(); version->uniforms = p_uniforms.utf8(); + version->code_sections.clear(); + for (Map<String, String>::Element *E = p_code.front(); E; E = E->next()) { + version->code_sections[StringName(E->key().to_upper())] = E->get().utf8(); + } + version->custom_defines.clear(); for (int i = 0; i < p_custom_defines.size(); i++) { version->custom_defines.push_back(p_custom_defines[i].utf8()); @@ -597,6 +673,8 @@ bool ShaderRD::is_variant_enabled(int p_variant) const { return variants_enabled[p_variant]; } +bool ShaderRD::shader_cache_cleanup_on_start = false; + ShaderRD::ShaderRD() { // Do not feel forced to use this, in most cases it makes little to no difference. bool use_32_threads = false; @@ -623,8 +701,64 @@ void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String variant_defines.push_back(p_variant_defines[i].utf8()); variants_enabled.push_back(true); } + + if (shader_cache_dir != String()) { + StringBuilder hash_build; + + hash_build.append("[base_hash]"); + hash_build.append(base_sha256); + hash_build.append("[general_defines]"); + hash_build.append(general_defines.get_data()); + for (int i = 0; i < variant_defines.size(); i++) { + hash_build.append("[variant_defines:" + itos(i) + "]"); + hash_build.append(variant_defines[i].get_data()); + } + + base_sha256 = hash_build.as_string().sha256_text(); + + DirAccessRef d = DirAccess::open(shader_cache_dir); + ERR_FAIL_COND(!d); + if (d->change_dir(name) != OK) { + Error err = d->make_dir(name); + ERR_FAIL_COND(err != OK); + d->change_dir(name); + } + + //erase other versions? + if (shader_cache_cleanup_on_start) { + } + // + if (d->change_dir(base_sha256) != OK) { + Error err = d->make_dir(base_sha256); + ERR_FAIL_COND(err != OK); + } + shader_cache_dir_valid = true; + + print_verbose("Shader '" + name + "' SHA256: " + base_sha256); + } +} + +void ShaderRD::set_shader_cache_dir(const String &p_dir) { + shader_cache_dir = p_dir; +} + +void ShaderRD::set_shader_cache_save_compressed(bool p_enable) { + shader_cache_save_compressed = p_enable; } +void ShaderRD::set_shader_cache_save_compressed_zstd(bool p_enable) { + shader_cache_save_compressed_zstd = p_enable; +} + +void ShaderRD::set_shader_cache_save_debug(bool p_enable) { + shader_cache_save_debug = p_enable; +} + +String ShaderRD::shader_cache_dir; +bool ShaderRD::shader_cache_save_compressed = true; +bool ShaderRD::shader_cache_save_compressed_zstd = true; +bool ShaderRD::shader_cache_save_debug = true; + ShaderRD::~ShaderRD() { List<RID> remaining; version_owner.get_owned_list(&remaining); |
