1use astrelis_core::profiling::profile_function;
7use ahash::HashMap;
8use std::hash::{Hash, Hasher};
9use std::sync::{Arc, RwLock};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct SamplerKey {
16 pub address_mode_u: wgpu::AddressMode,
18 pub address_mode_v: wgpu::AddressMode,
20 pub address_mode_w: wgpu::AddressMode,
22 pub mag_filter: wgpu::FilterMode,
24 pub min_filter: wgpu::FilterMode,
26 pub mipmap_filter: wgpu::FilterMode,
28 pub lod_min_clamp: u32, pub lod_max_clamp: u32, pub compare: Option<wgpu::CompareFunction>,
34 pub anisotropy_clamp: u16,
36 pub border_color: Option<wgpu::SamplerBorderColor>,
38}
39
40impl Hash for SamplerKey {
41 fn hash<H: Hasher>(&self, state: &mut H) {
42 self.address_mode_u.hash(state);
43 self.address_mode_v.hash(state);
44 self.address_mode_w.hash(state);
45 self.mag_filter.hash(state);
46 self.min_filter.hash(state);
47 self.mipmap_filter.hash(state);
48 self.lod_min_clamp.hash(state);
49 self.lod_max_clamp.hash(state);
50 self.compare.hash(state);
51 self.anisotropy_clamp.hash(state);
52 self.border_color.hash(state);
53 }
54}
55
56impl SamplerKey {
57 pub fn nearest_repeat() -> Self {
59 Self {
60 address_mode_u: wgpu::AddressMode::Repeat,
61 address_mode_v: wgpu::AddressMode::Repeat,
62 address_mode_w: wgpu::AddressMode::Repeat,
63 mag_filter: wgpu::FilterMode::Nearest,
64 min_filter: wgpu::FilterMode::Nearest,
65 mipmap_filter: wgpu::FilterMode::Nearest,
66 lod_min_clamp: 0.0f32.to_bits(),
67 lod_max_clamp: f32::MAX.to_bits(),
68 compare: None,
69 anisotropy_clamp: 1,
70 border_color: None,
71 }
72 }
73
74 pub fn linear_mirror() -> Self {
76 Self {
77 address_mode_u: wgpu::AddressMode::MirrorRepeat,
78 address_mode_v: wgpu::AddressMode::MirrorRepeat,
79 address_mode_w: wgpu::AddressMode::MirrorRepeat,
80 mag_filter: wgpu::FilterMode::Linear,
81 min_filter: wgpu::FilterMode::Linear,
82 mipmap_filter: wgpu::FilterMode::Linear,
83 lod_min_clamp: 0.0f32.to_bits(),
84 lod_max_clamp: f32::MAX.to_bits(),
85 compare: None,
86 anisotropy_clamp: 1,
87 border_color: None,
88 }
89 }
90
91 pub fn nearest_mirror() -> Self {
93 Self {
94 address_mode_u: wgpu::AddressMode::MirrorRepeat,
95 address_mode_v: wgpu::AddressMode::MirrorRepeat,
96 address_mode_w: wgpu::AddressMode::MirrorRepeat,
97 mag_filter: wgpu::FilterMode::Nearest,
98 min_filter: wgpu::FilterMode::Nearest,
99 mipmap_filter: wgpu::FilterMode::Nearest,
100 lod_min_clamp: 0.0f32.to_bits(),
101 lod_max_clamp: f32::MAX.to_bits(),
102 compare: None,
103 anisotropy_clamp: 1,
104 border_color: None,
105 }
106 }
107
108 pub fn from_descriptor(desc: &wgpu::SamplerDescriptor) -> Self {
110 Self {
111 address_mode_u: desc.address_mode_u,
112 address_mode_v: desc.address_mode_v,
113 address_mode_w: desc.address_mode_w,
114 mag_filter: desc.mag_filter,
115 min_filter: desc.min_filter,
116 mipmap_filter: desc.mipmap_filter,
117 lod_min_clamp: desc.lod_min_clamp.to_bits(),
118 lod_max_clamp: desc.lod_max_clamp.to_bits(),
119 compare: desc.compare,
120 anisotropy_clamp: desc.anisotropy_clamp,
121 border_color: desc.border_color,
122 }
123 }
124
125 pub fn to_descriptor<'a>(&self, label: Option<&'a str>) -> wgpu::SamplerDescriptor<'a> {
127 wgpu::SamplerDescriptor {
128 label,
129 address_mode_u: self.address_mode_u,
130 address_mode_v: self.address_mode_v,
131 address_mode_w: self.address_mode_w,
132 mag_filter: self.mag_filter,
133 min_filter: self.min_filter,
134 mipmap_filter: self.mipmap_filter,
135 lod_min_clamp: f32::from_bits(self.lod_min_clamp),
136 lod_max_clamp: f32::from_bits(self.lod_max_clamp),
137 compare: self.compare,
138 anisotropy_clamp: self.anisotropy_clamp,
139 border_color: self.border_color,
140 }
141 }
142
143 pub fn linear() -> Self {
145 Self {
146 address_mode_u: wgpu::AddressMode::ClampToEdge,
147 address_mode_v: wgpu::AddressMode::ClampToEdge,
148 address_mode_w: wgpu::AddressMode::ClampToEdge,
149 mag_filter: wgpu::FilterMode::Linear,
150 min_filter: wgpu::FilterMode::Linear,
151 mipmap_filter: wgpu::FilterMode::Linear,
152 lod_min_clamp: 0.0f32.to_bits(),
153 lod_max_clamp: f32::MAX.to_bits(),
154 compare: None,
155 anisotropy_clamp: 1,
156 border_color: None,
157 }
158 }
159
160 pub fn nearest() -> Self {
162 Self {
163 address_mode_u: wgpu::AddressMode::ClampToEdge,
164 address_mode_v: wgpu::AddressMode::ClampToEdge,
165 address_mode_w: wgpu::AddressMode::ClampToEdge,
166 mag_filter: wgpu::FilterMode::Nearest,
167 min_filter: wgpu::FilterMode::Nearest,
168 mipmap_filter: wgpu::FilterMode::Nearest,
169 lod_min_clamp: 0.0f32.to_bits(),
170 lod_max_clamp: f32::MAX.to_bits(),
171 compare: None,
172 anisotropy_clamp: 1,
173 border_color: None,
174 }
175 }
176
177 pub fn linear_repeat() -> Self {
179 Self {
180 address_mode_u: wgpu::AddressMode::Repeat,
181 address_mode_v: wgpu::AddressMode::Repeat,
182 address_mode_w: wgpu::AddressMode::Repeat,
183 mag_filter: wgpu::FilterMode::Linear,
184 min_filter: wgpu::FilterMode::Linear,
185 mipmap_filter: wgpu::FilterMode::Linear,
186 lod_min_clamp: 0.0f32.to_bits(),
187 lod_max_clamp: f32::MAX.to_bits(),
188 compare: None,
189 anisotropy_clamp: 1,
190 border_color: None,
191 }
192 }
193}
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
200pub enum ImageSampling {
201 #[default]
203 Linear,
204 Nearest,
206 LinearRepeat,
208 NearestRepeat,
210 LinearMirror,
212 NearestMirror,
214}
215
216impl ImageSampling {
217 pub fn to_sampler_key(&self) -> SamplerKey {
219 match self {
220 Self::Linear => SamplerKey::linear(),
221 Self::Nearest => SamplerKey::nearest(),
222 Self::LinearRepeat => SamplerKey::linear_repeat(),
223 Self::NearestRepeat => SamplerKey::nearest_repeat(),
224 Self::LinearMirror => SamplerKey::linear_mirror(),
225 Self::NearestMirror => SamplerKey::nearest_mirror(),
226 }
227 }
228}
229
230pub struct SamplerCache {
250 cache: RwLock<HashMap<SamplerKey, Arc<wgpu::Sampler>>>,
251}
252
253impl Default for SamplerCache {
254 fn default() -> Self {
255 Self::new()
256 }
257}
258
259impl SamplerCache {
260 pub fn new() -> Self {
262 Self {
263 cache: RwLock::new(HashMap::default()),
264 }
265 }
266
267 pub fn get_or_create(&self, device: &wgpu::Device, key: SamplerKey) -> Arc<wgpu::Sampler> {
275 profile_function!();
276 {
278 let cache = self.cache.read()
279 .expect("SamplerCache lock poisoned - a thread panicked while accessing the cache");
280 if let Some(sampler) = cache.get(&key) {
281 return Arc::clone(sampler);
282 }
283 }
284
285 let mut cache = self.cache.write()
287 .expect("SamplerCache lock poisoned - a thread panicked while accessing the cache");
288
289 if let Some(sampler) = cache.get(&key) {
291 return Arc::clone(sampler);
292 }
293
294 let descriptor = key.to_descriptor(Some("Cached Sampler"));
296 let sampler = Arc::new(device.create_sampler(&descriptor));
297 cache.insert(key, Arc::clone(&sampler));
298 sampler
299 }
300
301 pub fn get_or_create_from_descriptor(
305 &self,
306 device: &wgpu::Device,
307 descriptor: &wgpu::SamplerDescriptor,
308 ) -> Arc<wgpu::Sampler> {
309 let key = SamplerKey::from_descriptor(descriptor);
310 self.get_or_create(device, key)
311 }
312
313 pub fn linear(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
315 self.get_or_create(device, SamplerKey::linear())
316 }
317
318 pub fn nearest(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
320 self.get_or_create(device, SamplerKey::nearest())
321 }
322
323 pub fn linear_repeat(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
325 self.get_or_create(device, SamplerKey::linear_repeat())
326 }
327
328 pub fn nearest_repeat(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
330 self.get_or_create(device, SamplerKey::nearest_repeat())
331 }
332
333 pub fn linear_mirror(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
335 self.get_or_create(device, SamplerKey::linear_mirror())
336 }
337
338 pub fn nearest_mirror(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
340 self.get_or_create(device, SamplerKey::nearest_mirror())
341 }
342
343 pub fn from_sampling(&self, device: &wgpu::Device, sampling: ImageSampling) -> Arc<wgpu::Sampler> {
345 self.get_or_create(device, sampling.to_sampler_key())
346 }
347
348 pub fn len(&self) -> usize {
353 self.cache.read()
354 .expect("SamplerCache lock poisoned")
355 .len()
356 }
357
358 pub fn is_empty(&self) -> bool {
363 self.cache.read()
364 .expect("SamplerCache lock poisoned")
365 .is_empty()
366 }
367
368 pub fn clear(&self) {
376 self.cache.write()
377 .expect("SamplerCache lock poisoned")
378 .clear();
379 }
380}
381
382#[cfg(test)]
383mod tests {
384 use super::*;
385
386 #[test]
387 fn test_sampler_key_hash_equality() {
388 let key1 = SamplerKey::linear();
389 let key2 = SamplerKey::linear();
390 let key3 = SamplerKey::nearest();
391
392 assert_eq!(key1, key2);
393 assert_ne!(key1, key3);
394
395 use std::collections::hash_map::DefaultHasher;
397 let mut hasher1 = DefaultHasher::new();
398 let mut hasher2 = DefaultHasher::new();
399 key1.hash(&mut hasher1);
400 key2.hash(&mut hasher2);
401 assert_eq!(hasher1.finish(), hasher2.finish());
402 }
403
404 #[test]
405 fn test_sampler_key_roundtrip() {
406 let key = SamplerKey::linear();
407 let desc = key.to_descriptor(Some("Test"));
408 let key2 = SamplerKey::from_descriptor(&desc);
409 assert_eq!(key, key2);
410 }
411}