naga/back/wgsl/
writer.rs

1use super::Error;
2use crate::back::wgsl::polyfill::InversePolyfill;
3use crate::{
4    back::{self, Baked},
5    proc::{self, ExpressionKindTracker, NameKey},
6    valid, Handle, Module, ShaderStage, TypeInner,
7};
8use std::fmt::Write;
9
10/// Shorthand result used internally by the backend
11type BackendResult = Result<(), Error>;
12
13/// WGSL [attribute](https://gpuweb.github.io/gpuweb/wgsl/#attributes)
14enum Attribute {
15    Binding(u32),
16    BuiltIn(crate::BuiltIn),
17    Group(u32),
18    Invariant,
19    Interpolate(Option<crate::Interpolation>, Option<crate::Sampling>),
20    Location(u32),
21    SecondBlendSource,
22    Stage(ShaderStage),
23    WorkGroupSize([u32; 3]),
24}
25
26/// The WGSL form that `write_expr_with_indirection` should use to render a Naga
27/// expression.
28///
29/// Sometimes a Naga `Expression` alone doesn't provide enough information to
30/// choose the right rendering for it in WGSL. For example, one natural WGSL
31/// rendering of a Naga `LocalVariable(x)` expression might be `&x`, since
32/// `LocalVariable` produces a pointer to the local variable's storage. But when
33/// rendering a `Store` statement, the `pointer` operand must be the left hand
34/// side of a WGSL assignment, so the proper rendering is `x`.
35///
36/// The caller of `write_expr_with_indirection` must provide an `Expected` value
37/// to indicate how ambiguous expressions should be rendered.
38#[derive(Clone, Copy, Debug)]
39enum Indirection {
40    /// Render pointer-construction expressions as WGSL `ptr`-typed expressions.
41    ///
42    /// This is the right choice for most cases. Whenever a Naga pointer
43    /// expression is not the `pointer` operand of a `Load` or `Store`, it
44    /// must be a WGSL pointer expression.
45    Ordinary,
46
47    /// Render pointer-construction expressions as WGSL reference-typed
48    /// expressions.
49    ///
50    /// For example, this is the right choice for the `pointer` operand when
51    /// rendering a `Store` statement as a WGSL assignment.
52    Reference,
53}
54
55bitflags::bitflags! {
56    #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
57    #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
58    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
59    pub struct WriterFlags: u32 {
60        /// Always annotate the type information instead of inferring.
61        const EXPLICIT_TYPES = 0x1;
62    }
63}
64
65pub struct Writer<W> {
66    out: W,
67    flags: WriterFlags,
68    names: crate::FastHashMap<NameKey, String>,
69    namer: proc::Namer,
70    named_expressions: crate::NamedExpressions,
71    ep_results: Vec<(ShaderStage, Handle<crate::Type>)>,
72    required_polyfills: crate::FastIndexSet<InversePolyfill>,
73}
74
75impl<W: Write> Writer<W> {
76    pub fn new(out: W, flags: WriterFlags) -> Self {
77        Writer {
78            out,
79            flags,
80            names: crate::FastHashMap::default(),
81            namer: proc::Namer::default(),
82            named_expressions: crate::NamedExpressions::default(),
83            ep_results: vec![],
84            required_polyfills: crate::FastIndexSet::default(),
85        }
86    }
87
88    fn reset(&mut self, module: &Module) {
89        self.names.clear();
90        self.namer.reset(
91            module,
92            crate::keywords::wgsl::RESERVED,
93            // an identifier must not start with two underscore
94            &[],
95            &[],
96            &["__", "_naga"],
97            &mut self.names,
98        );
99        self.named_expressions.clear();
100        self.ep_results.clear();
101        self.required_polyfills.clear();
102    }
103
104    fn is_builtin_wgsl_struct(&self, module: &Module, handle: Handle<crate::Type>) -> bool {
105        module
106            .special_types
107            .predeclared_types
108            .values()
109            .any(|t| *t == handle)
110    }
111
112    pub fn write(&mut self, module: &Module, info: &valid::ModuleInfo) -> BackendResult {
113        if !module.overrides.is_empty() {
114            return Err(Error::Unimplemented(
115                "Pipeline constants are not yet supported for this back-end".to_string(),
116            ));
117        }
118
119        self.reset(module);
120
121        // Save all ep result types
122        for ep in &module.entry_points {
123            if let Some(ref result) = ep.function.result {
124                self.ep_results.push((ep.stage, result.ty));
125            }
126        }
127
128        // Write all structs
129        for (handle, ty) in module.types.iter() {
130            if let TypeInner::Struct { ref members, .. } = ty.inner {
131                {
132                    if !self.is_builtin_wgsl_struct(module, handle) {
133                        self.write_struct(module, handle, members)?;
134                        writeln!(self.out)?;
135                    }
136                }
137            }
138        }
139
140        // Write all named constants
141        let mut constants = module
142            .constants
143            .iter()
144            .filter(|&(_, c)| c.name.is_some())
145            .peekable();
146        while let Some((handle, _)) = constants.next() {
147            self.write_global_constant(module, handle)?;
148            // Add extra newline for readability on last iteration
149            if constants.peek().is_none() {
150                writeln!(self.out)?;
151            }
152        }
153
154        // Write all globals
155        for (ty, global) in module.global_variables.iter() {
156            self.write_global(module, global, ty)?;
157        }
158
159        if !module.global_variables.is_empty() {
160            // Add extra newline for readability
161            writeln!(self.out)?;
162        }
163
164        // Write all regular functions
165        for (handle, function) in module.functions.iter() {
166            let fun_info = &info[handle];
167
168            let func_ctx = back::FunctionCtx {
169                ty: back::FunctionType::Function(handle),
170                info: fun_info,
171                expressions: &function.expressions,
172                named_expressions: &function.named_expressions,
173                expr_kind_tracker: ExpressionKindTracker::from_arena(&function.expressions),
174            };
175
176            // Write the function
177            self.write_function(module, function, &func_ctx)?;
178
179            writeln!(self.out)?;
180        }
181
182        // Write all entry points
183        for (index, ep) in module.entry_points.iter().enumerate() {
184            let attributes = match ep.stage {
185                ShaderStage::Vertex | ShaderStage::Fragment => vec![Attribute::Stage(ep.stage)],
186                ShaderStage::Compute => vec![
187                    Attribute::Stage(ShaderStage::Compute),
188                    Attribute::WorkGroupSize(ep.workgroup_size),
189                ],
190            };
191
192            self.write_attributes(&attributes)?;
193            // Add a newline after attribute
194            writeln!(self.out)?;
195
196            let func_ctx = back::FunctionCtx {
197                ty: back::FunctionType::EntryPoint(index as u16),
198                info: info.get_entry_point(index),
199                expressions: &ep.function.expressions,
200                named_expressions: &ep.function.named_expressions,
201                expr_kind_tracker: ExpressionKindTracker::from_arena(&ep.function.expressions),
202            };
203            self.write_function(module, &ep.function, &func_ctx)?;
204
205            if index < module.entry_points.len() - 1 {
206                writeln!(self.out)?;
207            }
208        }
209
210        // Write any polyfills that were required.
211        for polyfill in &self.required_polyfills {
212            writeln!(self.out)?;
213            write!(self.out, "{}", polyfill.source)?;
214            writeln!(self.out)?;
215        }
216
217        Ok(())
218    }
219
220    /// Helper method used to write struct name
221    ///
222    /// # Notes
223    /// Adds no trailing or leading whitespace
224    fn write_struct_name(&mut self, module: &Module, handle: Handle<crate::Type>) -> BackendResult {
225        if module.types[handle].name.is_none() {
226            if let Some(&(stage, _)) = self.ep_results.iter().find(|&&(_, ty)| ty == handle) {
227                let name = match stage {
228                    ShaderStage::Compute => "ComputeOutput",
229                    ShaderStage::Fragment => "FragmentOutput",
230                    ShaderStage::Vertex => "VertexOutput",
231                };
232
233                write!(self.out, "{name}")?;
234                return Ok(());
235            }
236        }
237
238        write!(self.out, "{}", self.names[&NameKey::Type(handle)])?;
239
240        Ok(())
241    }
242
243    /// Helper method used to write
244    /// [functions](https://gpuweb.github.io/gpuweb/wgsl/#functions)
245    ///
246    /// # Notes
247    /// Ends in a newline
248    fn write_function(
249        &mut self,
250        module: &Module,
251        func: &crate::Function,
252        func_ctx: &back::FunctionCtx<'_>,
253    ) -> BackendResult {
254        let func_name = match func_ctx.ty {
255            back::FunctionType::EntryPoint(index) => &self.names[&NameKey::EntryPoint(index)],
256            back::FunctionType::Function(handle) => &self.names[&NameKey::Function(handle)],
257        };
258
259        // Write function name
260        write!(self.out, "fn {func_name}(")?;
261
262        // Write function arguments
263        for (index, arg) in func.arguments.iter().enumerate() {
264            // Write argument attribute if a binding is present
265            if let Some(ref binding) = arg.binding {
266                self.write_attributes(&map_binding_to_attribute(binding))?;
267            }
268            // Write argument name
269            let argument_name = &self.names[&func_ctx.argument_key(index as u32)];
270
271            write!(self.out, "{argument_name}: ")?;
272            // Write argument type
273            self.write_type(module, arg.ty)?;
274            if index < func.arguments.len() - 1 {
275                // Add a separator between args
276                write!(self.out, ", ")?;
277            }
278        }
279
280        write!(self.out, ")")?;
281
282        // Write function return type
283        if let Some(ref result) = func.result {
284            write!(self.out, " -> ")?;
285            if let Some(ref binding) = result.binding {
286                self.write_attributes(&map_binding_to_attribute(binding))?;
287            }
288            self.write_type(module, result.ty)?;
289        }
290
291        write!(self.out, " {{")?;
292        writeln!(self.out)?;
293
294        // Write function local variables
295        for (handle, local) in func.local_variables.iter() {
296            // Write indentation (only for readability)
297            write!(self.out, "{}", back::INDENT)?;
298
299            // Write the local name
300            // The leading space is important
301            write!(self.out, "var {}: ", self.names[&func_ctx.name_key(handle)])?;
302
303            // Write the local type
304            self.write_type(module, local.ty)?;
305
306            // Write the local initializer if needed
307            if let Some(init) = local.init {
308                // Put the equal signal only if there's a initializer
309                // The leading and trailing spaces aren't needed but help with readability
310                write!(self.out, " = ")?;
311
312                // Write the constant
313                // `write_constant` adds no trailing or leading space/newline
314                self.write_expr(module, init, func_ctx)?;
315            }
316
317            // Finish the local with `;` and add a newline (only for readability)
318            writeln!(self.out, ";")?
319        }
320
321        if !func.local_variables.is_empty() {
322            writeln!(self.out)?;
323        }
324
325        // Write the function body (statement list)
326        for sta in func.body.iter() {
327            // The indentation should always be 1 when writing the function body
328            self.write_stmt(module, sta, func_ctx, back::Level(1))?;
329        }
330
331        writeln!(self.out, "}}")?;
332
333        self.named_expressions.clear();
334
335        Ok(())
336    }
337
338    /// Helper method to write a attribute
339    fn write_attributes(&mut self, attributes: &[Attribute]) -> BackendResult {
340        for attribute in attributes {
341            match *attribute {
342                Attribute::Location(id) => write!(self.out, "@location({id}) ")?,
343                Attribute::SecondBlendSource => write!(self.out, "@second_blend_source ")?,
344                Attribute::BuiltIn(builtin_attrib) => {
345                    let builtin = builtin_str(builtin_attrib)?;
346                    write!(self.out, "@builtin({builtin}) ")?;
347                }
348                Attribute::Stage(shader_stage) => {
349                    let stage_str = match shader_stage {
350                        ShaderStage::Vertex => "vertex",
351                        ShaderStage::Fragment => "fragment",
352                        ShaderStage::Compute => "compute",
353                    };
354                    write!(self.out, "@{stage_str} ")?;
355                }
356                Attribute::WorkGroupSize(size) => {
357                    write!(
358                        self.out,
359                        "@workgroup_size({}, {}, {}) ",
360                        size[0], size[1], size[2]
361                    )?;
362                }
363                Attribute::Binding(id) => write!(self.out, "@binding({id}) ")?,
364                Attribute::Group(id) => write!(self.out, "@group({id}) ")?,
365                Attribute::Invariant => write!(self.out, "@invariant ")?,
366                Attribute::Interpolate(interpolation, sampling) => {
367                    if sampling.is_some() && sampling != Some(crate::Sampling::Center) {
368                        write!(
369                            self.out,
370                            "@interpolate({}, {}) ",
371                            interpolation_str(
372                                interpolation.unwrap_or(crate::Interpolation::Perspective)
373                            ),
374                            sampling_str(sampling.unwrap_or(crate::Sampling::Center))
375                        )?;
376                    } else if interpolation.is_some()
377                        && interpolation != Some(crate::Interpolation::Perspective)
378                    {
379                        write!(
380                            self.out,
381                            "@interpolate({}) ",
382                            interpolation_str(
383                                interpolation.unwrap_or(crate::Interpolation::Perspective)
384                            )
385                        )?;
386                    }
387                }
388            };
389        }
390        Ok(())
391    }
392
393    /// Helper method used to write structs
394    ///
395    /// # Notes
396    /// Ends in a newline
397    fn write_struct(
398        &mut self,
399        module: &Module,
400        handle: Handle<crate::Type>,
401        members: &[crate::StructMember],
402    ) -> BackendResult {
403        write!(self.out, "struct ")?;
404        self.write_struct_name(module, handle)?;
405        write!(self.out, " {{")?;
406        writeln!(self.out)?;
407        for (index, member) in members.iter().enumerate() {
408            // The indentation is only for readability
409            write!(self.out, "{}", back::INDENT)?;
410            if let Some(ref binding) = member.binding {
411                self.write_attributes(&map_binding_to_attribute(binding))?;
412            }
413            // Write struct member name and type
414            let member_name = &self.names[&NameKey::StructMember(handle, index as u32)];
415            write!(self.out, "{member_name}: ")?;
416            self.write_type(module, member.ty)?;
417            write!(self.out, ",")?;
418            writeln!(self.out)?;
419        }
420
421        write!(self.out, "}}")?;
422
423        writeln!(self.out)?;
424
425        Ok(())
426    }
427
428    /// Helper method used to write non image/sampler types
429    ///
430    /// # Notes
431    /// Adds no trailing or leading whitespace
432    fn write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult {
433        let inner = &module.types[ty].inner;
434        match *inner {
435            TypeInner::Struct { .. } => self.write_struct_name(module, ty)?,
436            ref other => self.write_value_type(module, other)?,
437        }
438
439        Ok(())
440    }
441
442    /// Helper method used to write value types
443    ///
444    /// # Notes
445    /// Adds no trailing or leading whitespace
446    fn write_value_type(&mut self, module: &Module, inner: &TypeInner) -> BackendResult {
447        match *inner {
448            TypeInner::Vector { size, scalar } => write!(
449                self.out,
450                "vec{}<{}>",
451                back::vector_size_str(size),
452                scalar_kind_str(scalar),
453            )?,
454            TypeInner::Sampler { comparison: false } => {
455                write!(self.out, "sampler")?;
456            }
457            TypeInner::Sampler { comparison: true } => {
458                write!(self.out, "sampler_comparison")?;
459            }
460            TypeInner::Image {
461                dim,
462                arrayed,
463                class,
464            } => {
465                // More about texture types: https://gpuweb.github.io/gpuweb/wgsl/#sampled-texture-type
466                use crate::ImageClass as Ic;
467
468                let dim_str = image_dimension_str(dim);
469                let arrayed_str = if arrayed { "_array" } else { "" };
470                let (class_str, multisampled_str, format_str, storage_str) = match class {
471                    Ic::Sampled { kind, multi } => (
472                        "",
473                        if multi { "multisampled_" } else { "" },
474                        scalar_kind_str(crate::Scalar { kind, width: 4 }),
475                        "",
476                    ),
477                    Ic::Depth { multi } => {
478                        ("depth_", if multi { "multisampled_" } else { "" }, "", "")
479                    }
480                    Ic::Storage { format, access } => (
481                        "storage_",
482                        "",
483                        storage_format_str(format),
484                        if access.contains(crate::StorageAccess::ATOMIC) {
485                            ",atomic"
486                        } else if access
487                            .contains(crate::StorageAccess::LOAD | crate::StorageAccess::STORE)
488                        {
489                            ",read_write"
490                        } else if access.contains(crate::StorageAccess::LOAD) {
491                            ",read"
492                        } else {
493                            ",write"
494                        },
495                    ),
496                };
497                write!(
498                    self.out,
499                    "texture_{class_str}{multisampled_str}{dim_str}{arrayed_str}"
500                )?;
501
502                if !format_str.is_empty() {
503                    write!(self.out, "<{format_str}{storage_str}>")?;
504                }
505            }
506            TypeInner::Scalar(scalar) => {
507                write!(self.out, "{}", scalar_kind_str(scalar))?;
508            }
509            TypeInner::Atomic(scalar) => {
510                write!(self.out, "atomic<{}>", scalar_kind_str(scalar))?;
511            }
512            TypeInner::Array {
513                base,
514                size,
515                stride: _,
516            } => {
517                // More info https://gpuweb.github.io/gpuweb/wgsl/#array-types
518                // array<A, 3> -- Constant array
519                // array<A> -- Dynamic array
520                write!(self.out, "array<")?;
521                match size {
522                    crate::ArraySize::Constant(len) => {
523                        self.write_type(module, base)?;
524                        write!(self.out, ", {len}")?;
525                    }
526                    crate::ArraySize::Pending(_) => {
527                        unreachable!();
528                    }
529                    crate::ArraySize::Dynamic => {
530                        self.write_type(module, base)?;
531                    }
532                }
533                write!(self.out, ">")?;
534            }
535            TypeInner::BindingArray { base, size } => {
536                // More info https://github.com/gpuweb/gpuweb/issues/2105
537                write!(self.out, "binding_array<")?;
538                match size {
539                    crate::ArraySize::Constant(len) => {
540                        self.write_type(module, base)?;
541                        write!(self.out, ", {len}")?;
542                    }
543                    crate::ArraySize::Pending(_) => {
544                        unreachable!();
545                    }
546                    crate::ArraySize::Dynamic => {
547                        self.write_type(module, base)?;
548                    }
549                }
550                write!(self.out, ">")?;
551            }
552            TypeInner::Matrix {
553                columns,
554                rows,
555                scalar,
556            } => {
557                write!(
558                    self.out,
559                    "mat{}x{}<{}>",
560                    back::vector_size_str(columns),
561                    back::vector_size_str(rows),
562                    scalar_kind_str(scalar)
563                )?;
564            }
565            TypeInner::Pointer { base, space } => {
566                let (address, maybe_access) = address_space_str(space);
567                // Everything but `AddressSpace::Handle` gives us a `address` name, but
568                // Naga IR never produces pointers to handles, so it doesn't matter much
569                // how we write such a type. Just write it as the base type alone.
570                if let Some(space) = address {
571                    write!(self.out, "ptr<{space}, ")?;
572                }
573                self.write_type(module, base)?;
574                if address.is_some() {
575                    if let Some(access) = maybe_access {
576                        write!(self.out, ", {access}")?;
577                    }
578                    write!(self.out, ">")?;
579                }
580            }
581            TypeInner::ValuePointer {
582                size: None,
583                scalar,
584                space,
585            } => {
586                let (address, maybe_access) = address_space_str(space);
587                if let Some(space) = address {
588                    write!(self.out, "ptr<{}, {}", space, scalar_kind_str(scalar))?;
589                    if let Some(access) = maybe_access {
590                        write!(self.out, ", {access}")?;
591                    }
592                    write!(self.out, ">")?;
593                } else {
594                    return Err(Error::Unimplemented(format!(
595                        "ValuePointer to AddressSpace::Handle {inner:?}"
596                    )));
597                }
598            }
599            TypeInner::ValuePointer {
600                size: Some(size),
601                scalar,
602                space,
603            } => {
604                let (address, maybe_access) = address_space_str(space);
605                if let Some(space) = address {
606                    write!(
607                        self.out,
608                        "ptr<{}, vec{}<{}>",
609                        space,
610                        back::vector_size_str(size),
611                        scalar_kind_str(scalar)
612                    )?;
613                    if let Some(access) = maybe_access {
614                        write!(self.out, ", {access}")?;
615                    }
616                    write!(self.out, ">")?;
617                } else {
618                    return Err(Error::Unimplemented(format!(
619                        "ValuePointer to AddressSpace::Handle {inner:?}"
620                    )));
621                }
622                write!(self.out, ">")?;
623            }
624            TypeInner::AccelerationStructure => write!(self.out, "acceleration_structure")?,
625            _ => {
626                return Err(Error::Unimplemented(format!("write_value_type {inner:?}")));
627            }
628        }
629
630        Ok(())
631    }
632    /// Helper method used to write statements
633    ///
634    /// # Notes
635    /// Always adds a newline
636    fn write_stmt(
637        &mut self,
638        module: &Module,
639        stmt: &crate::Statement,
640        func_ctx: &back::FunctionCtx<'_>,
641        level: back::Level,
642    ) -> BackendResult {
643        use crate::{Expression, Statement};
644
645        match *stmt {
646            Statement::Emit(ref range) => {
647                for handle in range.clone() {
648                    let info = &func_ctx.info[handle];
649                    let expr_name = if let Some(name) = func_ctx.named_expressions.get(&handle) {
650                        // Front end provides names for all variables at the start of writing.
651                        // But we write them to step by step. We need to recache them
652                        // Otherwise, we could accidentally write variable name instead of full expression.
653                        // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords.
654                        Some(self.namer.call(name))
655                    } else {
656                        let expr = &func_ctx.expressions[handle];
657                        let min_ref_count = expr.bake_ref_count();
658                        // Forcefully creating baking expressions in some cases to help with readability
659                        let required_baking_expr = match *expr {
660                            Expression::ImageLoad { .. }
661                            | Expression::ImageQuery { .. }
662                            | Expression::ImageSample { .. } => true,
663                            _ => false,
664                        };
665                        if min_ref_count <= info.ref_count || required_baking_expr {
666                            Some(Baked(handle).to_string())
667                        } else {
668                            None
669                        }
670                    };
671
672                    if let Some(name) = expr_name {
673                        write!(self.out, "{level}")?;
674                        self.start_named_expr(module, handle, func_ctx, &name)?;
675                        self.write_expr(module, handle, func_ctx)?;
676                        self.named_expressions.insert(handle, name);
677                        writeln!(self.out, ";")?;
678                    }
679                }
680            }
681            // TODO: copy-paste from glsl-out
682            Statement::If {
683                condition,
684                ref accept,
685                ref reject,
686            } => {
687                write!(self.out, "{level}")?;
688                write!(self.out, "if ")?;
689                self.write_expr(module, condition, func_ctx)?;
690                writeln!(self.out, " {{")?;
691
692                let l2 = level.next();
693                for sta in accept {
694                    // Increase indentation to help with readability
695                    self.write_stmt(module, sta, func_ctx, l2)?;
696                }
697
698                // If there are no statements in the reject block we skip writing it
699                // This is only for readability
700                if !reject.is_empty() {
701                    writeln!(self.out, "{level}}} else {{")?;
702
703                    for sta in reject {
704                        // Increase indentation to help with readability
705                        self.write_stmt(module, sta, func_ctx, l2)?;
706                    }
707                }
708
709                writeln!(self.out, "{level}}}")?
710            }
711            Statement::Return { value } => {
712                write!(self.out, "{level}")?;
713                write!(self.out, "return")?;
714                if let Some(return_value) = value {
715                    // The leading space is important
716                    write!(self.out, " ")?;
717                    self.write_expr(module, return_value, func_ctx)?;
718                }
719                writeln!(self.out, ";")?;
720            }
721            // TODO: copy-paste from glsl-out
722            Statement::Kill => {
723                write!(self.out, "{level}")?;
724                writeln!(self.out, "discard;")?
725            }
726            Statement::Store { pointer, value } => {
727                write!(self.out, "{level}")?;
728
729                let is_atomic_pointer = func_ctx
730                    .resolve_type(pointer, &module.types)
731                    .is_atomic_pointer(&module.types);
732
733                if is_atomic_pointer {
734                    write!(self.out, "atomicStore(")?;
735                    self.write_expr(module, pointer, func_ctx)?;
736                    write!(self.out, ", ")?;
737                    self.write_expr(module, value, func_ctx)?;
738                    write!(self.out, ")")?;
739                } else {
740                    self.write_expr_with_indirection(
741                        module,
742                        pointer,
743                        func_ctx,
744                        Indirection::Reference,
745                    )?;
746                    write!(self.out, " = ")?;
747                    self.write_expr(module, value, func_ctx)?;
748                }
749                writeln!(self.out, ";")?
750            }
751            Statement::Call {
752                function,
753                ref arguments,
754                result,
755            } => {
756                write!(self.out, "{level}")?;
757                if let Some(expr) = result {
758                    let name = Baked(expr).to_string();
759                    self.start_named_expr(module, expr, func_ctx, &name)?;
760                    self.named_expressions.insert(expr, name);
761                }
762                let func_name = &self.names[&NameKey::Function(function)];
763                write!(self.out, "{func_name}(")?;
764                for (index, &argument) in arguments.iter().enumerate() {
765                    if index != 0 {
766                        write!(self.out, ", ")?;
767                    }
768                    self.write_expr(module, argument, func_ctx)?;
769                }
770                writeln!(self.out, ");")?
771            }
772            Statement::Atomic {
773                pointer,
774                ref fun,
775                value,
776                result,
777            } => {
778                write!(self.out, "{level}")?;
779                if let Some(result) = result {
780                    let res_name = Baked(result).to_string();
781                    self.start_named_expr(module, result, func_ctx, &res_name)?;
782                    self.named_expressions.insert(result, res_name);
783                }
784
785                let fun_str = fun.to_wgsl();
786                write!(self.out, "atomic{fun_str}(")?;
787                self.write_expr(module, pointer, func_ctx)?;
788                if let crate::AtomicFunction::Exchange { compare: Some(cmp) } = *fun {
789                    write!(self.out, ", ")?;
790                    self.write_expr(module, cmp, func_ctx)?;
791                }
792                write!(self.out, ", ")?;
793                self.write_expr(module, value, func_ctx)?;
794                writeln!(self.out, ");")?
795            }
796            Statement::ImageAtomic {
797                image,
798                coordinate,
799                array_index,
800                ref fun,
801                value,
802            } => {
803                write!(self.out, "{level}")?;
804                let fun_str = fun.to_wgsl();
805                write!(self.out, "textureAtomic{fun_str}(")?;
806                self.write_expr(module, image, func_ctx)?;
807                write!(self.out, ", ")?;
808                self.write_expr(module, coordinate, func_ctx)?;
809                if let Some(array_index_expr) = array_index {
810                    write!(self.out, ", ")?;
811                    self.write_expr(module, array_index_expr, func_ctx)?;
812                }
813                write!(self.out, ", ")?;
814                self.write_expr(module, value, func_ctx)?;
815                writeln!(self.out, ");")?;
816            }
817            Statement::WorkGroupUniformLoad { pointer, result } => {
818                write!(self.out, "{level}")?;
819                // TODO: Obey named expressions here.
820                let res_name = Baked(result).to_string();
821                self.start_named_expr(module, result, func_ctx, &res_name)?;
822                self.named_expressions.insert(result, res_name);
823                write!(self.out, "workgroupUniformLoad(")?;
824                self.write_expr(module, pointer, func_ctx)?;
825                writeln!(self.out, ");")?;
826            }
827            Statement::ImageStore {
828                image,
829                coordinate,
830                array_index,
831                value,
832            } => {
833                write!(self.out, "{level}")?;
834                write!(self.out, "textureStore(")?;
835                self.write_expr(module, image, func_ctx)?;
836                write!(self.out, ", ")?;
837                self.write_expr(module, coordinate, func_ctx)?;
838                if let Some(array_index_expr) = array_index {
839                    write!(self.out, ", ")?;
840                    self.write_expr(module, array_index_expr, func_ctx)?;
841                }
842                write!(self.out, ", ")?;
843                self.write_expr(module, value, func_ctx)?;
844                writeln!(self.out, ");")?;
845            }
846            // TODO: copy-paste from glsl-out
847            Statement::Block(ref block) => {
848                write!(self.out, "{level}")?;
849                writeln!(self.out, "{{")?;
850                for sta in block.iter() {
851                    // Increase the indentation to help with readability
852                    self.write_stmt(module, sta, func_ctx, level.next())?
853                }
854                writeln!(self.out, "{level}}}")?
855            }
856            Statement::Switch {
857                selector,
858                ref cases,
859            } => {
860                // Start the switch
861                write!(self.out, "{level}")?;
862                write!(self.out, "switch ")?;
863                self.write_expr(module, selector, func_ctx)?;
864                writeln!(self.out, " {{")?;
865
866                let l2 = level.next();
867                let mut new_case = true;
868                for case in cases {
869                    if case.fall_through && !case.body.is_empty() {
870                        // TODO: we could do the same workaround as we did for the HLSL backend
871                        return Err(Error::Unimplemented(
872                            "fall-through switch case block".into(),
873                        ));
874                    }
875
876                    match case.value {
877                        crate::SwitchValue::I32(value) => {
878                            if new_case {
879                                write!(self.out, "{l2}case ")?;
880                            }
881                            write!(self.out, "{value}")?;
882                        }
883                        crate::SwitchValue::U32(value) => {
884                            if new_case {
885                                write!(self.out, "{l2}case ")?;
886                            }
887                            write!(self.out, "{value}u")?;
888                        }
889                        crate::SwitchValue::Default => {
890                            if new_case {
891                                if case.fall_through {
892                                    write!(self.out, "{l2}case ")?;
893                                } else {
894                                    write!(self.out, "{l2}")?;
895                                }
896                            }
897                            write!(self.out, "default")?;
898                        }
899                    }
900
901                    new_case = !case.fall_through;
902
903                    if case.fall_through {
904                        write!(self.out, ", ")?;
905                    } else {
906                        writeln!(self.out, ": {{")?;
907                    }
908
909                    for sta in case.body.iter() {
910                        self.write_stmt(module, sta, func_ctx, l2.next())?;
911                    }
912
913                    if !case.fall_through {
914                        writeln!(self.out, "{l2}}}")?;
915                    }
916                }
917
918                writeln!(self.out, "{level}}}")?
919            }
920            Statement::Loop {
921                ref body,
922                ref continuing,
923                break_if,
924            } => {
925                write!(self.out, "{level}")?;
926                writeln!(self.out, "loop {{")?;
927
928                let l2 = level.next();
929                for sta in body.iter() {
930                    self.write_stmt(module, sta, func_ctx, l2)?;
931                }
932
933                // The continuing is optional so we don't need to write it if
934                // it is empty, but the `break if` counts as a continuing statement
935                // so even if `continuing` is empty we must generate it if a
936                // `break if` exists
937                if !continuing.is_empty() || break_if.is_some() {
938                    writeln!(self.out, "{l2}continuing {{")?;
939                    for sta in continuing.iter() {
940                        self.write_stmt(module, sta, func_ctx, l2.next())?;
941                    }
942
943                    // The `break if` is always the last
944                    // statement of the `continuing` block
945                    if let Some(condition) = break_if {
946                        // The trailing space is important
947                        write!(self.out, "{}break if ", l2.next())?;
948                        self.write_expr(module, condition, func_ctx)?;
949                        // Close the `break if` statement
950                        writeln!(self.out, ";")?;
951                    }
952
953                    writeln!(self.out, "{l2}}}")?;
954                }
955
956                writeln!(self.out, "{level}}}")?
957            }
958            Statement::Break => {
959                writeln!(self.out, "{level}break;")?;
960            }
961            Statement::Continue => {
962                writeln!(self.out, "{level}continue;")?;
963            }
964            Statement::Barrier(barrier) => {
965                if barrier.contains(crate::Barrier::STORAGE) {
966                    writeln!(self.out, "{level}storageBarrier();")?;
967                }
968
969                if barrier.contains(crate::Barrier::WORK_GROUP) {
970                    writeln!(self.out, "{level}workgroupBarrier();")?;
971                }
972
973                if barrier.contains(crate::Barrier::SUB_GROUP) {
974                    writeln!(self.out, "{level}subgroupBarrier();")?;
975                }
976            }
977            Statement::RayQuery { .. } => unreachable!(),
978            Statement::SubgroupBallot { result, predicate } => {
979                write!(self.out, "{level}")?;
980                let res_name = Baked(result).to_string();
981                self.start_named_expr(module, result, func_ctx, &res_name)?;
982                self.named_expressions.insert(result, res_name);
983
984                write!(self.out, "subgroupBallot(")?;
985                if let Some(predicate) = predicate {
986                    self.write_expr(module, predicate, func_ctx)?;
987                }
988                writeln!(self.out, ");")?;
989            }
990            Statement::SubgroupCollectiveOperation {
991                op,
992                collective_op,
993                argument,
994                result,
995            } => {
996                write!(self.out, "{level}")?;
997                let res_name = Baked(result).to_string();
998                self.start_named_expr(module, result, func_ctx, &res_name)?;
999                self.named_expressions.insert(result, res_name);
1000
1001                match (collective_op, op) {
1002                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {
1003                        write!(self.out, "subgroupAll(")?
1004                    }
1005                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {
1006                        write!(self.out, "subgroupAny(")?
1007                    }
1008                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {
1009                        write!(self.out, "subgroupAdd(")?
1010                    }
1011                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {
1012                        write!(self.out, "subgroupMul(")?
1013                    }
1014                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {
1015                        write!(self.out, "subgroupMax(")?
1016                    }
1017                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {
1018                        write!(self.out, "subgroupMin(")?
1019                    }
1020                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {
1021                        write!(self.out, "subgroupAnd(")?
1022                    }
1023                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {
1024                        write!(self.out, "subgroupOr(")?
1025                    }
1026                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {
1027                        write!(self.out, "subgroupXor(")?
1028                    }
1029                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {
1030                        write!(self.out, "subgroupExclusiveAdd(")?
1031                    }
1032                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {
1033                        write!(self.out, "subgroupExclusiveMul(")?
1034                    }
1035                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {
1036                        write!(self.out, "subgroupInclusiveAdd(")?
1037                    }
1038                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {
1039                        write!(self.out, "subgroupInclusiveMul(")?
1040                    }
1041                    _ => unimplemented!(),
1042                }
1043                self.write_expr(module, argument, func_ctx)?;
1044                writeln!(self.out, ");")?;
1045            }
1046            Statement::SubgroupGather {
1047                mode,
1048                argument,
1049                result,
1050            } => {
1051                write!(self.out, "{level}")?;
1052                let res_name = Baked(result).to_string();
1053                self.start_named_expr(module, result, func_ctx, &res_name)?;
1054                self.named_expressions.insert(result, res_name);
1055
1056                match mode {
1057                    crate::GatherMode::BroadcastFirst => {
1058                        write!(self.out, "subgroupBroadcastFirst(")?;
1059                    }
1060                    crate::GatherMode::Broadcast(_) => {
1061                        write!(self.out, "subgroupBroadcast(")?;
1062                    }
1063                    crate::GatherMode::Shuffle(_) => {
1064                        write!(self.out, "subgroupShuffle(")?;
1065                    }
1066                    crate::GatherMode::ShuffleDown(_) => {
1067                        write!(self.out, "subgroupShuffleDown(")?;
1068                    }
1069                    crate::GatherMode::ShuffleUp(_) => {
1070                        write!(self.out, "subgroupShuffleUp(")?;
1071                    }
1072                    crate::GatherMode::ShuffleXor(_) => {
1073                        write!(self.out, "subgroupShuffleXor(")?;
1074                    }
1075                }
1076                self.write_expr(module, argument, func_ctx)?;
1077                match mode {
1078                    crate::GatherMode::BroadcastFirst => {}
1079                    crate::GatherMode::Broadcast(index)
1080                    | crate::GatherMode::Shuffle(index)
1081                    | crate::GatherMode::ShuffleDown(index)
1082                    | crate::GatherMode::ShuffleUp(index)
1083                    | crate::GatherMode::ShuffleXor(index) => {
1084                        write!(self.out, ", ")?;
1085                        self.write_expr(module, index, func_ctx)?;
1086                    }
1087                }
1088                writeln!(self.out, ");")?;
1089            }
1090        }
1091
1092        Ok(())
1093    }
1094
1095    /// Return the sort of indirection that `expr`'s plain form evaluates to.
1096    ///
1097    /// An expression's 'plain form' is the most general rendition of that
1098    /// expression into WGSL, lacking `&` or `*` operators:
1099    ///
1100    /// - The plain form of `LocalVariable(x)` is simply `x`, which is a reference
1101    ///   to the local variable's storage.
1102    ///
1103    /// - The plain form of `GlobalVariable(g)` is simply `g`, which is usually a
1104    ///   reference to the global variable's storage. However, globals in the
1105    ///   `Handle` address space are immutable, and `GlobalVariable` expressions for
1106    ///   those produce the value directly, not a pointer to it. Such
1107    ///   `GlobalVariable` expressions are `Ordinary`.
1108    ///
1109    /// - `Access` and `AccessIndex` are `Reference` when their `base` operand is a
1110    ///   pointer. If they are applied directly to a composite value, they are
1111    ///   `Ordinary`.
1112    ///
1113    /// Note that `FunctionArgument` expressions are never `Reference`, even when
1114    /// the argument's type is `Pointer`. `FunctionArgument` always evaluates to the
1115    /// argument's value directly, so any pointer it produces is merely the value
1116    /// passed by the caller.
1117    fn plain_form_indirection(
1118        &self,
1119        expr: Handle<crate::Expression>,
1120        module: &Module,
1121        func_ctx: &back::FunctionCtx<'_>,
1122    ) -> Indirection {
1123        use crate::Expression as Ex;
1124
1125        // Named expressions are `let` expressions, which apply the Load Rule,
1126        // so if their type is a Naga pointer, then that must be a WGSL pointer
1127        // as well.
1128        if self.named_expressions.contains_key(&expr) {
1129            return Indirection::Ordinary;
1130        }
1131
1132        match func_ctx.expressions[expr] {
1133            Ex::LocalVariable(_) => Indirection::Reference,
1134            Ex::GlobalVariable(handle) => {
1135                let global = &module.global_variables[handle];
1136                match global.space {
1137                    crate::AddressSpace::Handle => Indirection::Ordinary,
1138                    _ => Indirection::Reference,
1139                }
1140            }
1141            Ex::Access { base, .. } | Ex::AccessIndex { base, .. } => {
1142                let base_ty = func_ctx.resolve_type(base, &module.types);
1143                match *base_ty {
1144                    TypeInner::Pointer { .. } | TypeInner::ValuePointer { .. } => {
1145                        Indirection::Reference
1146                    }
1147                    _ => Indirection::Ordinary,
1148                }
1149            }
1150            _ => Indirection::Ordinary,
1151        }
1152    }
1153
1154    fn start_named_expr(
1155        &mut self,
1156        module: &Module,
1157        handle: Handle<crate::Expression>,
1158        func_ctx: &back::FunctionCtx,
1159        name: &str,
1160    ) -> BackendResult {
1161        // Some functions are marked as const, but are not yet implemented as constant expression
1162        let quantifier = if func_ctx.expr_kind_tracker.is_impl_const(handle) {
1163            "const"
1164        } else {
1165            "let"
1166        };
1167        // Write variable name
1168        write!(self.out, "{quantifier} {name}")?;
1169        if self.flags.contains(WriterFlags::EXPLICIT_TYPES) {
1170            write!(self.out, ": ")?;
1171            let ty = &func_ctx.info[handle].ty;
1172            // Write variable type
1173            match *ty {
1174                proc::TypeResolution::Handle(handle) => {
1175                    self.write_type(module, handle)?;
1176                }
1177                proc::TypeResolution::Value(ref inner) => {
1178                    self.write_value_type(module, inner)?;
1179                }
1180            }
1181        }
1182
1183        write!(self.out, " = ")?;
1184        Ok(())
1185    }
1186
1187    /// Write the ordinary WGSL form of `expr`.
1188    ///
1189    /// See `write_expr_with_indirection` for details.
1190    fn write_expr(
1191        &mut self,
1192        module: &Module,
1193        expr: Handle<crate::Expression>,
1194        func_ctx: &back::FunctionCtx<'_>,
1195    ) -> BackendResult {
1196        self.write_expr_with_indirection(module, expr, func_ctx, Indirection::Ordinary)
1197    }
1198
1199    /// Write `expr` as a WGSL expression with the requested indirection.
1200    ///
1201    /// In terms of the WGSL grammar, the resulting expression is a
1202    /// `singular_expression`. It may be parenthesized. This makes it suitable
1203    /// for use as the operand of a unary or binary operator without worrying
1204    /// about precedence.
1205    ///
1206    /// This does not produce newlines or indentation.
1207    ///
1208    /// The `requested` argument indicates (roughly) whether Naga
1209    /// `Pointer`-valued expressions represent WGSL references or pointers. See
1210    /// `Indirection` for details.
1211    fn write_expr_with_indirection(
1212        &mut self,
1213        module: &Module,
1214        expr: Handle<crate::Expression>,
1215        func_ctx: &back::FunctionCtx<'_>,
1216        requested: Indirection,
1217    ) -> BackendResult {
1218        // If the plain form of the expression is not what we need, emit the
1219        // operator necessary to correct that.
1220        let plain = self.plain_form_indirection(expr, module, func_ctx);
1221        match (requested, plain) {
1222            (Indirection::Ordinary, Indirection::Reference) => {
1223                write!(self.out, "(&")?;
1224                self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1225                write!(self.out, ")")?;
1226            }
1227            (Indirection::Reference, Indirection::Ordinary) => {
1228                write!(self.out, "(*")?;
1229                self.write_expr_plain_form(module, expr, func_ctx, plain)?;
1230                write!(self.out, ")")?;
1231            }
1232            (_, _) => self.write_expr_plain_form(module, expr, func_ctx, plain)?,
1233        }
1234
1235        Ok(())
1236    }
1237
1238    fn write_const_expression(
1239        &mut self,
1240        module: &Module,
1241        expr: Handle<crate::Expression>,
1242    ) -> BackendResult {
1243        self.write_possibly_const_expression(
1244            module,
1245            expr,
1246            &module.global_expressions,
1247            |writer, expr| writer.write_const_expression(module, expr),
1248        )
1249    }
1250
1251    fn write_possibly_const_expression<E>(
1252        &mut self,
1253        module: &Module,
1254        expr: Handle<crate::Expression>,
1255        expressions: &crate::Arena<crate::Expression>,
1256        write_expression: E,
1257    ) -> BackendResult
1258    where
1259        E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,
1260    {
1261        use crate::Expression;
1262
1263        match expressions[expr] {
1264            Expression::Literal(literal) => match literal {
1265                crate::Literal::F32(value) => write!(self.out, "{value}f")?,
1266                crate::Literal::U32(value) => write!(self.out, "{value}u")?,
1267                crate::Literal::I32(value) => {
1268                    // `-2147483648i` is not valid WGSL. The most negative `i32`
1269                    // value can only be expressed in WGSL using AbstractInt and
1270                    // a unary negation operator.
1271                    if value == i32::MIN {
1272                        write!(self.out, "i32({value})")?;
1273                    } else {
1274                        write!(self.out, "{value}i")?;
1275                    }
1276                }
1277                crate::Literal::Bool(value) => write!(self.out, "{value}")?,
1278                crate::Literal::F64(value) => write!(self.out, "{value:?}lf")?,
1279                crate::Literal::I64(value) => {
1280                    // `-9223372036854775808li` is not valid WGSL. The most negative `i64`
1281                    // value can only be expressed in WGSL using AbstractInt and
1282                    // a unary negation operator.
1283                    if value == i64::MIN {
1284                        write!(self.out, "i64({value})")?;
1285                    } else {
1286                        write!(self.out, "{value}li")?;
1287                    }
1288                }
1289                crate::Literal::U64(value) => write!(self.out, "{value:?}lu")?,
1290                crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
1291                    return Err(Error::Custom(
1292                        "Abstract types should not appear in IR presented to backends".into(),
1293                    ));
1294                }
1295            },
1296            Expression::Constant(handle) => {
1297                let constant = &module.constants[handle];
1298                if constant.name.is_some() {
1299                    write!(self.out, "{}", self.names[&NameKey::Constant(handle)])?;
1300                } else {
1301                    self.write_const_expression(module, constant.init)?;
1302                }
1303            }
1304            Expression::ZeroValue(ty) => {
1305                self.write_type(module, ty)?;
1306                write!(self.out, "()")?;
1307            }
1308            Expression::Compose { ty, ref components } => {
1309                self.write_type(module, ty)?;
1310                write!(self.out, "(")?;
1311                for (index, component) in components.iter().enumerate() {
1312                    if index != 0 {
1313                        write!(self.out, ", ")?;
1314                    }
1315                    write_expression(self, *component)?;
1316                }
1317                write!(self.out, ")")?
1318            }
1319            Expression::Splat { size, value } => {
1320                let size = back::vector_size_str(size);
1321                write!(self.out, "vec{size}(")?;
1322                write_expression(self, value)?;
1323                write!(self.out, ")")?;
1324            }
1325            _ => unreachable!(),
1326        }
1327
1328        Ok(())
1329    }
1330
1331    /// Write the 'plain form' of `expr`.
1332    ///
1333    /// An expression's 'plain form' is the most general rendition of that
1334    /// expression into WGSL, lacking `&` or `*` operators. The plain forms of
1335    /// `LocalVariable(x)` and `GlobalVariable(g)` are simply `x` and `g`. Such
1336    /// Naga expressions represent both WGSL pointers and references; it's the
1337    /// caller's responsibility to distinguish those cases appropriately.
1338    fn write_expr_plain_form(
1339        &mut self,
1340        module: &Module,
1341        expr: Handle<crate::Expression>,
1342        func_ctx: &back::FunctionCtx<'_>,
1343        indirection: Indirection,
1344    ) -> BackendResult {
1345        use crate::Expression;
1346
1347        if let Some(name) = self.named_expressions.get(&expr) {
1348            write!(self.out, "{name}")?;
1349            return Ok(());
1350        }
1351
1352        let expression = &func_ctx.expressions[expr];
1353
1354        // Write the plain WGSL form of a Naga expression.
1355        //
1356        // The plain form of `LocalVariable` and `GlobalVariable` expressions is
1357        // simply the variable name; `*` and `&` operators are never emitted.
1358        //
1359        // The plain form of `Access` and `AccessIndex` expressions are WGSL
1360        // `postfix_expression` forms for member/component access and
1361        // subscripting.
1362        match *expression {
1363            Expression::Literal(_)
1364            | Expression::Constant(_)
1365            | Expression::ZeroValue(_)
1366            | Expression::Compose { .. }
1367            | Expression::Splat { .. } => {
1368                self.write_possibly_const_expression(
1369                    module,
1370                    expr,
1371                    func_ctx.expressions,
1372                    |writer, expr| writer.write_expr(module, expr, func_ctx),
1373                )?;
1374            }
1375            Expression::Override(_) => unreachable!(),
1376            Expression::FunctionArgument(pos) => {
1377                let name_key = func_ctx.argument_key(pos);
1378                let name = &self.names[&name_key];
1379                write!(self.out, "{name}")?;
1380            }
1381            Expression::Binary { op, left, right } => {
1382                write!(self.out, "(")?;
1383                self.write_expr(module, left, func_ctx)?;
1384                write!(self.out, " {} ", back::binary_operation_str(op))?;
1385                self.write_expr(module, right, func_ctx)?;
1386                write!(self.out, ")")?;
1387            }
1388            Expression::Access { base, index } => {
1389                self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1390                write!(self.out, "[")?;
1391                self.write_expr(module, index, func_ctx)?;
1392                write!(self.out, "]")?
1393            }
1394            Expression::AccessIndex { base, index } => {
1395                let base_ty_res = &func_ctx.info[base].ty;
1396                let mut resolved = base_ty_res.inner_with(&module.types);
1397
1398                self.write_expr_with_indirection(module, base, func_ctx, indirection)?;
1399
1400                let base_ty_handle = match *resolved {
1401                    TypeInner::Pointer { base, space: _ } => {
1402                        resolved = &module.types[base].inner;
1403                        Some(base)
1404                    }
1405                    _ => base_ty_res.handle(),
1406                };
1407
1408                match *resolved {
1409                    TypeInner::Vector { .. } => {
1410                        // Write vector access as a swizzle
1411                        write!(self.out, ".{}", back::COMPONENTS[index as usize])?
1412                    }
1413                    TypeInner::Matrix { .. }
1414                    | TypeInner::Array { .. }
1415                    | TypeInner::BindingArray { .. }
1416                    | TypeInner::ValuePointer { .. } => write!(self.out, "[{index}]")?,
1417                    TypeInner::Struct { .. } => {
1418                        // This will never panic in case the type is a `Struct`, this is not true
1419                        // for other types so we can only check while inside this match arm
1420                        let ty = base_ty_handle.unwrap();
1421
1422                        write!(
1423                            self.out,
1424                            ".{}",
1425                            &self.names[&NameKey::StructMember(ty, index)]
1426                        )?
1427                    }
1428                    ref other => return Err(Error::Custom(format!("Cannot index {other:?}"))),
1429                }
1430            }
1431            Expression::ImageSample {
1432                image,
1433                sampler,
1434                gather: None,
1435                coordinate,
1436                array_index,
1437                offset,
1438                level,
1439                depth_ref,
1440            } => {
1441                use crate::SampleLevel as Sl;
1442
1443                let suffix_cmp = match depth_ref {
1444                    Some(_) => "Compare",
1445                    None => "",
1446                };
1447                let suffix_level = match level {
1448                    Sl::Auto => "",
1449                    Sl::Zero | Sl::Exact(_) => "Level",
1450                    Sl::Bias(_) => "Bias",
1451                    Sl::Gradient { .. } => "Grad",
1452                };
1453
1454                write!(self.out, "textureSample{suffix_cmp}{suffix_level}(")?;
1455                self.write_expr(module, image, func_ctx)?;
1456                write!(self.out, ", ")?;
1457                self.write_expr(module, sampler, func_ctx)?;
1458                write!(self.out, ", ")?;
1459                self.write_expr(module, coordinate, func_ctx)?;
1460
1461                if let Some(array_index) = array_index {
1462                    write!(self.out, ", ")?;
1463                    self.write_expr(module, array_index, func_ctx)?;
1464                }
1465
1466                if let Some(depth_ref) = depth_ref {
1467                    write!(self.out, ", ")?;
1468                    self.write_expr(module, depth_ref, func_ctx)?;
1469                }
1470
1471                match level {
1472                    Sl::Auto => {}
1473                    Sl::Zero => {
1474                        // Level 0 is implied for depth comparison
1475                        if depth_ref.is_none() {
1476                            write!(self.out, ", 0.0")?;
1477                        }
1478                    }
1479                    Sl::Exact(expr) => {
1480                        write!(self.out, ", ")?;
1481                        self.write_expr(module, expr, func_ctx)?;
1482                    }
1483                    Sl::Bias(expr) => {
1484                        write!(self.out, ", ")?;
1485                        self.write_expr(module, expr, func_ctx)?;
1486                    }
1487                    Sl::Gradient { x, y } => {
1488                        write!(self.out, ", ")?;
1489                        self.write_expr(module, x, func_ctx)?;
1490                        write!(self.out, ", ")?;
1491                        self.write_expr(module, y, func_ctx)?;
1492                    }
1493                }
1494
1495                if let Some(offset) = offset {
1496                    write!(self.out, ", ")?;
1497                    self.write_const_expression(module, offset)?;
1498                }
1499
1500                write!(self.out, ")")?;
1501            }
1502
1503            Expression::ImageSample {
1504                image,
1505                sampler,
1506                gather: Some(component),
1507                coordinate,
1508                array_index,
1509                offset,
1510                level: _,
1511                depth_ref,
1512            } => {
1513                let suffix_cmp = match depth_ref {
1514                    Some(_) => "Compare",
1515                    None => "",
1516                };
1517
1518                write!(self.out, "textureGather{suffix_cmp}(")?;
1519                match *func_ctx.resolve_type(image, &module.types) {
1520                    TypeInner::Image {
1521                        class: crate::ImageClass::Depth { multi: _ },
1522                        ..
1523                    } => {}
1524                    _ => {
1525                        write!(self.out, "{}, ", component as u8)?;
1526                    }
1527                }
1528                self.write_expr(module, image, func_ctx)?;
1529                write!(self.out, ", ")?;
1530                self.write_expr(module, sampler, func_ctx)?;
1531                write!(self.out, ", ")?;
1532                self.write_expr(module, coordinate, func_ctx)?;
1533
1534                if let Some(array_index) = array_index {
1535                    write!(self.out, ", ")?;
1536                    self.write_expr(module, array_index, func_ctx)?;
1537                }
1538
1539                if let Some(depth_ref) = depth_ref {
1540                    write!(self.out, ", ")?;
1541                    self.write_expr(module, depth_ref, func_ctx)?;
1542                }
1543
1544                if let Some(offset) = offset {
1545                    write!(self.out, ", ")?;
1546                    self.write_const_expression(module, offset)?;
1547                }
1548
1549                write!(self.out, ")")?;
1550            }
1551            Expression::ImageQuery { image, query } => {
1552                use crate::ImageQuery as Iq;
1553
1554                let texture_function = match query {
1555                    Iq::Size { .. } => "textureDimensions",
1556                    Iq::NumLevels => "textureNumLevels",
1557                    Iq::NumLayers => "textureNumLayers",
1558                    Iq::NumSamples => "textureNumSamples",
1559                };
1560
1561                write!(self.out, "{texture_function}(")?;
1562                self.write_expr(module, image, func_ctx)?;
1563                if let Iq::Size { level: Some(level) } = query {
1564                    write!(self.out, ", ")?;
1565                    self.write_expr(module, level, func_ctx)?;
1566                };
1567                write!(self.out, ")")?;
1568            }
1569
1570            Expression::ImageLoad {
1571                image,
1572                coordinate,
1573                array_index,
1574                sample,
1575                level,
1576            } => {
1577                write!(self.out, "textureLoad(")?;
1578                self.write_expr(module, image, func_ctx)?;
1579                write!(self.out, ", ")?;
1580                self.write_expr(module, coordinate, func_ctx)?;
1581                if let Some(array_index) = array_index {
1582                    write!(self.out, ", ")?;
1583                    self.write_expr(module, array_index, func_ctx)?;
1584                }
1585                if let Some(index) = sample.or(level) {
1586                    write!(self.out, ", ")?;
1587                    self.write_expr(module, index, func_ctx)?;
1588                }
1589                write!(self.out, ")")?;
1590            }
1591            Expression::GlobalVariable(handle) => {
1592                let name = &self.names[&NameKey::GlobalVariable(handle)];
1593                write!(self.out, "{name}")?;
1594            }
1595
1596            Expression::As {
1597                expr,
1598                kind,
1599                convert,
1600            } => {
1601                let inner = func_ctx.resolve_type(expr, &module.types);
1602                match *inner {
1603                    TypeInner::Matrix {
1604                        columns,
1605                        rows,
1606                        scalar,
1607                    } => {
1608                        let scalar = crate::Scalar {
1609                            kind,
1610                            width: convert.unwrap_or(scalar.width),
1611                        };
1612                        let scalar_kind_str = scalar_kind_str(scalar);
1613                        write!(
1614                            self.out,
1615                            "mat{}x{}<{}>",
1616                            back::vector_size_str(columns),
1617                            back::vector_size_str(rows),
1618                            scalar_kind_str
1619                        )?;
1620                    }
1621                    TypeInner::Vector {
1622                        size,
1623                        scalar: crate::Scalar { width, .. },
1624                    } => {
1625                        let scalar = crate::Scalar {
1626                            kind,
1627                            width: convert.unwrap_or(width),
1628                        };
1629                        let vector_size_str = back::vector_size_str(size);
1630                        let scalar_kind_str = scalar_kind_str(scalar);
1631                        if convert.is_some() {
1632                            write!(self.out, "vec{vector_size_str}<{scalar_kind_str}>")?;
1633                        } else {
1634                            write!(self.out, "bitcast<vec{vector_size_str}<{scalar_kind_str}>>")?;
1635                        }
1636                    }
1637                    TypeInner::Scalar(crate::Scalar { width, .. }) => {
1638                        let scalar = crate::Scalar {
1639                            kind,
1640                            width: convert.unwrap_or(width),
1641                        };
1642                        let scalar_kind_str = scalar_kind_str(scalar);
1643                        if convert.is_some() {
1644                            write!(self.out, "{scalar_kind_str}")?
1645                        } else {
1646                            write!(self.out, "bitcast<{scalar_kind_str}>")?
1647                        }
1648                    }
1649                    _ => {
1650                        return Err(Error::Unimplemented(format!(
1651                            "write_expr expression::as {inner:?}"
1652                        )));
1653                    }
1654                };
1655                write!(self.out, "(")?;
1656                self.write_expr(module, expr, func_ctx)?;
1657                write!(self.out, ")")?;
1658            }
1659            Expression::Load { pointer } => {
1660                let is_atomic_pointer = func_ctx
1661                    .resolve_type(pointer, &module.types)
1662                    .is_atomic_pointer(&module.types);
1663
1664                if is_atomic_pointer {
1665                    write!(self.out, "atomicLoad(")?;
1666                    self.write_expr(module, pointer, func_ctx)?;
1667                    write!(self.out, ")")?;
1668                } else {
1669                    self.write_expr_with_indirection(
1670                        module,
1671                        pointer,
1672                        func_ctx,
1673                        Indirection::Reference,
1674                    )?;
1675                }
1676            }
1677            Expression::LocalVariable(handle) => {
1678                write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])?
1679            }
1680            Expression::ArrayLength(expr) => {
1681                write!(self.out, "arrayLength(")?;
1682                self.write_expr(module, expr, func_ctx)?;
1683                write!(self.out, ")")?;
1684            }
1685
1686            Expression::Math {
1687                fun,
1688                arg,
1689                arg1,
1690                arg2,
1691                arg3,
1692            } => {
1693                use crate::MathFunction as Mf;
1694
1695                enum Function {
1696                    Regular(&'static str),
1697                    InversePolyfill(InversePolyfill),
1698                }
1699
1700                let function = match fun {
1701                    Mf::Abs => Function::Regular("abs"),
1702                    Mf::Min => Function::Regular("min"),
1703                    Mf::Max => Function::Regular("max"),
1704                    Mf::Clamp => Function::Regular("clamp"),
1705                    Mf::Saturate => Function::Regular("saturate"),
1706                    // trigonometry
1707                    Mf::Cos => Function::Regular("cos"),
1708                    Mf::Cosh => Function::Regular("cosh"),
1709                    Mf::Sin => Function::Regular("sin"),
1710                    Mf::Sinh => Function::Regular("sinh"),
1711                    Mf::Tan => Function::Regular("tan"),
1712                    Mf::Tanh => Function::Regular("tanh"),
1713                    Mf::Acos => Function::Regular("acos"),
1714                    Mf::Asin => Function::Regular("asin"),
1715                    Mf::Atan => Function::Regular("atan"),
1716                    Mf::Atan2 => Function::Regular("atan2"),
1717                    Mf::Asinh => Function::Regular("asinh"),
1718                    Mf::Acosh => Function::Regular("acosh"),
1719                    Mf::Atanh => Function::Regular("atanh"),
1720                    Mf::Radians => Function::Regular("radians"),
1721                    Mf::Degrees => Function::Regular("degrees"),
1722                    // decomposition
1723                    Mf::Ceil => Function::Regular("ceil"),
1724                    Mf::Floor => Function::Regular("floor"),
1725                    Mf::Round => Function::Regular("round"),
1726                    Mf::Fract => Function::Regular("fract"),
1727                    Mf::Trunc => Function::Regular("trunc"),
1728                    Mf::Modf => Function::Regular("modf"),
1729                    Mf::Frexp => Function::Regular("frexp"),
1730                    Mf::Ldexp => Function::Regular("ldexp"),
1731                    // exponent
1732                    Mf::Exp => Function::Regular("exp"),
1733                    Mf::Exp2 => Function::Regular("exp2"),
1734                    Mf::Log => Function::Regular("log"),
1735                    Mf::Log2 => Function::Regular("log2"),
1736                    Mf::Pow => Function::Regular("pow"),
1737                    // geometry
1738                    Mf::Dot => Function::Regular("dot"),
1739                    Mf::Cross => Function::Regular("cross"),
1740                    Mf::Distance => Function::Regular("distance"),
1741                    Mf::Length => Function::Regular("length"),
1742                    Mf::Normalize => Function::Regular("normalize"),
1743                    Mf::FaceForward => Function::Regular("faceForward"),
1744                    Mf::Reflect => Function::Regular("reflect"),
1745                    Mf::Refract => Function::Regular("refract"),
1746                    // computational
1747                    Mf::Sign => Function::Regular("sign"),
1748                    Mf::Fma => Function::Regular("fma"),
1749                    Mf::Mix => Function::Regular("mix"),
1750                    Mf::Step => Function::Regular("step"),
1751                    Mf::SmoothStep => Function::Regular("smoothstep"),
1752                    Mf::Sqrt => Function::Regular("sqrt"),
1753                    Mf::InverseSqrt => Function::Regular("inverseSqrt"),
1754                    Mf::Transpose => Function::Regular("transpose"),
1755                    Mf::Determinant => Function::Regular("determinant"),
1756                    Mf::QuantizeToF16 => Function::Regular("quantizeToF16"),
1757                    // bits
1758                    Mf::CountTrailingZeros => Function::Regular("countTrailingZeros"),
1759                    Mf::CountLeadingZeros => Function::Regular("countLeadingZeros"),
1760                    Mf::CountOneBits => Function::Regular("countOneBits"),
1761                    Mf::ReverseBits => Function::Regular("reverseBits"),
1762                    Mf::ExtractBits => Function::Regular("extractBits"),
1763                    Mf::InsertBits => Function::Regular("insertBits"),
1764                    Mf::FirstTrailingBit => Function::Regular("firstTrailingBit"),
1765                    Mf::FirstLeadingBit => Function::Regular("firstLeadingBit"),
1766                    // data packing
1767                    Mf::Pack4x8snorm => Function::Regular("pack4x8snorm"),
1768                    Mf::Pack4x8unorm => Function::Regular("pack4x8unorm"),
1769                    Mf::Pack2x16snorm => Function::Regular("pack2x16snorm"),
1770                    Mf::Pack2x16unorm => Function::Regular("pack2x16unorm"),
1771                    Mf::Pack2x16float => Function::Regular("pack2x16float"),
1772                    Mf::Pack4xI8 => Function::Regular("pack4xI8"),
1773                    Mf::Pack4xU8 => Function::Regular("pack4xU8"),
1774                    // data unpacking
1775                    Mf::Unpack4x8snorm => Function::Regular("unpack4x8snorm"),
1776                    Mf::Unpack4x8unorm => Function::Regular("unpack4x8unorm"),
1777                    Mf::Unpack2x16snorm => Function::Regular("unpack2x16snorm"),
1778                    Mf::Unpack2x16unorm => Function::Regular("unpack2x16unorm"),
1779                    Mf::Unpack2x16float => Function::Regular("unpack2x16float"),
1780                    Mf::Unpack4xI8 => Function::Regular("unpack4xI8"),
1781                    Mf::Unpack4xU8 => Function::Regular("unpack4xU8"),
1782                    Mf::Inverse => {
1783                        let typ = func_ctx.resolve_type(arg, &module.types);
1784
1785                        let Some(overload) = InversePolyfill::find_overload(typ) else {
1786                            return Err(Error::UnsupportedMathFunction(fun));
1787                        };
1788
1789                        Function::InversePolyfill(overload)
1790                    }
1791                    Mf::Outer => return Err(Error::UnsupportedMathFunction(fun)),
1792                };
1793
1794                match function {
1795                    Function::Regular(fun_name) => {
1796                        write!(self.out, "{fun_name}(")?;
1797                        self.write_expr(module, arg, func_ctx)?;
1798                        for arg in IntoIterator::into_iter([arg1, arg2, arg3]).flatten() {
1799                            write!(self.out, ", ")?;
1800                            self.write_expr(module, arg, func_ctx)?;
1801                        }
1802                        write!(self.out, ")")?
1803                    }
1804                    Function::InversePolyfill(inverse) => {
1805                        write!(self.out, "{}(", inverse.fun_name)?;
1806                        self.write_expr(module, arg, func_ctx)?;
1807                        write!(self.out, ")")?;
1808                        self.required_polyfills.insert(inverse);
1809                    }
1810                }
1811            }
1812
1813            Expression::Swizzle {
1814                size,
1815                vector,
1816                pattern,
1817            } => {
1818                self.write_expr(module, vector, func_ctx)?;
1819                write!(self.out, ".")?;
1820                for &sc in pattern[..size as usize].iter() {
1821                    self.out.write_char(back::COMPONENTS[sc as usize])?;
1822                }
1823            }
1824            Expression::Unary { op, expr } => {
1825                let unary = match op {
1826                    crate::UnaryOperator::Negate => "-",
1827                    crate::UnaryOperator::LogicalNot => "!",
1828                    crate::UnaryOperator::BitwiseNot => "~",
1829                };
1830
1831                write!(self.out, "{unary}(")?;
1832                self.write_expr(module, expr, func_ctx)?;
1833
1834                write!(self.out, ")")?
1835            }
1836
1837            Expression::Select {
1838                condition,
1839                accept,
1840                reject,
1841            } => {
1842                write!(self.out, "select(")?;
1843                self.write_expr(module, reject, func_ctx)?;
1844                write!(self.out, ", ")?;
1845                self.write_expr(module, accept, func_ctx)?;
1846                write!(self.out, ", ")?;
1847                self.write_expr(module, condition, func_ctx)?;
1848                write!(self.out, ")")?
1849            }
1850            Expression::Derivative { axis, ctrl, expr } => {
1851                use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
1852                let op = match (axis, ctrl) {
1853                    (Axis::X, Ctrl::Coarse) => "dpdxCoarse",
1854                    (Axis::X, Ctrl::Fine) => "dpdxFine",
1855                    (Axis::X, Ctrl::None) => "dpdx",
1856                    (Axis::Y, Ctrl::Coarse) => "dpdyCoarse",
1857                    (Axis::Y, Ctrl::Fine) => "dpdyFine",
1858                    (Axis::Y, Ctrl::None) => "dpdy",
1859                    (Axis::Width, Ctrl::Coarse) => "fwidthCoarse",
1860                    (Axis::Width, Ctrl::Fine) => "fwidthFine",
1861                    (Axis::Width, Ctrl::None) => "fwidth",
1862                };
1863                write!(self.out, "{op}(")?;
1864                self.write_expr(module, expr, func_ctx)?;
1865                write!(self.out, ")")?
1866            }
1867            Expression::Relational { fun, argument } => {
1868                use crate::RelationalFunction as Rf;
1869
1870                let fun_name = match fun {
1871                    Rf::All => "all",
1872                    Rf::Any => "any",
1873                    _ => return Err(Error::UnsupportedRelationalFunction(fun)),
1874                };
1875                write!(self.out, "{fun_name}(")?;
1876
1877                self.write_expr(module, argument, func_ctx)?;
1878
1879                write!(self.out, ")")?
1880            }
1881            // Not supported yet
1882            Expression::RayQueryGetIntersection { .. } => unreachable!(),
1883            // Nothing to do here, since call expression already cached
1884            Expression::CallResult(_)
1885            | Expression::AtomicResult { .. }
1886            | Expression::RayQueryProceedResult
1887            | Expression::SubgroupBallotResult
1888            | Expression::SubgroupOperationResult { .. }
1889            | Expression::WorkGroupUniformLoadResult { .. } => {}
1890        }
1891
1892        Ok(())
1893    }
1894
1895    /// Helper method used to write global variables
1896    /// # Notes
1897    /// Always adds a newline
1898    fn write_global(
1899        &mut self,
1900        module: &Module,
1901        global: &crate::GlobalVariable,
1902        handle: Handle<crate::GlobalVariable>,
1903    ) -> BackendResult {
1904        // Write group and binding attributes if present
1905        if let Some(ref binding) = global.binding {
1906            self.write_attributes(&[
1907                Attribute::Group(binding.group),
1908                Attribute::Binding(binding.binding),
1909            ])?;
1910            writeln!(self.out)?;
1911        }
1912
1913        // First write global name and address space if supported
1914        write!(self.out, "var")?;
1915        let (address, maybe_access) = address_space_str(global.space);
1916        if let Some(space) = address {
1917            write!(self.out, "<{space}")?;
1918            if let Some(access) = maybe_access {
1919                write!(self.out, ", {access}")?;
1920            }
1921            write!(self.out, ">")?;
1922        }
1923        write!(
1924            self.out,
1925            " {}: ",
1926            &self.names[&NameKey::GlobalVariable(handle)]
1927        )?;
1928
1929        // Write global type
1930        self.write_type(module, global.ty)?;
1931
1932        // Write initializer
1933        if let Some(init) = global.init {
1934            write!(self.out, " = ")?;
1935            self.write_const_expression(module, init)?;
1936        }
1937
1938        // End with semicolon
1939        writeln!(self.out, ";")?;
1940
1941        Ok(())
1942    }
1943
1944    /// Helper method used to write global constants
1945    ///
1946    /// # Notes
1947    /// Ends in a newline
1948    fn write_global_constant(
1949        &mut self,
1950        module: &Module,
1951        handle: Handle<crate::Constant>,
1952    ) -> BackendResult {
1953        let name = &self.names[&NameKey::Constant(handle)];
1954        // First write only constant name
1955        write!(self.out, "const {name}: ")?;
1956        self.write_type(module, module.constants[handle].ty)?;
1957        write!(self.out, " = ")?;
1958        let init = module.constants[handle].init;
1959        self.write_const_expression(module, init)?;
1960        writeln!(self.out, ";")?;
1961
1962        Ok(())
1963    }
1964
1965    // See https://github.com/rust-lang/rust-clippy/issues/4979.
1966    #[allow(clippy::missing_const_for_fn)]
1967    pub fn finish(self) -> W {
1968        self.out
1969    }
1970}
1971
1972fn builtin_str(built_in: crate::BuiltIn) -> Result<&'static str, Error> {
1973    use crate::BuiltIn as Bi;
1974
1975    Ok(match built_in {
1976        Bi::VertexIndex => "vertex_index",
1977        Bi::InstanceIndex => "instance_index",
1978        Bi::Position { .. } => "position",
1979        Bi::FrontFacing => "front_facing",
1980        Bi::FragDepth => "frag_depth",
1981        Bi::LocalInvocationId => "local_invocation_id",
1982        Bi::LocalInvocationIndex => "local_invocation_index",
1983        Bi::GlobalInvocationId => "global_invocation_id",
1984        Bi::WorkGroupId => "workgroup_id",
1985        Bi::NumWorkGroups => "num_workgroups",
1986        Bi::SampleIndex => "sample_index",
1987        Bi::SampleMask => "sample_mask",
1988        Bi::PrimitiveIndex => "primitive_index",
1989        Bi::ViewIndex => "view_index",
1990        Bi::NumSubgroups => "num_subgroups",
1991        Bi::SubgroupId => "subgroup_id",
1992        Bi::SubgroupSize => "subgroup_size",
1993        Bi::SubgroupInvocationId => "subgroup_invocation_id",
1994        Bi::BaseInstance
1995        | Bi::BaseVertex
1996        | Bi::ClipDistance
1997        | Bi::CullDistance
1998        | Bi::PointSize
1999        | Bi::PointCoord
2000        | Bi::WorkGroupSize
2001        | Bi::DrawID => return Err(Error::Custom(format!("Unsupported builtin {built_in:?}"))),
2002    })
2003}
2004
2005const fn image_dimension_str(dim: crate::ImageDimension) -> &'static str {
2006    use crate::ImageDimension as IDim;
2007
2008    match dim {
2009        IDim::D1 => "1d",
2010        IDim::D2 => "2d",
2011        IDim::D3 => "3d",
2012        IDim::Cube => "cube",
2013    }
2014}
2015
2016const fn scalar_kind_str(scalar: crate::Scalar) -> &'static str {
2017    use crate::Scalar;
2018    use crate::ScalarKind as Sk;
2019
2020    match scalar {
2021        Scalar {
2022            kind: Sk::Float,
2023            width: 8,
2024        } => "f64",
2025        Scalar {
2026            kind: Sk::Float,
2027            width: 4,
2028        } => "f32",
2029        Scalar {
2030            kind: Sk::Sint,
2031            width: 4,
2032        } => "i32",
2033        Scalar {
2034            kind: Sk::Uint,
2035            width: 4,
2036        } => "u32",
2037        Scalar {
2038            kind: Sk::Sint,
2039            width: 8,
2040        } => "i64",
2041        Scalar {
2042            kind: Sk::Uint,
2043            width: 8,
2044        } => "u64",
2045        Scalar {
2046            kind: Sk::Bool,
2047            width: 1,
2048        } => "bool",
2049        _ => unreachable!(),
2050    }
2051}
2052
2053const fn storage_format_str(format: crate::StorageFormat) -> &'static str {
2054    use crate::StorageFormat as Sf;
2055
2056    match format {
2057        Sf::R8Unorm => "r8unorm",
2058        Sf::R8Snorm => "r8snorm",
2059        Sf::R8Uint => "r8uint",
2060        Sf::R8Sint => "r8sint",
2061        Sf::R16Uint => "r16uint",
2062        Sf::R16Sint => "r16sint",
2063        Sf::R16Float => "r16float",
2064        Sf::Rg8Unorm => "rg8unorm",
2065        Sf::Rg8Snorm => "rg8snorm",
2066        Sf::Rg8Uint => "rg8uint",
2067        Sf::Rg8Sint => "rg8sint",
2068        Sf::R32Uint => "r32uint",
2069        Sf::R32Sint => "r32sint",
2070        Sf::R32Float => "r32float",
2071        Sf::Rg16Uint => "rg16uint",
2072        Sf::Rg16Sint => "rg16sint",
2073        Sf::Rg16Float => "rg16float",
2074        Sf::Rgba8Unorm => "rgba8unorm",
2075        Sf::Rgba8Snorm => "rgba8snorm",
2076        Sf::Rgba8Uint => "rgba8uint",
2077        Sf::Rgba8Sint => "rgba8sint",
2078        Sf::Bgra8Unorm => "bgra8unorm",
2079        Sf::Rgb10a2Uint => "rgb10a2uint",
2080        Sf::Rgb10a2Unorm => "rgb10a2unorm",
2081        Sf::Rg11b10Ufloat => "rg11b10float",
2082        Sf::R64Uint => "r64uint",
2083        Sf::Rg32Uint => "rg32uint",
2084        Sf::Rg32Sint => "rg32sint",
2085        Sf::Rg32Float => "rg32float",
2086        Sf::Rgba16Uint => "rgba16uint",
2087        Sf::Rgba16Sint => "rgba16sint",
2088        Sf::Rgba16Float => "rgba16float",
2089        Sf::Rgba32Uint => "rgba32uint",
2090        Sf::Rgba32Sint => "rgba32sint",
2091        Sf::Rgba32Float => "rgba32float",
2092        Sf::R16Unorm => "r16unorm",
2093        Sf::R16Snorm => "r16snorm",
2094        Sf::Rg16Unorm => "rg16unorm",
2095        Sf::Rg16Snorm => "rg16snorm",
2096        Sf::Rgba16Unorm => "rgba16unorm",
2097        Sf::Rgba16Snorm => "rgba16snorm",
2098    }
2099}
2100
2101/// Helper function that returns the string corresponding to the WGSL interpolation qualifier
2102const fn interpolation_str(interpolation: crate::Interpolation) -> &'static str {
2103    use crate::Interpolation as I;
2104
2105    match interpolation {
2106        I::Perspective => "perspective",
2107        I::Linear => "linear",
2108        I::Flat => "flat",
2109    }
2110}
2111
2112/// Return the WGSL auxiliary qualifier for the given sampling value.
2113const fn sampling_str(sampling: crate::Sampling) -> &'static str {
2114    use crate::Sampling as S;
2115
2116    match sampling {
2117        S::Center => "",
2118        S::Centroid => "centroid",
2119        S::Sample => "sample",
2120        S::First => "first",
2121        S::Either => "either",
2122    }
2123}
2124
2125const fn address_space_str(
2126    space: crate::AddressSpace,
2127) -> (Option<&'static str>, Option<&'static str>) {
2128    use crate::AddressSpace as As;
2129
2130    (
2131        Some(match space {
2132            As::Private => "private",
2133            As::Uniform => "uniform",
2134            As::Storage { access } => {
2135                if access.contains(crate::StorageAccess::ATOMIC) {
2136                    return (Some("storage"), Some("atomic"));
2137                } else if access.contains(crate::StorageAccess::STORE) {
2138                    return (Some("storage"), Some("read_write"));
2139                } else {
2140                    "storage"
2141                }
2142            }
2143            As::PushConstant => "push_constant",
2144            As::WorkGroup => "workgroup",
2145            As::Handle => return (None, None),
2146            As::Function => "function",
2147        }),
2148        None,
2149    )
2150}
2151
2152fn map_binding_to_attribute(binding: &crate::Binding) -> Vec<Attribute> {
2153    match *binding {
2154        crate::Binding::BuiltIn(built_in) => {
2155            if let crate::BuiltIn::Position { invariant: true } = built_in {
2156                vec![Attribute::BuiltIn(built_in), Attribute::Invariant]
2157            } else {
2158                vec![Attribute::BuiltIn(built_in)]
2159            }
2160        }
2161        crate::Binding::Location {
2162            location,
2163            interpolation,
2164            sampling,
2165            second_blend_source: false,
2166        } => vec![
2167            Attribute::Location(location),
2168            Attribute::Interpolate(interpolation, sampling),
2169        ],
2170        crate::Binding::Location {
2171            location,
2172            interpolation,
2173            sampling,
2174            second_blend_source: true,
2175        } => vec![
2176            Attribute::Location(location),
2177            Attribute::SecondBlendSource,
2178            Attribute::Interpolate(interpolation, sampling),
2179        ],
2180    }
2181}