egui/util/
cache.rs

1//! Computing the same thing each frame can be expensive,
2//! so often you want to save the result from the previous frame and reuse it.
3//!
4//! Enter [`FrameCache`]: it caches the results of a computation for one frame.
5//! If it is still used next frame, it is not recomputed.
6//! If it is not used next frame, it is evicted from the cache to save memory.
7
8/// Something that does an expensive computation that we want to cache
9/// to save us from recomputing it each frame.
10pub trait ComputerMut<Key, Value>: 'static + Send + Sync {
11    fn compute(&mut self, key: Key) -> Value;
12}
13
14/// Caches the results of a computation for one frame.
15/// If it is still used next frame, it is not recomputed.
16/// If it is not used next frame, it is evicted from the cache to save memory.
17pub struct FrameCache<Value, Computer> {
18    generation: u32,
19    computer: Computer,
20    cache: nohash_hasher::IntMap<u64, (u32, Value)>,
21}
22
23impl<Value, Computer> Default for FrameCache<Value, Computer>
24where
25    Computer: Default,
26{
27    fn default() -> Self {
28        Self::new(Computer::default())
29    }
30}
31
32impl<Value, Computer> FrameCache<Value, Computer> {
33    pub fn new(computer: Computer) -> Self {
34        Self {
35            generation: 0,
36            computer,
37            cache: Default::default(),
38        }
39    }
40
41    /// Must be called once per frame to clear the cache.
42    pub fn evice_cache(&mut self) {
43        let current_generation = self.generation;
44        self.cache.retain(|_key, cached| {
45            cached.0 == current_generation // only keep those that were used this frame
46        });
47        self.generation = self.generation.wrapping_add(1);
48    }
49}
50
51impl<Value, Computer> FrameCache<Value, Computer> {
52    /// Get from cache (if the same key was used last frame)
53    /// or recompute and store in the cache.
54    pub fn get<Key>(&mut self, key: Key) -> Value
55    where
56        Key: Copy + std::hash::Hash,
57        Value: Clone,
58        Computer: ComputerMut<Key, Value>,
59    {
60        let hash = crate::util::hash(key);
61
62        match self.cache.entry(hash) {
63            std::collections::hash_map::Entry::Occupied(entry) => {
64                let cached = entry.into_mut();
65                cached.0 = self.generation;
66                cached.1.clone()
67            }
68            std::collections::hash_map::Entry::Vacant(entry) => {
69                let value = self.computer.compute(key);
70                entry.insert((self.generation, value.clone()));
71                value
72            }
73        }
74    }
75}
76
77#[allow(clippy::len_without_is_empty)]
78pub trait CacheTrait: 'static + Send + Sync {
79    /// Call once per frame to evict cache.
80    fn update(&mut self);
81
82    /// Number of values currently in the cache.
83    fn len(&self) -> usize;
84
85    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
86}
87
88impl<Value: 'static + Send + Sync, Computer: 'static + Send + Sync> CacheTrait
89    for FrameCache<Value, Computer>
90{
91    fn update(&mut self) {
92        self.evice_cache();
93    }
94
95    fn len(&self) -> usize {
96        self.cache.len()
97    }
98
99    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
100        self
101    }
102}
103
104/// ```
105/// use egui::util::cache::{CacheStorage, ComputerMut, FrameCache};
106///
107/// #[derive(Default)]
108/// struct CharCounter {}
109/// impl ComputerMut<&str, usize> for CharCounter {
110///     fn compute(&mut self, s: &str) -> usize {
111///         s.chars().count()
112///     }
113/// }
114/// type CharCountCache<'a> = FrameCache<usize, CharCounter>;
115///
116/// # let mut cache_storage = CacheStorage::default();
117/// let mut cache = cache_storage.cache::<CharCountCache<'_>>();
118/// assert_eq!(cache.get("hello"), 5);
119/// ```
120#[derive(Default)]
121pub struct CacheStorage {
122    caches: ahash::HashMap<std::any::TypeId, Box<dyn CacheTrait>>,
123}
124
125impl CacheStorage {
126    pub fn cache<FrameCache: CacheTrait + Default>(&mut self) -> &mut FrameCache {
127        self.caches
128            .entry(std::any::TypeId::of::<FrameCache>())
129            .or_insert_with(|| Box::<FrameCache>::default())
130            .as_any_mut()
131            .downcast_mut::<FrameCache>()
132            .unwrap()
133    }
134
135    /// Total number of cached values
136    fn num_values(&self) -> usize {
137        self.caches.values().map(|cache| cache.len()).sum()
138    }
139
140    /// Call once per frame to evict cache.
141    pub fn update(&mut self) {
142        for cache in self.caches.values_mut() {
143            cache.update();
144        }
145    }
146}
147
148impl Clone for CacheStorage {
149    fn clone(&self) -> Self {
150        // We return an empty cache that can be filled in again.
151        Self::default()
152    }
153}
154
155impl std::fmt::Debug for CacheStorage {
156    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157        write!(
158            f,
159            "FrameCacheStorage[{} caches with {} elements]",
160            self.caches.len(),
161            self.num_values()
162        )
163    }
164}