1use crate::asset_changed::AssetChanges;
2use crate::{Asset, AssetEvent, AssetHandleProvider, AssetId, AssetServer, Handle, UntypedHandle};
3use alloc::{sync::Arc, vec::Vec};
4use bevy_ecs::{
5 message::MessageWriter,
6 resource::Resource,
7 system::{Res, ResMut, SystemChangeTick},
8};
9use bevy_platform::collections::HashMap;
10use bevy_reflect::{Reflect, TypePath};
11use core::ops::{Deref, DerefMut};
12use core::{any::TypeId, iter::Enumerate, marker::PhantomData, sync::atomic::AtomicU32};
13use crossbeam_channel::{Receiver, Sender};
14use serde::{Deserialize, Serialize};
15use thiserror::Error;
16use uuid::Uuid;
17
18#[derive(
21 Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Reflect, Serialize, Deserialize,
22)]
23pub struct AssetIndex {
24 pub(crate) generation: u32,
25 pub(crate) index: u32,
26}
27
28impl AssetIndex {
29 pub fn to_bits(self) -> u64 {
33 let Self { generation, index } = self;
34 ((generation as u64) << 32) | index as u64
35 }
36 pub fn from_bits(bits: u64) -> Self {
39 let index = ((bits << 32) >> 32) as u32;
40 let generation = (bits >> 32) as u32;
41 Self { generation, index }
42 }
43}
44
45pub(crate) struct AssetIndexAllocator {
47 next_index: AtomicU32,
49 recycled_queue_sender: Sender<AssetIndex>,
50 recycled_queue_receiver: Receiver<AssetIndex>,
52 recycled_sender: Sender<AssetIndex>,
53 recycled_receiver: Receiver<AssetIndex>,
54}
55
56impl Default for AssetIndexAllocator {
57 fn default() -> Self {
58 let (recycled_queue_sender, recycled_queue_receiver) = crossbeam_channel::unbounded();
59 let (recycled_sender, recycled_receiver) = crossbeam_channel::unbounded();
60 Self {
61 recycled_queue_sender,
62 recycled_queue_receiver,
63 recycled_sender,
64 recycled_receiver,
65 next_index: Default::default(),
66 }
67 }
68}
69
70impl AssetIndexAllocator {
71 pub fn reserve(&self) -> AssetIndex {
74 if let Ok(mut recycled) = self.recycled_queue_receiver.try_recv() {
75 recycled.generation += 1;
76 self.recycled_sender.send(recycled).unwrap();
77 recycled
78 } else {
79 AssetIndex {
80 index: self
81 .next_index
82 .fetch_add(1, core::sync::atomic::Ordering::Relaxed),
83 generation: 0,
84 }
85 }
86 }
87
88 pub fn recycle(&self, index: AssetIndex) {
90 self.recycled_queue_sender.send(index).unwrap();
91 }
92}
93
94#[derive(Asset, TypePath)]
98pub struct LoadedUntypedAsset {
99 #[dependency]
101 pub handle: UntypedHandle,
102}
103
104#[derive(Default)]
106enum Entry<A: Asset> {
107 #[default]
109 None,
110 Some { value: Option<A>, generation: u32 },
112}
113
114struct DenseAssetStorage<A: Asset> {
116 storage: Vec<Entry<A>>,
117 len: u32,
118 allocator: Arc<AssetIndexAllocator>,
119}
120
121impl<A: Asset> Default for DenseAssetStorage<A> {
122 fn default() -> Self {
123 Self {
124 len: 0,
125 storage: Default::default(),
126 allocator: Default::default(),
127 }
128 }
129}
130
131impl<A: Asset> DenseAssetStorage<A> {
132 pub(crate) fn len(&self) -> usize {
134 self.len as usize
135 }
136
137 pub(crate) fn is_empty(&self) -> bool {
139 self.len == 0
140 }
141
142 pub(crate) fn insert(
144 &mut self,
145 index: AssetIndex,
146 asset: A,
147 ) -> Result<bool, InvalidGenerationError> {
148 self.flush();
149 let entry = &mut self.storage[index.index as usize];
150 if let Entry::Some { value, generation } = entry {
151 if *generation == index.generation {
152 let exists = value.is_some();
153 if !exists {
154 self.len += 1;
155 }
156 *value = Some(asset);
157 Ok(exists)
158 } else {
159 Err(InvalidGenerationError::Occupied {
160 index,
161 current_generation: *generation,
162 })
163 }
164 } else {
165 Err(InvalidGenerationError::Removed { index })
166 }
167 }
168
169 pub(crate) fn remove_dropped(&mut self, index: AssetIndex) -> Option<A> {
172 self.remove_internal(index, |dense_storage| {
173 dense_storage.storage[index.index as usize] = Entry::None;
174 dense_storage.allocator.recycle(index);
175 })
176 }
177
178 pub(crate) fn remove_still_alive(&mut self, index: AssetIndex) -> Option<A> {
182 self.remove_internal(index, |_| {})
183 }
184
185 fn remove_internal(
186 &mut self,
187 index: AssetIndex,
188 removed_action: impl FnOnce(&mut Self),
189 ) -> Option<A> {
190 self.flush();
191 let value = match &mut self.storage[index.index as usize] {
192 Entry::None => return None,
193 Entry::Some { value, generation } => {
194 if *generation == index.generation {
195 value.take().inspect(|_| self.len -= 1)
196 } else {
197 return None;
198 }
199 }
200 };
201 removed_action(self);
202 value
203 }
204
205 pub(crate) fn get(&self, index: AssetIndex) -> Option<&A> {
206 let entry = self.storage.get(index.index as usize)?;
207 match entry {
208 Entry::None => None,
209 Entry::Some { value, generation } => {
210 if *generation == index.generation {
211 value.as_ref()
212 } else {
213 None
214 }
215 }
216 }
217 }
218
219 pub(crate) fn get_mut(&mut self, index: AssetIndex) -> Option<&mut A> {
220 let entry = self.storage.get_mut(index.index as usize)?;
221 match entry {
222 Entry::None => None,
223 Entry::Some { value, generation } => {
224 if *generation == index.generation {
225 value.as_mut()
226 } else {
227 None
228 }
229 }
230 }
231 }
232
233 pub(crate) fn flush(&mut self) {
234 let new_len = self
236 .allocator
237 .next_index
238 .load(core::sync::atomic::Ordering::Relaxed);
239 self.storage.resize_with(new_len as usize, || Entry::Some {
240 value: None,
241 generation: 0,
242 });
243 while let Ok(recycled) = self.allocator.recycled_receiver.try_recv() {
244 let entry = &mut self.storage[recycled.index as usize];
245 *entry = Entry::Some {
246 value: None,
247 generation: recycled.generation,
248 };
249 }
250 }
251
252 pub(crate) fn get_index_allocator(&self) -> Arc<AssetIndexAllocator> {
253 self.allocator.clone()
254 }
255
256 pub(crate) fn ids(&self) -> impl Iterator<Item = AssetId<A>> + '_ {
257 self.storage
258 .iter()
259 .enumerate()
260 .filter_map(|(i, v)| match v {
261 Entry::None => None,
262 Entry::Some { value, generation } => {
263 if value.is_some() {
264 Some(AssetId::from(AssetIndex {
265 index: i as u32,
266 generation: *generation,
267 }))
268 } else {
269 None
270 }
271 }
272 })
273 }
274}
275
276#[derive(Resource)]
288pub struct Assets<A: Asset> {
289 dense_storage: DenseAssetStorage<A>,
290 hash_map: HashMap<Uuid, A>,
291 handle_provider: AssetHandleProvider,
292 queued_events: Vec<AssetEvent<A>>,
293 duplicate_handles: HashMap<AssetIndex, u16>,
296}
297
298impl<A: Asset> Default for Assets<A> {
299 fn default() -> Self {
300 let dense_storage = DenseAssetStorage::default();
301 let handle_provider =
302 AssetHandleProvider::new(TypeId::of::<A>(), dense_storage.get_index_allocator());
303 Self {
304 dense_storage,
305 handle_provider,
306 hash_map: Default::default(),
307 queued_events: Default::default(),
308 duplicate_handles: Default::default(),
309 }
310 }
311}
312
313impl<A: Asset> Assets<A> {
314 pub fn get_handle_provider(&self) -> AssetHandleProvider {
317 self.handle_provider.clone()
318 }
319
320 pub fn reserve_handle(&self) -> Handle<A> {
322 self.handle_provider.reserve_handle().typed::<A>()
323 }
324
325 pub fn insert(
330 &mut self,
331 id: impl Into<AssetId<A>>,
332 asset: A,
333 ) -> Result<(), InvalidGenerationError> {
334 match id.into() {
335 AssetId::Index { index, .. } => self.insert_with_index(index, asset).map(|_| ()),
336 AssetId::Uuid { uuid } => {
337 self.insert_with_uuid(uuid, asset);
338 Ok(())
339 }
340 }
341 }
342
343 pub fn get_or_insert_with(
349 &mut self,
350 id: impl Into<AssetId<A>>,
351 insert_fn: impl FnOnce() -> A,
352 ) -> Result<AssetMut<'_, A>, InvalidGenerationError> {
353 let id: AssetId<A> = id.into();
354 if self.get(id).is_none() {
355 self.insert(id, insert_fn())?;
356 }
357 Ok(self
360 .get_mut(id)
361 .expect("the Asset was none even though we checked or inserted"))
362 }
363
364 pub fn contains(&self, id: impl Into<AssetId<A>>) -> bool {
366 match id.into() {
367 AssetId::Index { index, .. } => self.dense_storage.get(index).is_some(),
368 AssetId::Uuid { uuid } => self.hash_map.contains_key(&uuid),
369 }
370 }
371
372 pub(crate) fn insert_with_uuid(&mut self, uuid: Uuid, asset: A) -> Option<A> {
373 let result = self.hash_map.insert(uuid, asset);
374 if result.is_some() {
375 self.queued_events
376 .push(AssetEvent::Modified { id: uuid.into() });
377 } else {
378 self.queued_events
379 .push(AssetEvent::Added { id: uuid.into() });
380 }
381 result
382 }
383 pub(crate) fn insert_with_index(
384 &mut self,
385 index: AssetIndex,
386 asset: A,
387 ) -> Result<bool, InvalidGenerationError> {
388 let replaced = self.dense_storage.insert(index, asset)?;
389 if replaced {
390 self.queued_events
391 .push(AssetEvent::Modified { id: index.into() });
392 } else {
393 self.queued_events
394 .push(AssetEvent::Added { id: index.into() });
395 }
396 Ok(replaced)
397 }
398
399 #[inline]
401 pub fn add(&mut self, asset: impl Into<A>) -> Handle<A> {
402 let index = self.dense_storage.allocator.reserve();
403 self.insert_with_index(index, asset.into()).unwrap();
404 Handle::Strong(self.handle_provider.get_handle(index, false, None))
405 }
406
407 #[inline]
412 pub fn get_strong_handle(&mut self, id: AssetId<A>) -> Option<Handle<A>> {
413 if !self.contains(id) {
414 return None;
415 }
416 let index = match id {
417 AssetId::Index { index, .. } => index,
418 AssetId::Uuid { .. } => return None,
420 };
421 *self.duplicate_handles.entry(index).or_insert(0) += 1;
422 Some(Handle::Strong(
423 self.handle_provider.get_handle(index, false, None),
424 ))
425 }
426
427 #[inline]
430 pub fn get(&self, id: impl Into<AssetId<A>>) -> Option<&A> {
431 match id.into() {
432 AssetId::Index { index, .. } => self.dense_storage.get(index),
433 AssetId::Uuid { uuid } => self.hash_map.get(&uuid),
434 }
435 }
436
437 #[inline]
440 pub fn get_mut(&mut self, id: impl Into<AssetId<A>>) -> Option<AssetMut<'_, A>> {
441 let id: AssetId<A> = id.into();
442 let result = match id {
443 AssetId::Index { index, .. } => self.dense_storage.get_mut(index),
444 AssetId::Uuid { uuid } => self.hash_map.get_mut(&uuid),
445 };
446 Some(AssetMut {
447 asset: result?,
448 guard: AssetMutChangeNotifier {
449 changed: false,
450 asset_id: id,
451 queued_events: &mut self.queued_events,
452 },
453 })
454 }
455
456 #[inline]
460 pub fn get_mut_untracked(&mut self, id: impl Into<AssetId<A>>) -> Option<&mut A> {
461 let id: AssetId<A> = id.into();
462 match id {
463 AssetId::Index { index, .. } => self.dense_storage.get_mut(index),
464 AssetId::Uuid { uuid } => self.hash_map.get_mut(&uuid),
465 }
466 }
467
468 pub fn remove(&mut self, id: impl Into<AssetId<A>>) -> Option<A> {
471 let id: AssetId<A> = id.into();
472 let result = self.remove_untracked(id);
473 if result.is_some() {
474 self.queued_events.push(AssetEvent::Removed { id });
475 }
476 result
477 }
478
479 pub fn remove_untracked(&mut self, id: impl Into<AssetId<A>>) -> Option<A> {
484 let id: AssetId<A> = id.into();
485 match id {
486 AssetId::Index { index, .. } => self.dense_storage.remove_still_alive(index),
487 AssetId::Uuid { uuid } => self.hash_map.remove(&uuid),
488 }
489 }
490
491 pub(crate) fn remove_dropped(&mut self, index: AssetIndex) {
493 match self.duplicate_handles.get_mut(&index) {
494 None => {}
495 Some(0) => {
496 self.duplicate_handles.remove(&index);
497 }
498 Some(value) => {
499 *value -= 1;
500 return;
501 }
502 }
503
504 let existed = self.dense_storage.remove_dropped(index).is_some();
505
506 self.queued_events
507 .push(AssetEvent::Unused { id: index.into() });
508 if existed {
509 self.queued_events
510 .push(AssetEvent::Removed { id: index.into() });
511 }
512 }
513
514 pub fn is_empty(&self) -> bool {
516 self.dense_storage.is_empty() && self.hash_map.is_empty()
517 }
518
519 pub fn len(&self) -> usize {
521 self.dense_storage.len() + self.hash_map.len()
522 }
523
524 pub fn ids(&self) -> impl Iterator<Item = AssetId<A>> + '_ {
526 self.dense_storage
527 .ids()
528 .chain(self.hash_map.keys().map(|uuid| AssetId::from(*uuid)))
529 }
530
531 pub fn iter(&self) -> impl Iterator<Item = (AssetId<A>, &A)> {
534 self.dense_storage
535 .storage
536 .iter()
537 .enumerate()
538 .filter_map(|(i, v)| match v {
539 Entry::None => None,
540 Entry::Some { value, generation } => value.as_ref().map(|v| {
541 let id = AssetId::Index {
542 index: AssetIndex {
543 generation: *generation,
544 index: i as u32,
545 },
546 marker: PhantomData,
547 };
548 (id, v)
549 }),
550 })
551 .chain(
552 self.hash_map
553 .iter()
554 .map(|(i, v)| (AssetId::Uuid { uuid: *i }, v)),
555 )
556 }
557
558 pub fn iter_mut(&mut self) -> AssetsMutIterator<'_, A> {
561 AssetsMutIterator {
562 dense_storage: self.dense_storage.storage.iter_mut().enumerate(),
563 hash_map: self.hash_map.iter_mut(),
564 queued_events: &mut self.queued_events,
565 }
566 }
567
568 pub fn track_assets(mut assets: ResMut<Self>, asset_server: Res<AssetServer>) {
571 let mut infos = asset_server.write_infos();
576 while let Ok(drop_event) = assets.handle_provider.drop_receiver.try_recv() {
577 if drop_event.asset_server_managed {
578 if !infos.process_handle_drop(drop_event.index) {
580 continue;
582 }
583 }
584
585 assets.remove_dropped(drop_event.index.index);
586 }
587 }
588
589 pub(crate) fn asset_events(
593 mut assets: ResMut<Self>,
594 mut messages: MessageWriter<AssetEvent<A>>,
595 asset_changes: Option<ResMut<AssetChanges<A>>>,
596 ticks: SystemChangeTick,
597 ) {
598 use AssetEvent::{Added, LoadedWithDependencies, Modified, Removed};
599
600 if let Some(mut asset_changes) = asset_changes {
601 for new_event in &assets.queued_events {
602 match new_event {
603 Removed { id } | AssetEvent::Unused { id } => asset_changes.remove(id),
604 Added { id } | Modified { id } | LoadedWithDependencies { id } => {
605 asset_changes.insert(*id, ticks.this_run());
606 }
607 };
608 }
609 }
610 messages.write_batch(assets.queued_events.drain(..));
611 }
612
613 pub(crate) fn asset_events_condition(assets: Res<Self>) -> bool {
618 !assets.queued_events.is_empty()
619 }
620}
621
622pub struct AssetMut<'a, A: Asset> {
629 asset: &'a mut A,
630 guard: AssetMutChangeNotifier<'a, A>,
631}
632
633impl<'a, A: Asset> AssetMut<'a, A> {
634 pub fn into_inner(mut self) -> &'a mut A {
636 self.guard.changed = true;
637 self.asset
638 }
639
640 pub fn into_inner_untracked(self) -> &'a mut A {
642 self.asset
643 }
644
645 pub fn bypass_change_detection(&mut self) -> &mut A {
653 self.asset
654 }
655}
656
657impl<'a, A: Asset> Deref for AssetMut<'a, A> {
658 type Target = A;
659
660 fn deref(&self) -> &Self::Target {
661 self.asset
662 }
663}
664
665impl<'a, A: Asset> DerefMut for AssetMut<'a, A> {
666 fn deref_mut(&mut self) -> &mut Self::Target {
667 self.guard.changed = true;
668 self.asset
669 }
670}
671
672struct AssetMutChangeNotifier<'a, A: Asset> {
675 changed: bool,
676 asset_id: AssetId<A>,
677 queued_events: &'a mut Vec<AssetEvent<A>>,
678}
679
680impl<'a, A: Asset> Drop for AssetMutChangeNotifier<'a, A> {
681 fn drop(&mut self) {
682 if self.changed {
683 self.queued_events
684 .push(AssetEvent::Modified { id: self.asset_id });
685 }
686 }
687}
688
689pub struct AssetsMutIterator<'a, A: Asset> {
691 queued_events: &'a mut Vec<AssetEvent<A>>,
692 dense_storage: Enumerate<core::slice::IterMut<'a, Entry<A>>>,
693 hash_map: bevy_platform::collections::hash_map::IterMut<'a, Uuid, A>,
694}
695
696impl<'a, A: Asset> Iterator for AssetsMutIterator<'a, A> {
697 type Item = (AssetId<A>, &'a mut A);
698
699 fn next(&mut self) -> Option<Self::Item> {
700 for (i, entry) in &mut self.dense_storage {
701 match entry {
702 Entry::None => {
703 continue;
704 }
705 Entry::Some { value, generation } => {
706 let id = AssetId::Index {
707 index: AssetIndex {
708 generation: *generation,
709 index: i as u32,
710 },
711 marker: PhantomData,
712 };
713 self.queued_events.push(AssetEvent::Modified { id });
714 if let Some(value) = value {
715 return Some((id, value));
716 }
717 }
718 }
719 }
720 if let Some((key, value)) = self.hash_map.next() {
721 let id = AssetId::Uuid { uuid: *key };
722 self.queued_events.push(AssetEvent::Modified { id });
723 Some((id, value))
724 } else {
725 None
726 }
727 }
728}
729
730#[derive(Error, Debug, PartialEq, Eq)]
732pub enum InvalidGenerationError {
733 #[error("AssetIndex {index:?} has an invalid generation. The current generation is: '{current_generation}'.")]
736 Occupied {
737 index: AssetIndex,
739 current_generation: u32,
741 },
742 #[error("AssetIndex {index:?} has been removed")]
744 Removed { index: AssetIndex },
745}
746
747#[cfg(test)]
748mod test {
749 use crate::tests::create_app;
750 use crate::{Asset, AssetApp, AssetEvent, AssetIndex, Assets};
751 use bevy_ecs::prelude::Messages;
752 use bevy_reflect::TypePath;
753
754 #[test]
755 fn asset_index_round_trip() {
756 let asset_index = AssetIndex {
757 generation: 42,
758 index: 1337,
759 };
760 let roundtripped = AssetIndex::from_bits(asset_index.to_bits());
761 assert_eq!(asset_index, roundtripped);
762 }
763
764 #[test]
765 fn assets_mut_change_detection() {
766 #[derive(Asset, TypePath, Default)]
767 struct TestAsset {
768 value: u32,
769 }
770
771 let mut app = create_app().0;
772 app.init_asset::<TestAsset>();
773
774 let mut assets = app.world_mut().resource_mut::<Assets<TestAsset>>();
775 let my_asset_handle = assets.add(TestAsset::default());
776 let my_asset_id = my_asset_handle.id();
777
778 for _ in 0..3 {
780 {
782 let mut assets = app.world_mut().resource_mut::<Assets<TestAsset>>();
783 let mut asset = assets.get_mut(my_asset_id).unwrap();
784 asset.value += 1;
785 }
786
787 app.update();
788
789 let modified_count = app
790 .world_mut()
791 .resource_mut::<Messages<AssetEvent<TestAsset>>>()
792 .drain()
793 .filter(|event| event.is_modified(my_asset_id))
794 .count();
795
796 assert_eq!(
797 modified_count, 1,
798 "Asset value was changed but AssetEvent::Modified was not triggered",
799 );
800
801 {
803 let mut assets = app.world_mut().resource_mut::<Assets<TestAsset>>();
804 let asset = assets.get_mut(my_asset_id).unwrap();
805 let _temp = asset.value;
806 }
807
808 app.update();
809
810 let modified_count = app
811 .world_mut()
812 .resource_mut::<Messages<AssetEvent<TestAsset>>>()
813 .drain()
814 .filter(|event| event.is_modified(my_asset_id))
815 .count();
816
817 assert_eq!(
818 modified_count, 0,
819 "Asset value was not changed but AssetEvent::Modified was triggered",
820 );
821 }
822 }
823}