1use ahash::HashMap;
7use astrelis_core::profiling::profile_function;
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
279 .cache
280 .read()
281 .expect("SamplerCache lock poisoned - a thread panicked while accessing the cache");
282 if let Some(sampler) = cache.get(&key) {
283 return Arc::clone(sampler);
284 }
285 }
286
287 let mut cache = self
289 .cache
290 .write()
291 .expect("SamplerCache lock poisoned - a thread panicked while accessing the cache");
292
293 if let Some(sampler) = cache.get(&key) {
295 return Arc::clone(sampler);
296 }
297
298 let descriptor = key.to_descriptor(Some("Cached Sampler"));
300 let sampler = Arc::new(device.create_sampler(&descriptor));
301 cache.insert(key, Arc::clone(&sampler));
302 sampler
303 }
304
305 pub fn get_or_create_from_descriptor(
309 &self,
310 device: &wgpu::Device,
311 descriptor: &wgpu::SamplerDescriptor,
312 ) -> Arc<wgpu::Sampler> {
313 let key = SamplerKey::from_descriptor(descriptor);
314 self.get_or_create(device, key)
315 }
316
317 pub fn linear(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
319 self.get_or_create(device, SamplerKey::linear())
320 }
321
322 pub fn nearest(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
324 self.get_or_create(device, SamplerKey::nearest())
325 }
326
327 pub fn linear_repeat(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
329 self.get_or_create(device, SamplerKey::linear_repeat())
330 }
331
332 pub fn nearest_repeat(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
334 self.get_or_create(device, SamplerKey::nearest_repeat())
335 }
336
337 pub fn linear_mirror(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
339 self.get_or_create(device, SamplerKey::linear_mirror())
340 }
341
342 pub fn nearest_mirror(&self, device: &wgpu::Device) -> Arc<wgpu::Sampler> {
344 self.get_or_create(device, SamplerKey::nearest_mirror())
345 }
346
347 pub fn from_sampling(
349 &self,
350 device: &wgpu::Device,
351 sampling: ImageSampling,
352 ) -> Arc<wgpu::Sampler> {
353 self.get_or_create(device, sampling.to_sampler_key())
354 }
355
356 pub fn len(&self) -> usize {
361 self.cache.read().expect("SamplerCache lock poisoned").len()
362 }
363
364 pub fn is_empty(&self) -> bool {
369 self.cache
370 .read()
371 .expect("SamplerCache lock poisoned")
372 .is_empty()
373 }
374
375 pub fn clear(&self) {
383 self.cache
384 .write()
385 .expect("SamplerCache lock poisoned")
386 .clear();
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn test_sampler_key_hash_equality() {
396 let key1 = SamplerKey::linear();
397 let key2 = SamplerKey::linear();
398 let key3 = SamplerKey::nearest();
399
400 assert_eq!(key1, key2);
401 assert_ne!(key1, key3);
402
403 use std::collections::hash_map::DefaultHasher;
405 let mut hasher1 = DefaultHasher::new();
406 let mut hasher2 = DefaultHasher::new();
407 key1.hash(&mut hasher1);
408 key2.hash(&mut hasher2);
409 assert_eq!(hasher1.finish(), hasher2.finish());
410 }
411
412 #[test]
413 fn test_sampler_key_roundtrip() {
414 let key = SamplerKey::linear();
415 let desc = key.to_descriptor(Some("Test"));
416 let key2 = SamplerKey::from_descriptor(&desc);
417 assert_eq!(key, key2);
418 }
419}