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, 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, 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, .. } => {
487 self.duplicate_handles.remove(&index);
488 self.dense_storage.remove_still_alive(index)
489 }
490 AssetId::Uuid { uuid } => self.hash_map.remove(&uuid),
491 }
492 }
493
494 pub(crate) fn remove_dropped(&mut self, index: AssetIndex) {
496 match self.duplicate_handles.get_mut(&index) {
497 None => {}
498 Some(0) => {
499 self.duplicate_handles.remove(&index);
500 }
501 Some(value) => {
502 *value -= 1;
503 return;
504 }
505 }
506
507 let existed = self.dense_storage.remove_dropped(index).is_some();
508
509 self.queued_events
510 .push(AssetEvent::Unused { id: index.into() });
511 if existed {
512 self.queued_events
513 .push(AssetEvent::Removed { id: index.into() });
514 }
515 }
516
517 pub fn is_empty(&self) -> bool {
519 self.dense_storage.is_empty() && self.hash_map.is_empty()
520 }
521
522 pub fn len(&self) -> usize {
524 self.dense_storage.len() + self.hash_map.len()
525 }
526
527 pub fn ids(&self) -> impl Iterator<Item = AssetId<A>> + '_ {
529 self.dense_storage
530 .ids()
531 .chain(self.hash_map.keys().map(|uuid| AssetId::from(*uuid)))
532 }
533
534 pub fn iter(&self) -> impl Iterator<Item = (AssetId<A>, &A)> {
537 self.dense_storage
538 .storage
539 .iter()
540 .enumerate()
541 .filter_map(|(i, v)| match v {
542 Entry::None => None,
543 Entry::Some { value, generation } => value.as_ref().map(|v| {
544 let id = AssetId::Index {
545 index: AssetIndex {
546 generation: *generation,
547 index: i as u32,
548 },
549 marker: PhantomData,
550 };
551 (id, v)
552 }),
553 })
554 .chain(
555 self.hash_map
556 .iter()
557 .map(|(i, v)| (AssetId::Uuid { uuid: *i }, v)),
558 )
559 }
560
561 pub fn iter_mut(&mut self) -> AssetsMutIterator<'_, A> {
564 AssetsMutIterator {
565 dense_storage: self.dense_storage.storage.iter_mut().enumerate(),
566 hash_map: self.hash_map.iter_mut(),
567 queued_events: &mut self.queued_events,
568 }
569 }
570
571 pub fn track_assets(mut assets: ResMut<Self>, asset_server: Res<AssetServer>) {
574 let mut infos = asset_server.write_infos();
579 while let Ok(drop_event) = assets.handle_provider.drop_receiver.try_recv() {
580 if drop_event.asset_server_managed {
581 if !infos.process_handle_drop(drop_event.index) {
583 continue;
585 }
586 }
587
588 assets.remove_dropped(drop_event.index.index);
589 }
590 }
591
592 pub(crate) fn asset_events(
596 mut assets: ResMut<Self>,
597 mut messages: MessageWriter<AssetEvent<A>>,
598 asset_changes: Option<ResMut<AssetChanges<A>>>,
599 ticks: SystemChangeTick,
600 ) {
601 use AssetEvent::{Added, LoadedWithDependencies, Modified, Removed};
602
603 if let Some(mut asset_changes) = asset_changes {
604 for new_event in &assets.queued_events {
605 match new_event {
606 Removed { id } | AssetEvent::Unused { id } => asset_changes.remove(id),
607 Added { id } | Modified { id } | LoadedWithDependencies { id } => {
608 asset_changes.insert(*id, ticks.this_run());
609 }
610 };
611 }
612 }
613 messages.write_batch(assets.queued_events.drain(..));
614 }
615
616 pub(crate) fn asset_events_condition(assets: Res<Self>) -> bool {
621 !assets.queued_events.is_empty()
622 }
623}
624
625pub struct AssetMut<'a, A: Asset> {
632 asset: &'a mut A,
633 guard: AssetMutChangeNotifier<'a, A>,
634}
635
636impl<'a, A: Asset> AssetMut<'a, A> {
637 pub fn into_inner(mut self) -> &'a mut A {
639 self.guard.changed = true;
640 self.asset
641 }
642
643 pub fn into_inner_untracked(self) -> &'a mut A {
645 self.asset
646 }
647
648 pub fn bypass_change_detection(&mut self) -> &mut A {
656 self.asset
657 }
658}
659
660impl<'a, A: Asset> Deref for AssetMut<'a, A> {
661 type Target = A;
662
663 fn deref(&self) -> &Self::Target {
664 self.asset
665 }
666}
667
668impl<'a, A: Asset> DerefMut for AssetMut<'a, A> {
669 fn deref_mut(&mut self) -> &mut Self::Target {
670 self.guard.changed = true;
671 self.asset
672 }
673}
674
675struct AssetMutChangeNotifier<'a, A: Asset> {
678 changed: bool,
679 asset_id: AssetId<A>,
680 queued_events: &'a mut Vec<AssetEvent<A>>,
681}
682
683impl<'a, A: Asset> Drop for AssetMutChangeNotifier<'a, A> {
684 fn drop(&mut self) {
685 if self.changed {
686 self.queued_events
687 .push(AssetEvent::Modified { id: self.asset_id });
688 }
689 }
690}
691
692pub struct AssetsMutIterator<'a, A: Asset> {
694 queued_events: &'a mut Vec<AssetEvent<A>>,
695 dense_storage: Enumerate<core::slice::IterMut<'a, Entry<A>>>,
696 hash_map: bevy_platform::collections::hash_map::IterMut<'a, Uuid, A>,
697}
698
699impl<'a, A: Asset> Iterator for AssetsMutIterator<'a, A> {
700 type Item = (AssetId<A>, &'a mut A);
701
702 fn next(&mut self) -> Option<Self::Item> {
703 for (i, entry) in &mut self.dense_storage {
704 match entry {
705 Entry::None => {
706 continue;
707 }
708 Entry::Some { value, generation } => {
709 let id = AssetId::Index {
710 index: AssetIndex {
711 generation: *generation,
712 index: i as u32,
713 },
714 marker: PhantomData,
715 };
716 self.queued_events.push(AssetEvent::Modified { id });
717 if let Some(value) = value {
718 return Some((id, value));
719 }
720 }
721 }
722 }
723 if let Some((key, value)) = self.hash_map.next() {
724 let id = AssetId::Uuid { uuid: *key };
725 self.queued_events.push(AssetEvent::Modified { id });
726 Some((id, value))
727 } else {
728 None
729 }
730 }
731}
732
733#[derive(Error, Debug, PartialEq, Eq)]
735pub enum InvalidGenerationError {
736 #[error("AssetIndex {index:?} has an invalid generation. The current generation is: '{current_generation}'.")]
737 Occupied {
738 index: AssetIndex,
739 current_generation: u32,
740 },
741 #[error("AssetIndex {index:?} has been removed")]
742 Removed { index: AssetIndex },
743}
744
745#[cfg(test)]
746mod test {
747 use crate::tests::create_app;
748 use crate::{Asset, AssetApp, AssetEvent, AssetIndex, Assets};
749 use bevy_ecs::prelude::Messages;
750 use bevy_reflect::TypePath;
751
752 #[test]
753 fn asset_index_round_trip() {
754 let asset_index = AssetIndex {
755 generation: 42,
756 index: 1337,
757 };
758 let roundtripped = AssetIndex::from_bits(asset_index.to_bits());
759 assert_eq!(asset_index, roundtripped);
760 }
761
762 #[test]
763 fn assets_mut_change_detection() {
764 #[derive(Asset, TypePath, Default)]
765 struct TestAsset {
766 value: u32,
767 }
768
769 let mut app = create_app().0;
770 app.init_asset::<TestAsset>();
771
772 let mut assets = app.world_mut().resource_mut::<Assets<TestAsset>>();
773 let my_asset_handle = assets.add(TestAsset::default());
774 let my_asset_id = my_asset_handle.id();
775
776 for _ in 0..3 {
778 {
780 let mut assets = app.world_mut().resource_mut::<Assets<TestAsset>>();
781 let mut asset = assets.get_mut(my_asset_id).unwrap();
782 asset.value += 1;
783 }
784
785 app.update();
786
787 let modified_count = app
788 .world_mut()
789 .resource_mut::<Messages<AssetEvent<TestAsset>>>()
790 .drain()
791 .filter(|event| event.is_modified(my_asset_id))
792 .count();
793
794 assert_eq!(
795 modified_count, 1,
796 "Asset value was changed but AssetEvent::Modified was not triggered",
797 );
798
799 {
801 let mut assets = app.world_mut().resource_mut::<Assets<TestAsset>>();
802 let asset = assets.get_mut(my_asset_id).unwrap();
803 let _temp = asset.value;
804 }
805
806 app.update();
807
808 let modified_count = app
809 .world_mut()
810 .resource_mut::<Messages<AssetEvent<TestAsset>>>()
811 .drain()
812 .filter(|event| event.is_modified(my_asset_id))
813 .count();
814
815 assert_eq!(
816 modified_count, 0,
817 "Asset value was not changed but AssetEvent::Modified was triggered",
818 );
819 }
820 }
821}