-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
More ergonomic asset loading to avoid "check if asset is loaded" boilerplate #1701
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
What would be interesting to know is why people want to wait for asset to be loaded?
What are the other needs? |
I describe the situation that I have. I have a plugin that works with assets and needs to wait until the assets are loaded.
The second type is a collection of handles for the first type. But how the end user will check the plugin state? .add_system_set(SystemSet::on_update(GameState::Initialize `AND` PluginState::Done))
Does this mean that the plugin resource should return a |
If I understand correctly, you are loading in your plugin directly the two types of asset, and once everything is loaded you bind them together according to the second type. Ideally, your meta asset loader should handle itself loading the assets with data:
to "bypass" the fact that you have to wait for the meta asset to finish loading to access a data asset that you know how to access through a stable identifier, there is the notion of labeled asset. The Gltf loader is doing all that if you want to take a look. Of course, if this is even possible really depends on how the assets you want to load are formatted...
For example rendering will ignore meshes that are not yet loaded: bevy/crates/bevy_render/src/pipeline/render_pipelines.rs Lines 97 to 101 in 45b2db7
For assets that holds themselves an |
While I think the state approach is not bad, I can imagine that keeping track of your essential assets will be annoying and error prone. Imagine forgetting to add your ground collider mesh. On a slower systems they might be not present on-time and the player falls into the void, or even worse, only in 25% of the time. I would suggest that every asset you load will be part of the essential group, unless explicitly overriden. You would check if your essentials has loaded by something similary like: if let LoadState::Loaded =
asset_server.get_load_state() //name not final...
{
state.set_next(AppState::Finished).unwrap();
} Assets that are marked as non-esssential or can be streamed (textures, sounds, etc...) can be part of another group, so they don't delay the loading process to long. Something like |
I like that! We would have to discuss the exact API/naming/handle handling... but it would certainly simplify something very common |
Here are some questions we need to answer:
|
@julhe proposal is to have them essential by default and have an optout with
This wouldn't handle waiting by default and wouldn't change what is currently available, it just adds a method Another question is:
|
A reference counter should be fine! At the end of each frame, the asset system checks the reference count for each asset and frees up unused ones. (But keeping unused assets around in editor mode?)
I think allowing for custom groups is overkill. You could get the same effect by bundeling components into scenes and wait for load completion on theses scenes instead. For now, I would make the API slim:
|
@NiklasEi has made a community crate to address many of these pain points: https://github.com/NiklasEi/bevy_asset_loader I'm going to experiment with it some more, but I think this is an excellent candidate for integration into the engine itself once it matures a bit more. |
Yeah I think it makes that particular pattern much nicer. But before adding a new concept / high level abstraction like AssetCollection, I'd like to see how things like "async system + async asset loading" work out in terms of ergonomics. Ideally we can have lower level primitives that interact with each other ergonomically and allow developers to compose the desired behavior without the need to provide higher level abstractions on top. But if we can't make that work well enough I think integrating something like AssetCollection is probably the right call. |
A new user tried to implement this functionality for our Breakout example game as part of #2094. Here's what they found:
The gist of it is that #1295 is still frustrating, blocking the most natural solution. |
For now, we're going to proceed with a synchronous blocking loading solution, using a loop that sleeps to avoid eating all of the user's compute. |
Hi there! I'm the user who tried to implement the above. First, the use case, in relation to this:
Error checking :) I may be missing something here, but in the breakout example, if an asset load fails, the game proceeds, without text, and with a warning in the console. For reference, the related code is this:
I don't see any direct way to control error cases. Maybe there are ways to handle this, but I think that error handling is a legitimate case. Regarding the solutions:
I was wrong (sorry 😬). Solution #2 does not work. As per @TheRawMeatball suspicion, loading assets needs another system to do the work, so blocking the system in a loop will actually not exit, and block the whole game. So, it's even worse 😬
Based on a quick look, I think this uses states, which is my favourite solution, but AFAIK makes it incompatible with FixedTimeStep without trickery. It'd be interesting to see the trickery that the Bevy Cheatbook mentions. |
The idiomatic way would be with states... which may be hard to get to work with a fixed time steps. You could go the old way to do states:
|
@saveriomiroddi there might actually be a good way to do this: impl |
@alice-i-cecile How? You can't load assets synchronously thought the |
Oh wait, no, you're right there. AssetServer will never block. Hmm. |
Phew 😅 I think I won't implement (propose) this in the breakout, though. Unfortunately, the asset loading is part of a setup system (scoreboard), so implementing this strategy (which seems the only one 😱) would require moving it to the main (as in "running game") systems, which I think is an odd design, at least, something I wouldn't put in an official example/tutorial. I think it'd be good to put in the Cheat Book, though. I'll open a PR once my current PR on the same subject is merged. Ultimately, I think the root of the problem is having a FixedTimeStep compatible with states, which is something that @TheRawMeatball at some point will bake 🍰 😄. |
What problem does this solve or what need does it fill?
Assets need to be loaded before they can be delayed. However, with good reason, the asset loader does not block. In order to deal with this, you either need to wait until all of the assets are loaded or handle the possibility that your assets might not exist.
What solution would you like?
Bevy has a first-class built-in solution with minimal boilerplate. The state solution seems like a nice building block, but I'm not sure exactly what the correct API should be.
What alternative(s) have you considered?
State solution from examples.
Resource solution from cheatbook.
Both of these require a great deal of complex boilerplate.
Additional context
Async systems (#1393) might be the tool to solve this. Distill integration (#708) might also solve this.
The text was updated successfully, but these errors were encountered: