use super::CompiledShader;
use blake3::Hash;
use lru::LruCache;
use parking_lot::Mutex;
use std::num::NonZeroUsize;
const DEFAULT_CACHE_CAPACITY: NonZeroUsize = match NonZeroUsize::new(1000) {
Some(v) => v,
None => unreachable!(),
};
pub struct ShaderCache {
cache: Mutex<LruCache<Hash, CompiledShader>>,
hits: Mutex<u64>,
misses: Mutex<u64>,
}
impl ShaderCache {
pub fn new(capacity: usize) -> Self {
let cap = NonZeroUsize::new(capacity).unwrap_or(DEFAULT_CACHE_CAPACITY);
Self {
cache: Mutex::new(LruCache::new(cap)),
hits: Mutex::new(0),
misses: Mutex::new(0),
}
}
pub fn insert(&self, hash: Hash, shader: CompiledShader) {
let mut cache = self.cache.lock();
cache.put(hash, shader);
}
pub fn get(&self, hash: &Hash) -> Option<CompiledShader> {
let mut cache = self.cache.lock();
if let Some(shader) = cache.get(hash) {
*self.hits.lock() += 1;
Some(shader.clone())
} else {
*self.misses.lock() += 1;
None
}
}
pub fn contains(&self, hash: &Hash) -> bool {
let cache = self.cache.lock();
cache.contains(hash)
}
pub fn clear(&self) {
let mut cache = self.cache.lock();
cache.clear();
}
pub fn len(&self) -> usize {
let cache = self.cache.lock();
cache.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get_stats(&self) -> CacheStats {
let hits = *self.hits.lock();
let misses = *self.misses.lock();
let size = self.len();
CacheStats {
hits,
misses,
size,
hit_rate: if hits + misses > 0 {
(hits as f64) / ((hits + misses) as f64)
} else {
0.0
},
}
}
pub fn reset_stats(&self) {
*self.hits.lock() = 0;
*self.misses.lock() = 0;
}
}
#[derive(Debug, Clone)]
pub struct CacheStats {
pub hits: u64,
pub misses: u64,
pub size: usize,
pub hit_rate: f64,
}
impl CacheStats {
pub fn print(&self) {
println!("\nShader Cache Statistics:");
println!(" Hits: {}", self.hits);
println!(" Misses: {}", self.misses);
println!(" Hit rate: {:.1}%", self.hit_rate * 100.0);
println!(" Cache size: {}", self.size);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_creation() {
let cache = ShaderCache::new(100);
assert_eq!(cache.len(), 0);
assert!(cache.is_empty());
}
#[test]
fn test_cache_stats() {
let cache = ShaderCache::new(100);
let stats = cache.get_stats();
assert_eq!(stats.hits, 0);
assert_eq!(stats.misses, 0);
assert_eq!(stats.hit_rate, 0.0);
}
}