summaryrefslogtreecommitdiffstats
path: root/core/io/resource_loader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/io/resource_loader.cpp')
-rw-r--r--core/io/resource_loader.cpp93
1 files changed, 61 insertions, 32 deletions
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 6721ec0953..c3c37aa89d 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -36,6 +36,7 @@
#include "core/object/script_language.h"
#include "core/os/condition_variable.h"
#include "core/os/os.h"
+#include "core/os/safe_binary_mutex.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
#include "core/variant/variant_parser.h"
@@ -187,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");
@@ -239,16 +242,20 @@ ResourceLoader::LoadToken::~LoadToken() {
}
Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) {
+ const String &original_path = p_original_path.is_empty() ? p_path : p_original_path;
load_nesting++;
if (load_paths_stack->size()) {
thread_load_mutex.lock();
- HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(load_paths_stack->get(load_paths_stack->size() - 1));
- if (E) {
- E->value.sub_tasks.insert(p_path);
+ const String &parent_task_path = load_paths_stack->get(load_paths_stack->size() - 1);
+ HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(parent_task_path);
+ // Avoid double-tracking, for progress reporting, resources that boil down to a remapped path containing the real payload (e.g., imported resources).
+ bool is_remapped_load = original_path == parent_task_path;
+ if (E && !is_remapped_load) {
+ E->value.sub_tasks.insert(p_original_path);
}
thread_load_mutex.unlock();
}
- load_paths_stack->push_back(p_path);
+ load_paths_stack->push_back(original_path);
// Try all loaders and pick the first match for the type hint
bool found = false;
@@ -258,7 +265,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
continue;
}
found = true;
- res = loader[i]->load(p_path, !p_original_path.is_empty() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
+ res = loader[i]->load(p_path, original_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
if (!res.is_null()) {
break;
}
@@ -338,9 +345,21 @@ 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) {
- load_task.resource->set_path(load_task.local_path);
+ 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, replacing);
+ } else {
+ load_task.resource->set_path_cache(load_task.local_path);
}
if (load_task.xl_remapped) {
@@ -359,6 +378,17 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
if (_loaded_callback) {
_loaded_callback(load_task.resource, load_task.local_path);
}
+ } else if (!ignoring) {
+ Ref<Resource> existing = ResourceCache::get_ref(load_task.local_path);
+ if (existing.is_valid()) {
+ load_task.resource = existing;
+ load_task.status = THREAD_LOAD_LOADED;
+ load_task.progress = 1.0;
+
+ if (_loaded_callback) {
+ _loaded_callback(load_task.resource, load_task.local_path);
+ }
+ }
}
thread_load_mutex.unlock();
@@ -376,7 +406,7 @@ static String _validate_local_path(const String &p_path) {
if (uid != ResourceUID::INVALID_ID) {
return ResourceUID::get_singleton()->get_id_path(uid);
} else if (p_path.is_relative_path()) {
- return "res://" + p_path;
+ return ("res://" + p_path).simplify_path();
} else {
return ProjectSettings::get_singleton()->localize_path(p_path);
}
@@ -461,7 +491,7 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path,
load_task.type_hint = p_type_hint;
load_task.cache_mode = p_cache_mode;
load_task.use_sub_threads = p_thread_mode == LOAD_THREAD_DISTRIBUTE;
- if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ if (p_cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) {
Ref<Resource> existing = ResourceCache::get_ref(local_path);
if (existing.is_valid()) {
//referencing is fine
@@ -507,20 +537,20 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path,
float ResourceLoader::_dependency_get_progress(const String &p_path) {
if (thread_load_tasks.has(p_path)) {
ThreadLoadTask &load_task = thread_load_tasks[p_path];
+ float current_progress = 0.0;
int dep_count = load_task.sub_tasks.size();
if (dep_count > 0) {
- float dep_progress = 0;
for (const String &E : load_task.sub_tasks) {
- dep_progress += _dependency_get_progress(E);
+ current_progress += _dependency_get_progress(E);
}
- dep_progress /= float(dep_count);
- dep_progress *= 0.5;
- dep_progress += load_task.progress * 0.5;
- return dep_progress;
+ current_progress /= float(dep_count);
+ current_progress *= 0.5;
+ current_progress += load_task.progress * 0.5;
} else {
- return load_task.progress;
+ current_progress = load_task.progress;
}
-
+ load_task.max_reported_progress = MAX(load_task.max_reported_progress, current_progress);
+ return load_task.max_reported_progress;
} else {
return 1.0; //assume finished loading it so it no longer exists
}
@@ -628,16 +658,14 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro
if (load_task.task_id != 0) {
// Loading thread is in the worker pool.
- load_task.awaited = true;
thread_load_mutex.unlock();
Error err = WorkerThreadPool::get_singleton()->wait_for_task_completion(load_task.task_id);
if (err == ERR_BUSY) {
- // The WorkerThreadPool has scheduled tasks in a way that the current load depends on
- // another one in a lower stack frame. Restart such load here. When the stack is eventually
- // unrolled, the original load will have been notified to go on.
-#ifdef DEV_ENABLED
- print_verbose("ResourceLoader: Load task happened to wait on another one deep in the call stack. Attempting to avoid deadlock by re-issuing the load now.");
-#endif
+ // The WorkerThreadPool has reported that the current task wants to await on an older one.
+ // That't not allowed for safety, to avoid deadlocks. Fortunately, though, in the context of
+ // resource loading that means that the task to wait for can be restarted here to break the
+ // cycle, with as much recursion into this process as needed.
+ // When the stack is eventually unrolled, the original load will have been notified to go on.
// CACHE_MODE_IGNORE is needed because, otherwise, the new request would just see there's
// an ongoing load for that resource and wait for it again. This value forces a new load.
Ref<ResourceLoader::LoadToken> token = _load_start(load_task.local_path, load_task.type_hint, LOAD_THREAD_DISTRIBUTE, ResourceFormatLoader::CACHE_MODE_IGNORE);
@@ -650,6 +678,7 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro
} else {
DEV_ASSERT(err == OK);
thread_load_mutex.lock();
+ load_task.awaited = true;
}
} else {
// Loading thread is main or user thread.
@@ -918,7 +947,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
}
// Fallback to p_path if new_path does not exist.
- if (!FileAccess::exists(new_path)) {
+ if (!FileAccess::exists(new_path + ".import") && !FileAccess::exists(new_path)) {
WARN_PRINT(vformat("Translation remap '%s' does not exist. Falling back to '%s'.", new_path, p_path));
new_path = p_path;
}
@@ -1010,8 +1039,9 @@ void ResourceLoader::load_translation_remaps() {
Array langs = remaps[E];
Vector<String> lang_remaps;
lang_remaps.resize(langs.size());
- for (int i = 0; i < langs.size(); i++) {
- lang_remaps.write[i] = langs[i];
+ String *lang_remaps_ptrw = lang_remaps.ptrw();
+ for (const Variant &lang : langs) {
+ *lang_remaps_ptrw++ = lang;
}
translation_remaps[String(E)] = lang_remaps;
@@ -1090,7 +1120,7 @@ void ResourceLoader::set_load_callback(ResourceLoadedCallback p_callback) {
ResourceLoadedCallback ResourceLoader::_loaded_callback = nullptr;
-Ref<ResourceFormatLoader> ResourceLoader::_find_custom_resource_format_loader(String path) {
+Ref<ResourceFormatLoader> ResourceLoader::_find_custom_resource_format_loader(const String &path) {
for (int i = 0; i < loader_count; ++i) {
if (loader[i]->get_script_instance() && loader[i]->get_script_instance()->get_script()->get_path() == path) {
return loader[i];
@@ -1099,7 +1129,7 @@ Ref<ResourceFormatLoader> ResourceLoader::_find_custom_resource_format_loader(St
return Ref<ResourceFormatLoader>();
}
-bool ResourceLoader::add_custom_resource_format_loader(String script_path) {
+bool ResourceLoader::add_custom_resource_format_loader(const String &script_path) {
if (_find_custom_resource_format_loader(script_path).is_valid()) {
return false;
}
@@ -1111,11 +1141,10 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) {
Ref<Script> s = res;
StringName ibt = s->get_instance_base_type();
bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatLoader");
- ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceLoader: " + script_path + ".");
+ ERR_FAIL_COND_V_MSG(!valid_type, false, vformat("Failed to add a custom resource loader, script '%s' does not inherit 'ResourceFormatLoader'.", script_path));
Object *obj = ClassDB::instantiate(ibt);
-
- ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + ".");
+ ERR_FAIL_NULL_V_MSG(obj, false, vformat("Failed to add a custom resource loader, cannot instantiate '%s'.", ibt));
Ref<ResourceFormatLoader> crl = Object::cast_to<ResourceFormatLoader>(obj);
crl->set_script(s);