-
-
Notifications
You must be signed in to change notification settings - Fork 3.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
Asset system usability and load state bugs #7479
Comments
Have you looked at the source for You seem to have a drastically different approach to asset loading as |
Thanks, that's a good suggestion. I had a look at the GLTF loader, and unfortunately I suspect I had already looked at when I wrote my custom asset loader because it's very similar.
The only extra thing I do is call I don't think what I do is drastically different from anything. I only seem to do something a tiny bit less trivial because the database asset doesn't directly produce secondary assets (model) but simply references them, while the model files both reference assets (GLTF) and create secondary ones on the fly (materials). So there's one extra "depth" to the dependency tree compared to GLTF loading I think. Having a system which polls until an asset is loaded sounds pretty standard; I don't know how you can manage that otherwise since The only thing I see that possibly makes things more complex in my case as opposed to the GLTF loader, if I understand how that code works, is that the GLTF loader calls There's also some obscure use of If anyone can shed some light on part or all of this, so I can document those, that'd be very helpful. |
I should mention, currently I reference the GLTF from my custom asset via a string, and during // Load GLTF
let gltf_path = AssetPath::new_ref(...);
let gltf: Handle<Gltf> = load_context.get_handle(gltf_path.clone());
// Inline-create secondary asset for material
let material = StandardMaterial { ... };
let material: Handle<StandardMaterial> = load_context.set_labeled_asset("material", LoadedAsset::new(material));
// Primary asset (model)
let model = Model { gltf, material, ... };
let primary_asset = LoadedAsset::new(model).with_dependency(gltf_path);
load_context.set_default_asset(primary_asset); which is how I ensure the GLTF is loaded via the existing GLTF loader. |
You're right on your interpretation of the gltf loader. It's loading its subassets files and waiting for the files to be loaded, to be able to work on their bytes. It's allowed to do that as it's in an async context. The From a system, you don't have a choice and must load asset asynchronously, getting back only an handle. From a loader, you are already in an async context, so you have the choice when loading subassets of either getting an handle, or getting their bytes. All this is very underdocumented... I'm one of the person who edited the most the gLTF loader, but I would still have to re-read the code to explain what is happening. It has never been documented because bevy_asset has been under a looming rewrite for a long time... |
But getting a handle does not guarantee completion of loading so you end up with an asset "loaded" but with unloaded dependencies. And getting the bytes doesn't allow you to reuse an existing asset loader. Did I get that right? |
Yup
That will depend on how the asset loader is implemented. The asset loader itself can't be called if I remember correctly, but nothing stops the asset loader implementor to also expose a function taking bytes. The gltf loader almost does that, there is a reusable function... but it's not public. |
It seems unsatisfactory and error prone that we have to rely on how the loader is implemented. We should try to encode that use case in the trait itself. |
Totally! |
So in summary:
Are there plans already or on-going work to tackle this, since 0.10 was supposed to focus on the asset pipeline? @cart anything you're working on maybe? Ideally I'd rather have a proper asset dependency graph where I can specify strong/weak refs, and where the |
I'm implementing a more exotic asset loader now. And I'm hitting the exact same limitations as @djeedai. I did some research specifically on |
It used to be used by the gltf loader, but that was removed to ensure the same result from external files or from included files in a glb. Not sure what you mean by |
Closing this out now following Assets v2. |
Bevy version
0.9.1
What you did
Attempt to write a non-trivial yet reasonably simple asset loading system with dependencies.
What went wrong
Panics. Panics everywhere.
The loading state (
LoadState
) of assets and their dependent assets makes little sense to the non-expert eye. And writing a system that loads an asset and its dependencies requires heavy work and deep understanding from the user.Additional information
I'm very frustrated by the asset system.
I've been trying to do some level loading for my game for the past week. This involves loading a core file (database; text) referencing other files (models; text), each referencing a gltf which contains a scene to spawn. A non-trivial but still relatively simple dependency tree situation. All files are small, the tree is balanced, no loops, really nothing crazy. Yet I've been having hundreds of panics on assets not loaded (which are non-deterministic due to threading).
First, after
AssetServer::load()
is called the main asset might still be loading. This is kind of well known, but already annoying to handle; you have to write a system that needs to poll each frame, and does different things depending on the loading state, which makes reasoning more complex. Or you have to write two systems and use stages. Either way, this is quite the overhead. And this traps every single new Bevy user. On top of that, that asset may even sometimes be in weird states likeNotLoaded
, even though I have a strong reference to it! 🤨Next is GLTF. I just had an assert where the
Handle<Gltf>
asset is loaded, butGltf::default_scene
points to aScene
that is not in theAssets<Scene>
(yet?). And this is not a malformed asset; those same assets loaded ten times without issue in previous runs, and fine in subsequent runs too, and I didn't touch them in-between. Just randomly the scene is not ready (race condition).Dependent assets set with
LoadedAsset::with_dependency()
also seem to count only for triggering the loading, but again are completely uncoupled from the loading state of their parent, so the parent can be inLoadState::Loaded
but a dependent asset not loaded yet. Does that make sense? If the parent is loaded, shouldn't that imply that any dependencies are loaded too? Otherwise can we really call those dependencies? In any case, this means your system that was polling assets load state now needs to poll multiple assets AND know about the graph of dependencies so it can poll all of them and report when a subtree is ready (parent and all children loaded). And again, I even saw those dependent assets still inNotLoaded
state like if they had never been referenced; I would have expected their parent to keep a strong dependency to them no? Otherwise, what'sLoadedAsset::with_dependency()
doing then?I had already written a simple
Loader
component to manage a flat list of assets, because it was a pain to write again and again the same polling code (loading queue, sync primitives, atomic counter, etc. to have a single polling call for a "transaction" of assets) for 2-3 assets that need to be loaded for some system to work. But it seems that now I also need to write an entire asset dependency graph polling system? Is that by design really?I've read #3972 and its thread on asset dependencies but I can't see any plan or discussion that would address the above issues. I may have missed something, or I'm doing something completely wrong (which is entirely possible). But for now the way I've experienced it so far is that any non-trivial asset loading requires a mountain of work from users. Documentation on
LoadState
and the asset system in general would also benefit from some extra attention; I'm available to make PRs, provided I can understand how things actually work and are designed to be used.The text was updated successfully, but these errors were encountered: