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