Skip to content
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

Removed ResourceInteractiveLoader, add built-in threaded loading. #36640

Merged
merged 1 commit into from Feb 28, 2020

Conversation

reduz
Copy link
Member

@reduz reduz commented Feb 28, 2020

ResourceInteractiveLoader was an old vestige of the past, when Godot was aimed mainly at devices that only had one processor, like mobile, low end desktops and some consoles.

Godot has supported loading of resources on threads since a long time, so this old method of loading was obsolete.

In exchange, a new set of functions were added:

Error ResourceLoader.load_threaded_request(path: String, type_hint: String = "", use_sub_threads: bool = false)
ThreadLoadStatus ResourceLoader.load_threaded_get_status(path: String, r_progress: Array = [  ] ) 
Resource ResourceLoader.load_threaded_get(path: String)

First, a thread loading process is requested. It can be a single thread or multiple threads. If you want to focus on loadig times (load as fast as possible), then multiple threads can be specified. If you wan to focus on not affecting the game performance (just background load), then single threaded mode can be specified.

	ResourceLoader.load_threaded_request("res://myresource.tscn","",true)

Status can be requested at any time by calling:

	var status = ResourceLoader.load_threaded_get_status("res://myresource.tscn")

Optionally, progress can be retrieved from the status, too:

	var progress= []
	var status = ResourceLoader.load_threaded_get_status("res://myresource.tscn",progress)
	print("Progress: ",int(progress[0]*100),"%")

Finally, when status is complete (ResourceLoader.THREAD_LOAD_LOADED), the resource can be obtained:

	var res = ResourceLoader.load_threaded_get("res://myresource.tscn")

Calling the above function before thread is done loading will just result in blocking the current thread until the resource has finished loading.

This is a very useful feature because it allows the common pattern of start loading something (like, the first level while on the intro and title screen), and the block until completion, severely reducing load times in games.

It is important to not block the main game thread during this process, else the engine will deadlock. If you want your loading to happen entirely on the main thread, use the old ResourceLoader.load() funciton, don't use these.

Using threaded loading in my tests resulted in an improvement of 4x to 6x times faster loading.

@KoBeWi
Copy link
Member

KoBeWi commented Feb 28, 2020

If you want to focus on loadig times (load as fast as possible), then multiple threads can be specified. If you wan to focus on not affecting the game performance (just background load), then single threaded mode can be specified.

What if I want to load as fast as possible without affecting the game performance?

@dreamsComeTrue
Copy link
Contributor

Will that allow for something like 'level-streaming' in background functionality?
Or more than that - swapping/unloading assets while playing?

@Calinou
Copy link
Member

Calinou commented Feb 28, 2020

What if I want to load as fast as possible without affecting the game performance?

There's a tradeoff here 🙂

When you load resources in the background, start as early as possible so you never have to stall the main thread to wait for a resource to finish loading. Other than that, I don't think much can be done about it.

Will that allow for something like 'level-streaming' in background functionality?
Or more than that - swapping/unloading assets while playing?

This is what load_threaded_request() should allow if you don't use subthreads (which is the default). Only one thread will be used to load the resource, which ensures some CPU resources remain available for the main thread.

@qarmin
Copy link
Contributor

qarmin commented Feb 28, 2020

What about multi-threaded resource loading in editor?
Does this PR implements it?

@reduz
Copy link
Member Author

reduz commented Feb 28, 2020

@KoBeWi The problem is that there is no real metric for this. You can control how much it may affect game performance by the number of threads used, but this is entirely different on each hardware, so in the end the only "safe" value is to just use threads or don't use them.

@reduz
Copy link
Member Author

reduz commented Feb 28, 2020

@qarmin could eventually happen i suppose. Once this is well tested, it can be enabled.

@jejay
Copy link

jejay commented Feb 28, 2020

@KoBeWi The problem is that there is no real metric for this. You can control how much it may affect game performance by the number of threads used, but this is entirely different on each hardware, so in the end the only "safe" value is to just use threads or don't use them.

What about multi threading and lower priority than the main game threads? Would be then the one fits all solution. Probably a mess when trying to implement that cross platform, though. Just thinking loudly. Looks really awesome!

