Skip to main content

motion_core/asset/
store.rs

1//! Project-local decoded resource accounting and identity.
2
3use super::*;
4
5/// Byte and entry allowance for a [`ResourceStore`].
6///
7/// This is independent of the compute-cache [`crate::CacheLimits`]: a store
8/// retains decoded resources, while caches retain recomputable values.
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub struct ResourceLimits {
11    /// Logical decoded payload allowance; must be positive.
12    pub max_bytes: usize,
13    /// Cached-entry allowance, in `1..=4096`.
14    pub max_entries: usize,
15}
16
17impl Default for ResourceLimits {
18    fn default() -> Self {
19        Self {
20            max_bytes: MAX_STORE_BYTES,
21            max_entries: MAX_ENTRIES,
22        }
23    }
24}
25
26/// Explicit project resource store and logical allowance; clones share identity and accounting.
27/// Each new Assets owner starts isolated unless with_store supplies a shared store.
28/// Decoded Arc values can outlive all owners/handles and are not then store-accounted.
29///
30/// It holds decoded resources that are loaded on demand, unlike the compute caches
31/// ([`crate::SceneCache`], [`crate::RenderCache`]) that retain recomputable values.
32#[derive(Clone, Default)]
33pub struct ResourceStore(pub(super) Arc<Store>);
34
35pub(super) struct Store {
36    pub(super) bytes: AtomicUsize,
37    pub(super) entries: Mutex<BTreeMap<CacheKey, Weak<Erased>>>,
38    pub(super) limits: ResourceLimits,
39}
40
41impl Default for Store {
42    fn default() -> Self {
43        Self {
44            bytes: AtomicUsize::new(0),
45            entries: Mutex::new(BTreeMap::new()),
46            limits: ResourceLimits::default(),
47        }
48    }
49}
50
51impl ResourceStore {
52    /// Creates an isolated 256 MiB / 4096-entry logical project store.
53    pub fn new() -> Self {
54        Self::default()
55    }
56
57    /// Uses a positive payload allowance and 1..=4096 cached entries.
58    pub fn with_limits(limits: ResourceLimits) -> crate::Result<Self> {
59        if limits.max_bytes == 0 || !(1..=MAX_ENTRIES).contains(&limits.max_entries) {
60            return Err(io::Error::new(
61                io::ErrorKind::InvalidInput,
62                "Resource limits require positive bytes and 1..=4096 entries",
63            )
64            .into());
65        }
66        Ok(Self(Arc::new(Store {
67            limits,
68            ..Store::default()
69        })))
70    }
71
72    /// Configured logical payload allowance.
73    pub fn max_bytes(&self) -> usize {
74        self.0.limits.max_bytes
75    }
76
77    /// Configured cached-entry allowance.
78    pub fn max_entries(&self) -> usize {
79        self.0.limits.max_entries
80    }
81
82    /// Logical payload retained by this project, not process RSS.
83    pub fn retained_bytes(&self) -> usize {
84        self.0.bytes.load(Ordering::Acquire)
85    }
86
87    /// Whether two owners charge the same project store.
88    pub fn same_store(&self, other: &Self) -> bool {
89        Arc::ptr_eq(&self.0, &other.0)
90    }
91}