diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 238db4bb7ae..c5975653c5d 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -666,6 +666,14 @@ Ref 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]; + if (load_task.in_progress_check) { + // Given the fact that any resource loaded when an outer stack frame is + // loading another one is considered a dependency of it, for progress + // tracking purposes, a cycle can happen if even if the original resource + // graphs involved have none. For instance, preload() can cause this. + return load_task.max_reported_progress; + } + load_task.in_progress_check = true; float current_progress = 0.0; int dep_count = load_task.sub_tasks.size(); if (dep_count > 0) { @@ -679,6 +687,7 @@ float ResourceLoader::_dependency_get_progress(const String &p_path) { current_progress = load_task.progress; } load_task.max_reported_progress = MAX(load_task.max_reported_progress, current_progress); + load_task.in_progress_check = false; return load_task.max_reported_progress; } else { return 1.0; //assume finished loading it so it no longer exists diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index c300b0d925b..33b79b5a800 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -176,10 +176,8 @@ private: struct ThreadLoadTask { WorkerThreadPool::TaskID task_id = 0; // Used if run on a worker thread from the pool. Thread::ID thread_id = 0; // Used if running on an user thread (e.g., simple non-threaded load). - bool awaited = false; // If it's in the pool, this helps not awaiting from more than one dependent thread. ConditionVariable *cond_var = nullptr; // In not in the worker pool or already awaiting, this is used as a secondary awaiting mechanism. uint32_t awaiters_count = 0; - bool need_wait = true; LoadToken *load_token = nullptr; String local_path; String type_hint; @@ -190,17 +188,26 @@ private: ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE; Error error = OK; Ref resource; - bool use_sub_threads = false; HashSet sub_tasks; + bool awaited : 1; // If it's in the pool, this helps not awaiting from more than one dependent thread. + bool need_wait : 1; + bool in_progress_check : 1; // Measure against recursion cycles in progress reporting. Cycles are not expected, but can happen due to how it's currently implemented. + bool use_sub_threads : 1; + struct ResourceChangedConnection { Resource *source = nullptr; Callable callable; uint32_t flags = 0; }; LocalVector resource_changed_connections; - }; + ThreadLoadTask() : + awaited(false), + need_wait(true), + in_progress_check(false), + use_sub_threads(false) {} + }; static void _run_load_task(void *p_userdata); static thread_local bool import_thread;