Meta-issue for improving reflect-deserialized asset loading
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 171
Description
This is a meta-issue for tracking the progress of improving how reflect-deserialized assets get loaded. This is a cross-cutting concern between both reflection and assets.
The motivating use case comes from [`bevy_animation_graph`](https://github.com/mbrea-c/bevy_animation_graph), where we deserialize `AnimationGraph`s from RON files. This looks like:
```rs
#[derive(Asset, Reflect)]
struct AnimationGraph {
pub nodes: Vec,
pub edges: (...),
}
#[derive(Reflect)]
struct AnimationNode {
pub name: String,
pub inner: Box, // NodeLike: Reflect
}
#[derive(Reflect)]
#[reflect(NodeLike)]
struct AnimationClipNode {
pub clip: Handle,
}
#[derive(Reflect)]
#[reflect(NodeLike)]
struct ChangeSpeedNode;
(
nodes: [
(
name: "Walk clip",
ty: "bevy_animation_graph::node::AnimationClipNode",
inner: (
clip: "animation_clips/walk.animclip.ron",
)
),
(
name: "Change speed",
ty: "bevy_animation_graph::node::ChangeSpeedNode",
inner: (),
),
],
edges: ( ... ),
)
```
The two important things here are:
- `AnimationNode` stores a type-erased `NodeLike`
- `AnimationClipNode`, and other node types, may store asset handles
However, it's currently very annoying to write code to deserialize and load this asset properly. Why?
- `AnimationNode` can't derive `Reflect` because `Box` isn't `Reflect`
- ~~https://github.com/bevyengine/bevy/pull/14776~~
- https://github.com/bevyengine/bevy/pull/15532
- This means you have to write a manual `impl Reflect for AnimationNode`
- We need a way to read the value of that `ty` field, and deserialize `inner` based on the `TypeRegistration` we look up
- This is a perfect candidate for `DeserializeFromRegistry`
- https://github.com/bevyengine/bevy/pull/8611
- When `Handle` is deserialized, it gets set to `Handle::default` which is useless
- In the `AssetLoader::load` call, we get a `LoadContext` - we should use that to actually kick off a load for this asset handle when we encounter it
- https://github.com/bevyengine/bevy/pull/15482
- Note: the difference between what we're doing here and what we're doing in the step above, is that in this step, we have access to a `&mut LoadContext` which we only get inside the asset processor. It doesn't exist in the `TypeRegistry` used by `DeserializeFromRegistry`.
- Using the `LoadContext` / `NestedLoader`, we can't load a dynamic-typed asset (since we're using reflection, we *do* know the `T` in the `Handle`, but only at runtime - i.e. only its `TypeId`)
- https://github.com/bevyengine/bevy/pull/15509
- There should be an inbuilt impl of `ReflectDeserializerProcessor` which accepts a `LoadContext` and does the two above steps for you
- PR to be written
- ~~You have to write boilerplate `DeserializeSeed` code for custom deserialization of an `AnimationNode` because of that `ty`~~
- ~~Honestly not sure what to do for this. The custom `DeserializeSeed` actually isn't too bad compared to the rest of the boilerplate that you have to write, but I'm sure there is a better way to do this.~~
- The better way of doing this is using `DeserializeFromRegistry` from https://github.com/bevyengine/bevy/pull/8611
- Also have to be able to save this kind of asset back to disk, serializing `Handle`s as just string asset paths
- https://github.com/bevyengine/bevy/pull/15548
If these issues are solved, we have a much more powerful way to easily define these kinds of complex assets which depend on other assets, and that also use reflection to deserialize values of unknown types.
Once these are resolved, I would like to write an example which deserializes an asset in this reflective way, showing off the automatic recursive `Handle` loading, and deserializing type-erased `Box`s.
Contributor guide
Research direction
Review the linked reflection and asset-loading PRs, then trace AssetLoader::load, LoadContext/NestedLoader, TypeRegistry, DeserializeFromRegistry, and ReflectDeserializerProcessor. The meta-issue spans dynamic typed handles, recursive loading, reflective deserialization, and serialization; done is an integrated solution plus the proposed reflective asset example.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- game-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100