egui/cache/
frame_cache.rs

1use super::CacheTrait;
2
3/// Something that does an expensive computation that we want to cache
4/// to save us from recomputing it each frame.
5pub trait ComputerMut<Key, Value>: 'static + Send + Sync {
6    fn compute(&mut self, key: Key) -> Value;
7}
8
9/// Caches the results of a computation for one frame.
10/// If it is still used next frame, it is not recomputed.
11/// If it is not used next frame, it is evicted from the cache to save memory.
12pub struct FrameCache<Value, Computer> {
13    generation: u32,
14    computer: Computer,
15    cache: nohash_hasher::IntMap<u64, (u32, Value)>,
16}
17
18impl<Value, Computer> Default for FrameCache<Value, Computer>
19where
20    Computer: Default,
21{
22    fn default() -> Self {
23        Self::new(Computer::default())
24    }
25}
26
27impl<Value, Computer> FrameCache<Value, Computer> {
28    pub fn new(computer: Computer) -> Self {
29        Self {
30            generation: 0,
31            computer,
32            cache: Default::default(),
33        }
34    }
35
36    /// Must be called once per frame to clear the cache.
37    pub fn evict_cache(&mut self) {
38        let current_generation = self.generation;
39        self.cache.retain(|_key, cached| {
40            cached.0 == current_generation // only keep those that were used this frame
41        });
42        self.generation = self.generation.wrapping_add(1);
43    }
44}
45
46impl<Value, Computer> FrameCache<Value, Computer> {
47    /// Get from cache (if the same key was used last frame)
48    /// or recompute and store in the cache.
49    pub fn get<Key>(&mut self, key: Key) -> &Value
50    where
51        Key: Copy + std::hash::Hash,
52        Computer: ComputerMut<Key, Value>,
53    {
54        let hash = crate::util::hash(key);
55
56        match self.cache.entry(hash) {
57            std::collections::hash_map::Entry::Occupied(entry) => {
58                let cached = entry.into_mut();
59                cached.0 = self.generation;
60                &cached.1
61            }
62            std::collections::hash_map::Entry::Vacant(entry) => {
63                let value = self.computer.compute(key);
64                let inserted = entry.insert((self.generation, value));
65                &inserted.1
66            }
67        }
68    }
69}
70
71impl<Value: 'static + Send + Sync, Computer: 'static + Send + Sync> CacheTrait
72    for FrameCache<Value, Computer>
73{
74    fn update(&mut self) {
75        self.evict_cache();
76    }
77
78    fn len(&self) -> usize {
79        self.cache.len()
80    }
81}