2014-02-09 22:10:30 -03:00
/**************************************************************************/
/* resource_loader.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-09 22:10:30 -03:00
# pragma once
2020-11-07 19:33:38 -03:00
# include "core/io/resource.h"
2021-08-21 22:52:44 -03:00
# include "core/object/gdvirtual.gen.inc"
2023-05-10 11:23:07 +02:00
# include "core/object/worker_thread_pool.h"
2019-02-16 17:38:09 -03:00
# include "core/os/thread.h"
2014-02-09 22:10:30 -03:00
2025-03-12 12:47:08 -05:00
namespace CoreBind {
2025-01-18 19:46:03 +08:00
class ResourceLoader ;
}
2023-02-20 19:00:26 +01:00
class ConditionVariable ;
2024-02-04 16:59:54 +01:00
template < int Tag >
class SafeBinaryMutex ;
2021-06-04 18:03:15 +02:00
class ResourceFormatLoader : public RefCounted {
GDCLASS ( ResourceFormatLoader , RefCounted ) ;
2018-06-11 02:59:53 +02:00
2021-02-11 14:18:45 -03:00
public :
enum CacheMode {
2024-02-22 12:53:19 +01:00
CACHE_MODE_IGNORE ,
CACHE_MODE_REUSE ,
CACHE_MODE_REPLACE ,
CACHE_MODE_IGNORE_DEEP ,
CACHE_MODE_REPLACE_DEEP ,
2021-02-11 14:18:45 -03:00
} ;
2018-06-11 02:59:53 +02:00
protected :
static void _bind_methods ( ) ;
2021-08-21 22:52:44 -03:00
GDVIRTUAL0RC ( Vector < String > , _get_recognized_extensions )
2022-10-11 11:40:04 +02:00
GDVIRTUAL2RC ( bool , _recognize_path , String , StringName )
2021-08-21 22:52:44 -03:00
GDVIRTUAL1RC ( bool , _handles_type , StringName )
GDVIRTUAL1RC ( String , _get_resource_type , String )
2023-01-19 19:12:25 +01:00
GDVIRTUAL1RC ( String , _get_resource_script_class , String )
2021-08-21 22:52:44 -03:00
GDVIRTUAL1RC ( ResourceUID : : ID , _get_resource_uid , String )
GDVIRTUAL2RC ( Vector < String > , _get_dependencies , String , bool )
2022-07-14 14:18:18 +02:00
GDVIRTUAL1RC ( Vector < String > , _get_classes_used , String )
2023-01-15 20:33:20 +01:00
GDVIRTUAL2RC ( Error , _rename_dependencies , String , Dictionary )
2021-08-21 22:52:44 -03:00
GDVIRTUAL1RC ( bool , _exists , String )
2025-06-25 22:24:07 +02:00
GDVIRTUAL4RC_REQUIRED ( Variant , _load , String , String , bool , int )
2021-08-21 22:52:44 -03:00
2014-02-09 22:10:30 -03:00
public :
2022-05-03 01:43:50 +02:00
virtual Ref < Resource > load ( const String & p_path , const String & p_original_path = " " , Error * r_error = nullptr , bool p_use_sub_threads = false , float * r_progress = nullptr , CacheMode p_cache_mode = CACHE_MODE_REUSE ) ;
2018-08-10 15:57:43 -03:00
virtual bool exists ( const String & p_path ) const ;
2018-06-11 02:59:53 +02:00
virtual void get_recognized_extensions ( List < String > * p_extensions ) const ;
2014-02-09 22:10:30 -03:00
virtual void get_recognized_extensions_for_type ( const String & p_type , List < String > * p_extensions ) const ;
2017-02-01 09:45:45 -03:00
virtual bool recognize_path ( const String & p_path , const String & p_for_type = String ( ) ) const ;
2018-06-11 02:59:53 +02:00
virtual bool handles_type ( const String & p_type ) const ;
2022-07-14 14:18:18 +02:00
virtual void get_classes_used ( const String & p_path , HashSet < StringName > * r_classes ) ;
2018-06-11 02:59:53 +02:00
virtual String get_resource_type ( const String & p_path ) const ;
2023-01-19 19:12:25 +01:00
virtual String get_resource_script_class ( const String & p_path ) const ;
2021-07-23 16:01:18 -03:00
virtual ResourceUID : : ID get_resource_uid ( const String & p_path ) const ;
2024-09-23 10:42:18 +02:00
virtual bool has_custom_uid_support ( ) const ;
2015-08-23 20:15:56 -03:00
virtual void get_dependencies ( const String & p_path , List < String > * p_dependencies , bool p_add_types = false ) ;
2022-05-13 15:04:37 +02:00
virtual Error rename_dependencies ( const String & p_path , const HashMap < String , String > & p_map ) ;
2017-08-29 19:50:58 -03:00
virtual bool is_import_valid ( const String & p_path ) const { return true ; }
2019-03-04 11:06:15 -03:00
virtual bool is_imported ( const String & p_path ) const { return false ; }
2017-09-20 20:59:19 -03:00
virtual int get_import_order ( const String & p_path ) const { return 0 ; }
2019-04-19 15:54:33 -03:00
virtual String get_import_group_file ( const String & p_path ) const { return " " ; } //no group
2014-02-09 22:10:30 -03:00
virtual ~ ResourceFormatLoader ( ) { }
} ;
2021-02-19 13:35:31 +01:00
VARIANT_ENUM_CAST ( ResourceFormatLoader : : CacheMode )
2023-01-17 14:26:16 +01:00
typedef void ( * ResourceLoadErrorNotify ) ( const String & p_text ) ;
typedef void ( * DependencyErrorNotify ) ( const String & p_loading , const String & p_which , const String & p_type ) ;
2014-02-09 22:10:30 -03:00
2018-10-04 23:00:02 -03:00
typedef Error ( * ResourceLoaderImport ) ( const String & p_path ) ;
2022-05-03 01:43:50 +02:00
typedef void ( * ResourceLoadedCallback ) ( Ref < Resource > p_resource , const String & p_path ) ;
2018-10-04 23:00:02 -03:00
2016-03-09 00:00:52 +01:00
class ResourceLoader {
2024-07-10 12:01:22 +02:00
friend class LoadToken ;
2025-03-12 12:47:08 -05:00
friend class CoreBind : : ResourceLoader ;
2024-07-10 12:01:22 +02:00
2014-02-09 22:10:30 -03:00
enum {
MAX_LOADERS = 64
} ;
2016-03-09 00:00:52 +01:00
2024-09-05 08:35:05 +02:00
struct ThreadLoadTask ;
2020-02-28 08:27:04 -03:00
public :
enum ThreadLoadStatus {
THREAD_LOAD_INVALID_RESOURCE ,
THREAD_LOAD_IN_PROGRESS ,
THREAD_LOAD_FAILED ,
THREAD_LOAD_LOADED
} ;
2023-03-05 01:09:18 +01:00
enum LoadThreadMode {
LOAD_THREAD_FROM_CURRENT ,
LOAD_THREAD_SPAWN_SINGLE ,
LOAD_THREAD_DISTRIBUTE ,
} ;
struct LoadToken : public RefCounted {
String local_path ;
String user_path ;
2024-07-15 10:30:02 +02:00
uint32_t user_rc = 0 ; // Having user RC implies regular RC incremented in one, until the user RC reaches zero.
2024-09-05 08:35:05 +02:00
ThreadLoadTask * task_if_unregistered = nullptr ;
2023-03-05 01:09:18 +01:00
void clear ( ) ;
virtual ~ LoadToken ( ) ;
} ;
2023-02-20 19:00:26 +01:00
static const int BINARY_MUTEX_TAG = 1 ;
2024-07-15 10:30:02 +02:00
static Ref < LoadToken > _load_start ( const String & p_path , const String & p_type_hint , LoadThreadMode p_thread_mode , ResourceFormatLoader : : CacheMode p_cache_mode , bool p_for_user = false ) ;
2023-03-05 01:09:18 +01:00
static Ref < Resource > _load_complete ( LoadToken & p_load_token , Error * r_error ) ;
2020-02-28 08:27:04 -03:00
private :
2024-07-15 10:30:02 +02:00
static LoadToken * _load_threaded_request_reuse_user_token ( const String & p_path ) ;
static void _load_threaded_request_setup_user_token ( LoadToken * p_token , const String & p_path ) ;
2023-03-05 01:09:18 +01:00
static Ref < Resource > _load_complete_inner ( LoadToken & p_load_token , Error * r_error , MutexLock < SafeBinaryMutex < BINARY_MUTEX_TAG > > & p_thread_load_lock ) ;
2018-06-11 02:59:53 +02:00
static Ref < ResourceFormatLoader > loader [ MAX_LOADERS ] ;
2014-02-09 22:10:30 -03:00
static int loader_count ;
static bool timestamp_on_load ;
static void * err_notify_ud ;
static ResourceLoadErrorNotify err_notify ;
2015-08-23 20:15:56 -03:00
static void * dep_err_notify_ud ;
static DependencyErrorNotify dep_err_notify ;
2014-02-09 22:10:30 -03:00
static bool abort_on_missing_resource ;
2022-04-28 22:49:10 +02:00
static bool create_missing_resources_if_class_unavailable ;
2020-03-17 07:33:00 +01:00
static HashMap < String , Vector < String > > translation_remaps ;
2017-06-28 17:00:18 -03:00
2020-04-02 01:20:12 +02:00
static String _path_remap ( const String & p_path , bool * r_translation_remapped = nullptr ) ;
2017-06-28 17:00:18 -03:00
friend class Resource ;
static SelfList < Resource > : : List remapped_list ;
2014-02-09 22:10:30 -03:00
2017-08-31 18:57:03 -03:00
friend class ResourceFormatImporter ;
2023-03-05 01:09:18 +01:00
2022-05-03 01:43:50 +02:00
static Ref < Resource > _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 ) ;
2017-08-31 18:57:03 -03:00
2018-10-29 16:36:31 -03:00
static ResourceLoadedCallback _loaded_callback ;
2024-01-08 22:36:19 -03:00
static Ref < ResourceFormatLoader > _find_custom_resource_format_loader ( const String & path ) ;
2019-02-16 17:38:09 -03:00
2020-02-28 08:27:04 -03:00
struct ThreadLoadTask {
2023-05-10 11:23:07 +02:00
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).
ConditionVariable * cond_var = nullptr ; // In not in the worker pool or already awaiting, this is used as a secondary awaiting mechanism.
2024-07-29 20:20:55 +03:00
uint32_t awaiters_count = 0 ;
2023-03-05 01:09:18 +01:00
LoadToken * load_token = nullptr ;
2020-02-28 08:27:04 -03:00
String local_path ;
String type_hint ;
2024-01-05 19:17:51 +01:00
float progress = 0.0f ;
float max_reported_progress = 0.0f ;
2024-06-28 11:16:59 +02:00
uint64_t last_progress_check_main_thread_frame = UINT64_MAX ;
2020-02-28 08:27:04 -03:00
ThreadLoadStatus status = THREAD_LOAD_IN_PROGRESS ;
2021-02-11 14:18:45 -03:00
ResourceFormatLoader : : CacheMode cache_mode = ResourceFormatLoader : : CACHE_MODE_REUSE ;
2020-03-02 19:17:20 +01:00
Error error = OK ;
2022-05-03 01:43:50 +02:00
Ref < Resource > resource ;
2022-05-19 17:00:06 +02:00
HashSet < String > sub_tasks ;
2024-09-05 09:48:13 +02:00
2025-11-24 17:31:01 +01:00
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 ;
2024-09-05 09:48:13 +02:00
struct ResourceChangedConnection {
Resource * source = nullptr ;
Callable callable ;
uint32_t flags = 0 ;
} ;
LocalVector < ResourceChangedConnection > resource_changed_connections ;
2019-02-16 17:38:09 -03:00
2025-11-24 17:31:01 +01:00
ThreadLoadTask ( ) :
awaited ( false ) ,
need_wait ( true ) ,
in_progress_check ( false ) ,
use_sub_threads ( false ) { }
} ;
2024-07-10 12:53:14 +02:00
static void _run_load_task ( void * p_userdata ) ;
2023-03-05 01:09:18 +01:00
2025-03-12 09:18:39 +02:00
static thread_local bool import_thread ;
2023-03-05 01:09:18 +01:00
static thread_local int load_nesting ;
2024-06-25 14:12:40 +02:00
static thread_local HashMap < int , HashMap < String , Ref < Resource > > > res_ref_overrides ; // Outermost key is nesting level.
2024-06-19 07:27:33 +02:00
static thread_local Vector < String > load_paths_stack ;
2024-09-05 09:48:13 +02:00
static thread_local ThreadLoadTask * curr_load_task ;
2024-07-18 14:54:58 +02:00
2023-03-05 01:09:18 +01:00
static SafeBinaryMutex < BINARY_MUTEX_TAG > thread_load_mutex ;
2024-07-18 14:54:58 +02:00
friend SafeBinaryMutex < BINARY_MUTEX_TAG > & _get_res_loader_mutex ( ) ;
2020-02-28 08:27:04 -03:00
static HashMap < String , ThreadLoadTask > thread_load_tasks ;
2023-03-05 01:09:18 +01:00
static bool cleaning_tasks ;
static HashMap < String , LoadToken * > user_load_tokens ;
2019-01-27 19:24:55 -03:00
2020-02-28 08:27:04 -03:00
static float _dependency_get_progress ( const String & p_path ) ;
2018-06-11 02:59:53 +02:00
2024-06-28 11:16:59 +02:00
static bool _ensure_load_progress ( ) ;
2025-01-18 19:46:03 +08:00
static String _validate_local_path ( const String & p_path ) ;
2014-02-09 22:10:30 -03:00
public :
2023-03-05 01:09:18 +01:00
static Error load_threaded_request ( const String & p_path , const String & p_type_hint = " " , bool p_use_sub_threads = false , ResourceFormatLoader : : CacheMode p_cache_mode = ResourceFormatLoader : : CACHE_MODE_REUSE ) ;
2020-02-28 08:27:04 -03:00
static ThreadLoadStatus load_threaded_get_status ( const String & p_path , float * r_progress = nullptr ) ;
2022-05-03 01:43:50 +02:00
static Ref < Resource > load_threaded_get ( const String & p_path , Error * r_error = nullptr ) ;
2020-02-28 08:27:04 -03:00
2023-05-09 18:59:59 +02:00
static bool is_within_load ( ) { return load_nesting > 0 ; }
2024-09-05 09:48:13 +02:00
static void resource_changed_connect ( Resource * p_source , const Callable & p_callable , uint32_t p_flags ) ;
static void resource_changed_disconnect ( Resource * p_source , const Callable & p_callable ) ;
static void resource_changed_emit ( Resource * p_source ) ;
2022-05-03 01:43:50 +02:00
static Ref < Resource > load ( const String & p_path , const String & p_type_hint = " " , ResourceFormatLoader : : CacheMode p_cache_mode = ResourceFormatLoader : : CACHE_MODE_REUSE , Error * r_error = nullptr ) ;
2018-08-10 15:57:43 -03:00
static bool exists ( const String & p_path , const String & p_type_hint = " " ) ;
2014-02-09 22:10:30 -03:00
static void get_recognized_extensions_for_type ( const String & p_type , List < String > * p_extensions ) ;
2018-06-11 02:59:53 +02:00
static void add_resource_format_loader ( Ref < ResourceFormatLoader > p_format_loader , bool p_at_front = false ) ;
static void remove_resource_format_loader ( Ref < ResourceFormatLoader > p_format_loader ) ;
2022-07-14 14:18:18 +02:00
static void get_classes_used ( const String & p_path , HashSet < StringName > * r_classes ) ;
2014-02-09 22:10:30 -03:00
static String get_resource_type ( const String & p_path ) ;
2023-01-19 19:12:25 +01:00
static String get_resource_script_class ( const String & p_path ) ;
2021-07-23 16:01:18 -03:00
static ResourceUID : : ID get_resource_uid ( const String & p_path ) ;
2025-02-06 16:36:37 +01:00
static bool should_create_uid_file ( const String & p_path ) ;
2015-08-23 20:15:56 -03:00
static void get_dependencies ( const String & p_path , List < String > * p_dependencies , bool p_add_types = false ) ;
2022-05-13 15:04:37 +02:00
static Error rename_dependencies ( const String & p_path , const HashMap < String , String > & p_map ) ;
2017-08-29 19:50:58 -03:00
static bool is_import_valid ( const String & p_path ) ;
2019-04-19 15:54:33 -03:00
static String get_import_group_file ( const String & p_path ) ;
2019-03-04 11:06:15 -03:00
static bool is_imported ( const String & p_path ) ;
2017-03-05 16:44:50 +01:00
2025-03-12 09:18:39 +02:00
static void set_is_import_thread ( bool p_import_thread ) ;
2014-02-09 22:10:30 -03:00
static void set_timestamp_on_load ( bool p_timestamp ) { timestamp_on_load = p_timestamp ; }
2018-12-02 16:24:23 +00:00
static bool get_timestamp_on_load ( ) { return timestamp_on_load ; }
2017-03-05 16:44:50 +01:00
2023-01-19 13:45:04 +01:00
// Loaders can safely use this regardless which thread they are running on.
2014-02-09 22:10:30 -03:00
static void notify_load_error ( const String & p_err ) {
2020-05-14 16:41:43 +02:00
if ( err_notify ) {
2024-05-27 13:20:45 +02:00
MessageQueue : : get_main_singleton ( ) - > push_callable ( callable_mp_static ( err_notify ) . bind ( p_err ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
}
2023-01-17 14:26:16 +01:00
static void set_error_notify_func ( ResourceLoadErrorNotify p_err_notify ) {
2014-02-09 22:10:30 -03:00
err_notify = p_err_notify ;
2015-08-23 20:15:56 -03:00
}
2017-03-05 16:44:50 +01:00
2023-01-19 13:45:04 +01:00
// Loaders can safely use this regardless which thread they are running on.
2015-08-23 20:15:56 -03:00
static void notify_dependency_error ( const String & p_path , const String & p_dependency , const String & p_type ) {
2020-05-14 16:41:43 +02:00
if ( dep_err_notify ) {
2023-09-24 13:43:10 +01:00
if ( Thread : : get_caller_id ( ) = = Thread : : get_main_id ( ) ) {
dep_err_notify ( p_path , p_dependency , p_type ) ;
} else {
2024-05-27 13:20:45 +02:00
MessageQueue : : get_main_singleton ( ) - > push_callable ( callable_mp_static ( dep_err_notify ) . bind ( p_path , p_dependency , p_type ) ) ;
2023-09-24 13:43:10 +01:00
}
2020-05-14 16:41:43 +02:00
}
2015-08-23 20:15:56 -03:00
}
2023-01-17 14:26:16 +01:00
static void set_dependency_error_notify_func ( DependencyErrorNotify p_err_notify ) {
2015-08-23 20:15:56 -03:00
dep_err_notify = p_err_notify ;
2017-03-05 16:44:50 +01:00
}
2014-02-09 22:10:30 -03:00
static void set_abort_on_missing_resources ( bool p_abort ) { abort_on_missing_resource = p_abort ; }
static bool get_abort_on_missing_resources ( ) { return abort_on_missing_resource ; }
2017-06-28 17:00:18 -03:00
static String path_remap ( const String & p_path ) ;
static String import_remap ( const String & p_path ) ;
static void reload_translation_remaps ( ) ;
static void load_translation_remaps ( ) ;
static void clear_translation_remaps ( ) ;
2018-10-04 23:00:02 -03:00
2022-12-06 13:51:06 -05:00
static void clear_thread_load_tasks ( ) ;
2018-10-29 16:36:31 -03:00
static void set_load_callback ( ResourceLoadedCallback p_callback ) ;
2018-10-04 23:00:02 -03:00
static ResourceLoaderImport import ;
2018-06-11 02:59:53 +02:00
2024-01-08 22:36:19 -03:00
static bool add_custom_resource_format_loader ( const String & script_path ) ;
2018-06-11 02:59:53 +02:00
static void add_custom_loaders ( ) ;
static void remove_custom_loaders ( ) ;
2019-01-27 19:24:55 -03:00
2022-04-28 22:49:10 +02:00
static void set_create_missing_resources_if_class_unavailable ( bool p_enable ) ;
_FORCE_INLINE_ static bool is_creating_missing_resources_if_class_unavailable_enabled ( ) { return create_missing_resources_if_class_unavailable ; }
2024-06-25 14:12:40 +02:00
static Ref < Resource > ensure_resource_ref_override_for_outer_load ( const String & p_path , const String & p_res_type ) ;
static Ref < Resource > get_resource_ref_override ( const String & p_path ) ;
2023-03-05 01:09:18 +01:00
static bool is_cleaning_tasks ( ) ;
2024-09-05 08:52:37 +02:00
static Vector < String > list_directory ( const String & p_directory ) ;
2019-01-27 19:24:55 -03:00
static void initialize ( ) ;
static void finalize ( ) ;
2014-02-09 22:10:30 -03:00
} ;