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 22, 2024
1 parent 8bad68d commit ec4c19d
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 27 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
29 changes: 22 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,20 @@ 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;
break;
case CACHE_MODE_IGNORE_DEEP:
loader.cache_mode = ResourceFormatLoader::CACHE_MODE_IGNORE;
break;
case CACHE_MODE_REPLACE_DEEP:
loader.cache_mode = ResourceFormatLoader::CACHE_MODE_REPLACE;
break;
}
loader.cache_mode_for_external = p_cache_mode;
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">
Resource and subresources are neither retrieved from nor stored into the resource cache. External resources are loaded with [constant CACHE_MODE_REUSE].
</constant>
<constant name="CACHE_MODE_REUSE" value="1" enum="CacheMode">
Resource, subresources, and external resources are retrieved from cache if present, instead of loaded. If not present, they are loaded and stored into the cache. The same rules are propagated to external resources.
</constant>
<constant name="CACHE_MODE_REPLACE" value="2" enum="CacheMode">
Like [constant CACHE_MODE_REUSE], but both the main resource and its subresources, if already in the cache, have their data refreshed instead of being recreated as completely new instances.
</constant>
<constant name="CACHE_MODE_IGNORE_DEEP" value="3" enum="CacheMode">
Like [constant CACHE_MODE_IGNORE], but propagates recursively among loads of external resources.
</constant>
<constant name="CACHE_MODE_REPLACE_DEEP" value="4" enum="CacheMode">
Like [constant CACHE_MODE_REPLACE], but propagates recursively among loads of 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.
Resource and subresources are neither retrieved from nor stored into the resource cache. 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.
Resource, subresources, and external resources are retrieved from cache if present, instead of loaded. If not present, they are loaded and stored into the cache. The same rules are propagated to 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 both the main resource and its subresources, if already in the cache, have their data refreshed instead of being recreated as completely new instances.
</constant>
<constant name="CACHE_MODE_IGNORE_DEEP" value="3" enum="CacheMode">
Like [constant CACHE_MODE_IGNORE], but propagates recursively among loads of external resources.
</constant>
<constant name="CACHE_MODE_REPLACE_DEEP" value="4" enum="CacheMode">
Like [constant CACHE_MODE_REPLACE], but propagates recursively among loads of external resources.
</constant>
</constants>
</class>
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
30 changes: 25 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,20 @@ 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;
break;
case CACHE_MODE_IGNORE_DEEP:
loader.cache_mode = ResourceFormatLoader::CACHE_MODE_IGNORE;
break;
case CACHE_MODE_REPLACE_DEEP:
loader.cache_mode = ResourceFormatLoader::CACHE_MODE_REPLACE;
break;
}
loader.cache_mode_for_external = p_cache_mode;
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 ec4c19d

Please sign in to comment.