-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Fix #11111 by making asset settings part of AssetPath
#19187
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
Draft
greeble-dev
wants to merge
32
commits into
bevyengine:main
Choose a base branch
from
greeble-dev:asset-identity-includes-settings
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Fix #11111 by making asset settings part of AssetPath
#19187
greeble-dev
wants to merge
32
commits into
bevyengine:main
from
greeble-dev:asset-identity-includes-settings
+410
−343
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
…lly override the settings in `AssetMeta`.
…Path::with_settings`.
… them when loading materials. This fixes materials using the wrong handles - it was recalculating the handles without the settings. But it might break some dependency handling - see the comment where `texture_handles` is defined.
This reverts commit feb7aba.
…y `AssetPath::with_settings`." This reverts commit f0f645c.
This reverts commit 5779ba2.
…f a setting value. This makes the interface backwards compatible.
…nce clients can provide the settings in the `AssetPath` parameter of `load`.
…, and added `ErasedSettings::value()` that correctly derefs the box. Also updated various comments.
…n::ser::to_write` + hash writer.
…nsistently use `load_with_settings` rather than mixing them with `load`.
…s dependent on how the writes are chunked.
…e `HashWriter` is flawed because separate calls to FixedHasher's write will give a different result from accumulating all the bytes and writing once. Maybe that's fine since the hash is not supposed to be persistent, but I don't want to take a chance. Could also consider a HashWriter that consistently writes the same size slices via buffering.
… the material code can reload the path to register the dependency, and also verify that the handle matches.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
A-Assets
Load files from disk to use for things like images, models, and sounds
C-Bug
An unexpected or incorrect behavior
M-Needs-Migration-Guide
A breaking change to Bevy's public API that needs to be noted in a migration guide
S-Needs-Design
This issue requires design work to think about how it would best be accomplished
X-Controversial
There is active debate or serious implications around merging this PR
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Objective
Fix #11111.
Why A Draft?
The PR is basically working and tested, but parts are unfinished and the design is likely to be controversial. I'm hoping to get feedback before I finish it up. The remaining work is mostly error handling and documentation.
The main issues will be that custom settings are serialized for hashing/equality, and asset paths with settings can't be round-tripped via text.
Background
Bevy identifies assets at rest with
AssetPath
, and assets in memory withHandle
. Assets are loaded by callingAssetServer::load(AssetPath) -> Handle
. The (unstated?) contract is that multiple calls toload
with the sameAssetPath
will return the sameHandle
.This causes surprising behaviour for users who call
AssetServer::load_with_settings
- a load that takes anAssetPath
and a closure that customises the asset loader's settings. If an asset with the sameAssetPath
is already in memory thenload_with_settings
returns that asset'sHandle
and doesn't run the loader - the settings are effectively ignored. Or the reverse can happen - a plainload
can return an asset that was previously customised byload_with_settings
.This behaviour can occur even if the user doesn't directly call
load_with_settings
. One example is the glTF importer, which callsload_with_settings
on external texture files to apply sampler settings. If the user loads two different glTFs that share the same texture file but with different settings, then one of the glTFs wins and the other is broken.I've previously considered a few simpler solutions, but decided they weren't viable.
Solution
This PR moves the loader settings into
AssetPath
. Now they're part of the asset's identity, so different settings will result in a differentHandle
.pub struct AssetPath<'a> { source: AssetSourceId<'a>, path: CowArc<'a, Path>, label: Option<CowArc<'a, str>>, + settings: Option<Arc<ErasedSettings>>, }
AssetServer::load_with_settings
is preserved for backwards compatibility, but can be replaced byAssetPath::with_settings
.Problems
💀💀💀 Equality and hashing of settings is done by serializing to RON 💀💀💀.
AssetPath
to contain an erased settings value and implementHash
andEq
.Serialize
in practice (for meta file support), so this is backwards compatible.Hash
andEq
themselves.Asset paths are no longer guaranteed to be round-trippable via text.
AssetPath
formatted byDisplay
could be parsed byAssetPath::parse
.Asset identity has some asterisks.
path
versuspath.with_settings(SomeSettings::default())
load_with_settings
is not fully backwards compatible.AssetServer::load
can't immediately return aHandle
- it has to wait until the meta file is loaded (a laload_untyped
).load_with_settings
is fully backwards compatible if the asset doesn't have a meta file.Fixing asset identity can reveal bugs.
load_with_settings
andload
on the same asset path would return the sameHandle
.alter_sprites
andalter_mesh
examples.Performance.
AssetPath
equality/hashing just needs to do a bit more to check theNone
settings.Handle
rather than a function pointer.AssetServer::load
- it must be done immediately to return the rightHandle
.Hash
+Eq
.Arc
, while otherAssetPath
members are aCowArc
.'static
or borrowed.CowArc
but ran into mysterious errors about traits not being satisfied. Maybe this can be fixed.Arc
becameCowArc
then it would be +24 bytes.Option<CowArc>
being 24 bytes is unfortunate? 15 of those bytes are unused)Lack of test coverage.
load_with_settings
, and various examples that load glTFs or do asset work.alter_mesh
due to Mesh not showing(or updating) in alter_mesh example #18967.glTF importer issues
load_with_settings
and a laterload
returning the same handle.AssetPath
once with the right settings, which adds some annoying plumbing.Removing
MetaTransform
is debatable.MetaTransform
because it seems incompatible with asset identity.Maybe this PR is too big.
AssetPath
.load
andload_with_settings
equivalence.AssetPath
.MetaTransform
.AssetPath::with_settings
overAssetServer::load_with_settings
.Alternatives
So this PR has a bunch of problems. What are the alternatives?
First, lets define the asset system by three constraints:
AssetPath
andHandle
are both ways of identifying an asset value at rest or in memory. AHandle
can be mapped to exactly oneAssetPath
at runtime, and vice-versa.The current asset system sacrifices constraint 1, so paths and handles don't properly identify an asset value. This is untenable - arguably constraint 1 is the founding principle of any asset system.
This PR sacrifices constraint 2 - asset paths now awkwardly include the settings (either as a string or hash).
I don't think there's a way for the engine to satisfy constraints 2 and 3 and also support non-trivial asset processing. Maybe that's a valid strategy - there is precedent for engines that push all complex asset processing onto the user - but I'm assuming it's not the strategy Bevy has chosen.
That leaves one alternative - what happens if we sacrifice constraint 3?
Meta All The Things
What if apps can load meta files directly, and meta files are more flexible in what they represent?
AssetPath
stays the same and doesn't need to contain settings.foo.gltf
then I always get aGltf
with the default loader settings.AssetPath::from("foo.gltf.meta")
then I get an asset with that meta's settings.bar.meta
that points tofoo.gltf
.alternative_bar.meta
that also points tofoo.gltf
but with different settings.bar_mesh_1.meta
that points tofoo.gltf#Mesh1
.bar_meshlet_1.meta
that points tobar_mesh_1.meta
but applies meshlet processing.AssetMeta
in memory and loading it.AssetServer::load_with_meta(AssetMeta) -> Handle
.AssetSource
, so the interface isAssetServer::register_meta(AssetMeta) -> AssetPath
.In terms of user experience, I'd expect the Bevy Editor to include tools for generating and managing meta files. Most users would not edit meta files as text.
Pros:
Cons:
There's some precedent for this approach in Unreal's asset system - Unreal games always reference intermediate
.uasset
files instead of source files. Although there's also plenty of differences, and Unreal's asset system is not all gravy.Summary
Overall, I think leaning into meta files is better than this PR, but also a bigger and more controversial change. Maybe this PR could be a short-term solution to #11111, then Meta All The Things is left open as a longer-term solution.