-
-
Notifications
You must be signed in to change notification settings - Fork 21.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add cyclic loading abstraction to ResourceLoader
#71478
Add cyclic loading abstraction to ResourceLoader
#71478
Conversation
4121a76
to
b288a60
Compare
if (r_error) { | ||
// As we could open the file, we can assume that any error opening the file is linked to a cyclic reference | ||
*r_error = ERR_CYCLIC_LINK; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems wrong, it will be overridden below by
if (r_error) {
*r_error = OK;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Semantically, it's also weird to set an error in an external variable before actually attempting to load the script which may not at all be an error. It's less descriptive for the reader, and could lead to bugs if there are race conditions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see pre-existing code does the same though with
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
It's a bit weird.
Normally we'd use a local Error err
to keep track of the error, and only set r_error
before returning (on failure, or at the end of the function on success).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All loaders use a pointer for errors instead of editing the reference itself. It's because it's editing the value of the ResourceLoader::ThreadLoadTask
struct error
value.
See this example:
godot/core/io/image_loader.cpp
Lines 150 to 157 in 629796c
Ref<Resource> ResourceFormatLoaderImage::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { | |
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); | |
if (f.is_null()) { | |
if (r_error) { | |
*r_error = ERR_CANT_OPEN; | |
} | |
return Ref<Resource>(); | |
} |
*r_error = ERR_CYCLIC_LINK; | ||
} | ||
|
||
err = OK; | ||
Ref<GDScript> scr = GDScriptCache::get_full_script(p_path, err, "", p_cache_mode == CACHE_MODE_IGNORE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't get_full_script()
tell you that it can't get a full script due to cyclic dependency? Instead of having to make an educated guess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loaders must make educated guesses because when loading, they don't have control anymore.
@akien-mga @vnen The code now tests if the file can be loaded. If so, the error is changed to be a cyclic load error. It will not affect other loaders and there's no presumption to be done about |
b288a60
to
84e84b7
Compare
84e84b7
to
ce2a4fe
Compare
My PR fixes #71610 too. |
I am still not convinced in the approach or whether this a problem, if you cyclic preloads() with scenes, then you will not be able to easily free them eventually. I think its correct that it fails and should not be fixed. The error you receive should let you know that this is a problem and that you should use load() instead and take care of properly freeing this scene manually to break the cyclic link. |
@reduz If I would happen to create a global scope function to free cyclic references, like you wanted to implement, would this PR legitimacy be impacted positively? The usage of the issue #70985 seems to be legitimate. I'll suggest the creation of the Squirrel-like function tomorrow at our GDScript meeting. |
@adamscott Circular reference for scripts should be valid to some extent, because you can have a reference to the parser code (at least how it was meant to be implemented when we discussed about this with @vnen) however cyclic reference for resources is not because its way too difficult to fix and extremely error prone. As such the use case described in the linked issue should always be considered a bug. |
Creating code that checks scripts and see if they contain cyclical references and eventually break them is not very difficult, because all you have to do is eliminate the scripts from the objects that contain it and were marked as unused (hence the cyclical link breaks and the objects are freed). Extending this to the whole resource system is extremely inefficient and would force users to have to do this check for cyclical inclusion errors constantly when instead you can just prevent them on load. |
Closing this PR. The GDScript team will have to discuss how to fix this issue otherwise, taking @reduz comments into account. |
This creates an abstraction for
ResourceLoader
andResourceFormatLoader
to load cyclic resources, ie. ressources currently loading that normally returns a nullResource
when queried.Supersedes #71004
Fixes #70985
Fixes #71610