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}