Skip to main content

spirv_std/
vector.rs

1//! Traits related to vectors.
2
3use crate::glam;
4use crate::glam::{Vec3Swizzles, Vec4Swizzles};
5use crate::sealed::Sealed;
6use crate::{Scalar, ScalarComposite, ScalarOrVector, ScalarOrVectorTransform};
7use core::num::NonZeroUsize;
8
9/// Abstract trait representing a SPIR-V vector type.
10///
11/// To implement this trait, your struct must be marked with:
12/// ```no_run
13/// #[cfg_attr(target_arch = "spirv", rust_gpu::vector::v1)]
14/// # struct Bla(f32, f32);
15/// ```
16///
17/// This places these additional constraints on your type, checked by the spirv codegen:
18/// * must be a struct
19/// * members must be a spirv [`Scalar`] type, which includes:
20///   * Floating-point type: f32, f64
21///   * Integer type: u8, u16, u32, u64, i8, i16, i32, i64
22///   * Boolean type: bool
23/// * all members must be of the same primitive type
24/// * must have 2, 3 or 4 vector components / members
25/// * type must derive Copy, Clone, Default
26///
27/// The spirv codegen backend will then emit your type as an `OpTypeVector` instead of an `OpTypeStruct`. The layout of
28/// your type is unaffected, the size, alignment and member offsets will follow standard rustc layout rules. This hint
29/// does nothing on other target platforms.
30///
31/// See the SPIRV spec on [Types](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_types) and the
32/// "Data rules" in the [Universal Validation Rules](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_universal_validation_rules).
33///
34/// # Example
35/// ```no_run
36/// #[derive(Copy, Clone, Default)]
37/// #[cfg_attr(target_arch = "spirv", rust_gpu::vector::v1)]
38/// struct MyColor {
39///     r: f32,
40///     b: f32,
41///     g: f32,
42/// }
43/// ```
44///
45///
46/// # Safety
47/// * Must only be implemented on types that the spirv codegen emits as valid `OpTypeVector`. This includes all structs
48///   marked with `#[rust_gpu::vector::v1]`, like [`glam`]'s non-SIMD "scalar" vector types.
49/// * `ScalarOrVector::DIM == N`, since const equality is behind rustc feature `associated_const_equality`
50// Note(@firestar99) I would like to have these two generics be associated types instead. Doesn't make much sense for
51// a vector type to implement this interface multiple times with different Scalar types or N, after all.
52// While it's possible with `T: Scalar`, it's not with `const N: usize`, since some impl blocks in `image::params` need
53// to be conditional on a specific N value. And you can only express that with const generics, but not with associated
54// constants due to lack of const generics support in rustc.
55pub unsafe trait Vector<T: Scalar, const N: usize>: ScalarOrVector<Scalar = T> {}
56
57macro_rules! impl_vector {
58    ($($ty:ty: [$scalar:ty; $n:literal];)+) => {
59        $(
60            impl Sealed for $ty {}
61            impl ScalarComposite for $ty {
62                #[inline]
63                fn transform<F: ScalarOrVectorTransform>(self, f: &mut F) -> Self {
64                    f.transform_vector(self)
65                }
66            }
67            unsafe impl ScalarOrVector for $ty {
68                type Scalar = $scalar;
69                const N: NonZeroUsize = NonZeroUsize::new($n).unwrap();
70            }
71            unsafe impl Vector<$scalar, $n> for $ty {}
72        )+
73    };
74}
75
76impl_vector! {
77    glam::Vec2: [f32; 2];
78    glam::Vec3: [f32; 3];
79    glam::Vec3A: [f32; 3];
80    glam::Vec4: [f32; 4];
81    glam::DVec2: [f64; 2];
82    glam::DVec3: [f64; 3];
83    glam::DVec4: [f64; 4];
84    glam::UVec2: [u32; 2];
85    glam::UVec3: [u32; 3];
86    glam::UVec4: [u32; 4];
87    glam::IVec2: [i32; 2];
88    glam::IVec3: [i32; 3];
89    glam::IVec4: [i32; 4];
90    glam::BVec2: [bool; 2];
91    glam::BVec3: [bool; 3];
92    glam::BVec4: [bool; 4];
93}
94
95/// Trait that implements slicing of a vector into a scalar or vector of lower dimensions, by
96/// ignoring the higher dimensions
97pub trait VectorTruncateInto<T> {
98    /// Slices the vector into a lower dimensional type by ignoring the higher components
99    fn truncate_into(self) -> T;
100}
101
102macro_rules! vec_trunc_impl {
103    ($a:ty, $b:ty, $self:ident $(.$($e:tt)*)?) => {
104        impl VectorTruncateInto<$a> for $b {
105            fn truncate_into($self) -> $a {
106                $self $(. $($e)*)?
107            }
108        }
109    };
110}
111macro_rules! vec_trunc_impls {
112    ($s:ty, $v2:ty, $v3:ty, $v4:ty) => {
113        vec_trunc_impl! {$s, $s, self}
114        vec_trunc_impl! {$s, $v2, self.x}
115        vec_trunc_impl! {$s, $v3, self.x}
116        vec_trunc_impl! {$s, $v4, self.x}
117
118        vec_trunc_impl! {$v2, $v2, self}
119        vec_trunc_impl! {$v2, $v3, self.xy()}
120        vec_trunc_impl! {$v2, $v4, self.xy()}
121
122        vec_trunc_impl! {$v3, $v3, self}
123        vec_trunc_impl! {$v3, $v4, self.xyz()}
124
125        vec_trunc_impl! {$v4, $v4, self}
126    };
127}
128
129vec_trunc_impls! { f32, glam::Vec2, glam::Vec3, glam::Vec4 }
130vec_trunc_impls! { f64, glam::DVec2, glam::DVec3, glam::DVec4 }
131vec_trunc_impls! { i32, glam::IVec2, glam::IVec3, glam::IVec4 }
132vec_trunc_impls! { u32, glam::UVec2, glam::UVec3, glam::UVec4 }