Skip to main content

naga/back/glsl/
writer.rs

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