Skip to content

Commit 409857f

Browse files
authored
Stop using AssetId in the AssetServer and use AssetIndex instead. (#19927)
# Objective - This is sort of a step towards #19024. - This is a step towards #11266. Context: The AssetServer **never** deals with Uuid assets anyway. We always allocate asset indices, and asset UUIDs are not handled properly (dependencies on UUID assets are never loaded, so their dependents won't ever be notified). ## Solution - Create `ErasedAssetIndex` as a replacement for `UntypedAssetId`. - Use `ErasedAssetIndex` or `AssetIndex` where relevant within the `AssetServer`.
1 parent af5f307 commit 409857f

File tree

7 files changed

+366
-231
lines changed

7 files changed

+366
-231
lines changed

crates/bevy_asset/src/assets.rs

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ pub struct Assets<A: Asset> {
291291
queued_events: Vec<AssetEvent<A>>,
292292
/// Assets managed by the `Assets` struct with live strong `Handle`s
293293
/// originating from `get_strong_handle`.
294-
duplicate_handles: HashMap<AssetId<A>, u16>,
294+
duplicate_handles: HashMap<AssetIndex, u16>,
295295
}
296296

297297
impl<A: Asset> Default for Assets<A> {
@@ -400,10 +400,7 @@ impl<A: Asset> Assets<A> {
400400
pub fn add(&mut self, asset: impl Into<A>) -> Handle<A> {
401401
let index = self.dense_storage.allocator.reserve();
402402
self.insert_with_index(index, asset.into()).unwrap();
403-
Handle::Strong(
404-
self.handle_provider
405-
.get_handle(index.into(), false, None, None),
406-
)
403+
Handle::Strong(self.handle_provider.get_handle(index, false, None, None))
407404
}
408405

409406
/// Upgrade an `AssetId` into a strong `Handle` that will prevent asset drop.
@@ -415,11 +412,12 @@ impl<A: Asset> Assets<A> {
415412
if !self.contains(id) {
416413
return None;
417414
}
418-
*self.duplicate_handles.entry(id).or_insert(0) += 1;
419415
let index = match id {
420-
AssetId::Index { index, .. } => index.into(),
421-
AssetId::Uuid { uuid } => uuid.into(),
416+
AssetId::Index { index, .. } => index,
417+
// We don't support strong handles for Uuid assets.
418+
AssetId::Uuid { .. } => return None,
422419
};
420+
*self.duplicate_handles.entry(index).or_insert(0) += 1;
423421
Some(Handle::Strong(
424422
self.handle_provider.get_handle(index, false, None, None),
425423
))
@@ -479,34 +477,35 @@ impl<A: Asset> Assets<A> {
479477
/// This is the same as [`Assets::remove`] except it doesn't emit [`AssetEvent::Removed`].
480478
pub fn remove_untracked(&mut self, id: impl Into<AssetId<A>>) -> Option<A> {
481479
let id: AssetId<A> = id.into();
482-
self.duplicate_handles.remove(&id);
483480
match id {
484-
AssetId::Index { index, .. } => self.dense_storage.remove_still_alive(index),
481+
AssetId::Index { index, .. } => {
482+
self.duplicate_handles.remove(&index);
483+
self.dense_storage.remove_still_alive(index)
484+
}
485485
AssetId::Uuid { uuid } => self.hash_map.remove(&uuid),
486486
}
487487
}
488488

489489
/// Removes the [`Asset`] with the given `id`.
490-
pub(crate) fn remove_dropped(&mut self, id: AssetId<A>) {
491-
match self.duplicate_handles.get_mut(&id) {
490+
pub(crate) fn remove_dropped(&mut self, index: AssetIndex) {
491+
match self.duplicate_handles.get_mut(&index) {
492492
None => {}
493493
Some(0) => {
494-
self.duplicate_handles.remove(&id);
494+
self.duplicate_handles.remove(&index);
495495
}
496496
Some(value) => {
497497
*value -= 1;
498498
return;
499499
}
500500
}
501501

502-
let existed = match id {
503-
AssetId::Index { index, .. } => self.dense_storage.remove_dropped(index).is_some(),
504-
AssetId::Uuid { uuid } => self.hash_map.remove(&uuid).is_some(),
505-
};
502+
let existed = self.dense_storage.remove_dropped(index).is_some();
506503

507-
self.queued_events.push(AssetEvent::Unused { id });
504+
self.queued_events
505+
.push(AssetEvent::Unused { id: index.into() });
508506
if existed {
509-
self.queued_events.push(AssetEvent::Removed { id });
507+
self.queued_events
508+
.push(AssetEvent::Removed { id: index.into() });
510509
}
511510
}
512511

@@ -574,19 +573,15 @@ impl<A: Asset> Assets<A> {
574573
// to other asset info operations
575574
let mut infos = asset_server.write_infos();
576575
while let Ok(drop_event) = assets.handle_provider.drop_receiver.try_recv() {
577-
let id = drop_event.id.typed();
578-
579576
if drop_event.asset_server_managed {
580-
let untyped_id = id.untyped();
581-
582577
// the process_handle_drop call checks whether new handles have been created since the drop event was fired, before removing the asset
583-
if !infos.process_handle_drop(untyped_id) {
578+
if !infos.process_handle_drop(drop_event.index) {
584579
// a new handle has been created, or the asset doesn't exist
585580
continue;
586581
}
587582
}
588583

589-
assets.remove_dropped(id);
584+
assets.remove_dropped(drop_event.index.index);
590585
}
591586
}
592587

crates/bevy_asset/src/handle.rs

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
2-
meta::MetaTransform, Asset, AssetId, AssetIndexAllocator, AssetPath, InternalAssetId,
3-
UntypedAssetId,
2+
meta::MetaTransform, Asset, AssetId, AssetIndex, AssetIndexAllocator, AssetPath,
3+
ErasedAssetIndex, UntypedAssetId,
44
};
55
use alloc::sync::Arc;
66
use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
@@ -26,7 +26,7 @@ pub struct AssetHandleProvider {
2626

2727
#[derive(Debug)]
2828
pub(crate) struct DropEvent {
29-
pub(crate) id: InternalAssetId,
29+
pub(crate) index: ErasedAssetIndex,
3030
pub(crate) asset_server_managed: bool,
3131
}
3232

@@ -45,18 +45,19 @@ impl AssetHandleProvider {
4545
/// [`UntypedHandle`] will match the [`Asset`] [`TypeId`] assigned to this [`AssetHandleProvider`].
4646
pub fn reserve_handle(&self) -> UntypedHandle {
4747
let index = self.allocator.reserve();
48-
UntypedHandle::Strong(self.get_handle(InternalAssetId::Index(index), false, None, None))
48+
UntypedHandle::Strong(self.get_handle(index, false, None, None))
4949
}
5050

5151
pub(crate) fn get_handle(
5252
&self,
53-
id: InternalAssetId,
53+
index: AssetIndex,
5454
asset_server_managed: bool,
5555
path: Option<AssetPath<'static>>,
5656
meta_transform: Option<MetaTransform>,
5757
) -> Arc<StrongHandle> {
5858
Arc::new(StrongHandle {
59-
id: id.untyped(self.type_id),
59+
index,
60+
type_id: self.type_id,
6061
drop_sender: self.drop_sender.clone(),
6162
meta_transform,
6263
path,
@@ -71,20 +72,16 @@ impl AssetHandleProvider {
7172
meta_transform: Option<MetaTransform>,
7273
) -> Arc<StrongHandle> {
7374
let index = self.allocator.reserve();
74-
self.get_handle(
75-
InternalAssetId::Index(index),
76-
asset_server_managed,
77-
path,
78-
meta_transform,
79-
)
75+
self.get_handle(index, asset_server_managed, path, meta_transform)
8076
}
8177
}
8278

8379
/// The internal "strong" [`Asset`] handle storage for [`Handle::Strong`] and [`UntypedHandle::Strong`]. When this is dropped,
8480
/// the [`Asset`] will be freed. It also stores some asset metadata for easy access from handles.
8581
#[derive(TypePath)]
8682
pub struct StrongHandle {
87-
pub(crate) id: UntypedAssetId,
83+
pub(crate) index: AssetIndex,
84+
pub(crate) type_id: TypeId,
8885
pub(crate) asset_server_managed: bool,
8986
pub(crate) path: Option<AssetPath<'static>>,
9087
/// Modifies asset meta. This is stored on the handle because it is:
@@ -97,7 +94,7 @@ pub struct StrongHandle {
9794
impl Drop for StrongHandle {
9895
fn drop(&mut self) {
9996
let _ = self.drop_sender.send(DropEvent {
100-
id: self.id.internal(),
97+
index: ErasedAssetIndex::new(self.index, self.type_id),
10198
asset_server_managed: self.asset_server_managed,
10299
});
103100
}
@@ -106,7 +103,8 @@ impl Drop for StrongHandle {
106103
impl core::fmt::Debug for StrongHandle {
107104
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
108105
f.debug_struct("StrongHandle")
109-
.field("id", &self.id)
106+
.field("index", &self.index)
107+
.field("type_id", &self.type_id)
110108
.field("asset_server_managed", &self.asset_server_managed)
111109
.field("path", &self.path)
112110
.field("drop_sender", &self.drop_sender)
@@ -154,7 +152,10 @@ impl<A: Asset> Handle<A> {
154152
#[inline]
155153
pub fn id(&self) -> AssetId<A> {
156154
match self {
157-
Handle::Strong(handle) => handle.id.typed_unchecked(),
155+
Handle::Strong(handle) => AssetId::Index {
156+
index: handle.index,
157+
marker: PhantomData,
158+
},
158159
Handle::Uuid(uuid, ..) => AssetId::Uuid { uuid: *uuid },
159160
}
160161
}
@@ -202,9 +203,8 @@ impl<A: Asset> core::fmt::Debug for Handle<A> {
202203
Handle::Strong(handle) => {
203204
write!(
204205
f,
205-
"StrongHandle<{name}>{{ id: {:?}, path: {:?} }}",
206-
handle.id.internal(),
207-
handle.path
206+
"StrongHandle<{name}>{{ index: {:?}, type_id: {:?}, path: {:?} }}",
207+
handle.index, handle.type_id, handle.path
208208
)
209209
}
210210
Handle::Uuid(uuid, ..) => write!(f, "UuidHandle<{name}>({uuid:?})"),
@@ -298,7 +298,10 @@ impl UntypedHandle {
298298
#[inline]
299299
pub fn id(&self) -> UntypedAssetId {
300300
match self {
301-
UntypedHandle::Strong(handle) => handle.id,
301+
UntypedHandle::Strong(handle) => UntypedAssetId::Index {
302+
type_id: handle.type_id,
303+
index: handle.index,
304+
},
302305
UntypedHandle::Uuid { type_id, uuid } => UntypedAssetId::Uuid {
303306
uuid: *uuid,
304307
type_id: *type_id,
@@ -319,7 +322,7 @@ impl UntypedHandle {
319322
#[inline]
320323
pub fn type_id(&self) -> TypeId {
321324
match self {
322-
UntypedHandle::Strong(handle) => handle.id.type_id(),
325+
UntypedHandle::Strong(handle) => handle.type_id,
323326
UntypedHandle::Uuid { type_id, .. } => *type_id,
324327
}
325328
}
@@ -400,9 +403,7 @@ impl core::fmt::Debug for UntypedHandle {
400403
write!(
401404
f,
402405
"StrongHandle{{ type_id: {:?}, id: {:?}, path: {:?} }}",
403-
handle.id.type_id(),
404-
handle.id.internal(),
405-
handle.path
406+
handle.type_id, handle.index, handle.path
406407
)
407408
}
408409
UntypedHandle::Uuid { type_id, uuid } => {

crates/bevy_asset/src/id.rs

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{Asset, AssetIndex};
1+
use crate::{Asset, AssetIndex, Handle, UntypedHandle};
22
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
33
use serde::{Deserialize, Serialize};
44
use uuid::Uuid;
@@ -68,7 +68,7 @@ impl<A: Asset> AssetId<A> {
6868
}
6969

7070
#[inline]
71-
pub(crate) fn internal(self) -> InternalAssetId {
71+
fn internal(self) -> InternalAssetId {
7272
match self {
7373
AssetId::Index { index, .. } => InternalAssetId::Index(index),
7474
AssetId::Uuid { uuid } => InternalAssetId::Uuid(uuid),
@@ -255,7 +255,7 @@ impl UntypedAssetId {
255255
}
256256

257257
#[inline]
258-
pub(crate) fn internal(self) -> InternalAssetId {
258+
fn internal(self) -> InternalAssetId {
259259
match self {
260260
UntypedAssetId::Index { index, .. } => InternalAssetId::Index(index),
261261
UntypedAssetId::Uuid { uuid, .. } => InternalAssetId::Uuid(uuid),
@@ -314,36 +314,33 @@ impl PartialOrd for UntypedAssetId {
314314

315315
/// An asset id without static or dynamic types associated with it.
316316
///
317-
/// This exist to support efficient type erased id drop tracking. We
318-
/// could use [`UntypedAssetId`] for this, but the [`TypeId`] is unnecessary.
319-
///
320-
/// Do not _ever_ use this across asset types for comparison.
321-
/// [`InternalAssetId`] contains no type information and will happily collide
322-
/// with indices across types.
317+
/// This is provided to make implementing traits easier for the many different asset ID types.
323318
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, From)]
324-
pub(crate) enum InternalAssetId {
319+
enum InternalAssetId {
325320
Index(AssetIndex),
326321
Uuid(Uuid),
327322
}
328323

329-
impl InternalAssetId {
330-
#[inline]
331-
pub(crate) fn typed<A: Asset>(self) -> AssetId<A> {
332-
match self {
333-
InternalAssetId::Index(index) => AssetId::Index {
334-
index,
335-
marker: PhantomData,
336-
},
337-
InternalAssetId::Uuid(uuid) => AssetId::Uuid { uuid },
338-
}
324+
/// An asset index bundled with its (dynamic) type.
325+
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
326+
pub(crate) struct ErasedAssetIndex {
327+
pub(crate) index: AssetIndex,
328+
pub(crate) type_id: TypeId,
329+
}
330+
331+
impl ErasedAssetIndex {
332+
pub(crate) fn new(index: AssetIndex, type_id: TypeId) -> Self {
333+
Self { index, type_id }
339334
}
335+
}
340336

341-
#[inline]
342-
pub(crate) fn untyped(self, type_id: TypeId) -> UntypedAssetId {
343-
match self {
344-
InternalAssetId::Index(index) => UntypedAssetId::Index { index, type_id },
345-
InternalAssetId::Uuid(uuid) => UntypedAssetId::Uuid { uuid, type_id },
346-
}
337+
impl Display for ErasedAssetIndex {
338+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
339+
f.debug_struct("ErasedAssetIndex")
340+
.field("type_id", &self.type_id)
341+
.field("index", &self.index.index)
342+
.field("generation", &self.index.generation)
343+
.finish()
347344
}
348345
}
349346

@@ -414,6 +411,52 @@ impl<A: Asset> TryFrom<UntypedAssetId> for AssetId<A> {
414411
}
415412
}
416413

414+
impl TryFrom<UntypedAssetId> for ErasedAssetIndex {
415+
type Error = UuidNotSupportedError;
416+
417+
fn try_from(asset_id: UntypedAssetId) -> Result<Self, Self::Error> {
418+
match asset_id {
419+
UntypedAssetId::Index { type_id, index } => Ok(ErasedAssetIndex { index, type_id }),
420+
UntypedAssetId::Uuid { .. } => Err(UuidNotSupportedError),
421+
}
422+
}
423+
}
424+
425+
impl<A: Asset> TryFrom<&Handle<A>> for ErasedAssetIndex {
426+
type Error = UuidNotSupportedError;
427+
428+
fn try_from(handle: &Handle<A>) -> Result<Self, Self::Error> {
429+
match handle {
430+
Handle::Strong(handle) => Ok(Self::new(handle.index, handle.type_id)),
431+
Handle::Uuid(..) => Err(UuidNotSupportedError),
432+
}
433+
}
434+
}
435+
436+
impl TryFrom<&UntypedHandle> for ErasedAssetIndex {
437+
type Error = UuidNotSupportedError;
438+
439+
fn try_from(handle: &UntypedHandle) -> Result<Self, Self::Error> {
440+
match handle {
441+
UntypedHandle::Strong(handle) => Ok(Self::new(handle.index, handle.type_id)),
442+
UntypedHandle::Uuid { .. } => Err(UuidNotSupportedError),
443+
}
444+
}
445+
}
446+
447+
impl From<ErasedAssetIndex> for UntypedAssetId {
448+
fn from(value: ErasedAssetIndex) -> Self {
449+
Self::Index {
450+
type_id: value.type_id,
451+
index: value.index,
452+
}
453+
}
454+
}
455+
456+
#[derive(Error, Debug)]
457+
#[error("Attempted to create a TypedAssetIndex from a Uuid")]
458+
pub(crate) struct UuidNotSupportedError;
459+
417460
/// Errors preventing the conversion of to/from an [`UntypedAssetId`] and an [`AssetId`].
418461
#[derive(Error, Debug, PartialEq, Clone)]
419462
#[non_exhaustive]

0 commit comments

Comments
 (0)