1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use super::gpu::arc::ArcSampler;
use std::collections::HashMap;

/// Sampler state that is used when sampling images on the GPU.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Sampler {
    /// Clamping mode in the U (x) direction.
    pub clamp_u: ClampMode,
    /// Clamping mode in the V (y) direction.
    pub clamp_v: ClampMode,
    /// Clamping mode in the W (z) direction.
    pub clamp_w: ClampMode,
    /// Magnification (upscaling) filter.
    pub mag: FilterMode,
    /// Minification (downscaling) filter.
    pub min: FilterMode,
}

impl Sampler {
    /// Sampler state with linear filtering and edge clamping.
    pub fn linear_clamp() -> Self {
        Sampler {
            clamp_u: ClampMode::Clamp,
            clamp_v: ClampMode::Clamp,
            clamp_w: ClampMode::Clamp,
            mag: FilterMode::Linear,
            min: FilterMode::Linear,
        }
    }

    /// Sampler state with nearest filtering and edge clamping.
    ///
    /// Ideal for pixel art.
    pub fn nearest_clamp() -> Self {
        Sampler {
            mag: FilterMode::Nearest,
            min: FilterMode::Nearest,
            ..Self::linear_clamp()
        }
    }
}

impl Default for Sampler {
    fn default() -> Self {
        Self::linear_clamp()
    }
}

impl<'a> From<Sampler> for wgpu::SamplerDescriptor<'a> {
    fn from(sampler: Sampler) -> Self {
        wgpu::SamplerDescriptor {
            label: None,
            address_mode_u: sampler.clamp_u.into(),
            address_mode_v: sampler.clamp_v.into(),
            address_mode_w: sampler.clamp_w.into(),
            mag_filter: sampler.mag.into(),
            min_filter: sampler.min.into(),
            mipmap_filter: wgpu::FilterMode::Linear,
            lod_min_clamp: 0.0,
            lod_max_clamp: 1.0,
            compare: None,
            anisotropy_clamp: 1,
            border_color: None,
        }
    }
}

impl From<FilterMode> for Sampler {
    fn from(filter: FilterMode) -> Self {
        match filter {
            FilterMode::Nearest => Self::nearest_clamp(),
            FilterMode::Linear => Self::linear_clamp(),
        }
    }
}

/// Describes the clamping mode of a sampler, used when the shader writes to sample outside of texture boundaries.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ClampMode {
    /// The corresponding texel at the nearest edge is sampled.
    Clamp,
    /// The sample coordinates wrap, effectively repeating the texture.
    Repeat,
    /// The sample coordinates wrap and mirror, effectively repeating the texture and flipping.
    MirrorRepeat,
}

impl From<ClampMode> for wgpu::AddressMode {
    fn from(clamp: ClampMode) -> Self {
        match clamp {
            ClampMode::Clamp => wgpu::AddressMode::ClampToEdge,
            ClampMode::Repeat => wgpu::AddressMode::Repeat,
            ClampMode::MirrorRepeat => wgpu::AddressMode::MirrorRepeat,
        }
    }
}

/// Describes the filter mode of a sampler, used when magnification or minification of a texture occurs (i.e. scaling).
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum FilterMode {
    /// The nearest texel is sampled.
    Nearest,
    /// The neighbouring texels are linearly interpolated.
    Linear,
}

impl From<FilterMode> for wgpu::FilterMode {
    fn from(filter: FilterMode) -> Self {
        match filter {
            FilterMode::Nearest => wgpu::FilterMode::Nearest,
            FilterMode::Linear => wgpu::FilterMode::Linear,
        }
    }
}

#[derive(Debug)]
pub(crate) struct SamplerCache {
    cache: HashMap<Sampler, ArcSampler>,
}

impl SamplerCache {
    pub fn new() -> Self {
        SamplerCache {
            cache: Default::default(),
        }
    }

    pub fn get(&mut self, device: &wgpu::Device, sampler: Sampler) -> ArcSampler {
        self.cache
            .entry(sampler)
            .or_insert_with(|| ArcSampler::new(device.create_sampler(&sampler.into())))
            .clone()
    }
}