trueno_graph/gpu/
cache.rs1use std::collections::{HashMap, VecDeque};
7use wgpu::Buffer;
8
9pub type TileId = usize;
11
12pub struct LruTileCache {
17 capacity: usize,
19
20 buffers: HashMap<TileId, Buffer>,
22
23 access_order: VecDeque<TileId>,
25}
26
27impl LruTileCache {
28 #[must_use]
30 pub fn new(capacity: usize) -> Self {
31 Self { capacity: capacity.max(1), buffers: HashMap::new(), access_order: VecDeque::new() }
32 }
33
34 pub fn get(&mut self, tile_id: TileId) -> Option<&Buffer> {
36 if self.buffers.contains_key(&tile_id) {
37 self.access_order.retain(|&id| id != tile_id);
39 self.access_order.push_front(tile_id);
40 self.buffers.get(&tile_id)
41 } else {
42 None
43 }
44 }
45
46 pub fn insert(&mut self, tile_id: TileId, buffer: Buffer) -> Option<TileId> {
50 let mut evicted = None;
51
52 if self.buffers.contains_key(&tile_id) {
54 self.access_order.retain(|&id| id != tile_id);
55 self.access_order.push_front(tile_id);
56 self.buffers.insert(tile_id, buffer);
57 return None;
58 }
59
60 if self.buffers.len() >= self.capacity {
62 if let Some(lru_id) = self.access_order.pop_back() {
63 self.buffers.remove(&lru_id);
64 evicted = Some(lru_id);
65 }
66 }
67
68 self.buffers.insert(tile_id, buffer);
70 self.access_order.push_front(tile_id);
71
72 evicted
73 }
74
75 #[must_use]
77 pub fn contains(&self, tile_id: TileId) -> bool {
78 self.buffers.contains_key(&tile_id)
79 }
80
81 #[must_use]
83 pub fn len(&self) -> usize {
84 self.buffers.len()
85 }
86
87 #[must_use]
89 pub fn is_empty(&self) -> bool {
90 self.buffers.is_empty()
91 }
92
93 pub fn clear(&mut self) {
95 self.buffers.clear();
96 self.access_order.clear();
97 }
98
99 #[must_use]
101 pub fn capacity(&self) -> usize {
102 self.capacity
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::gpu::GpuDevice;
110
111 #[tokio::test]
112 async fn test_lru_cache_basic() {
113 if !GpuDevice::is_gpu_available().await {
114 eprintln!("⚠️ Skipping test_lru_cache_basic: GPU not available");
115 return;
116 }
117
118 let device = GpuDevice::new().await.unwrap();
119 let mut cache = LruTileCache::new(3);
120
121 let buf1 = device.create_buffer("tile_1", 1024, wgpu::BufferUsages::STORAGE).unwrap();
123 let buf2 = device.create_buffer("tile_2", 1024, wgpu::BufferUsages::STORAGE).unwrap();
124 let buf3 = device.create_buffer("tile_3", 1024, wgpu::BufferUsages::STORAGE).unwrap();
125 let buf4 = device.create_buffer("tile_4", 1024, wgpu::BufferUsages::STORAGE).unwrap();
126
127 assert_eq!(cache.insert(1, buf1), None);
129 assert_eq!(cache.insert(2, buf2), None);
130 assert_eq!(cache.insert(3, buf3), None);
131 assert_eq!(cache.len(), 3);
132
133 assert!(cache.get(1).is_some());
135
136 assert_eq!(cache.insert(4, buf4), Some(2));
138 assert_eq!(cache.len(), 3);
139 assert!(cache.contains(1));
140 assert!(!cache.contains(2)); assert!(cache.contains(3));
142 assert!(cache.contains(4));
143 }
144
145 #[test]
146 fn test_lru_cache_capacity() {
147 let cache = LruTileCache::new(5);
148 assert_eq!(cache.capacity(), 5);
149 assert_eq!(cache.len(), 0);
150 assert!(cache.is_empty());
151 }
152
153 #[tokio::test]
154 async fn test_lru_cache_reinsertion() {
155 if !GpuDevice::is_gpu_available().await {
156 eprintln!("⚠️ Skipping test_lru_cache_reinsertion: GPU not available");
157 return;
158 }
159
160 let device = GpuDevice::new().await.unwrap();
161 let mut cache = LruTileCache::new(2);
162
163 let buf1 = device.create_buffer("tile_1", 1024, wgpu::BufferUsages::STORAGE).unwrap();
164 let buf1_new =
165 device.create_buffer("tile_1_new", 1024, wgpu::BufferUsages::STORAGE).unwrap();
166
167 assert_eq!(cache.insert(1, buf1), None);
169 assert_eq!(cache.len(), 1);
170
171 assert_eq!(cache.insert(1, buf1_new), None);
173 assert_eq!(cache.len(), 1);
174 assert!(cache.contains(1));
175 }
176
177 #[tokio::test]
178 async fn test_lru_cache_clear() {
179 if !GpuDevice::is_gpu_available().await {
180 eprintln!("⚠️ Skipping test_lru_cache_clear: GPU not available");
181 return;
182 }
183
184 let device = GpuDevice::new().await.unwrap();
185 let mut cache = LruTileCache::new(3);
186
187 let buf1 = device.create_buffer("tile_1", 1024, wgpu::BufferUsages::STORAGE).unwrap();
188 let buf2 = device.create_buffer("tile_2", 1024, wgpu::BufferUsages::STORAGE).unwrap();
189
190 cache.insert(1, buf1);
191 cache.insert(2, buf2);
192 assert_eq!(cache.len(), 2);
193
194 cache.clear();
195 assert_eq!(cache.len(), 0);
196 assert!(cache.is_empty());
197 assert!(!cache.contains(1));
198 assert!(!cache.contains(2));
199 }
200
201 #[tokio::test]
202 async fn test_lru_cache_get_nonexistent() {
203 if !GpuDevice::is_gpu_available().await {
204 eprintln!("⚠️ Skipping test_lru_cache_get_nonexistent: GPU not available");
205 return;
206 }
207
208 let mut cache = LruTileCache::new(3);
209 assert!(cache.get(999).is_none());
210 }
211
212 #[test]
213 fn test_lru_cache_zero_capacity() {
214 let cache = LruTileCache::new(0);
215 assert_eq!(cache.capacity(), 1); }
217}