Fix AssetServer::load_with_settings by making settings part of AssetPath#19187
Draft
greeble-dev wants to merge 32 commits intobevyengine:mainfrom
Draft
Fix AssetServer::load_with_settings by making settings part of AssetPath#19187greeble-dev wants to merge 32 commits intobevyengine:mainfrom
AssetServer::load_with_settings by making settings part of AssetPath#19187greeble-dev wants to merge 32 commits intobevyengine:mainfrom
Conversation
…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.
AssetPathAssetServer::load_with_settings by making settings part of AssetPath
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 (
AssetServer::load_with_settingscan return the asset without the custom settings, along with other surprises).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.
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 toloadwith the sameAssetPathwill only create one copy of the asset, and return the sameHandle.This causes surprising behaviour for users who call
AssetServer::load_with_settings- a load that takes anAssetPathand a closure that customises the asset loader's settings. If an asset with the sameAssetPathis already loaded thenload_with_settingsreturns that asset'sHandleand doesn't run the loader - the settings are effectively ignored. Or the reverse can happen - aloadcan 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_settingson 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_settingsis preserved for backwards compatibility, but can be replaced byAssetPath::with_settings.let path = AssetPath::from("path/to/image.ktx2"); -let settings = |s: &mut ImageLoaderSettings| s.is_srgb = true; -let handle = asset_server.load_with_settings(path, settings); +let settings = ImageLoaderSettings { is_srgb: true, ..default() }; +let handle = asset_server.load(path.with_settings(settings));This sounds straightforward, but there's a catch...
Why Is This Controversial?
I'm expecting this PR to be controversial because of some surprises hiding in
ErasedSettings. Let's first explain how I got there.load_with_settingsrequires identifying settings so they can be compared. There's no getting around this, and all the possible solutions have issues.PartialEqorHashis an obvious solution, but this would be a new requirement and a pain for users to implement.Serialize, and we need a RON serializer for meta files.That's got some major ick factor, but maybe it's the least worst solution? The alternatives I could think of are:
PartialEq/Hash.MetaTransformand find some way to automatically use the boxed closure as an identity.load_with_settingswith some GUID.Other Problems
Asset paths are no longer guaranteed to be round-trippable via text.
AssetPathformatted byDisplaycould be parsed byAssetPath::parse.Asset identity has some asterisks.
pathversuspath.with_settings(SomeSettings::default())load_with_settingsis not fully backwards compatible.AssetServer::loadcan't immediately return aHandle- it has to wait until the meta file is loaded (a laload_untyped).load_with_settingsis fully backwards compatible if the asset doesn't have a meta file.MetaTransformhas been removed.MetaTransformbecause it seems incompatible with asset identity.Fixing asset identity can reveal bugs.
load_with_settingsandloadon the same asset path would return the sameHandle.alter_spritesandalter_meshexamples needed fixes.glTF importer issues
load_with_settingsand a laterloadreturning the same handle.AssetPathonce with the right settings, which adds some annoying plumbing.Performance.
AssetPathequality/hashing just needs to do a bit more to check theNonesettings.Handlerather than a function pointer.AssetServer::load- it must be done immediately to return the rightHandle.Hash+Eq.Arc, while otherAssetPathmembers are aCowArc.'staticor borrowed.CowArcbut ran into mysterious errors about traits not being satisfied. Maybe this can be fixed.ArcbecameCowArcthen 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_meshdue to Mesh not showing(or updating) in alter_mesh example #18967.Maybe this PR is too big.
AssetPath.loadandload_with_settingsequivalence.AssetPath.MetaTransform.AssetPath::with_settingsoverAssetServer::load_with_settings.