fyrox_resource/
state.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! A module that handles resource states.
22
23use crate::{
24    core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*},
25    manager::ResourceManager,
26    ResourceData, ResourceLoadError,
27};
28use std::{
29    ops::{Deref, DerefMut},
30    sync::Arc,
31    task::Waker,
32};
33
34#[doc(hidden)]
35#[derive(Reflect, Debug, Default)]
36#[reflect(hide_all)]
37pub struct WakersList(Vec<Waker>);
38
39impl Deref for WakersList {
40    type Target = Vec<Waker>;
41
42    fn deref(&self) -> &Self::Target {
43        &self.0
44    }
45}
46
47impl DerefMut for WakersList {
48    fn deref_mut(&mut self) -> &mut Self::Target {
49        &mut self.0
50    }
51}
52
53/// Arbitrary loading error, that could be optionally be empty.  
54#[derive(Reflect, Debug, Clone, Default)]
55#[reflect(hide_all)]
56pub struct LoadError(pub Option<Arc<dyn ResourceLoadError>>);
57
58impl LoadError {
59    /// Creates new loading error from a value of the given type.
60    pub fn new<T: ResourceLoadError>(value: T) -> Self {
61        Self(Some(Arc::new(value)))
62    }
63}
64
65/// Resource could be in three possible states:
66/// 1. Pending - it is loading.
67/// 2. LoadError - an error has occurred during the load.
68/// 3. Ok - resource is fully loaded and ready to use.
69///
70/// Why it is so complex?
71/// Short answer: asynchronous loading.
72/// Long answer: when you loading a scene you expect it to be loaded as fast as
73/// possible, use all available power of the CPU. To achieve that each resource
74/// ideally should be loaded on separate core of the CPU, but since this is
75/// asynchronous, we must have the ability to track the state of the resource.
76#[derive(Debug, Reflect)]
77pub enum ResourceState {
78    /// Resource is loading from external resource or in the queue to load.
79    Pending {
80        /// List of wakers to wake future when resource is fully loaded.
81        wakers: WakersList,
82    },
83    /// An error has occurred during the load.
84    LoadError {
85        /// An error. This wrapped in Option only to be Default_ed.
86        error: LoadError,
87    },
88    /// Actual resource data when it is fully loaded.
89    Ok(Box<dyn ResourceData>),
90}
91
92impl Default for ResourceState {
93    fn default() -> Self {
94        Self::LoadError {
95            error: Default::default(),
96        }
97    }
98}
99
100impl Drop for ResourceState {
101    fn drop(&mut self) {
102        if let ResourceState::Pending { wakers, .. } = self {
103            assert_eq!(wakers.len(), 0);
104        }
105    }
106}
107
108impl Visit for ResourceState {
109    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
110        if visitor.is_reading() {
111            let mut type_uuid = Uuid::default();
112            type_uuid.visit("TypeUuid", visitor)?;
113
114            let resource_manager = visitor.blackboard.get::<ResourceManager>().expect(
115                "Resource data constructor container must be \
116                provided when serializing resources!",
117            );
118            let resource_manager_state = resource_manager.state();
119
120            if let Some(mut instance) = resource_manager_state
121                .constructors_container
122                .try_create(&type_uuid)
123            {
124                drop(resource_manager_state);
125                instance.visit(name, visitor)?;
126                *self = Self::Ok(instance);
127                Ok(())
128            } else {
129                Err(VisitError::User(format!(
130                    "There's no constructor registered for type {type_uuid}!"
131                )))
132            }
133        } else if let Self::Ok(instance) = self {
134            instance.visit(name, visitor)?;
135            Ok(())
136        } else {
137            // Do not save other variants, because they're needed only for runtime purposes.
138            Ok(())
139        }
140    }
141}
142
143impl ResourceState {
144    /// Creates new resource in pending state.
145    #[inline]
146    pub fn new_pending() -> Self {
147        Self::Pending {
148            wakers: Default::default(),
149        }
150    }
151
152    /// Creates new resource in error state.
153    #[inline]
154    pub fn new_load_error(error: LoadError) -> Self {
155        Self::LoadError { error }
156    }
157
158    /// Creates new resource in ok (resource with data) state.
159    #[inline]
160    pub fn new_ok<T: ResourceData>(data: T) -> Self {
161        Self::Ok(Box::new(data))
162    }
163
164    /// Checks whether the resource is still loading or not.
165    pub fn is_loading(&self) -> bool {
166        matches!(self, ResourceState::Pending { .. })
167    }
168
169    /// Switches the internal state of the resource to [`ResourceState::Pending`].
170    pub fn switch_to_pending_state(&mut self) {
171        *self = ResourceState::Pending {
172            wakers: Default::default(),
173        };
174    }
175
176    /// Changes ResourceState::Pending state to ResourceState::Ok(data) with given `data`.
177    /// Additionally it wakes all futures.
178    #[inline]
179    pub fn commit(&mut self, state: ResourceState) {
180        assert!(!matches!(state, ResourceState::Pending { .. }));
181
182        let wakers = if let ResourceState::Pending { ref mut wakers } = self {
183            std::mem::take(wakers)
184        } else {
185            unreachable!()
186        };
187
188        *self = state;
189
190        for waker in wakers.0 {
191            waker.wake();
192        }
193    }
194
195    /// Changes internal state to [`ResourceState::Ok`]
196    pub fn commit_ok<T: ResourceData>(&mut self, data: T) {
197        self.commit(ResourceState::Ok(Box::new(data)))
198    }
199
200    /// Changes internal state to [`ResourceState::LoadError`].
201    pub fn commit_error<E: ResourceLoadError>(&mut self, error: E) {
202        self.commit(ResourceState::LoadError {
203            error: LoadError::new(error),
204        })
205    }
206}
207
208#[cfg(test)]
209mod test {
210    use fyrox_core::{
211        reflect::{FieldInfo, Reflect},
212        TypeUuidProvider,
213    };
214    use std::error::Error;
215    use std::path::Path;
216
217    use super::*;
218
219    #[derive(Debug, Default, Reflect, Visit)]
220    struct Stub {}
221
222    impl ResourceData for Stub {
223        fn type_uuid(&self) -> Uuid {
224            Uuid::default()
225        }
226
227        fn save(&mut self, _path: &Path) -> Result<(), Box<dyn Error>> {
228            Err("Saving is not supported!".to_string().into())
229        }
230
231        fn can_be_saved(&self) -> bool {
232            false
233        }
234    }
235
236    impl TypeUuidProvider for Stub {
237        fn type_uuid() -> Uuid {
238            Uuid::default()
239        }
240    }
241
242    #[test]
243    fn resource_state_new_pending() {
244        let state = ResourceState::new_pending();
245
246        assert!(matches!(state, ResourceState::Pending { wakers: _ }));
247        assert!(state.is_loading());
248    }
249
250    #[test]
251    fn resource_state_new_load_error() {
252        let state = ResourceState::new_load_error(Default::default());
253
254        assert!(matches!(state, ResourceState::LoadError { error: _ }));
255        assert!(!state.is_loading());
256    }
257
258    #[test]
259    fn resource_state_new_ok() {
260        let state = ResourceState::new_ok(Stub {});
261        assert!(matches!(state, ResourceState::Ok(_)));
262        assert!(!state.is_loading());
263    }
264
265    #[test]
266    fn resource_state_switch_to_pending_state() {
267        // from Ok
268        let mut state = ResourceState::new_ok(Stub {});
269        state.switch_to_pending_state();
270
271        assert!(matches!(state, ResourceState::Pending { wakers: _ }));
272
273        // from LoadError
274        let mut state = ResourceState::new_load_error(Default::default());
275        state.switch_to_pending_state();
276
277        assert!(matches!(state, ResourceState::Pending { wakers: _ }));
278
279        // from Pending
280        let mut state = ResourceState::new_pending();
281        state.switch_to_pending_state();
282
283        assert!(matches!(state, ResourceState::Pending { wakers: _ }));
284    }
285
286    #[test]
287    fn visit_for_resource_state() {
288        // Visit Pending
289        let mut state = ResourceState::new_pending();
290        let mut visitor = Visitor::default();
291
292        assert!(state.visit("name", &mut visitor).is_ok());
293
294        // Visit LoadError
295        let mut state = ResourceState::new_load_error(Default::default());
296        let mut visitor = Visitor::default();
297
298        assert!(state.visit("name", &mut visitor).is_ok());
299
300        // Visit Ok
301        let mut state = ResourceState::new_ok(Stub {});
302        let mut visitor = Visitor::default();
303
304        assert!(state.visit("name", &mut visitor).is_ok());
305    }
306}