Skip to main content

steel_core/block_state_provider/
noise_cache.rs

1use std::cell::RefCell;
2use std::sync::Arc;
3
4use steel_registry::feature::FeatureNoiseParameters;
5use steel_worldgen::noise::NormalNoise;
6
7use super::BlockStateProviderEvaluator;
8
9// keep custom providers from growing a worker sampler cache indefinitely
10const MAX_CACHED_NOISES: usize = 16;
11
12thread_local! {
13    static PROVIDER_NOISES: RefCell<NoiseCache> = const { RefCell::new(NoiseCache::new()) };
14}
15
16pub(super) struct NoiseCache {
17    entries: Vec<CachedNoise>,
18    next_eviction: usize,
19}
20
21struct CachedNoise {
22    key: NoiseKey,
23    noise: Arc<NormalNoise>,
24}
25
26struct NoiseKey {
27    seed: i64,
28    base_octave: i32,
29    octave_count: i32,
30    normalize: bool,
31    base_amplitude_bits: u64,
32    amplitude_modifier_bits: Box<[u64]>,
33}
34
35impl NoiseCache {
36    const fn new() -> Self {
37        Self {
38            entries: Vec::new(),
39            next_eviction: 0,
40        }
41    }
42
43    pub(super) fn get(parameters: &FeatureNoiseParameters, seed: i64) -> Arc<NormalNoise> {
44        PROVIDER_NOISES.with_borrow_mut(|cache| cache.get_or_create(parameters, seed))
45    }
46
47    fn get_or_create(
48        &mut self,
49        parameters: &FeatureNoiseParameters,
50        seed: i64,
51    ) -> Arc<NormalNoise> {
52        if let Some(entry) = self
53            .entries
54            .iter()
55            .find(|entry| entry.key.matches(parameters, seed))
56        {
57            return Arc::clone(&entry.noise);
58        }
59
60        let noise = Arc::new(BlockStateProviderEvaluator::normal_noise(parameters, seed));
61        let entry = CachedNoise {
62            key: NoiseKey::new(parameters, seed),
63            noise: Arc::clone(&noise),
64        };
65
66        if self.entries.len() < MAX_CACHED_NOISES {
67            self.entries.push(entry);
68        } else {
69            self.entries[self.next_eviction] = entry;
70            self.next_eviction = (self.next_eviction + 1) % MAX_CACHED_NOISES;
71        }
72
73        noise
74    }
75}
76
77impl NoiseKey {
78    fn new(parameters: &FeatureNoiseParameters, seed: i64) -> Self {
79        Self {
80            seed,
81            base_octave: parameters.base_octave,
82            octave_count: parameters.octave_count,
83            normalize: parameters.normalize,
84            base_amplitude_bits: parameters.base_amplitude.to_bits(),
85            amplitude_modifier_bits: parameters
86                .amplitude_modifiers
87                .iter()
88                .map(|value| value.to_bits())
89                .collect(),
90        }
91    }
92
93    fn matches(&self, parameters: &FeatureNoiseParameters, seed: i64) -> bool {
94        self.seed == seed
95            && self.base_octave == parameters.base_octave
96            && self.octave_count == parameters.octave_count
97            && self.normalize == parameters.normalize
98            && self.base_amplitude_bits == parameters.base_amplitude.to_bits()
99            && self.amplitude_modifier_bits.iter().copied().eq(parameters
100                .amplitude_modifiers
101                .iter()
102                .map(|value| value.to_bits()))
103    }
104}