Skip to content

Commit

Permalink
Enhance cache modes in resource loading
Browse files Browse the repository at this point in the history
- Unify documentation, hoping to clear misconcepctions about about propagation of the cache mode across dependant loads.
- Clarify in docs that `CACHE_MODE_REPLACE` now also works on the main resource (from godotengine#87008).
- Add two recursive modes, counterparts of `CACHE_MODE_REPLACE` and `CACHE_MODE_IGNORE`, since it seems some need them (see godotengine#59669, godotengine#82830).
- Let resources, even loaded with one of the ignore-cache modes, get a path, which is useful for tools.
  • Loading branch information
RandomShaper committed Feb 26, 2024
1 parent bb6b06c commit 5e14402
Show file tree
Hide file tree
Showing 15 changed files with 104 additions and 28 deletions.
2 changes: 2 additions & 0 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ void ResourceLoader::_bind_methods() {
BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE);
BIND_ENUM_CONSTANT(CACHE_MODE_REUSE);
BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE);
BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE_DEEP);
BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE_DEEP);
}

////// ResourceSaver //////
Expand Down
8 changes: 5 additions & 3 deletions core/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,11 @@ class ResourceLoader : public Object {
};

enum CacheMode {
CACHE_MODE_IGNORE, // Resource and subresources do not use path cache, no path is set into resource.
CACHE_MODE_REUSE, // Resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available.
CACHE_MODE_REPLACE, // Resource and subresource use path cache, but replace existing loaded resources when available with information from disk.
CACHE_MODE_IGNORE,
CACHE_MODE_REUSE,
CACHE_MODE_REPLACE,
CACHE_MODE_IGNORE_DEEP,
CACHE_MODE_REPLACE_DEEP,
};

static ResourceLoader *get_singleton() { return singleton; }
Expand Down
2 changes: 1 addition & 1 deletion core/io/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class Resource : public RefCounted {

virtual void set_path(const String &p_path, bool p_take_over = false);
String get_path() const;
void set_path_cache(const String &p_path); // Set raw path without involving resource cache.
virtual void set_path_cache(const String &p_path); // Set raw path without involving resource cache.
_FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.contains("::") || path_cache.begins_with("local://"); }

static String generate_scene_unique_id();
Expand Down
31 changes: 24 additions & 7 deletions core/io/resource_format_binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
path = remaps[path];
}

Ref<Resource> res = ResourceLoader::load(path, exttype);
Ref<Resource> res = ResourceLoader::load(path, exttype, cache_mode_for_external);

if (res.is_null()) {
WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data());
Expand Down Expand Up @@ -683,7 +683,7 @@ Error ResourceLoaderBinary::load() {
}

external_resources.write[i].path = path; //remap happens here, not on load because on load it can actually be used for filesystem dock resource remap
external_resources.write[i].load_token = ResourceLoader::_load_start(path, external_resources[i].type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, ResourceFormatLoader::CACHE_MODE_REUSE);
external_resources.write[i].load_token = ResourceLoader::_load_start(path, external_resources[i].type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, cache_mode_for_external);
if (!external_resources[i].load_token.is_valid()) {
if (!ResourceLoader::get_abort_on_missing_resources()) {
ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type);
Expand Down Expand Up @@ -772,10 +772,12 @@ Error ResourceLoaderBinary::load() {
}

res = Ref<Resource>(r);
if (!path.is_empty() && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
r->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); //if got here because the resource with same path has different type, replace it
} else if (!path.is_resource_file()) {
r->set_path_cache(path);
if (!path.is_empty()) {
if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
r->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); // If got here because the resource with same path has different type, replace it.
} else {
r->set_path_cache(path);
}
}
r->set_scene_unique_id(id);
}
Expand Down Expand Up @@ -1187,7 +1189,22 @@ Ref<Resource> ResourceFormatLoaderBinary::load(const String &p_path, const Strin
ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot open file '" + p_path + "'.");

ResourceLoaderBinary loader;
loader.cache_mode = p_cache_mode;
switch (p_cache_mode) {
case CACHE_MODE_IGNORE:
case CACHE_MODE_REUSE:
case CACHE_MODE_REPLACE:
loader.cache_mode = p_cache_mode;
loader.cache_mode_for_external = CACHE_MODE_REUSE;
break;
case CACHE_MODE_IGNORE_DEEP:
loader.cache_mode = CACHE_MODE_IGNORE;
loader.cache_mode_for_external = p_cache_mode;
break;
case CACHE_MODE_REPLACE_DEEP:
loader.cache_mode = CACHE_MODE_REPLACE;
loader.cache_mode_for_external = p_cache_mode;
break;
}
loader.use_sub_threads = p_use_sub_threads;
loader.progress = r_progress;
String path = !p_original_path.is_empty() ? p_original_path : p_path;
Expand Down
1 change: 1 addition & 0 deletions core/io/resource_format_binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class ResourceLoaderBinary {
Error error = OK;

ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE;
ResourceFormatLoader::CacheMode cache_mode_for_external = ResourceFormatLoader::CACHE_MODE_REUSE;

friend class ResourceFormatLoaderBinary;

Expand Down
14 changes: 9 additions & 5 deletions core/io/resource_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ void ResourceFormatLoader::_bind_methods() {
BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE);
BIND_ENUM_CONSTANT(CACHE_MODE_REUSE);
BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE);
BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE_DEEP);
BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE_DEEP);

