naga/back/wgsl/
writer.rs

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