Skip to main content

valo_dl/
resources.rs

1use std::sync::atomic::{AtomicU64, Ordering};
2use std::sync::Arc;
3
4/// `Image` is a cheap-to-clone handle to an immutable GPU image.
5///
6/// Display lists retain cloned handles, and the texture is released after the
7/// last handle and in-flight GPU use are gone.
8#[derive(Clone)]
9pub struct Image {
10    inner: Arc<ImageInner>,
11}
12
13/// `ImageInner` contains the shared resources and metadata behind an [`Image`].
14pub struct ImageInner {
15    /// `id` is the process-unique identity of this image.
16    pub id: u64,
17    /// `size` is the image dimensions in pixels.
18    pub size: [u32; 2],
19    /// `texture` stores the image pixels.
20    pub texture: wgpu::Texture,
21    /// `view` exposes the complete texture for sampling.
22    pub view: wgpu::TextureView,
23    /// `mip_levels` is the number of available mip levels.
24    pub mip_levels: u32,
25}
26
27/// `Image` equality compares image identity rather than pixel contents.
28impl PartialEq for Image {
29    fn eq(&self, other: &Self) -> bool {
30        self.inner.id == other.inner.id
31    }
32}
33
34static NEXT_IMAGE_ID: AtomicU64 = AtomicU64::new(1);
35
36impl Image {
37    /// `from_texture` creates an image from an existing texture.
38    ///
39    /// Prefer `Context::import_image` through the `valo` facade unless managing
40    /// renderer resources directly.
41    pub fn from_texture(texture: wgpu::Texture, size: [u32; 2], mip_levels: u32) -> Self {
42        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
43        Self {
44            inner: Arc::new(ImageInner {
45                id: NEXT_IMAGE_ID.fetch_add(1, Ordering::Relaxed),
46                size,
47                texture,
48                view,
49                mip_levels,
50            }),
51        }
52    }
53
54    /// `id` returns the process-unique identity of this image.
55    pub fn id(&self) -> u64 {
56        self.inner.id
57    }
58
59    /// `size` returns the image dimensions in pixels.
60    pub fn size(&self) -> [u32; 2] {
61        self.inner.size
62    }
63
64    /// `width` returns the image width in pixels.
65    pub fn width(&self) -> f32 {
66        self.inner.size[0] as f32
67    }
68
69    /// `height` returns the image height in pixels.
70    pub fn height(&self) -> f32 {
71        self.inner.size[1] as f32
72    }
73
74    /// `view` returns the texture view used for sampling.
75    pub fn view(&self) -> &wgpu::TextureView {
76        &self.inner.view
77    }
78
79    /// `texture` returns the underlying GPU texture.
80    pub fn texture(&self) -> &wgpu::Texture {
81        &self.inner.texture
82    }
83
84    /// `mip_levels` returns the number of available mip levels.
85    pub fn mip_levels(&self) -> u32 {
86        self.inner.mip_levels
87    }
88
89    /// `downgrade` returns a non-owning handle to this image's resources.
90    pub fn downgrade(&self) -> std::sync::Weak<ImageInner> {
91        Arc::downgrade(&self.inner)
92    }
93}
94
95impl std::fmt::Debug for Image {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        f.debug_struct("Image")
98            .field("id", &self.inner.id)
99            .field("size", &self.inner.size)
100            .field("mip_levels", &self.inner.mip_levels)
101            .finish()
102    }
103}
104
105#[cfg(feature = "serde")]
106impl serde::Serialize for Image {
107    /// Dumps identity, not pixels: the serde dump exists for diffs and bug
108    /// reports, never for persisting GPU state.
109    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
110        use serde::ser::SerializeStruct;
111        let mut st = s.serialize_struct("Image", 2)?;
112        st.serialize_field("id", &self.inner.id)?;
113        st.serialize_field("size", &self.inner.size)?;
114        st.end()
115    }
116}
117
118/// `Sampling` controls filtering, mip selection, and behavior outside an image.
119#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
120#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
121pub struct Sampling {
122    /// `filter` controls interpolation between neighboring texels.
123    pub filter: Filter,
124    /// `mipmap` controls level selection when mipmaps are available.
125    pub mipmap: MipmapMode,
126    /// `tile_x` controls sampling outside the horizontal image bounds.
127    pub tile_x: TileMode,
128    /// `tile_y` controls sampling outside the vertical image bounds.
129    pub tile_y: TileMode,
130}
131
132/// `MipmapMode` controls how minified images select mip levels.
133#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
134#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
135pub enum MipmapMode {
136    /// `None` always samples level zero.
137    None,
138    /// `Nearest` samples the closest mip level.
139    Nearest,
140    /// `Linear` blends the two nearest mip levels.
141    #[default]
142    Linear,
143}
144
145/// `Filter` controls interpolation between neighboring texels.
146#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
147#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
148pub enum Filter {
149    /// `Linear` uses bilinear interpolation.
150    #[default]
151    Linear,
152    /// `Nearest` selects the nearest texel without interpolation.
153    Nearest,
154}
155
156/// `TileMode` controls samples outside an image's bounds.
157#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
158#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
159pub enum TileMode {
160    /// `Clamp` extends the nearest edge texel.
161    #[default]
162    Clamp,
163    /// `Repeat` repeats the image in the same orientation.
164    Repeat,
165    /// `Mirror` repeats the image with alternating orientation.
166    Mirror,
167    /// `Decal` returns transparent pixels outside the image.
168    Decal,
169}