Skip to main content

wgsl_types/
ty.rs

1//! WGSL [`Type`]s.
2
3use std::str::FromStr;
4
5#[cfg(feature = "naga-ext")]
6use crate::tplt::AccelerationStructureTags;
7use crate::{Error, Instance, inst::*, syntax::*};
8
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct StructMemberType {
11    pub name: String,
12    pub ty: Type,
13    pub size: Option<u32>,
14    pub align: Option<u32>,
15}
16
17impl StructMemberType {
18    pub fn new(name: String, ty: Type) -> Self {
19        Self {
20            name,
21            ty,
22            size: None,
23            align: None,
24        }
25    }
26}
27
28#[derive(Clone, Debug, PartialEq, Eq)]
29pub struct StructType {
30    pub name: String,
31    pub members: Vec<StructMemberType>,
32}
33
34impl From<StructType> for Type {
35    fn from(value: StructType) -> Self {
36        Self::Struct(Box::new(value))
37    }
38}
39
40#[derive(Clone, Debug, PartialEq, Eq, Hash)]
41pub enum TextureType {
42    // sampled
43    Sampled1D(SampledType),
44    Sampled2D(SampledType),
45    Sampled2DArray(SampledType),
46    Sampled3D(SampledType),
47    SampledCube(SampledType),
48    SampledCubeArray(SampledType),
49    // multisampled
50    Multisampled2D(SampledType),
51    DepthMultisampled2D,
52    // external
53    External,
54    // storage
55    Storage1D(TexelFormat, AccessMode),
56    Storage2D(TexelFormat, AccessMode),
57    Storage2DArray(TexelFormat, AccessMode),
58    Storage3D(TexelFormat, AccessMode),
59    // depth
60    Depth2D,
61    Depth2DArray,
62    DepthCube,
63    DepthCubeArray,
64    #[cfg(feature = "naga-ext")]
65    Sampled1DArray(SampledType),
66    #[cfg(feature = "naga-ext")]
67    Storage1DArray(TexelFormat, AccessMode),
68    #[cfg(feature = "naga-ext")]
69    Multisampled2DArray(SampledType),
70}
71
72#[derive(Clone, Debug, PartialEq, Eq)]
73pub enum TextureDimensions {
74    D1,
75    D2,
76    D3,
77}
78
79impl TextureType {
80    pub fn dimensions(&self) -> TextureDimensions {
81        match self {
82            Self::Sampled1D(_) | Self::Storage1D(_, _) => TextureDimensions::D1,
83            Self::Sampled2D(_)
84            | Self::Sampled2DArray(_)
85            | Self::SampledCube(_)
86            | Self::SampledCubeArray(_)
87            | Self::Multisampled2D(_)
88            | Self::Depth2D
89            | Self::Depth2DArray
90            | Self::DepthCube
91            | Self::DepthCubeArray
92            | Self::DepthMultisampled2D
93            | Self::Storage2D(_, _)
94            | Self::Storage2DArray(_, _)
95            | Self::External => TextureDimensions::D2,
96            Self::Sampled3D(_) | Self::Storage3D(_, _) => TextureDimensions::D3,
97            #[cfg(feature = "naga-ext")]
98            Self::Sampled1DArray(_) | Self::Storage1DArray(_, _) => TextureDimensions::D1,
99            #[cfg(feature = "naga-ext")]
100            Self::Multisampled2DArray(_) => TextureDimensions::D2,
101        }
102    }
103    pub fn sampled_type(&self) -> Option<SampledType> {
104        match self {
105            TextureType::Sampled1D(st) => Some(*st),
106            TextureType::Sampled2D(st) => Some(*st),
107            TextureType::Sampled2DArray(st) => Some(*st),
108            TextureType::Sampled3D(st) => Some(*st),
109            TextureType::SampledCube(st) => Some(*st),
110            TextureType::SampledCubeArray(st) => Some(*st),
111            TextureType::Multisampled2D(st) => Some(*st),
112            TextureType::DepthMultisampled2D => None,
113            TextureType::External => None,
114            TextureType::Storage1D(_, _) => None,
115            TextureType::Storage2D(_, _) => None,
116            TextureType::Storage2DArray(_, _) => None,
117            TextureType::Storage3D(_, _) => None,
118            TextureType::Depth2D => None,
119            TextureType::Depth2DArray => None,
120            TextureType::DepthCube => None,
121            TextureType::DepthCubeArray => None,
122            #[cfg(feature = "naga-ext")]
123            TextureType::Sampled1DArray(st) => Some(*st),
124            #[cfg(feature = "naga-ext")]
125            TextureType::Storage1DArray(_, _) => None,
126            #[cfg(feature = "naga-ext")]
127            TextureType::Multisampled2DArray(st) => Some(*st),
128        }
129    }
130    pub fn channel_type(&self) -> SampledType {
131        match self {
132            TextureType::Sampled1D(st) => *st,
133            TextureType::Sampled2D(st) => *st,
134            TextureType::Sampled2DArray(st) => *st,
135            TextureType::Sampled3D(st) => *st,
136            TextureType::SampledCube(st) => *st,
137            TextureType::SampledCubeArray(st) => *st,
138            TextureType::Multisampled2D(st) => *st,
139            TextureType::DepthMultisampled2D => SampledType::F32,
140            TextureType::External => SampledType::F32,
141            TextureType::Storage1D(f, _) => f.channel_type(),
142            TextureType::Storage2D(f, _) => f.channel_type(),
143            TextureType::Storage2DArray(f, _) => f.channel_type(),
144            TextureType::Storage3D(f, _) => f.channel_type(),
145            TextureType::Depth2D => SampledType::F32,
146            TextureType::Depth2DArray => SampledType::F32,
147            TextureType::DepthCube => SampledType::F32,
148            TextureType::DepthCubeArray => SampledType::F32,
149            #[cfg(feature = "naga-ext")]
150            TextureType::Sampled1DArray(st) => *st,
151            #[cfg(feature = "naga-ext")]
152            TextureType::Storage1DArray(f, _) => f.channel_type(),
153            #[cfg(feature = "naga-ext")]
154            TextureType::Multisampled2DArray(st) => *st,
155        }
156    }
157    /// NOTE: a `texture_depth_multisampled_2d` is *not* considered a depth texture.
158    pub fn is_depth(&self) -> bool {
159        matches!(
160            self,
161            TextureType::Depth2D
162                | TextureType::Depth2DArray
163                | TextureType::DepthCube
164                | TextureType::DepthCubeArray
165        )
166    }
167    pub fn is_storage(&self) -> bool {
168        match self {
169            TextureType::Storage1D(_, _)
170            | TextureType::Storage2D(_, _)
171            | TextureType::Storage2DArray(_, _)
172            | TextureType::Storage3D(_, _) => true,
173            #[cfg(feature = "naga-ext")]
174            TextureType::Storage1DArray(_, _) => true,
175            _ => false,
176        }
177    }
178    pub fn is_sampled(&self) -> bool {
179        match self {
180            TextureType::Sampled1D(_)
181            | TextureType::Sampled2D(_)
182            | TextureType::Sampled2DArray(_)
183            | TextureType::Sampled3D(_)
184            | TextureType::SampledCube(_)
185            | TextureType::SampledCubeArray(_) => true,
186            #[cfg(feature = "naga-ext")]
187            TextureType::Sampled1DArray(_) => true,
188            _ => false,
189        }
190    }
191    pub fn is_arrayed(&self) -> bool {
192        match self {
193            TextureType::Sampled2DArray(_)
194            | TextureType::SampledCubeArray(_)
195            | TextureType::Storage2DArray(_, _)
196            | TextureType::Depth2DArray
197            | TextureType::DepthCubeArray => true,
198            #[cfg(feature = "naga-ext")]
199            TextureType::Sampled1DArray(_)
200            | TextureType::Storage1DArray(_, _)
201            | TextureType::Multisampled2DArray(_) => true,
202            _ => false,
203        }
204    }
205    pub fn is_multisampled(&self) -> bool {
206        match self {
207            TextureType::Multisampled2D(_) | TextureType::DepthMultisampled2D => true,
208            #[cfg(feature = "naga-ext")]
209            TextureType::Multisampled2DArray(_) => true,
210            _ => false,
211        }
212    }
213    pub fn is_cube(&self) -> bool {
214        matches!(
215            self,
216            TextureType::SampledCube(_)
217                | TextureType::SampledCubeArray(_)
218                | TextureType::DepthCube
219                | TextureType::DepthCubeArray
220        )
221    }
222}
223
224impl TryFrom<&Type> for SampledType {
225    type Error = Error;
226
227    fn try_from(value: &Type) -> Result<Self, Self::Error> {
228        match value {
229            Type::I32 => Ok(SampledType::I32),
230            Type::U32 => Ok(SampledType::U32),
231            Type::F32 => Ok(SampledType::F32),
232            _ => Err(Error::SampledType(value.clone())),
233        }
234    }
235}
236
237impl From<SampledType> for Type {
238    fn from(value: SampledType) -> Self {
239        match value {
240            SampledType::I32 => Type::I32,
241            SampledType::U32 => Type::U32,
242            SampledType::F32 => Type::F32,
243            #[cfg(feature = "naga-ext")]
244            SampledType::U64 => Type::U64,
245        }
246    }
247}
248
249#[derive(Clone, Debug, PartialEq, Eq, Hash)]
250pub enum SamplerType {
251    Sampler,
252    SamplerComparison,
253}
254
255impl FromStr for SamplerType {
256    type Err = ();
257
258    fn from_str(s: &str) -> Result<Self, Self::Err> {
259        match s {
260            "sampler" => Ok(Self::Sampler),
261            "sampler_comparison" => Ok(Self::SamplerComparison),
262            _ => Err(()),
263        }
264    }
265}
266
267/// WGSL type.
268#[derive(Clone, Debug, PartialEq, Eq)]
269pub enum Type {
270    Bool,
271    AbstractInt,
272    AbstractFloat,
273    I32,
274    U32,
275    F32,
276    F16,
277    Struct(Box<StructType>),
278    Array(Box<Type>, Option<usize>),
279    Vec(u8, Box<Type>),
280    Mat(u8, u8, Box<Type>),
281    Atomic(Box<Type>),
282    Ptr(AddressSpace, Box<Type>, AccessMode),
283    Ref(AddressSpace, Box<Type>, AccessMode),
284    Texture(TextureType),
285    Sampler(SamplerType),
286    /// This variant is used by wgsl-analyzer and other type-checking tools when
287    /// a type is unknown. It is not an regular WGSL type.
288    Unknown,
289    #[cfg(feature = "naga-ext")]
290    I64,
291    #[cfg(feature = "naga-ext")]
292    U64,
293    #[cfg(feature = "naga-ext")]
294    F64,
295    #[cfg(feature = "naga-ext")]
296    BindingArray(Box<Type>, Option<usize>),
297    #[cfg(feature = "naga-ext")]
298    RayQuery(Option<AccelerationStructureTags>),
299    #[cfg(feature = "naga-ext")]
300    AccelerationStructure(Option<AccelerationStructureTags>),
301}
302
303impl Type {
304    /// Reference: <https://www.w3.org/TR/WGSL/#scalar>
305    pub fn is_scalar(&self) -> bool {
306        match self {
307            Type::Bool
308            | Type::AbstractInt
309            | Type::AbstractFloat
310            | Type::I32
311            | Type::U32
312            | Type::F32
313            | Type::F16 => true,
314            #[cfg(feature = "naga-ext")]
315            Type::I64 | Type::U64 | Type::F64 => true,
316            _ => false,
317        }
318    }
319
320    /// Reference: <https://www.w3.org/TR/WGSL/#numeric-scalar>
321    pub fn is_numeric(&self) -> bool {
322        match self {
323            Type::AbstractInt
324            | Type::AbstractFloat
325            | Type::I32
326            | Type::U32
327            | Type::F32
328            | Type::F16 => true,
329            #[cfg(feature = "naga-ext")]
330            Type::I64 | Type::U64 | Type::F64 => true,
331            _ => false,
332        }
333    }
334
335    /// Reference: <https://www.w3.org/TR/WGSL/#integer-scalar>
336    pub fn is_integer(&self) -> bool {
337        match self {
338            Type::AbstractInt | Type::I32 | Type::U32 => true,
339            #[cfg(feature = "naga-ext")]
340            Type::I64 | Type::U64 => true,
341            _ => false,
342        }
343    }
344
345    /// Is a signed numeric type.
346    pub fn is_signed(&self) -> bool {
347        match self {
348            Type::AbstractInt | Type::AbstractFloat | Type::I32 | Type::F32 | Type::F16 => true,
349            #[cfg(feature = "naga-ext")]
350            Type::I64 | Type::F64 => true,
351            _ => false,
352        }
353    }
354
355    /// Reference: <https://www.w3.org/TR/WGSL/#floating-point-types>
356    pub fn is_float(&self) -> bool {
357        match self {
358            Type::AbstractFloat | Type::F32 | Type::F16 => true,
359            #[cfg(feature = "naga-ext")]
360            Type::F64 => true,
361            _ => false,
362        }
363    }
364
365    /// Reference: <https://www.w3.org/TR/WGSL/#abstract-types>
366    pub fn is_abstract(&self) -> bool {
367        match self {
368            Type::AbstractInt => true,
369            Type::AbstractFloat => true,
370            Type::Array(ty, _) | Type::Vec(_, ty) | Type::Mat(_, _, ty) => ty.is_abstract(),
371            _ => false,
372        }
373    }
374
375    pub fn is_concrete(&self) -> bool {
376        match self {
377            Type::Unknown => false,
378            _ => !self.is_abstract(),
379        }
380    }
381
382    /// Reference: <https://www.w3.org/TR/WGSL/#storable-types>
383    pub fn is_storable(&self) -> bool {
384        self.is_concrete()
385            && match self {
386                Type::Bool
387                | Type::I32
388                | Type::U32
389                | Type::F32
390                | Type::F16
391                | Type::Struct(_)
392                | Type::Array(_, _)
393                | Type::Vec(_, _)
394                | Type::Mat(_, _, _)
395                | Type::Atomic(_) => true,
396                #[cfg(feature = "naga-ext")]
397                Type::I64 | Type::U64 | Type::F64 => true,
398                _ => false,
399            }
400    }
401
402    pub fn is_array(&self) -> bool {
403        matches!(self, Type::Array(_, _))
404    }
405    pub fn is_vec(&self) -> bool {
406        matches!(self, Type::Vec(_, _))
407    }
408    pub fn is_i32(&self) -> bool {
409        matches!(self, Type::I32)
410    }
411    pub fn is_u32(&self) -> bool {
412        matches!(self, Type::U32)
413    }
414    pub fn is_f32(&self) -> bool {
415        matches!(self, Type::F32)
416    }
417    #[cfg(feature = "naga-ext")]
418    pub fn is_i64(&self) -> bool {
419        matches!(self, Type::I64)
420    }
421    #[cfg(feature = "naga-ext")]
422    pub fn is_u64(&self) -> bool {
423        matches!(self, Type::U64)
424    }
425    #[cfg(feature = "naga-ext")]
426    pub fn is_f64(&self) -> bool {
427        matches!(self, Type::F64)
428    }
429    pub fn is_bool(&self) -> bool {
430        matches!(self, Type::Bool)
431    }
432    pub fn is_mat(&self) -> bool {
433        matches!(self, Type::Mat(_, _, _))
434    }
435    pub fn is_abstract_int(&self) -> bool {
436        matches!(self, Type::AbstractInt)
437    }
438
439    pub fn unwrap_atomic(self) -> Box<Type> {
440        match self {
441            Type::Atomic(ty) => ty,
442            val => panic!("called `Type::unwrap_atomic()` on a `{val}` value"),
443        }
444    }
445
446    pub fn unwrap_struct(self) -> Box<StructType> {
447        match self {
448            Type::Struct(ty) => ty,
449            val => panic!("called `Type::unwrap_struct()` on a `{val}` value"),
450        }
451    }
452
453    pub fn unwrap_vec(self) -> (u8, Box<Type>) {
454        match self {
455            Type::Vec(size, ty) => (size, ty),
456            val => panic!("called `Type::unwrap_vec()` on a `{val}` value"),
457        }
458    }
459}
460
461pub trait Ty {
462    /// get the type of an instance.
463    fn ty(&self) -> Type;
464
465    /// get the inner type of an instance (not recursive).
466    ///
467    /// e.g. the inner type of `array<vec3<u32>>` is `vec3<u32>`.
468    fn inner_ty(&self) -> Type {
469        self.ty()
470    }
471}
472
473impl Ty for Type {
474    fn ty(&self) -> Type {
475        self.clone()
476    }
477
478    fn inner_ty(&self) -> Type {
479        match self {
480            Type::Bool => self.clone(),
481            Type::AbstractInt => self.clone(),
482            Type::AbstractFloat => self.clone(),
483            Type::I32 => self.clone(),
484            Type::U32 => self.clone(),
485            Type::F32 => self.clone(),
486            Type::F16 => self.clone(),
487            Type::Struct(_) => self.clone(),
488            Type::Array(ty, _) => ty.ty(),
489            Type::Vec(_, ty) => ty.ty(),
490            Type::Mat(_, _, ty) => ty.ty(),
491            Type::Atomic(ty) => ty.ty(),
492            Type::Ptr(_, ty, _) => ty.ty(),
493            Type::Ref(_, ty, _) => ty.ty(),
494            Type::Texture(_) => self.clone(),
495            Type::Sampler(_) => self.clone(),
496            Type::Unknown => self.clone(),
497            #[cfg(feature = "naga-ext")]
498            Type::I64 => self.clone(),
499            #[cfg(feature = "naga-ext")]
500            Type::U64 => self.clone(),
501            #[cfg(feature = "naga-ext")]
502            Type::F64 => self.clone(),
503            #[cfg(feature = "naga-ext")]
504            Type::BindingArray(ty, _) => ty.ty(),
505            #[cfg(feature = "naga-ext")]
506            Type::RayQuery(_) => self.clone(),
507            #[cfg(feature = "naga-ext")]
508            Type::AccelerationStructure(_) => self.clone(),
509        }
510    }
511}
512
513impl Ty for Instance {
514    fn ty(&self) -> Type {
515        match self {
516            Instance::Literal(l) => l.ty(),
517            Instance::Struct(s) => s.ty(),
518            Instance::Array(a) => a.ty(),
519            Instance::Vec(v) => v.ty(),
520            Instance::Mat(m) => m.ty(),
521            Instance::Ptr(p) => p.ty(),
522            Instance::Ref(r) => r.ty(),
523            Instance::Atomic(a) => a.ty(),
524            Instance::Deferred(t) => t.ty(),
525        }
526    }
527    fn inner_ty(&self) -> Type {
528        match self {
529            Instance::Literal(l) => l.inner_ty(),
530            Instance::Struct(s) => s.inner_ty(),
531            Instance::Array(a) => a.inner_ty(),
532            Instance::Vec(v) => v.inner_ty(),
533            Instance::Mat(m) => m.inner_ty(),
534            Instance::Ptr(p) => p.inner_ty(),
535            Instance::Ref(r) => r.inner_ty(),
536            Instance::Atomic(a) => a.inner_ty(),
537            Instance::Deferred(t) => t.inner_ty(),
538        }
539    }
540}
541
542impl Ty for LiteralInstance {
543    fn ty(&self) -> Type {
544        match self {
545            LiteralInstance::Bool(_) => Type::Bool,
546            LiteralInstance::AbstractInt(_) => Type::AbstractInt,
547            LiteralInstance::AbstractFloat(_) => Type::AbstractFloat,
548            LiteralInstance::I32(_) => Type::I32,
549            LiteralInstance::U32(_) => Type::U32,
550            LiteralInstance::F32(_) => Type::F32,
551            LiteralInstance::F16(_) => Type::F16,
552            #[cfg(feature = "naga-ext")]
553            LiteralInstance::I64(_) => Type::I64,
554            #[cfg(feature = "naga-ext")]
555            LiteralInstance::U64(_) => Type::U64,
556            #[cfg(feature = "naga-ext")]
557            LiteralInstance::F64(_) => Type::F64,
558        }
559    }
560}
561
562impl Ty for StructInstance {
563    fn ty(&self) -> Type {
564        self.ty.clone().into()
565    }
566}
567
568impl Ty for ArrayInstance {
569    fn ty(&self) -> Type {
570        Type::Array(
571            Box::new(self.inner_ty().clone()),
572            (!self.runtime_sized).then_some(self.n()),
573        )
574    }
575    fn inner_ty(&self) -> Type {
576        self.get(0).unwrap().ty()
577    }
578}
579
580impl Ty for VecInstance {
581    fn ty(&self) -> Type {
582        Type::Vec(self.n() as u8, Box::new(self.inner_ty()))
583    }
584    fn inner_ty(&self) -> Type {
585        self.get(0).unwrap().ty()
586    }
587}
588
589impl Ty for MatInstance {
590    fn ty(&self) -> Type {
591        Type::Mat(self.c() as u8, self.r() as u8, Box::new(self.inner_ty()))
592    }
593    fn inner_ty(&self) -> Type {
594        self.get(0, 0).unwrap().ty()
595    }
596}
597
598impl Ty for PtrInstance {
599    fn ty(&self) -> Type {
600        Type::Ptr(
601            self.ptr.space,
602            Box::new(self.ptr.ty.clone()),
603            self.ptr.access,
604        )
605    }
606}
607
608impl Ty for RefInstance {
609    fn ty(&self) -> Type {
610        Type::Ref(self.space, Box::new(self.ty.clone()), self.access)
611    }
612}
613
614impl Ty for AtomicInstance {
615    fn ty(&self) -> Type {
616        Type::Atomic(self.inner_ty().into())
617    }
618    fn inner_ty(&self) -> Type {
619        self.inner().ty()
620    }
621}