Skip to main content

wgsl_types/
ty.rs

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