GDVIRTUAL_BIND(_get_recognized_extensions);
GDVIRTUAL_BIND(_recognize_path, "path", "type");
Expand Down Expand Up @@ -339,18 +341,20 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
load_task.cond_var = nullptr;
}

bool ignoring = load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE || load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP;
bool replacing = load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE || load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE_DEEP;
if (load_task.resource.is_valid()) {
if (load_task.cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
if (load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE) {
if (!ignoring) {
if (replacing) {
Ref<Resource> old_res = ResourceCache::get_ref(load_task.local_path);
if (old_res.is_valid() && old_res != load_task.resource) {
// If resource is already loaded, only replace its data, to avoid existing invalidating instances.
old_res->copy_from(load_task.resource);
load_task.resource = old_res;
}
}
load_task.resource->set_path(load_task.local_path, load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
} else if (!load_task.local_path.is_resource_file()) {
load_task.resource->set_path(load_task.local_path, replacing);
} else {
load_task.resource->set_path_cache(load_task.local_path);
}

Expand All @@ -370,7 +374,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
if (_loaded_callback) {
_loaded_callback(load_task.resource, load_task.local_path);
}
} else if (load_task.cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
} else if (!ignoring) {
Ref<Resource> existing = ResourceCache::get_ref(load_task.local_path);
if (existing.is_valid()) {
load_task.resource = existing;
Expand Down
8 changes: 5 additions & 3 deletions core/io/resource_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ class ResourceFormatLoader : public RefCounted {

public:
enum CacheMode {
CACHE_MODE_IGNORE, // Resource and subresources do not use path cache, no path is set into resource.
CACHE_MODE_REUSE, // Resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available.
CACHE_MODE_REPLACE, // Resource and subresource use path cache, but replace existing loaded resources when available with information from disk.
CACHE_MODE_IGNORE,
CACHE_MODE_REUSE,
CACHE_MODE_REPLACE,
CACHE_MODE_IGNORE_DEEP,
CACHE_MODE_REPLACE_DEEP,
};

protected:
Expand Down
9 changes: 9 additions & 0 deletions doc/classes/ResourceFormatLoader.xml
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,19 @@
</methods>
<constants>
<constant name="CACHE_MODE_IGNORE" value="0" enum="CacheMode">
Neither the main resource (the one requested to be loaded) nor any of its subresources are retrieved from cache nor stored into it. Dependencies (external resources) are loaded with [constant CACHE_MODE_REUSE].
</constant>
<constant name="CACHE_MODE_REUSE" value="1" enum="CacheMode">
The main resource (the one requested to be loaded), its subresources, and its dependencies (external resources) are retrieved from cache if present, instead of loaded. Those not cached are loaded and then stored into the cache. The same rules are propagated recursively down the tree of dependencies (external resources).
</constant>
<constant name="CACHE_MODE_REPLACE" value="2" enum="CacheMode">
Like [constant CACHE_MODE_REUSE], but the cache is checked for the main resource (the one requested to be loaded) as well as for each of its subresources. Those already in the cache, as long as the loaded and cached types match, have their data refreshed from storage into the already existing instances. Otherwise, they are recreated as completely new objects.
</constant>
<constant name="CACHE_MODE_IGNORE_DEEP" value="3" enum="CacheMode">
Like [constant CACHE_MODE_IGNORE], but propagated recursively down the tree of dependencies (external resources).
</constant>
<constant name="CACHE_MODE_REPLACE_DEEP" value="4" enum="CacheMode">
Like [constant CACHE_MODE_REPLACE], but propagated recursively down the tree of dependencies (external resources).
</constant>
</constants>
</class>
12 changes: 9 additions & 3 deletions doc/classes/ResourceLoader.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,19 @@
The resource was loaded successfully and can be accessed via [method load_threaded_get].
</constant>
<constant name="CACHE_MODE_IGNORE" value="0" enum="CacheMode">
The resource is always loaded from disk, even if a cache entry exists for its path, and the newly loaded copy will not be cached. Instances loaded with this mode will exist independently.
Neither the main resource (the one requested to be loaded) nor any of its subresources are retrieved from cache nor stored into it. Dependencies (external resources) are loaded with [constant CACHE_MODE_REUSE].
</constant>
<constant name="CACHE_MODE_REUSE" value="1" enum="CacheMode">
If a resource is cached, returns the cached reference. Otherwise it's loaded from disk.
The main resource (the one requested to be loaded), its subresources, and its dependencies (external resources) are retrieved from cache if present, instead of loaded. Those not cached are loaded and then stored into the cache. The same rules are propagated recursively down the tree of dependencies (external resources).
</constant>
<constant name="CACHE_MODE_REPLACE" value="2" enum="CacheMode">
The resource is always loaded from disk, even if a cache entry exists for its path. The cached entry will be replaced by the newly loaded copy.
Like [constant CACHE_MODE_REUSE], but the cache is checked for the main resource (the one requested to be loaded) as well as for each of its subresources. Those already in the cache, as long as the loaded and cached types match, have their data refreshed from storage into the already existing instances. Otherwise, they are recreated as completely new objects.
</constant>
<constant name="CACHE_MODE_IGNORE_DEEP" value="3" enum="CacheMode">
Like [constant CACHE_MODE_IGNORE], but propagated recursively down the tree of dependencies (external resources).
</constant>
<constant name="CACHE_MODE_REPLACE_DEEP" value="4" enum="CacheMode">
Like [constant CACHE_MODE_REPLACE], but propagated recursively down the tree of dependencies (external resources).
</constant>
</constants>
</class>
3 changes: 2 additions & 1 deletion modules/gdscript/gdscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2817,7 +2817,8 @@ Ref<GDScript> GDScriptLanguage::get_script_by_fully_qualified_name(const String

Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Error err;
Ref<GDScript> scr = GDScriptCache::get_full_script(p_path, err, "", p_cache_mode == CACHE_MODE_IGNORE);
bool ignoring = p_cache_mode == CACHE_MODE_IGNORE || p_cache_mode == CACHE_MODE_IGNORE_DEEP;
Ref<GDScript> scr = GDScriptCache::get_full_script(p_path, err, "", ignoring);

if (err && scr.is_valid()) {
// If !scr.is_valid(), the error was likely from scr->load_source_code(), which already generates an error.
Expand Down
2 changes: 2 additions & 0 deletions modules/mono/csharp_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2854,6 +2854,7 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const
Ref<Resource> existing = ResourceCache::get_ref(p_path);
switch (p_cache_mode) {
case ResourceFormatLoader::CACHE_MODE_IGNORE:
case ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP:
break;
case ResourceFormatLoader::CACHE_MODE_REUSE:
if (existing.is_null()) {
Expand All @@ -2863,6 +2864,7 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const
}
break;
case ResourceFormatLoader::CACHE_MODE_REPLACE:
case ResourceFormatLoader::CACHE_MODE_REPLACE_DEEP:
scr->set_path(p_original_path, true);
break;
}
Expand Down
5 changes: 5 additions & 0 deletions scene/resources/packed_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2045,6 +2045,11 @@ void PackedScene::set_path(const String &p_path, bool p_take_over) {
Resource::set_path(p_path, p_take_over);
}

void PackedScene::set_path_cache(const String &p_path) {
state->set_path(p_path);
Resource::set_path_cache(p_path);
}

void PackedScene::reset_state() {
clear();
}
Expand Down
2 changes: 2 additions & 0 deletions scene/resources/packed_scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ class PackedScene : public Resource {
virtual void reload_from_file() override;

virtual void set_path(const String &p_path, bool p_take_over = false) override;
virtual void set_path_cache(const String &p_path) override;

#ifdef TOOLS_ENABLED
virtual void set_last_modified_time(uint64_t p_time) override {
Resource::set_last_modified_time(p_time);
Expand Down
32 changes: 27 additions & 5 deletions scene/resources/resource_format_text.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ Error ResourceLoaderText::load() {

ext_resources[id].path = path;
ext_resources[id].type = type;
ext_resources[id].load_token = ResourceLoader::_load_start(path, type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, ResourceFormatLoader::CACHE_MODE_REUSE);
ext_resources[id].load_token = ResourceLoader::_load_start(path, type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, cache_mode_for_external);
if (!ext_resources[id].load_token.is_valid()) {
if (ResourceLoader::get_abort_on_missing_resources()) {
error = ERR_FILE_CORRUPT;
Expand Down Expand Up @@ -584,7 +584,7 @@ Error ResourceLoaderText::load() {
if (do_assign) {
if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
} else if (!path.is_resource_file()) {
} else {
res->set_path_cache(path);
}
res->set_scene_unique_id(id);
Expand Down Expand Up @@ -721,6 +721,8 @@ Error ResourceLoaderText::load() {
resource->set_path(res_path);
}
resource->set_as_translation_remapped(translation_remapped);
} else {
resource->set_path_cache(res_path);
}
}
return error;
Expand Down Expand Up @@ -805,8 +807,13 @@ Error ResourceLoaderText::load() {
error = OK;
//get it here
resource = packed_scene;
if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE && !ResourceCache::has(res_path)) {
packed_scene->set_path(res_path);
if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
if (!ResourceCache::has(res_path)) {
packed_scene->set_path(res_path);
}
} else {
packed_scene->get_state()->set_path(res_path);
packed_scene->set_path_cache(res_path);
}

resource_current++;
Expand Down Expand Up @@ -1650,7 +1657,22 @@ Ref<Resource> ResourceFormatLoaderText::load(const String &p_path, const String

ResourceLoaderText loader;
String path = !p_original_path.is_empty() ? p_original_path : p_path;
loader.cache_mode = p_cache_mode;
switch (p_cache_mode) {
case CACHE_MODE_IGNORE:
case CACHE_MODE_REUSE:
case CACHE_MODE_REPLACE:
loader.cache_mode = p_cache_mode;
loader.cache_mode_for_external = CACHE_MODE_REUSE;
break;
case CACHE_MODE_IGNORE_DEEP:
loader.cache_mode = ResourceFormatLoader::CACHE_MODE_IGNORE;
loader.cache_mode_for_external = p_cache_mode;
break;
case CACHE_MODE_REPLACE_DEEP:
loader.cache_mode = ResourceFormatLoader::CACHE_MODE_REPLACE;
loader.cache_mode_for_external = p_cache_mode;
break;
}
loader.use_sub_threads = p_use_sub_threads;
loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
loader.progress = r_progress;
Expand Down
1 change: 1 addition & 0 deletions scene/resources/resource_format_text.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class ResourceLoaderText {
VariantParser::Tag next_tag;

ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE;
ResourceFormatLoader::CacheMode cache_mode_for_external = ResourceFormatLoader::CACHE_MODE_REUSE;

bool use_sub_threads = false;
float *progress = nullptr;
Expand Down

0 comments on commit 5e14402

Please sign in to comment.