bevy_defer/access/
async_asset.rs

1use std::any::type_name;
2use std::sync::atomic::{AtomicI32, Ordering};
3use std::sync::Arc;
4
5use crate::access::AsyncWorld;
6use crate::executor::{with_world_mut, ASSET_SERVER};
7use crate::sync::oneshot::MaybeChannelOut;
8use crate::{AccessError, AccessResult};
9use bevy::asset::meta::Settings;
10use bevy::asset::{Asset, AssetId, AssetPath, AssetServer, Assets, Handle, LoadState};
11use bevy::ecs::world::World;
12use event_listener::Event;
13use futures::future::{ready, Either};
14
15#[derive(Debug, Default)]
16pub struct AssetBarrierInner {
17    pub count: AtomicI32,
18    pub notify: Event,
19}
20
21#[derive(Debug, Default)]
22pub struct AssetBarrierGuard(Arc<AssetBarrierInner>);
23
24impl Clone for AssetBarrierGuard {
25    fn clone(&self) -> Self {
26        self.0.count.fetch_add(1, Ordering::AcqRel);
27        Self(self.0.clone())
28    }
29}
30
31impl Drop for AssetBarrierGuard {
32    fn drop(&mut self) {
33        let prev = self.0.count.fetch_sub(1, Ordering::AcqRel);
34        if prev <= 1 {
35            self.0.notify.notify(usize::MAX);
36        }
37    }
38}
39
40/// A set that can wait for multiple assets to finish loading.
41#[derive(Debug, Default)]
42pub struct AssetSet(Arc<AssetBarrierInner>);
43
44impl AssetSet {
45    pub fn new(&self) -> AssetSet {
46        AssetSet::default()
47    }
48
49    /// Start loading an asset and register for waiting.
50    pub fn load<A: Asset>(&self, path: impl Into<AssetPath<'static>>) -> Handle<A> {
51        if !ASSET_SERVER.is_set() {
52            panic!("AssetServer does not exist.")
53        }
54        self.0.count.fetch_add(1, Ordering::AcqRel);
55        ASSET_SERVER.with(|s| s.load_acquire::<A, _>(path, AssetBarrierGuard(self.0.clone())))
56    }
57
58    /// Wait for all loading to complete.
59    pub async fn wait(&self) {
60        loop {
61            if self.0.count.load(Ordering::Acquire) == 0 {
62                return;
63            }
64            self.0.notify.listen().await;
65        }
66    }
67}
68
69/// Async version of [`Handle`].
70#[derive(Debug)]
71pub struct AsyncAsset<A: Asset>(pub(crate) Handle<A>);
72
73impl<A: Asset> Clone for AsyncAsset<A> {
74    fn clone(&self) -> Self {
75        Self(self.0.clone())
76    }
77}
78
79impl<A: Asset> From<Handle<A>> for AsyncAsset<A> {
80    fn from(value: Handle<A>) -> Self {
81        AsyncAsset(value)
82    }
83}
84
85impl<A: Asset> From<&AsyncAsset<A>> for AssetId<A> {
86    fn from(val: &AsyncAsset<A>) -> Self {
87        val.id()
88    }
89}
90
91impl AsyncWorld {
92    /// Obtain an [`AsyncAsset`] from a [`Handle`].
93    ///
94    /// # Example
95    ///
96    /// ```
97    /// # bevy_defer::test_spawn!({
98    /// let square = AsyncWorld.load_asset::<Image>("square.png");
99    /// AsyncWorld.asset(&square);
100    /// # });
101    /// ```
102    pub fn asset<A: Asset>(&self, handle: impl Into<AssetId<A>>) -> AsyncAsset<A> {
103        AsyncAsset(Handle::Weak(handle.into()))
104    }
105
106    /// Load an asset from an [`AssetPath`], equivalent to `AssetServer::load`.
107    /// Does not wait for `Asset` to be loaded.
108    ///
109    /// # Panics
110    ///
111    /// If `AssetServer` does not exist in the world.
112    ///
113    /// # Example
114    ///
115    /// ```
116    /// # bevy_defer::test_spawn!({
117    /// let square = AsyncWorld.load_asset::<Image>("square.png");
118    /// # });
119    /// ```
120    pub fn load_asset<A: Asset>(
121        &self,
122        path: impl Into<AssetPath<'static>> + Send + 'static,
123    ) -> AsyncAsset<A> {
124        if !ASSET_SERVER.is_set() {
125            panic!("AssetServer does not exist.")
126        }
127        AsyncAsset(ASSET_SERVER.with(|s| s.load::<A>(path)))
128    }
129
130    /// Begins loading an Asset of type `A` stored at path.
131    /// The given settings function will override the asset's AssetLoader settings.
132    pub fn load_asset_with_settings<A: Asset, S: Settings>(
133        &self,
134        path: impl Into<AssetPath<'static>> + Send + 'static,
135        f: impl Fn(&mut S) + Send + Sync + 'static,
136    ) -> AsyncAsset<A> {
137        if !ASSET_SERVER.is_set() {
138            panic!("AssetServer does not exist.")
139        }
140        AsyncAsset(ASSET_SERVER.with(|s| s.load_with_settings::<A, S>(path, f)))
141    }
142
143    /// Add an asset and obtain its handle.
144    pub fn add_asset<A: Asset + 'static>(&self, item: A) -> AccessResult<Handle<A>> {
145        with_world_mut(|w| {
146            Ok(w.get_resource_mut::<Assets<A>>()
147                .ok_or(AccessError::ResourceNotFound {
148                    name: type_name::<Assets<A>>(),
149                })?
150                .add(item))
151        })
152    }
153}
154
155impl<A: Asset> AsyncAsset<A> {
156    /// Obtain the underlying [`AssetId`].
157    pub fn id(&self) -> AssetId<A> {
158        self.0.id()
159    }
160
161    /// Obtain the underlying [`Handle`].
162    pub fn handle(&self) -> &Handle<A> {
163        &self.0
164    }
165
166    /// Create an [`AsyncAsset`] from a [`Handle`].
167    pub fn from_handle(handle: Handle<A>) -> Self {
168        AsyncAsset(handle)
169    }
170
171    /// Obtain the underlying [`Handle`].
172    pub fn into_handle(self) -> Handle<A> {
173        self.0
174    }
175
176    /// Repeat until the asset is loaded, returns false if loading failed.
177    pub fn loaded(&self) -> MaybeChannelOut<bool> {
178        if !ASSET_SERVER.is_set() {
179            panic!("AssetServer does not exist.")
180        }
181        match ASSET_SERVER.with(|server| server.load_state(&self.0)) {
182            LoadState::Loaded => return Either::Right(ready(true)),
183            LoadState::Failed(..) => return Either::Right(ready(false)),
184            _ => (),
185        };
186        let handle = self.0.id();
187        AsyncWorld.watch_left(move |world: &mut World| {
188            match world.resource::<AssetServer>().load_state(handle) {
189                LoadState::Loaded => Some(true),
190                LoadState::Failed(..) => Some(false),
191                _ => None,
192            }
193        })
194    }
195}