naga/back/glsl/
mod.rs

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