1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use std::any::{Any, TypeId};

use ahash::HashMap;
use parking_lot::Mutex;
use re_chunk_store::ChunkStoreEvent;

/// Does memoization of different objects for the immediate mode UI.
#[derive(Default)]
pub struct Caches(Mutex<HashMap<TypeId, Box<dyn Cache>>>);

impl Caches {
    /// Call once per frame to potentially flush the cache(s).
    ///
    /// `renderer_active_frame_idx`: The global frame index as reported by [`re_renderer::RenderContext::active_frame_idx`].
    pub fn begin_frame(&self, renderer_active_frame_idx: u64) {
        re_tracing::profile_function!();
        for cache in self.0.lock().values_mut() {
            cache.begin_frame(renderer_active_frame_idx);
        }
    }

    /// Attempt to free up memory.
    pub fn purge_memory(&self) {
        re_tracing::profile_function!();
        for cache in self.0.lock().values_mut() {
            cache.purge_memory();
        }
    }

    /// React to the chunk store's changelog, if needed.
    ///
    /// Useful to e.g. invalidate unreachable data.
    pub fn on_store_events(&self, events: &[ChunkStoreEvent]) {
        re_tracing::profile_function!();

        for cache in self.0.lock().values_mut() {
            cache.on_store_events(events);
        }
    }

    /// Accesses a cache for reading and writing.
    ///
    /// Adds the cache lazily if it wasn't already there.
    pub fn entry<C: Cache + Default, R>(&self, f: impl FnOnce(&mut C) -> R) -> R {
        #[allow(clippy::unwrap_or_default)] // or_default doesn't work here.
        f(self
            .0
            .lock()
            .entry(TypeId::of::<C>())
            .or_insert(Box::<C>::default())
            .as_any_mut()
            .downcast_mut::<C>()
            .expect("Downcast failed, this indicates a bug in how `Caches` adds new cache types."))
    }
}

/// A cache for memoizing things in order to speed up immediate mode UI & other immediate mode style things.
pub trait Cache: std::any::Any + Send + Sync {
    /// Called once per frame to potentially flush the cache.
    ///
    /// `_renderer_active_frame_idx`: The global frame index as reported by [`re_renderer::RenderContext::active_frame_idx`].
    fn begin_frame(&mut self, _renderer_active_frame_idx: u64) {}

    /// Attempt to free up memory.
    fn purge_memory(&mut self);

    /// React to the chunk store's changelog, if needed.
    ///
    /// Useful to e.g. invalidate unreachable data.
    fn on_store_events(&mut self, events: &[ChunkStoreEvent]) {
        _ = events;
    }

    // TODO(andreas): Track bytes used for each cache and show in the memory panel!
    //fn bytes_used(&self) -> usize;

    /// Converts itself to a mutable reference of [`Any`], which enables mutable downcasting to concrete types.
    fn as_any_mut(&mut self) -> &mut dyn Any;
}