naga/front/wgsl/
to_wgsl.rs

1//! Producing the WGSL forms of types, for use in error messages.
2
3use crate::proc::GlobalCtx;
4use crate::Handle;
5
6impl crate::proc::TypeResolution {
7    pub fn to_wgsl(&self, gctx: &GlobalCtx) -> String {
8        match *self {
9            crate::proc::TypeResolution::Handle(handle) => handle.to_wgsl(gctx),
10            crate::proc::TypeResolution::Value(ref inner) => inner.to_wgsl(gctx),
11        }
12    }
13}
14
15impl Handle<crate::Type> {
16    /// Formats the type as it is written in wgsl.
17    ///
18    /// For example `vec3<f32>`.
19    pub fn to_wgsl(self, gctx: &GlobalCtx) -> String {
20        let ty = &gctx.types[self];
21        match ty.name {
22            Some(ref name) => name.clone(),
23            None => ty.inner.to_wgsl(gctx),
24        }
25    }
26}
27
28impl crate::TypeInner {
29    /// Formats the type as it is written in wgsl.
30    ///
31    /// For example `vec3<f32>`.
32    ///
33    /// Note: `TypeInner::Struct` doesn't include the name of the
34    /// struct type. Therefore this method will simply return "struct"
35    /// for them.
36    pub fn to_wgsl(&self, gctx: &GlobalCtx) -> String {
37        use crate::TypeInner as Ti;
38
39        match *self {
40            Ti::Scalar(scalar) => scalar.to_wgsl(),
41            Ti::Vector { size, scalar } => {
42                format!("vec{}<{}>", size as u32, scalar.to_wgsl())
43            }
44            Ti::Matrix {
45                columns,
46                rows,
47                scalar,
48            } => {
49                format!(
50                    "mat{}x{}<{}>",
51                    columns as u32,
52                    rows as u32,
53                    scalar.to_wgsl(),
54                )
55            }
56            Ti::Atomic(scalar) => {
57                format!("atomic<{}>", scalar.to_wgsl())
58            }
59            Ti::Pointer { base, .. } => {
60                let name = base.to_wgsl(gctx);
61                format!("ptr<{name}>")
62            }
63            Ti::ValuePointer { scalar, .. } => {
64                format!("ptr<{}>", scalar.to_wgsl())
65            }
66            Ti::Array { base, size, .. } => {
67                let base = base.to_wgsl(gctx);
68                match size {
69                    crate::ArraySize::Constant(size) => format!("array<{base}, {size}>"),
70                    crate::ArraySize::Pending(_) => unreachable!(),
71                    crate::ArraySize::Dynamic => format!("array<{base}>"),
72                }
73            }
74            Ti::Struct { .. } => {
75                // TODO: Actually output the struct?
76                "struct".to_string()
77            }
78            Ti::Image {
79                dim,
80                arrayed,
81                class,
82            } => {
83                let dim_suffix = match dim {
84                    crate::ImageDimension::D1 => "_1d",
85                    crate::ImageDimension::D2 => "_2d",
86                    crate::ImageDimension::D3 => "_3d",
87                    crate::ImageDimension::Cube => "_cube",
88                };
89                let array_suffix = if arrayed { "_array" } else { "" };
90
91                let class_suffix = match class {
92                    crate::ImageClass::Sampled { multi: true, .. } => "_multisampled",
93                    crate::ImageClass::Depth { multi: false } => "_depth",
94                    crate::ImageClass::Depth { multi: true } => "_depth_multisampled",
95                    crate::ImageClass::Sampled { multi: false, .. }
96                    | crate::ImageClass::Storage { .. } => "",
97                };
98
99                let type_in_brackets = match class {
100                    crate::ImageClass::Sampled { kind, .. } => {
101                        // Note: The only valid widths are 4 bytes wide.
102                        // The lexer has already verified this, so we can safely assume it here.
103                        // https://gpuweb.github.io/gpuweb/wgsl/#sampled-texture-type
104                        let element_type = crate::Scalar { kind, width: 4 }.to_wgsl();
105                        format!("<{element_type}>")
106                    }
107                    crate::ImageClass::Depth { multi: _ } => String::new(),
108                    crate::ImageClass::Storage { format, access } => {
109                        if access.contains(crate::StorageAccess::STORE) {
110                            format!("<{},write>", format.to_wgsl())
111                        } else {
112                            format!("<{}>", format.to_wgsl())
113                        }
114                    }
115                };
116
117                format!("texture{class_suffix}{dim_suffix}{array_suffix}{type_in_brackets}")
118            }
119            Ti::Sampler { .. } => "sampler".to_string(),
120            Ti::AccelerationStructure => "acceleration_structure".to_string(),
121            Ti::RayQuery => "ray_query".to_string(),
122            Ti::BindingArray { base, size, .. } => {
123                let member_type = &gctx.types[base];
124                let base = member_type.name.as_deref().unwrap_or("unknown");
125                match size {
126                    crate::ArraySize::Constant(size) => format!("binding_array<{base}, {size}>"),
127                    crate::ArraySize::Pending(_) => unreachable!(),
128                    crate::ArraySize::Dynamic => format!("binding_array<{base}>"),
129                }
130            }
131        }
132    }
133}
134
135impl crate::Scalar {
136    /// Format a scalar kind+width as a type is written in wgsl.
137    ///
138    /// Examples: `f32`, `u64`, `bool`.
139    pub fn to_wgsl(self) -> String {
140        let prefix = match self.kind {
141            crate::ScalarKind::Sint => "i",
142            crate::ScalarKind::Uint => "u",
143            crate::ScalarKind::Float => "f",
144            crate::ScalarKind::Bool => return "bool".to_string(),
145            crate::ScalarKind::AbstractInt => return "{AbstractInt}".to_string(),
146            crate::ScalarKind::AbstractFloat => return "{AbstractFloat}".to_string(),
147        };
148        format!("{}{}", prefix, self.width * 8)
149    }
150}
151
152impl crate::StorageFormat {
153    pub const fn to_wgsl(self) -> &'static str {
154        use crate::StorageFormat as Sf;
155        match self {
156            Sf::R8Unorm => "r8unorm",
157            Sf::R8Snorm => "r8snorm",
158            Sf::R8Uint => "r8uint",
159            Sf::R8Sint => "r8sint",
160            Sf::R16Uint => "r16uint",
161            Sf::R16Sint => "r16sint",
162            Sf::R16Float => "r16float",
163            Sf::Rg8Unorm => "rg8unorm",
164            Sf::Rg8Snorm => "rg8snorm",
165            Sf::Rg8Uint => "rg8uint",
166            Sf::Rg8Sint => "rg8sint",
167            Sf::R32Uint => "r32uint",
168            Sf::R32Sint => "r32sint",
169            Sf::R32Float => "r32float",
170            Sf::Rg16Uint => "rg16uint",
171            Sf::Rg16Sint => "rg16sint",
172            Sf::Rg16Float => "rg16float",
173            Sf::Rgba8Unorm => "rgba8unorm",
174            Sf::Rgba8Snorm => "rgba8snorm",
175            Sf::Rgba8Uint => "rgba8uint",
176            Sf::Rgba8Sint => "rgba8sint",
177            Sf::Bgra8Unorm => "bgra8unorm",
178            Sf::Rgb10a2Uint => "rgb10a2uint",
179            Sf::Rgb10a2Unorm => "rgb10a2unorm",
180            Sf::Rg11b10Ufloat => "rg11b10float",
181            Sf::R64Uint => "r64uint",
182            Sf::Rg32Uint => "rg32uint",
183            Sf::Rg32Sint => "rg32sint",
184            Sf::Rg32Float => "rg32float",
185            Sf::Rgba16Uint => "rgba16uint",
186            Sf::Rgba16Sint => "rgba16sint",
187            Sf::Rgba16Float => "rgba16float",
188            Sf::Rgba32Uint => "rgba32uint",
189            Sf::Rgba32Sint => "rgba32sint",
190            Sf::Rgba32Float => "rgba32float",
191            Sf::R16Unorm => "r16unorm",
192            Sf::R16Snorm => "r16snorm",
193            Sf::Rg16Unorm => "rg16unorm",
194            Sf::Rg16Snorm => "rg16snorm",
195            Sf::Rgba16Unorm => "rgba16unorm",
196            Sf::Rgba16Snorm => "rgba16snorm",
197        }
198    }
199}
200
201mod tests {
202    #[test]
203    fn to_wgsl() {
204        use std::num::NonZeroU32;
205
206        let mut types = crate::UniqueArena::new();
207
208        let mytype1 = types.insert(
209            crate::Type {
210                name: Some("MyType1".to_string()),
211                inner: crate::TypeInner::Struct {
212                    members: vec![],
213                    span: 0,
214                },
215            },
216            Default::default(),
217        );
218        let mytype2 = types.insert(
219            crate::Type {
220                name: Some("MyType2".to_string()),
221                inner: crate::TypeInner::Struct {
222                    members: vec![],
223                    span: 0,
224                },
225            },
226            Default::default(),
227        );
228
229        let gctx = crate::proc::GlobalCtx {
230            types: &types,
231            constants: &crate::Arena::new(),
232            overrides: &crate::Arena::new(),
233            global_expressions: &crate::Arena::new(),
234        };
235        let array = crate::TypeInner::Array {
236            base: mytype1,
237            stride: 4,
238            size: crate::ArraySize::Constant(unsafe { NonZeroU32::new_unchecked(32) }),
239        };
240        assert_eq!(array.to_wgsl(&gctx), "array<MyType1, 32>");
241
242        let mat = crate::TypeInner::Matrix {
243            rows: crate::VectorSize::Quad,
244            columns: crate::VectorSize::Bi,
245            scalar: crate::Scalar::F64,
246        };
247        assert_eq!(mat.to_wgsl(&gctx), "mat2x4<f64>");
248
249        let ptr = crate::TypeInner::Pointer {
250            base: mytype2,
251            space: crate::AddressSpace::Storage {
252                access: crate::StorageAccess::default(),
253            },
254        };
255        assert_eq!(ptr.to_wgsl(&gctx), "ptr<MyType2>");
256
257        let img1 = crate::TypeInner::Image {
258            dim: crate::ImageDimension::D2,
259            arrayed: false,
260            class: crate::ImageClass::Sampled {
261                kind: crate::ScalarKind::Float,
262                multi: true,
263            },
264        };
265        assert_eq!(img1.to_wgsl(&gctx), "texture_multisampled_2d<f32>");
266
267        let img2 = crate::TypeInner::Image {
268            dim: crate::ImageDimension::Cube,
269            arrayed: true,
270            class: crate::ImageClass::Depth { multi: false },
271        };
272        assert_eq!(img2.to_wgsl(&gctx), "texture_depth_cube_array");
273
274        let img3 = crate::TypeInner::Image {
275            dim: crate::ImageDimension::D2,
276            arrayed: false,
277            class: crate::ImageClass::Depth { multi: true },
278        };
279        assert_eq!(img3.to_wgsl(&gctx), "texture_depth_multisampled_2d");
280
281        let array = crate::TypeInner::BindingArray {
282            base: mytype1,
283            size: crate::ArraySize::Constant(unsafe { NonZeroU32::new_unchecked(32) }),
284        };
285        assert_eq!(array.to_wgsl(&gctx), "binding_array<MyType1, 32>");
286    }
287}