use crate::asset_changed::AssetChanges;
use crate::{Asset, AssetEvent, AssetHandleProvider, AssetId, AssetServer, Handle, UntypedHandle};
use alloc::{sync::Arc, vec::Vec};
use bevy_ecs::{
prelude::EventWriter,
resource::Resource,
system::{Res, ResMut, SystemChangeTick},
};
use bevy_platform::collections::HashMap;
use bevy_reflect::{Reflect, TypePath};
use core::{any::TypeId, iter::Enumerate, marker::PhantomData, sync::atomic::AtomicU32};
use crossbeam_channel::{Receiver, Sender};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use uuid::Uuid;
#[derive(
Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Reflect, Serialize, Deserialize,
)]
pub struct AssetIndex {
pub(crate) generation: u32,
pub(crate) index: u32,
}
impl AssetIndex {
pub fn to_bits(self) -> u64 {
let Self { generation, index } = self;
((generation as u64) << 32) | index as u64
}
pub fn from_bits(bits: u64) -> Self {
let index = ((bits << 32) >> 32) as u32;
let generation = (bits >> 32) as u32;
Self { generation, index }
}
}
pub(crate) struct AssetIndexAllocator {
next_index: AtomicU32,
recycled_queue_sender: Sender<AssetIndex>,
recycled_queue_receiver: Receiver<AssetIndex>,
recycled_sender: Sender<AssetIndex>,
recycled_receiver: Receiver<AssetIndex>,
}
impl Default for AssetIndexAllocator {
fn default() -> Self {
let (recycled_queue_sender, recycled_queue_receiver) = crossbeam_channel::unbounded();
let (recycled_sender, recycled_receiver) = crossbeam_channel::unbounded();
Self {
recycled_queue_sender,
recycled_queue_receiver,
recycled_sender,
recycled_receiver,
next_index: Default::default(),
}
}
}
impl AssetIndexAllocator {
pub fn reserve(&self) -> AssetIndex {
if let Ok(mut recycled) = self.recycled_queue_receiver.try_recv() {
recycled.generation += 1;
self.recycled_sender.send(recycled).unwrap();
recycled
} else {
AssetIndex {
index: self
.next_index
.fetch_add(1, core::sync::atomic::Ordering::Relaxed),
generation: 0,
}
}
}
pub fn recycle(&self, index: AssetIndex) {
self.recycled_queue_sender.send(index).unwrap();
}
}
#[derive(Asset, TypePath)]
pub struct LoadedUntypedAsset {
#[dependency]
pub handle: UntypedHandle,
}
#[derive(Default)]
enum Entry<A: Asset> {
#[default]
None,
Some { value: Option<A>, generation: u32 },
}
struct DenseAssetStorage<A: Asset> {
storage: Vec<Entry<A>>,
len: u32,
allocator: Arc<AssetIndexAllocator>,
}
impl<A: Asset> Default for DenseAssetStorage<A> {
fn default() -> Self {
Self {
len: 0,
storage: Default::default(),
allocator: Default::default(),
}
}
}
impl<A: Asset> DenseAssetStorage<A> {
pub(crate) fn len(&self) -> usize {
self.len as usize
}
pub(crate) fn is_empty(&self) -> bool {
self.len == 0
}
pub(crate) fn insert(
&mut self,
index: AssetIndex,
asset: A,
) -> Result<bool, InvalidGenerationError> {
self.flush();
let entry = &mut self.storage[index.index as usize];
if let Entry::Some { value, generation } = entry {
if *generation == index.generation {
let exists = value.is_some();
if !exists {
self.len += 1;
}
*value = Some(asset);
Ok(exists)
} else {
Err(InvalidGenerationError::Occupied {
index,
current_generation: *generation,
})
}
} else {
Err(InvalidGenerationError::Removed { index })
}
}
pub(crate) fn remove_dropped(&mut self, index: AssetIndex) -> Option<A> {
self.remove_internal(index, |dense_storage| {
dense_storage.storage[index.index as usize] = Entry::None;
dense_storage.allocator.recycle(index);
})
}
pub(crate) fn remove_still_alive(&mut self, index: AssetIndex) -> Option<A> {
self.remove_internal(index, |_| {})
}
fn remove_internal(
&mut self,
index: AssetIndex,
removed_action: impl FnOnce(&mut Self),
) -> Option<A> {
self.flush();
let value = match &mut self.storage[index.index as usize] {
Entry::None => return None,
Entry::Some { value, generation } => {
if *generation == index.generation {
value.take().inspect(|_| self.len -= 1)
} else {
return None;
}
}
};
removed_action(self);
value
}
pub(crate) fn get(&self, index: AssetIndex) -> Option<&A> {
let entry = self.storage.get(index.index as usize)?;
match entry {
Entry::None => None,
Entry::Some { value, generation } => {
if *generation == index.generation {
value.as_ref()
} else {
None
}
}
}
}
pub(crate) fn get_mut(&mut self, index: AssetIndex) -> Option<&mut A> {
let entry = self.storage.get_mut(index.index as usize)?;
match entry {
Entry::None => None,
Entry::Some { value, generation } => {
if *generation == index.generation {
value.as_mut()
} else {
None
}
}
}
}
pub(crate) fn flush(&mut self) {
let new_len = self
.allocator
.next_index
.load(core::sync::atomic::Ordering::Relaxed);
self.storage.resize_with(new_len as usize, || Entry::Some {
value: None,
generation: 0,
});
while let Ok(recycled) = self.allocator.recycled_receiver.try_recv() {
let entry = &mut self.storage[recycled.index as usize];
*entry = Entry::Some {
value: None,
generation: recycled.generation,
};
}
}
pub(crate) fn get_index_allocator(&self) -> Arc<AssetIndexAllocator> {
self.allocator.clone()
}
pub(crate) fn ids(&self) -> impl Iterator<Item = AssetId<A>> + '_ {
self.storage
.iter()
.enumerate()
.filter_map(|(i, v)| match v {
Entry::None => None,
Entry::Some { value, generation } => {
if value.is_some() {
Some(AssetId::from(AssetIndex {
index: i as u32,
generation: *generation,
}))
} else {
None
}
}
})
}
}
#[derive(Resource)]
pub struct Assets<A: Asset> {
dense_storage: DenseAssetStorage<A>,
hash_map: HashMap<Uuid, A>,
handle_provider: AssetHandleProvider,
queued_events: Vec<AssetEvent<A>>,
duplicate_handles: HashMap<AssetId<A>, u16>,
}
impl<A: Asset> Default for Assets<A> {
fn default() -> Self {
let dense_storage = DenseAssetStorage::default();
let handle_provider =
AssetHandleProvider::new(TypeId::of::<A>(), dense_storage.get_index_allocator());
Self {
dense_storage,
handle_provider,
hash_map: Default::default(),
queued_events: Default::default(),
duplicate_handles: Default::default(),
}
}
}
impl<A: Asset> Assets<A> {
pub fn get_handle_provider(&self) -> AssetHandleProvider {
self.handle_provider.clone()
}
pub fn reserve_handle(&self) -> Handle<A> {
self.handle_provider.reserve_handle().typed::<A>()
}
pub fn insert(
&mut self,
id: impl Into<AssetId<A>>,
asset: A,
) -> Result<(), InvalidGenerationError> {
match id.into() {
AssetId::Index { index, .. } => self.insert_with_index(index, asset).map(|_| ()),
AssetId::Uuid { uuid } => {
self.insert_with_uuid(uuid, asset);
Ok(())
}
}
}
pub fn get_or_insert_with(
&mut self,
id: impl Into<AssetId<A>>,
insert_fn: impl FnOnce() -> A,
) -> Result<&mut A, InvalidGenerationError> {
let id: AssetId<A> = id.into();
if self.get(id).is_none() {
self.insert(id, insert_fn())?;
}
Ok(self
.get_mut(id)
.expect("the Asset was none even though we checked or inserted"))
}
pub fn contains(&self, id: impl Into<AssetId<A>>) -> bool {
match id.into() {
AssetId::Index { index, .. } => self.dense_storage.get(index).is_some(),
AssetId::Uuid { uuid } => self.hash_map.contains_key(&uuid),
}
}
pub(crate) fn insert_with_uuid(&mut self, uuid: Uuid, asset: A) -> Option<A> {
let result = self.hash_map.insert(uuid, asset);
if result.is_some() {
self.queued_events
.push(AssetEvent::Modified { id: uuid.into() });
} else {
self.queued_events
.push(AssetEvent::Added { id: uuid.into() });
}
result
}
pub(crate) fn insert_with_index(
&mut self,
index: AssetIndex,
asset: A,
) -> Result<bool, InvalidGenerationError> {
let replaced = self.dense_storage.insert(index, asset)?;
if replaced {
self.queued_events
.push(AssetEvent::Modified { id: index.into() });
} else {
self.queued_events
.push(AssetEvent::Added { id: index.into() });
}
Ok(replaced)
}
#[inline]
pub fn add(&mut self, asset: impl Into<A>) -> Handle<A> {
let index = self.dense_storage.allocator.reserve();
self.insert_with_index(index, asset.into()).unwrap();
Handle::Strong(
self.handle_provider
.get_handle(index.into(), false, None, None),
)
}
#[inline]
pub fn get_strong_handle(&mut self, id: AssetId<A>) -> Option<Handle<A>> {
if !self.contains(id) {
return None;
}
*self.duplicate_handles.entry(id).or_insert(0) += 1;
let index = match id {
AssetId::Index { index, .. } => index.into(),
AssetId::Uuid { uuid } => uuid.into(),
};
Some(Handle::Strong(
self.handle_provider.get_handle(index, false, None, None),
))
}
#[inline]
pub fn get(&self, id: impl Into<AssetId<A>>) -> Option<&A> {
match id.into() {
AssetId::Index { index, .. } => self.dense_storage.get(index),
AssetId::Uuid { uuid } => self.hash_map.get(&uuid),
}
}
#[inline]
pub fn get_mut(&mut self, id: impl Into<AssetId<A>>) -> Option<&mut A> {
let id: AssetId<A> = id.into();
let result = match id {
AssetId::Index { index, .. } => self.dense_storage.get_mut(index),
AssetId::Uuid { uuid } => self.hash_map.get_mut(&uuid),
};
if result.is_some() {
self.queued_events.push(AssetEvent::Modified { id });
}
result
}
#[inline]
pub fn get_mut_untracked(&mut self, id: impl Into<AssetId<A>>) -> Option<&mut A> {
let id: AssetId<A> = id.into();
match id {
AssetId::Index { index, .. } => self.dense_storage.get_mut(index),
AssetId::Uuid { uuid } => self.hash_map.get_mut(&uuid),
}
}
pub fn remove(&mut self, id: impl Into<AssetId<A>>) -> Option<A> {
let id: AssetId<A> = id.into();
let result = self.remove_untracked(id);
if result.is_some() {
self.queued_events.push(AssetEvent::Removed { id });
}
result
}
pub fn remove_untracked(&mut self, id: impl Into<AssetId<A>>) -> Option<A> {
let id: AssetId<A> = id.into();
self.duplicate_handles.remove(&id);
match id {
AssetId::Index { index, .. } => self.dense_storage.remove_still_alive(index),
AssetId::Uuid { uuid } => self.hash_map.remove(&uuid),
}
}
pub(crate) fn remove_dropped(&mut self, id: AssetId<A>) {
match self.duplicate_handles.get_mut(&id) {
None => {}
Some(0) => {
self.duplicate_handles.remove(&id);
}
Some(value) => {
*value -= 1;
return;
}
}
let existed = match id {
AssetId::Index { index, .. } => self.dense_storage.remove_dropped(index).is_some(),
AssetId::Uuid { uuid } => self.hash_map.remove(&uuid).is_some(),
};
self.queued_events.push(AssetEvent::Unused { id });
if existed {
self.queued_events.push(AssetEvent::Removed { id });
}
}
pub fn is_empty(&self) -> bool {
self.dense_storage.is_empty() && self.hash_map.is_empty()
}
pub fn len(&self) -> usize {
self.dense_storage.len() + self.hash_map.len()
}
pub fn ids(&self) -> impl Iterator<Item = AssetId<A>> + '_ {
self.dense_storage
.ids()
.chain(self.hash_map.keys().map(|uuid| AssetId::from(*uuid)))
}
pub fn iter(&self) -> impl Iterator<Item = (AssetId<A>, &A)> {
self.dense_storage
.storage
.iter()
.enumerate()
.filter_map(|(i, v)| match v {
Entry::None => None,
Entry::Some { value, generation } => value.as_ref().map(|v| {
let id = AssetId::Index {
index: AssetIndex {
generation: *generation,
index: i as u32,
},
marker: PhantomData,
};
(id, v)
}),
})
.chain(
self.hash_map
.iter()
.map(|(i, v)| (AssetId::Uuid { uuid: *i }, v)),
)
}
pub fn iter_mut(&mut self) -> AssetsMutIterator<'_, A> {
AssetsMutIterator {
dense_storage: self.dense_storage.storage.iter_mut().enumerate(),
hash_map: self.hash_map.iter_mut(),
queued_events: &mut self.queued_events,
}
}
pub fn track_assets(mut assets: ResMut<Self>, asset_server: Res<AssetServer>) {
let assets = &mut *assets;
let mut infos = asset_server.data.infos.write();
while let Ok(drop_event) = assets.handle_provider.drop_receiver.try_recv() {
let id = drop_event.id.typed();
if drop_event.asset_server_managed {
let untyped_id = id.untyped();
if !infos.process_handle_drop(untyped_id) {
continue;
}
}
assets.remove_dropped(id);
}
}
pub(crate) fn asset_events(
mut assets: ResMut<Self>,
mut events: EventWriter<AssetEvent<A>>,
asset_changes: Option<ResMut<AssetChanges<A>>>,
ticks: SystemChangeTick,
) {
use AssetEvent::{Added, LoadedWithDependencies, Modified, Removed};
if let Some(mut asset_changes) = asset_changes {
for new_event in &assets.queued_events {
match new_event {
Removed { id } | AssetEvent::Unused { id } => asset_changes.remove(id),
Added { id } | Modified { id } | LoadedWithDependencies { id } => {
asset_changes.insert(*id, ticks.this_run());
}
};
}
}
events.write_batch(assets.queued_events.drain(..));
}
pub(crate) fn asset_events_condition(assets: Res<Self>) -> bool {
!assets.queued_events.is_empty()
}
}
pub struct AssetsMutIterator<'a, A: Asset> {
queued_events: &'a mut Vec<AssetEvent<A>>,
dense_storage: Enumerate<core::slice::IterMut<'a, Entry<A>>>,
hash_map: bevy_platform::collections::hash_map::IterMut<'a, Uuid, A>,
}
impl<'a, A: Asset> Iterator for AssetsMutIterator<'a, A> {
type Item = (AssetId<A>, &'a mut A);
fn next(&mut self) -> Option<Self::Item> {
for (i, entry) in &mut self.dense_storage {
match entry {
Entry::None => {
continue;
}
Entry::Some { value, generation } => {
let id = AssetId::Index {
index: AssetIndex {
generation: *generation,
index: i as u32,
},
marker: PhantomData,
};
self.queued_events.push(AssetEvent::Modified { id });
if let Some(value) = value {
return Some((id, value));
}
}
}
}
if let Some((key, value)) = self.hash_map.next() {
let id = AssetId::Uuid { uuid: *key };
self.queued_events.push(AssetEvent::Modified { id });
Some((id, value))
} else {
None
}
}
}
#[derive(Error, Debug, PartialEq, Eq)]
pub enum InvalidGenerationError {
#[error("AssetIndex {index:?} has an invalid generation. The current generation is: '{current_generation}'.")]
Occupied {
index: AssetIndex,
current_generation: u32,
},
#[error("AssetIndex {index:?} has been removed")]
Removed { index: AssetIndex },
}
#[cfg(test)]
mod test {
use crate::AssetIndex;
#[test]
fn asset_index_round_trip() {
let asset_index = AssetIndex {
generation: 42,
index: 1337,
};
let roundtripped = AssetIndex::from_bits(asset_index.to_bits());
assert_eq!(asset_index, roundtripped);
}
}