Skip to main content

naga_rust_rt/
texture.rs

1use core::marker::PhantomData;
2use core::num::NonZeroU32;
3
4use crate::{Scalar, Vec2, Vec3, Vec4};
5
6// -------------------------------------------------------------------------------------------------
7
8/// Texture sampler (placeholder).
9///
10/// Use this type to satisfy a sampler binding in a resource struct.
11pub struct Sampler;
12
13/// The number 1, as a [`NonZeroU32`].
14///
15/// Convenience alias because textures heavily use non-zero sizes.
16pub const ONE: NonZeroU32 = NonZeroU32::MIN;
17
18// -------------------------------------------------------------------------------------------------
19
20/// 1-dimensional texture object.
21///
22/// Use this type to satisfy a texture binding in a resource struct,
23/// when the binding has WGSL type `texture_1d`.
24#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
25pub struct Texture1d<T: ?Sized> {
26    pub dimensions: Scalar<NonZeroU32>,
27    pub mip_levels: NonZeroU32,
28    pub data: T,
29}
30
31/// 2-dimensional texture object.
32///
33/// Use this type to satisfy a texture binding in a resource struct,
34/// when the binding has WGSL type `texture_2d` or `texture_external`.
35#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
36pub struct Texture2d<T: ?Sized> {
37    pub dimensions: Vec2<NonZeroU32>,
38    pub mip_levels: NonZeroU32,
39    pub data: T,
40}
41
42/// 2-dimensional array texture object.
43///
44/// Use this type to satisfy a texture binding in a resource struct,
45/// when the binding has WGSL type `texture_2d_array`.
46#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
47pub struct Texture2dArray<T: ?Sized> {
48    pub dimensions: Vec2<NonZeroU32>,
49    pub array_layers: NonZeroU32,
50    pub mip_levels: NonZeroU32,
51    pub data: T,
52}
53
54/// 3-dimensional texture object.
55///
56/// Use this type to satisfy a texture binding in a resource struct,
57/// when the binding has WGSL type `texture_3d`.
58#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
59pub struct Texture3d<T: ?Sized> {
60    pub dimensions: Vec3<NonZeroU32>,
61    pub mip_levels: NonZeroU32,
62    pub data: T,
63}
64
65/// Cubemap texture object.
66///
67/// Use this type to satisfy a texture binding in a resource struct,
68/// when the binding has WGSL type `texture_cube`.
69#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
70pub struct TextureCube<T: ?Sized> {
71    pub dimensions: Vec2<NonZeroU32>,
72    pub mip_levels: NonZeroU32,
73    pub data: T,
74}
75
76/// Cubemap array texture object.
77///
78/// Use this type to satisfy a texture binding in a resource struct,
79/// when the binding has WGSL type `texture_cube_array`.
80#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
81pub struct TextureCubeArray<T: ?Sized> {
82    pub dimensions: Vec2<NonZeroU32>,
83    pub array_layers: NonZeroU32,
84    pub mip_levels: NonZeroU32,
85    pub data: T,
86}
87
88/// Multisampled texture object.
89///
90/// Use this type to satisfy a texture binding in a resource struct,
91/// when the binding has WGSL type `texture_multisampled_2d`.
92#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
93pub struct TextureMultisampled2d<T: ?Sized> {
94    pub dimensions: Vec2<NonZeroU32>,
95    pub samples: NonZeroU32,
96    pub data: T,
97}
98
99// -------------------------------------------------------------------------------------------------
100
101// TODO: we want to have these convenient constructors but without a requirement for borrowing.
102// We could solve that by using `Box` instead of `&`, but it would be better to work on a general
103// mechanism to avoid `dyn`.
104
105impl<'d, C: Component> Texture1d<&'d dyn Read<Coordinates = Scalar<i32>, Component = C>> {
106    pub fn one_texel(texel: &'d Constant<Scalar<i32>, C>) -> Self {
107        Self {
108            dimensions: Scalar::new(ONE),
109            mip_levels: ONE,
110            data: texel,
111        }
112    }
113}
114
115impl<'d, C: Component> Texture2d<&'d dyn Read<Coordinates = Vec2<i32>, Component = C>> {
116    pub fn one_texel(texel: &'d Constant<Vec2<i32>, C>) -> Self {
117        Self {
118            dimensions: Vec2::splat(ONE),
119            mip_levels: ONE,
120            data: texel,
121        }
122    }
123}
124
125// -------------------------------------------------------------------------------------------------
126
127/// Reads one texel from a texture.
128///
129/// Implement this trait and then put the implementation in a [`Texture1d`], [`Texture2d`], etc.
130/// to provide a texture to your shader code.
131//
132// TODO: Usage example.
133pub trait Read {
134    type Coordinates: Copy + 'static;
135    type Component: Component;
136
137    /// Loads a single texel from the texture.
138    ///
139    /// If the coordinates are out of bounds, the implementation should not panic,
140    /// but perform one of the behaviors specified in <https://www.w3.org/TR/WGSL/#textureload>.
141    fn read_texel(
142        &self,
143        coordinates: Self::Coordinates,
144        array_layer: i32,
145        sample: i32,
146        mip_level: i32,
147    ) -> Vec4<Self::Component>;
148}
149
150/// Information about a texture.
151///
152/// This trait is implemented by all the texture types like [`Texture1d`], [`Texture2d`], etc.
153pub trait Query {
154    /// Type of the dimensions of the texture.
155    /// Should be a [`Scalar`], [`Vec2`], or [`Vec3`] of [`NonZeroU32`].
156    type Dimensions: Dimensions;
157
158    /// Numeric type exposed to the shader.
159    ///
160    /// Should be one of [`u32`], [`i32`], or [`f32`], and must match the parameter of the
161    /// texture type in the shader.
162    type Component: Component;
163
164    /// Returns the dimensions of mip level 0 of the texture.
165    fn base_dimensions(&self) -> Self::Dimensions;
166
167    /// Returns the count of array layers of the texture.
168    ///
169    /// For non-array textures, returns 1.
170    fn array_layers(&self) -> NonZeroU32 {
171        ONE
172    }
173
174    /// Returns the count of mip levels of the texture.
175    fn mip_levels(&self) -> NonZeroU32 {
176        ONE
177    }
178
179    /// Returns the count of samples of the texture.
180    ///
181    /// For non-multisampled textures, returns 1.
182    fn samples(&self) -> NonZeroU32 {
183        ONE
184    }
185}
186
187/// Types which can be components of texels.
188pub trait Component: Copy + 'static {}
189impl Component for u32 {}
190impl Component for i32 {}
191impl Component for f32 {}
192
193// -------------------------------------------------------------------------------------------------
194
195macro_rules! impl_read_forwarder_struct {
196    ($texture_type:ident) => {
197        impl<T: ?Sized + Read> Read for $texture_type<T> {
198            type Coordinates = T::Coordinates;
199            type Component = T::Component;
200
201            fn read_texel(
202                &self,
203                coordinates: Self::Coordinates,
204                array_layer: i32,
205                sample: i32,
206                mip_level: i32,
207            ) -> Vec4<Self::Component> {
208                self.data
209                    .read_texel(coordinates, array_layer, sample, mip_level)
210            }
211        }
212    };
213}
214
215impl_read_forwarder_struct!(Texture1d);
216impl_read_forwarder_struct!(Texture2d);
217impl_read_forwarder_struct!(Texture2dArray);
218impl_read_forwarder_struct!(Texture3d);
219impl_read_forwarder_struct!(TextureCube);
220impl_read_forwarder_struct!(TextureCubeArray);
221impl_read_forwarder_struct!(TextureMultisampled2d);
222
223macro_rules! impl_read_forwarder_deref {
224    ($($ty:tt)*) => {
225        impl<T: ?Sized + Read> Read for $($ty)* {
226            type Coordinates = T::Coordinates;
227            type Component = T::Component;
228
229            fn read_texel(
230                &self,
231                coordinates: Self::Coordinates,
232                array_layer: i32,
233                sample: i32,
234                mip_level: i32,
235            ) -> Vec4<Self::Component> {
236                T::read_texel(&**self, coordinates, array_layer, sample, mip_level)
237            }
238        }
239    };
240}
241
242impl_read_forwarder_deref!(&T);
243impl_read_forwarder_deref!(&mut T);
244#[cfg(feature = "alloc")]
245impl_read_forwarder_deref!(alloc::boxed::Box<T>);
246#[cfg(feature = "alloc")]
247impl_read_forwarder_deref!(alloc::rc::Rc<T>);
248#[cfg(feature = "alloc")]
249impl_read_forwarder_deref!(alloc::sync::Arc<T>);
250
251// -------------------------------------------------------------------------------------------------
252
253macro_rules! query_common {
254    () => {
255        type Component = T::Component;
256        fn base_dimensions(&self) -> Self::Dimensions {
257            self.dimensions
258        }
259    };
260}
261
262macro_rules! query_mip {
263    () => {
264        fn mip_levels(&self) -> NonZeroU32 {
265            self.mip_levels
266        }
267    };
268}
269
270impl<T: ?Sized + Read> Query for Texture1d<T> {
271    type Dimensions = Scalar<NonZeroU32>;
272    query_common!();
273    query_mip!();
274}
275
276impl<T: ?Sized + Read> Query for Texture2d<T> {
277    type Dimensions = Vec2<NonZeroU32>;
278    query_common!();
279    query_mip!();
280}
281
282impl<T: ?Sized + Read> Query for Texture2dArray<T> {
283    type Dimensions = Vec2<NonZeroU32>;
284    query_common!();
285    query_mip!();
286    fn array_layers(&self) -> NonZeroU32 {
287        self.array_layers
288    }
289}
290
291impl<T: ?Sized + Read> Query for Texture3d<T> {
292    type Dimensions = Vec3<NonZeroU32>;
293    query_common!();
294    query_mip!();
295}
296
297impl<T: ?Sized + Read> Query for TextureCube<T> {
298    type Dimensions = Vec2<NonZeroU32>;
299    query_common!();
300    query_mip!();
301}
302
303impl<T: ?Sized + Read> Query for TextureCubeArray<T> {
304    type Dimensions = Vec2<NonZeroU32>;
305    query_common!();
306    query_mip!();
307    fn array_layers(&self) -> NonZeroU32 {
308        self.array_layers
309    }
310}
311
312impl<T: ?Sized + Read> Query for TextureMultisampled2d<T> {
313    type Dimensions = Vec2<NonZeroU32>;
314    query_common!();
315    fn samples(&self) -> NonZeroU32 {
316        self.samples
317    }
318}
319
320// -------------------------------------------------------------------------------------------------
321
322/// Generic texture data container for textures whose texels are all identical.
323#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] // TODO: derives have bounds on C
324pub struct Constant<C, T> {
325    pub texel: Vec4<T>,
326    _phantom: PhantomData<fn(C)>,
327}
328
329impl<C: Copy + 'static, T: Component> Constant<C, T> {
330    pub const fn new(texel: Vec4<T>) -> Self {
331        Self {
332            texel,
333            _phantom: PhantomData,
334        }
335    }
336}
337
338impl<C: Copy + 'static, T: Component> Read for Constant<C, T> {
339    type Coordinates = C;
340    type Component = T;
341
342    fn read_texel(
343        &self,
344        _coordinates: Self::Coordinates,
345        _array_layer: i32,
346        _sample: i32,
347        _mip_level: i32,
348    ) -> Vec4<Self::Component> {
349        self.texel
350    }
351}
352
353// -------------------------------------------------------------------------------------------------
354
355/// Computes the dimensions of a specific mip level of a texture.
356///
357/// The result is meaningless if the mip level is out of bounds.
358///
359/// As per WGSL [`textureDimensions`](https://www.w3.org/TR/2026/CRD-WGSL-20260507/#texturedimensions).
360//
361// Design note: The result is `u32`, not `NonZeroU32`, for the sake of the shader code which does
362// not have `NonZero` types.
363pub fn dimensions<Q>(texture: &Q, mip_level: i32) -> <Q::Dimensions as Dimensions>::Plain
364where
365    Q: ?Sized + Query,
366{
367    Dimensions::at_mip_level(texture.base_dimensions(), mip_level as u32)
368}
369
370/// Adapter for calling texture query functions.
371///
372/// TODO: This should probably be something more general, but it’s hard to say what.
373#[doc(hidden)]
374pub fn non_zero_to_scalar(input: NonZeroU32) -> Scalar<u32> {
375    Scalar(input.get())
376}
377
378use dim::Dimensions;
379mod dim {
380    use super::*;
381
382    /// Semi-private trait implemented for vectors that can describe texture dimensions.
383    /// Helper for [`dimensions()`].
384    pub trait Dimensions: Copy + 'static {
385        /// The vector with `u32` components instead of `NonZeroU32`.
386        type Plain;
387
388        fn at_mip_level(self, mip_level: u32) -> Self::Plain;
389    }
390
391    impl Dimensions for Scalar<NonZeroU32> {
392        type Plain = Scalar<u32>;
393        fn at_mip_level(self, mip_level: u32) -> Self::Plain {
394            Scalar::new(mip_divide_size(self.0, mip_level))
395        }
396    }
397    impl Dimensions for Vec2<NonZeroU32> {
398        type Plain = Vec2<u32>;
399        fn at_mip_level(self, mip_level: u32) -> Self::Plain {
400            Vec2::new(
401                mip_divide_size(self.x, mip_level),
402                mip_divide_size(self.y, mip_level),
403            )
404        }
405    }
406    impl Dimensions for Vec3<NonZeroU32> {
407        type Plain = Vec3<u32>;
408        fn at_mip_level(self, mip_level: u32) -> Self::Plain {
409            Vec3::new(
410                mip_divide_size(self.x, mip_level),
411                mip_divide_size(self.y, mip_level),
412                mip_divide_size(self.z, mip_level),
413            )
414        }
415    }
416
417    /// Computes one component of the size of a given mip level of a texture,
418    /// given the size at level 0.
419    fn mip_divide_size(size: NonZeroU32, mip_level: u32) -> u32 {
420        // Per <https://www.w3.org/TR/2026/CRD-webgpu-20260507/#abstract-opdef-logical-miplevel-specific-texture-extent>
421        (size.get() >> mip_level).max(1)
422    }
423}