wgpu_conveyor/
cache.rs

1#![allow(clippy::eval_order_dependence)] // Tons of false positives
2
3use crate::{AutomatedBuffer, BeltBufferId, IdBuffer};
4use std::{hash::Hash, sync::Arc};
5use wgpu::*;
6
7/// Values that can be used with [`BindGroupCache`].
8///
9/// Implemented for &AutomatedBuffer and 1, 2, 3, and 4-tuples thereof.
10pub trait AutomatedBufferSet<'buf> {
11    /// Key type corresponding to this buffer type.
12    ///
13    /// [`BufferCache1`], [`BufferCache2`], [`BufferCache3`], and [`BufferCache4`]
14    /// are aliases for this value for the corresponding value, 2, 3, and 4-tuple.
15    type Key: Hash + Eq + Clone;
16    /// Underlying buffer type.
17    type Value;
18    /// Get the buffer values
19    fn get(self) -> Self::Value;
20    /// Translate a value into a key
21    fn value_to_key(value: &Self::Value) -> Self::Key;
22}
23
24impl<'buf> AutomatedBufferSet<'buf> for &'buf AutomatedBuffer {
25    type Key = BeltBufferId;
26    type Value = Arc<IdBuffer>;
27    fn get(self) -> Self::Value {
28        self.get_current_inner()
29    }
30
31    fn value_to_key(value: &Self::Value) -> Self::Key {
32        value.id
33    }
34}
35
36impl<'buf> AutomatedBufferSet<'buf> for (&'buf AutomatedBuffer,) {
37    type Key = BeltBufferId;
38    type Value = Arc<IdBuffer>;
39    fn get(self) -> Self::Value {
40        self.0.get_current_inner()
41    }
42
43    fn value_to_key(value: &Self::Value) -> Self::Key {
44        value.id
45    }
46}
47
48impl<'buf> AutomatedBufferSet<'buf> for (&'buf AutomatedBuffer, &'buf AutomatedBuffer) {
49    type Key = (BeltBufferId, BeltBufferId);
50    type Value = (Arc<IdBuffer>, Arc<IdBuffer>);
51    fn get(self) -> Self::Value {
52        (self.0.get_current_inner(), self.1.get_current_inner())
53    }
54
55    fn value_to_key(value: &Self::Value) -> Self::Key {
56        (value.0.id, value.1.id)
57    }
58}
59
60impl<'buf> AutomatedBufferSet<'buf> for (&'buf AutomatedBuffer, &'buf AutomatedBuffer, &'buf AutomatedBuffer) {
61    type Key = (BeltBufferId, BeltBufferId, BeltBufferId);
62    type Value = (Arc<IdBuffer>, Arc<IdBuffer>, Arc<IdBuffer>);
63    fn get(self) -> Self::Value {
64        (
65            self.0.get_current_inner(),
66            self.1.get_current_inner(),
67            self.2.get_current_inner(),
68        )
69    }
70
71    fn value_to_key(value: &Self::Value) -> Self::Key {
72        (value.0.id, value.1.id, value.2.id)
73    }
74}
75
76impl<'buf> AutomatedBufferSet<'buf>
77    for (
78        &'buf AutomatedBuffer,
79        &'buf AutomatedBuffer,
80        &'buf AutomatedBuffer,
81        &'buf AutomatedBuffer,
82    )
83{
84    type Key = (BeltBufferId, BeltBufferId, BeltBufferId, BeltBufferId);
85    type Value = (Arc<IdBuffer>, Arc<IdBuffer>, Arc<IdBuffer>, Arc<IdBuffer>);
86    fn get(self) -> Self::Value {
87        (
88            self.0.get_current_inner(),
89            self.1.get_current_inner(),
90            self.2.get_current_inner(),
91            self.3.get_current_inner(),
92        )
93    }
94
95    fn value_to_key(value: &Self::Value) -> Self::Key {
96        (value.0.id, value.1.id, value.2.id, value.3.id)
97    }
98}
99
100/// Key type for a single buffer.
101pub type BufferCache1 = BeltBufferId;
102/// Key type for two buffers.
103pub type BufferCache2 = (BeltBufferId, BeltBufferId);
104/// Key type for three buffers.
105pub type BufferCache3 = (BeltBufferId, BeltBufferId, BeltBufferId);
106/// Key type for four buffers.
107pub type BufferCache4 = (BeltBufferId, BeltBufferId, BeltBufferId, BeltBufferId);
108
109/// Bind group cache. Corresponds to a single bind group.
110///
111/// If you use multiple bind groups in a single cache, you will likely have cache
112/// misses constantly.
113pub struct BindGroupCache<Key: Hash + Eq + Clone> {
114    cache: lru::LruCache<Key, BindGroup>,
115}
116impl<Key: Hash + Eq + Clone> BindGroupCache<Key> {
117    /// Create a bind group cache with default size 4.
118    #[must_use]
119    pub fn new() -> Self {
120        Self::with_capacity(4)
121    }
122
123    /// Create a bind group cache with given size.
124    #[must_use]
125    pub fn with_capacity(size: usize) -> Self {
126        Self {
127            cache: lru::LruCache::new(size),
128        }
129    }
130
131    /// Empty cache to force repopulation
132    pub fn clear(&mut self) {
133        self.cache.clear();
134    }
135
136    /// Using the set of [`AutomatedBuffer`](AutomatedBuffer)s provided
137    /// in `buffers`, create or retrieve a bind group and return the key
138    /// to call `get` with.
139    ///
140    /// If a bind group is not found, `bind_group_fn` will be called with
141    /// the resolved [`BeltBufferId`](BeltBufferId) for the given buffers.
142    ///
143    /// This result will then be cached.
144    ///
145    /// `use_cache`, if false, will always call the function and overwrite
146    /// the value (if any) in the cache. This is useful if bind groups
147    /// are changing every invocation.
148    pub fn create_bind_group<'a, Set, BindGroupFn>(
149        &mut self,
150        buffers: Set,
151        use_cache: bool,
152        bind_group_fn: BindGroupFn,
153    ) -> Key
154    where
155        Set: AutomatedBufferSet<'a, Key = Key>,
156        BindGroupFn: FnOnce(&Set::Value) -> BindGroup,
157    {
158        let value = buffers.get();
159        let key = Set::value_to_key(&value);
160        if self.cache.contains(&key) && use_cache {
161            return key;
162        }
163        // Bumps LRU-ness
164        self.cache.put(key.clone(), bind_group_fn(&value));
165        key
166    }
167
168    pub fn get(&self, key: &Key) -> Option<&BindGroup> {
169        self.cache.peek(key)
170    }
171}
172
173impl<Key: Hash + Eq + Clone> Default for BindGroupCache<Key> {
174    fn default() -> Self {
175        Self::new()
176    }
177}