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