Skip to main content

oxigdal_gpu_advanced/shader_compiler/
cache.rs

1//! Shader compilation cache with LRU eviction.
2
3use super::CompiledShader;
4use blake3::Hash;
5use lru::LruCache;
6use parking_lot::Mutex;
7use std::num::NonZeroUsize;
8
9/// Default cache capacity (1000 shaders)
10const DEFAULT_CACHE_CAPACITY: NonZeroUsize = match NonZeroUsize::new(1000) {
11    Some(v) => v,
12    None => unreachable!(),
13};
14
15/// Shader cache with LRU eviction policy
16pub struct ShaderCache {
17    /// LRU cache for compiled shaders
18    cache: Mutex<LruCache<Hash, CompiledShader>>,
19    /// Cache hits
20    hits: Mutex<u64>,
21    /// Cache misses
22    misses: Mutex<u64>,
23}
24
25impl ShaderCache {
26    /// Create a new shader cache with given capacity
27    pub fn new(capacity: usize) -> Self {
28        let cap = NonZeroUsize::new(capacity).unwrap_or(DEFAULT_CACHE_CAPACITY);
29        Self {
30            cache: Mutex::new(LruCache::new(cap)),
31            hits: Mutex::new(0),
32            misses: Mutex::new(0),
33        }
34    }
35
36    /// Insert a compiled shader into cache
37    pub fn insert(&self, hash: Hash, shader: CompiledShader) {
38        let mut cache = self.cache.lock();
39        cache.put(hash, shader);
40    }
41
42    /// Get a compiled shader from cache
43    pub fn get(&self, hash: &Hash) -> Option<CompiledShader> {
44        let mut cache = self.cache.lock();
45        if let Some(shader) = cache.get(hash) {
46            *self.hits.lock() += 1;
47            Some(shader.clone())
48        } else {
49            *self.misses.lock() += 1;
50            None
51        }
52    }
53
54    /// Check if cache contains a shader
55    pub fn contains(&self, hash: &Hash) -> bool {
56        let cache = self.cache.lock();
57        cache.contains(hash)
58    }
59
60    /// Clear the cache
61    pub fn clear(&self) {
62        let mut cache = self.cache.lock();
63        cache.clear();
64    }
65
66    /// Get cache size
67    pub fn len(&self) -> usize {
68        let cache = self.cache.lock();
69        cache.len()
70    }
71
72    /// Check if cache is empty
73    pub fn is_empty(&self) -> bool {
74        self.len() == 0
75    }
76
77    /// Get cache statistics
78    pub fn get_stats(&self) -> CacheStats {
79        let hits = *self.hits.lock();
80        let misses = *self.misses.lock();
81        let size = self.len();
82
83        CacheStats {
84            hits,
85            misses,
86            size,
87            hit_rate: if hits + misses > 0 {
88                (hits as f64) / ((hits + misses) as f64)
89            } else {
90                0.0
91            },
92        }
93    }
94
95    /// Reset statistics
96    pub fn reset_stats(&self) {
97        *self.hits.lock() = 0;
98        *self.misses.lock() = 0;
99    }
100}
101
102/// Cache statistics
103#[derive(Debug, Clone)]
104pub struct CacheStats {
105    /// Cache hits
106    pub hits: u64,
107    /// Cache misses
108    pub misses: u64,
109    /// Current cache size
110    pub size: usize,
111    /// Hit rate (0.0 to 1.0)
112    pub hit_rate: f64,
113}
114
115impl CacheStats {
116    /// Print statistics
117    pub fn print(&self) {
118        println!("\nShader Cache Statistics:");
119        println!("  Hits: {}", self.hits);
120        println!("  Misses: {}", self.misses);
121        println!("  Hit rate: {:.1}%", self.hit_rate * 100.0);
122        println!("  Cache size: {}", self.size);
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn test_cache_creation() {
132        let cache = ShaderCache::new(100);
133        assert_eq!(cache.len(), 0);
134        assert!(cache.is_empty());
135    }
136
137    #[test]
138    fn test_cache_stats() {
139        let cache = ShaderCache::new(100);
140        let stats = cache.get_stats();
141        assert_eq!(stats.hits, 0);
142        assert_eq!(stats.misses, 0);
143        assert_eq!(stats.hit_rate, 0.0);
144    }
145}