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}