Skip to main content

naga/back/glsl/
writer.rs

1use super::*;
2
3/// Writer responsible for all code generation.
4#[expect(missing_debug_implementations, reason = "would be way too verbose?")]
5pub struct Writer<'a, W> {
6    // Inputs
7    /// The module being written.
8    pub(in crate::back::glsl) module: &'a crate::Module,
9    /// The module analysis.
10    pub(in crate::back::glsl) info: &'a valid::ModuleInfo,
11    /// The output writer.
12    out: W,
13    /// User defined configuration to be used.
14    pub(in crate::back::glsl) options: &'a Options,
15    /// The bound checking policies to be used
16    pub(in crate::back::glsl) policies: proc::BoundsCheckPolicies,
17
18    // Internal State
19    /// Features manager used to store all the needed features and write them.
20    pub(in crate::back::glsl) features: FeaturesManager,
21    namer: proc::Namer,
22    /// A map with all the names needed for writing the module
23    /// (generated by a [`Namer`](crate::proc::Namer)).
24    names: crate::FastHashMap<NameKey, String>,
25    /// A map with the names of global variables needed for reflections.
26    reflection_names_globals: crate::FastHashMap<Handle<crate::GlobalVariable>, String>,
27    /// The selected entry point.
28    pub(in crate::back::glsl) entry_point: &'a crate::EntryPoint,
29    /// The index of the selected entry point.
30    pub(in crate::back::glsl) entry_point_idx: proc::EntryPointIndex,
31    /// A generator for unique block numbers.
32    block_id: IdGenerator,
33    /// Set of expressions that have associated temporary variables.
34    named_expressions: crate::NamedExpressions,
35    /// Set of expressions that need to be baked to avoid unnecessary repetition in output
36    need_bake_expressions: back::NeedBakeExpressions,
37    /// Information about nesting of loops and switches.
38    ///
39    /// Used for forwarding continue statements in switches that have been
40    /// transformed to `do {} while(false);` loops.
41    continue_ctx: back::continue_forward::ContinueCtx,
42    /// How many views to render to, if doing multiview rendering.
43    pub(in crate::back::glsl) multiview: Option<core::num::NonZeroU32>,
44    /// Mapping of varying variables to their location. Needed for reflections.
45    varying: crate::FastHashMap<String, VaryingLocation>,
46    /// Number of user-defined clip planes. Only non-zero for vertex shaders.
47    clip_distance_count: u32,
48}
49
50impl<'a, W: Write> Writer<'a, W> {
51    /// Creates a new [`Writer`] instance.
52    ///
53    /// # Errors
54    /// - If the version specified is invalid or supported.
55    /// - If the entry point couldn't be found in the module.
56    /// - If the version specified doesn't support some used features.
57    pub fn new(
58        out: W,
59        module: &'a crate::Module,
60        info: &'a valid::ModuleInfo,
61        options: &'a Options,
62        pipeline_options: &'a PipelineOptions,
63        policies: proc::BoundsCheckPolicies,
64    ) -> Result<Self, Error> {
65        // Check if the requested version is supported
66        if !options.version.is_supported() {
67            log::error!("Version {}", options.version);
68            return Err(Error::VersionNotSupported);
69        }
70
71        // Try to find the entry point and corresponding index
72        let ep_idx = module
73            .entry_points
74            .iter()
75            .position(|ep| {
76                pipeline_options.shader_stage == ep.stage && pipeline_options.entry_point == ep.name
77            })
78            .ok_or(Error::EntryPointNotFound)?;
79
80        // Generate a map with names required to write the module
81        let mut names = crate::FastHashMap::default();
82        let mut namer = proc::Namer::default();
83        namer.reset(
84            module,
85            &keywords::RESERVED_KEYWORD_SET,
86            proc::KeywordSet::empty(),
87            proc::CaseInsensitiveKeywordSet::empty(),
88            &[
89                "gl_",                  // all GL built-in variables
90                "_group",               // all normal bindings
91                "_immediates_binding_", // all immediate data bindings
92            ],
93            &mut names,
94        );
95
96        // Build the instance
97        let mut this = Self {
98            module,
99            info,
100            out,
101            options,
102            policies,
103
104            namer,
105            features: FeaturesManager::new(),
106            names,
107            reflection_names_globals: crate::FastHashMap::default(),
108            entry_point: &module.entry_points[ep_idx],
109            entry_point_idx: ep_idx as u16,
110            multiview: pipeline_options.multiview,
111            block_id: IdGenerator::default(),
112            named_expressions: Default::default(),
113            need_bake_expressions: Default::default(),
114            continue_ctx: back::continue_forward::ContinueCtx::default(),
115            varying: Default::default(),
116            clip_distance_count: 0,
117        };
118
119        // Find all features required to print this module
120        this.collect_required_features()?;
121
122        Ok(this)
123    }
124
125    /// Writes the [`Module`](crate::Module) as glsl to the output
126    ///
127    /// # Notes
128    /// If an error occurs while writing, the output might have been written partially
129    ///
130    /// # Panics
131    /// Might panic if the module is invalid
132    pub fn write(&mut self) -> Result<ReflectionInfo, Error> {
133        // We use `writeln!(self.out)` throughout the write to add newlines
134        // to make the output more readable
135
136        let es = self.options.version.is_es();
137
138        // Write the version (It must be the first thing or it isn't a valid glsl output)
139        writeln!(self.out, "#version {}", self.options.version)?;
140        // Write all the needed extensions
141        //
142        // This used to be the last thing being written as it allowed to search for features while
143        // writing the module saving some loops but some older versions (420 or less) required the
144        // extensions to appear before being used, even though extensions are part of the
145        // preprocessor not the processor ¯\_(ツ)_/¯
146        self.features.write(self.options, &mut self.out)?;
147
148        // glsl es requires a precision to be specified for floats and ints
149        // TODO: Should this be user configurable?
150        if es {
151            writeln!(self.out)?;
152            writeln!(self.out, "precision highp float;")?;
153            writeln!(self.out, "precision highp int;")?;
154            writeln!(self.out)?;
155        }
156
157        if self.entry_point.stage == ShaderStage::Compute {
158            let workgroup_size = self.entry_point.workgroup_size;
159            writeln!(
160                self.out,
161                "layout(local_size_x = {}, local_size_y = {}, local_size_z = {}) in;",
162                workgroup_size[0], workgroup_size[1], workgroup_size[2]
163            )?;
164            writeln!(self.out)?;
165        }
166
167        if self.entry_point.stage == ShaderStage::Vertex
168            && !self
169                .options
170                .writer_flags
171                .contains(WriterFlags::DRAW_PARAMETERS)
172            && self.features.contains(Features::INSTANCE_INDEX)
173        {
174            writeln!(self.out, "uniform uint {FIRST_INSTANCE_BINDING};")?;
175            writeln!(self.out)?;
176        }
177
178        // Enable early depth tests if needed
179        if let Some(early_depth_test) = self.entry_point.early_depth_test {
180            // If early depth test is supported for this version of GLSL
181            if self.options.version.supports_early_depth_test() {
182                match early_depth_test {
183                    crate::EarlyDepthTest::Force => {
184                        writeln!(self.out, "layout(early_fragment_tests) in;")?;
185                    }
186                    crate::EarlyDepthTest::Allow { conservative, .. } => {
187                        use crate::ConservativeDepth as Cd;
188                        let depth = match conservative {
189                            Cd::GreaterEqual => "greater",
190                            Cd::LessEqual => "less",
191                            Cd::Unchanged => "unchanged",
192                        };
193                        writeln!(self.out, "layout (depth_{depth}) out float gl_FragDepth;")?;
194                    }
195                }
196            } else {
197                log::warn!(
198                    "Early depth testing is not supported for this version of GLSL: {}",
199                    self.options.version
200                );
201            }
202        }
203
204        if self.entry_point.stage == ShaderStage::Vertex && self.options.version.is_webgl() {
205            if let Some(multiview) = self.multiview.as_ref() {
206                writeln!(self.out, "layout(num_views = {multiview}) in;")?;
207                writeln!(self.out)?;
208            }
209        }
210
211        // Write struct types.
212        //
213        // This are always ordered because the IR is structured in a way that
214        // you can't make a struct without adding all of its members first.
215        for (handle, ty) in self.module.types.iter() {
216            if let TypeInner::Struct { ref members, .. } = ty.inner {
217                let struct_name = &self.names[&NameKey::Type(handle)];
218
219                // Structures ending with runtime-sized arrays can only be
220                // rendered as shader storage blocks in GLSL, not stand-alone
221                // struct types.
222                if !self.module.types[members.last().unwrap().ty]
223                    .inner
224                    .is_dynamically_sized(&self.module.types)
225                {
226                    write!(self.out, "struct {struct_name} ")?;
227                    self.write_struct_body(handle, members)?;
228                    writeln!(self.out, ";")?;
229                }
230            }
231        }
232
233        // Write functions for special types.
234        for (type_key, struct_ty) in self.module.special_types.predeclared_types.iter() {
235            match type_key {
236                &crate::PredeclaredType::ModfResult { size, scalar }
237                | &crate::PredeclaredType::FrexpResult { size, scalar } => {
238                    let struct_name = &self.names[&NameKey::Type(*struct_ty)];
239                    let arg_type_name_owner;
240                    let arg_type_name = if let Some(size) = size {
241                        arg_type_name_owner = format!(
242                            "{}vec{}",
243                            if scalar.width == 8 { "d" } else { "" },
244                            size as u8
245                        );
246                        &arg_type_name_owner
247                    } else if scalar.width == 8 {
248                        "double"
249                    } else {
250                        "float"
251                    };
252
253                    let other_type_name_owner;
254                    let (defined_func_name, called_func_name, other_type_name) =
255                        if matches!(type_key, &crate::PredeclaredType::ModfResult { .. }) {
256                            (MODF_FUNCTION, "modf", arg_type_name)
257                        } else {
258                            let other_type_name = if let Some(size) = size {
259                                other_type_name_owner = format!("ivec{}", size as u8);
260                                &other_type_name_owner
261                            } else {
262                                "int"
263                            };
264                            (FREXP_FUNCTION, "frexp", other_type_name)
265                        };
266
267                    writeln!(self.out)?;
268                    if !self.options.version.supports_frexp_function()
269                        && matches!(type_key, &crate::PredeclaredType::FrexpResult { .. })
270                    {
271                        writeln!(
272                            self.out,
273                            "{struct_name} {defined_func_name}({arg_type_name} arg) {{
274    {other_type_name} other = arg == {arg_type_name}(0) ? {other_type_name}(0) : {other_type_name}({arg_type_name}(1) + log2(arg));
275    {arg_type_name} fract = arg * exp2({arg_type_name}(-other));
276    return {struct_name}(fract, other);
277}}",
278                        )?;
279                    } else {
280                        writeln!(
281                            self.out,
282                            "{struct_name} {defined_func_name}({arg_type_name} arg) {{
283    {other_type_name} other;
284    {arg_type_name} fract = {called_func_name}(arg, other);
285    return {struct_name}(fract, other);
286}}",
287                        )?;
288                    }
289                }
290                &crate::PredeclaredType::AtomicCompareExchangeWeakResult(_) => {
291                    // Handled by the general struct writing loop earlier.
292                }
293            }
294        }
295
296        // Write all named constants
297        let mut constants = self
298            .module
299            .constants
300            .iter()
301            .filter(|&(_, c)| c.name.is_some())
302            .peekable();
303        while let Some((handle, _)) = constants.next() {
304            self.write_global_constant(handle)?;
305            // Add extra newline for readability on last iteration
306            if constants.peek().is_none() {
307                writeln!(self.out)?;
308            }
309        }
310
311        let ep_info = self.info.get_entry_point(self.entry_point_idx as usize);
312
313        // Write the globals
314        //
315        // Unless explicitly disabled with WriterFlags::INCLUDE_UNUSED_ITEMS,
316        // we filter all globals that aren't used by the selected entry point as they might be
317        // interfere with each other (i.e. two globals with the same location but different with
318        // different classes)
319        let include_unused = self
320            .options
321            .writer_flags
322            .contains(WriterFlags::INCLUDE_UNUSED_ITEMS);
323        for (handle, global) in self.module.global_variables.iter() {
324            let is_unused = ep_info[handle].is_empty();
325            if !include_unused && is_unused {
326                continue;
327            }
328
329            match self.module.types[global.ty].inner {
330                // We treat images separately because they might require
331                // writing the storage format
332                TypeInner::Image {
333                    mut dim,
334                    arrayed,
335                    class,
336                } => {
337                    // Gather the storage format if needed
338                    let storage_format_access = match self.module.types[global.ty].inner {
339                        TypeInner::Image {
340                            class: crate::ImageClass::Storage { format, access },
341                            ..
342                        } => Some((format, access)),
343                        _ => None,
344                    };
345
346                    if dim == crate::ImageDimension::D1 && es {
347                        dim = crate::ImageDimension::D2
348                    }
349
350                    // Gether the location if needed
351                    let layout_binding = if self.options.version.supports_explicit_locations() {
352                        let br = global.binding.as_ref().unwrap();
353                        self.options.binding_map.get(br).cloned()
354                    } else {
355                        None
356                    };
357
358                    // Write all the layout qualifiers
359                    if layout_binding.is_some() || storage_format_access.is_some() {
360                        write!(self.out, "layout(")?;
361                        if let Some(binding) = layout_binding {
362                            write!(self.out, "binding = {binding}")?;
363                        }
364                        if let Some((format, _)) = storage_format_access {
365                            let format_str = glsl_storage_format(format)?;
366                            let separator = match layout_binding {
367                                Some(_) => ",",
368                                None => "",
369                            };
370                            write!(self.out, "{separator}{format_str}")?;
371                        }
372                        write!(self.out, ") ")?;
373                    }
374
375                    if let Some((_, access)) = storage_format_access {
376                        self.write_storage_access(access)?;
377                    }
378
379                    // All images in glsl are `uniform`
380                    // The trailing space is important
381                    write!(self.out, "uniform ")?;
382
383                    // write the type
384                    //
385                    // This is way we need the leading space because `write_image_type` doesn't add
386                    // any spaces at the beginning or end
387                    self.write_image_type(dim, arrayed, class)?;
388
389                    // Finally write the name and end the global with a `;`
390                    // The leading space is important
391                    let global_name = self.get_global_name(handle, global);
392                    writeln!(self.out, " {global_name};")?;
393                    writeln!(self.out)?;
394
395                    self.reflection_names_globals.insert(handle, global_name);
396                }
397                // glsl has no concept of samplers so we just ignore it
398                TypeInner::Sampler { .. } => continue,
399                // All other globals are written by `write_global`
400                _ => {
401                    self.write_global(handle, global)?;
402                    // Add a newline (only for readability)
403                    writeln!(self.out)?;
404                }
405            }
406        }
407
408        for arg in self.entry_point.function.arguments.iter() {
409            self.write_varying(arg.binding.as_ref(), arg.ty, false)?;
410        }
411        if let Some(ref result) = self.entry_point.function.result {
412            self.write_varying(result.binding.as_ref(), result.ty, true)?;
413        }
414        writeln!(self.out)?;
415
416        // Write all regular functions
417        for (handle, function) in self.module.functions.iter() {
418            // Check that the function doesn't use globals that aren't supported
419            // by the current entry point
420            if !include_unused && !ep_info.dominates_global_use(&self.info[handle]) {
421                continue;
422            }
423
424            let fun_info = &self.info[handle];
425
426            // Skip functions that that are not compatible with this entry point's stage.
427            //
428            // When validation is enabled, it rejects modules whose entry points try to call
429            // incompatible functions, so if we got this far, then any functions incompatible
430            // with our selected entry point must not be used.
431            //
432            // When validation is disabled, `fun_info.available_stages` is always just
433            // `ShaderStages::all()`, so this will write all functions in the module, and
434            // the downstream GLSL compiler will catch any problems.
435            if !fun_info.available_stages.contains(ep_info.available_stages) {
436                continue;
437            }
438
439            // Write the function
440            self.write_function(back::FunctionType::Function(handle), function, fun_info)?;
441
442            writeln!(self.out)?;
443        }
444
445        self.write_function(
446            back::FunctionType::EntryPoint(self.entry_point_idx),
447            &self.entry_point.function,
448            ep_info,
449        )?;
450
451        // Add newline at the end of file
452        writeln!(self.out)?;
453
454        // Collect all reflection info and return it to the user
455        self.collect_reflection_info()
456    }
457
458    fn write_array_size(
459        &mut self,
460        base: Handle<crate::Type>,
461        size: crate::ArraySize,
462    ) -> BackendResult {
463        write!(self.out, "[")?;
464
465        // Write the array size
466        // Writes nothing if `IndexableLength::Dynamic`
467        match size.resolve(self.module.to_ctx())? {
468            proc::IndexableLength::Known(size) => {
469                write!(self.out, "{size}")?;
470            }
471            proc::IndexableLength::Dynamic => (),
472        }
473
474        write!(self.out, "]")?;
475
476        if let TypeInner::Array {
477            base: next_base,
478            size: next_size,
479            ..
480        } = self.module.types[base].inner
481        {
482            self.write_array_size(next_base, next_size)?;
483        }
484
485        Ok(())
486    }
487
488    /// Helper method used to write value types
489    ///
490    /// # Notes
491    /// Adds no trailing or leading whitespace
492    fn write_value_type(&mut self, inner: &TypeInner) -> BackendResult {
493        match *inner {
494            // Scalars are simple we just get the full name from `glsl_scalar`
495            TypeInner::Scalar(scalar)
496            | TypeInner::Atomic(scalar)
497            | TypeInner::ValuePointer {
498                size: None,
499                scalar,
500                space: _,
501            } => write!(self.out, "{}", glsl_scalar(scalar)?.full)?,
502            // Vectors are just `gvecN` where `g` is the scalar prefix and `N` is the vector size
503            TypeInner::Vector { size, scalar }
504            | TypeInner::ValuePointer {
505                size: Some(size),
506                scalar,
507                space: _,
508            } => write!(self.out, "{}vec{}", glsl_scalar(scalar)?.prefix, size as u8)?,
509            // Matrices are written with `gmatMxN` where `g` is the scalar prefix (only floats and
510            // doubles are allowed), `M` is the columns count and `N` is the rows count
511            //
512            // glsl supports a matrix shorthand `gmatN` where `N` = `M` but it doesn't justify the
513            // extra branch to write matrices this way
514            TypeInner::Matrix {
515                columns,
516                rows,
517                scalar,
518            } => write!(
519                self.out,
520                "{}mat{}x{}",
521                glsl_scalar(scalar)?.prefix,
522                columns as u8,
523                rows as u8
524            )?,
525            // GLSL arrays are written as `type name[size]`
526            // Here we only write the size of the array i.e. `[size]`
527            // Base `type` and `name` should be written outside
528            TypeInner::Array { base, size, .. } => self.write_array_size(base, size)?,
529            // Write all variants instead of `_` so that if new variants are added a
530            // no exhaustiveness error is thrown
531            TypeInner::Pointer { .. }
532            | TypeInner::Struct { .. }
533            | TypeInner::Image { .. }
534            | TypeInner::Sampler { .. }
535            | TypeInner::AccelerationStructure { .. }
536            | TypeInner::RayQuery { .. }
537            | TypeInner::BindingArray { .. }
538            | TypeInner::CooperativeMatrix { .. } => {
539                return Err(Error::Custom(format!("Unable to write type {inner:?}")))
540            }
541        }
542
543        Ok(())
544    }
545
546    /// Helper method used to write non image/sampler types
547    ///
548    /// # Notes
549    /// Adds no trailing or leading whitespace
550    fn write_type(&mut self, ty: Handle<crate::Type>) -> BackendResult {
551        match self.module.types[ty].inner {
552            // glsl has no pointer types so just write types as normal and loads are skipped
553            TypeInner::Pointer { base, .. } => self.write_type(base),
554            // glsl structs are written as just the struct name
555            TypeInner::Struct { .. } => {
556                // Get the struct name
557                let name = &self.names[&NameKey::Type(ty)];
558                write!(self.out, "{name}")?;
559                Ok(())
560            }
561            // glsl array has the size separated from the base type
562            TypeInner::Array { base, .. } => self.write_type(base),
563            ref other => self.write_value_type(other),
564        }
565    }
566
567    /// Helper method to write a image type
568    ///
569    /// # Notes
570    /// Adds no leading or trailing whitespace
571    fn write_image_type(
572        &mut self,
573        dim: crate::ImageDimension,
574        arrayed: bool,
575        class: crate::ImageClass,
576    ) -> BackendResult {
577        // glsl images consist of four parts the scalar prefix, the image "type", the dimensions
578        // and modifiers
579        //
580        // There exists two image types
581        // - sampler - for sampled images
582        // - image - for storage images
583        //
584        // There are three possible modifiers that can be used together and must be written in
585        // this order to be valid
586        // - MS - used if it's a multisampled image
587        // - Array - used if it's an image array
588        // - Shadow - used if it's a depth image
589        use crate::ImageClass as Ic;
590        use crate::Scalar as S;
591        let float = S {
592            kind: crate::ScalarKind::Float,
593            width: 4,
594        };
595        let (base, scalar, ms, comparison) = match class {
596            Ic::Sampled { kind, multi: true } => ("sampler", S { kind, width: 4 }, "MS", ""),
597            Ic::Sampled { kind, multi: false } => ("sampler", S { kind, width: 4 }, "", ""),
598            Ic::Depth { multi: true } => ("sampler", float, "MS", ""),
599            Ic::Depth { multi: false } => ("sampler", float, "", "Shadow"),
600            Ic::Storage { format, .. } => ("image", format.into(), "", ""),
601            Ic::External => unimplemented!(),
602        };
603
604        let precision = if self.options.version.is_es() {
605            "highp "
606        } else {
607            ""
608        };
609
610        write!(
611            self.out,
612            "{}{}{}{}{}{}{}",
613            precision,
614            glsl_scalar(scalar)?.prefix,
615            base,
616            glsl_dimension(dim),
617            ms,
618            if arrayed { "Array" } else { "" },
619            comparison
620        )?;
621
622        Ok(())
623    }
624
625    /// Helper method used by [Self::write_global] to write just the layout part of
626    /// a non image/sampler global variable, if applicable.
627    ///
628    /// # Notes
629    ///
630    /// Adds trailing whitespace if any layout qualifier is written
631    fn write_global_layout(&mut self, global: &crate::GlobalVariable) -> BackendResult {
632        // Determine which (if any) explicit memory layout to use, and whether we support it
633        let layout = match global.space {
634            crate::AddressSpace::Uniform => {
635                if !self.options.version.supports_std140_layout() {
636                    return Err(Error::Custom(
637                        "Uniform address space requires std140 layout support".to_string(),
638                    ));
639                }
640
641                Some("std140")
642            }
643            crate::AddressSpace::Storage { .. } => {
644                if !self.options.version.supports_std430_layout() {
645                    return Err(Error::Custom(
646                        "Storage address space requires std430 layout support".to_string(),
647                    ));
648                }
649
650                Some("std430")
651            }
652            _ => None,
653        };
654
655        // If our version supports explicit layouts, we can also output the explicit binding
656        // if we have it
657        if self.options.version.supports_explicit_locations() {
658            if let Some(ref br) = global.binding {
659                match self.options.binding_map.get(br) {
660                    Some(binding) => {
661                        write!(self.out, "layout(")?;
662
663                        if let Some(layout) = layout {
664                            write!(self.out, "{layout}, ")?;
665                        }
666
667                        write!(self.out, "binding = {binding}) ")?;
668
669                        return Ok(());
670                    }
671                    None => {
672                        log::debug!("unassigned binding for {:?}", global.name);
673                    }
674                }
675            }
676        }
677
678        // Either no explicit bindings are supported or we didn't have any.
679        // Write just the memory layout.
680        if let Some(layout) = layout {
681            write!(self.out, "layout({layout}) ")?;
682        }
683
684        Ok(())
685    }
686
687    /// Helper method used to write non images/sampler globals
688    ///
689    /// # Notes
690    /// Adds a newline
691    ///
692    /// # Panics
693    /// If the global has type sampler
694    fn write_global(
695        &mut self,
696        handle: Handle<crate::GlobalVariable>,
697        global: &crate::GlobalVariable,
698    ) -> BackendResult {
699        self.write_global_layout(global)?;
700
701        if let crate::AddressSpace::Storage { access } = global.space {
702            self.write_storage_access(access)?;
703            if global
704                .memory_decorations
705                .contains(crate::MemoryDecorations::COHERENT)
706            {
707                write!(self.out, "coherent ")?;
708            }
709            if global
710                .memory_decorations
711                .contains(crate::MemoryDecorations::VOLATILE)
712            {
713                write!(self.out, "volatile ")?;
714            }
715        }
716
717        if let Some(storage_qualifier) = glsl_storage_qualifier(global.space) {
718            write!(self.out, "{storage_qualifier} ")?;
719        }
720
721        match global.space {
722            crate::AddressSpace::Private => {
723                self.write_simple_global(handle, global)?;
724            }
725            crate::AddressSpace::WorkGroup => {
726                self.write_simple_global(handle, global)?;
727            }
728            crate::AddressSpace::Immediate => {
729                self.write_simple_global(handle, global)?;
730            }
731            crate::AddressSpace::Uniform => {
732                self.write_interface_block(handle, global)?;
733            }
734            crate::AddressSpace::Storage { .. } => {
735                self.write_interface_block(handle, global)?;
736            }
737            crate::AddressSpace::TaskPayload => {
738                self.write_interface_block(handle, global)?;
739            }
740            // A global variable in the `Function` address space is a
741            // contradiction in terms.
742            crate::AddressSpace::Function => unreachable!(),
743            // Textures and samplers are handled directly in `Writer::write`.
744            crate::AddressSpace::Handle => unreachable!(),
745            // ray tracing pipelines unsupported
746            crate::AddressSpace::RayPayload | crate::AddressSpace::IncomingRayPayload => {
747                unreachable!()
748            }
749        }
750
751        Ok(())
752    }
753
754    fn write_simple_global(
755        &mut self,
756        handle: Handle<crate::GlobalVariable>,
757        global: &crate::GlobalVariable,
758    ) -> BackendResult {
759        self.write_type(global.ty)?;
760        write!(self.out, " ")?;
761        self.write_global_name(handle, global)?;
762
763        if let TypeInner::Array { base, size, .. } = self.module.types[global.ty].inner {
764            self.write_array_size(base, size)?;
765        }
766
767        if global.space.initializable() && is_value_init_supported(self.module, global.ty) {
768            write!(self.out, " = ")?;
769            if let Some(init) = global.init {
770                self.write_const_expr(init, &self.module.global_expressions)?;
771            } else {
772                self.write_zero_init_value(global.ty)?;
773            }
774        }
775
776        writeln!(self.out, ";")?;
777
778        if let crate::AddressSpace::Immediate = global.space {
779            let global_name = self.get_global_name(handle, global);
780            self.reflection_names_globals.insert(handle, global_name);
781        }
782
783        Ok(())
784    }
785
786    /// Write an interface block for a single Naga global.
787    ///
788    /// Write `block_name { members }`. Since `block_name` must be unique
789    /// between blocks and structs, we add `_block_ID` where `ID` is a
790    /// `IdGenerator` generated number. Write `members` in the same way we write
791    /// a struct's members.
792    fn write_interface_block(
793        &mut self,
794        handle: Handle<crate::GlobalVariable>,
795        global: &crate::GlobalVariable,
796    ) -> BackendResult {
797        // Write the block name, it's just the struct name appended with `_block_ID`
798        let ty_name = &self.names[&NameKey::Type(global.ty)];
799        let block_name = format!(
800            "{}_block_{}{:?}",
801            // avoid double underscores as they are reserved in GLSL
802            ty_name.trim_end_matches('_'),
803            self.block_id.generate(),
804            self.entry_point.stage,
805        );
806        write!(self.out, "{block_name} ")?;
807        self.reflection_names_globals.insert(handle, block_name);
808
809        match self.module.types[global.ty].inner {
810            TypeInner::Struct { ref members, .. }
811                if self.module.types[members.last().unwrap().ty]
812                    .inner
813                    .is_dynamically_sized(&self.module.types) =>
814            {
815                // Structs with dynamically sized arrays must have their
816                // members lifted up as members of the interface block. GLSL
817                // can't write such struct types anyway.
818                self.write_struct_body(global.ty, members)?;
819                write!(self.out, " ")?;
820                self.write_global_name(handle, global)?;
821            }
822            _ => {
823                // A global of any other type is written as the sole member
824                // of the interface block. Since the interface block is
825                // anonymous, this becomes visible in the global scope.
826                write!(self.out, "{{ ")?;
827                self.write_type(global.ty)?;
828                write!(self.out, " ")?;
829                self.write_global_name(handle, global)?;
830                if let TypeInner::Array { base, size, .. } = self.module.types[global.ty].inner {
831                    self.write_array_size(base, size)?;
832                }
833                write!(self.out, "; }}")?;
834            }
835        }
836
837        writeln!(self.out, ";")?;
838
839        Ok(())
840    }
841
842    /// Helper method used to find which expressions of a given function require baking
843    ///
844    /// # Notes
845    /// Clears `need_bake_expressions` set before adding to it
846    fn update_expressions_to_bake(&mut self, func: &crate::Function, info: &valid::FunctionInfo) {
847        use crate::Expression;
848        self.need_bake_expressions.clear();
849        for (fun_handle, expr) in func.expressions.iter() {
850            let expr_info = &info[fun_handle];
851            let min_ref_count = func.expressions[fun_handle].bake_ref_count();
852            if min_ref_count <= expr_info.ref_count {
853                self.need_bake_expressions.insert(fun_handle);
854            }
855
856            let inner = expr_info.ty.inner_with(&self.module.types);
857
858            if let Expression::Math {
859                fun,
860                arg,
861                arg1,
862                arg2,
863                ..
864            } = *expr
865            {
866                match fun {
867                    crate::MathFunction::Dot => {
868                        // if the expression is a Dot product with integer arguments,
869                        // then the args needs baking as well
870                        if let TypeInner::Scalar(crate::Scalar {
871                            kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
872                            ..
873                        }) = *inner
874                        {
875                            self.need_bake_expressions.insert(arg);
876                            self.need_bake_expressions.insert(arg1.unwrap());
877                        }
878                    }
879                    crate::MathFunction::Dot4U8Packed | crate::MathFunction::Dot4I8Packed => {
880                        self.need_bake_expressions.insert(arg);
881                        self.need_bake_expressions.insert(arg1.unwrap());
882                    }
883                    crate::MathFunction::Pack4xI8
884                    | crate::MathFunction::Pack4xU8
885                    | crate::MathFunction::Pack4xI8Clamp
886                    | crate::MathFunction::Pack4xU8Clamp
887                    | crate::MathFunction::Unpack4xI8
888                    | crate::MathFunction::Unpack4xU8
889                    | crate::MathFunction::QuantizeToF16 => {
890                        self.need_bake_expressions.insert(arg);
891                    }
892                    /* crate::MathFunction::Pack4x8unorm | */
893                    crate::MathFunction::Unpack4x8snorm
894                        if !self.options.version.supports_pack_unpack_4x8() =>
895                    {
896                        // We have a fallback if the platform doesn't natively support these
897                        self.need_bake_expressions.insert(arg);
898                    }
899                    /* crate::MathFunction::Pack4x8unorm | */
900                    crate::MathFunction::Unpack4x8unorm
901                        if !self.options.version.supports_pack_unpack_4x8() =>
902                    {
903                        self.need_bake_expressions.insert(arg);
904                    }
905                    /* crate::MathFunction::Pack2x16snorm |  */
906                    crate::MathFunction::Unpack2x16snorm
907                        if !self.options.version.supports_pack_unpack_snorm_2x16() =>
908                    {
909                        self.need_bake_expressions.insert(arg);
910                    }
911                    /* crate::MathFunction::Pack2x16unorm | */
912                    crate::MathFunction::Unpack2x16unorm
913                        if !self.options.version.supports_pack_unpack_unorm_2x16() =>
914                    {
915                        self.need_bake_expressions.insert(arg);
916                    }
917                    crate::MathFunction::ExtractBits => {
918                        // Only argument 1 is re-used.
919                        self.need_bake_expressions.insert(arg1.unwrap());
920                    }
921                    crate::MathFunction::InsertBits => {
922                        // Only argument 2 is re-used.
923                        self.need_bake_expressions.insert(arg2.unwrap());
924                    }
925                    crate::MathFunction::CountLeadingZeros => {
926                        if let Some(crate::ScalarKind::Sint) = inner.scalar_kind() {
927                            self.need_bake_expressions.insert(arg);
928                        }
929                    }
930                    _ => {}
931                }
932            }
933
934            if let Expression::Binary {
935                op: crate::BinaryOperator::Modulo,
936                left,
937                right,
938            } = *expr
939            {
940                // Integer `%` is lowered to `left - right * (left / right)` in
941                // write_expr (`BinaryOperation::ModuloInt`), which references each
942                // operand twice, so bake both to avoid re-evaluating them.
943                if let Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint) = inner.scalar_kind()
944                {
945                    self.need_bake_expressions.insert(left);
946                    self.need_bake_expressions.insert(right);
947                }
948            }
949        }
950
951        for statement in func.body.iter() {
952            match *statement {
953                crate::Statement::Atomic {
954                    fun: crate::AtomicFunction::Exchange { compare: Some(cmp) },
955                    ..
956                } => {
957                    self.need_bake_expressions.insert(cmp);
958                }
959                _ => {}
960            }
961        }
962    }
963
964    /// Helper method used to get a name for a global
965    ///
966    /// Globals have different naming schemes depending on their binding:
967    /// - Globals without bindings use the name from the [`Namer`](crate::proc::Namer)
968    /// - Globals with resource binding are named `_group_X_binding_Y` where `X`
969    ///   is the group and `Y` is the binding
970    fn get_global_name(
971        &self,
972        handle: Handle<crate::GlobalVariable>,
973        global: &crate::GlobalVariable,
974    ) -> String {
975        match (&global.binding, global.space) {
976            (&Some(ref br), _) => {
977                format!(
978                    "_group_{}_binding_{}_{}",
979                    br.group,
980                    br.binding,
981                    shader_stage_to_str(self.entry_point.stage)
982                )
983            }
984            (&None, crate::AddressSpace::Immediate) => {
985                format!(
986                    "_immediates_binding_{}",
987                    shader_stage_to_str(self.entry_point.stage)
988                )
989            }
990            (&None, _) => self.names[&NameKey::GlobalVariable(handle)].clone(),
991        }
992    }
993
994    /// Helper method used to write a name for a global without additional heap allocation
995    fn write_global_name(
996        &mut self,
997        handle: Handle<crate::GlobalVariable>,
998        global: &crate::GlobalVariable,
999    ) -> BackendResult {
1000        match (&global.binding, global.space) {
1001            (&Some(ref br), _) => write!(
1002                self.out,
1003                "_group_{}_binding_{}_{}",
1004                br.group,
1005                br.binding,
1006                shader_stage_to_str(self.entry_point.stage)
1007            )?,
1008            (&None, crate::AddressSpace::Immediate) => write!(
1009                self.out,
1010                "_immediates_binding_{}",
1011                shader_stage_to_str(self.entry_point.stage)
1012            )?,
1013            (&None, _) => write!(self.out, "{}", self.names[&NameKey::GlobalVariable(handle)])?,
1014        }
1015
1016        Ok(())
1017    }
1018
1019    /// Write a GLSL global that will carry a Naga entry point's argument or return value.
1020    ///
1021    /// A Naga entry point's arguments and return value are rendered in GLSL as
1022    /// variables at global scope with the `in` and `out` storage qualifiers.
1023    /// The code we generate for `main` loads from all the `in` globals into
1024    /// appropriately named locals. Before it returns, `main` assigns the
1025    /// components of its return value into all the `out` globals.
1026    ///
1027    /// This function writes a declaration for one such GLSL global,
1028    /// representing a value passed into or returned from [`self.entry_point`]
1029    /// that has a [`Location`] binding. The global's name is generated based on
1030    /// the location index and the shader stages being connected; see
1031    /// [`VaryingName`]. This means we don't need to know the names of
1032    /// arguments, just their types and bindings.
1033    ///
1034    /// Emit nothing for entry point arguments or return values with [`BuiltIn`]
1035    /// bindings; `main` will read from or assign to the appropriate GLSL
1036    /// special variable; these are pre-declared. As an exception, we do declare
1037    /// `gl_Position` or `gl_FragCoord` with the `invariant` qualifier if
1038    /// needed.
1039    ///
1040    /// Use `output` together with [`self.entry_point.stage`] to determine which
1041    /// shader stages are being connected, and choose the `in` or `out` storage
1042    /// qualifier.
1043    ///
1044    /// [`self.entry_point`]: Writer::entry_point
1045    /// [`self.entry_point.stage`]: crate::EntryPoint::stage
1046    /// [`Location`]: crate::Binding::Location
1047    /// [`BuiltIn`]: crate::Binding::BuiltIn
1048    fn write_varying(
1049        &mut self,
1050        binding: Option<&crate::Binding>,
1051        ty: Handle<crate::Type>,
1052        output: bool,
1053    ) -> Result<(), Error> {
1054        // For a struct, emit a separate global for each member with a binding.
1055        if let TypeInner::Struct { ref members, .. } = self.module.types[ty].inner {
1056            for member in members {
1057                self.write_varying(member.binding.as_ref(), member.ty, output)?;
1058            }
1059            return Ok(());
1060        }
1061
1062        let binding = match binding {
1063            None => return Ok(()),
1064            Some(binding) => binding,
1065        };
1066
1067        let (location, interpolation, sampling, blend_src) = match *binding {
1068            crate::Binding::Location {
1069                location,
1070                interpolation,
1071                sampling,
1072                blend_src,
1073                per_primitive: _,
1074            } => (location, interpolation, sampling, blend_src),
1075            crate::Binding::BuiltIn(built_in) => {
1076                match built_in {
1077                    crate::BuiltIn::Position { invariant: true } => {
1078                        match (self.options.version, self.entry_point.stage) {
1079                            (
1080                                Version::Embedded {
1081                                    version: 300,
1082                                    is_webgl: true,
1083                                },
1084                                ShaderStage::Fragment,
1085                            ) => {
1086                                // `invariant gl_FragCoord` is not allowed in WebGL2 and possibly
1087                                // OpenGL ES in general (waiting on confirmation).
1088                                //
1089                                // See https://github.com/KhronosGroup/WebGL/issues/3518
1090                            }
1091                            _ => {
1092                                writeln!(
1093                                    self.out,
1094                                    "invariant {};",
1095                                    glsl_built_in(
1096                                        built_in,
1097                                        VaryingOptions::from_writer_options(self.options, output)
1098                                    )
1099                                )?;
1100                            }
1101                        }
1102                    }
1103                    crate::BuiltIn::ClipDistances => {
1104                        // Re-declare `gl_ClipDistance` with number of clip planes.
1105                        let TypeInner::Array { size, .. } = self.module.types[ty].inner else {
1106                            unreachable!();
1107                        };
1108                        let proc::IndexableLength::Known(size) =
1109                            size.resolve(self.module.to_ctx())?
1110                        else {
1111                            unreachable!();
1112                        };
1113                        self.clip_distance_count = size;
1114                        writeln!(self.out, "out float gl_ClipDistance[{size}];")?;
1115                    }
1116                    _ => {}
1117                }
1118                return Ok(());
1119            }
1120        };
1121
1122        // Write the interpolation modifier if needed
1123        //
1124        // We ignore all interpolation and auxiliary modifiers that aren't used in fragment
1125        // shaders' input globals or vertex shaders' output globals.
1126        let emit_interpolation_and_auxiliary = match self.entry_point.stage {
1127            ShaderStage::Vertex => output,
1128            ShaderStage::Fragment => !output,
1129            ShaderStage::Compute => false,
1130            ShaderStage::Task
1131            | ShaderStage::Mesh
1132            | ShaderStage::RayGeneration
1133            | ShaderStage::AnyHit
1134            | ShaderStage::ClosestHit
1135            | ShaderStage::Miss => unreachable!(),
1136        };
1137
1138        // Write the I/O locations, if allowed
1139        let io_location = if self.options.version.supports_explicit_locations()
1140            || !emit_interpolation_and_auxiliary
1141        {
1142            if self.options.version.supports_io_locations() {
1143                if let Some(blend_src) = blend_src {
1144                    write!(
1145                        self.out,
1146                        "layout(location = {location}, index = {blend_src}) "
1147                    )?;
1148                } else {
1149                    write!(self.out, "layout(location = {location}) ")?;
1150                }
1151                None
1152            } else {
1153                Some(VaryingLocation {
1154                    location,
1155                    index: blend_src.unwrap_or(0),
1156                })
1157            }
1158        } else {
1159            None
1160        };
1161
1162        // Write the interpolation qualifier.
1163        if let Some(interp) = interpolation {
1164            if emit_interpolation_and_auxiliary {
1165                write!(self.out, "{} ", glsl_interpolation(interp))?;
1166            }
1167        }
1168
1169        // Write the sampling auxiliary qualifier.
1170        //
1171        // Before GLSL 4.2, the `centroid` and `sample` qualifiers were required to appear
1172        // immediately before the `in` / `out` qualifier, so we'll just follow that rule
1173        // here, regardless of the version.
1174        if let Some(sampling) = sampling {
1175            if emit_interpolation_and_auxiliary {
1176                if let Some(qualifier) = glsl_sampling(sampling)? {
1177                    write!(self.out, "{qualifier} ")?;
1178                }
1179            }
1180        }
1181
1182        // Write the input/output qualifier.
1183        write!(self.out, "{} ", if output { "out" } else { "in" })?;
1184
1185        // Write the type
1186        // `write_type` adds no leading or trailing spaces
1187        self.write_type(ty)?;
1188
1189        // Finally write the global name and end the global with a `;` and a newline
1190        // Leading space is important
1191        let vname = VaryingName {
1192            binding: &crate::Binding::Location {
1193                location,
1194                interpolation: None,
1195                sampling: None,
1196                blend_src,
1197                per_primitive: false,
1198            },
1199            stage: self.entry_point.stage,
1200            options: VaryingOptions::from_writer_options(self.options, output),
1201        };
1202        writeln!(self.out, " {vname};")?;
1203
1204        if let Some(location) = io_location {
1205            self.varying.insert(vname.to_string(), location);
1206        }
1207
1208        Ok(())
1209    }
1210
1211    /// Helper method used to write functions (both entry points and regular functions)
1212    ///
1213    /// # Notes
1214    /// Adds a newline
1215    fn write_function(
1216        &mut self,
1217        ty: back::FunctionType,
1218        func: &crate::Function,
1219        info: &valid::FunctionInfo,
1220    ) -> BackendResult {
1221        // Create a function context for the function being written
1222        let ctx = back::FunctionCtx {
1223            ty,
1224            info,
1225            expressions: &func.expressions,
1226            named_expressions: &func.named_expressions,
1227        };
1228
1229        self.named_expressions.clear();
1230        self.update_expressions_to_bake(func, info);
1231
1232        // Write the function header
1233        //
1234        // glsl headers are the same as in c:
1235        // `ret_type name(args)`
1236        // `ret_type` is the return type
1237        // `name` is the function name
1238        // `args` is a comma separated list of `type name`
1239        //  | - `type` is the argument type
1240        //  | - `name` is the argument name
1241
1242        // Start by writing the return type if any otherwise write void
1243        // This is the only place where `void` is a valid type
1244        // (though it's more a keyword than a type)
1245        if let back::FunctionType::EntryPoint(_) = ctx.ty {
1246            write!(self.out, "void")?;
1247        } else if let Some(ref result) = func.result {
1248            self.write_type(result.ty)?;
1249            if let TypeInner::Array { base, size, .. } = self.module.types[result.ty].inner {
1250                self.write_array_size(base, size)?
1251            }
1252        } else {
1253            write!(self.out, "void")?;
1254        }
1255
1256        // Write the function name and open parentheses for the argument list
1257        let function_name = match ctx.ty {
1258            back::FunctionType::Function(handle) => &self.names[&NameKey::Function(handle)],
1259            back::FunctionType::EntryPoint(_) => "main",
1260        };
1261        write!(self.out, " {function_name}(")?;
1262
1263        // Write the comma separated argument list
1264        //
1265        // We need access to `Self` here so we use the reference passed to the closure as an
1266        // argument instead of capturing as that would cause a borrow checker error
1267        let arguments = match ctx.ty {
1268            back::FunctionType::EntryPoint(_) => &[][..],
1269            back::FunctionType::Function(_) => &func.arguments,
1270        };
1271        let arguments: Vec<_> = arguments
1272            .iter()
1273            .enumerate()
1274            .filter(|&(_, arg)| match self.module.types[arg.ty].inner {
1275                TypeInner::Sampler { .. } => false,
1276                _ => true,
1277            })
1278            .collect();
1279        self.write_slice(&arguments, |this, _, &(i, arg)| {
1280            // Write the argument type
1281            match this.module.types[arg.ty].inner {
1282                // We treat images separately because they might require
1283                // writing the storage format
1284                TypeInner::Image {
1285                    dim,
1286                    arrayed,
1287                    class,
1288                } => {
1289                    // Write the storage format if needed
1290                    if let TypeInner::Image {
1291                        class: crate::ImageClass::Storage { format, .. },
1292                        ..
1293                    } = this.module.types[arg.ty].inner
1294                    {
1295                        write!(this.out, "layout({}) ", glsl_storage_format(format)?)?;
1296                    }
1297
1298                    // write the type
1299                    //
1300                    // This is way we need the leading space because `write_image_type` doesn't add
1301                    // any spaces at the beginning or end
1302                    this.write_image_type(dim, arrayed, class)?;
1303                }
1304                TypeInner::Pointer { base, .. } => {
1305                    // write parameter qualifiers
1306                    write!(this.out, "inout ")?;
1307                    this.write_type(base)?;
1308                }
1309                // All other types are written by `write_type`
1310                _ => {
1311                    this.write_type(arg.ty)?;
1312                }
1313            }
1314
1315            // Write the argument name
1316            // The leading space is important
1317            write!(this.out, " {}", this.names[&ctx.argument_key(i as u32)])?;
1318
1319            // Write array size
1320            match this.module.types[arg.ty].inner {
1321                TypeInner::Array { base, size, .. } => {
1322                    this.write_array_size(base, size)?;
1323                }
1324                TypeInner::Pointer { base, .. } => {
1325                    if let TypeInner::Array { base, size, .. } = this.module.types[base].inner {
1326                        this.write_array_size(base, size)?;
1327                    }
1328                }
1329                _ => {}
1330            }
1331
1332            Ok(())
1333        })?;
1334
1335        // Close the parentheses and open braces to start the function body
1336        writeln!(self.out, ") {{")?;
1337
1338        if self.options.zero_initialize_workgroup_memory
1339            && ctx.ty.is_compute_like_entry_point(self.module)
1340        {
1341            self.write_workgroup_variables_initialization(&ctx)?;
1342        }
1343
1344        // Compose the function arguments from globals, in case of an entry point.
1345        if let back::FunctionType::EntryPoint(ep_index) = ctx.ty {
1346            let stage = self.module.entry_points[ep_index as usize].stage;
1347            for (index, arg) in func.arguments.iter().enumerate() {
1348                write!(self.out, "{}", back::INDENT)?;
1349                self.write_type(arg.ty)?;
1350                let name = &self.names[&NameKey::EntryPointArgument(ep_index, index as u32)];
1351                write!(self.out, " {name}")?;
1352                write!(self.out, " = ")?;
1353                match self.module.types[arg.ty].inner {
1354                    TypeInner::Struct { ref members, .. } => {
1355                        self.write_type(arg.ty)?;
1356                        write!(self.out, "(")?;
1357                        for (index, member) in members.iter().enumerate() {
1358                            let varying_name = VaryingName {
1359                                binding: member.binding.as_ref().unwrap(),
1360                                stage,
1361                                options: VaryingOptions::from_writer_options(self.options, false),
1362                            };
1363                            if index != 0 {
1364                                write!(self.out, ", ")?;
1365                            }
1366                            write!(self.out, "{varying_name}")?;
1367                        }
1368                        writeln!(self.out, ");")?;
1369                    }
1370                    _ => {
1371                        let varying_name = VaryingName {
1372                            binding: arg.binding.as_ref().unwrap(),
1373                            stage,
1374                            options: VaryingOptions::from_writer_options(self.options, false),
1375                        };
1376                        writeln!(self.out, "{varying_name};")?;
1377                    }
1378                }
1379            }
1380        }
1381
1382        // Write all function locals
1383        // Locals are `type name (= init)?;` where the init part (including the =) are optional
1384        //
1385        // Always adds a newline
1386        for (handle, local) in func.local_variables.iter() {
1387            // Write indentation (only for readability) and the type
1388            // `write_type` adds no trailing space
1389            write!(self.out, "{}", back::INDENT)?;
1390            self.write_type(local.ty)?;
1391
1392            // Write the local name
1393            // The leading space is important
1394            write!(self.out, " {}", self.names[&ctx.name_key(handle)])?;
1395            // Write size for array type
1396            if let TypeInner::Array { base, size, .. } = self.module.types[local.ty].inner {
1397                self.write_array_size(base, size)?;
1398            }
1399            // Write the local initializer if needed
1400            if let Some(init) = local.init {
1401                // Put the equal signal only if there's a initializer
1402                // The leading and trailing spaces aren't needed but help with readability
1403                write!(self.out, " = ")?;
1404
1405                // Write the constant
1406                // `write_constant` adds no trailing or leading space/newline
1407                self.write_expr(init, &ctx)?;
1408            } else if is_value_init_supported(self.module, local.ty) {
1409                write!(self.out, " = ")?;
1410                self.write_zero_init_value(local.ty)?;
1411            }
1412
1413            // Finish the local with `;` and add a newline (only for readability)
1414            writeln!(self.out, ";")?
1415        }
1416
1417        // Write the function body (statement list)
1418        for sta in func.body.iter() {
1419            // Write a statement, the indentation should always be 1 when writing the function body
1420            // `write_stmt` adds a newline
1421            self.write_stmt(sta, &ctx, back::Level(1))?;
1422        }
1423
1424        // Close braces and add a newline
1425        writeln!(self.out, "}}")?;
1426
1427        Ok(())
1428    }
1429
1430    fn write_workgroup_variables_initialization(
1431        &mut self,
1432        ctx: &back::FunctionCtx,
1433    ) -> BackendResult {
1434        let mut vars = self
1435            .module
1436            .global_variables
1437            .iter()
1438            .filter(|&(handle, var)| {
1439                !ctx.info[handle].is_empty() && var.space == crate::AddressSpace::WorkGroup
1440            })
1441            .peekable();
1442
1443        if vars.peek().is_some() {
1444            let level = back::Level(1);
1445
1446            writeln!(self.out, "{level}if (gl_LocalInvocationID == uvec3(0u)) {{")?;
1447
1448            for (handle, var) in vars {
1449                let name = &self.names[&NameKey::GlobalVariable(handle)];
1450                write!(self.out, "{}{} = ", level.next(), name)?;
1451                self.write_zero_init_value(var.ty)?;
1452                writeln!(self.out, ";")?;
1453            }
1454
1455            writeln!(self.out, "{level}}}")?;
1456            self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;
1457        }
1458
1459        Ok(())
1460    }
1461
1462    /// Write a list of comma separated `T` values using a writer function `F`.
1463    ///
1464    /// The writer function `F` receives a mutable reference to `self` that if needed won't cause
1465    /// borrow checker issues (using for example a closure with `self` will cause issues), the
1466    /// second argument is the 0 based index of the element on the list, and the last element is
1467    /// a reference to the element `T` being written
1468    ///
1469    /// # Notes
1470    /// - Adds no newlines or leading/trailing whitespace
1471    /// - The last element won't have a trailing `,`
1472    fn write_slice<T, F: FnMut(&mut Self, u32, &T) -> BackendResult>(
1473        &mut self,
1474        data: &[T],
1475        mut f: F,
1476    ) -> BackendResult {
1477        // Loop through `data` invoking `f` for each element
1478        for (index, item) in data.iter().enumerate() {
1479            if index != 0 {
1480                write!(self.out, ", ")?;
1481            }
1482            f(self, index as u32, item)?;
1483        }
1484
1485        Ok(())
1486    }
1487
1488    /// Helper method used to write global constants
1489    fn write_global_constant(&mut self, handle: Handle<crate::Constant>) -> BackendResult {
1490        write!(self.out, "const ")?;
1491        let constant = &self.module.constants[handle];
1492        self.write_type(constant.ty)?;
1493        let name = &self.names[&NameKey::Constant(handle)];
1494        write!(self.out, " {name}")?;
1495        if let TypeInner::Array { base, size, .. } = self.module.types[constant.ty].inner {
1496            self.write_array_size(base, size)?;
1497        }
1498        write!(self.out, " = ")?;
1499        self.write_const_expr(constant.init, &self.module.global_expressions)?;
1500        writeln!(self.out, ";")?;
1501        Ok(())
1502    }
1503
1504    /// Helper method used to output a dot product as an arithmetic expression
1505    ///
1506    fn write_dot_product(
1507        &mut self,
1508        arg: Handle<crate::Expression>,
1509        arg1: Handle<crate::Expression>,
1510        size: usize,
1511        ctx: &back::FunctionCtx,
1512    ) -> BackendResult {
1513        // Write parentheses around the dot product expression to prevent operators
1514        // with different precedences from applying earlier.
1515        write!(self.out, "(")?;
1516
1517        // Cycle through all the components of the vector
1518        for index in 0..size {
1519            let component = back::COMPONENTS[index];
1520            // Write the addition to the previous product
1521            // This will print an extra '+' at the beginning but that is fine in glsl
1522            write!(self.out, " + ")?;
1523            // Write the first vector expression, this expression is marked to be
1524            // cached so unless it can't be cached (for example, it's a Constant)
1525            // it shouldn't produce large expressions.
1526            self.write_expr(arg, ctx)?;
1527            // Access the current component on the first vector
1528            write!(self.out, ".{component} * ")?;
1529            // Write the second vector expression, this expression is marked to be
1530            // cached so unless it can't be cached (for example, it's a Constant)
1531            // it shouldn't produce large expressions.
1532            self.write_expr(arg1, ctx)?;
1533            // Access the current component on the second vector
1534            write!(self.out, ".{component}")?;
1535        }
1536
1537        write!(self.out, ")")?;
1538        Ok(())
1539    }
1540
1541    /// Helper method used to write structs
1542    ///
1543    /// # Notes
1544    /// Ends in a newline
1545    fn write_struct_body(
1546        &mut self,
1547        handle: Handle<crate::Type>,
1548        members: &[crate::StructMember],
1549    ) -> BackendResult {
1550        // glsl structs are written as in C
1551        // `struct name() { members };`
1552        //  | `struct` is a keyword
1553        //  | `name` is the struct name
1554        //  | `members` is a semicolon separated list of `type name`
1555        //      | `type` is the member type
1556        //      | `name` is the member name
1557        writeln!(self.out, "{{")?;
1558
1559        for (idx, member) in members.iter().enumerate() {
1560            // The indentation is only for readability
1561            write!(self.out, "{}", back::INDENT)?;
1562
1563            match self.module.types[member.ty].inner {
1564                TypeInner::Array {
1565                    base,
1566                    size,
1567                    stride: _,
1568                } => {
1569                    self.write_type(base)?;
1570                    write!(
1571                        self.out,
1572                        " {}",
1573                        self.names[&NameKey::StructMember(handle, idx as u32)]
1574                    )?;
1575                    // Write [size]
1576                    self.write_array_size(base, size)?;
1577                    // Newline is important
1578                    writeln!(self.out, ";")?;
1579                }
1580                _ => {
1581                    // Write the member type
1582                    // Adds no trailing space
1583                    self.write_type(member.ty)?;
1584
1585                    // Write the member name and put a semicolon
1586                    // The leading space is important
1587                    // All members must have a semicolon even the last one
1588                    writeln!(
1589                        self.out,
1590                        " {};",
1591                        self.names[&NameKey::StructMember(handle, idx as u32)]
1592                    )?;
1593                }
1594            }
1595        }
1596
1597        write!(self.out, "}}")?;
1598        Ok(())
1599    }
1600
1601    /// Helper method used to write statements
1602    ///
1603    /// # Notes
1604    /// Always adds a newline
1605    fn write_stmt(
1606        &mut self,
1607        sta: &crate::Statement,
1608        ctx: &back::FunctionCtx,
1609        level: back::Level,
1610    ) -> BackendResult {
1611        use crate::Statement;
1612
1613        match *sta {
1614            // This is where we can generate intermediate constants for some expression types.
1615            Statement::Emit(ref range) => {
1616                for handle in range.clone() {
1617                    let ptr_class = ctx.resolve_type(handle, &self.module.types).pointer_space();
1618                    let expr_name = if ptr_class.is_some() {
1619                        // GLSL can't save a pointer-valued expression in a variable,
1620                        // but we shouldn't ever need to: they should never be named expressions,
1621                        // and none of the expression types flagged by bake_ref_count can be pointer-valued.
1622                        None
1623                    } else if let Some(name) = ctx.named_expressions.get(&handle) {
1624                        // Front end provides names for all variables at the start of writing.
1625                        // But we write them to step by step. We need to recache them
1626                        // Otherwise, we could accidentally write variable name instead of full expression.
1627                        // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords.
1628                        Some(self.namer.call(name))
1629                    } else if self.need_bake_expressions.contains(&handle) {
1630                        Some(Baked(handle).to_string())
1631                    } else {
1632                        None
1633                    };
1634
1635                    // If we are going to write an `ImageLoad` next and the target image
1636                    // is sampled and we are using the `Restrict` policy for bounds
1637                    // checking images we need to write a local holding the clamped lod.
1638                    if let crate::Expression::ImageLoad {
1639                        image,
1640                        level: Some(level_expr),
1641                        ..
1642                    } = ctx.expressions[handle]
1643                    {
1644                        if let TypeInner::Image {
1645                            class: crate::ImageClass::Sampled { .. },
1646                            ..
1647                        } = *ctx.resolve_type(image, &self.module.types)
1648                        {
1649                            if let proc::BoundsCheckPolicy::Restrict = self.policies.image_load {
1650                                write!(self.out, "{level}")?;
1651                                self.write_clamped_lod(ctx, handle, image, level_expr)?
1652                            }
1653                        }
1654                    }
1655
1656                    if let Some(name) = expr_name {
1657                        write!(self.out, "{level}")?;
1658                        self.write_named_expr(handle, name, handle, ctx)?;
1659                    }
1660                }
1661            }
1662            // Blocks are simple we just need to write the block statements between braces
1663            // We could also just print the statements but this is more readable and maps more
1664            // closely to the IR
1665            Statement::Block(ref block) => {
1666                write!(self.out, "{level}")?;
1667                writeln!(self.out, "{{")?;
1668                for sta in block.iter() {
1669                    // Increase the indentation to help with readability
1670                    self.write_stmt(sta, ctx, level.next())?
1671                }
1672                writeln!(self.out, "{level}}}")?
1673            }
1674            // Ifs are written as in C:
1675            // ```
1676            // if(condition) {
1677            //  accept
1678            // } else {
1679            //  reject
1680            // }
1681            // ```
1682            Statement::If {
1683                condition,
1684                ref accept,
1685                ref reject,
1686            } => {
1687                write!(self.out, "{level}")?;
1688                write!(self.out, "if (")?;
1689                self.write_expr(condition, ctx)?;
1690                writeln!(self.out, ") {{")?;
1691
1692                for sta in accept {
1693                    // Increase indentation to help with readability
1694                    self.write_stmt(sta, ctx, level.next())?;
1695                }
1696
1697                // If there are no statements in the reject block we skip writing it
1698                // This is only for readability
1699                if !reject.is_empty() {
1700                    writeln!(self.out, "{level}}} else {{")?;
1701
1702                    for sta in reject {
1703                        // Increase indentation to help with readability
1704                        self.write_stmt(sta, ctx, level.next())?;
1705                    }
1706                }
1707
1708                writeln!(self.out, "{level}}}")?
1709            }
1710            // Switch are written as in C:
1711            // ```
1712            // switch (selector) {
1713            //      // Fallthrough
1714            //      case label:
1715            //          block
1716            //      // Non fallthrough
1717            //      case label:
1718            //          block
1719            //          break;
1720            //      default:
1721            //          block
1722            //  }
1723            //  ```
1724            //  Where the `default` case happens isn't important but we put it last
1725            //  so that we don't need to print a `break` for it
1726            Statement::Switch {
1727                selector,
1728                ref cases,
1729            } => {
1730                let l2 = level.next();
1731                // Some GLSL consumers may not handle switches with a single
1732                // body correctly: See wgpu#4514. Write such switch statements
1733                // as a `do {} while(false);` loop instead.
1734                //
1735                // Since doing so may inadvertently capture `continue`
1736                // statements in the switch body, we must apply continue
1737                // forwarding. See the `naga::back::continue_forward` module
1738                // docs for details.
1739                let one_body = cases
1740                    .iter()
1741                    .rev()
1742                    .skip(1)
1743                    .all(|case| case.fall_through && case.body.is_empty());
1744                if one_body {
1745                    // Unlike HLSL, in GLSL `continue_ctx` only needs to know
1746                    // about [`Switch`] statements that are being rendered as
1747                    // `do-while` loops.
1748                    if let Some(variable) = self.continue_ctx.enter_switch(&mut self.namer) {
1749                        writeln!(self.out, "{level}bool {variable} = false;",)?;
1750                    };
1751                    writeln!(self.out, "{level}do {{")?;
1752                    // Note: Expressions have no side-effects so we don't need to emit selector expression.
1753
1754                    // Body
1755                    if let Some(case) = cases.last() {
1756                        for sta in case.body.iter() {
1757                            self.write_stmt(sta, ctx, l2)?;
1758                        }
1759                    }
1760                    // End do-while
1761                    writeln!(self.out, "{level}}} while(false);")?;
1762
1763                    // Handle any forwarded continue statements.
1764                    use back::continue_forward::ExitControlFlow;
1765                    let op = match self.continue_ctx.exit_switch() {
1766                        ExitControlFlow::None => None,
1767                        ExitControlFlow::Continue { variable } => Some(("continue", variable)),
1768                        ExitControlFlow::Break { variable } => Some(("break", variable)),
1769                    };
1770                    if let Some((control_flow, variable)) = op {
1771                        writeln!(self.out, "{level}if ({variable}) {{")?;
1772                        writeln!(self.out, "{l2}{control_flow};")?;
1773                        writeln!(self.out, "{level}}}")?;
1774                    }
1775                } else {
1776                    // Start the switch
1777                    write!(self.out, "{level}")?;
1778                    write!(self.out, "switch(")?;
1779                    self.write_expr(selector, ctx)?;
1780                    writeln!(self.out, ") {{")?;
1781
1782                    // Write all cases
1783                    for case in cases {
1784                        match case.value {
1785                            crate::SwitchValue::I32(value) => {
1786                                write!(self.out, "{l2}case {value}:")?
1787                            }
1788                            crate::SwitchValue::U32(value) => {
1789                                write!(self.out, "{l2}case {value}u:")?
1790                            }
1791                            crate::SwitchValue::Default => write!(self.out, "{l2}default:")?,
1792                        }
1793
1794                        let write_block_braces = !(case.fall_through && case.body.is_empty());
1795                        if write_block_braces {
1796                            writeln!(self.out, " {{")?;
1797                        } else {
1798                            writeln!(self.out)?;
1799                        }
1800
1801                        for sta in case.body.iter() {
1802                            self.write_stmt(sta, ctx, l2.next())?;
1803                        }
1804
1805                        if !case.fall_through && case.body.last().is_none_or(|s| !s.is_terminator())
1806                        {
1807                            writeln!(self.out, "{}break;", l2.next())?;
1808                        }
1809
1810                        if write_block_braces {
1811                            writeln!(self.out, "{l2}}}")?;
1812                        }
1813                    }
1814
1815                    writeln!(self.out, "{level}}}")?
1816                }
1817            }
1818            // Loops in naga IR are based on wgsl loops, glsl can emulate the behaviour by using a
1819            // while true loop and appending the continuing block to the body resulting on:
1820            // ```
1821            // bool loop_init = true;
1822            // while(true) {
1823            //  if (!loop_init) { <continuing> }
1824            //  loop_init = false;
1825            //  <body>
1826            // }
1827            // ```
1828            Statement::Loop {
1829                ref body,
1830                ref continuing,
1831                break_if,
1832            } => {
1833                self.continue_ctx.enter_loop();
1834                if !continuing.is_empty() || break_if.is_some() {
1835                    let gate_name = self.namer.call("loop_init");
1836                    writeln!(self.out, "{level}bool {gate_name} = true;")?;
1837                    writeln!(self.out, "{level}while(true) {{")?;
1838                    let l2 = level.next();
1839                    let l3 = l2.next();
1840                    writeln!(self.out, "{l2}if (!{gate_name}) {{")?;
1841                    for sta in continuing {
1842                        self.write_stmt(sta, ctx, l3)?;
1843                    }
1844                    if let Some(condition) = break_if {
1845                        write!(self.out, "{l3}if (")?;
1846                        self.write_expr(condition, ctx)?;
1847                        writeln!(self.out, ") {{")?;
1848                        writeln!(self.out, "{}break;", l3.next())?;
1849                        writeln!(self.out, "{l3}}}")?;
1850                    }
1851                    writeln!(self.out, "{l2}}}")?;
1852                    writeln!(self.out, "{}{} = false;", level.next(), gate_name)?;
1853                } else {
1854                    writeln!(self.out, "{level}while(true) {{")?;
1855                }
1856                for sta in body {
1857                    self.write_stmt(sta, ctx, level.next())?;
1858                }
1859                writeln!(self.out, "{level}}}")?;
1860                self.continue_ctx.exit_loop();
1861            }
1862            // Break, continue and return as written as in C
1863            // `break;`
1864            Statement::Break => {
1865                write!(self.out, "{level}")?;
1866                writeln!(self.out, "break;")?
1867            }
1868            // `continue;`
1869            Statement::Continue => {
1870                // Sometimes we must render a `Continue` statement as a `break`.
1871                // See the docs for the `back::continue_forward` module.
1872                if let Some(variable) = self.continue_ctx.continue_encountered() {
1873                    writeln!(self.out, "{level}{variable} = true;",)?;
1874                    writeln!(self.out, "{level}break;")?
1875                } else {
1876                    writeln!(self.out, "{level}continue;")?
1877                }
1878            }
1879            // `return expr;`, `expr` is optional
1880            Statement::Return { value } => {
1881                write!(self.out, "{level}")?;
1882                match ctx.ty {
1883                    back::FunctionType::Function(_) => {
1884                        write!(self.out, "return")?;
1885                        // Write the expression to be returned if needed
1886                        if let Some(expr) = value {
1887                            write!(self.out, " ")?;
1888                            self.write_expr(expr, ctx)?;
1889                        }
1890                        writeln!(self.out, ";")?;
1891                    }
1892                    back::FunctionType::EntryPoint(ep_index) => {
1893                        let mut has_point_size = false;
1894                        let ep = &self.module.entry_points[ep_index as usize];
1895                        if let Some(ref result) = ep.function.result {
1896                            let value = value.unwrap();
1897                            match self.module.types[result.ty].inner {
1898                                TypeInner::Struct { ref members, .. } => {
1899                                    let temp_struct_name = match ctx.expressions[value] {
1900                                        crate::Expression::Compose { .. } => {
1901                                            let return_struct = "_tmp_return";
1902                                            write!(
1903                                                self.out,
1904                                                "{ty} {return_struct} = ",
1905                                                ty = self.names[&NameKey::Type(result.ty)],
1906                                            )?;
1907                                            self.write_expr(value, ctx)?;
1908                                            writeln!(self.out, ";")?;
1909                                            write!(self.out, "{level}")?;
1910                                            Some(return_struct)
1911                                        }
1912                                        _ => None,
1913                                    };
1914
1915                                    for (index, member) in members.iter().enumerate() {
1916                                        if let Some(crate::Binding::BuiltIn(
1917                                            crate::BuiltIn::PointSize,
1918                                        )) = member.binding
1919                                        {
1920                                            has_point_size = true;
1921                                        }
1922
1923                                        let varying_name = VaryingName {
1924                                            binding: member.binding.as_ref().unwrap(),
1925                                            stage: ep.stage,
1926                                            options: VaryingOptions::from_writer_options(
1927                                                self.options,
1928                                                true,
1929                                            ),
1930                                        };
1931                                        write!(self.out, "{varying_name} = ")?;
1932
1933                                        if let Some(struct_name) = temp_struct_name {
1934                                            write!(self.out, "{struct_name}")?;
1935                                        } else {
1936                                            self.write_expr(value, ctx)?;
1937                                        }
1938
1939                                        // Write field name
1940                                        writeln!(
1941                                            self.out,
1942                                            ".{};",
1943                                            self.names
1944                                                [&NameKey::StructMember(result.ty, index as u32)]
1945                                        )?;
1946                                        write!(self.out, "{level}")?;
1947                                    }
1948                                }
1949                                _ => {
1950                                    let name = VaryingName {
1951                                        binding: result.binding.as_ref().unwrap(),
1952                                        stage: ep.stage,
1953                                        options: VaryingOptions::from_writer_options(
1954                                            self.options,
1955                                            true,
1956                                        ),
1957                                    };
1958                                    write!(self.out, "{name} = ")?;
1959                                    self.write_expr(value, ctx)?;
1960                                    writeln!(self.out, ";")?;
1961                                    write!(self.out, "{level}")?;
1962                                }
1963                            }
1964                        }
1965
1966                        let is_vertex_stage = self.module.entry_points[ep_index as usize].stage
1967                            == ShaderStage::Vertex;
1968                        if is_vertex_stage
1969                            && self
1970                                .options
1971                                .writer_flags
1972                                .contains(WriterFlags::ADJUST_COORDINATE_SPACE)
1973                        {
1974                            writeln!(
1975                                self.out,
1976                                "gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);",
1977                            )?;
1978                            write!(self.out, "{level}")?;
1979                        }
1980
1981                        if is_vertex_stage
1982                            && self
1983                                .options
1984                                .writer_flags
1985                                .contains(WriterFlags::FORCE_POINT_SIZE)
1986                            && !has_point_size
1987                        {
1988                            writeln!(self.out, "gl_PointSize = 1.0;")?;
1989                            write!(self.out, "{level}")?;
1990                        }
1991                        writeln!(self.out, "return;")?;
1992                    }
1993                }
1994            }
1995            // This is one of the places were glsl adds to the syntax of C in this case the discard
1996            // keyword which ceases all further processing in a fragment shader, it's called OpKill
1997            // in spir-v that's why it's called `Statement::Kill`
1998            Statement::Kill => writeln!(self.out, "{level}discard;")?,
1999            Statement::ControlBarrier(flags) => {
2000                self.write_control_barrier(flags, level)?;
2001            }
2002            Statement::MemoryBarrier(flags) => {
2003                self.write_memory_barrier(flags, level)?;
2004            }
2005            // Stores in glsl are just variable assignments written as `pointer = value;`
2006            Statement::Store { pointer, value } => {
2007                write!(self.out, "{level}")?;
2008                let is_atomic_pointer = ctx
2009                    .resolve_type(pointer, &self.module.types)
2010                    .is_atomic_pointer(&self.module.types);
2011                if is_atomic_pointer {
2012                    write!(self.out, "atomicExchange(")?;
2013                    self.write_expr(pointer, ctx)?;
2014                    write!(self.out, ", ")?;
2015                    self.write_expr(value, ctx)?;
2016                    writeln!(self.out, ");")?
2017                } else {
2018                    self.write_expr(pointer, ctx)?;
2019                    write!(self.out, " = ")?;
2020                    self.write_expr(value, ctx)?;
2021                    writeln!(self.out, ";")?
2022                }
2023            }
2024            Statement::WorkGroupUniformLoad { pointer, result } => {
2025                // GLSL doesn't have pointers, which means that this backend needs to ensure that
2026                // the actual "loading" is happening between the two barriers.
2027                // This is done in `Emit` by never emitting a variable name for pointer variables
2028                self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;
2029
2030                let result_name = Baked(result).to_string();
2031                write!(self.out, "{level}")?;
2032                // Expressions cannot have side effects, so just writing the expression here is fine.
2033                self.write_named_expr(pointer, result_name, result, ctx)?;
2034
2035                self.write_control_barrier(crate::Barrier::WORK_GROUP, level)?;
2036            }
2037            // Stores a value into an image.
2038            Statement::ImageStore {
2039                image,
2040                coordinate,
2041                array_index,
2042                value,
2043            } => {
2044                write!(self.out, "{level}")?;
2045                self.write_image_store(ctx, image, coordinate, array_index, value)?
2046            }
2047            // A `Call` is written `name(arguments)` where `arguments` is a comma separated expressions list
2048            Statement::Call {
2049                function,
2050                ref arguments,
2051                result,
2052            } => {
2053                write!(self.out, "{level}")?;
2054                if let Some(expr) = result {
2055                    let name = Baked(expr).to_string();
2056                    let result = self.module.functions[function].result.as_ref().unwrap();
2057                    self.write_type(result.ty)?;
2058                    write!(self.out, " {name}")?;
2059                    if let TypeInner::Array { base, size, .. } = self.module.types[result.ty].inner
2060                    {
2061                        self.write_array_size(base, size)?
2062                    }
2063                    write!(self.out, " = ")?;
2064                    self.named_expressions.insert(expr, name);
2065                }
2066                write!(self.out, "{}(", self.names[&NameKey::Function(function)])?;
2067                let arguments: Vec<_> = arguments
2068                    .iter()
2069                    .enumerate()
2070                    .filter_map(|(i, arg)| {
2071                        let arg_ty = self.module.functions[function].arguments[i].ty;
2072                        match self.module.types[arg_ty].inner {
2073                            TypeInner::Sampler { .. } => None,
2074                            _ => Some(*arg),
2075                        }
2076                    })
2077                    .collect();
2078                self.write_slice(&arguments, |this, _, arg| this.write_expr(*arg, ctx))?;
2079                writeln!(self.out, ");")?
2080            }
2081            Statement::Atomic {
2082                pointer,
2083                ref fun,
2084                value,
2085                result,
2086            } => {
2087                write!(self.out, "{level}")?;
2088
2089                match *fun {
2090                    crate::AtomicFunction::Exchange {
2091                        compare: Some(compare_expr),
2092                    } => {
2093                        let result_handle = result.expect("CompareExchange must have a result");
2094                        let res_name = Baked(result_handle).to_string();
2095                        self.write_type(ctx.info[result_handle].ty.handle().unwrap())?;
2096                        write!(self.out, " {res_name};")?;
2097                        write!(self.out, " {res_name}.old_value = atomicCompSwap(")?;
2098                        self.write_expr(pointer, ctx)?;
2099                        write!(self.out, ", ")?;
2100                        self.write_expr(compare_expr, ctx)?;
2101                        write!(self.out, ", ")?;
2102                        self.write_expr(value, ctx)?;
2103                        writeln!(self.out, ");")?;
2104
2105                        write!(
2106                            self.out,
2107                            "{level}{res_name}.exchanged = ({res_name}.old_value == "
2108                        )?;
2109                        self.write_expr(compare_expr, ctx)?;
2110                        writeln!(self.out, ");")?;
2111                        self.named_expressions.insert(result_handle, res_name);
2112                    }
2113                    _ => {
2114                        if let Some(result) = result {
2115                            let res_name = Baked(result).to_string();
2116                            self.write_type(ctx.info[result].ty.handle().unwrap())?;
2117                            write!(self.out, " {res_name} = ")?;
2118                            self.named_expressions.insert(result, res_name);
2119                        }
2120                        let fun_str = fun.to_glsl();
2121                        write!(self.out, "atomic{fun_str}(")?;
2122                        self.write_expr(pointer, ctx)?;
2123                        write!(self.out, ", ")?;
2124                        if let crate::AtomicFunction::Subtract = *fun {
2125                            // Emulate `atomicSub` with `atomicAdd` by negating the value.
2126                            //
2127                            // Use a space to avoid accidentally producing a prefix increment
2128                            // (`--`).
2129                            write!(self.out, "- ")?;
2130                        }
2131                        self.write_expr(value, ctx)?;
2132                        writeln!(self.out, ");")?;
2133                    }
2134                }
2135            }
2136            // Stores a value into an image.
2137            Statement::ImageAtomic {
2138                image,
2139                coordinate,
2140                array_index,
2141                fun,
2142                value,
2143            } => {
2144                write!(self.out, "{level}")?;
2145                self.write_image_atomic(ctx, image, coordinate, array_index, fun, value)?
2146            }
2147            Statement::RayQuery { .. } => unreachable!(),
2148            Statement::SubgroupBallot { result, predicate } => {
2149                write!(self.out, "{level}")?;
2150                let res_name = Baked(result).to_string();
2151                let res_ty = ctx.info[result].ty.inner_with(&self.module.types);
2152                self.write_value_type(res_ty)?;
2153                write!(self.out, " {res_name} = ")?;
2154                self.named_expressions.insert(result, res_name);
2155
2156                write!(self.out, "subgroupBallot(")?;
2157                match predicate {
2158                    Some(predicate) => self.write_expr(predicate, ctx)?,
2159                    None => write!(self.out, "true")?,
2160                }
2161                writeln!(self.out, ");")?;
2162            }
2163            Statement::SubgroupCollectiveOperation {
2164                op,
2165                collective_op,
2166                argument,
2167                result,
2168            } => {
2169                write!(self.out, "{level}")?;
2170                let res_name = Baked(result).to_string();
2171                let res_ty = ctx.info[result].ty.inner_with(&self.module.types);
2172                self.write_value_type(res_ty)?;
2173                write!(self.out, " {res_name} = ")?;
2174                self.named_expressions.insert(result, res_name);
2175
2176                match (collective_op, op) {
2177                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::All) => {
2178                        write!(self.out, "subgroupAll(")?
2179                    }
2180                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Any) => {
2181                        write!(self.out, "subgroupAny(")?
2182                    }
2183                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Add) => {
2184                        write!(self.out, "subgroupAdd(")?
2185                    }
2186                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Mul) => {
2187                        write!(self.out, "subgroupMul(")?
2188                    }
2189                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Max) => {
2190                        write!(self.out, "subgroupMax(")?
2191                    }
2192                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Min) => {
2193                        write!(self.out, "subgroupMin(")?
2194                    }
2195                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::And) => {
2196                        write!(self.out, "subgroupAnd(")?
2197                    }
2198                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Or) => {
2199                        write!(self.out, "subgroupOr(")?
2200                    }
2201                    (crate::CollectiveOperation::Reduce, crate::SubgroupOperation::Xor) => {
2202                        write!(self.out, "subgroupXor(")?
2203                    }
2204                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Add) => {
2205                        write!(self.out, "subgroupExclusiveAdd(")?
2206                    }
2207                    (crate::CollectiveOperation::ExclusiveScan, crate::SubgroupOperation::Mul) => {
2208                        write!(self.out, "subgroupExclusiveMul(")?
2209                    }
2210                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Add) => {
2211                        write!(self.out, "subgroupInclusiveAdd(")?
2212                    }
2213                    (crate::CollectiveOperation::InclusiveScan, crate::SubgroupOperation::Mul) => {
2214                        write!(self.out, "subgroupInclusiveMul(")?
2215                    }
2216                    _ => unimplemented!(),
2217                }
2218                self.write_expr(argument, ctx)?;
2219                writeln!(self.out, ");")?;
2220            }
2221            Statement::SubgroupGather {
2222                mode,
2223                argument,
2224                result,
2225            } => {
2226                write!(self.out, "{level}")?;
2227                let res_name = Baked(result).to_string();
2228                let res_ty = ctx.info[result].ty.inner_with(&self.module.types);
2229                self.write_value_type(res_ty)?;
2230                write!(self.out, " {res_name} = ")?;
2231                self.named_expressions.insert(result, res_name);
2232
2233                match mode {
2234                    crate::GatherMode::BroadcastFirst => {
2235                        write!(self.out, "subgroupBroadcastFirst(")?;
2236                    }
2237                    crate::GatherMode::Broadcast(_) => {
2238                        write!(self.out, "subgroupBroadcast(")?;
2239                    }
2240                    crate::GatherMode::Shuffle(_) => {
2241                        write!(self.out, "subgroupShuffle(")?;
2242                    }
2243                    crate::GatherMode::ShuffleDown(_) => {
2244                        write!(self.out, "subgroupShuffleDown(")?;
2245                    }
2246                    crate::GatherMode::ShuffleUp(_) => {
2247                        write!(self.out, "subgroupShuffleUp(")?;
2248                    }
2249                    crate::GatherMode::ShuffleXor(_) => {
2250                        write!(self.out, "subgroupShuffleXor(")?;
2251                    }
2252                    crate::GatherMode::QuadBroadcast(_) => {
2253                        write!(self.out, "subgroupQuadBroadcast(")?;
2254                    }
2255                    crate::GatherMode::QuadSwap(direction) => match direction {
2256                        crate::Direction::X => {
2257                            write!(self.out, "subgroupQuadSwapHorizontal(")?;
2258                        }
2259                        crate::Direction::Y => {
2260                            write!(self.out, "subgroupQuadSwapVertical(")?;
2261                        }
2262                        crate::Direction::Diagonal => {
2263                            write!(self.out, "subgroupQuadSwapDiagonal(")?;
2264                        }
2265                    },
2266                }
2267                self.write_expr(argument, ctx)?;
2268                match mode {
2269                    crate::GatherMode::BroadcastFirst => {}
2270                    crate::GatherMode::Broadcast(index)
2271                    | crate::GatherMode::Shuffle(index)
2272                    | crate::GatherMode::ShuffleDown(index)
2273                    | crate::GatherMode::ShuffleUp(index)
2274                    | crate::GatherMode::ShuffleXor(index)
2275                    | crate::GatherMode::QuadBroadcast(index) => {
2276                        write!(self.out, ", ")?;
2277                        self.write_expr(index, ctx)?;
2278                    }
2279                    crate::GatherMode::QuadSwap(_) => {}
2280                }
2281                writeln!(self.out, ");")?;
2282            }
2283            Statement::CooperativeStore { .. } => unimplemented!(),
2284            Statement::RayPipelineFunction(_) => unimplemented!(),
2285            Statement::DebugPrintf { .. } => unimplemented!(),
2286        }
2287
2288        Ok(())
2289    }
2290
2291    /// Write a const expression.
2292    ///
2293    /// Write `expr`, a handle to an [`Expression`] in the current [`Module`]'s
2294    /// constant expression arena, as GLSL expression.
2295    ///
2296    /// # Notes
2297    /// Adds no newlines or leading/trailing whitespace
2298    ///
2299    /// [`Expression`]: crate::Expression
2300    /// [`Module`]: crate::Module
2301    fn write_const_expr(
2302        &mut self,
2303        expr: Handle<crate::Expression>,
2304        arena: &crate::Arena<crate::Expression>,
2305    ) -> BackendResult {
2306        self.write_possibly_const_expr(
2307            expr,
2308            arena,
2309            |expr| &self.info[expr],
2310            |writer, expr| writer.write_const_expr(expr, arena),
2311        )
2312    }
2313
2314    /// Write [`Expression`] variants that can occur in both runtime and const expressions.
2315    ///
2316    /// Write `expr`, a handle to an [`Expression`] in the arena `expressions`,
2317    /// as as GLSL expression. This must be one of the [`Expression`] variants
2318    /// that is allowed to occur in constant expressions.
2319    ///
2320    /// Use `write_expression` to write subexpressions.
2321    ///
2322    /// This is the common code for `write_expr`, which handles arbitrary
2323    /// runtime expressions, and `write_const_expr`, which only handles
2324    /// const-expressions. Each of those callers passes itself (essentially) as
2325    /// the `write_expression` callback, so that subexpressions are restricted
2326    /// to the appropriate variants.
2327    ///
2328    /// # Notes
2329    /// Adds no newlines or leading/trailing whitespace
2330    ///
2331    /// [`Expression`]: crate::Expression
2332    fn write_possibly_const_expr<'w, I, E>(
2333        &'w mut self,
2334        expr: Handle<crate::Expression>,
2335        expressions: &crate::Arena<crate::Expression>,
2336        info: I,
2337        write_expression: E,
2338    ) -> BackendResult
2339    where
2340        I: Fn(Handle<crate::Expression>) -> &'w proc::TypeResolution,
2341        E: Fn(&mut Self, Handle<crate::Expression>) -> BackendResult,
2342    {
2343        use crate::Expression;
2344
2345        match expressions[expr] {
2346            Expression::Literal(literal) => {
2347                match literal {
2348                    // Floats are written using `Debug` instead of `Display` because it always appends the
2349                    // decimal part even it's zero which is needed for a valid glsl float constant
2350                    crate::Literal::F64(value) => write!(self.out, "{value:?}LF")?,
2351                    crate::Literal::F32(value) => write!(self.out, "{value:?}")?,
2352                    crate::Literal::F16(_) => {
2353                        return Err(Error::Custom("GLSL has no 16-bit float type".into()));
2354                    }
2355                    // Unsigned integers need a `u` at the end
2356                    //
2357                    // While `core` doesn't necessarily need it, it's allowed and since `es` needs it we
2358                    // always write it as the extra branch wouldn't have any benefit in readability
2359                    crate::Literal::U16(value) => write!(self.out, "uint16_t({value})")?,
2360                    crate::Literal::I16(value) => write!(self.out, "int16_t({value})")?,
2361                    crate::Literal::U32(value) => write!(self.out, "{value}u")?,
2362                    crate::Literal::I32(value) => write!(self.out, "{value}")?,
2363                    crate::Literal::Bool(value) => write!(self.out, "{value}")?,
2364                    crate::Literal::I64(_) => {
2365                        return Err(Error::Custom("GLSL has no 64-bit integer type".into()));
2366                    }
2367                    crate::Literal::U64(_) => {
2368                        return Err(Error::Custom("GLSL has no 64-bit integer type".into()));
2369                    }
2370                    crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => {
2371                        return Err(Error::Custom(
2372                            "Abstract types should not appear in IR presented to backends".into(),
2373                        ));
2374                    }
2375                }
2376            }
2377            Expression::Constant(handle) => {
2378                let constant = &self.module.constants[handle];
2379                if constant.name.is_some() {
2380                    write!(self.out, "{}", self.names[&NameKey::Constant(handle)])?;
2381                } else {
2382                    self.write_const_expr(constant.init, &self.module.global_expressions)?;
2383                }
2384            }
2385            Expression::ZeroValue(ty) => {
2386                self.write_zero_init_value(ty)?;
2387            }
2388            Expression::Compose { ty, ref components } => {
2389                self.write_type(ty)?;
2390
2391                if let TypeInner::Array { base, size, .. } = self.module.types[ty].inner {
2392                    self.write_array_size(base, size)?;
2393                }
2394
2395                write!(self.out, "(")?;
2396                for (index, component) in components.iter().enumerate() {
2397                    if index != 0 {
2398                        write!(self.out, ", ")?;
2399                    }
2400                    write_expression(self, *component)?;
2401                }
2402                write!(self.out, ")")?
2403            }
2404            // `Splat` needs to actually write down a vector, it's not always inferred in GLSL.
2405            Expression::Splat { size: _, value } => {
2406                let resolved = info(expr).inner_with(&self.module.types);
2407                self.write_value_type(resolved)?;
2408                write!(self.out, "(")?;
2409                write_expression(self, value)?;
2410                write!(self.out, ")")?
2411            }
2412            _ => {
2413                return Err(Error::Override);
2414            }
2415        }
2416
2417        Ok(())
2418    }
2419
2420    /// Helper method to write expressions
2421    ///
2422    /// # Notes
2423    /// Doesn't add any newlines or leading/trailing spaces
2424    #[allow(clippy::large_stack_frames)] // TODO(https://github.com/gfx-rs/wgpu/issues/9456)
2425    fn write_expr(
2426        &mut self,
2427        expr: Handle<crate::Expression>,
2428        ctx: &back::FunctionCtx,
2429    ) -> BackendResult {
2430        use crate::Expression;
2431
2432        if let Some(name) = self.named_expressions.get(&expr) {
2433            write!(self.out, "{name}")?;
2434            return Ok(());
2435        }
2436
2437        match ctx.expressions[expr] {
2438            Expression::Literal(_)
2439            | Expression::Constant(_)
2440            | Expression::ZeroValue(_)
2441            | Expression::Compose { .. }
2442            | Expression::Splat { .. } => {
2443                self.write_possibly_const_expr(
2444                    expr,
2445                    ctx.expressions,
2446                    |expr| &ctx.info[expr].ty,
2447                    |writer, expr| writer.write_expr(expr, ctx),
2448                )?;
2449            }
2450            Expression::Override(_) => return Err(Error::Override),
2451            // `Access` is applied to arrays, vectors and matrices and is written as indexing
2452            Expression::Access { base, index } => {
2453                self.write_expr(base, ctx)?;
2454                write!(self.out, "[")?;
2455                self.write_expr(index, ctx)?;
2456                write!(self.out, "]")?
2457            }
2458            // `AccessIndex` is the same as `Access` except that the index is a constant and it can
2459            // be applied to structs, in this case we need to find the name of the field at that
2460            // index and write `base.field_name`
2461            Expression::AccessIndex { base, index } => {
2462                self.write_expr(base, ctx)?;
2463
2464                let base_ty_res = &ctx.info[base].ty;
2465                let mut resolved = base_ty_res.inner_with(&self.module.types);
2466                let base_ty_handle = match *resolved {
2467                    TypeInner::Pointer { base, space: _ } => {
2468                        resolved = &self.module.types[base].inner;
2469                        Some(base)
2470                    }
2471                    _ => base_ty_res.handle(),
2472                };
2473
2474                match *resolved {
2475                    TypeInner::Vector { .. } => {
2476                        // Write vector access as a swizzle
2477                        write!(self.out, ".{}", back::COMPONENTS[index as usize])?
2478                    }
2479                    TypeInner::Matrix { .. }
2480                    | TypeInner::Array { .. }
2481                    | TypeInner::ValuePointer { .. } => write!(self.out, "[{index}]")?,
2482                    TypeInner::Struct { .. } => {
2483                        // This will never panic in case the type is a `Struct`, this is not true
2484                        // for other types so we can only check while inside this match arm
2485                        let ty = base_ty_handle.unwrap();
2486
2487                        write!(
2488                            self.out,
2489                            ".{}",
2490                            self.names[&NameKey::StructMember(ty, index)]
2491                        )?
2492                    }
2493                    ref other => return Err(Error::Custom(format!("Cannot index {other:?}"))),
2494                }
2495            }
2496            // `Swizzle` adds a few letters behind the dot.
2497            Expression::Swizzle {
2498                size,
2499                vector,
2500                pattern,
2501            } => {
2502                self.write_expr(vector, ctx)?;
2503                write!(self.out, ".")?;
2504                for &sc in pattern[..size as usize].iter() {
2505                    self.out.write_char(back::COMPONENTS[sc as usize])?;
2506                }
2507            }
2508            // Function arguments are written as the argument name
2509            Expression::FunctionArgument(pos) => {
2510                write!(self.out, "{}", self.names[&ctx.argument_key(pos)])?
2511            }
2512            // Global variables need some special work for their name but
2513            // `get_global_name` does the work for us
2514            Expression::GlobalVariable(handle) => {
2515                let global = &self.module.global_variables[handle];
2516                self.write_global_name(handle, global)?
2517            }
2518            // A local is written as it's name
2519            Expression::LocalVariable(handle) => {
2520                write!(self.out, "{}", self.names[&ctx.name_key(handle)])?
2521            }
2522            // glsl has no pointers so there's no load operation, just write the pointer expression
2523            Expression::Load { pointer } => {
2524                let ty_inner = ctx.resolve_type(pointer, &self.module.types);
2525                if ty_inner.is_atomic_pointer(&self.module.types) {
2526                    let mut suffix = "";
2527                    if let TypeInner::Pointer { base, .. } = *ty_inner {
2528                        if let TypeInner::Atomic(scalar) = self.module.types[base].inner {
2529                            suffix = match (scalar.kind, scalar.width) {
2530                                (crate::ScalarKind::Uint, 8) => "ul",
2531                                (crate::ScalarKind::Sint, 8) => "l",
2532                                (crate::ScalarKind::Uint, _) => "u",
2533                                _ => "",
2534                            };
2535                        }
2536                    }
2537                    write!(self.out, "atomicOr(")?;
2538                    self.write_expr(pointer, ctx)?;
2539                    write!(self.out, ", 0{})", suffix)?
2540                } else {
2541                    self.write_expr(pointer, ctx)?
2542                }
2543            }
2544            // `ImageSample` is a bit complicated compared to the rest of the IR.
2545            //
2546            // First there are three variations depending whether the sample level is explicitly set,
2547            // if it's automatic or it it's bias:
2548            // `texture(image, coordinate)` - Automatic sample level
2549            // `texture(image, coordinate, bias)` - Bias sample level
2550            // `textureLod(image, coordinate, level)` - Zero or Exact sample level
2551            //
2552            // Furthermore if `depth_ref` is some we need to append it to the coordinate vector
2553            Expression::ImageSample {
2554                image,
2555                sampler: _, //TODO?
2556                gather,
2557                coordinate,
2558                array_index,
2559                offset,
2560                level,
2561                depth_ref,
2562                clamp_to_edge: _,
2563            } => {
2564                let (dim, class, arrayed) = match *ctx.resolve_type(image, &self.module.types) {
2565                    TypeInner::Image {
2566                        dim,
2567                        class,
2568                        arrayed,
2569                        ..
2570                    } => (dim, class, arrayed),
2571                    _ => unreachable!(),
2572                };
2573                let mut err = None;
2574                if dim == crate::ImageDimension::Cube {
2575                    if offset.is_some() {
2576                        err = Some("gsamplerCube[Array][Shadow] doesn't support texture sampling with offsets");
2577                    }
2578                    if arrayed
2579                        && matches!(class, crate::ImageClass::Depth { .. })
2580                        && matches!(level, crate::SampleLevel::Gradient { .. })
2581                    {
2582                        err = Some("samplerCubeArrayShadow don't support textureGrad");
2583                    }
2584                }
2585                if gather.is_some() && level != crate::SampleLevel::Zero {
2586                    err = Some("textureGather doesn't support LOD parameters");
2587                }
2588                if let Some(err) = err {
2589                    return Err(Error::Custom(String::from(err)));
2590                }
2591
2592                // `textureLod[Offset]` on `sampler2DArrayShadow` and `samplerCubeShadow` does not exist in GLSL,
2593                // unless `GL_EXT_texture_shadow_lod` is present.
2594                // But if the target LOD is zero, we can emulate that by using `textureGrad[Offset]` with a constant gradient of 0.
2595                let workaround_lod_with_grad = ((dim == crate::ImageDimension::Cube && !arrayed)
2596                    || (dim == crate::ImageDimension::D2 && arrayed))
2597                    && level == crate::SampleLevel::Zero
2598                    && matches!(class, crate::ImageClass::Depth { .. })
2599                    && !self.features.contains(Features::TEXTURE_SHADOW_LOD);
2600
2601                // Write the function to be used depending on the sample level
2602                let fun_name = match level {
2603                    crate::SampleLevel::Zero if gather.is_some() => "textureGather",
2604                    crate::SampleLevel::Zero if workaround_lod_with_grad => "textureGrad",
2605                    crate::SampleLevel::Auto | crate::SampleLevel::Bias(_) => "texture",
2606                    crate::SampleLevel::Zero | crate::SampleLevel::Exact(_) => "textureLod",
2607                    crate::SampleLevel::Gradient { .. } => "textureGrad",
2608                };
2609                let offset_name = match offset {
2610                    Some(_) => "Offset",
2611                    None => "",
2612                };
2613
2614                write!(self.out, "{fun_name}{offset_name}(")?;
2615
2616                // Write the image that will be used
2617                self.write_expr(image, ctx)?;
2618                // The space here isn't required but it helps with readability
2619                write!(self.out, ", ")?;
2620
2621                // TODO: handle clamp_to_edge
2622                // https://github.com/gfx-rs/wgpu/issues/7791
2623
2624                // We need to get the coordinates vector size to later build a vector that's `size + 1`
2625                // if `depth_ref` is some, if it isn't a vector we panic as that's not a valid expression
2626                let mut coord_dim = match *ctx.resolve_type(coordinate, &self.module.types) {
2627                    TypeInner::Vector { size, .. } => size as u8,
2628                    TypeInner::Scalar { .. } => 1,
2629                    _ => unreachable!(),
2630                };
2631
2632                if array_index.is_some() {
2633                    coord_dim += 1;
2634                }
2635                let merge_depth_ref = depth_ref.is_some() && gather.is_none() && coord_dim < 4;
2636                if merge_depth_ref {
2637                    coord_dim += 1;
2638                }
2639
2640                let tex_1d_hack = dim == crate::ImageDimension::D1 && self.options.version.is_es();
2641                let is_vec = tex_1d_hack || coord_dim != 1;
2642                // Compose a new texture coordinates vector
2643                if is_vec {
2644                    write!(self.out, "vec{}(", coord_dim + tex_1d_hack as u8)?;
2645                }
2646                self.write_expr(coordinate, ctx)?;
2647                if tex_1d_hack {
2648                    write!(self.out, ", 0.0")?;
2649                }
2650                if let Some(expr) = array_index {
2651                    write!(self.out, ", ")?;
2652                    self.write_expr(expr, ctx)?;
2653                }
2654                if merge_depth_ref {
2655                    write!(self.out, ", ")?;
2656                    self.write_expr(depth_ref.unwrap(), ctx)?;
2657                }
2658                if is_vec {
2659                    write!(self.out, ")")?;
2660                }
2661
2662                if let (Some(expr), false) = (depth_ref, merge_depth_ref) {
2663                    write!(self.out, ", ")?;
2664                    self.write_expr(expr, ctx)?;
2665                }
2666
2667                match level {
2668                    // Auto needs no more arguments
2669                    crate::SampleLevel::Auto => (),
2670                    // Zero needs level set to 0
2671                    crate::SampleLevel::Zero => {
2672                        if workaround_lod_with_grad {
2673                            let vec_dim = match dim {
2674                                crate::ImageDimension::Cube => 3,
2675                                _ => 2,
2676                            };
2677                            write!(self.out, ", vec{vec_dim}(0.0), vec{vec_dim}(0.0)")?;
2678                        } else if gather.is_none() {
2679                            write!(self.out, ", 0.0")?;
2680                        }
2681                    }
2682                    // Exact and bias require another argument
2683                    crate::SampleLevel::Exact(expr) => {
2684                        write!(self.out, ", ")?;
2685                        self.write_expr(expr, ctx)?;
2686                    }
2687                    crate::SampleLevel::Bias(_) => {
2688                        // This needs to be done after the offset writing
2689                    }
2690                    crate::SampleLevel::Gradient { x, y } => {
2691                        // If we are using sampler2D to replace sampler1D, we also
2692                        // need to make sure to use vec2 gradients
2693                        if tex_1d_hack {
2694                            write!(self.out, ", vec2(")?;
2695                            self.write_expr(x, ctx)?;
2696                            write!(self.out, ", 0.0)")?;
2697                            write!(self.out, ", vec2(")?;
2698                            self.write_expr(y, ctx)?;
2699                            write!(self.out, ", 0.0)")?;
2700                        } else {
2701                            write!(self.out, ", ")?;
2702                            self.write_expr(x, ctx)?;
2703                            write!(self.out, ", ")?;
2704                            self.write_expr(y, ctx)?;
2705                        }
2706                    }
2707                }
2708
2709                if let Some(constant) = offset {
2710                    write!(self.out, ", ")?;
2711                    if tex_1d_hack {
2712                        write!(self.out, "ivec2(")?;
2713                    }
2714                    self.write_const_expr(constant, ctx.expressions)?;
2715                    if tex_1d_hack {
2716                        write!(self.out, ", 0)")?;
2717                    }
2718                }
2719
2720                // Bias is always the last argument
2721                if let crate::SampleLevel::Bias(expr) = level {
2722                    write!(self.out, ", ")?;
2723                    self.write_expr(expr, ctx)?;
2724                }
2725
2726                if let (Some(component), None) = (gather, depth_ref) {
2727                    write!(self.out, ", {}", component as usize)?;
2728                }
2729
2730                // End the function
2731                write!(self.out, ")")?
2732            }
2733            Expression::ImageLoad {
2734                image,
2735                coordinate,
2736                array_index,
2737                sample,
2738                level,
2739            } => self.write_image_load(expr, ctx, image, coordinate, array_index, sample, level)?,
2740            // Query translates into one of the:
2741            // - textureSize/imageSize
2742            // - textureQueryLevels
2743            // - textureSamples/imageSamples
2744            Expression::ImageQuery { image, query } => {
2745                use crate::ImageClass;
2746
2747                // This will only panic if the module is invalid
2748                let (dim, class) = match *ctx.resolve_type(image, &self.module.types) {
2749                    TypeInner::Image {
2750                        dim,
2751                        arrayed: _,
2752                        class,
2753                    } => (dim, class),
2754                    _ => unreachable!(),
2755                };
2756                let components = match dim {
2757                    crate::ImageDimension::D1 => 1,
2758                    crate::ImageDimension::D2 => 2,
2759                    crate::ImageDimension::D3 => 3,
2760                    crate::ImageDimension::Cube => 2,
2761                };
2762
2763                if let crate::ImageQuery::Size { .. } = query {
2764                    match components {
2765                        1 => write!(self.out, "uint(")?,
2766                        _ => write!(self.out, "uvec{components}(")?,
2767                    }
2768                } else {
2769                    write!(self.out, "uint(")?;
2770                }
2771
2772                match query {
2773                    crate::ImageQuery::Size { level } => {
2774                        match class {
2775                            ImageClass::Sampled { multi, .. } | ImageClass::Depth { multi } => {
2776                                write!(self.out, "textureSize(")?;
2777                                self.write_expr(image, ctx)?;
2778                                if let Some(expr) = level {
2779                                    let cast_to_int = matches!(
2780                                        *ctx.resolve_type(expr, &self.module.types),
2781                                        TypeInner::Scalar(crate::Scalar {
2782                                            kind: crate::ScalarKind::Uint,
2783                                            ..
2784                                        })
2785                                    );
2786
2787                                    write!(self.out, ", ")?;
2788
2789                                    if cast_to_int {
2790                                        write!(self.out, "int(")?;
2791                                    }
2792
2793                                    self.write_expr(expr, ctx)?;
2794
2795                                    if cast_to_int {
2796                                        write!(self.out, ")")?;
2797                                    }
2798                                } else if !multi {
2799                                    // All textureSize calls requires an lod argument
2800                                    // except for multisampled samplers
2801                                    write!(self.out, ", 0")?;
2802                                }
2803                            }
2804                            ImageClass::Storage { .. } => {
2805                                write!(self.out, "imageSize(")?;
2806                                self.write_expr(image, ctx)?;
2807                            }
2808                            ImageClass::External => unimplemented!(),
2809                        }
2810                        write!(self.out, ")")?;
2811                        if components != 1 || self.options.version.is_es() {
2812                            write!(self.out, ".{}", &"xyz"[..components])?;
2813                        }
2814                    }
2815                    crate::ImageQuery::NumLevels => {
2816                        write!(self.out, "textureQueryLevels(",)?;
2817                        self.write_expr(image, ctx)?;
2818                        write!(self.out, ")",)?;
2819                    }
2820                    crate::ImageQuery::NumLayers => {
2821                        let fun_name = match class {
2822                            ImageClass::Sampled { .. } | ImageClass::Depth { .. } => "textureSize",
2823                            ImageClass::Storage { .. } => "imageSize",
2824                            ImageClass::External => unimplemented!(),
2825                        };
2826                        write!(self.out, "{fun_name}(")?;
2827                        self.write_expr(image, ctx)?;
2828                        // All textureSize calls requires an lod argument
2829                        // except for multisampled samplers
2830                        if !class.is_multisampled() {
2831                            write!(self.out, ", 0")?;
2832                        }
2833                        write!(self.out, ")")?;
2834                        if components != 1 || self.options.version.is_es() {
2835                            write!(self.out, ".{}", back::COMPONENTS[components])?;
2836                        }
2837                    }
2838                    crate::ImageQuery::NumSamples => {
2839                        let fun_name = match class {
2840                            ImageClass::Sampled { .. } | ImageClass::Depth { .. } => {
2841                                "textureSamples"
2842                            }
2843                            ImageClass::Storage { .. } => "imageSamples",
2844                            ImageClass::External => unimplemented!(),
2845                        };
2846                        write!(self.out, "{fun_name}(")?;
2847                        self.write_expr(image, ctx)?;
2848                        write!(self.out, ")",)?;
2849                    }
2850                }
2851
2852                write!(self.out, ")")?;
2853            }
2854            Expression::Unary { op, expr } => {
2855                let operator_or_fn = match op {
2856                    crate::UnaryOperator::Negate => "-",
2857                    crate::UnaryOperator::LogicalNot => {
2858                        match *ctx.resolve_type(expr, &self.module.types) {
2859                            TypeInner::Vector { .. } => "not",
2860                            _ => "!",
2861                        }
2862                    }
2863                    crate::UnaryOperator::BitwiseNot => "~",
2864                };
2865                write!(self.out, "{operator_or_fn}(")?;
2866
2867                self.write_expr(expr, ctx)?;
2868
2869                write!(self.out, ")")?
2870            }
2871            // `Binary` we just write `left op right`, except when dealing with
2872            // comparison operations on vectors as they are implemented with
2873            // builtin functions.
2874            // Once again we wrap everything in parentheses to avoid precedence issues
2875            Expression::Binary {
2876                mut op,
2877                left,
2878                right,
2879            } => {
2880                // Holds `Some(function_name)` if the binary operation is
2881                // implemented as a function call
2882                use crate::{BinaryOperator as Bo, ScalarKind as Sk, TypeInner as Ti};
2883
2884                let left_inner = ctx.resolve_type(left, &self.module.types);
2885                let right_inner = ctx.resolve_type(right, &self.module.types);
2886
2887                let function = match (left_inner, right_inner) {
2888                    (&Ti::Vector { scalar, .. }, &Ti::Vector { .. }) => match op {
2889                        Bo::Less
2890                        | Bo::LessEqual
2891                        | Bo::Greater
2892                        | Bo::GreaterEqual
2893                        | Bo::Equal
2894                        | Bo::NotEqual => BinaryOperation::VectorCompare,
2895                        Bo::Modulo if scalar.kind == Sk::Float => BinaryOperation::Modulo,
2896                        Bo::Modulo if scalar.kind == Sk::Sint || scalar.kind == Sk::Uint => {
2897                            BinaryOperation::ModuloInt
2898                        }
2899                        Bo::And if scalar.kind == Sk::Bool => {
2900                            op = crate::BinaryOperator::LogicalAnd;
2901                            BinaryOperation::VectorComponentWise
2902                        }
2903                        Bo::InclusiveOr if scalar.kind == Sk::Bool => {
2904                            op = crate::BinaryOperator::LogicalOr;
2905                            BinaryOperation::VectorComponentWise
2906                        }
2907                        _ => BinaryOperation::Other,
2908                    },
2909                    _ => match (left_inner.scalar_kind(), right_inner.scalar_kind()) {
2910                        (Some(Sk::Float), _) | (_, Some(Sk::Float)) => match op {
2911                            Bo::Modulo => BinaryOperation::Modulo,
2912                            _ => BinaryOperation::Other,
2913                        },
2914                        (Some(Sk::Sint | Sk::Uint), _) | (_, Some(Sk::Sint | Sk::Uint))
2915                            if op == Bo::Modulo =>
2916                        {
2917                            BinaryOperation::ModuloInt
2918                        }
2919                        (Some(Sk::Bool), Some(Sk::Bool)) => match op {
2920                            Bo::InclusiveOr => {
2921                                op = crate::BinaryOperator::LogicalOr;
2922                                BinaryOperation::Other
2923                            }
2924                            Bo::And => {
2925                                op = crate::BinaryOperator::LogicalAnd;
2926                                BinaryOperation::Other
2927                            }
2928                            _ => BinaryOperation::Other,
2929                        },
2930                        _ => BinaryOperation::Other,
2931                    },
2932                };
2933
2934                match function {
2935                    BinaryOperation::VectorCompare => {
2936                        let op_str = match op {
2937                            Bo::Less => "lessThan(",
2938                            Bo::LessEqual => "lessThanEqual(",
2939                            Bo::Greater => "greaterThan(",
2940                            Bo::GreaterEqual => "greaterThanEqual(",
2941                            Bo::Equal => "equal(",
2942                            Bo::NotEqual => "notEqual(",
2943                            _ => unreachable!(),
2944                        };
2945                        write!(self.out, "{op_str}")?;
2946                        self.write_expr(left, ctx)?;
2947                        write!(self.out, ", ")?;
2948                        self.write_expr(right, ctx)?;
2949                        write!(self.out, ")")?;
2950                    }
2951                    BinaryOperation::VectorComponentWise => {
2952                        self.write_value_type(left_inner)?;
2953                        write!(self.out, "(")?;
2954
2955                        let size = match *left_inner {
2956                            Ti::Vector { size, .. } => size,
2957                            _ => unreachable!(),
2958                        };
2959
2960                        for i in 0..size as usize {
2961                            if i != 0 {
2962                                write!(self.out, ", ")?;
2963                            }
2964
2965                            self.write_expr(left, ctx)?;
2966                            write!(self.out, ".{}", back::COMPONENTS[i])?;
2967
2968                            write!(self.out, " {} ", back::binary_operation_str(op))?;
2969
2970                            self.write_expr(right, ctx)?;
2971                            write!(self.out, ".{}", back::COMPONENTS[i])?;
2972                        }
2973
2974                        write!(self.out, ")")?;
2975                    }
2976                    // Signed/unsigned integer `%` with a negative operand is handled by
2977                    // `BinaryOperation::ModuloInt` below. Remaining TODO: the degenerate
2978                    // div-by-zero / `INT_MIN % -1` cases (this backend also leaves integer
2979                    // `/` unguarded), and float `% 0` (see
2980                    // https://github.com/gpuweb/gpuweb/issues/2798).
2981                    BinaryOperation::Modulo => {
2982                        write!(self.out, "(")?;
2983
2984                        // write `e1 - e2 * trunc(e1 / e2)`
2985                        self.write_expr(left, ctx)?;
2986                        write!(self.out, " - ")?;
2987                        self.write_expr(right, ctx)?;
2988                        write!(self.out, " * ")?;
2989                        write!(self.out, "trunc(")?;
2990                        self.write_expr(left, ctx)?;
2991                        write!(self.out, " / ")?;
2992                        self.write_expr(right, ctx)?;
2993                        write!(self.out, ")")?;
2994
2995                        write!(self.out, ")")?;
2996                    }
2997                    BinaryOperation::ModuloInt => {
2998                        // GLSL's `%` is undefined when either operand is negative.
2999                        // Integer division truncates toward zero (which is well
3000                        // defined), so reconstruct the remainder as `e1 - e2 * (e1 / e2)`.
3001                        // This matches WGSL's truncated `%` for all operands; the
3002                        // degenerate `x % 0` / `INT_MIN % -1` cases stay consistent
3003                        // with this backend's unguarded integer `/`.
3004                        write!(self.out, "(")?;
3005                        self.write_expr(left, ctx)?;
3006                        write!(self.out, " - ")?;
3007                        self.write_expr(right, ctx)?;
3008                        write!(self.out, " * (")?;
3009                        self.write_expr(left, ctx)?;
3010                        write!(self.out, " / ")?;
3011                        self.write_expr(right, ctx)?;
3012                        write!(self.out, "))")?;
3013                    }
3014                    BinaryOperation::Other => {
3015                        write!(self.out, "(")?;
3016
3017                        self.write_expr(left, ctx)?;
3018                        write!(self.out, " {} ", back::binary_operation_str(op))?;
3019                        self.write_expr(right, ctx)?;
3020
3021                        write!(self.out, ")")?;
3022                    }
3023                }
3024            }
3025            // `Select` is written as `condition ? accept : reject`
3026            // We wrap everything in parentheses to avoid precedence issues
3027            Expression::Select {
3028                condition,
3029                accept,
3030                reject,
3031            } => {
3032                let cond_ty = ctx.resolve_type(condition, &self.module.types);
3033                let vec_select = if let TypeInner::Vector { .. } = *cond_ty {
3034                    true
3035                } else {
3036                    false
3037                };
3038
3039                // TODO: Boolean mix on desktop required GL_EXT_shader_integer_mix
3040                if vec_select {
3041                    // Glsl defines that for mix when the condition is a boolean the first element
3042                    // is picked if condition is false and the second if condition is true
3043                    write!(self.out, "mix(")?;
3044                    self.write_expr(reject, ctx)?;
3045                    write!(self.out, ", ")?;
3046                    self.write_expr(accept, ctx)?;
3047                    write!(self.out, ", ")?;
3048                    self.write_expr(condition, ctx)?;
3049                } else {
3050                    write!(self.out, "(")?;
3051                    self.write_expr(condition, ctx)?;
3052                    write!(self.out, " ? ")?;
3053                    self.write_expr(accept, ctx)?;
3054                    write!(self.out, " : ")?;
3055                    self.write_expr(reject, ctx)?;
3056                }
3057
3058                write!(self.out, ")")?
3059            }
3060            // `Derivative` is a function call to a glsl provided function
3061            Expression::Derivative { axis, ctrl, expr } => {
3062                use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
3063                let fun_name = if self.options.version.supports_derivative_control() {
3064                    match (axis, ctrl) {
3065                        (Axis::X, Ctrl::Coarse) => "dFdxCoarse",
3066                        (Axis::X, Ctrl::Fine) => "dFdxFine",
3067                        (Axis::X, Ctrl::None) => "dFdx",
3068                        (Axis::Y, Ctrl::Coarse) => "dFdyCoarse",
3069                        (Axis::Y, Ctrl::Fine) => "dFdyFine",
3070                        (Axis::Y, Ctrl::None) => "dFdy",
3071                        (Axis::Width, Ctrl::Coarse) => "fwidthCoarse",
3072                        (Axis::Width, Ctrl::Fine) => "fwidthFine",
3073                        (Axis::Width, Ctrl::None) => "fwidth",
3074                    }
3075                } else {
3076                    match axis {
3077                        Axis::X => "dFdx",
3078                        Axis::Y => "dFdy",
3079                        Axis::Width => "fwidth",
3080                    }
3081                };
3082                write!(self.out, "{fun_name}(")?;
3083                self.write_expr(expr, ctx)?;
3084                write!(self.out, ")")?
3085            }
3086            // `Relational` is a normal function call to some glsl provided functions
3087            Expression::Relational { fun, argument } => {
3088                use crate::RelationalFunction as Rf;
3089
3090                let fun_name = match fun {
3091                    Rf::IsInf => "isinf",
3092                    Rf::IsNan => "isnan",
3093                    Rf::All => "all",
3094                    Rf::Any => "any",
3095                };
3096                write!(self.out, "{fun_name}(")?;
3097
3098                self.write_expr(argument, ctx)?;
3099
3100                write!(self.out, ")")?
3101            }
3102            Expression::Math {
3103                fun,
3104                arg,
3105                arg1,
3106                arg2,
3107                arg3,
3108            } => {
3109                use crate::MathFunction as Mf;
3110
3111                let fun_name = match fun {
3112                    // comparison
3113                    Mf::Abs => "abs",
3114                    Mf::Min => "min",
3115                    Mf::Max => "max",
3116                    Mf::Clamp => {
3117                        let scalar_kind = ctx
3118                            .resolve_type(arg, &self.module.types)
3119                            .scalar_kind()
3120                            .unwrap();
3121                        match scalar_kind {
3122                            crate::ScalarKind::Float => "clamp",
3123                            // Clamp is undefined if min > max. In practice this means it can use a median-of-three
3124                            // instruction to determine the value. This is fine according to the WGSL spec for float
3125                            // clamp, but integer clamp _must_ use min-max. As such we write out min/max.
3126                            _ => {
3127                                write!(self.out, "min(max(")?;
3128                                self.write_expr(arg, ctx)?;
3129                                write!(self.out, ", ")?;
3130                                self.write_expr(arg1.unwrap(), ctx)?;
3131                                write!(self.out, "), ")?;
3132                                self.write_expr(arg2.unwrap(), ctx)?;
3133                                write!(self.out, ")")?;
3134
3135                                return Ok(());
3136                            }
3137                        }
3138                    }
3139                    Mf::Saturate => {
3140                        write!(self.out, "clamp(")?;
3141
3142                        self.write_expr(arg, ctx)?;
3143
3144                        match *ctx.resolve_type(arg, &self.module.types) {
3145                            TypeInner::Vector { size, .. } => write!(
3146                                self.out,
3147                                ", vec{}(0.0), vec{0}(1.0)",
3148                                common::vector_size_str(size)
3149                            )?,
3150                            _ => write!(self.out, ", 0.0, 1.0")?,
3151                        }
3152
3153                        write!(self.out, ")")?;
3154
3155                        return Ok(());
3156                    }
3157                    // trigonometry
3158                    Mf::Cos => "cos",
3159                    Mf::Cosh => "cosh",
3160                    Mf::Sin => "sin",
3161                    Mf::Sinh => "sinh",
3162                    Mf::Tan => "tan",
3163                    Mf::Tanh => "tanh",
3164                    Mf::Acos => "acos",
3165                    Mf::Asin => "asin",
3166                    Mf::Atan => "atan",
3167                    Mf::Asinh => "asinh",
3168                    Mf::Acosh => "acosh",
3169                    Mf::Atanh => "atanh",
3170                    Mf::Radians => "radians",
3171                    Mf::Degrees => "degrees",
3172                    // glsl doesn't have atan2 function
3173                    // use two-argument variation of the atan function
3174                    Mf::Atan2 => "atan",
3175                    // decomposition
3176                    Mf::Ceil => "ceil",
3177                    Mf::Floor => "floor",
3178                    Mf::Round => "roundEven",
3179                    Mf::Fract => "fract",
3180                    Mf::Trunc => "trunc",
3181                    Mf::Modf => MODF_FUNCTION,
3182                    Mf::Frexp => FREXP_FUNCTION,
3183                    Mf::Ldexp => "ldexp",
3184                    // exponent
3185                    Mf::Exp => "exp",
3186                    Mf::Exp2 => "exp2",
3187                    Mf::Log => "log",
3188                    Mf::Log2 => "log2",
3189                    Mf::Pow => "pow",
3190                    // geometry
3191                    Mf::Dot => match *ctx.resolve_type(arg, &self.module.types) {
3192                        TypeInner::Vector {
3193                            scalar:
3194                                crate::Scalar {
3195                                    kind: crate::ScalarKind::Float,
3196                                    ..
3197                                },
3198                            ..
3199                        } => "dot",
3200                        TypeInner::Vector { size, .. } => {
3201                            return self.write_dot_product(arg, arg1.unwrap(), size as usize, ctx)
3202                        }
3203                        _ => unreachable!(
3204                            "Correct TypeInner for dot product should be already validated"
3205                        ),
3206                    },
3207                    fun @ (Mf::Dot4I8Packed | Mf::Dot4U8Packed) => {
3208                        let conversion = match fun {
3209                            Mf::Dot4I8Packed => "int",
3210                            Mf::Dot4U8Packed => "",
3211                            _ => unreachable!(),
3212                        };
3213
3214                        let arg1 = arg1.unwrap();
3215
3216                        // Write parentheses around the dot product expression to prevent operators
3217                        // with different precedences from applying earlier.
3218                        write!(self.out, "(")?;
3219                        for i in 0..4 {
3220                            // Since `bitfieldExtract` only sign extends if the value is signed, we
3221                            // need to convert the inputs to `int` in case of `Dot4I8Packed`. For
3222                            // `Dot4U8Packed`, the code below only introduces parenthesis around
3223                            // each factor, which aren't strictly needed because both operands are
3224                            // baked, but which don't hurt either.
3225                            write!(self.out, "bitfieldExtract({conversion}(")?;
3226                            self.write_expr(arg, ctx)?;
3227                            write!(self.out, "), {}, 8)", i * 8)?;
3228
3229                            write!(self.out, " * bitfieldExtract({conversion}(")?;
3230                            self.write_expr(arg1, ctx)?;
3231                            write!(self.out, "), {}, 8)", i * 8)?;
3232
3233                            if i != 3 {
3234                                write!(self.out, " + ")?;
3235                            }
3236                        }
3237                        write!(self.out, ")")?;
3238
3239                        return Ok(());
3240                    }
3241                    Mf::Outer => "outerProduct",
3242                    Mf::Cross => "cross",
3243                    Mf::Distance => "distance",
3244                    Mf::Length => "length",
3245                    Mf::Normalize => "normalize",
3246                    Mf::FaceForward => "faceforward",
3247                    Mf::Reflect => "reflect",
3248                    Mf::Refract => "refract",
3249                    // computational
3250                    Mf::Sign => "sign",
3251                    Mf::Fma => {
3252                        if self.options.version.supports_fma_function() {
3253                            // Use the fma function when available
3254                            "fma"
3255                        } else {
3256                            // No fma support. Transform the function call into an arithmetic expression
3257                            write!(self.out, "(")?;
3258
3259                            self.write_expr(arg, ctx)?;
3260                            write!(self.out, " * ")?;
3261
3262                            let arg1 =
3263                                arg1.ok_or_else(|| Error::Custom("Missing fma arg1".to_owned()))?;
3264                            self.write_expr(arg1, ctx)?;
3265                            write!(self.out, " + ")?;
3266
3267                            let arg2 =
3268                                arg2.ok_or_else(|| Error::Custom("Missing fma arg2".to_owned()))?;
3269                            self.write_expr(arg2, ctx)?;
3270                            write!(self.out, ")")?;
3271
3272                            return Ok(());
3273                        }
3274                    }
3275                    Mf::Mix => "mix",
3276                    Mf::Step => "step",
3277                    Mf::SmoothStep => "smoothstep",
3278                    Mf::Sqrt => "sqrt",
3279                    Mf::InverseSqrt => "inversesqrt",
3280                    Mf::Inverse => "inverse",
3281                    Mf::Transpose => "transpose",
3282                    Mf::Determinant => "determinant",
3283                    Mf::QuantizeToF16 => match *ctx.resolve_type(arg, &self.module.types) {
3284                        TypeInner::Scalar { .. } => {
3285                            write!(self.out, "unpackHalf2x16(packHalf2x16(vec2(")?;
3286                            self.write_expr(arg, ctx)?;
3287                            write!(self.out, "))).x")?;
3288                            return Ok(());
3289                        }
3290                        TypeInner::Vector {
3291                            size: crate::VectorSize::Bi,
3292                            ..
3293                        } => {
3294                            write!(self.out, "unpackHalf2x16(packHalf2x16(")?;
3295                            self.write_expr(arg, ctx)?;
3296                            write!(self.out, "))")?;
3297                            return Ok(());
3298                        }
3299                        TypeInner::Vector {
3300                            size: crate::VectorSize::Tri,
3301                            ..
3302                        } => {
3303                            write!(self.out, "vec3(unpackHalf2x16(packHalf2x16(")?;
3304                            self.write_expr(arg, ctx)?;
3305                            write!(self.out, ".xy)), unpackHalf2x16(packHalf2x16(")?;
3306                            self.write_expr(arg, ctx)?;
3307                            write!(self.out, ".zz)).x)")?;
3308                            return Ok(());
3309                        }
3310                        TypeInner::Vector {
3311                            size: crate::VectorSize::Quad,
3312                            ..
3313                        } => {
3314                            write!(self.out, "vec4(unpackHalf2x16(packHalf2x16(")?;
3315                            self.write_expr(arg, ctx)?;
3316                            write!(self.out, ".xy)), unpackHalf2x16(packHalf2x16(")?;
3317                            self.write_expr(arg, ctx)?;
3318                            write!(self.out, ".zw)))")?;
3319                            return Ok(());
3320                        }
3321                        _ => unreachable!(
3322                            "Correct TypeInner for QuantizeToF16 should be already validated"
3323                        ),
3324                    },
3325                    // bits
3326                    Mf::CountTrailingZeros => {
3327                        match *ctx.resolve_type(arg, &self.module.types) {
3328                            TypeInner::Vector { size, scalar, .. } => {
3329                                let s = common::vector_size_str(size);
3330                                if let crate::ScalarKind::Uint = scalar.kind {
3331                                    write!(self.out, "min(uvec{s}(findLSB(")?;
3332                                    self.write_expr(arg, ctx)?;
3333                                    write!(self.out, ")), uvec{s}(32u))")?;
3334                                } else {
3335                                    write!(self.out, "ivec{s}(min(uvec{s}(findLSB(")?;
3336                                    self.write_expr(arg, ctx)?;
3337                                    write!(self.out, ")), uvec{s}(32u)))")?;
3338                                }
3339                            }
3340                            TypeInner::Scalar(scalar) => {
3341                                if let crate::ScalarKind::Uint = scalar.kind {
3342                                    write!(self.out, "min(uint(findLSB(")?;
3343                                    self.write_expr(arg, ctx)?;
3344                                    write!(self.out, ")), 32u)")?;
3345                                } else {
3346                                    write!(self.out, "int(min(uint(findLSB(")?;
3347                                    self.write_expr(arg, ctx)?;
3348                                    write!(self.out, ")), 32u))")?;
3349                                }
3350                            }
3351                            _ => unreachable!(),
3352                        };
3353                        return Ok(());
3354                    }
3355                    Mf::CountLeadingZeros => {
3356                        if self.options.version.supports_integer_functions() {
3357                            match *ctx.resolve_type(arg, &self.module.types) {
3358                                TypeInner::Vector { size, scalar } => {
3359                                    let s = common::vector_size_str(size);
3360
3361                                    if let crate::ScalarKind::Uint = scalar.kind {
3362                                        write!(self.out, "uvec{s}(ivec{s}(31) - findMSB(")?;
3363                                        self.write_expr(arg, ctx)?;
3364                                        write!(self.out, "))")?;
3365                                    } else {
3366                                        write!(self.out, "mix(ivec{s}(31) - findMSB(")?;
3367                                        self.write_expr(arg, ctx)?;
3368                                        write!(self.out, "), ivec{s}(0), lessThan(")?;
3369                                        self.write_expr(arg, ctx)?;
3370                                        write!(self.out, ", ivec{s}(0)))")?;
3371                                    }
3372                                }
3373                                TypeInner::Scalar(scalar) => {
3374                                    if let crate::ScalarKind::Uint = scalar.kind {
3375                                        write!(self.out, "uint(31 - findMSB(")?;
3376                                    } else {
3377                                        write!(self.out, "(")?;
3378                                        self.write_expr(arg, ctx)?;
3379                                        write!(self.out, " < 0 ? 0 : 31 - findMSB(")?;
3380                                    }
3381
3382                                    self.write_expr(arg, ctx)?;
3383                                    write!(self.out, "))")?;
3384                                }
3385                                _ => unreachable!(),
3386                            };
3387                        } else {
3388                            match *ctx.resolve_type(arg, &self.module.types) {
3389                                TypeInner::Vector { size, scalar } => {
3390                                    let s = common::vector_size_str(size);
3391
3392                                    if let crate::ScalarKind::Uint = scalar.kind {
3393                                        write!(self.out, "uvec{s}(")?;
3394                                        write!(self.out, "vec{s}(31.0) - floor(log2(vec{s}(")?;
3395                                        self.write_expr(arg, ctx)?;
3396                                        write!(self.out, ") + 0.5)))")?;
3397                                    } else {
3398                                        write!(self.out, "ivec{s}(")?;
3399                                        write!(self.out, "mix(vec{s}(31.0) - floor(log2(vec{s}(")?;
3400                                        self.write_expr(arg, ctx)?;
3401                                        write!(self.out, ") + 0.5)), ")?;
3402                                        write!(self.out, "vec{s}(0.0), lessThan(")?;
3403                                        self.write_expr(arg, ctx)?;
3404                                        write!(self.out, ", ivec{s}(0u))))")?;
3405                                    }
3406                                }
3407                                TypeInner::Scalar(scalar) => {
3408                                    if let crate::ScalarKind::Uint = scalar.kind {
3409                                        write!(self.out, "uint(31.0 - floor(log2(float(")?;
3410                                        self.write_expr(arg, ctx)?;
3411                                        write!(self.out, ") + 0.5)))")?;
3412                                    } else {
3413                                        write!(self.out, "(")?;
3414                                        self.write_expr(arg, ctx)?;
3415                                        write!(self.out, " < 0 ? 0 : int(")?;
3416                                        write!(self.out, "31.0 - floor(log2(float(")?;
3417                                        self.write_expr(arg, ctx)?;
3418                                        write!(self.out, ") + 0.5))))")?;
3419                                    }
3420                                }
3421                                _ => unreachable!(),
3422                            };
3423                        }
3424
3425                        return Ok(());
3426                    }
3427                    Mf::CountOneBits => "bitCount",
3428                    Mf::ReverseBits => "bitfieldReverse",
3429                    Mf::ExtractBits => {
3430                        // The behavior of ExtractBits is undefined when offset + count > bit_width. We need
3431                        // to first sanitize the offset and count first. If we don't do this, AMD and Intel chips
3432                        // will return out-of-spec values if the extracted range is not within the bit width.
3433                        //
3434                        // This encodes the exact formula specified by the wgsl spec, without temporary values:
3435                        // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin
3436                        //
3437                        // w = sizeof(x) * 8
3438                        // o = min(offset, w)
3439                        // c = min(count, w - o)
3440                        //
3441                        // bitfieldExtract(x, o, c)
3442                        //
3443                        // extract_bits(e, min(offset, w), min(count, w - min(offset, w))))
3444                        let scalar_bits = ctx
3445                            .resolve_type(arg, &self.module.types)
3446                            .scalar_width()
3447                            .unwrap()
3448                            * 8;
3449
3450                        write!(self.out, "bitfieldExtract(")?;
3451                        self.write_expr(arg, ctx)?;
3452                        write!(self.out, ", int(min(")?;
3453                        self.write_expr(arg1.unwrap(), ctx)?;
3454                        write!(self.out, ", {scalar_bits}u)), int(min(",)?;
3455                        self.write_expr(arg2.unwrap(), ctx)?;
3456                        write!(self.out, ", {scalar_bits}u - min(")?;
3457                        self.write_expr(arg1.unwrap(), ctx)?;
3458                        write!(self.out, ", {scalar_bits}u))))")?;
3459
3460                        return Ok(());
3461                    }
3462                    Mf::InsertBits => {
3463                        // InsertBits has the same considerations as ExtractBits above
3464                        let scalar_bits = ctx
3465                            .resolve_type(arg, &self.module.types)
3466                            .scalar_width()
3467                            .unwrap()
3468                            * 8;
3469
3470                        write!(self.out, "bitfieldInsert(")?;
3471                        self.write_expr(arg, ctx)?;
3472                        write!(self.out, ", ")?;
3473                        self.write_expr(arg1.unwrap(), ctx)?;
3474                        write!(self.out, ", int(min(")?;
3475                        self.write_expr(arg2.unwrap(), ctx)?;
3476                        write!(self.out, ", {scalar_bits}u)), int(min(",)?;
3477                        self.write_expr(arg3.unwrap(), ctx)?;
3478                        write!(self.out, ", {scalar_bits}u - min(")?;
3479                        self.write_expr(arg2.unwrap(), ctx)?;
3480                        write!(self.out, ", {scalar_bits}u))))")?;
3481
3482                        return Ok(());
3483                    }
3484                    Mf::FirstTrailingBit => "findLSB",
3485                    Mf::FirstLeadingBit => "findMSB",
3486                    // data packing
3487                    Mf::Pack4x8snorm => {
3488                        if self.options.version.supports_pack_unpack_4x8() {
3489                            "packSnorm4x8"
3490                        } else {
3491                            // polyfill should go here. Needs a corresponding entry in `need_bake_expression`
3492                            return Err(Error::UnsupportedExternal("packSnorm4x8".into()));
3493                        }
3494                    }
3495                    Mf::Pack4x8unorm => {
3496                        if self.options.version.supports_pack_unpack_4x8() {
3497                            "packUnorm4x8"
3498                        } else {
3499                            return Err(Error::UnsupportedExternal("packUnorm4x8".to_owned()));
3500                        }
3501                    }
3502                    Mf::Pack2x16snorm => {
3503                        if self.options.version.supports_pack_unpack_snorm_2x16() {
3504                            "packSnorm2x16"
3505                        } else {
3506                            return Err(Error::UnsupportedExternal("packSnorm2x16".to_owned()));
3507                        }
3508                    }
3509                    Mf::Pack2x16unorm => {
3510                        if self.options.version.supports_pack_unpack_unorm_2x16() {
3511                            "packUnorm2x16"
3512                        } else {
3513                            return Err(Error::UnsupportedExternal("packUnorm2x16".to_owned()));
3514                        }
3515                    }
3516                    Mf::Pack2x16float => {
3517                        if self.options.version.supports_pack_unpack_half_2x16() {
3518                            "packHalf2x16"
3519                        } else {
3520                            return Err(Error::UnsupportedExternal("packHalf2x16".to_owned()));
3521                        }
3522                    }
3523
3524                    fun @ (Mf::Pack4xI8 | Mf::Pack4xU8 | Mf::Pack4xI8Clamp | Mf::Pack4xU8Clamp) => {
3525                        let was_signed = matches!(fun, Mf::Pack4xI8 | Mf::Pack4xI8Clamp);
3526                        let clamp_bounds = match fun {
3527                            Mf::Pack4xI8Clamp => Some(("-128", "127")),
3528                            Mf::Pack4xU8Clamp => Some(("0", "255")),
3529                            _ => None,
3530                        };
3531                        let const_suffix = if was_signed { "" } else { "u" };
3532                        if was_signed {
3533                            write!(self.out, "uint(")?;
3534                        }
3535                        let write_arg = |this: &mut Self| -> BackendResult {
3536                            if let Some((min, max)) = clamp_bounds {
3537                                write!(this.out, "clamp(")?;
3538                                this.write_expr(arg, ctx)?;
3539                                write!(this.out, ", {min}{const_suffix}, {max}{const_suffix})")?;
3540                            } else {
3541                                this.write_expr(arg, ctx)?;
3542                            }
3543                            Ok(())
3544                        };
3545                        write!(self.out, "(")?;
3546                        write_arg(self)?;
3547                        write!(self.out, "[0] & 0xFF{const_suffix}) | ((")?;
3548                        write_arg(self)?;
3549                        write!(self.out, "[1] & 0xFF{const_suffix}) << 8) | ((")?;
3550                        write_arg(self)?;
3551                        write!(self.out, "[2] & 0xFF{const_suffix}) << 16) | ((")?;
3552                        write_arg(self)?;
3553                        write!(self.out, "[3] & 0xFF{const_suffix}) << 24)")?;
3554                        if was_signed {
3555                            write!(self.out, ")")?;
3556                        }
3557
3558                        return Ok(());
3559                    }
3560                    // data unpacking
3561                    Mf::Unpack2x16float => {
3562                        if self.options.version.supports_pack_unpack_half_2x16() {
3563                            "unpackHalf2x16"
3564                        } else {
3565                            return Err(Error::UnsupportedExternal("unpackHalf2x16".into()));
3566                        }
3567                    }
3568                    Mf::Unpack2x16snorm => {
3569                        if self.options.version.supports_pack_unpack_snorm_2x16() {
3570                            "unpackSnorm2x16"
3571                        } else {
3572                            let scale = 32767;
3573
3574                            write!(self.out, "(vec2(ivec2(")?;
3575                            self.write_expr(arg, ctx)?;
3576                            write!(self.out, " << 16, ")?;
3577                            self.write_expr(arg, ctx)?;
3578                            write!(self.out, ") >> 16) / {scale}.0)")?;
3579                            return Ok(());
3580                        }
3581                    }
3582                    Mf::Unpack2x16unorm => {
3583                        if self.options.version.supports_pack_unpack_unorm_2x16() {
3584                            "unpackUnorm2x16"
3585                        } else {
3586                            let scale = 65535;
3587
3588                            write!(self.out, "(vec2(")?;
3589                            self.write_expr(arg, ctx)?;
3590                            write!(self.out, " & 0xFFFFu, ")?;
3591                            self.write_expr(arg, ctx)?;
3592                            write!(self.out, " >> 16) / {scale}.0)")?;
3593                            return Ok(());
3594                        }
3595                    }
3596                    Mf::Unpack4x8snorm => {
3597                        if self.options.version.supports_pack_unpack_4x8() {
3598                            "unpackSnorm4x8"
3599                        } else {
3600                            let scale = 127;
3601
3602                            write!(self.out, "(vec4(ivec4(")?;
3603                            self.write_expr(arg, ctx)?;
3604                            write!(self.out, " << 24, ")?;
3605                            self.write_expr(arg, ctx)?;
3606                            write!(self.out, " << 16, ")?;
3607                            self.write_expr(arg, ctx)?;
3608                            write!(self.out, " << 8, ")?;
3609                            self.write_expr(arg, ctx)?;
3610                            write!(self.out, ") >> 24) / {scale}.0)")?;
3611                            return Ok(());
3612                        }
3613                    }
3614                    Mf::Unpack4x8unorm => {
3615                        if self.options.version.supports_pack_unpack_4x8() {
3616                            "unpackUnorm4x8"
3617                        } else {
3618                            let scale = 255;
3619
3620                            write!(self.out, "(vec4(")?;
3621                            self.write_expr(arg, ctx)?;
3622                            write!(self.out, " & 0xFFu, ")?;
3623                            self.write_expr(arg, ctx)?;
3624                            write!(self.out, " >> 8 & 0xFFu, ")?;
3625                            self.write_expr(arg, ctx)?;
3626                            write!(self.out, " >> 16 & 0xFFu, ")?;
3627                            self.write_expr(arg, ctx)?;
3628                            write!(self.out, " >> 24) / {scale}.0)")?;
3629                            return Ok(());
3630                        }
3631                    }
3632                    fun @ (Mf::Unpack4xI8 | Mf::Unpack4xU8) => {
3633                        let sign_prefix = match fun {
3634                            Mf::Unpack4xI8 => 'i',
3635                            Mf::Unpack4xU8 => 'u',
3636                            _ => unreachable!(),
3637                        };
3638                        write!(self.out, "{sign_prefix}vec4(")?;
3639                        for i in 0..4 {
3640                            write!(self.out, "bitfieldExtract(")?;
3641                            // Since bitfieldExtract only sign extends if the value is signed, this
3642                            // cast is needed
3643                            match fun {
3644                                Mf::Unpack4xI8 => {
3645                                    write!(self.out, "int(")?;
3646                                    self.write_expr(arg, ctx)?;
3647                                    write!(self.out, ")")?;
3648                                }
3649                                Mf::Unpack4xU8 => self.write_expr(arg, ctx)?,
3650                                _ => unreachable!(),
3651                            };
3652                            write!(self.out, ", {}, 8)", i * 8)?;
3653                            if i != 3 {
3654                                write!(self.out, ", ")?;
3655                            }
3656                        }
3657                        write!(self.out, ")")?;
3658
3659                        return Ok(());
3660                    }
3661                };
3662
3663                let extract_bits = fun == Mf::ExtractBits;
3664                let insert_bits = fun == Mf::InsertBits;
3665
3666                // Some GLSL functions always return signed integers (like findMSB),
3667                // so they need to be cast to uint if the argument is also an uint.
3668                let ret_might_need_int_to_uint = matches!(
3669                    fun,
3670                    Mf::FirstTrailingBit | Mf::FirstLeadingBit | Mf::CountOneBits | Mf::Abs
3671                );
3672
3673                // Some GLSL functions only accept signed integers (like abs),
3674                // so they need their argument cast from uint to int.
3675                let arg_might_need_uint_to_int = matches!(fun, Mf::Abs);
3676
3677                // Check if the argument is an unsigned integer and return the vector size
3678                // in case it's a vector
3679                let maybe_uint_size = match *ctx.resolve_type(arg, &self.module.types) {
3680                    TypeInner::Scalar(crate::Scalar {
3681                        kind: crate::ScalarKind::Uint,
3682                        ..
3683                    }) => Some(None),
3684                    TypeInner::Vector {
3685                        scalar:
3686                            crate::Scalar {
3687                                kind: crate::ScalarKind::Uint,
3688                                ..
3689                            },
3690                        size,
3691                    } => Some(Some(size)),
3692                    _ => None,
3693                };
3694
3695                // Cast to uint if the function needs it
3696                if ret_might_need_int_to_uint {
3697                    if let Some(maybe_size) = maybe_uint_size {
3698                        match maybe_size {
3699                            Some(size) => write!(self.out, "uvec{}(", size as u8)?,
3700                            None => write!(self.out, "uint(")?,
3701                        }
3702                    }
3703                }
3704
3705                write!(self.out, "{fun_name}(")?;
3706
3707                // Cast to int if the function needs it
3708                if arg_might_need_uint_to_int {
3709                    if let Some(maybe_size) = maybe_uint_size {
3710                        match maybe_size {
3711                            Some(size) => write!(self.out, "ivec{}(", size as u8)?,
3712                            None => write!(self.out, "int(")?,
3713                        }
3714                    }
3715                }
3716
3717                self.write_expr(arg, ctx)?;
3718
3719                // Close the cast from uint to int
3720                if arg_might_need_uint_to_int && maybe_uint_size.is_some() {
3721                    write!(self.out, ")")?
3722                }
3723
3724                if let Some(arg) = arg1 {
3725                    write!(self.out, ", ")?;
3726                    if extract_bits {
3727                        write!(self.out, "int(")?;
3728                        self.write_expr(arg, ctx)?;
3729                        write!(self.out, ")")?;
3730                    } else {
3731                        self.write_expr(arg, ctx)?;
3732                    }
3733                }
3734                if let Some(arg) = arg2 {
3735                    write!(self.out, ", ")?;
3736                    if extract_bits || insert_bits {
3737                        write!(self.out, "int(")?;
3738                        self.write_expr(arg, ctx)?;
3739                        write!(self.out, ")")?;
3740                    } else {
3741                        self.write_expr(arg, ctx)?;
3742                    }
3743                }
3744                if let Some(arg) = arg3 {
3745                    write!(self.out, ", ")?;
3746                    if insert_bits {
3747                        write!(self.out, "int(")?;
3748                        self.write_expr(arg, ctx)?;
3749                        write!(self.out, ")")?;
3750                    } else {
3751                        self.write_expr(arg, ctx)?;
3752                    }
3753                }
3754                write!(self.out, ")")?;
3755
3756                // Close the cast from int to uint
3757                if ret_might_need_int_to_uint && maybe_uint_size.is_some() {
3758                    write!(self.out, ")")?
3759                }
3760            }
3761            // `As` is always a call.
3762            // If `convert` is true the function name is the type
3763            // Else the function name is one of the glsl provided bitcast functions
3764            Expression::As {
3765                expr,
3766                kind: target_kind,
3767                convert,
3768            } => {
3769                let inner = ctx.resolve_type(expr, &self.module.types);
3770                match convert {
3771                    Some(width) => {
3772                        // this is similar to `write_type`, but with the target kind
3773                        let scalar = glsl_scalar(crate::Scalar {
3774                            kind: target_kind,
3775                            width,
3776                        })?;
3777                        match *inner {
3778                            TypeInner::Matrix { columns, rows, .. } => write!(
3779                                self.out,
3780                                "{}mat{}x{}",
3781                                scalar.prefix, columns as u8, rows as u8
3782                            )?,
3783                            TypeInner::Vector { size, .. } => {
3784                                write!(self.out, "{}vec{}", scalar.prefix, size as u8)?
3785                            }
3786                            _ => write!(self.out, "{}", scalar.full)?,
3787                        }
3788
3789                        write!(self.out, "(")?;
3790                        self.write_expr(expr, ctx)?;
3791                        write!(self.out, ")")?
3792                    }
3793                    None => {
3794                        use crate::ScalarKind as Sk;
3795
3796                        let target_vector_type = match *inner {
3797                            TypeInner::Vector { size, scalar } => Some(TypeInner::Vector {
3798                                size,
3799                                scalar: crate::Scalar {
3800                                    kind: target_kind,
3801                                    width: scalar.width,
3802                                },
3803                            }),
3804                            _ => None,
3805                        };
3806
3807                        let source_kind = inner.scalar_kind().unwrap();
3808
3809                        match (source_kind, target_kind, target_vector_type) {
3810                            // No conversion needed
3811                            (Sk::Sint, Sk::Sint, _)
3812                            | (Sk::Uint, Sk::Uint, _)
3813                            | (Sk::Float, Sk::Float, _)
3814                            | (Sk::Bool, Sk::Bool, _) => {
3815                                self.write_expr(expr, ctx)?;
3816                                return Ok(());
3817                            }
3818
3819                            // Cast to/from floats
3820                            (Sk::Float, Sk::Sint, _) => write!(self.out, "floatBitsToInt")?,
3821                            (Sk::Float, Sk::Uint, _) => write!(self.out, "floatBitsToUint")?,
3822                            (Sk::Sint, Sk::Float, _) => write!(self.out, "intBitsToFloat")?,
3823                            (Sk::Uint, Sk::Float, _) => write!(self.out, "uintBitsToFloat")?,
3824
3825                            // Cast between vector types
3826                            (_, _, Some(vector)) => {
3827                                self.write_value_type(&vector)?;
3828                            }
3829
3830                            // There is no way to bitcast between Uint/Sint in glsl. Use constructor conversion
3831                            (Sk::Uint | Sk::Bool, Sk::Sint, None) => write!(self.out, "int")?,
3832                            (Sk::Sint | Sk::Bool, Sk::Uint, None) => write!(self.out, "uint")?,
3833                            (Sk::Bool, Sk::Float, None) => write!(self.out, "float")?,
3834                            (Sk::Sint | Sk::Uint | Sk::Float, Sk::Bool, None) => {
3835                                write!(self.out, "bool")?
3836                            }
3837
3838                            (Sk::AbstractInt | Sk::AbstractFloat, _, _)
3839                            | (_, Sk::AbstractInt | Sk::AbstractFloat, _) => unreachable!(),
3840                        };
3841
3842                        write!(self.out, "(")?;
3843                        self.write_expr(expr, ctx)?;
3844                        write!(self.out, ")")?;
3845                    }
3846                }
3847            }
3848            // These expressions never show up in `Emit`.
3849            Expression::CallResult(_)
3850            | Expression::AtomicResult { .. }
3851            | Expression::RayQueryProceedResult
3852            | Expression::WorkGroupUniformLoadResult { .. }
3853            | Expression::SubgroupOperationResult { .. }
3854            | Expression::SubgroupBallotResult => unreachable!(),
3855            // `ArrayLength` is written as `expr.length()` and we convert it to a uint
3856            Expression::ArrayLength(expr) => {
3857                write!(self.out, "uint(")?;
3858                self.write_expr(expr, ctx)?;
3859                write!(self.out, ".length())")?
3860            }
3861            // not supported yet
3862            Expression::RayQueryGetIntersection { .. }
3863            | Expression::RayQueryVertexPositions { .. }
3864            | Expression::CooperativeLoad { .. }
3865            | Expression::CooperativeMultiplyAdd { .. } => unreachable!(),
3866        }
3867
3868        Ok(())
3869    }
3870
3871    /// Helper function to write the local holding the clamped lod
3872    fn write_clamped_lod(
3873        &mut self,
3874        ctx: &back::FunctionCtx,
3875        expr: Handle<crate::Expression>,
3876        image: Handle<crate::Expression>,
3877        level_expr: Handle<crate::Expression>,
3878    ) -> Result<(), Error> {
3879        // Define our local and start a call to `clamp`
3880        write!(
3881            self.out,
3882            "int {}{} = clamp(",
3883            Baked(expr),
3884            CLAMPED_LOD_SUFFIX
3885        )?;
3886        // Write the lod that will be clamped
3887        self.write_expr(level_expr, ctx)?;
3888        // Set the min value to 0 and start a call to `textureQueryLevels` to get
3889        // the maximum value
3890        write!(self.out, ", 0, textureQueryLevels(")?;
3891        // Write the target image as an argument to `textureQueryLevels`
3892        self.write_expr(image, ctx)?;
3893        // Close the call to `textureQueryLevels` subtract 1 from it since
3894        // the lod argument is 0 based, close the `clamp` call and end the
3895        // local declaration statement.
3896        writeln!(self.out, ") - 1);")?;
3897
3898        Ok(())
3899    }
3900
3901    // Helper method used to retrieve how many elements a coordinate vector
3902    // for the images operations need.
3903    fn get_coordinate_vector_size(&self, dim: crate::ImageDimension, arrayed: bool) -> u8 {
3904        // openGL es doesn't have 1D images so we need workaround it
3905        let tex_1d_hack = dim == crate::ImageDimension::D1 && self.options.version.is_es();
3906        // Get how many components the coordinate vector needs for the dimensions only
3907        let tex_coord_size = match dim {
3908            crate::ImageDimension::D1 => 1,
3909            crate::ImageDimension::D2 => 2,
3910            crate::ImageDimension::D3 => 3,
3911            crate::ImageDimension::Cube => 2,
3912        };
3913        // Calculate the true size of the coordinate vector by adding 1 for arrayed images
3914        // and another 1 if we need to workaround 1D images by making them 2D
3915        tex_coord_size + tex_1d_hack as u8 + arrayed as u8
3916    }
3917
3918    /// Helper method to write the coordinate vector for image operations
3919    fn write_texture_coord(
3920        &mut self,
3921        ctx: &back::FunctionCtx,
3922        vector_size: u8,
3923        coordinate: Handle<crate::Expression>,
3924        array_index: Option<Handle<crate::Expression>>,
3925        // Emulate 1D images as 2D for profiles that don't support it (glsl es)
3926        tex_1d_hack: bool,
3927    ) -> Result<(), Error> {
3928        match array_index {
3929            // If the image needs an array indice we need to add it to the end of our
3930            // coordinate vector, to do so we will use the `ivec(ivec, scalar)`
3931            // constructor notation (NOTE: the inner `ivec` can also be a scalar, this
3932            // is important for 1D arrayed images).
3933            Some(layer_expr) => {
3934                write!(self.out, "ivec{vector_size}(")?;
3935                self.write_expr(coordinate, ctx)?;
3936                write!(self.out, ", ")?;
3937                // If we are replacing sampler1D with sampler2D we also need
3938                // to add another zero to the coordinates vector for the y component
3939                if tex_1d_hack {
3940                    write!(self.out, "0, ")?;
3941                }
3942                self.write_expr(layer_expr, ctx)?;
3943                write!(self.out, ")")?;
3944            }
3945            // Otherwise write just the expression (and the 1D hack if needed)
3946            None => {
3947                let uvec_size = match *ctx.resolve_type(coordinate, &self.module.types) {
3948                    TypeInner::Scalar(crate::Scalar {
3949                        kind: crate::ScalarKind::Uint,
3950                        ..
3951                    }) => Some(None),
3952                    TypeInner::Vector {
3953                        size,
3954                        scalar:
3955                            crate::Scalar {
3956                                kind: crate::ScalarKind::Uint,
3957                                ..
3958                            },
3959                    } => Some(Some(size as u32)),
3960                    _ => None,
3961                };
3962                if tex_1d_hack {
3963                    write!(self.out, "ivec2(")?;
3964                } else if uvec_size.is_some() {
3965                    match uvec_size {
3966                        Some(None) => write!(self.out, "int(")?,
3967                        Some(Some(size)) => write!(self.out, "ivec{size}(")?,
3968                        _ => {}
3969                    }
3970                }
3971                self.write_expr(coordinate, ctx)?;
3972                if tex_1d_hack {
3973                    write!(self.out, ", 0)")?;
3974                } else if uvec_size.is_some() {
3975                    write!(self.out, ")")?;
3976                }
3977            }
3978        }
3979
3980        Ok(())
3981    }
3982
3983    /// Helper method to write the `ImageStore` statement
3984    fn write_image_store(
3985        &mut self,
3986        ctx: &back::FunctionCtx,
3987        image: Handle<crate::Expression>,
3988        coordinate: Handle<crate::Expression>,
3989        array_index: Option<Handle<crate::Expression>>,
3990        value: Handle<crate::Expression>,
3991    ) -> Result<(), Error> {
3992        use crate::ImageDimension as IDim;
3993
3994        // NOTE: openGL requires that `imageStore`s have no effects when the texel is invalid
3995        // so we don't need to generate bounds checks (OpenGL 4.2 Core §3.9.20)
3996
3997        // This will only panic if the module is invalid
3998        let dim = match *ctx.resolve_type(image, &self.module.types) {
3999            TypeInner::Image { dim, .. } => dim,
4000            _ => unreachable!(),
4001        };
4002
4003        // Begin our call to `imageStore`
4004        write!(self.out, "imageStore(")?;
4005        self.write_expr(image, ctx)?;
4006        // Separate the image argument from the coordinates
4007        write!(self.out, ", ")?;
4008
4009        // openGL es doesn't have 1D images so we need workaround it
4010        let tex_1d_hack = dim == IDim::D1 && self.options.version.is_es();
4011        // Write the coordinate vector
4012        self.write_texture_coord(
4013            ctx,
4014            // Get the size of the coordinate vector
4015            self.get_coordinate_vector_size(dim, array_index.is_some()),
4016            coordinate,
4017            array_index,
4018            tex_1d_hack,
4019        )?;
4020
4021        // Separate the coordinate from the value to write and write the expression
4022        // of the value to write.
4023        write!(self.out, ", ")?;
4024        self.write_expr(value, ctx)?;
4025        // End the call to `imageStore` and the statement.
4026        writeln!(self.out, ");")?;
4027
4028        Ok(())
4029    }
4030
4031    /// Helper method to write the `ImageAtomic` statement
4032    fn write_image_atomic(
4033        &mut self,
4034        ctx: &back::FunctionCtx,
4035        image: Handle<crate::Expression>,
4036        coordinate: Handle<crate::Expression>,
4037        array_index: Option<Handle<crate::Expression>>,
4038        fun: crate::AtomicFunction,
4039        value: Handle<crate::Expression>,
4040    ) -> Result<(), Error> {
4041        use crate::ImageDimension as IDim;
4042
4043        // NOTE: openGL requires that `imageAtomic`s have no effects when the texel is invalid
4044        // so we don't need to generate bounds checks (OpenGL 4.2 Core §3.9.20)
4045
4046        // This will only panic if the module is invalid
4047        let dim = match *ctx.resolve_type(image, &self.module.types) {
4048            TypeInner::Image { dim, .. } => dim,
4049            _ => unreachable!(),
4050        };
4051
4052        // Begin our call to `imageAtomic`
4053        let fun_str = fun.to_glsl();
4054        write!(self.out, "imageAtomic{fun_str}(")?;
4055        self.write_expr(image, ctx)?;
4056        // Separate the image argument from the coordinates
4057        write!(self.out, ", ")?;
4058
4059        // openGL es doesn't have 1D images so we need workaround it
4060        let tex_1d_hack = dim == IDim::D1 && self.options.version.is_es();
4061        // Write the coordinate vector
4062        self.write_texture_coord(
4063            ctx,
4064            // Get the size of the coordinate vector
4065            self.get_coordinate_vector_size(dim, false),
4066            coordinate,
4067            array_index,
4068            tex_1d_hack,
4069        )?;
4070
4071        // Separate the coordinate from the value to write and write the expression
4072        // of the value to write.
4073        write!(self.out, ", ")?;
4074        self.write_expr(value, ctx)?;
4075        // End the call to `imageAtomic` and the statement.
4076        writeln!(self.out, ");")?;
4077
4078        Ok(())
4079    }
4080
4081    /// Helper method for writing an `ImageLoad` expression.
4082    #[allow(clippy::too_many_arguments)]
4083    fn write_image_load(
4084        &mut self,
4085        handle: Handle<crate::Expression>,
4086        ctx: &back::FunctionCtx,
4087        image: Handle<crate::Expression>,
4088        coordinate: Handle<crate::Expression>,
4089        array_index: Option<Handle<crate::Expression>>,
4090        sample: Option<Handle<crate::Expression>>,
4091        level: Option<Handle<crate::Expression>>,
4092    ) -> Result<(), Error> {
4093        use crate::ImageDimension as IDim;
4094
4095        // `ImageLoad` is a bit complicated.
4096        // There are two functions one for sampled
4097        // images another for storage images, the former uses `texelFetch` and the
4098        // latter uses `imageLoad`.
4099        //
4100        // Furthermore we have `level` which is always `Some` for sampled images
4101        // and `None` for storage images, so we end up with two functions:
4102        // - `texelFetch(image, coordinate, level)` for sampled images
4103        // - `imageLoad(image, coordinate)` for storage images
4104        //
4105        // Finally we also have to consider bounds checking, for storage images
4106        // this is easy since openGL requires that invalid texels always return
4107        // 0, for sampled images we need to either verify that all arguments are
4108        // in bounds (`ReadZeroSkipWrite`) or make them a valid texel (`Restrict`).
4109
4110        // This will only panic if the module is invalid
4111        let (dim, class) = match *ctx.resolve_type(image, &self.module.types) {
4112            TypeInner::Image {
4113                dim,
4114                arrayed: _,
4115                class,
4116            } => (dim, class),
4117            _ => unreachable!(),
4118        };
4119
4120        // Get the name of the function to be used for the load operation
4121        // and the policy to be used with it.
4122        let (fun_name, policy) = match class {
4123            // Sampled images inherit the policy from the user passed policies
4124            crate::ImageClass::Sampled { .. } => ("texelFetch", self.policies.image_load),
4125            crate::ImageClass::Storage { .. } => {
4126                // OpenGL ES 3.1 mentions in Chapter "8.22 Texture Image Loads and Stores" that:
4127                // "Invalid image loads will return a vector where the value of R, G, and B components
4128                // is 0 and the value of the A component is undefined."
4129                //
4130                // OpenGL 4.2 Core mentions in Chapter "3.9.20 Texture Image Loads and Stores" that:
4131                // "Invalid image loads will return zero."
4132                //
4133                // So, we only inject bounds checks for ES
4134                let policy = if self.options.version.is_es() {
4135                    self.policies.image_load
4136                } else {
4137                    proc::BoundsCheckPolicy::Unchecked
4138                };
4139                ("imageLoad", policy)
4140            }
4141            // TODO: Is there even a function for this?
4142            crate::ImageClass::Depth { multi: _ } => {
4143                return Err(Error::Custom(
4144                    "WGSL `textureLoad` from depth textures is not supported in GLSL".to_string(),
4145                ))
4146            }
4147            crate::ImageClass::External => unimplemented!(),
4148        };
4149
4150        // openGL es doesn't have 1D images so we need workaround it
4151        let tex_1d_hack = dim == IDim::D1 && self.options.version.is_es();
4152        // Get the size of the coordinate vector
4153        let vector_size = self.get_coordinate_vector_size(dim, array_index.is_some());
4154
4155        if let proc::BoundsCheckPolicy::ReadZeroSkipWrite = policy {
4156            // To write the bounds checks for `ReadZeroSkipWrite` we will use a
4157            // ternary operator since we are in the middle of an expression and
4158            // need to return a value.
4159            //
4160            // NOTE: glsl does short circuit when evaluating logical
4161            // expressions so we can be sure that after we test a
4162            // condition it will be true for the next ones
4163
4164            // Write parentheses around the ternary operator to prevent problems with
4165            // expressions emitted before or after it having more precedence
4166            write!(self.out, "(",)?;
4167
4168            // The lod check needs to precede the size check since we need
4169            // to use the lod to get the size of the image at that level.
4170            if let Some(level_expr) = level {
4171                self.write_expr(level_expr, ctx)?;
4172                write!(self.out, " < textureQueryLevels(",)?;
4173                self.write_expr(image, ctx)?;
4174                // Chain the next check
4175                write!(self.out, ") && ")?;
4176            }
4177
4178            // Check that the sample arguments doesn't exceed the number of samples
4179            if let Some(sample_expr) = sample {
4180                self.write_expr(sample_expr, ctx)?;
4181                write!(self.out, " < textureSamples(",)?;
4182                self.write_expr(image, ctx)?;
4183                // Chain the next check
4184                write!(self.out, ") && ")?;
4185            }
4186
4187            // We now need to write the size checks for the coordinates and array index
4188            // first we write the comparison function in case the image is 1D non arrayed
4189            // (and no 1D to 2D hack was needed) we are comparing scalars so the less than
4190            // operator will suffice, but otherwise we'll be comparing two vectors so we'll
4191            // need to use the `lessThan` function but it returns a vector of booleans (one
4192            // for each comparison) so we need to fold it all in one scalar boolean, since
4193            // we want all comparisons to pass we use the `all` function which will only
4194            // return `true` if all the elements of the boolean vector are also `true`.
4195            //
4196            // So we'll end with one of the following forms
4197            // - `coord < textureSize(image, lod)` for 1D images
4198            // - `all(lessThan(coord, textureSize(image, lod)))` for normal images
4199            // - `all(lessThan(ivec(coord, array_index), textureSize(image, lod)))`
4200            //    for arrayed images
4201            // - `all(lessThan(coord, textureSize(image)))` for multi sampled images
4202
4203            if vector_size != 1 {
4204                write!(self.out, "all(lessThan(")?;
4205            }
4206
4207            // Write the coordinate vector
4208            self.write_texture_coord(ctx, vector_size, coordinate, array_index, tex_1d_hack)?;
4209
4210            if vector_size != 1 {
4211                // If we used the `lessThan` function we need to separate the
4212                // coordinates from the image size.
4213                write!(self.out, ", ")?;
4214            } else {
4215                // If we didn't use it (ie. 1D images) we perform the comparison
4216                // using the less than operator.
4217                write!(self.out, " < ")?;
4218            }
4219
4220            // Call `textureSize` to get our image size
4221            write!(self.out, "textureSize(")?;
4222            self.write_expr(image, ctx)?;
4223            // `textureSize` uses the lod as a second argument for mipmapped images
4224            if let Some(level_expr) = level {
4225                // Separate the image from the lod
4226                write!(self.out, ", ")?;
4227                self.write_expr(level_expr, ctx)?;
4228            }
4229            // Close the `textureSize` call
4230            write!(self.out, ")")?;
4231
4232            if vector_size != 1 {
4233                // Close the `all` and `lessThan` calls
4234                write!(self.out, "))")?;
4235            }
4236
4237            // Finally end the condition part of the ternary operator
4238            write!(self.out, " ? ")?;
4239        }
4240
4241        // Begin the call to the function used to load the texel
4242        write!(self.out, "{fun_name}(")?;
4243        self.write_expr(image, ctx)?;
4244        write!(self.out, ", ")?;
4245
4246        // If we are using `Restrict` bounds checking we need to pass valid texel
4247        // coordinates, to do so we use the `clamp` function to get a value between
4248        // 0 and the image size - 1 (indexing begins at 0)
4249        if let proc::BoundsCheckPolicy::Restrict = policy {
4250            write!(self.out, "clamp(")?;
4251        }
4252
4253        // Write the coordinate vector
4254        self.write_texture_coord(ctx, vector_size, coordinate, array_index, tex_1d_hack)?;
4255
4256        // If we are using `Restrict` bounds checking we need to write the rest of the
4257        // clamp we initiated before writing the coordinates.
4258        if let proc::BoundsCheckPolicy::Restrict = policy {
4259            // Write the min value 0
4260            if vector_size == 1 {
4261                write!(self.out, ", 0")?;
4262            } else {
4263                write!(self.out, ", ivec{vector_size}(0)")?;
4264            }
4265            // Start the `textureSize` call to use as the max value.
4266            write!(self.out, ", textureSize(")?;
4267            self.write_expr(image, ctx)?;
4268            // If the image is mipmapped we need to add the lod argument to the
4269            // `textureSize` call, but this needs to be the clamped lod, this should
4270            // have been generated earlier and put in a local.
4271            if class.is_mipmapped() {
4272                write!(self.out, ", {}{}", Baked(handle), CLAMPED_LOD_SUFFIX)?;
4273            }
4274            // Close the `textureSize` call
4275            write!(self.out, ")")?;
4276
4277            // Subtract 1 from the `textureSize` call since the coordinates are zero based.
4278            if vector_size == 1 {
4279                write!(self.out, " - 1")?;
4280            } else {
4281                write!(self.out, " - ivec{vector_size}(1)")?;
4282            }
4283
4284            // Close the `clamp` call
4285            write!(self.out, ")")?;
4286
4287            // Add the clamped lod (if present) as the second argument to the
4288            // image load function.
4289            if level.is_some() {
4290                write!(self.out, ", {}{}", Baked(handle), CLAMPED_LOD_SUFFIX)?;
4291            }
4292
4293            // If a sample argument is needed we need to clamp it between 0 and
4294            // the number of samples the image has.
4295            if let Some(sample_expr) = sample {
4296                write!(self.out, ", clamp(")?;
4297                self.write_expr(sample_expr, ctx)?;
4298                // Set the min value to 0 and start the call to `textureSamples`
4299                write!(self.out, ", 0, textureSamples(")?;
4300                self.write_expr(image, ctx)?;
4301                // Close the `textureSamples` call, subtract 1 from it since the sample
4302                // argument is zero based, and close the `clamp` call
4303                writeln!(self.out, ") - 1)")?;
4304            }
4305        } else if let Some(sample_or_level) = sample.or(level) {
4306            // GLSL only support SInt on this field while WGSL support also UInt
4307            let cast_to_int = matches!(
4308                *ctx.resolve_type(sample_or_level, &self.module.types),
4309                TypeInner::Scalar(crate::Scalar {
4310                    kind: crate::ScalarKind::Uint,
4311                    ..
4312                })
4313            );
4314
4315            // If no bounds checking is need just add the sample or level argument
4316            // after the coordinates
4317            write!(self.out, ", ")?;
4318
4319            if cast_to_int {
4320                write!(self.out, "int(")?;
4321            }
4322
4323            self.write_expr(sample_or_level, ctx)?;
4324
4325            if cast_to_int {
4326                write!(self.out, ")")?;
4327            }
4328        }
4329
4330        // Close the image load function.
4331        write!(self.out, ")")?;
4332
4333        // If we were using the `ReadZeroSkipWrite` policy we need to end the first branch
4334        // (which is taken if the condition is `true`) with a colon (`:`) and write the
4335        // second branch which is just a 0 value.
4336        if let proc::BoundsCheckPolicy::ReadZeroSkipWrite = policy {
4337            // Get the kind of the output value.
4338            let kind = match class {
4339                // Only sampled images can reach here since storage images
4340                // don't need bounds checks and depth images aren't implemented
4341                crate::ImageClass::Sampled { kind, .. } => kind,
4342                _ => unreachable!(),
4343            };
4344
4345            // End the first branch
4346            write!(self.out, " : ")?;
4347            // Write the 0 value
4348            write!(
4349                self.out,
4350                "{}vec4(",
4351                glsl_scalar(crate::Scalar { kind, width: 4 })?.prefix,
4352            )?;
4353            self.write_zero_init_scalar(kind)?;
4354            // Close the zero value constructor
4355            write!(self.out, ")")?;
4356            // Close the parentheses surrounding our ternary
4357            write!(self.out, ")")?;
4358        }
4359
4360        Ok(())
4361    }
4362
4363    fn write_named_expr(
4364        &mut self,
4365        handle: Handle<crate::Expression>,
4366        name: String,
4367        // The expression which is being named.
4368        // Generally, this is the same as handle, except in WorkGroupUniformLoad
4369        named: Handle<crate::Expression>,
4370        ctx: &back::FunctionCtx,
4371    ) -> BackendResult {
4372        match ctx.info[named].ty {
4373            proc::TypeResolution::Handle(ty_handle) => match self.module.types[ty_handle].inner {
4374                TypeInner::Struct { .. } => {
4375                    let ty_name = &self.names[&NameKey::Type(ty_handle)];
4376                    write!(self.out, "{ty_name}")?;
4377                }
4378                _ => {
4379                    self.write_type(ty_handle)?;
4380                }
4381            },
4382            proc::TypeResolution::Value(ref inner) => {
4383                self.write_value_type(inner)?;
4384            }
4385        }
4386
4387        let resolved = ctx.resolve_type(named, &self.module.types);
4388
4389        write!(self.out, " {name}")?;
4390        if let TypeInner::Array { base, size, .. } = *resolved {
4391            self.write_array_size(base, size)?;
4392        }
4393        write!(self.out, " = ")?;
4394        self.write_expr(handle, ctx)?;
4395        writeln!(self.out, ";")?;
4396        self.named_expressions.insert(named, name);
4397
4398        Ok(())
4399    }
4400
4401    /// Helper function that write string with default zero initialization for supported types
4402    fn write_zero_init_value(&mut self, ty: Handle<crate::Type>) -> BackendResult {
4403        let inner = &self.module.types[ty].inner;
4404        match *inner {
4405            TypeInner::Scalar(scalar) | TypeInner::Atomic(scalar) => {
4406                self.write_zero_init_scalar(scalar.kind)?;
4407            }
4408            TypeInner::Vector { scalar, .. } => {
4409                self.write_value_type(inner)?;
4410                write!(self.out, "(")?;
4411                self.write_zero_init_scalar(scalar.kind)?;
4412                write!(self.out, ")")?;
4413            }
4414            TypeInner::Matrix { .. } => {
4415                self.write_value_type(inner)?;
4416                write!(self.out, "(")?;
4417                self.write_zero_init_scalar(crate::ScalarKind::Float)?;
4418                write!(self.out, ")")?;
4419            }
4420            TypeInner::Array { base, size, .. } => {
4421                let count = match size.resolve(self.module.to_ctx())? {
4422                    proc::IndexableLength::Known(count) => count,
4423                    proc::IndexableLength::Dynamic => return Ok(()),
4424                };
4425                self.write_type(base)?;
4426                self.write_array_size(base, size)?;
4427                write!(self.out, "(")?;
4428                for _ in 1..count {
4429                    self.write_zero_init_value(base)?;
4430                    write!(self.out, ", ")?;
4431                }
4432                // write last parameter without comma and space
4433                self.write_zero_init_value(base)?;
4434                write!(self.out, ")")?;
4435            }
4436            TypeInner::Struct { ref members, .. } => {
4437                let name = &self.names[&NameKey::Type(ty)];
4438                write!(self.out, "{name}(")?;
4439                for (index, member) in members.iter().enumerate() {
4440                    if index != 0 {
4441                        write!(self.out, ", ")?;
4442                    }
4443                    self.write_zero_init_value(member.ty)?;
4444                }
4445                write!(self.out, ")")?;
4446            }
4447            _ => unreachable!(),
4448        }
4449
4450        Ok(())
4451    }
4452
4453    /// Helper function that write string with zero initialization for scalar
4454    fn write_zero_init_scalar(&mut self, kind: crate::ScalarKind) -> BackendResult {
4455        match kind {
4456            crate::ScalarKind::Bool => write!(self.out, "false")?,
4457            crate::ScalarKind::Uint => write!(self.out, "0u")?,
4458            crate::ScalarKind::Float => write!(self.out, "0.0")?,
4459            crate::ScalarKind::Sint => write!(self.out, "0")?,
4460            crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {
4461                return Err(Error::Custom(
4462                    "Abstract types should not appear in IR presented to backends".to_string(),
4463                ))
4464            }
4465        }
4466
4467        Ok(())
4468    }
4469
4470    /// Issue a control barrier.
4471    fn write_control_barrier(
4472        &mut self,
4473        flags: crate::Barrier,
4474        level: back::Level,
4475    ) -> BackendResult {
4476        self.write_memory_barrier(flags, level)?;
4477        writeln!(self.out, "{level}barrier();")?;
4478        Ok(())
4479    }
4480
4481    /// Issue a memory barrier.
4482    fn write_memory_barrier(&mut self, flags: crate::Barrier, level: back::Level) -> BackendResult {
4483        if flags.contains(crate::Barrier::STORAGE) {
4484            writeln!(self.out, "{level}memoryBarrierBuffer();")?;
4485        }
4486        if flags.contains(crate::Barrier::WORK_GROUP) {
4487            writeln!(self.out, "{level}memoryBarrierShared();")?;
4488        }
4489        if flags.contains(crate::Barrier::SUB_GROUP) {
4490            writeln!(self.out, "{level}subgroupMemoryBarrier();")?;
4491        }
4492        if flags.contains(crate::Barrier::TEXTURE) {
4493            writeln!(self.out, "{level}memoryBarrierImage();")?;
4494        }
4495        Ok(())
4496    }
4497
4498    /// Helper function that return the glsl storage access string of [`StorageAccess`](crate::StorageAccess)
4499    ///
4500    /// glsl allows adding both `readonly` and `writeonly` but this means that
4501    /// they can only be used to query information about the resource which isn't what
4502    /// we want here so when storage access is both `LOAD` and `STORE` add no modifiers
4503    fn write_storage_access(&mut self, storage_access: crate::StorageAccess) -> BackendResult {
4504        if storage_access.contains(crate::StorageAccess::ATOMIC) {
4505            return Ok(());
4506        }
4507        if !storage_access.contains(crate::StorageAccess::STORE) {
4508            write!(self.out, "readonly ")?;
4509        }
4510        if !storage_access.contains(crate::StorageAccess::LOAD) {
4511            write!(self.out, "writeonly ")?;
4512        }
4513        Ok(())
4514    }
4515
4516    /// Helper method used to produce the reflection info that's returned to the user
4517    fn collect_reflection_info(&mut self) -> Result<ReflectionInfo, Error> {
4518        let info = self.info.get_entry_point(self.entry_point_idx as usize);
4519        let mut texture_mapping = crate::FastHashMap::default();
4520        let mut uniforms = crate::FastHashMap::default();
4521
4522        for sampling in info.sampling_set.iter() {
4523            let tex_name = self.reflection_names_globals[&sampling.image].clone();
4524
4525            match texture_mapping.entry(tex_name) {
4526                hash_map::Entry::Vacant(v) => {
4527                    v.insert(TextureMapping {
4528                        texture: sampling.image,
4529                        sampler: Some(sampling.sampler),
4530                    });
4531                }
4532                hash_map::Entry::Occupied(e) => {
4533                    if e.get().sampler != Some(sampling.sampler) {
4534                        log::error!("Conflicting samplers for {}", e.key());
4535                        return Err(Error::ImageMultipleSamplers);
4536                    }
4537                }
4538            }
4539        }
4540
4541        let mut immediates_info = None;
4542        for (handle, var) in self.module.global_variables.iter() {
4543            if info[handle].is_empty() {
4544                continue;
4545            }
4546            match self.module.types[var.ty].inner {
4547                TypeInner::Image { .. } => {
4548                    let tex_name = self.reflection_names_globals[&handle].clone();
4549                    match texture_mapping.entry(tex_name) {
4550                        hash_map::Entry::Vacant(v) => {
4551                            v.insert(TextureMapping {
4552                                texture: handle,
4553                                sampler: None,
4554                            });
4555                        }
4556                        hash_map::Entry::Occupied(_) => {
4557                            // already used with a sampler, do nothing
4558                        }
4559                    }
4560                }
4561                _ => match var.space {
4562                    crate::AddressSpace::Uniform | crate::AddressSpace::Storage { .. } => {
4563                        let name = self.reflection_names_globals[&handle].clone();
4564                        uniforms.insert(handle, name);
4565                    }
4566                    crate::AddressSpace::Immediate => {
4567                        let name = self.reflection_names_globals[&handle].clone();
4568                        immediates_info = Some((name, var.ty));
4569                    }
4570                    _ => (),
4571                },
4572            }
4573        }
4574
4575        let mut immediates_segments = Vec::new();
4576        let mut immediates_items = vec![];
4577
4578        if let Some((name, ty)) = immediates_info {
4579            // We don't have a layouter available to us, so we need to create one.
4580            //
4581            // This is potentially a bit wasteful, but the set of types in the program
4582            // shouldn't be too large.
4583            let mut layouter = proc::Layouter::default();
4584            layouter.update(self.module.to_ctx()).unwrap();
4585
4586            // We start with the name of the binding itself.
4587            immediates_segments.push(name);
4588
4589            // We then recursively collect all the uniform fields of the immediate data.
4590            self.collect_immediates_items(
4591                ty,
4592                &mut immediates_segments,
4593                &layouter,
4594                &mut 0,
4595                &mut immediates_items,
4596            );
4597        }
4598
4599        Ok(ReflectionInfo {
4600            texture_mapping,
4601            uniforms,
4602            varying: mem::take(&mut self.varying),
4603            immediates_items,
4604            clip_distance_count: self.clip_distance_count,
4605        })
4606    }
4607
4608    fn collect_immediates_items(
4609        &mut self,
4610        ty: Handle<crate::Type>,
4611        segments: &mut Vec<String>,
4612        layouter: &proc::Layouter,
4613        offset: &mut u32,
4614        items: &mut Vec<ImmediateItem>,
4615    ) {
4616        // At this point in the recursion, `segments` contains the path
4617        // needed to access `ty` from the root.
4618
4619        let layout = &layouter[ty];
4620        *offset = layout.alignment.round_up(*offset);
4621        match self.module.types[ty].inner {
4622            // All these types map directly to GL uniforms.
4623            TypeInner::Scalar { .. } | TypeInner::Vector { .. } | TypeInner::Matrix { .. } => {
4624                // Build the full name, by combining all current segments.
4625                let name: String = segments.iter().map(String::as_str).collect();
4626                items.push(ImmediateItem {
4627                    access_path: name,
4628                    offset: *offset,
4629                    ty: (&self.module.types[ty].inner).try_into().unwrap(),
4630                    size_bytes: layout.size,
4631                });
4632                *offset += layout.size;
4633            }
4634            // Arrays are recursed into.
4635            TypeInner::Array { base, size, .. } => {
4636                let crate::ArraySize::Constant(count) = size else {
4637                    unreachable!("Cannot have dynamic arrays in immediates");
4638                };
4639
4640                for i in 0..count.get() {
4641                    // Add the array accessor and recurse.
4642                    segments.push(format!("[{i}]"));
4643                    self.collect_immediates_items(base, segments, layouter, offset, items);
4644                    segments.pop();
4645                }
4646
4647                // Ensure the stride is kept by rounding up to the alignment.
4648                *offset = layout.alignment.round_up(*offset)
4649            }
4650            TypeInner::Struct { ref members, .. } => {
4651                for (index, member) in members.iter().enumerate() {
4652                    // Add struct accessor and recurse.
4653                    segments.push(format!(
4654                        ".{}",
4655                        self.names[&NameKey::StructMember(ty, index as u32)]
4656                    ));
4657                    self.collect_immediates_items(member.ty, segments, layouter, offset, items);
4658                    segments.pop();
4659                }
4660
4661                // Ensure ending padding is kept by rounding up to the alignment.
4662                *offset = layout.alignment.round_up(*offset)
4663            }
4664            _ => unreachable!(),
4665        }
4666    }
4667}