Forget it, just realised my error. This obviously makes no sense when you have a game that is CPU capped without introducing weird stuff like fps caps.

@reduz
Copy link
Member Author

reduz commented Feb 28, 2020

@jejay I really prefer to not have to play with thread priorities nowadays. It just opens a different can of worms. Generally the high amount of cores and schedulers make sure everything more or less works ok,and in most operating systems the main UI or rendering thread already is given more priority so for now I would prefer to not get into that stuff. On iOS as an example, you can start having problems using spinlocks if you have threads of too high or low priority.

@akien-mga akien-mga merged commit 620030b into godotengine:master Feb 28, 2020
@akien-mga
Copy link
Member

Thanks!

@Duroxxigar
Copy link
Contributor

I didn't see that any documentation was added with the addition of these 3 methods. Any plans on making that happen? @reduz

@trommlbomml
Copy link

trommlbomml commented Feb 29, 2020

Really great improvement, but

It is important to not block the main game thread during this process, else the engine will deadlock.

Makes me a bit suspicious. Is it a real deadlock? It is very bad and A runtime error, or at least warning would be way better - if not better get rid of that behavior.

@aabmets
Copy link

aabmets commented Feb 29, 2020

@reduz, with your proposed implementation, the user would have to continuously poll the progress of the loading process before they're safely able to get the resource (without blocking their main process).
Since Godot is heavily focused on Signals, would it not be better to implement a Signal for the resource loader, which the user can hook their get function into?
With a resource_load_complete Signal, the devs are able to pre-empt, I'm guessing, a huge number of bugs and errors from being created by engine users.

@reduz
Copy link
Member Author

reduz commented Mar 8, 2020

@aspenforest A signal may be a bit complex, but with the new callable API, maybe you can just specify a function to get called when it's done. The only downside to this is that it will be called on the main thread. May be enough, though.

@qq715152910
Copy link

load_threaded_request
Isn't this feature currently available?
API documentation already exists this function.
but:When using this feature in 3.2.1, it prompts me that this function cannot be found.

@akien-mga
Copy link
Member

This is a Godot 4.0 feature.

@endragor
Copy link
Contributor

How does this work with OpenGL contexts that may only be active in a single thread at a time? As far as I can tell, calls like glTexImage will fail in a separate thread without additional preparation. And the ideal solution would be to fully load resources in a separate thread, i.e. GPU stuff should be available for use on the GPU.

This would require special synchronization calls both in OpenGL and Vulkan and I don't see such things here, so I wonder how this works. From what I see these calls are not even directly available in GLES2, but Android and iOS provide their own ways to resolve this (Android via EGL, Apple via extensions that mimic GLES3). All in all, it seems there still a lot of work to do before actual multithreaded resource loading is there.

At the sime time, the "interactive loader" was already removed, and it was the main means of loading a scene in chunks, providing more granular feedback of the loading progress to the user.

@PranavSK
Copy link

This is cool. But the API is very weird. Why not use an Object named AsyncLoadResult, which is returned when the request is made. It’s properties could store the path to the resource and wrap the other 2 functions, namely progress, status and a get_result function. Additionally it could have a completed signal. This would work great with the new GDScript async. Also the use of Array like this is very inconsistent. Many other functions expect a non empty array while some (like this one) just replace the passes Array.

@Calinou
Copy link
Member

Calinou commented Aug 29, 2020

@PranavSK Please open a proposal on the Godot proposals repository to request improvements.

@jitspoe
Copy link
Contributor

jitspoe commented Jan 21, 2022

Any chance of this making it over to 3.x? :D I keep running into bizarre issues using multithreading with loading in gdscript. Now I'm having to do scary things like add a 1 second delay to avoid errors and crashes, but I don't feel good about it.

@Calinou
Copy link
Member

Calinou commented Jan 21, 2022

Any chance of this making it over to 3.x? :D I keep running into bizarre issues using multithreading with loading in gdscript. Now I'm having to do scary things like add a 1 second delay to avoid errors and crashes, but I don't feel good about it.

This relies on core refactoring (and breaks compatibility with existing projects), so it can't be backported to 3.x.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet