1//! Cache computations and efficiently reuse them.
2use std::cell::RefCell;
3use std::fmt;
4use std::mem;
5use std::sync::atomic::{self, AtomicU64};
67/// A simple cache that stores generated values to avoid recomputation.
8///
9/// Keeps track of the last generated value after clearing.
10pub struct Cache<T> {
11 group: Group,
12 state: RefCell<State<T>>,
13}
1415impl<T> Cache<T> {
16/// Creates a new empty [`Cache`].
17pub fn new() -> Self {
18 Cache {
19 group: Group::singleton(),
20 state: RefCell::new(State::Empty { previous: None }),
21 }
22 }
2324/// Creates a new empty [`Cache`] with the given [`Group`].
25 ///
26 /// Caches within the same group may reuse internal rendering storage.
27 ///
28 /// You should generally group caches that are likely to change
29 /// together.
30pub fn with_group(group: Group) -> Self {
31assert!(
32 !group.is_singleton(),
33"The group {group:?} cannot be shared!"
34);
3536 Cache {
37 group,
38 state: RefCell::new(State::Empty { previous: None }),
39 }
40 }
4142/// Returns the [`Group`] of the [`Cache`].
43pub fn group(&self) -> Group {
44self.group
45 }
4647/// Puts the given value in the [`Cache`].
48 ///
49 /// Notice that, given this is a cache, a mutable reference is not
50 /// necessary to call this method. You can safely update the cache in
51 /// rendering code.
52pub fn put(&self, value: T) {
53*self.state.borrow_mut() = State::Filled { current: value };
54 }
5556/// Returns a reference cell to the internal [`State`] of the [`Cache`].
57pub fn state(&self) -> &RefCell<State<T>> {
58&self.state
59 }
6061/// Clears the [`Cache`].
62pub fn clear(&self) {
63let mut state = self.state.borrow_mut();
6465let previous =
66 mem::replace(&mut *state, State::Empty { previous: None });
6768let previous = match previous {
69 State::Empty { previous } => previous,
70 State::Filled { current } => Some(current),
71 };
7273*state = State::Empty { previous };
74 }
75}
7677/// A cache group.
78///
79/// Caches that share the same group generally change together.
80///
81/// A cache group can be used to implement certain performance
82/// optimizations during rendering, like batching or sharing atlases.
83#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
84pub struct Group {
85 id: u64,
86 is_singleton: bool,
87}
8889impl Group {
90/// Generates a new unique cache [`Group`].
91pub fn unique() -> Self {
92static NEXT: AtomicU64 = AtomicU64::new(0);
9394Self {
95 id: NEXT.fetch_add(1, atomic::Ordering::Relaxed),
96 is_singleton: false,
97 }
98 }
99100/// Returns `true` if the [`Group`] can only ever have a
101 /// single [`Cache`] in it.
102 ///
103 /// This is the default kind of [`Group`] assigned when using
104 /// [`Cache::new`].
105 ///
106 /// Knowing that a [`Group`] will never be shared may be
107 /// useful for rendering backends to perform additional
108 /// optimizations.
109pub fn is_singleton(self) -> bool {
110self.is_singleton
111 }
112113fn singleton() -> Self {
114Self {
115 is_singleton: true,
116 ..Self::unique()
117 }
118 }
119}
120121impl<T> fmt::Debug for Cache<T>
122where
123T: fmt::Debug,
124{
125fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126use std::ops::Deref;
127128let state = self.state.borrow();
129130match state.deref() {
131 State::Empty { previous } => {
132write!(f, "Cache::Empty {{ previous: {previous:?} }}")
133 }
134 State::Filled { current } => {
135write!(f, "Cache::Filled {{ current: {current:?} }}")
136 }
137 }
138 }
139}
140141impl<T> Default for Cache<T> {
142fn default() -> Self {
143Self::new()
144 }
145}
146147/// The state of a [`Cache`].
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
149pub enum State<T> {
150/// The [`Cache`] is empty.
151Empty {
152/// The previous value of the [`Cache`].
153previous: Option<T>,
154 },
155/// The [`Cache`] is filled.
156Filled {
157/// The current value of the [`Cache`]
158current: T,
159 },
160}
161162/// A piece of data that can be cached.
163pub trait Cached: Sized {
164/// The type of cache produced.
165type Cache: Clone;
166167/// Loads the [`Cache`] into a proper instance.
168 ///
169 /// [`Cache`]: Self::Cache
170fn load(cache: &Self::Cache) -> Self;
171172/// Caches this value, producing its corresponding [`Cache`].
173 ///
174 /// [`Cache`]: Self::Cache
175fn cache(self, group: Group, previous: Option<Self::Cache>) -> Self::Cache;
176}
177178#[cfg(debug_assertions)]
179impl Cached for () {
180type Cache = ();
181182fn load(_cache: &Self::Cache) -> Self {}
183184fn cache(
185self,
186 _group: Group,
187 _previous: Option<Self::Cache>,
188 ) -> Self::Cache {
189 }
190}