Skip to content

Comments

Implement minimal asset saving#22622

Merged
alice-i-cecile merged 11 commits intobevyengine:mainfrom
andriyDev:asset-saving-v0
Feb 4, 2026
Merged

Implement minimal asset saving#22622
alice-i-cecile merged 11 commits intobevyengine:mainfrom
andriyDev:asset-saving-v0

Conversation

@andriyDev
Copy link
Contributor

Objective

Solution

  • Allow users to build SavedAsset instances by building up their labeled assets. This can either take an existing handle + asset ref, or will create a handle for your asset ref.
  • Provide a function to correctly call the AssetSaver, and write out the correct meta file.

Some things I am leaving to future PRs

  • Making it easier to access subassets in an AssetSaver.
  • Making a nicer API for asset saving (e.g., registering asset savers and looking up the appropriate saver for type ID + extension).

Weird implementation details

  • I needed to create my own Cow-like type (which I named Moo), since I needed to store just a ref or owned hashmap. I wrote a lengthy comment explaining why it's needed (TL;DR variance is complicated, Cow doesn't work). Note: it's possible we could just use CowArc instead, but we never need to clone the map so this seems like overkill. Also having a nice place to explain the variance problems is useful here.
  • For the builder, I needed to add an extra lifetime to add_labeled_asset_with_*<'b: 'a>, since otherwise, the CowArc gets coerced to 'static which means the lifetime of the subasset needs to be 'static, which practically makes this unusable. By adding a second lifetime dedicated to the CowArc,

Testing

  • Created a new example: this allows you to make a small "scene" of boxes, and save and load it to your heart's content!
  • Added a couple simple tests.

@andriyDev andriyDev added C-Feature A new feature, making something new possible A-Assets Load files from disk to use for things like images, models, and sounds D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jan 21, 2026
@andriyDev andriyDev force-pushed the asset-saving-v0 branch 3 times, most recently from 1d215e4 to 77a14a9 Compare January 21, 2026 09:30
@andriyDev andriyDev added this to the 0.19 milestone Jan 21, 2026
@andriyDev andriyDev requested a review from kristoff3r January 21, 2026 10:10
@alice-i-cecile alice-i-cecile added the M-Release-Note Work that should be called out in the blog due to impact label Jan 21, 2026
@andriyDev andriyDev requested a review from greeble-dev January 22, 2026 16:54
Copy link
Contributor

@greeble-dev greeble-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm clicking approve as the PR does what it says and is backed up by tests and an example. I left a few minor suggestions.

I do have some concerns with the overall direction:

  1. The code is significantly complicated by having to support handles and avoid copies. I'm not sure this complexity is worth it right now.
  2. We're not dogfooding since the engine doesn't have a complicated saver built-in - the example code is fairly contrived. This makes it hard to predict if the API will work well.
  3. My guess is that the subtleties around sub-assets and when to use add_labeled_asset_with_new_handle versus add_labeled_asset_with_existing_handle will be challenging for users.

I don't think these concerns are blocking, but maybe they deserve more discussion. And as ever, there's the possibility that assets-as-entities ends up remaking the whole thing.

CowArc<'static, str>: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
pub fn get_labeled<B: Asset>(&self, label: &str) -> Option<SavedAsset<'a, '_, B>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change means get_labeled can no longer be called directly with a CowArc<str>. Only a minor regression, but is it necessary? I guess the lifetimes might be tricky.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now accept impl AsRef<str>, so this should work now I think.

@@ -0,0 +1,372 @@
//! This example demonstrates how to save assets.
Copy link
Contributor

@greeble-dev greeble-dev Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth calling the example asset_saving_subassets.rs? Or subasset_saving.rs? It's a fairly complicated example, so this would leave space for a simpler example that doesn't involve sub-assets.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay with this for now, and then we can rename once a simpler example is prepared.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can make a followup PR to split this into a simple saving example and a complex saving example.

I primarily focuses on the complex case since it's the one that this PR actually enables (the simple case you could previously do anyway).


## 1. Building the `SavedAsset`

To build the `SavedAsset`, either use `SavedAsset::from_asset`, or `SavedAssetBuilder`. For example:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could do with a brief example of SavedAsset::from_asset that's separate from the SavedAssetBuilder example? Would make clear that SavedAssetBuilder is only needed for more complicated cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a more simple example just for SavedAsset::from_asset!

Comment on lines 522 to 525
// NOTE: We can't handle embedded dependencies in any way, since we need to write to
// another file to do so.
embedded_dependencies: vec![],
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// NOTE: We can't handle embedded dependencies in any way, since we need to write to
// another file to do so.
embedded_dependencies: vec![],
};
embedded_dependencies: vec![],
};
// NOTE: We can't handle embedded dependencies in any way, since we need to write to
// another file to do so.
assert!(asset.embedded_dependencies.is_empty());

Assert seems safer if this is only used in tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

.web-asset-cache
examples/large_scenes/bistro/assets/*
examples/large_scenes/caldera_hotel/assets/*
examples/asset/saved_assets
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what this is for? The example asset+meta is already tracked by git, and the example doesn't add new ones.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woops yup my jumping around my branch made me add these files accidentally.

I've gone and deleted them from the original commit so they remain untracked.

@andriyDev andriyDev added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Feb 2, 2026
Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to see progress here. I think this is a sensible design, and the code is clear enough to be maintainable. I would also prefer a simpler example (keeping this one for a more advanced one), but I'm not going to block on that.

@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Feb 2, 2026
Note that since these assets are borrowed, building the `SavedAsset` should happen in the same async
task as the next step.

## 2. Calling `save_using_saver`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simply rename this to save? Are there plans for a simpler API that don't require an explicit saver?

If not, we should definitely use the simpler name for the standard path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, I think we will likely have a separate save function that looks up the appropriate saver for you (the same way load looks up the loader). This minimal version still seems reasonable to support in that world anyway, so it's not clear to me we will delete this function.

@alice-i-cecile alice-i-cecile added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged and removed S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it labels Feb 2, 2026
@andriyDev andriyDev added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Feb 3, 2026
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Feb 4, 2026
Merged via the queue into bevyengine:main with commit 5d9a10c Feb 4, 2026
44 checks passed
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-Feature A new feature, making something new possible D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes M-Release-Note Work that should be called out in the blog due to impact S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants