Skip to content

Asset system usability and load state bugs #7479

@djeedai

Description

@djeedai

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 like NotLoaded, even though I have a strong reference to it! 🤨

Next is GLTF. I just had an assert where the Handle<Gltf> asset is loaded, but Gltf::default_scene points to a Scene that is not in the Assets<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 in LoadState::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 in NotLoaded state like if they had never been referenced; I would have expected their parent to keep a strong dependency to them no? Otherwise, what's LoadedAsset::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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-AssetsLoad files from disk to use for things like images, models, and soundsC-BugAn unexpected or incorrect behaviorC-DocsAn addition or correction to our documentationC-FeatureA new feature, making something new possibleC-UsabilityA targeted quality-of-life change that makes Bevy easier to use

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions