You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-05 12:10:55 +00:00
Implement shader caching
* Shader compilation is now cached. Subsequent loads take less than a millisecond. * Improved game, editor and project manager startup time. * Editor uses .godot/shader_cache to store shaders. * Game uses user://shader_cache * Project manager uses $config_dir/shader_cache * Options to tweak shader caching in project settings. * Editor path configuration moved from EditorSettings to new class, EditorPaths, so it can be available early on (before shaders are compiled). * Reworked ShaderCompilerRD to ensure deterministic shader code creation (else shader may change and cache will be invalidated). * Added shader compression with SMOLV: https://github.com/aras-p/smol-v
This commit is contained in:
@@ -369,17 +369,24 @@ void ShaderCompilerRD::_dump_function_deps(const SL::ShaderNode *p_node, const S
|
||||
|
||||
ERR_FAIL_COND(fidx == -1);
|
||||
|
||||
Vector<StringName> uses_functions;
|
||||
|
||||
for (Set<StringName>::Element *E = p_node->functions[fidx].uses_function.front(); E; E = E->next()) {
|
||||
if (added.has(E->get())) {
|
||||
uses_functions.push_back(E->get());
|
||||
}
|
||||
uses_functions.sort_custom<StringName::AlphCompare>(); //ensure order is deterministic so the same shader is always produced
|
||||
|
||||
for (int k = 0; k < uses_functions.size(); k++) {
|
||||
if (added.has(uses_functions[k])) {
|
||||
continue; //was added already
|
||||
}
|
||||
|
||||
_dump_function_deps(p_node, E->get(), p_func_code, r_to_add, added);
|
||||
_dump_function_deps(p_node, uses_functions[k], p_func_code, r_to_add, added);
|
||||
|
||||
SL::FunctionNode *fnode = nullptr;
|
||||
|
||||
for (int i = 0; i < p_node->functions.size(); i++) {
|
||||
if (p_node->functions[i].name == E->get()) {
|
||||
if (p_node->functions[i].name == uses_functions[k]) {
|
||||
fnode = p_node->functions[i].function;
|
||||
break;
|
||||
}
|
||||
@@ -427,9 +434,9 @@ void ShaderCompilerRD::_dump_function_deps(const SL::ShaderNode *p_node, const S
|
||||
|
||||
header += ")\n";
|
||||
r_to_add += header;
|
||||
r_to_add += p_func_code[E->get()];
|
||||
r_to_add += p_func_code[uses_functions[k]];
|
||||
|
||||
added.insert(E->get());
|
||||
added.insert(uses_functions[k]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,63 +588,74 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
|
||||
uniform_defines.resize(max_uniforms);
|
||||
bool uses_uniforms = false;
|
||||
|
||||
Vector<StringName> uniform_names;
|
||||
|
||||
for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = pnode->uniforms.front(); E; E = E->next()) {
|
||||
uniform_names.push_back(E->key());
|
||||
}
|
||||
|
||||
uniform_names.sort_custom<StringName::AlphCompare>(); //ensure order is deterministic so the same shader is always produced
|
||||
|
||||
for (int k = 0; k < uniform_names.size(); k++) {
|
||||
StringName uniform_name = uniform_names[k];
|
||||
const SL::ShaderNode::Uniform &uniform = pnode->uniforms[uniform_name];
|
||||
|
||||
String ucode;
|
||||
|
||||
if (E->get().scope == SL::ShaderNode::Uniform::SCOPE_INSTANCE) {
|
||||
if (uniform.scope == SL::ShaderNode::Uniform::SCOPE_INSTANCE) {
|
||||
//insert, but don't generate any code.
|
||||
p_actions.uniforms->insert(E->key(), E->get());
|
||||
p_actions.uniforms->insert(uniform_name, uniform);
|
||||
continue; //instances are indexed directly, dont need index uniforms
|
||||
}
|
||||
if (SL::is_sampler_type(E->get().type)) {
|
||||
ucode = "layout(set = " + itos(actions.texture_layout_set) + ", binding = " + itos(actions.base_texture_binding_index + E->get().texture_order) + ") uniform ";
|
||||
if (SL::is_sampler_type(uniform.type)) {
|
||||
ucode = "layout(set = " + itos(actions.texture_layout_set) + ", binding = " + itos(actions.base_texture_binding_index + uniform.texture_order) + ") uniform ";
|
||||
}
|
||||
|
||||
bool is_buffer_global = !SL::is_sampler_type(E->get().type) && E->get().scope == SL::ShaderNode::Uniform::SCOPE_GLOBAL;
|
||||
bool is_buffer_global = !SL::is_sampler_type(uniform.type) && uniform.scope == SL::ShaderNode::Uniform::SCOPE_GLOBAL;
|
||||
|
||||
if (is_buffer_global) {
|
||||
//this is an integer to index the global table
|
||||
ucode += _typestr(ShaderLanguage::TYPE_UINT);
|
||||
} else {
|
||||
ucode += _prestr(E->get().precision);
|
||||
ucode += _typestr(E->get().type);
|
||||
ucode += _prestr(uniform.precision);
|
||||
ucode += _typestr(uniform.type);
|
||||
}
|
||||
|
||||
ucode += " " + _mkid(E->key());
|
||||
ucode += " " + _mkid(uniform_name);
|
||||
ucode += ";\n";
|
||||
if (SL::is_sampler_type(E->get().type)) {
|
||||
if (SL::is_sampler_type(uniform.type)) {
|
||||
for (int j = 0; j < STAGE_MAX; j++) {
|
||||
r_gen_code.stage_globals[j] += ucode;
|
||||
}
|
||||
|
||||
GeneratedCode::Texture texture;
|
||||
texture.name = E->key();
|
||||
texture.hint = E->get().hint;
|
||||
texture.type = E->get().type;
|
||||
texture.filter = E->get().filter;
|
||||
texture.repeat = E->get().repeat;
|
||||
texture.global = E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL;
|
||||
texture.name = uniform_name;
|
||||
texture.hint = uniform.hint;
|
||||
texture.type = uniform.type;
|
||||
texture.filter = uniform.filter;
|
||||
texture.repeat = uniform.repeat;
|
||||
texture.global = uniform.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL;
|
||||
if (texture.global) {
|
||||
r_gen_code.uses_global_textures = true;
|
||||
}
|
||||
|
||||
r_gen_code.texture_uniforms.write[E->get().texture_order] = texture;
|
||||
r_gen_code.texture_uniforms.write[uniform.texture_order] = texture;
|
||||
} else {
|
||||
if (!uses_uniforms) {
|
||||
uses_uniforms = true;
|
||||
}
|
||||
uniform_defines.write[E->get().order] = ucode;
|
||||
uniform_defines.write[uniform.order] = ucode;
|
||||
if (is_buffer_global) {
|
||||
//globals are indices into the global table
|
||||
uniform_sizes.write[E->get().order] = _get_datatype_size(ShaderLanguage::TYPE_UINT);
|
||||
uniform_alignments.write[E->get().order] = _get_datatype_alignment(ShaderLanguage::TYPE_UINT);
|
||||
uniform_sizes.write[uniform.order] = _get_datatype_size(ShaderLanguage::TYPE_UINT);
|
||||
uniform_alignments.write[uniform.order] = _get_datatype_alignment(ShaderLanguage::TYPE_UINT);
|
||||
} else {
|
||||
uniform_sizes.write[E->get().order] = _get_datatype_size(E->get().type);
|
||||
uniform_alignments.write[E->get().order] = _get_datatype_alignment(E->get().type);
|
||||
uniform_sizes.write[uniform.order] = _get_datatype_size(uniform.type);
|
||||
uniform_alignments.write[uniform.order] = _get_datatype_alignment(uniform.type);
|
||||
}
|
||||
}
|
||||
|
||||
p_actions.uniforms->insert(E->key(), E->get());
|
||||
p_actions.uniforms->insert(uniform_name, uniform);
|
||||
}
|
||||
|
||||
for (int i = 0; i < max_uniforms; i++) {
|
||||
@@ -704,21 +722,32 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
|
||||
|
||||
List<Pair<StringName, SL::ShaderNode::Varying>> var_frag_to_light;
|
||||
|
||||
Vector<StringName> varying_names;
|
||||
|
||||
for (Map<StringName, SL::ShaderNode::Varying>::Element *E = pnode->varyings.front(); E; E = E->next()) {
|
||||
if (E->get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || E->get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) {
|
||||
var_frag_to_light.push_back(Pair<StringName, SL::ShaderNode::Varying>(E->key(), E->get()));
|
||||
fragment_varyings.insert(E->key());
|
||||
varying_names.push_back(E->key());
|
||||
}
|
||||
|
||||
varying_names.sort_custom<StringName::AlphCompare>(); //ensure order is deterministic so the same shader is always produced
|
||||
|
||||
for (int k = 0; k < varying_names.size(); k++) {
|
||||
StringName varying_name = varying_names[k];
|
||||
const SL::ShaderNode::Varying &varying = pnode->varyings[varying_name];
|
||||
|
||||
if (varying.stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || varying.stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) {
|
||||
var_frag_to_light.push_back(Pair<StringName, SL::ShaderNode::Varying>(varying_name, varying));
|
||||
fragment_varyings.insert(varying_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
String vcode;
|
||||
String interp_mode = _interpstr(E->get().interpolation);
|
||||
vcode += _prestr(E->get().precision);
|
||||
vcode += _typestr(E->get().type);
|
||||
vcode += " " + _mkid(E->key());
|
||||
if (E->get().array_size > 0) {
|
||||
String interp_mode = _interpstr(varying.interpolation);
|
||||
vcode += _prestr(varying.precision);
|
||||
vcode += _typestr(varying.type);
|
||||
vcode += " " + _mkid(varying_name);
|
||||
if (varying.array_size > 0) {
|
||||
vcode += "[";
|
||||
vcode += itos(E->get().array_size);
|
||||
vcode += itos(varying.array_size);
|
||||
vcode += "]";
|
||||
}
|
||||
vcode += ";\n";
|
||||
|
||||
Reference in New Issue
Block a user