naga/back/wgsl/
writer.rs

1use alloc::{
2    format,
3    string::{String, ToString},
4    vec,
5    vec::Vec,
6};
7use core::fmt::Write;
8
9use super::Error;
10use super::ToWgslIfImplemented as _;
11use crate::{back::wgsl::polyfill::InversePolyfill, common::wgsl::TypeContext};
12use crate::{
13    back::{self, Baked},
14    common::{
15        self,
16        wgsl::{address_space_str, ToWgsl, TryToWgsl},
17    },
18    proc::{self, NameKey},
19    valid, Handle, Module, ShaderStage, TypeInner,
20};
21
22/// Shorthand result used internally by the backend
23type BackendResult = Result<(), Error>;
24
25/// WGSL [attribute](https://gpuweb.github.io/gpuweb/wgsl/#attributes)
26enum Attribute {
27    Binding(u32),
28    BuiltIn(crate::BuiltIn),
29    Group(u32),
30    Invariant,
31    Interpolate(Option<crate::Interpolation>, Option<crate::Sampling>),
32    Location(u32),
33    BlendSrc(u32),
34    Stage(ShaderStage),
35    WorkGroupSize([u32; 3]),
36}
37
38/// The WGSL form that `write_expr_with_indirection` should use to render a Naga
39/// expression.
40///
41/// Sometimes a Naga `Expression` alone doesn't provide enough information to
42/// choose the right rendering for it in WGSL. For example, one natural WGSL
43/// rendering of a Naga `LocalVariable(x)` expression might be `&x`, since
44/// `LocalVariable` produces a pointer to the local variable's storage. But when
45/// rendering a `Store` statement, the `pointer` operand must be the left hand
46/// side of a WGSL assignment, so the proper rendering is `x`.
47///
48/// The caller of `write_expr_with_indirection` must provide an `Expected` value
49/// to indicate how ambiguous expressions should be rendered.
50#[derive(Clone, Copy, Debug)]
51enum Indirection {
52    /// Render pointer-construction expressions as WGSL `ptr`-typed expressions.
53    ///
54    /// This is the right choice for most cases. Whenever a Naga pointer
55    /// expression is not the `pointer` operand of a `Load` or `Store`, it
56    /// must be a WGSL pointer expression.
57    Ordinary,
58
59    /// Render pointer-construction expressions as WGSL reference-typed
60    /// expressions.
61    ///
62    /// For example, this is the right choice for the `pointer` operand when
63    /// rendering a `Store` statement as a WGSL assignment.
64    Reference,
65}
66
67bitflags::bitflags! {
68    #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
69    #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
70    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
71    pub struct WriterFlags: u32 {
72        /// Always annotate the type information instead of inferring.
73        const EXPLICIT_TYPES = 0x1;
74    }
75}
76
77pub struct Writer<W> {
78    out: W,
79    flags: WriterFlags,
80    names: crate::FastHashMap<NameKey, String>,
81    namer: proc::Namer,
82    named_expressions: crate::NamedExpressions,
83    required_polyfills: crate::FastIndexSet<InversePolyfill>,
84}
85
86impl<W: Write> Writer<W> {
87    pub fn new(out: W, flags: WriterFlags) -> Self {
88        Writer {
89            out,
90            flags,
91            names: crate::FastHashMap::default(),
92            namer: proc::Namer::default(),
93            named_expressions: crate::NamedExpressions::default(),
94            required_polyfills: crate::FastIndexSet::default(),
95        }
96    }
97
98    fn reset(&mut self, module: &Module) {
99        self.names.clear();
100        self.namer.reset(
101            module,
102            &crate::keywords::wgsl::RESERVED_SET,
103            // an identifier must not start with two underscore
104            proc::CaseInsensitiveKeywordSet::empty(),
105            &["__", "_naga"],
106            &mut self.names,
107        );
108        self.named_expressions.clear();
109        self.required_polyfills.clear();
110    }
111
112    /// Determine if `ty` is the Naga IR presentation of a WGSL builtin type.
113    ///
114    /// Return true if `ty` refers to the Naga IR form of a WGSL builtin type
115    /// like `__atomic_compare_exchange_result`.
116    ///
117    /// Even though the module may use the type, the WGSL backend should avoid
118    /// emitting a definition for it, since it is [predeclared] in WGSL.
119    ///
120    /// This also covers types like [`NagaExternalTextureParams`], which other
121    /// backends use to lower WGSL constructs like external textures to their
122    /// implementations. WGSL can express these directly, so the types need not
123    /// be emitted.
124    ///
125    /// [predeclared]: https://www.w3.org/TR/WGSL/#predeclared
126    /// [`NagaExternalTextureParams`]: crate::ir::SpecialTypes::external_texture_params
127    fn is_builtin_wgsl_struct(&self, module: &Module, ty: Handle<crate::Type>) -> bool {
128        module
129            .special_types
130            .predeclared_types
131            .values()
132            .any(|t| *t == ty)
133            || Some(ty) == module.special_types.external_texture_params
134            || Some(ty) == module.special_types.external_texture_transfer_function
135    }
136
137    pub fn write(&mut self, module: &Module, info: &valid::ModuleInfo) -> BackendResult {
138        if !module.overrides.is_empty() {
139            return Err(Error::Unimplemented(
140                "Pipeline constants are not yet supported for this back-end".to_string(),
141            ));
142        }
143
144        self.reset(module);
145
146        // Write all `enable` declarations
147        self.write_enable_declarations(module)?;
148
149        // Write all structs
150        for (handle, ty) in module.types.iter() {
151            if let TypeInner::Struct { ref members, .. } = ty.inner {
152                {
153                    if !self.is_builtin_wgsl_struct(module, handle) {
154                        self.write_struct(module, handle, members)?;
155                        writeln!(self.out)?;
156                    }
157                }
158            }
159        }
160
161        // Write all named constants
162        let mut constants = module
163            .constants
164            .iter()
165            .filter(|&(_, c)| c.name.is_some())
166            .peekable();
167        while let Some((handle, _)) = constants.next() {
168            self.write_global_constant(module, handle)?;
169            // Add extra newline for readability on last iteration
170            if constants.peek().is_none() {
171                writeln!(self.out)?;
172            }
173        }
174
175        // Write all globals
176        for (ty, global) in module.global_variables.iter() {
177            self.write_global(module, global, ty)?;
178        }
179
180        if !module.global_variables.is_empty() {
181            // Add extra newline for readability
182            writeln!(self.out)?;
183        }
184
185        // Write all regular functions
186        for (handle, function) in module.functions.iter() {
187            let fun_info = &info[handle];
188
189            let func_ctx = back::FunctionCtx {
190                ty: back::FunctionType::Function(handle),
191                info: fun_info,
192                expressions: &function.expressions,
193                named_expressions: &function.named_expressions,
194            };
195
196            // Write the function
197            self.write_function(module, function, &func_ctx)?;
198
199            writeln!(self.out)?;
200        }
201
202        // Write all entry points
203        for (index, ep) in module.entry_points.iter().enumerate() {
204            let attributes = match ep.stage {
205                ShaderStage::Vertex | ShaderStage::Fragment => vec![Attribute::Stage(ep.stage)],
206                ShaderStage::Compute => vec![
207                    Attribute::Stage(ShaderStage::Compute),
208                    Attribute::WorkGroupSize(ep.workgroup_size),
209                ],
210                ShaderStage::Mesh | ShaderStage::Task => unreachable!(),
211            };
212
213            self.write_attributes(&attributes)?;
214            // Add a newline after attribute
215            writeln!(self.out)?;
216
217            let func_ctx = back::FunctionCtx {
218                ty: back::FunctionType::EntryPoint(index as u16),
219                info: info.get_entry_point(index),
220                expressions: &ep.function.expressions,
221                named_expressions: &ep.function.named_expressions,
222            };
223            self.write_function(module, &ep.function, &func_ctx)?;
224
225            if index < module.entry_points.len() - 1 {
226                writeln!(self.out)?;
227            }
228        }
229
230        // Write any polyfills that were required.
231        for polyfill in &self.required_polyfills {
232            writeln!(self.out)?;
233            write!(self.out, "{}", polyfill.source)?;
234            writeln!(self.out)?;
235        }
236
237        Ok(())
238    }
239
240    /// Helper method which writes all the `enable` declarations
241    /// needed for a module.
242    fn write_enable_declarations(&mut self, module: &Module) -> BackendResult {
243        let mut needs_f16 = false;
244        let mut needs_dual_source_blending = false;
245        let mut needs_clip_distances = false;
246
247        // Determine which `enable` declarations are needed
248        for (_, ty) in module.types.iter() {
249            match ty.inner {
250                TypeInner::Scalar(scalar)
251                | TypeInner::Vector { scalar, .. }
252                | TypeInner::Matrix { scalar, .. } => {
253                    needs_f16 |= scalar == crate::Scalar::F16;
254                }
255                TypeInner::Struct { ref members, .. } => {
256                    for binding in members.iter().filter_map(|m| m.binding.as_ref()) {
257                        match *binding {
258                            crate::Binding::Location {
259                                blend_src: Some(_), ..
260                            } => {
261                                needs_dual_source_blending = true;
262                            }
263                            crate::Binding::BuiltIn(crate::BuiltIn::ClipDistance) => {
264                                needs_clip_distances = true;
265                            }
266                            _ => {}
267                        }
268                    }
269                }
270                _ => {}
271            }
272        }
273
274        // Write required declarations
275        let mut any_written = false;
276        if needs_f16 {
277            writeln!(self.out, "enable f16;")?;
278            any_written = true;
279        }
280        if needs_dual_source_blending {
281            writeln!(self.out, "enable dual_source_blending;")?;
282            any_written = true;
283        }
284        if needs_clip_distances {
285            writeln!(self.out, "enable clip_distances;")?;
286            any_written = true;
287        }
288        if any_written {
289            // Empty line for readability
290            writeln!(self.out)?;
291        }
292
293        Ok(())
294    }
295
296    /// Helper method used to write
297    /// [functions](https://gpuweb.github.io/gpuweb/wgsl/#functions)
298    ///
299    /// # Notes
300    /// Ends in a newline
301    fn write_function(
302        &mut self,
303        module: &Module,
304        func: &crate::Function,
305        func_ctx: &back::FunctionCtx<'_>,
306    ) -> BackendResult {
307        let func_name = match func_ctx.ty {
308            back::FunctionType::EntryPoint(index) => &self.names[&NameKey::EntryPoint(index)],
309            back::FunctionType::Function(handle) => &self.names[&NameKey::Function(handle)],
310        };
311
312        // Write function name
313        write!(self.out, "fn {func_name}(")?;
314
315        // Write function arguments
316        for (index, arg) in func.arguments.iter().enumerate() {
317            // Write argument attribute if a binding is present
318            if let Some(ref binding) = arg.binding {
319                self.write_attributes(&map_binding_to_attribute(binding))?;
320            }
321            // Write argument name
322            let argument_name = &self.names[&func_ctx.argument_key(index as u32)];
323
324            write!(self.out, "{argument_name}: ")?;
325            // Write argument type
326            self.write_type(module, arg.ty)?;
327            if index < func.arguments.len() - 1 {
328                // Add a separator between args
329                write!(self.out, ", ")?;
330            }
331        }
332
333        write!(self.out, ")")?;
334
335        // Write function return type
336        if let Some(ref result) = func.result {
337            write!(self.out, " -> ")?;
338            if let Some(ref binding) = result.binding {
339                self.write_attributes(&map_binding_to_attribute(binding))?;
340            }
341            self.write_type(module, result.ty)?;
342        }
343
344        write!(self.out, " {{")?;
345        writeln!(self.out)?;
346
347        // Write function local variables
348        for (handle, local) in func.local_variables.iter() {
349            // Write indentation (only for readability)
350            write!(self.out, "{}", back::INDENT)?;
351
352            // Write the local name
353            // The leading space is important
354            write!(self.out, "var {}: ", self.names[&func_ctx.name_key(handle)])?;
355
356            // Write the local type
357            self.write_type(module, local.ty)?;
358
359            // Write the local initializer if needed
360            if let Some(init) = local.init {
361                // Put the equal signal only if there's a initializer
362                // The leading and trailing spaces aren't needed but help with readability
363                write!(self.out, " = ")?;
364
365                // Write the constant
366                // `write_constant` adds no trailing or leading space/newline
367                self.write_expr(module, init, func_ctx)?;
368            }
369
370            // Finish the local with `;` and add a newline (only for readability)
371            writeln!(self.out, ";")?
372        }
373
374        if !func.local_variables.is_empty() {
375            writeln!(self.out)?;
376        }
377
378        // Write the function body (statement list)
379        for sta in func.body.iter() {
380            // The indentation should always be 1 when writing the function body
381            self.write_stmt(module, sta, func_ctx, back::Level(1))?;
382        }
383
384        writeln!(self.out, "}}")?;
385
386        self.named_expressions.clear();
387
388        Ok(())
389    }
390
391    /// Helper method to write a attribute
392    fn write_attributes(&mut self, attributes: &[Attribute]) -> BackendResult {
393        for attribute in attributes {
394            match *attribute {
395                Attribute::Location(id) => write!(self.out, "@location({id}) ")?,
396                Attribute::BlendSrc(blend_src) => write!(self.out, "@blend_src({blend_src}) ")?,
397                Attribute::BuiltIn(builtin_attrib) => {
398                    let builtin = builtin_attrib.to_wgsl_if_implemented()?;
399                    write!(self.out, "@builtin({builtin}) ")?;
400                }
401                Attribute::Stage(shader_stage) => {
402                    let stage_str = match shader_stage {
403                        ShaderStage::Vertex => "vertex",
404                        ShaderStage::Fragment => "fragment",
405                        ShaderStage::Compute => "compute",
406                        ShaderStage::Task | ShaderStage::Mesh => unreachable!(),
407                    };
408                    write!(self.out, "@{stage_str} ")?;
409                }
410                Attribute::WorkGroupSize(size) => {
411                    write!(
412                        self.out,
413                        "@workgroup_size({}, {}, {}) ",
414                        size[0], size[1], size[2]
415                    )?;
416                }
417                Attribute::Binding(id) => write!(self.out, "@binding({id}) ")?,
418                Attribute::Group(id) => write!(self.out, "@group({id}) ")?,
419                Attribute::Invariant => write!(self.out, "@invariant ")?,
420                Attribute::Interpolate(interpolation, sampling) => {
421                    if sampling.is_some() && sampling != Some(crate::Sampling::Center) {
422                        let interpolation = interpolation
423                            .unwrap_or(crate::Interpolation::Perspective)
424                            .to_wgsl();
425                        let sampling = sampling.unwrap_or(crate::Sampling::Center).to_wgsl();
426                        write!(self.out, "@interpolate({interpolation}, {sampling}) ")?;
427                    } else if interpolation.is_some()
428                        && interpolation != Some(crate::Interpolation::Perspective)
429                    {
430                        let interpolation = interpolation
431                            .unwrap_or(crate::Interpolation::Perspective)
432                            .to_wgsl();
433                        write!(self.out, "@interpolate({interpolation}) ")?;
434                    }
435                }
436            };
437        }
438        Ok(())
439    }
440
441    /// Helper method used to write structs
442    /// Write the full declaration of a struct type.
443    ///
444    /// Write out a definition of the struct type referred to by
445    /// `handle` in `module`. The output will be an instance of the
446    /// `struct_decl` production in the WGSL grammar.
447    ///
448    /// Use `members` as the list of `handle`'s members. (This
449    /// function is usually called after matching a `TypeInner`, so
450    /// the callers already have the members at hand.)
451    fn write_struct(
452        &mut self,
453        module: &Module,
454        handle: Handle<crate::Type>,
455        members: &[crate::StructMember],
456    ) -> BackendResult {
457        write!(self.out, "struct {}", self.names[&NameKey::Type(handle)])?;
458        write!(self.out, " {{")?;
459        writeln!(self.out)?;
460        for (index, member) in members.iter().enumerate() {
461            // The indentation is only for readability
462            write!(self.out, "{}", back::INDENT)?;
463            if let Some(ref binding) = member.binding {
464                self.write_attributes(&map_binding_to_attribute(binding))?;
465            }
466            // Write struct member name and type
467            let member_name = &self.names[&NameKey::StructMember(handle, index as u32)];
468            write!(self.out, "{member_name}: ")?;
469            self.write_type(module, member.ty)?;
470            write!(self.out, ",")?;
471            writeln!(self.out)?;
472        }
473
474        writeln!(self.out, "}}")?;
475
476        Ok(())
477    }
478
479    fn write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult {
480        // This actually can't be factored out into a nice constructor method,
481        // because the borrow checker needs to be able to see that the borrows
482        // of `self.names` and `self.out` are disjoint.
483        let type_context = WriterTypeContext {
484            module,
485            names: &self.names,
486        };
487        type_context.write_type(ty, &mut self.out)?;
488
489        Ok(())
490    }
491
492    fn write_type_resolution(
493        &mut self,
494        module: &Module,
495        resolution: &proc::TypeResolution,
496    ) -> BackendResult {
497        // This actually can't be factored out into a nice constructor method,
498        // because the borrow checker needs to be able to see that the borrows
499        // of `self.names` and `self.out` are disjoint.
500        let type_context = WriterTypeContext {
501            module,
502            names: &self.names,
503        };
504        type_context.write_type_resolution(resolution, &mut self.out)?;
505
506        Ok(())
507    }
508
509    /// Helper method used to write statements
510    ///
511    /// # Notes
512    /// Always adds a newline
513    fn write_stmt(
514        &mut self,
515        module: &Module,
516        stmt: &crate::Statement,
517        func_ctx: &back::FunctionCtx<'_>,
518        level: back::Level,
519    ) -> BackendResult {
520        use crate::{Expression, Statement};
521
522        match *stmt {
523            Statement::Emit(ref range) => {
524                for handle in range.clone() {
525                    let info = &func_ctx.info[handle];
526                    let expr_name = if let Some(name) = func_ctx.named_expressions.get(&handle) {
527                        // Front end provides names for all variables at the start of writing.
528                        // But we write them to step by step. We need to recache them
529                        // Otherwise, we could accidentally write variable name instead of full expression.
530                        // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords.
531                        Some(self.namer.call(name))
532                    } else {
533                        let expr = &func_ctx.expressions[handle];
534                        let min_ref_count = expr.bake_ref_count();
535                        // Forcefully creating baking expressions in some cases to help with readability
536                        let required_baking_expr = match *expr {
537                            Expression::ImageLoad { .. }
538                            | Expression::ImageQuery { .. }
539                            | Expression::ImageSample { .. } => true,
540                            _ => false,
541                        };
542                        if min_ref_count <= info.ref_count || required_baking_expr {
543                            Some(Baked(handle).to_string())
544                        } else {
545                            None
546                        }
547                    };
548
549                    if let Some(name) = expr_name {
550                        write!(self.out, "{level}")?;
551                        self.start_named_expr(module, handle, func_ctx, &name)?;
552                        self.write_expr(module, handle, func_ctx)?;
553                        self.named_expressions.insert(handle, name);
554                        writeln!(self.out, ";")?;
555                    }
556                }
557            }
558            // TODO: copy-paste from glsl-out
559            Statement::If {
560                condition,
561                ref accept,
562                ref reject,
563            } => {
564                write!(self.out, "{level}")?;
565                write!(self.out, "if ")?;
566                self.write_expr(module, condition, func_ctx)?;
567                writeln!(self.out, " {{")?;
568
569                let l2 = level.next();
570                for sta in accept {
571                    // Increase indentation to help with readability
572                    self.write_stmt(module, sta, func_ctx, l2)?;
573                }
574
575                // If there are no statements in the reject block we skip writing it
576                // This is only for readability
577                if !reject.is_empty() {
578                    writeln!(self.out, "{level}}} else {{")?;
579
580                    for sta in reject {
581                        // Increase indentation to help with readability
582                        self.write_stmt(module, sta, func_ctx, l2)?;
583                    }
584                }
585
586                writeln!(self.out, "{level}}}")?
587            }
588            Statement::Return { value } => {
589                write!(self.out, "{level}")?;
590                write!(self.out, "return")?;
591                if let Some(return_value) = value {
592                    // The leading space is important
593                    write!(self.out, " ")?;
594                    self.write_expr(module, return_value, func_ctx)?;
595                }
596                writeln!(self.out, ";")?;
597            }
598            // TODO: copy-paste from glsl-out
599            Statement::Kill => {
600                write!(self.out, "{level}")?;
601                writeln!(self.out, "discard;")?
602            }
603            Statement::Store { pointer, value } => {
604                write!(self.out, "{level}")?;
605
606                let is_atomic_pointer = func_ctx
607                    .resolve_type(pointer, &module.types)
608                    .is_atomic_pointer(&module.types);
609
610                if is_atomic_pointer {
611                    write!(self.out, "atomicStore(")?;
612                    self.write_expr(module, pointer, func_ctx)?;
613                    write!(self.out, ", ")?;
614                    self.write_expr(module, value, func_ctx)?;
615                    write!(self.out, ")")?;
616                } else {
617                    self.write_expr_with_indirection(
618                        module,
619                        pointer,
620                        func_ctx,
621                        Indirection::Reference,
622                    )?;
623                    write!(self.out, " = ")?;
624                    self.write_expr(module, value, func_ctx)?;
625                }
626                writeln!(self.out, ";")?
627            }
628            Statement::Call {
629                function,
630                ref arguments,
631                result,
632            } => {
633                write!(self.out, "{level}")?;
634                if let Some(expr) = result {
635                    let name = Baked(expr).to_string();
636                    self.start_named_expr(module, expr, func_ctx, &name)?;
637                    self.named_expressions.insert(expr, name);
638                }
639                let func_name = &self.names[&NameKey::Function(function)];
640                write!(self.out, "{func_name}(")?;
641                for (index, &argument) in arguments.iter().enumerate() {
642                    if index != 0 {
643                        write!(self.out, ", ")?;
644                    }
645                    self.write_expr(module, argument, func_ctx)?;
646                }
647                writeln!(self.out, ");")?
648            }
649            Statement::Atomic {
650                pointer,
651                ref fun,
652                value,
653                result,
654            } => {
655                write!(self.out, "{level}")?;
656                if let Some(result) = result {
657                    let res_name = Baked(result).to_string();
658                    self.start_named_expr(module, result, func_ctx, &res_name)?;
659                    self.named_expressions.insert(result, res_name);
660                }
661
662                let fun_str = fun.to_wgsl();
663                write!(self.out, "atomic{fun_str}(")?;
664                self.write_expr(module, pointer, func_ctx)?;
665                if let crate::AtomicFunction::Exchange { compare: Some(cmp) } = *fun {
666                    write!(self.out, ", ")?;
667                    self.write_expr(module, cmp, func_ctx)?;
668                }
669                write!(self.out, ", ")?;
670                self.write_expr(module, value, func_ctx)?;
671                writeln!(self.out, ");")?
672            }
673            Statement::ImageAtomic {
674                image,
675                coordinate,
676                array_index,
677                ref fun,
678                value,
679            } => {
680                write!(self.out, "{level}")?;
681                let fun_str = fun.to_wgsl();
682                write!(self.out, "textureAtomic{fun_str}(")?;
683                self.write_expr(module, image, func_ctx)?;
684                write!(self.out, ", ")?;
685                self.write_expr(module, coordinate, func_ctx)?;
686                if let Some(array_index_expr) = array_index {
687                    write!(self.out, ", ")?;
688                    self.write_expr(module, array_index_expr, func_ctx)?;
689                }
690                write!(self.out, ", ")?;
691                self.write_expr(module, value, func_ctx)?;
692                writeln!(self.out, ");")?;
693            }
694            Statement::WorkGroupUniformLoad { pointer, result } => {
695                write!(self.out, "{level}")?;
696                // TODO: Obey named expressions here.
697                let res_name = Baked(result).to_string();
698                self.start_named_expr(module, result, func_ctx, &res_name)?;
699                self.named_expressions.insert(result, res_name);
700                write!(self.out, "workgroupUniformLoad(")?;
701                self.write_expr(module, pointer, func_ctx)?;
702                writeln!(self.out, ");")?;
703            }
704            Statement::ImageStore {
705                image,
706                coordinate,
707                array_index,
708                value,
709            } => {
710                write!(self.out, "{level}")?;
711                write!(self.out, "textureStore(")?;
712                self.write_expr(module, image, func_ctx)?;
713                write!(self.out, ", ")?;
714                self.write_expr(module, coordinate, func_ctx)?;
715                if let Some(array_index_expr) = array_index {
716                    write!(self.out, ", ")?;
717                    self.write_expr(module, array_index_expr, func_ctx)?;
718                }
719                write!(self.out, ", ")?;
720                self.write_expr(module, value, func_ctx)?;
721                writeln!(self.out, ");")?;
722            }
723            // TODO: copy-paste from glsl-out
724            Statement::Block(ref block) => {
725                write!(self.out, "{level}")?;
726                writeln!(self.out, "{{")?;
727                for sta in block.iter() {
728                    // Increase the indentation to help with readability
729                    self.write_stmt(module, sta, func_ctx, level.next())?
730                }
731                writeln!(self.out, "{level}}}")?
732            }
733            Statement::Switch {
734                selector,
735                ref cases,
736            } => {
737                // Start the switch
738                write!(self.out, "{level}")?;
739                write!(self.out, "switch ")?;
740                self.write_expr(module, selector, func_ctx)?;
741                writeln!(self.out, " {{")?;
742
743                let l2 = level.next();
744                let mut new_case = true;
745                for case in cases {
746                    if case.fall_through && !case.body.is_empty() {
747                        // TODO: we could do the same workaround as we did for the HLSL backend
748                        return Err(Error::Unimplemented(
749                            "fall-through switch case block".into(),
750                        ));
751                    }
752
753                    match case.value {
754                        crate::SwitchValue::I32(value) => {
755                            if new_case {
756                                write!(self.out, "{l2}case ")?;
757                            }
758                            write!(self.out, "{value}")?;
759                        }
760                        crate::SwitchValue::U32(value) => {
761                            if new_case {
762                                write!(self.out, "{l2}case ")?;
763                            }
764                            write!(self.out, "{value}u")?;
765                        }
766                        crate::SwitchValue::Default => {
767                            if new_case {
768                                if case.fall_through {
769                                    write!(self.out, "{l2}case ")?;
770                                } else {
771                                    write!(self.out, "{l2}")?;
772                                }
773                            }
774                            write!(self.out, "default")?;
775                        }
776                    }
777
778                    new_case = !case.fall_through;
779
780                    if case.fall_through {
781                        write!(self.out, ", ")?;
782                    } else {
783                        writeln!(self.out, ": {{")?;
784                    }
785
786                    for sta in case.body.iter() {
787                        self.write_stmt(module, sta, func_ctx, l2.next())?;
788                    }
789
790                    if !case.fall_through {
791                        writeln!(self.out, "{l2}}}")?;
792                    }
793                }
794
795                writeln!(self.out, "{level}}}")?
796            }
797            Statement::Loop {
798                ref body,
799                ref continuing,
800                break_if,
801            } => {
802                write!(self.out, "{level}")?;
803                writeln!(self.out, "loop {{")?;
804
805                let l2 = level.next();
806                for sta in body.iter() {
807                    self.write_stmt(module, sta, func_ctx, l2)?;
808                }
809
810                // The continuing is optional so we don't need to write it if
811                // it is empty, but the `break if` counts as a continuing statement
812                // so even if `continuing` is empty we must generate it if a
813                // `break if` exists
814                if !continuing.is_empty() || break_if.is_some() {
815                    writeln!(self.out, "{l2}continuing {{")?;
816                    for sta in continuing.iter() {
817                        self.write_stmt(module, sta, func_ctx, l2.next())?;
818                    }
819
820                    // The `break if` is always the last
821                    // statement of the `continuing` block
822                    if let Some(condition) = break_if {
823                        // The trailing space is important
824                        write!(self.out, "{}break if ", l2.next())?;
825                        self.write_expr(module, condition, func_ctx)?;
826                        // Close the `break if` statement
827                        writeln!(self.out, ";")?;
828                    }
829
830                    writeln!(self.out, "{l2}}}")?;
831                }
832
833                writeln!(self.out, "{level}}}")?
834            }
835            Statement::Break => {
836                writeln!(self.out, "{level}break;")?;
837            }
838            Statement::Continue => {
839                writeln!(self.out, "{level}continue;")?;
840            }
841            Statement::ControlBarrier(barrier) | Statement::MemoryBarrier(barrier) => {
842                if barrier.contains(crate::Barrier::STORAGE) {
843                    writeln!(self.out, "{level}storageBarrier();")?;
844                }
845
846                if barrier.contains(crate::Barrier::WORK_GROUP) {
847                    writeln!(self.out, "{level}workgroupBarrier();")?;
848                }
849
850                if barrier.contains(crate::Barrier::SUB_GROUP) {
851                    writeln!(self.out, "{level}subgroupBarrier();")?;
852                }
853
854                if barrier.contains(crate::Barrier::TEXTURE) {
855                    writeln!(self.out, "{level}textureBarrier();")?;
856                }
857            }
858            Statement::RayQuery { .. } => unreachable!(),
859            Statement::MeshFunction(..) => unreachable!(),
860            Statement::SubgroupBallot { result, predicate } => {
861                write!(self.out, "{level}")?;
862                let res_name = Baked(result).to_string();
863                self.start_named_expr(module, result, func_ctx, &res_name)?;
864                self.named_expressions.insert(result, res_name);
865
866                write!(self.out, "subgroupBallot(")?;
867                if let Some(predicate) = predicate {
868                    self.write_expr(module, predicate, func_ctx)?;
869                }
870                writeln!(self.out, ");")?;
871            }
872            Statement::SubgroupCollectiveOperation {
873                op,
874                collective_op,
875                argument,
876                result,
877            } => {
878                write!(self.out, "{level}")?;
879                let res_name = Baked(result).to_string();
880                self.start_named_expr(module, result, func_ctx, &res_name)?;
881                self.named_expressions.insert(result, res_name);
882
883                match (collective_op, op) {
884                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {
885                        write!(self.out, "subgroupAll(")?
886                    }
887                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {
888                        write!(self.out, "subgroupAny(")?
889                    }
890                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {
891                        write!(self.out, "subgroupAdd(")?
892                    }
893                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {
894                        write!(self.out, "subgroupMul(")?
895                    }
896                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {
897                        write!(self.out, "subgroupMax(")?
898                    }
899                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {
900                        write!(self.out, "subgroupMin(")?
901                    }
902                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {
903                        write!(self.out, "subgroupAnd(")?
904                    }
905                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {
906                        write!(self.out, "subgroupOr(")?
907                    }
908                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {
909                        write!(self.out, "subgroupXor(")?
910                    }
911                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {
912                        write!(self.out, "subgroupExclusiveAdd(")?
913                    }
914                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {
915                        write!(self.out, "subgroupExclusiveMul(")?
916                    }
917                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {
918                        write!(self.out, "subgroupInclusiveAdd(")?
919                    }
920                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {
921                        write!(self.out, "subgroupInclusiveMul(")?
922                    }
923                    _ => unimplemented!(),
924                }
925                self.write_expr(module, argument, func_ctx)?;
926                writeln!(self.out, ");")?;
927            }
928            Statement::SubgroupGather {
929                mode,
930                argument,
931                result,
932            } => {
933                write!(self.out, "{level}")?;
934                let res_name = Baked(result).to_string();
935                self.start_named_expr(module, result, func_ctx, &res_name)?;
936                self.named_expressions.insert(result, res_name);
937
938                match mode {
939                    crate::GatherMode::BroadcastFirst => {
940                        write!(self.out, "subgroupBroadcastFirst(")?;
941                    }
942                    crate::GatherMode::Broadcast(_) => {
943                        write!(self.out, "subgroupBroadcast(")?;
944                    }
945                    crate::GatherMode::Shuffle(_) => {
946                        write!(self.out, "subgroupShuffle(")?;
947                    }
948                    crate::GatherMode::ShuffleDown(_) => {
949                        write!(self.out, "subgroupShuffleDown(")?;
950                    }
951                    crate::GatherMode::ShuffleUp(_) => {
952                        write!(self.out, "subgroupShuffleUp(")?;
953                    }
954                    crate::GatherMode::ShuffleXor(_) => {
955                        write!(self.out, "subgroupShuffleXor(")?;
956                    }
957                    crate::GatherMode::QuadBroadcast(_) => {
958                        write!(self.out, "quadBroadcast(")?;
959                    }
960                    crate::GatherMode::QuadSwap(direction) => match direction {
961                        crate::Direction::X => {
962                            write!(self.out, "quadSwapX(")?;
963                        }
964                        crate::Direction::Y => {
965                            write!(self.out, "quadSwapY(")?;
966                        }
967                        crate::Direction::Diagonal => {
968                            write!(self.out, "quadSwapDiagonal(")?;
969                        }
970                    },
971                }
972                self.write_expr(module, argument, func_ctx)?;
973                match mode {
974                    crate::GatherMode::BroadcastFirst => {}
975                    crate::GatherMode::Broadcast(index)
976                    | crate::GatherMode::Shuffle(index)
977                    | crate::GatherMode::ShuffleDown(index)
978                    | crate::GatherMode::ShuffleUp(index)
979                    | crate::GatherMode::ShuffleXor(index)
980                    | crate::GatherMode::QuadBroadcast(index) => {
981                        write!(self.out, ", ")?;
982                        self.write_expr(module, index, func_ctx)?;
983                    }
984                    crate::GatherMode::QuadSwap(_) => {}
985                }
986                writeln!(self.out, ");")?;
987            }
988        }
989
990        Ok(())
991    }
992
993    /// Return the sort of indirection that `expr`'s plain form evaluates to.
994    ///
995    /// An expression's 'plain form' is the most general rendition of that
996    /// expression into WGSL, lacking `&` or `*` operators:
997    ///
998    /// - The plain form of `LocalVariable(x)` is simply `x`, which is a reference
999    ///   to the local variable's storage.
1000    ///
1001    /// - The plain form of `GlobalVariable(g)` is simply `g`, which is usually a
1002    ///   reference to the global variable's storage. However, globals in the
1003    ///   `Handle` address space are immutable, and `GlobalVariable` expressions for
1004    ///   those produce the value directly, not a pointer to it. Such
1005    ///   `GlobalVariable` expressions are `Ordinary`.
1006    ///
1007    /// - `Access` and `AccessIndex` are `Reference` when their `base` operand is a
1008    ///   pointer. If they are applied directly to a composite value, they are
1009    ///   `Ordinary`.
1010    ///
1011    /// Note that `FunctionArgument` expressions are never `Reference`, even when
1012    /// the argument's type is `Pointer`. `FunctionArgument` always evaluates to the
1013    /// argument's value directly, so any pointer it produces is merely the value
1014    /// passed by the caller.
1015    fn plain_form_indirection(
1016        &self,
1017        expr: Handle<crate::Expression>,
1018        module: &Module,
1019        func_ctx: &back::FunctionCtx<'_>,
1020    ) -> Indirection {
1021        use crate::Expression as Ex;
1022
1023        // Named expressions are `let` expressions, which apply the Load Rule,
1024        // so if their type is a Naga pointer, then that must be a WGSL pointer
1025        // as well.
1026        if self.named_expressions.contains_key(&expr) {
1027            return Indirection::Ordinary;
1028        }
1029
1030        match func_ctx.expressions[expr] {
1031            Ex::LocalVariable(_) => Indirection::Reference,
1032            Ex::GlobalVariable(handle) => {
1033                let global = &module.global_variables[handle];
1034                match global.space {
1035                    crate::AddressSpace::Handle => Indirection::Ordinary,
1036                    _ => Indirection::Reference,
1037                }
1038            }
1039            Ex::Access { base, .. } | Ex::AccessIndex { base, .. } => {
1040                let base_ty = func_ctx.resolve_type(base, &module.types);
1041                match *base_ty {
1042                    TypeInner::Pointer { .. } | TypeInner::ValuePointer { .. } => {
1043                        Indirection::Reference
1044                    }
1045                    _ => Indirection::Ordinary,
1046                }
1047            }
1048            _ => Indirection::Ordinary,
1049        }
1050    }
1051
1052    fn start_named_expr(
1053        &mut self,
1054        module: &Module,
1055        handle: Handle<crate::Expression>,
1056        func_ctx: &back::FunctionCtx,
1057        name: &str,
1058    ) -> BackendResult {
1059        // Write variable name
1060        write!(self.out, "let {name}")?;
1061        if self.flags.contains(WriterFlags::EXPLICIT_TYPES) {
1062            write!(self.out, ": ")?;
1063            // Write variable type
1064            self.write_type_resolution(module, &func_ctx.info[handle].ty)?;
1065        }
1066
1067        write!(self.out, " = ")?;
1068        Ok(())
1069    }
1070
1071    /// Write the ordinary WGSL form of `expr`.
1072    ///
1073    /// See `write_expr_with_indirection` for details.
1074    fn write_expr(
1075        &mut self,
1076        module: &Module,
1077        expr: Handle<crate::Expression>,
1078        func_ctx: &back::FunctionCtx<'_>,
1079    ) -> BackendResult {
1080        self.write_expr_with_indirection(module, expr, func_ctx, Indirection::Ordinary)
1081    }
1082
1083    /// Write `expr` as a WGSL expression with the requested indirection.
1084    ///
1085    /// In terms of the WGSL grammar, the resulting expression is a
1086    /// `singular_expression`. It may be parenthesized. This makes it suitable
1087    /// for use as the operand of a unary or binary operator without worrying
1088    /// about precedence.
1089    ///
1090    /// This does not produce newlines or indentation.
1091    ///
1092    /// The `requested` argument indicates (roughly) whether Naga
1093    /// `Pointer`-valued expressions represent WGSL references or pointers. See
1094    /// `Indirection` for details.
1095    fn write_expr_with_indirection(
1096        &mut self,
1097        module: &Module,
1098        expr: Handle<crate::Expression>,
1099        func_ctx: &back::FunctionCtx<'_>,
1100        requested: Indirection,
1101    ) -> BackendResult {
1102        // If the plain form of the expression is not what we need, emit the
1103        // operator necessary to correct that.
1104        let plain = self.plain_form_indirection(expr, module, func_ctx);
1105        match (requested, plain) {
1106            (Indirection::Ordinary, Indirection::Reference) => {
1107                write!(self.out, "(&")?;
1108                self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1109                write!(self.out, ")")?;
1110            }
1111            (Indirection::Reference, Indirection::Ordinary) => {
1112                write!(self.out, "(*")?;
1113                self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1114                write!(self.out, ")")?;
1115            }
1116            (_, _) => self.write_expr_plain_form(module, expr, func_ctx, plain)?,
1117        }
1118
1119        Ok(())
1120    }
1121
1122    fn write_const_expression(
1123        &mut self,
1124        module: &Module,
1125        expr: Handle<crate::Expression>,
1126        arena: &crate::Arena<crate::Expression>,
1127    ) -> BackendResult {
1128        self.write_possibly_const_expression(module, expr, arena, |writer, expr| {
1129            writer.write_const_expression(module, expr, arena)
1130        })
1131    }
1132
1133    fn write_possibly_const_expression<E>(
1134        &mut self,
1135        module: &Module,
1136        expr: Handle<crate::Expression>,
1137        expressions: &crate::Arena<crate::Expression>,
1138        write_expression: E,
1139    ) -> BackendResult
1140    where
1141        E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,
1142    {
1143        use crate::Expression;
1144
1145        match expressions[expr] {
1146            Expression::Literal(literal) => match literal {
1147                crate::Literal::F16(value) => write!(self.out, "{value}h")?,
1148                crate::Literal::F32(value) => write!(self.out, "{value}f")?,
1149                crate::Literal::U32(value) => write!(self.out, "{value}u")?,
1150                crate::Literal::I32(value) => {
1151                    // `-2147483648i` is not valid WGSL. The most negative `i32`
1152                    // value can only be expressed in WGSL using AbstractInt and
1153                    // a unary negation operator.
1154                    if value == i32::MIN {
1155                        write!(self.out, "i32({value})")?;
1156                    } else {
1157                        write!(self.out, "{value}i")?;
1158                    }
1159                }
1160                crate::Literal::Bool(value) => write!(self.out, "{value}")?,
1161                crate::Literal::F64(value) => write!(self.out, "{value:?}lf")?,
1162                crate::Literal::I64(value) => {
1163                    // `-9223372036854775808li` is not valid WGSL. Nor can we simply use the
1164                    // AbstractInt trick above, as AbstractInt also cannot represent
1165                    // `9223372036854775808`. Instead construct the second most negative
1166                    // AbstractInt, subtract one from it, then cast to i64.
1167                    if value == i64::MIN {
1168                        write!(self.out, "i64({} - 1)", value + 1)?;
1169                    } else {
1170                        write!(self.out, "{value}li")?;
1171                    }
1172                }
1173                crate::Literal::U64(value) => write!(self.out, "{value:?}lu")?,
1174                crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
1175                    return Err(Error::Custom(
1176                        "Abstract types should not appear in IR presented to backends".into(),
1177                    ));
1178                }
1179            },
1180            Expression::Constant(handle) => {
1181                let constant = &module.constants[handle];
1182                if constant.name.is_some() {
1183                    write!(self.out, "{}", self.names[&NameKey::Constant(handle)])?;
1184                } else {
1185                    self.write_const_expression(module, constant.init, &module.global_expressions)?;
1186                }
1187            }
1188            Expression::ZeroValue(ty) => {
1189                self.write_type(module, ty)?;
1190                write!(self.out, "()")?;
1191            }
1192            Expression::Compose { ty, ref components } => {
1193                self.write_type(module, ty)?;
1194                write!(self.out, "(")?;
1195                for (index, component) in components.iter().enumerate() {
1196                    if index != 0 {
1197                        write!(self.out, ", ")?;
1198                    }
1199                    write_expression(self, *component)?;
1200                }
1201                write!(self.out, ")")?
1202            }
1203            Expression::Splat { size, value } => {
1204                let size = common::vector_size_str(size);
1205                write!(self.out, "vec{size}(")?;
1206                write_expression(self, value)?;
1207                write!(self.out, ")")?;
1208            }
1209            _ => unreachable!(),
1210        }
1211
1212        Ok(())
1213    }
1214
1215    /// Write the 'plain form' of `expr`.
1216    ///
1217    /// An expression's 'plain form' is the most general rendition of that
1218    /// expression into WGSL, lacking `&` or `*` operators. The plain forms of
1219    /// `LocalVariable(x)` and `GlobalVariable(g)` are simply `x` and `g`. Such
1220    /// Naga expressions represent both WGSL pointers and references; it's the
1221    /// caller's responsibility to distinguish those cases appropriately.
1222    fn write_expr_plain_form(
1223        &mut self,
1224        module: &Module,
1225        expr: Handle<crate::Expression>,
1226        func_ctx: &back::FunctionCtx<'_>,
1227        indirection: Indirection,
1228    ) -> BackendResult {
1229        use crate::Expression;
1230
1231        if let Some(name) = self.named_expressions.get(&expr) {
1232            write!(self.out, "{name}")?;
1233            return Ok(());
1234        }
1235
1236        let expression = &func_ctx.expressions[expr];
1237
1238        // Write the plain WGSL form of a Naga expression.
1239        //
1240        // The plain form of `LocalVariable` and `GlobalVariable` expressions is
1241        // simply the variable name; `*` and `&` operators are never emitted.
1242        //
1243        // The plain form of `Access` and `AccessIndex` expressions are WGSL
1244        // `postfix_expression` forms for member/component access and
1245        // subscripting.
1246        match *expression {
1247            Expression::Literal(_)
1248            | Expression::Constant(_)
1249            | Expression::ZeroValue(_)
1250            | Expression::Compose { .. }
1251            | Expression::Splat { .. } => {
1252                self.write_possibly_const_expression(
1253                    module,
1254                    expr,
1255                    func_ctx.expressions,
1256                    |writer, expr| writer.write_expr(module, expr, func_ctx),
1257                )?;
1258            }
1259            Expression::Override(_) => unreachable!(),
1260            Expression::FunctionArgument(pos) => {
1261                let name_key = func_ctx.argument_key(pos);
1262                let name = &self.names[&name_key];
1263                write!(self.out, "{name}")?;
1264            }
1265            Expression::Binary { op, left, right } => {
1266                write!(self.out, "(")?;
1267                self.write_expr(module, left, func_ctx)?;
1268                write!(self.out, " {} ", back::binary_operation_str(op))?;
1269                self.write_expr(module, right, func_ctx)?;
1270                write!(self.out, ")")?;
1271            }
1272            Expression::Access { base, index } => {
1273                self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1274                write!(self.out, "[")?;
1275                self.write_expr(module, index, func_ctx)?;
1276                write!(self.out, "]")?
1277            }
1278            Expression::AccessIndex { base, index } => {
1279                let base_ty_res = &func_ctx.info[base].ty;
1280                let mut resolved = base_ty_res.inner_with(&module.types);
1281
1282                self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1283
1284                let base_ty_handle = match *resolved {
1285                    TypeInner::Pointer { base, space: _ } => {
1286                        resolved = &module.types[base].inner;
1287                        Some(base)
1288                    }
1289                    _ => base_ty_res.handle(),
1290                };
1291
1292                match *resolved {
1293                    TypeInner::Vector { .. } => {
1294                        // Write vector access as a swizzle
1295                        write!(self.out, ".{}", back::COMPONENTS[index as usize])?
1296                    }
1297                    TypeInner::Matrix { .. }
1298                    | TypeInner::Array { .. }
1299                    | TypeInner::BindingArray { .. }
1300                    | TypeInner::ValuePointer { .. } => write!(self.out, "[{index}]")?,
1301                    TypeInner::Struct { .. } => {
1302                        // This will never panic in case the type is a `Struct`, this is not true
1303                        // for other types so we can only check while inside this match arm
1304                        let ty = base_ty_handle.unwrap();
1305
1306                        write!(
1307                            self.out,
1308                            ".{}",
1309                            &self.names[&NameKey::StructMember(ty, index)]
1310                        )?
1311                    }
1312                    ref other => return Err(Error::Custom(format!("Cannot index {other:?}"))),
1313                }
1314            }
1315            Expression::ImageSample {
1316                image,
1317                sampler,
1318                gather: None,
1319                coordinate,
1320                array_index,
1321                offset,
1322                level,
1323                depth_ref,
1324                clamp_to_edge,
1325            } => {
1326                use crate::SampleLevel as Sl;
1327
1328                let suffix_cmp = match depth_ref {
1329                    Some(_) => "Compare",
1330                    None => "",
1331                };
1332                let suffix_level = match level {
1333                    Sl::Auto => "",
1334                    Sl::Zero if clamp_to_edge => "BaseClampToEdge",
1335                    Sl::Zero | Sl::Exact(_) => "Level",
1336                    Sl::Bias(_) => "Bias",
1337                    Sl::Gradient { .. } => "Grad",
1338                };
1339
1340                write!(self.out, "textureSample{suffix_cmp}{suffix_level}(")?;
1341                self.write_expr(module, image, func_ctx)?;
1342                write!(self.out, ", ")?;
1343                self.write_expr(module, sampler, func_ctx)?;
1344                write!(self.out, ", ")?;
1345                self.write_expr(module, coordinate, func_ctx)?;
1346
1347                if let Some(array_index) = array_index {
1348                    write!(self.out, ", ")?;
1349                    self.write_expr(module, array_index, func_ctx)?;
1350                }
1351
1352                if let Some(depth_ref) = depth_ref {
1353                    write!(self.out, ", ")?;
1354                    self.write_expr(module, depth_ref, func_ctx)?;
1355                }
1356
1357                match level {
1358                    Sl::Auto => {}
1359                    Sl::Zero => {
1360                        // Level 0 is implied for depth comparison and BaseClampToEdge
1361                        if depth_ref.is_none() && !clamp_to_edge {
1362                            write!(self.out, ", 0.0")?;
1363                        }
1364                    }
1365                    Sl::Exact(expr) => {
1366                        write!(self.out, ", ")?;
1367                        self.write_expr(module, expr, func_ctx)?;
1368                    }
1369                    Sl::Bias(expr) => {
1370                        write!(self.out, ", ")?;
1371                        self.write_expr(module, expr, func_ctx)?;
1372                    }
1373                    Sl::Gradient { x, y } => {
1374                        write!(self.out, ", ")?;
1375                        self.write_expr(module, x, func_ctx)?;
1376                        write!(self.out, ", ")?;
1377                        self.write_expr(module, y, func_ctx)?;
1378                    }
1379                }
1380
1381                if let Some(offset) = offset {
1382                    write!(self.out, ", ")?;
1383                    self.write_const_expression(module, offset, func_ctx.expressions)?;
1384                }
1385
1386                write!(self.out, ")")?;
1387            }
1388
1389            Expression::ImageSample {
1390                image,
1391                sampler,
1392                gather: Some(component),
1393                coordinate,
1394                array_index,
1395                offset,
1396                level: _,
1397                depth_ref,
1398                clamp_to_edge: _,
1399            } => {
1400                let suffix_cmp = match depth_ref {
1401                    Some(_) => "Compare",
1402                    None => "",
1403                };
1404
1405                write!(self.out, "textureGather{suffix_cmp}(")?;
1406                match *func_ctx.resolve_type(image, &module.types) {
1407                    TypeInner::Image {
1408                        class: crate::ImageClass::Depth { multi: _ },
1409                        ..
1410                    } => {}
1411                    _ => {
1412                        write!(self.out, "{}, ", component as u8)?;
1413                    }
1414                }
1415                self.write_expr(module, image, func_ctx)?;
1416                write!(self.out, ", ")?;
1417                self.write_expr(module, sampler, func_ctx)?;
1418                write!(self.out, ", ")?;
1419                self.write_expr(module, coordinate, func_ctx)?;
1420
1421                if let Some(array_index) = array_index {
1422                    write!(self.out, ", ")?;
1423                    self.write_expr(module, array_index, func_ctx)?;
1424                }
1425
1426                if let Some(depth_ref) = depth_ref {
1427                    write!(self.out, ", ")?;
1428                    self.write_expr(module, depth_ref, func_ctx)?;
1429                }
1430
1431                if let Some(offset) = offset {
1432                    write!(self.out, ", ")?;
1433                    self.write_const_expression(module, offset, func_ctx.expressions)?;
1434                }
1435
1436                write!(self.out, ")")?;
1437            }
1438            Expression::ImageQuery { image, query } => {
1439                use crate::ImageQuery as Iq;
1440
1441                let texture_function = match query {
1442                    Iq::Size { .. } => "textureDimensions",
1443                    Iq::NumLevels => "textureNumLevels",
1444                    Iq::NumLayers => "textureNumLayers",
1445                    Iq::NumSamples => "textureNumSamples",
1446                };
1447
1448                write!(self.out, "{texture_function}(")?;
1449                self.write_expr(module, image, func_ctx)?;
1450                if let Iq::Size { level: Some(level) } = query {
1451                    write!(self.out, ", ")?;
1452                    self.write_expr(module, level, func_ctx)?;
1453                };
1454                write!(self.out, ")")?;
1455            }
1456
1457            Expression::ImageLoad {
1458                image,
1459                coordinate,
1460                array_index,
1461                sample,
1462                level,
1463            } => {
1464                write!(self.out, "textureLoad(")?;
1465                self.write_expr(module, image, func_ctx)?;
1466                write!(self.out, ", ")?;
1467                self.write_expr(module, coordinate, func_ctx)?;
1468                if let Some(array_index) = array_index {
1469                    write!(self.out, ", ")?;
1470                    self.write_expr(module, array_index, func_ctx)?;
1471                }
1472                if let Some(index) = sample.or(level) {
1473                    write!(self.out, ", ")?;
1474                    self.write_expr(module, index, func_ctx)?;
1475                }
1476                write!(self.out, ")")?;
1477            }
1478            Expression::GlobalVariable(handle) => {
1479                let name = &self.names[&NameKey::GlobalVariable(handle)];
1480                write!(self.out, "{name}")?;
1481            }
1482
1483            Expression::As {
1484                expr,
1485                kind,
1486                convert,
1487            } => {
1488                let inner = func_ctx.resolve_type(expr, &module.types);
1489                match *inner {
1490                    TypeInner::Matrix {
1491                        columns,
1492                        rows,
1493                        scalar,
1494                    } => {
1495                        let scalar = crate::Scalar {
1496                            kind,
1497                            width: convert.unwrap_or(scalar.width),
1498                        };
1499                        let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1500                        write!(
1501                            self.out,
1502                            "mat{}x{}<{}>",
1503                            common::vector_size_str(columns),
1504                            common::vector_size_str(rows),
1505                            scalar_kind_str
1506                        )?;
1507                    }
1508                    TypeInner::Vector {
1509                        size,
1510                        scalar: crate::Scalar { width, .. },
1511                    } => {
1512                        let scalar = crate::Scalar {
1513                            kind,
1514                            width: convert.unwrap_or(width),
1515                        };
1516                        let vector_size_str = common::vector_size_str(size);
1517                        let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1518                        if convert.is_some() {
1519                            write!(self.out, "vec{vector_size_str}<{scalar_kind_str}>")?;
1520                        } else {
1521                            write!(self.out, "bitcast<vec{vector_size_str}<{scalar_kind_str}>>")?;
1522                        }
1523                    }
1524                    TypeInner::Scalar(crate::Scalar { width, .. }) => {
1525                        let scalar = crate::Scalar {
1526                            kind,
1527                            width: convert.unwrap_or(width),
1528                        };
1529                        let scalar_kind_str = scalar.to_wgsl_if_implemented()?;
1530                        if convert.is_some() {
1531                            write!(self.out, "{scalar_kind_str}")?
1532                        } else {
1533                            write!(self.out, "bitcast<{scalar_kind_str}>")?
1534                        }
1535                    }
1536                    _ => {
1537                        return Err(Error::Unimplemented(format!(
1538                            "write_expr expression::as {inner:?}"
1539                        )));
1540                    }
1541                };
1542                write!(self.out, "(")?;
1543                self.write_expr(module, expr, func_ctx)?;
1544                write!(self.out, ")")?;
1545            }
1546            Expression::Load { pointer } => {
1547                let is_atomic_pointer = func_ctx
1548                    .resolve_type(pointer, &module.types)
1549                    .is_atomic_pointer(&module.types);
1550
1551                if is_atomic_pointer {
1552                    write!(self.out, "atomicLoad(")?;
1553                    self.write_expr(module, pointer, func_ctx)?;
1554                    write!(self.out, ")")?;
1555                } else {
1556                    self.write_expr_with_indirection(
1557                        module,
1558                        pointer,
1559                        func_ctx,
1560                        Indirection::Reference,
1561                    )?;
1562                }
1563            }
1564            Expression::LocalVariable(handle) => {
1565                write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])?
1566            }
1567            Expression::ArrayLength(expr) => {
1568                write!(self.out, "arrayLength(")?;
1569                self.write_expr(module, expr, func_ctx)?;
1570                write!(self.out, ")")?;
1571            }
1572
1573            Expression::Math {
1574                fun,
1575                arg,
1576                arg1,
1577                arg2,
1578                arg3,
1579            } => {
1580                use crate::MathFunction as Mf;
1581
1582                enum Function {
1583                    Regular(&'static str),
1584                    InversePolyfill(InversePolyfill),
1585                }
1586
1587                let function = match fun.try_to_wgsl() {
1588                    Some(name) => Function::Regular(name),
1589                    None => match fun {
1590                        Mf::Inverse => {
1591                            let ty = func_ctx.resolve_type(arg, &module.types);
1592                            let Some(overload) = InversePolyfill::find_overload(ty) else {
1593                                return Err(Error::unsupported("math function", fun));
1594                            };
1595
1596                            Function::InversePolyfill(overload)
1597                        }
1598                        _ => return Err(Error::unsupported("math function", fun)),
1599                    },
1600                };
1601
1602                match function {
1603                    Function::Regular(fun_name) => {
1604                        write!(self.out, "{fun_name}(")?;
1605                        self.write_expr(module, arg, func_ctx)?;
1606                        for arg in IntoIterator::into_iter([arg1, arg2, arg3]).flatten() {
1607                            write!(self.out, ", ")?;
1608                            self.write_expr(module, arg, func_ctx)?;
1609                        }
1610                        write!(self.out, ")")?
1611                    }
1612                    Function::InversePolyfill(inverse) => {
1613                        write!(self.out, "{}(", inverse.fun_name)?;
1614                        self.write_expr(module, arg, func_ctx)?;
1615                        write!(self.out, ")")?;
1616                        self.required_polyfills.insert(inverse);
1617                    }
1618                }
1619            }
1620
1621            Expression::Swizzle {
1622                size,
1623                vector,
1624                pattern,
1625            } => {
1626                self.write_expr(module, vector, func_ctx)?;
1627                write!(self.out, ".")?;
1628                for &sc in pattern[..size as usize].iter() {
1629                    self.out.write_char(back::COMPONENTS[sc as usize])?;
1630                }
1631            }
1632            Expression::Unary { op, expr } => {
1633                let unary = match op {
1634                    crate::UnaryOperator::Negate => "-",
1635                    crate::UnaryOperator::LogicalNot => "!",
1636                    crate::UnaryOperator::BitwiseNot => "~",
1637                };
1638
1639                write!(self.out, "{unary}(")?;
1640                self.write_expr(module, expr, func_ctx)?;
1641
1642                write!(self.out, ")")?
1643            }
1644
1645            Expression::Select {
1646                condition,
1647                accept,
1648                reject,
1649            } => {
1650                write!(self.out, "select(")?;
1651                self.write_expr(module, reject, func_ctx)?;
1652                write!(self.out, ", ")?;
1653                self.write_expr(module, accept, func_ctx)?;
1654                write!(self.out, ", ")?;
1655                self.write_expr(module, condition, func_ctx)?;
1656                write!(self.out, ")")?
1657            }
1658            Expression::Derivative { axis, ctrl, expr } => {
1659                use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
1660                let op = match (axis, ctrl) {
1661                    (Axis::X, Ctrl::Coarse) => "dpdxCoarse",
1662                    (Axis::X, Ctrl::Fine) => "dpdxFine",
1663                    (Axis::X, Ctrl::None) => "dpdx",
1664                    (Axis::Y, Ctrl::Coarse) => "dpdyCoarse",
1665                    (Axis::Y, Ctrl::Fine) => "dpdyFine",
1666                    (Axis::Y, Ctrl::None) => "dpdy",
1667                    (Axis::Width, Ctrl::Coarse) => "fwidthCoarse",
1668                    (Axis::Width, Ctrl::Fine) => "fwidthFine",
1669                    (Axis::Width, Ctrl::None) => "fwidth",
1670                };
1671                write!(self.out, "{op}(")?;
1672                self.write_expr(module, expr, func_ctx)?;
1673                write!(self.out, ")")?
1674            }
1675            Expression::Relational { fun, argument } => {
1676                use crate::RelationalFunction as Rf;
1677
1678                let fun_name = match fun {
1679                    Rf::All => "all",
1680                    Rf::Any => "any",
1681                    _ => return Err(Error::UnsupportedRelationalFunction(fun)),
1682                };
1683                write!(self.out, "{fun_name}(")?;
1684
1685                self.write_expr(module, argument, func_ctx)?;
1686
1687                write!(self.out, ")")?
1688            }
1689            // Not supported yet
1690            Expression::RayQueryGetIntersection { .. }
1691            | Expression::RayQueryVertexPositions { .. } => unreachable!(),
1692            // Nothing to do here, since call expression already cached
1693            Expression::CallResult(_)
1694            | Expression::AtomicResult { .. }
1695            | Expression::RayQueryProceedResult
1696            | Expression::SubgroupBallotResult
1697            | Expression::SubgroupOperationResult { .. }
1698            | Expression::WorkGroupUniformLoadResult { .. } => {}
1699        }
1700
1701        Ok(())
1702    }
1703
1704    /// Helper method used to write global variables
1705    /// # Notes
1706    /// Always adds a newline
1707    fn write_global(
1708        &mut self,
1709        module: &Module,
1710        global: &crate::GlobalVariable,
1711        handle: Handle<crate::GlobalVariable>,
1712    ) -> BackendResult {
1713        // Write group and binding attributes if present
1714        if let Some(ref binding) = global.binding {
1715            self.write_attributes(&[
1716                Attribute::Group(binding.group),
1717                Attribute::Binding(binding.binding),
1718            ])?;
1719            writeln!(self.out)?;
1720        }
1721
1722        // First write global name and address space if supported
1723        write!(self.out, "var")?;
1724        let (address, maybe_access) = address_space_str(global.space);
1725        if let Some(space) = address {
1726            write!(self.out, "<{space}")?;
1727            if let Some(access) = maybe_access {
1728                write!(self.out, ", {access}")?;
1729            }
1730            write!(self.out, ">")?;
1731        }
1732        write!(
1733            self.out,
1734            " {}: ",
1735            &self.names[&NameKey::GlobalVariable(handle)]
1736        )?;
1737
1738        // Write global type
1739        self.write_type(module, global.ty)?;
1740
1741        // Write initializer
1742        if let Some(init) = global.init {
1743            write!(self.out, " = ")?;
1744            self.write_const_expression(module, init, &module.global_expressions)?;
1745        }
1746
1747        // End with semicolon
1748        writeln!(self.out, ";")?;
1749
1750        Ok(())
1751    }
1752
1753    /// Helper method used to write global constants
1754    ///
1755    /// # Notes
1756    /// Ends in a newline
1757    fn write_global_constant(
1758        &mut self,
1759        module: &Module,
1760        handle: Handle<crate::Constant>,
1761    ) -> BackendResult {
1762        let name = &self.names[&NameKey::Constant(handle)];
1763        // First write only constant name
1764        write!(self.out, "const {name}: ")?;
1765        self.write_type(module, module.constants[handle].ty)?;
1766        write!(self.out, " = ")?;
1767        let init = module.constants[handle].init;
1768        self.write_const_expression(module, init, &module.global_expressions)?;
1769        writeln!(self.out, ";")?;
1770
1771        Ok(())
1772    }
1773
1774    // See https://github.com/rust-lang/rust-clippy/issues/4979.
1775    #[allow(clippy::missing_const_for_fn)]
1776    pub fn finish(self) -> W {
1777        self.out
1778    }
1779}
1780
1781struct WriterTypeContext<'m> {
1782    module: &'m Module,
1783    names: &'m crate::FastHashMap<NameKey, String>,
1784}
1785
1786impl TypeContext for WriterTypeContext<'_> {
1787    fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type {
1788        &self.module.types[handle]
1789    }
1790
1791    fn type_name(&self, handle: Handle<crate::Type>) -> &str {
1792        self.names[&NameKey::Type(handle)].as_str()
1793    }
1794
1795    fn write_unnamed_struct<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
1796        unreachable!("the WGSL back end should always provide type handles");
1797    }
1798
1799    fn write_override<W: Write>(&self, _: Handle<crate::Override>, _: &mut W) -> core::fmt::Result {
1800        unreachable!("overrides should be validated out");
1801    }
1802
1803    fn write_non_wgsl_inner<W: Write>(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result {
1804        unreachable!("backends should only be passed validated modules");
1805    }
1806
1807    fn write_non_wgsl_scalar<W: Write>(&self, _: crate::Scalar, _: &mut W) -> core::fmt::Result {
1808        unreachable!("backends should only be passed validated modules");
1809    }
1810}
1811
1812fn map_binding_to_attribute(binding: &crate::Binding) -> Vec<Attribute> {
1813    match *binding {
1814        crate::Binding::BuiltIn(built_in) => {
1815            if let crate::BuiltIn::Position { invariant: true } = built_in {
1816                vec![Attribute::BuiltIn(built_in), Attribute::Invariant]
1817            } else {
1818                vec![Attribute::BuiltIn(built_in)]
1819            }
1820        }
1821        crate::Binding::Location {
1822            location,
1823            interpolation,
1824            sampling,
1825            blend_src: None,
1826            per_primitive: _,
1827        } => vec![
1828            Attribute::Location(location),
1829            Attribute::Interpolate(interpolation, sampling),
1830        ],
1831        crate::Binding::Location {
1832            location,
1833            interpolation,
1834            sampling,
1835            blend_src: Some(blend_src),
1836            per_primitive: _,
1837        } => vec![
1838            Attribute::Location(location),
1839            Attribute::BlendSrc(blend_src),
1840            Attribute::Interpolate(interpolation, sampling),
1841        ],
1842    }
1843}