naga/front/wgsl/
mod.rs

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