This page documents the ResourceLoader singleton, the ResourceFormatLoader interface, cache modes, and the threaded loading system. For details on the Resource base class itself, see Resource Class. For the resource cache internals, see Resource Cache. For the save-side counterpart, see ResourceSaver.
ResourceLoader is Godot's central dispatch point for loading resources from disk. It is a static singleton class defined in core/io/resource_loader.h103-311 that maintains a registry of up to 64 ResourceFormatLoader instances. When a load is requested, ResourceLoader queries each registered loader in order, delegates to the first one that recognizes the file, caches the result, and returns a Ref<Resource>.
The system has two main layers:
ResourceLoader — the static singleton handling path resolution, cache lookup, thread coordination, and loader dispatch.ResourceFormatLoader — a RefCounted-based interface that format-specific implementations override to parse actual file data.Diagram: ResourceLoader Component Overview
Sources: core/io/resource_loader.h103-311 core/io/resource_loader.cpp58-60
ResourceFormatLoader (declared in core/io/resource_loader.h48-95) is a RefCounted class with GDVIRTUAL methods. Subclasses implement the methods that correspond to their file format. All virtual methods have default C++ implementations that call through GDVIRTUAL, so they can be overridden from GDScript.
| Method | Signature | Purpose |
|---|---|---|
_get_recognized_extensions | → Vector<String> | List of file extensions this loader handles |
_recognize_path | (path, type) → bool | Override path recognition logic |
_handles_type | (type) → bool | Whether this loader produces the named type |
_get_resource_type | (path) → String | Inspect the type without fully loading |
_get_resource_script_class | (path) → String | Return script class name stored in file header |
_get_resource_uid | (path) → int | Return the UID associated with the resource |
_get_dependencies | (path, add_types) → Vector<String> | List external resource dependencies |
_get_classes_used | (path) → Vector<String> | Classes referenced inside the file |
_rename_dependencies | (path, renames) → Error | Rewrite dependency paths in-file |
_exists | (path) → bool | Check file existence without loading |
_load | (path, original_path, use_sub_threads, cache_mode) → Variant | Required. Load and return the resource or an Error code |
The _load virtual is marked GDVIRTUAL4RC_REQUIRED (core/io/resource_loader.h73), meaning GDScript subclasses must implement it.
recognize_path() calls _recognize_path if overridden; otherwise it compares the path's extension against get_recognized_extensions() with a case-insensitive suffix match core/io/resource_loader.cpp62-83
If _get_resource_uid is not overridden, the default implementation reads the sidecar <path>.uid file core/io/resource_loader.cpp118-129 Loaders that store UIDs in the file header should override _get_resource_uid and return true from has_custom_uid_support().
Diagram: ResourceFormatLoader Interface (GDVirtual bindings)
Sources: core/io/resource_loader.h48-95 core/io/resource_loader.cpp62-221
ResourceFormatLoader::CacheMode controls how a load interacts with ResourceCache. It is defined as an enum in core/io/resource_loader.h52-57 and exposed to scripts.
| Enum Value | Int | Behavior |
|---|---|---|
CACHE_MODE_IGNORE | 0 | Skip cache on read and write. Dependencies use CACHE_MODE_REUSE. |
CACHE_MODE_REUSE | 1 | Return cached instance if present; cache newly loaded resource. (default) |
CACHE_MODE_REPLACE | 2 | Like REUSE, but refreshes an already-cached instance in-place via copy_from(). |
CACHE_MODE_IGNORE_DEEP | 3 | Like IGNORE, propagated recursively to all external dependencies. |
CACHE_MODE_REPLACE_DEEP | 4 | Like REPLACE, propagated recursively to all external dependencies. |
In practice, REPLACE is used by the editor when reloading a modified resource without invalidating existing references to it. The "deep" variants affect what cache mode is passed down when loading [ext_resource] entries inside .tscn/.tres files.
Sources: core/io/resource_loader.h52-57 core/io/resource_loader.cpp165-182
Before any load begins, ResourceLoader normalizes the path:
uid://...), it is resolved to a filesystem path via ResourceUID.p_original_path and the remapped path becomes p_path.Sources: core/io/resource_loader.cpp475-484 core/io/resource_loader.cpp363-367
ResourceLoader::load() is the primary synchronous entry point (core/io/resource_loader.cpp511-534).
Internal call sequence:
Key internals:
_load_start() core/io/resource_loader.cpp536-640 creates a ThreadLoadTask, checks for an in-flight load of the same path (to avoid duplicate work), and either runs the task immediately (synchronous) or enqueues it in WorkerThreadPool._load() core/io/resource_loader.cpp273-331 iterates registered loaders and calls the first matching one's load() method.set_path() on the resource inserts it into ResourceCache unless CACHE_MODE_IGNORE is active core/io/resource.cpp79-116Sources: core/io/resource_loader.cpp273-331 core/io/resource_loader.cpp511-534 core/io/resource_loader.cpp536-640 core/io/resource.cpp79-116
ResourceLoader supports background loading via three public functions intended to be used together:
| Function | Purpose |
|---|---|
load_threaded_request(path, type_hint, use_sub_threads, cache_mode) | Enqueue a background load and return OK if started |
load_threaded_get_status(path, progress[]) | Poll status; optionally fills a progress float |
load_threaded_get(path) | Block until complete and return the loaded resource |
ResourceLoader::LoadToken (core/io/resource_loader.h134-143) is a RefCounted handle representing an in-flight load. Format loaders (like ResourceLoaderBinary and ResourceLoaderText) hold Ref<LoadToken> for each external dependency they kick off in parallel, then call ResourceLoader::_load_complete() on those tokens when they need the result.
This allows a .tscn file to kick off parallel background loads for all [ext_resource] entries before sequentially parsing the rest of the file.
The internal ThreadLoadTask struct tracks everything about an in-flight load, including task_id (from WorkerThreadPool), status (THREAD_LOAD_IN_PROGRESS, etc.), and the resulting resource.
When _load_complete() waits on a pool task and WorkerThreadPool reports that waiting would deadlock, the engine re-runs the blocked task directly on the waiting thread to break the cycle. This is the mechanism that handles cyclic scene dependencies without hanging.
Diagram: Threaded Loading State Machine
Sources: core/io/resource_loader.h121-143 core/io/resource_loader.cpp335-473 core/io/resource_loader.cpp810-944
Three format loaders are registered at engine startup:
| Class | File | Formats |
|---|---|---|
ResourceFormatLoaderBinary | core/io/resource_format_binary.h | .res, .scn (binary) |
ResourceFormatLoaderText | scene/resources/resource_format_text.h | .tres, .tscn (text) |
ResourceFormatImporter | core/io/resource_importer.h | All imported formats via .import sidecar |
ResourceLoaderText)Parses Godot's text-based resource format. During load(), it:
[ext_resource] tags and calls ResourceLoader::_load_start() on each, obtaining a LoadToken per dependency scene/resources/resource_format_text.cpp145-148[sub_resource] sections, instantiating objects.ResourceLoader::_load_complete() on each token when the actual property value is needed scene/resources/resource_format_text.cpp148-162ResourceFormatImporter)Handles source assets that have been imported (e.g. .png → .ctex). It reads the .import sidecar file to find the true path and type of the imported output, then delegates to ResourceLoader::load_internal().
Sources: scene/resources/resource_format_text.cpp126-181 core/io/resource_importer.cpp44-170
During background loads, Resource::connect_changed() and disconnect_changed() cannot safely call into the signal system from off-main-thread. The loader intercepts these calls and queues them on the active ThreadLoadTask:
resource_changed_connect() core/io/resource_loader.cpp958-973resource_changed_disconnect() core/io/resource_loader.cpp976-988resource_changed_emit() core/io/resource_loader.cpp990-1000Once the load completes and is collected on the main thread, the connections are migrated to the real signal system.
Sources: core/io/resource_loader.cpp958-1000 core/io/resource.cpp209-218
| Method | Description |
|---|---|
load(path, type_hint, cache_mode) | Synchronous blocking load |
load_threaded_request(...) | Start background load |
load_threaded_get_status(...) | Poll background load state |
load_threaded_get(...) | Collect background load result |
exists(path, type_hint) | Check if a recognized resource exists at path |
get_dependencies(path) | List external resource dependencies |
add_resource_format_loader(...) | Register a new format loader |
Sources: core/io/resource_loader.h147-311 core/io/resource_loader.cpp141-271
Refresh this wiki
This wiki was recently refreshed. Please wait 3 days to refresh again.