appcore_render/
resources.rs1use crate::{RenderError, RenderLimits};
12use std::sync::atomic::{AtomicU64, Ordering};
13static NEXT_OWNER: AtomicU64 = AtomicU64::new(1);
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct ResourceId {
18 owner: u64,
19 index: u32,
20 generation: u64,
21}
22struct Slot<T> {
23 generation: u64,
24 value: Option<T>,
25 bytes: u64,
26}
27pub struct ResourceArena<T> {
30 owner: u64,
31 limits: RenderLimits,
32 slots: Vec<Slot<T>>,
33 bytes: u64,
34}
35impl<T> ResourceArena<T> {
36 pub fn new(limits: RenderLimits) -> Result<Self, RenderError> {
38 let limits = limits.validate()?;
39 let owner = NEXT_OWNER
40 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |n| n.checked_add(1))
41 .map_err(|_| RenderError::IdentityExhausted)?;
42 Ok(Self {
43 owner,
44 limits,
45 slots: Vec::new(),
46 bytes: 0,
47 })
48 }
49 pub fn insert(&mut self, value: T, bytes: u64) -> Result<ResourceId, RenderError> {
51 let total = self
52 .bytes
53 .checked_add(bytes)
54 .ok_or(RenderError::BudgetExceeded)?;
55 if total > self.limits.managed_bytes {
56 return Err(RenderError::BudgetExceeded);
57 }
58 let index = if let Some(i) = self
59 .slots
60 .iter()
61 .position(|s| s.value.is_none() && s.generation < u64::MAX)
62 {
63 i
64 } else {
65 if self.slots.len() >= self.limits.resource_slots as usize {
66 return Err(RenderError::BudgetExceeded);
67 }
68 self.slots.push(Slot {
69 generation: 0,
70 value: None,
71 bytes: 0,
72 });
73 self.slots.len() - 1
74 };
75 let slot = &mut self.slots[index];
76 slot.generation += 1;
77 slot.value = Some(value);
78 slot.bytes = bytes;
79 self.bytes = total;
80 Ok(ResourceId {
81 owner: self.owner,
82 index: index as u32,
83 generation: slot.generation,
84 })
85 }
86 fn slot(&self, id: ResourceId) -> Result<&Slot<T>, RenderError> {
87 if id.owner != self.owner {
88 return Err(RenderError::InvalidResource);
89 }
90 self.slots
91 .get(id.index as usize)
92 .filter(|s| s.generation == id.generation && s.value.is_some())
93 .ok_or(RenderError::InvalidResource)
94 }
95 pub fn get(&self, id: ResourceId) -> Result<&T, RenderError> {
97 self.slot(id)?
98 .value
99 .as_ref()
100 .ok_or(RenderError::InvalidResource)
101 }
102 pub fn remove(&mut self, id: ResourceId) -> Result<T, RenderError> {
104 self.slot(id)?;
105 let slot = &mut self.slots[id.index as usize];
106 let value = slot.value.take().ok_or(RenderError::InvalidResource)?;
107 self.bytes -= slot.bytes;
108 slot.bytes = 0;
109 Ok(value)
110 }
111 pub fn managed_bytes(&self) -> u64 {
113 self.bytes
114 }
115}