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