naga/front/glsl/
builtins.rs

1use alloc::{vec, vec::Vec};
2
3use super::{
4    ast::{
5        BuiltinVariations, FunctionDeclaration, FunctionKind, Overload, ParameterInfo,
6        ParameterQualifier,
7    },
8    context::Context,
9    Error, ErrorKind, Frontend, Result,
10};
11use crate::{
12    BinaryOperator, DerivativeAxis as Axis, DerivativeControl as Ctrl, Expression, Handle,
13    ImageClass, ImageDimension as Dim, ImageQuery, MathFunction, Module, RelationalFunction,
14    SampleLevel, Scalar, ScalarKind as Sk, Span, Type, TypeInner, UnaryOperator, VectorSize,
15};
16
17impl crate::ScalarKind {
18    const fn dummy_storage_format(&self) -> crate::StorageFormat {
19        match *self {
20            Sk::Sint => crate::StorageFormat::R16Sint,
21            Sk::Uint => crate::StorageFormat::R16Uint,
22            _ => crate::StorageFormat::R16Float,
23        }
24    }
25}
26
27impl Module {
28    /// Helper function, to create a function prototype for a builtin
29    fn add_builtin(&mut self, args: Vec<TypeInner>, builtin: MacroCall) -> Overload {
30        let mut parameters = Vec::with_capacity(args.len());
31        let mut parameters_info = Vec::with_capacity(args.len());
32
33        for arg in args {
34            parameters.push(self.types.insert(
35                Type {
36                    name: None,
37                    inner: arg,
38                },
39                Span::default(),
40            ));
41            parameters_info.push(ParameterInfo {
42                qualifier: ParameterQualifier::In,
43                depth: false,
44            });
45        }
46
47        Overload {
48            parameters,
49            parameters_info,
50            kind: FunctionKind::Macro(builtin),
51            defined: false,
52            internal: true,
53            void: false,
54        }
55    }
56}
57
58const fn make_coords_arg(number_of_components: usize, kind: Sk) -> TypeInner {
59    let scalar = Scalar { kind, width: 4 };
60
61    match number_of_components {
62        1 => TypeInner::Scalar(scalar),
63        _ => TypeInner::Vector {
64            size: match number_of_components {
65                2 => VectorSize::Bi,
66                3 => VectorSize::Tri,
67                _ => VectorSize::Quad,
68            },
69            scalar,
70        },
71    }
72}
73
74/// Inject builtins into the declaration
75///
76/// This is done to not add a large startup cost and not increase memory
77/// usage if it isn't needed.
78pub fn inject_builtin(
79    declaration: &mut FunctionDeclaration,
80    module: &mut Module,
81    name: &str,
82    mut variations: BuiltinVariations,
83) {
84    log::trace!(
85        "{} variations: {:?} {:?}",
86        name,
87        variations,
88        declaration.variations
89    );
90    // Don't regeneate variations
91    variations.remove(declaration.variations);
92    declaration.variations |= variations;
93
94    if variations.contains(BuiltinVariations::STANDARD) {
95        inject_standard_builtins(declaration, module, name)
96    }
97
98    if variations.contains(BuiltinVariations::DOUBLE) {
99        inject_double_builtin(declaration, module, name)
100    }
101
102    match name {
103        "texture"
104        | "textureGrad"
105        | "textureGradOffset"
106        | "textureLod"
107        | "textureLodOffset"
108        | "textureOffset"
109        | "textureProj"
110        | "textureProjGrad"
111        | "textureProjGradOffset"
112        | "textureProjLod"
113        | "textureProjLodOffset"
114        | "textureProjOffset" => {
115            let f = |kind, dim, arrayed, multi, shadow| {
116                for bits in 0..=0b11 {
117                    let variant = bits & 0b1 != 0;
118                    let bias = bits & 0b10 != 0;
119
120                    let (proj, offset, level_type) = match name {
121                        // texture(gsampler, gvec P, [float bias]);
122                        "texture" => (false, false, TextureLevelType::None),
123                        // textureGrad(gsampler, gvec P, gvec dPdx, gvec dPdy);
124                        "textureGrad" => (false, false, TextureLevelType::Grad),
125                        // textureGradOffset(gsampler, gvec P, gvec dPdx, gvec dPdy, ivec offset);
126                        "textureGradOffset" => (false, true, TextureLevelType::Grad),
127                        // textureLod(gsampler, gvec P, float lod);
128                        "textureLod" => (false, false, TextureLevelType::Lod),
129                        // textureLodOffset(gsampler, gvec P, float lod, ivec offset);
130                        "textureLodOffset" => (false, true, TextureLevelType::Lod),
131                        // textureOffset(gsampler, gvec+1 P, ivec offset, [float bias]);
132                        "textureOffset" => (false, true, TextureLevelType::None),
133                        // textureProj(gsampler, gvec+1 P, [float bias]);
134                        "textureProj" => (true, false, TextureLevelType::None),
135                        // textureProjGrad(gsampler, gvec+1 P, gvec dPdx, gvec dPdy);
136                        "textureProjGrad" => (true, false, TextureLevelType::Grad),
137                        // textureProjGradOffset(gsampler, gvec+1 P, gvec dPdx, gvec dPdy, ivec offset);
138                        "textureProjGradOffset" => (true, true, TextureLevelType::Grad),
139                        // textureProjLod(gsampler, gvec+1 P, float lod);
140                        "textureProjLod" => (true, false, TextureLevelType::Lod),
141                        // textureProjLodOffset(gsampler, gvec+1 P, gvec dPdx, gvec dPdy, ivec offset);
142                        "textureProjLodOffset" => (true, true, TextureLevelType::Lod),
143                        // textureProjOffset(gsampler, gvec+1 P, ivec offset, [float bias]);
144                        "textureProjOffset" => (true, true, TextureLevelType::None),
145                        _ => unreachable!(),
146                    };
147
148                    let builtin = MacroCall::Texture {
149                        proj,
150                        offset,
151                        shadow,
152                        level_type,
153                    };
154
155                    // Parse out the variant settings.
156                    let grad = level_type == TextureLevelType::Grad;
157                    let lod = level_type == TextureLevelType::Lod;
158
159                    let supports_variant = proj && !shadow;
160                    if variant && !supports_variant {
161                        continue;
162                    }
163
164                    if bias && !matches!(level_type, TextureLevelType::None) {
165                        continue;
166                    }
167
168                    // Proj doesn't work with arrayed or Cube
169                    if proj && (arrayed || dim == Dim::Cube) {
170                        continue;
171                    }
172
173                    // texture operations with offset are not supported for cube maps
174                    if dim == Dim::Cube && offset {
175                        continue;
176                    }
177
178                    // sampler2DArrayShadow can't be used in textureLod or in texture with bias
179                    if (lod || bias) && arrayed && shadow && dim == Dim::D2 {
180                        continue;
181                    }
182
183                    // TODO: glsl supports using bias with depth samplers but naga doesn't
184                    if bias && shadow {
185                        continue;
186                    }
187
188                    let class = match shadow {
189                        true => ImageClass::Depth { multi },
190                        false => ImageClass::Sampled { kind, multi },
191                    };
192
193                    let image = TypeInner::Image {
194                        dim,
195                        arrayed,
196                        class,
197                    };
198
199                    let num_coords_from_dim = image_dims_to_coords_size(dim).min(3);
200                    let mut num_coords = num_coords_from_dim;
201
202                    if shadow && proj {
203                        num_coords = 4;
204                    } else if dim == Dim::D1 && shadow {
205                        num_coords = 3;
206                    } else if shadow {
207                        num_coords += 1;
208                    } else if proj {
209                        if variant && num_coords == 4 {
210                            // Normal form already has 4 components, no need to have a variant form.
211                            continue;
212                        } else if variant {
213                            num_coords = 4;
214                        } else {
215                            num_coords += 1;
216                        }
217                    }
218
219                    if !(dim == Dim::D1 && shadow) {
220                        num_coords += arrayed as usize;
221                    }
222
223                    // Special case: texture(gsamplerCubeArrayShadow) kicks the shadow compare ref to a separate argument,
224                    // since it would otherwise take five arguments. It also can't take a bias, nor can it be proj/grad/lod/offset
225                    // (presumably because nobody asked for it, and implementation complexity?)
226                    if num_coords >= 5 {
227                        if lod || grad || offset || proj || bias {
228                            continue;
229                        }
230                        debug_assert!(dim == Dim::Cube && shadow && arrayed);
231                    }
232                    debug_assert!(num_coords <= 5);
233
234                    let vector = make_coords_arg(num_coords, Sk::Float);
235                    let mut args = vec![image, vector];
236
237                    if num_coords == 5 {
238                        args.push(TypeInner::Scalar(Scalar::F32));
239                    }
240
241                    match level_type {
242                        TextureLevelType::Lod => {
243                            args.push(TypeInner::Scalar(Scalar::F32));
244                        }
245                        TextureLevelType::Grad => {
246                            args.push(make_coords_arg(num_coords_from_dim, Sk::Float));
247                            args.push(make_coords_arg(num_coords_from_dim, Sk::Float));
248                        }
249                        TextureLevelType::None => {}
250                    };
251
252                    if offset {
253                        args.push(make_coords_arg(num_coords_from_dim, Sk::Sint));
254                    }
255
256                    if bias {
257                        args.push(TypeInner::Scalar(Scalar::F32));
258                    }
259
260                    declaration
261                        .overloads
262                        .push(module.add_builtin(args, builtin));
263                }
264            };
265
266            texture_args_generator(TextureArgsOptions::SHADOW | variations.into(), f)
267        }
268        "textureSize" => {
269            let f = |kind, dim, arrayed, multi, shadow| {
270                let class = match shadow {
271                    true => ImageClass::Depth { multi },
272                    false => ImageClass::Sampled { kind, multi },
273                };
274
275                let image = TypeInner::Image {
276                    dim,
277                    arrayed,
278                    class,
279                };
280
281                let mut args = vec![image];
282
283                if !multi {
284                    args.push(TypeInner::Scalar(Scalar::I32))
285                }
286
287                declaration
288                    .overloads
289                    .push(module.add_builtin(args, MacroCall::TextureSize { arrayed }))
290            };
291
292            texture_args_generator(
293                TextureArgsOptions::SHADOW | TextureArgsOptions::MULTI | variations.into(),
294                f,
295            )
296        }
297        "textureQueryLevels" => {
298            let f = |kind, dim, arrayed, multi, shadow| {
299                let class = match shadow {
300                    true => ImageClass::Depth { multi },
301                    false => ImageClass::Sampled { kind, multi },
302                };
303
304                let image = TypeInner::Image {
305                    dim,
306                    arrayed,
307                    class,
308                };
309
310                declaration
311                    .overloads
312                    .push(module.add_builtin(vec![image], MacroCall::TextureQueryLevels))
313            };
314
315            texture_args_generator(TextureArgsOptions::SHADOW | variations.into(), f)
316        }
317        "texelFetch" | "texelFetchOffset" => {
318            let offset = "texelFetchOffset" == name;
319            let f = |kind, dim, arrayed, multi, _shadow| {
320                // Cube images aren't supported
321                if let Dim::Cube = dim {
322                    return;
323                }
324
325                let image = TypeInner::Image {
326                    dim,
327                    arrayed,
328                    class: ImageClass::Sampled { kind, multi },
329                };
330
331                let dim_value = image_dims_to_coords_size(dim);
332                let coordinates = make_coords_arg(dim_value + arrayed as usize, Sk::Sint);
333
334                let mut args = vec![image, coordinates, TypeInner::Scalar(Scalar::I32)];
335
336                if offset {
337                    args.push(make_coords_arg(dim_value, Sk::Sint));
338                }
339
340                declaration
341                    .overloads
342                    .push(module.add_builtin(args, MacroCall::ImageLoad { multi }))
343            };
344
345            // Don't generate shadow images since they aren't supported
346            texture_args_generator(TextureArgsOptions::MULTI | variations.into(), f)
347        }
348        "imageSize" => {
349            let f = |kind: Sk, dim, arrayed, _, _| {
350                // Naga doesn't support cube images and it's usefulness
351                // is questionable, so they won't be supported for now
352                if dim == Dim::Cube {
353                    return;
354                }
355
356                let image = TypeInner::Image {
357                    dim,
358                    arrayed,
359                    class: ImageClass::Storage {
360                        format: kind.dummy_storage_format(),
361                        access: crate::StorageAccess::empty(),
362                    },
363                };
364
365                declaration
366                    .overloads
367                    .push(module.add_builtin(vec![image], MacroCall::TextureSize { arrayed }))
368            };
369
370            texture_args_generator(variations.into(), f)
371        }
372        "imageLoad" => {
373            let f = |kind: Sk, dim, arrayed, _, _| {
374                // Naga doesn't support cube images and it's usefulness
375                // is questionable, so they won't be supported for now
376                if dim == Dim::Cube {
377                    return;
378                }
379
380                let image = TypeInner::Image {
381                    dim,
382                    arrayed,
383                    class: ImageClass::Storage {
384                        format: kind.dummy_storage_format(),
385                        access: crate::StorageAccess::LOAD,
386                    },
387                };
388
389                let dim_value = image_dims_to_coords_size(dim);
390                let mut coord_size = dim_value + arrayed as usize;
391                // > Every OpenGL API call that operates on cubemap array
392                // > textures takes layer-faces, not array layers
393                //
394                // So this means that imageCubeArray only takes a three component
395                // vector coordinate and the third component is a layer index.
396                if Dim::Cube == dim && arrayed {
397                    coord_size = 3
398                }
399                let coordinates = make_coords_arg(coord_size, Sk::Sint);
400
401                let args = vec![image, coordinates];
402
403                declaration
404                    .overloads
405                    .push(module.add_builtin(args, MacroCall::ImageLoad { multi: false }))
406            };
407
408            // Don't generate shadow nor multisampled images since they aren't supported
409            texture_args_generator(variations.into(), f)
410        }
411        "imageStore" => {
412            let f = |kind: Sk, dim, arrayed, _, _| {
413                // Naga doesn't support cube images and it's usefulness
414                // is questionable, so they won't be supported for now
415                if dim == Dim::Cube {
416                    return;
417                }
418
419                let image = TypeInner::Image {
420                    dim,
421                    arrayed,
422                    class: ImageClass::Storage {
423                        format: kind.dummy_storage_format(),
424                        access: crate::StorageAccess::STORE,
425                    },
426                };
427
428                let dim_value = image_dims_to_coords_size(dim);
429                let mut coord_size = dim_value + arrayed as usize;
430                // > Every OpenGL API call that operates on cubemap array
431                // > textures takes layer-faces, not array layers
432                //
433                // So this means that imageCubeArray only takes a three component
434                // vector coordinate and the third component is a layer index.
435                if Dim::Cube == dim && arrayed {
436                    coord_size = 3
437                }
438                let coordinates = make_coords_arg(coord_size, Sk::Sint);
439
440                let args = vec![
441                    image,
442                    coordinates,
443                    TypeInner::Vector {
444                        size: VectorSize::Quad,
445                        scalar: Scalar { kind, width: 4 },
446                    },
447                ];
448
449                let mut overload = module.add_builtin(args, MacroCall::ImageStore);
450                overload.void = true;
451                declaration.overloads.push(overload)
452            };
453
454            // Don't generate shadow nor multisampled images since they aren't supported
455            texture_args_generator(variations.into(), f)
456        }
457        _ => {}
458    }
459}
460
461/// Injects the builtins into declaration that don't need any special variations
462fn inject_standard_builtins(
463    declaration: &mut FunctionDeclaration,
464    module: &mut Module,
465    name: &str,
466) {
467    // Some samplers (sampler1D, etc...) can be float, int, or uint
468    let anykind_sampler = if name.starts_with("sampler") {
469        Some((name, Sk::Float))
470    } else if name.starts_with("usampler") {
471        Some((&name[1..], Sk::Uint))
472    } else if name.starts_with("isampler") {
473        Some((&name[1..], Sk::Sint))
474    } else {
475        None
476    };
477    if let Some((sampler, kind)) = anykind_sampler {
478        match sampler {
479            "sampler1D" | "sampler1DArray" | "sampler2D" | "sampler2DArray" | "sampler2DMS"
480            | "sampler2DMSArray" | "sampler3D" | "samplerCube" | "samplerCubeArray" => {
481                declaration.overloads.push(module.add_builtin(
482                    vec![
483                        TypeInner::Image {
484                            dim: match sampler {
485                                "sampler1D" | "sampler1DArray" => Dim::D1,
486                                "sampler2D" | "sampler2DArray" | "sampler2DMS"
487                                | "sampler2DMSArray" => Dim::D2,
488                                "sampler3D" => Dim::D3,
489                                _ => Dim::Cube,
490                            },
491                            arrayed: matches!(
492                                sampler,
493                                "sampler1DArray"
494                                    | "sampler2DArray"
495                                    | "sampler2DMSArray"
496                                    | "samplerCubeArray"
497                            ),
498                            class: ImageClass::Sampled {
499                                kind,
500                                multi: matches!(sampler, "sampler2DMS" | "sampler2DMSArray"),
501                            },
502                        },
503                        TypeInner::Sampler { comparison: false },
504                    ],
505                    MacroCall::Sampler,
506                ));
507                return;
508            }
509            _ => (),
510        }
511    }
512
513    match name {
514        // Shadow sampler can only be of kind `Sk::Float`
515        "sampler1DShadow"
516        | "sampler1DArrayShadow"
517        | "sampler2DShadow"
518        | "sampler2DArrayShadow"
519        | "samplerCubeShadow"
520        | "samplerCubeArrayShadow" => {
521            let dim = match name {
522                "sampler1DShadow" | "sampler1DArrayShadow" => Dim::D1,
523                "sampler2DShadow" | "sampler2DArrayShadow" => Dim::D2,
524                _ => Dim::Cube,
525            };
526            let arrayed = matches!(
527                name,
528                "sampler1DArrayShadow" | "sampler2DArrayShadow" | "samplerCubeArrayShadow"
529            );
530
531            for i in 0..2 {
532                let ty = TypeInner::Image {
533                    dim,
534                    arrayed,
535                    class: match i {
536                        0 => ImageClass::Sampled {
537                            kind: Sk::Float,
538                            multi: false,
539                        },
540                        _ => ImageClass::Depth { multi: false },
541                    },
542                };
543
544                declaration.overloads.push(module.add_builtin(
545                    vec![ty, TypeInner::Sampler { comparison: true }],
546                    MacroCall::SamplerShadow,
547                ))
548            }
549        }
550        "sin" | "exp" | "exp2" | "sinh" | "cos" | "cosh" | "tan" | "tanh" | "acos" | "asin"
551        | "log" | "log2" | "radians" | "degrees" | "asinh" | "acosh" | "atanh"
552        | "floatBitsToInt" | "floatBitsToUint" | "dFdx" | "dFdxFine" | "dFdxCoarse" | "dFdy"
553        | "dFdyFine" | "dFdyCoarse" | "fwidth" | "fwidthFine" | "fwidthCoarse" => {
554            // bits layout
555            // bit 0 through 1 - dims
556            for bits in 0..0b100 {
557                let size = match bits {
558                    0b00 => None,
559                    0b01 => Some(VectorSize::Bi),
560                    0b10 => Some(VectorSize::Tri),
561                    _ => Some(VectorSize::Quad),
562                };
563                let scalar = Scalar::F32;
564
565                declaration.overloads.push(module.add_builtin(
566                    vec![match size {
567                        Some(size) => TypeInner::Vector { size, scalar },
568                        None => TypeInner::Scalar(scalar),
569                    }],
570                    match name {
571                        "sin" => MacroCall::MathFunction(MathFunction::Sin),
572                        "exp" => MacroCall::MathFunction(MathFunction::Exp),
573                        "exp2" => MacroCall::MathFunction(MathFunction::Exp2),
574                        "sinh" => MacroCall::MathFunction(MathFunction::Sinh),
575                        "cos" => MacroCall::MathFunction(MathFunction::Cos),
576                        "cosh" => MacroCall::MathFunction(MathFunction::Cosh),
577                        "tan" => MacroCall::MathFunction(MathFunction::Tan),
578                        "tanh" => MacroCall::MathFunction(MathFunction::Tanh),
579                        "acos" => MacroCall::MathFunction(MathFunction::Acos),
580                        "asin" => MacroCall::MathFunction(MathFunction::Asin),
581                        "log" => MacroCall::MathFunction(MathFunction::Log),
582                        "log2" => MacroCall::MathFunction(MathFunction::Log2),
583                        "asinh" => MacroCall::MathFunction(MathFunction::Asinh),
584                        "acosh" => MacroCall::MathFunction(MathFunction::Acosh),
585                        "atanh" => MacroCall::MathFunction(MathFunction::Atanh),
586                        "radians" => MacroCall::MathFunction(MathFunction::Radians),
587                        "degrees" => MacroCall::MathFunction(MathFunction::Degrees),
588                        "floatBitsToInt" => MacroCall::BitCast(Sk::Sint),
589                        "floatBitsToUint" => MacroCall::BitCast(Sk::Uint),
590                        "dFdxCoarse" => MacroCall::Derivate(Axis::X, Ctrl::Coarse),
591                        "dFdyCoarse" => MacroCall::Derivate(Axis::Y, Ctrl::Coarse),
592                        "fwidthCoarse" => MacroCall::Derivate(Axis::Width, Ctrl::Coarse),
593                        "dFdxFine" => MacroCall::Derivate(Axis::X, Ctrl::Fine),
594                        "dFdyFine" => MacroCall::Derivate(Axis::Y, Ctrl::Fine),
595                        "fwidthFine" => MacroCall::Derivate(Axis::Width, Ctrl::Fine),
596                        "dFdx" => MacroCall::Derivate(Axis::X, Ctrl::None),
597                        "dFdy" => MacroCall::Derivate(Axis::Y, Ctrl::None),
598                        "fwidth" => MacroCall::Derivate(Axis::Width, Ctrl::None),
599                        _ => unreachable!(),
600                    },
601                ))
602            }
603        }
604        "intBitsToFloat" | "uintBitsToFloat" => {
605            // bits layout
606            // bit 0 through 1 - dims
607            for bits in 0..0b100 {
608                let size = match bits {
609                    0b00 => None,
610                    0b01 => Some(VectorSize::Bi),
611                    0b10 => Some(VectorSize::Tri),
612                    _ => Some(VectorSize::Quad),
613                };
614                let scalar = match name {
615                    "intBitsToFloat" => Scalar::I32,
616                    _ => Scalar::U32,
617                };
618
619                declaration.overloads.push(module.add_builtin(
620                    vec![match size {
621                        Some(size) => TypeInner::Vector { size, scalar },
622                        None => TypeInner::Scalar(scalar),
623                    }],
624                    MacroCall::BitCast(Sk::Float),
625                ))
626            }
627        }
628        "pow" => {
629            // bits layout
630            // bit 0 through 1 - dims
631            for bits in 0..0b100 {
632                let size = match bits {
633                    0b00 => None,
634                    0b01 => Some(VectorSize::Bi),
635                    0b10 => Some(VectorSize::Tri),
636                    _ => Some(VectorSize::Quad),
637                };
638                let scalar = Scalar::F32;
639                let ty = || match size {
640                    Some(size) => TypeInner::Vector { size, scalar },
641                    None => TypeInner::Scalar(scalar),
642                };
643
644                declaration.overloads.push(
645                    module
646                        .add_builtin(vec![ty(), ty()], MacroCall::MathFunction(MathFunction::Pow)),
647                )
648            }
649        }
650        "abs" | "sign" => {
651            // bits layout
652            // bit 0 through 1 - dims
653            // bit 2 - float/sint
654            for bits in 0..0b1000 {
655                let size = match bits & 0b11 {
656                    0b00 => None,
657                    0b01 => Some(VectorSize::Bi),
658                    0b10 => Some(VectorSize::Tri),
659                    _ => Some(VectorSize::Quad),
660                };
661                let scalar = match bits >> 2 {
662                    0b0 => Scalar::F32,
663                    _ => Scalar::I32,
664                };
665
666                let args = vec![match size {
667                    Some(size) => TypeInner::Vector { size, scalar },
668                    None => TypeInner::Scalar(scalar),
669                }];
670
671                declaration.overloads.push(module.add_builtin(
672                    args,
673                    MacroCall::MathFunction(match name {
674                        "abs" => MathFunction::Abs,
675                        "sign" => MathFunction::Sign,
676                        _ => unreachable!(),
677                    }),
678                ))
679            }
680        }
681        "bitCount" | "bitfieldReverse" | "bitfieldExtract" | "bitfieldInsert" | "findLSB"
682        | "findMSB" => {
683            let fun = match name {
684                "bitCount" => MathFunction::CountOneBits,
685                "bitfieldReverse" => MathFunction::ReverseBits,
686                "bitfieldExtract" => MathFunction::ExtractBits,
687                "bitfieldInsert" => MathFunction::InsertBits,
688                "findLSB" => MathFunction::FirstTrailingBit,
689                "findMSB" => MathFunction::FirstLeadingBit,
690                _ => unreachable!(),
691            };
692
693            let mc = match fun {
694                MathFunction::ExtractBits => MacroCall::BitfieldExtract,
695                MathFunction::InsertBits => MacroCall::BitfieldInsert,
696                _ => MacroCall::MathFunction(fun),
697            };
698
699            // bits layout
700            // bit 0 - int/uint
701            // bit 1 through 2 - dims
702            for bits in 0..0b1000 {
703                let scalar = match bits & 0b1 {
704                    0b0 => Scalar::I32,
705                    _ => Scalar::U32,
706                };
707                let size = match bits >> 1 {
708                    0b00 => None,
709                    0b01 => Some(VectorSize::Bi),
710                    0b10 => Some(VectorSize::Tri),
711                    _ => Some(VectorSize::Quad),
712                };
713
714                let ty = || match size {
715                    Some(size) => TypeInner::Vector { size, scalar },
716                    None => TypeInner::Scalar(scalar),
717                };
718
719                let mut args = vec![ty()];
720
721                match fun {
722                    MathFunction::ExtractBits => {
723                        args.push(TypeInner::Scalar(Scalar::I32));
724                        args.push(TypeInner::Scalar(Scalar::I32));
725                    }
726                    MathFunction::InsertBits => {
727                        args.push(ty());
728                        args.push(TypeInner::Scalar(Scalar::I32));
729                        args.push(TypeInner::Scalar(Scalar::I32));
730                    }
731                    _ => {}
732                }
733
734                // we need to cast the return type of findLsb / findMsb
735                let mc = if scalar.kind == Sk::Uint {
736                    match mc {
737                        MacroCall::MathFunction(MathFunction::FirstTrailingBit) => {
738                            MacroCall::FindLsbUint
739                        }
740                        MacroCall::MathFunction(MathFunction::FirstLeadingBit) => {
741                            MacroCall::FindMsbUint
742                        }
743                        mc => mc,
744                    }
745                } else {
746                    mc
747                };
748
749                declaration.overloads.push(module.add_builtin(args, mc))
750            }
751        }
752        "packSnorm4x8" | "packUnorm4x8" | "packSnorm2x16" | "packUnorm2x16" | "packHalf2x16" => {
753            let fun = match name {
754                "packSnorm4x8" => MathFunction::Pack4x8snorm,
755                "packUnorm4x8" => MathFunction::Pack4x8unorm,
756                "packSnorm2x16" => MathFunction::Pack2x16unorm,
757                "packUnorm2x16" => MathFunction::Pack2x16snorm,
758                "packHalf2x16" => MathFunction::Pack2x16float,
759                _ => unreachable!(),
760            };
761
762            let ty = match fun {
763                MathFunction::Pack4x8snorm | MathFunction::Pack4x8unorm => TypeInner::Vector {
764                    size: VectorSize::Quad,
765                    scalar: Scalar::F32,
766                },
767                MathFunction::Pack2x16unorm
768                | MathFunction::Pack2x16snorm
769                | MathFunction::Pack2x16float => TypeInner::Vector {
770                    size: VectorSize::Bi,
771                    scalar: Scalar::F32,
772                },
773                _ => unreachable!(),
774            };
775
776            let args = vec![ty];
777
778            declaration
779                .overloads
780                .push(module.add_builtin(args, MacroCall::MathFunction(fun)));
781        }
782        "unpackSnorm4x8" | "unpackUnorm4x8" | "unpackSnorm2x16" | "unpackUnorm2x16"
783        | "unpackHalf2x16" => {
784            let fun = match name {
785                "unpackSnorm4x8" => MathFunction::Unpack4x8snorm,
786                "unpackUnorm4x8" => MathFunction::Unpack4x8unorm,
787                "unpackSnorm2x16" => MathFunction::Unpack2x16snorm,
788                "unpackUnorm2x16" => MathFunction::Unpack2x16unorm,
789                "unpackHalf2x16" => MathFunction::Unpack2x16float,
790                _ => unreachable!(),
791            };
792
793            let args = vec![TypeInner::Scalar(Scalar::U32)];
794
795            declaration
796                .overloads
797                .push(module.add_builtin(args, MacroCall::MathFunction(fun)));
798        }
799        "atan" => {
800            // bits layout
801            // bit 0 - atan/atan2
802            // bit 1 through 2 - dims
803            for bits in 0..0b1000 {
804                let fun = match bits & 0b1 {
805                    0b0 => MathFunction::Atan,
806                    _ => MathFunction::Atan2,
807                };
808                let size = match bits >> 1 {
809                    0b00 => None,
810                    0b01 => Some(VectorSize::Bi),
811                    0b10 => Some(VectorSize::Tri),
812                    _ => Some(VectorSize::Quad),
813                };
814                let scalar = Scalar::F32;
815                let ty = || match size {
816                    Some(size) => TypeInner::Vector { size, scalar },
817                    None => TypeInner::Scalar(scalar),
818                };
819
820                let mut args = vec![ty()];
821
822                if fun == MathFunction::Atan2 {
823                    args.push(ty())
824                }
825
826                declaration
827                    .overloads
828                    .push(module.add_builtin(args, MacroCall::MathFunction(fun)))
829            }
830        }
831        "all" | "any" | "not" => {
832            // bits layout
833            // bit 0 through 1 - dims
834            for bits in 0..0b11 {
835                let size = match bits {
836                    0b00 => VectorSize::Bi,
837                    0b01 => VectorSize::Tri,
838                    _ => VectorSize::Quad,
839                };
840
841                let args = vec![TypeInner::Vector {
842                    size,
843                    scalar: Scalar::BOOL,
844                }];
845
846                let fun = match name {
847                    "all" => MacroCall::Relational(RelationalFunction::All),
848                    "any" => MacroCall::Relational(RelationalFunction::Any),
849                    "not" => MacroCall::Unary(UnaryOperator::LogicalNot),
850                    _ => unreachable!(),
851                };
852
853                declaration.overloads.push(module.add_builtin(args, fun))
854            }
855        }
856        "lessThan" | "greaterThan" | "lessThanEqual" | "greaterThanEqual" => {
857            for bits in 0..0b1001 {
858                let (size, scalar) = match bits {
859                    0b0000 => (VectorSize::Bi, Scalar::F32),
860                    0b0001 => (VectorSize::Tri, Scalar::F32),
861                    0b0010 => (VectorSize::Quad, Scalar::F32),
862                    0b0011 => (VectorSize::Bi, Scalar::I32),
863                    0b0100 => (VectorSize::Tri, Scalar::I32),
864                    0b0101 => (VectorSize::Quad, Scalar::I32),
865                    0b0110 => (VectorSize::Bi, Scalar::U32),
866                    0b0111 => (VectorSize::Tri, Scalar::U32),
867                    _ => (VectorSize::Quad, Scalar::U32),
868                };
869
870                let ty = || TypeInner::Vector { size, scalar };
871                let args = vec![ty(), ty()];
872
873                let fun = MacroCall::Binary(match name {
874                    "lessThan" => BinaryOperator::Less,
875                    "greaterThan" => BinaryOperator::Greater,
876                    "lessThanEqual" => BinaryOperator::LessEqual,
877                    "greaterThanEqual" => BinaryOperator::GreaterEqual,
878                    _ => unreachable!(),
879                });
880
881                declaration.overloads.push(module.add_builtin(args, fun))
882            }
883        }
884        "equal" | "notEqual" => {
885            for bits in 0..0b1100 {
886                let (size, scalar) = match bits {
887                    0b0000 => (VectorSize::Bi, Scalar::F32),
888                    0b0001 => (VectorSize::Tri, Scalar::F32),
889                    0b0010 => (VectorSize::Quad, Scalar::F32),
890                    0b0011 => (VectorSize::Bi, Scalar::I32),
891                    0b0100 => (VectorSize::Tri, Scalar::I32),
892                    0b0101 => (VectorSize::Quad, Scalar::I32),
893                    0b0110 => (VectorSize::Bi, Scalar::U32),
894                    0b0111 => (VectorSize::Tri, Scalar::U32),
895                    0b1000 => (VectorSize::Quad, Scalar::U32),
896                    0b1001 => (VectorSize::Bi, Scalar::BOOL),
897                    0b1010 => (VectorSize::Tri, Scalar::BOOL),
898                    _ => (VectorSize::Quad, Scalar::BOOL),
899                };
900
901                let ty = || TypeInner::Vector { size, scalar };
902                let args = vec![ty(), ty()];
903
904                let fun = MacroCall::Binary(match name {
905                    "equal" => BinaryOperator::Equal,
906                    "notEqual" => BinaryOperator::NotEqual,
907                    _ => unreachable!(),
908                });
909
910                declaration.overloads.push(module.add_builtin(args, fun))
911            }
912        }
913        "min" | "max" => {
914            // bits layout
915            // bit 0 through 1 - scalar kind
916            // bit 2 through 4 - dims
917            for bits in 0..0b11100 {
918                let scalar = match bits & 0b11 {
919                    0b00 => Scalar::F32,
920                    0b01 => Scalar::I32,
921                    0b10 => Scalar::U32,
922                    _ => continue,
923                };
924                let (size, second_size) = match bits >> 2 {
925                    0b000 => (None, None),
926                    0b001 => (Some(VectorSize::Bi), None),
927                    0b010 => (Some(VectorSize::Tri), None),
928                    0b011 => (Some(VectorSize::Quad), None),
929                    0b100 => (Some(VectorSize::Bi), Some(VectorSize::Bi)),
930                    0b101 => (Some(VectorSize::Tri), Some(VectorSize::Tri)),
931                    _ => (Some(VectorSize::Quad), Some(VectorSize::Quad)),
932                };
933
934                let args = vec![
935                    match size {
936                        Some(size) => TypeInner::Vector { size, scalar },
937                        None => TypeInner::Scalar(scalar),
938                    },
939                    match second_size {
940                        Some(size) => TypeInner::Vector { size, scalar },
941                        None => TypeInner::Scalar(scalar),
942                    },
943                ];
944
945                let fun = match name {
946                    "max" => MacroCall::Splatted(MathFunction::Max, size, 1),
947                    "min" => MacroCall::Splatted(MathFunction::Min, size, 1),
948                    _ => unreachable!(),
949                };
950
951                declaration.overloads.push(module.add_builtin(args, fun))
952            }
953        }
954        "mix" => {
955            // bits layout
956            // bit 0 through 1 - dims
957            // bit 2 through 4 - types
958            //
959            // 0b10011 is the last element since splatted single elements
960            // were already added
961            for bits in 0..0b10011 {
962                let size = match bits & 0b11 {
963                    0b00 => Some(VectorSize::Bi),
964                    0b01 => Some(VectorSize::Tri),
965                    0b10 => Some(VectorSize::Quad),
966                    _ => None,
967                };
968                let (scalar, splatted, boolean) = match bits >> 2 {
969                    0b000 => (Scalar::I32, false, true),
970                    0b001 => (Scalar::U32, false, true),
971                    0b010 => (Scalar::F32, false, true),
972                    0b011 => (Scalar::F32, false, false),
973                    _ => (Scalar::F32, true, false),
974                };
975
976                let ty = |scalar| match size {
977                    Some(size) => TypeInner::Vector { size, scalar },
978                    None => TypeInner::Scalar(scalar),
979                };
980                let args = vec![
981                    ty(scalar),
982                    ty(scalar),
983                    match (boolean, splatted) {
984                        (true, _) => ty(Scalar::BOOL),
985                        (_, false) => TypeInner::Scalar(scalar),
986                        _ => ty(scalar),
987                    },
988                ];
989
990                declaration.overloads.push(module.add_builtin(
991                    args,
992                    match boolean {
993                        true => MacroCall::MixBoolean,
994                        false => MacroCall::Splatted(MathFunction::Mix, size, 2),
995                    },
996                ))
997            }
998        }
999        "clamp" => {
1000            // bits layout
1001            // bit 0 through 1 - float/int/uint
1002            // bit 2 through 3 - dims
1003            // bit 4 - splatted
1004            //
1005            // 0b11010 is the last element since splatted single elements
1006            // were already added
1007            for bits in 0..0b11011 {
1008                let scalar = match bits & 0b11 {
1009                    0b00 => Scalar::F32,
1010                    0b01 => Scalar::I32,
1011                    0b10 => Scalar::U32,
1012                    _ => continue,
1013                };
1014                let size = match (bits >> 2) & 0b11 {
1015                    0b00 => Some(VectorSize::Bi),
1016                    0b01 => Some(VectorSize::Tri),
1017                    0b10 => Some(VectorSize::Quad),
1018                    _ => None,
1019                };
1020                let splatted = bits & 0b10000 == 0b10000;
1021
1022                let base_ty = || match size {
1023                    Some(size) => TypeInner::Vector { size, scalar },
1024                    None => TypeInner::Scalar(scalar),
1025                };
1026                let limit_ty = || match splatted {
1027                    true => TypeInner::Scalar(scalar),
1028                    false => base_ty(),
1029                };
1030
1031                let args = vec![base_ty(), limit_ty(), limit_ty()];
1032
1033                declaration
1034                    .overloads
1035                    .push(module.add_builtin(args, MacroCall::Clamp(size)))
1036            }
1037        }
1038        "barrier" => declaration
1039            .overloads
1040            .push(module.add_builtin(Vec::new(), MacroCall::Barrier)),
1041        // Add common builtins with floats
1042        _ => inject_common_builtin(declaration, module, name, 4),
1043    }
1044}
1045
1046/// Injects the builtins into declaration that need doubles
1047fn inject_double_builtin(declaration: &mut FunctionDeclaration, module: &mut Module, name: &str) {
1048    match name {
1049        "abs" | "sign" => {
1050            // bits layout
1051            // bit 0 through 1 - dims
1052            for bits in 0..0b100 {
1053                let size = match bits {
1054                    0b00 => None,
1055                    0b01 => Some(VectorSize::Bi),
1056                    0b10 => Some(VectorSize::Tri),
1057                    _ => Some(VectorSize::Quad),
1058                };
1059                let scalar = Scalar::F64;
1060
1061                let args = vec![match size {
1062                    Some(size) => TypeInner::Vector { size, scalar },
1063                    None => TypeInner::Scalar(scalar),
1064                }];
1065
1066                declaration.overloads.push(module.add_builtin(
1067                    args,
1068                    MacroCall::MathFunction(match name {
1069                        "abs" => MathFunction::Abs,
1070                        "sign" => MathFunction::Sign,
1071                        _ => unreachable!(),
1072                    }),
1073                ))
1074            }
1075        }
1076        "min" | "max" => {
1077            // bits layout
1078            // bit 0 through 2 - dims
1079            for bits in 0..0b111 {
1080                let (size, second_size) = match bits {
1081                    0b000 => (None, None),
1082                    0b001 => (Some(VectorSize::Bi), None),
1083                    0b010 => (Some(VectorSize::Tri), None),
1084                    0b011 => (Some(VectorSize::Quad), None),
1085                    0b100 => (Some(VectorSize::Bi), Some(VectorSize::Bi)),
1086                    0b101 => (Some(VectorSize::Tri), Some(VectorSize::Tri)),
1087                    _ => (Some(VectorSize::Quad), Some(VectorSize::Quad)),
1088                };
1089                let scalar = Scalar::F64;
1090
1091                let args = vec![
1092                    match size {
1093                        Some(size) => TypeInner::Vector { size, scalar },
1094                        None => TypeInner::Scalar(scalar),
1095                    },
1096                    match second_size {
1097                        Some(size) => TypeInner::Vector { size, scalar },
1098                        None => TypeInner::Scalar(scalar),
1099                    },
1100                ];
1101
1102                let fun = match name {
1103                    "max" => MacroCall::Splatted(MathFunction::Max, size, 1),
1104                    "min" => MacroCall::Splatted(MathFunction::Min, size, 1),
1105                    _ => unreachable!(),
1106                };
1107
1108                declaration.overloads.push(module.add_builtin(args, fun))
1109            }
1110        }
1111        "mix" => {
1112            // bits layout
1113            // bit 0 through 1 - dims
1114            // bit 2 through 3 - splatted/boolean
1115            //
1116            // 0b1010 is the last element since splatted with single elements
1117            // is equal to normal single elements
1118            for bits in 0..0b1011 {
1119                let size = match bits & 0b11 {
1120                    0b00 => Some(VectorSize::Quad),
1121                    0b01 => Some(VectorSize::Bi),
1122                    0b10 => Some(VectorSize::Tri),
1123                    _ => None,
1124                };
1125                let scalar = Scalar::F64;
1126                let (splatted, boolean) = match bits >> 2 {
1127                    0b00 => (false, false),
1128                    0b01 => (false, true),
1129                    _ => (true, false),
1130                };
1131
1132                let ty = |scalar| match size {
1133                    Some(size) => TypeInner::Vector { size, scalar },
1134                    None => TypeInner::Scalar(scalar),
1135                };
1136                let args = vec![
1137                    ty(scalar),
1138                    ty(scalar),
1139                    match (boolean, splatted) {
1140                        (true, _) => ty(Scalar::BOOL),
1141                        (_, false) => TypeInner::Scalar(scalar),
1142                        _ => ty(scalar),
1143                    },
1144                ];
1145
1146                declaration.overloads.push(module.add_builtin(
1147                    args,
1148                    match boolean {
1149                        true => MacroCall::MixBoolean,
1150                        false => MacroCall::Splatted(MathFunction::Mix, size, 2),
1151                    },
1152                ))
1153            }
1154        }
1155        "clamp" => {
1156            // bits layout
1157            // bit 0 through 1 - dims
1158            // bit 2 - splatted
1159            //
1160            // 0b110 is the last element since splatted with single elements
1161            // is equal to normal single elements
1162            for bits in 0..0b111 {
1163                let scalar = Scalar::F64;
1164                let size = match bits & 0b11 {
1165                    0b00 => Some(VectorSize::Bi),
1166                    0b01 => Some(VectorSize::Tri),
1167                    0b10 => Some(VectorSize::Quad),
1168                    _ => None,
1169                };
1170                let splatted = bits & 0b100 == 0b100;
1171
1172                let base_ty = || match size {
1173                    Some(size) => TypeInner::Vector { size, scalar },
1174                    None => TypeInner::Scalar(scalar),
1175                };
1176                let limit_ty = || match splatted {
1177                    true => TypeInner::Scalar(scalar),
1178                    false => base_ty(),
1179                };
1180
1181                let args = vec![base_ty(), limit_ty(), limit_ty()];
1182
1183                declaration
1184                    .overloads
1185                    .push(module.add_builtin(args, MacroCall::Clamp(size)))
1186            }
1187        }
1188        "lessThan" | "greaterThan" | "lessThanEqual" | "greaterThanEqual" | "equal"
1189        | "notEqual" => {
1190            let scalar = Scalar::F64;
1191            for bits in 0..0b11 {
1192                let size = match bits {
1193                    0b00 => VectorSize::Bi,
1194                    0b01 => VectorSize::Tri,
1195                    _ => VectorSize::Quad,
1196                };
1197
1198                let ty = || TypeInner::Vector { size, scalar };
1199                let args = vec![ty(), ty()];
1200
1201                let fun = MacroCall::Binary(match name {
1202                    "lessThan" => BinaryOperator::Less,
1203                    "greaterThan" => BinaryOperator::Greater,
1204                    "lessThanEqual" => BinaryOperator::LessEqual,
1205                    "greaterThanEqual" => BinaryOperator::GreaterEqual,
1206                    "equal" => BinaryOperator::Equal,
1207                    "notEqual" => BinaryOperator::NotEqual,
1208                    _ => unreachable!(),
1209                });
1210
1211                declaration.overloads.push(module.add_builtin(args, fun))
1212            }
1213        }
1214        // Add common builtins with doubles
1215        _ => inject_common_builtin(declaration, module, name, 8),
1216    }
1217}
1218
1219/// Injects the builtins into declaration that can used either float or doubles
1220fn inject_common_builtin(
1221    declaration: &mut FunctionDeclaration,
1222    module: &mut Module,
1223    name: &str,
1224    float_width: crate::Bytes,
1225) {
1226    let float_scalar = Scalar {
1227        kind: Sk::Float,
1228        width: float_width,
1229    };
1230    match name {
1231        "ceil" | "round" | "roundEven" | "floor" | "fract" | "trunc" | "sqrt" | "inversesqrt"
1232        | "normalize" | "length" | "isinf" | "isnan" => {
1233            // bits layout
1234            // bit 0 through 1 - dims
1235            for bits in 0..0b100 {
1236                let size = match bits {
1237                    0b00 => None,
1238                    0b01 => Some(VectorSize::Bi),
1239                    0b10 => Some(VectorSize::Tri),
1240                    _ => Some(VectorSize::Quad),
1241                };
1242
1243                let args = vec![match size {
1244                    Some(size) => TypeInner::Vector {
1245                        size,
1246                        scalar: float_scalar,
1247                    },
1248                    None => TypeInner::Scalar(float_scalar),
1249                }];
1250
1251                let fun = match name {
1252                    "ceil" => MacroCall::MathFunction(MathFunction::Ceil),
1253                    "round" | "roundEven" => MacroCall::MathFunction(MathFunction::Round),
1254                    "floor" => MacroCall::MathFunction(MathFunction::Floor),
1255                    "fract" => MacroCall::MathFunction(MathFunction::Fract),
1256                    "trunc" => MacroCall::MathFunction(MathFunction::Trunc),
1257                    "sqrt" => MacroCall::MathFunction(MathFunction::Sqrt),
1258                    "inversesqrt" => MacroCall::MathFunction(MathFunction::InverseSqrt),
1259                    "normalize" => MacroCall::MathFunction(MathFunction::Normalize),
1260                    "length" => MacroCall::MathFunction(MathFunction::Length),
1261                    "isinf" => MacroCall::Relational(RelationalFunction::IsInf),
1262                    "isnan" => MacroCall::Relational(RelationalFunction::IsNan),
1263                    _ => unreachable!(),
1264                };
1265
1266                declaration.overloads.push(module.add_builtin(args, fun))
1267            }
1268        }
1269        "dot" | "reflect" | "distance" | "ldexp" => {
1270            // bits layout
1271            // bit 0 through 1 - dims
1272            for bits in 0..0b100 {
1273                let size = match bits {
1274                    0b00 => None,
1275                    0b01 => Some(VectorSize::Bi),
1276                    0b10 => Some(VectorSize::Tri),
1277                    _ => Some(VectorSize::Quad),
1278                };
1279                let ty = |scalar| match size {
1280                    Some(size) => TypeInner::Vector { size, scalar },
1281                    None => TypeInner::Scalar(scalar),
1282                };
1283
1284                let fun = match name {
1285                    "dot" => MacroCall::MathFunction(MathFunction::Dot),
1286                    "reflect" => MacroCall::MathFunction(MathFunction::Reflect),
1287                    "distance" => MacroCall::MathFunction(MathFunction::Distance),
1288                    "ldexp" => MacroCall::MathFunction(MathFunction::Ldexp),
1289                    _ => unreachable!(),
1290                };
1291
1292                let second_scalar = match fun {
1293                    MacroCall::MathFunction(MathFunction::Ldexp) => Scalar::I32,
1294                    _ => float_scalar,
1295                };
1296
1297                declaration
1298                    .overloads
1299                    .push(module.add_builtin(vec![ty(float_scalar), ty(second_scalar)], fun))
1300            }
1301        }
1302        "transpose" => {
1303            // bits layout
1304            // bit 0 through 3 - dims
1305            for bits in 0..0b1001 {
1306                let (rows, columns) = match bits {
1307                    0b0000 => (VectorSize::Bi, VectorSize::Bi),
1308                    0b0001 => (VectorSize::Bi, VectorSize::Tri),
1309                    0b0010 => (VectorSize::Bi, VectorSize::Quad),
1310                    0b0011 => (VectorSize::Tri, VectorSize::Bi),
1311                    0b0100 => (VectorSize::Tri, VectorSize::Tri),
1312                    0b0101 => (VectorSize::Tri, VectorSize::Quad),
1313                    0b0110 => (VectorSize::Quad, VectorSize::Bi),
1314                    0b0111 => (VectorSize::Quad, VectorSize::Tri),
1315                    _ => (VectorSize::Quad, VectorSize::Quad),
1316                };
1317
1318                declaration.overloads.push(module.add_builtin(
1319                    vec![TypeInner::Matrix {
1320                        columns,
1321                        rows,
1322                        scalar: float_scalar,
1323                    }],
1324                    MacroCall::MathFunction(MathFunction::Transpose),
1325                ))
1326            }
1327        }
1328        "inverse" | "determinant" => {
1329            // bits layout
1330            // bit 0 through 1 - dims
1331            for bits in 0..0b11 {
1332                let (rows, columns) = match bits {
1333                    0b00 => (VectorSize::Bi, VectorSize::Bi),
1334                    0b01 => (VectorSize::Tri, VectorSize::Tri),
1335                    _ => (VectorSize::Quad, VectorSize::Quad),
1336                };
1337
1338                let args = vec![TypeInner::Matrix {
1339                    columns,
1340                    rows,
1341                    scalar: float_scalar,
1342                }];
1343
1344                declaration.overloads.push(module.add_builtin(
1345                    args,
1346                    MacroCall::MathFunction(match name {
1347                        "inverse" => MathFunction::Inverse,
1348                        "determinant" => MathFunction::Determinant,
1349                        _ => unreachable!(),
1350                    }),
1351                ))
1352            }
1353        }
1354        "mod" | "step" => {
1355            // bits layout
1356            // bit 0 through 2 - dims
1357            for bits in 0..0b111 {
1358                let (size, second_size) = match bits {
1359                    0b000 => (None, None),
1360                    0b001 => (Some(VectorSize::Bi), None),
1361                    0b010 => (Some(VectorSize::Tri), None),
1362                    0b011 => (Some(VectorSize::Quad), None),
1363                    0b100 => (Some(VectorSize::Bi), Some(VectorSize::Bi)),
1364                    0b101 => (Some(VectorSize::Tri), Some(VectorSize::Tri)),
1365                    _ => (Some(VectorSize::Quad), Some(VectorSize::Quad)),
1366                };
1367
1368                let mut args = Vec::with_capacity(2);
1369                let step = name == "step";
1370
1371                for i in 0..2 {
1372                    let maybe_size = match i == step as u32 {
1373                        true => size,
1374                        false => second_size,
1375                    };
1376
1377                    args.push(match maybe_size {
1378                        Some(size) => TypeInner::Vector {
1379                            size,
1380                            scalar: float_scalar,
1381                        },
1382                        None => TypeInner::Scalar(float_scalar),
1383                    })
1384                }
1385
1386                let fun = match name {
1387                    "mod" => MacroCall::Mod(size),
1388                    "step" => MacroCall::Splatted(MathFunction::Step, size, 0),
1389                    _ => unreachable!(),
1390                };
1391
1392                declaration.overloads.push(module.add_builtin(args, fun))
1393            }
1394        }
1395        // TODO: https://github.com/gfx-rs/naga/issues/2526
1396        // "modf" | "frexp" => { ... }
1397        "cross" => {
1398            let args = vec![
1399                TypeInner::Vector {
1400                    size: VectorSize::Tri,
1401                    scalar: float_scalar,
1402                },
1403                TypeInner::Vector {
1404                    size: VectorSize::Tri,
1405                    scalar: float_scalar,
1406                },
1407            ];
1408
1409            declaration
1410                .overloads
1411                .push(module.add_builtin(args, MacroCall::MathFunction(MathFunction::Cross)))
1412        }
1413        "outerProduct" => {
1414            // bits layout
1415            // bit 0 through 3 - dims
1416            for bits in 0..0b1001 {
1417                let (size1, size2) = match bits {
1418                    0b0000 => (VectorSize::Bi, VectorSize::Bi),
1419                    0b0001 => (VectorSize::Bi, VectorSize::Tri),
1420                    0b0010 => (VectorSize::Bi, VectorSize::Quad),
1421                    0b0011 => (VectorSize::Tri, VectorSize::Bi),
1422                    0b0100 => (VectorSize::Tri, VectorSize::Tri),
1423                    0b0101 => (VectorSize::Tri, VectorSize::Quad),
1424                    0b0110 => (VectorSize::Quad, VectorSize::Bi),
1425                    0b0111 => (VectorSize::Quad, VectorSize::Tri),
1426                    _ => (VectorSize::Quad, VectorSize::Quad),
1427                };
1428
1429                let args = vec![
1430                    TypeInner::Vector {
1431                        size: size1,
1432                        scalar: float_scalar,
1433                    },
1434                    TypeInner::Vector {
1435                        size: size2,
1436                        scalar: float_scalar,
1437                    },
1438                ];
1439
1440                declaration
1441                    .overloads
1442                    .push(module.add_builtin(args, MacroCall::MathFunction(MathFunction::Outer)))
1443            }
1444        }
1445        "faceforward" | "fma" => {
1446            // bits layout
1447            // bit 0 through 1 - dims
1448            for bits in 0..0b100 {
1449                let size = match bits {
1450                    0b00 => None,
1451                    0b01 => Some(VectorSize::Bi),
1452                    0b10 => Some(VectorSize::Tri),
1453                    _ => Some(VectorSize::Quad),
1454                };
1455
1456                let ty = || match size {
1457                    Some(size) => TypeInner::Vector {
1458                        size,
1459                        scalar: float_scalar,
1460                    },
1461                    None => TypeInner::Scalar(float_scalar),
1462                };
1463                let args = vec![ty(), ty(), ty()];
1464
1465                let fun = match name {
1466                    "faceforward" => MacroCall::MathFunction(MathFunction::FaceForward),
1467                    "fma" => MacroCall::MathFunction(MathFunction::Fma),
1468                    _ => unreachable!(),
1469                };
1470
1471                declaration.overloads.push(module.add_builtin(args, fun))
1472            }
1473        }
1474        "refract" => {
1475            // bits layout
1476            // bit 0 through 1 - dims
1477            for bits in 0..0b100 {
1478                let size = match bits {
1479                    0b00 => None,
1480                    0b01 => Some(VectorSize::Bi),
1481                    0b10 => Some(VectorSize::Tri),
1482                    _ => Some(VectorSize::Quad),
1483                };
1484
1485                let ty = || match size {
1486                    Some(size) => TypeInner::Vector {
1487                        size,
1488                        scalar: float_scalar,
1489                    },
1490                    None => TypeInner::Scalar(float_scalar),
1491                };
1492                let args = vec![ty(), ty(), TypeInner::Scalar(Scalar::F32)];
1493                declaration
1494                    .overloads
1495                    .push(module.add_builtin(args, MacroCall::MathFunction(MathFunction::Refract)))
1496            }
1497        }
1498        "smoothstep" => {
1499            // bit 0 - splatted
1500            // bit 1 through 2 - dims
1501            for bits in 0..0b1000 {
1502                let splatted = bits & 0b1 == 0b1;
1503                let size = match bits >> 1 {
1504                    0b00 => None,
1505                    0b01 => Some(VectorSize::Bi),
1506                    0b10 => Some(VectorSize::Tri),
1507                    _ => Some(VectorSize::Quad),
1508                };
1509
1510                if splatted && size.is_none() {
1511                    continue;
1512                }
1513
1514                let base_ty = || match size {
1515                    Some(size) => TypeInner::Vector {
1516                        size,
1517                        scalar: float_scalar,
1518                    },
1519                    None => TypeInner::Scalar(float_scalar),
1520                };
1521                let ty = || match splatted {
1522                    true => TypeInner::Scalar(float_scalar),
1523                    false => base_ty(),
1524                };
1525                declaration.overloads.push(module.add_builtin(
1526                    vec![ty(), ty(), base_ty()],
1527                    MacroCall::SmoothStep { splatted: size },
1528                ))
1529            }
1530        }
1531        // The function isn't a builtin or we don't yet support it
1532        _ => {}
1533    }
1534}
1535
1536#[derive(Clone, Copy, PartialEq, Debug)]
1537pub enum TextureLevelType {
1538    None,
1539    Lod,
1540    Grad,
1541}
1542
1543/// A compiler defined builtin function
1544#[derive(Clone, Copy, PartialEq, Debug)]
1545pub enum MacroCall {
1546    Sampler,
1547    SamplerShadow,
1548    Texture {
1549        proj: bool,
1550        offset: bool,
1551        shadow: bool,
1552        level_type: TextureLevelType,
1553    },
1554    TextureSize {
1555        arrayed: bool,
1556    },
1557    TextureQueryLevels,
1558    ImageLoad {
1559        multi: bool,
1560    },
1561    ImageStore,
1562    MathFunction(MathFunction),
1563    FindLsbUint,
1564    FindMsbUint,
1565    BitfieldExtract,
1566    BitfieldInsert,
1567    Relational(RelationalFunction),
1568    Unary(UnaryOperator),
1569    Binary(BinaryOperator),
1570    Mod(Option<VectorSize>),
1571    Splatted(MathFunction, Option<VectorSize>, usize),
1572    MixBoolean,
1573    Clamp(Option<VectorSize>),
1574    BitCast(Sk),
1575    Derivate(Axis, Ctrl),
1576    Barrier,
1577    /// SmoothStep needs a separate variant because it might need it's inputs
1578    /// to be splatted depending on the overload
1579    SmoothStep {
1580        /// The size of the splat operation if some
1581        splatted: Option<VectorSize>,
1582    },
1583}
1584
1585impl MacroCall {
1586    /// Adds the necessary expressions and statements to the passed body and
1587    /// finally returns the final expression with the correct result
1588    pub fn call(
1589        &self,
1590        frontend: &mut Frontend,
1591        ctx: &mut Context,
1592        args: &mut [Handle<Expression>],
1593        meta: Span,
1594    ) -> Result<Option<Handle<Expression>>> {
1595        Ok(Some(match *self {
1596            MacroCall::Sampler => {
1597                ctx.samplers.insert(args[0], args[1]);
1598                args[0]
1599            }
1600            MacroCall::SamplerShadow => {
1601                sampled_to_depth(ctx, args[0], meta, &mut frontend.errors);
1602                ctx.invalidate_expression(args[0], meta)?;
1603                ctx.samplers.insert(args[0], args[1]);
1604                args[0]
1605            }
1606            MacroCall::Texture {
1607                proj,
1608                offset,
1609                shadow,
1610                level_type,
1611            } => {
1612                let mut coords = args[1];
1613
1614                if proj {
1615                    let size = match *ctx.resolve_type(coords, meta)? {
1616                        TypeInner::Vector { size, .. } => size,
1617                        _ => unreachable!(),
1618                    };
1619                    let mut right = ctx.add_expression(
1620                        Expression::AccessIndex {
1621                            base: coords,
1622                            index: size as u32 - 1,
1623                        },
1624                        Span::default(),
1625                    )?;
1626                    let left = if let VectorSize::Bi = size {
1627                        ctx.add_expression(
1628                            Expression::AccessIndex {
1629                                base: coords,
1630                                index: 0,
1631                            },
1632                            Span::default(),
1633                        )?
1634                    } else {
1635                        let size = match size {
1636                            VectorSize::Tri => VectorSize::Bi,
1637                            _ => VectorSize::Tri,
1638                        };
1639                        right = ctx.add_expression(
1640                            Expression::Splat { size, value: right },
1641                            Span::default(),
1642                        )?;
1643                        ctx.vector_resize(size, coords, Span::default())?
1644                    };
1645                    coords = ctx.add_expression(
1646                        Expression::Binary {
1647                            op: BinaryOperator::Divide,
1648                            left,
1649                            right,
1650                        },
1651                        Span::default(),
1652                    )?;
1653                }
1654
1655                let extra = args.get(2).copied();
1656                let comps = frontend.coordinate_components(ctx, args[0], coords, extra, meta)?;
1657
1658                let mut num_args = 2;
1659
1660                if comps.used_extra {
1661                    num_args += 1;
1662                };
1663
1664                // Parse out explicit texture level.
1665                let mut level = match level_type {
1666                    TextureLevelType::None => SampleLevel::Auto,
1667
1668                    TextureLevelType::Lod => {
1669                        num_args += 1;
1670
1671                        if shadow {
1672                            log::warn!("Assuming LOD {:?} is zero", args[2],);
1673
1674                            SampleLevel::Zero
1675                        } else {
1676                            SampleLevel::Exact(args[2])
1677                        }
1678                    }
1679
1680                    TextureLevelType::Grad => {
1681                        num_args += 2;
1682
1683                        if shadow {
1684                            log::warn!(
1685                                "Assuming gradients {:?} and {:?} are not greater than 1",
1686                                args[2],
1687                                args[3],
1688                            );
1689                            SampleLevel::Zero
1690                        } else {
1691                            SampleLevel::Gradient {
1692                                x: args[2],
1693                                y: args[3],
1694                            }
1695                        }
1696                    }
1697                };
1698
1699                let texture_offset = match offset {
1700                    true => {
1701                        let offset_arg = args[num_args];
1702                        num_args += 1;
1703                        Some(offset_arg)
1704                    }
1705                    false => None,
1706                };
1707
1708                // Now go back and look for optional bias arg (if available)
1709                if let TextureLevelType::None = level_type {
1710                    level = args
1711                        .get(num_args)
1712                        .copied()
1713                        .map_or(SampleLevel::Auto, SampleLevel::Bias);
1714                }
1715
1716                texture_call(ctx, args[0], level, comps, texture_offset, meta)?
1717            }
1718
1719            MacroCall::TextureSize { arrayed } => {
1720                let mut expr = ctx.add_expression(
1721                    Expression::ImageQuery {
1722                        image: args[0],
1723                        query: ImageQuery::Size {
1724                            level: args.get(1).copied(),
1725                        },
1726                    },
1727                    Span::default(),
1728                )?;
1729
1730                if arrayed {
1731                    let mut components = Vec::with_capacity(4);
1732
1733                    let size = match *ctx.resolve_type(expr, meta)? {
1734                        TypeInner::Vector { size: ori_size, .. } => {
1735                            for index in 0..(ori_size as u32) {
1736                                components.push(ctx.add_expression(
1737                                    Expression::AccessIndex { base: expr, index },
1738                                    Span::default(),
1739                                )?)
1740                            }
1741
1742                            match ori_size {
1743                                VectorSize::Bi => VectorSize::Tri,
1744                                _ => VectorSize::Quad,
1745                            }
1746                        }
1747                        _ => {
1748                            components.push(expr);
1749                            VectorSize::Bi
1750                        }
1751                    };
1752
1753                    components.push(ctx.add_expression(
1754                        Expression::ImageQuery {
1755                            image: args[0],
1756                            query: ImageQuery::NumLayers,
1757                        },
1758                        Span::default(),
1759                    )?);
1760
1761                    let ty = ctx.module.types.insert(
1762                        Type {
1763                            name: None,
1764                            inner: TypeInner::Vector {
1765                                size,
1766                                scalar: Scalar::U32,
1767                            },
1768                        },
1769                        Span::default(),
1770                    );
1771
1772                    expr = ctx.add_expression(Expression::Compose { components, ty }, meta)?
1773                }
1774
1775                ctx.add_expression(
1776                    Expression::As {
1777                        expr,
1778                        kind: Sk::Sint,
1779                        convert: Some(4),
1780                    },
1781                    Span::default(),
1782                )?
1783            }
1784            MacroCall::TextureQueryLevels => {
1785                let expr = ctx.add_expression(
1786                    Expression::ImageQuery {
1787                        image: args[0],
1788                        query: ImageQuery::NumLevels,
1789                    },
1790                    Span::default(),
1791                )?;
1792
1793                ctx.add_expression(
1794                    Expression::As {
1795                        expr,
1796                        kind: Sk::Sint,
1797                        convert: Some(4),
1798                    },
1799                    Span::default(),
1800                )?
1801            }
1802            MacroCall::ImageLoad { multi } => {
1803                let comps = frontend.coordinate_components(ctx, args[0], args[1], None, meta)?;
1804                let (sample, level) = match (multi, args.get(2)) {
1805                    (_, None) => (None, None),
1806                    (true, Some(&arg)) => (Some(arg), None),
1807                    (false, Some(&arg)) => (None, Some(arg)),
1808                };
1809                ctx.add_expression(
1810                    Expression::ImageLoad {
1811                        image: args[0],
1812                        coordinate: comps.coordinate,
1813                        array_index: comps.array_index,
1814                        sample,
1815                        level,
1816                    },
1817                    Span::default(),
1818                )?
1819            }
1820            MacroCall::ImageStore => {
1821                let comps = frontend.coordinate_components(ctx, args[0], args[1], None, meta)?;
1822                ctx.emit_restart();
1823                ctx.body.push(
1824                    crate::Statement::ImageStore {
1825                        image: args[0],
1826                        coordinate: comps.coordinate,
1827                        array_index: comps.array_index,
1828                        value: args[2],
1829                    },
1830                    meta,
1831                );
1832                return Ok(None);
1833            }
1834            MacroCall::MathFunction(fun) => ctx.add_expression(
1835                Expression::Math {
1836                    fun,
1837                    arg: args[0],
1838                    arg1: args.get(1).copied(),
1839                    arg2: args.get(2).copied(),
1840                    arg3: args.get(3).copied(),
1841                },
1842                Span::default(),
1843            )?,
1844            mc @ (MacroCall::FindLsbUint | MacroCall::FindMsbUint) => {
1845                let fun = match mc {
1846                    MacroCall::FindLsbUint => MathFunction::FirstTrailingBit,
1847                    MacroCall::FindMsbUint => MathFunction::FirstLeadingBit,
1848                    _ => unreachable!(),
1849                };
1850                let res = ctx.add_expression(
1851                    Expression::Math {
1852                        fun,
1853                        arg: args[0],
1854                        arg1: None,
1855                        arg2: None,
1856                        arg3: None,
1857                    },
1858                    Span::default(),
1859                )?;
1860                ctx.add_expression(
1861                    Expression::As {
1862                        expr: res,
1863                        kind: Sk::Sint,
1864                        convert: Some(4),
1865                    },
1866                    Span::default(),
1867                )?
1868            }
1869            MacroCall::BitfieldInsert => {
1870                let conv_arg_2 = ctx.add_expression(
1871                    Expression::As {
1872                        expr: args[2],
1873                        kind: Sk::Uint,
1874                        convert: Some(4),
1875                    },
1876                    Span::default(),
1877                )?;
1878                let conv_arg_3 = ctx.add_expression(
1879                    Expression::As {
1880                        expr: args[3],
1881                        kind: Sk::Uint,
1882                        convert: Some(4),
1883                    },
1884                    Span::default(),
1885                )?;
1886                ctx.add_expression(
1887                    Expression::Math {
1888                        fun: MathFunction::InsertBits,
1889                        arg: args[0],
1890                        arg1: Some(args[1]),
1891                        arg2: Some(conv_arg_2),
1892                        arg3: Some(conv_arg_3),
1893                    },
1894                    Span::default(),
1895                )?
1896            }
1897            MacroCall::BitfieldExtract => {
1898                let conv_arg_1 = ctx.add_expression(
1899                    Expression::As {
1900                        expr: args[1],
1901                        kind: Sk::Uint,
1902                        convert: Some(4),
1903                    },
1904                    Span::default(),
1905                )?;
1906                let conv_arg_2 = ctx.add_expression(
1907                    Expression::As {
1908                        expr: args[2],
1909                        kind: Sk::Uint,
1910                        convert: Some(4),
1911                    },
1912                    Span::default(),
1913                )?;
1914                ctx.add_expression(
1915                    Expression::Math {
1916                        fun: MathFunction::ExtractBits,
1917                        arg: args[0],
1918                        arg1: Some(conv_arg_1),
1919                        arg2: Some(conv_arg_2),
1920                        arg3: None,
1921                    },
1922                    Span::default(),
1923                )?
1924            }
1925            MacroCall::Relational(fun) => ctx.add_expression(
1926                Expression::Relational {
1927                    fun,
1928                    argument: args[0],
1929                },
1930                Span::default(),
1931            )?,
1932            MacroCall::Unary(op) => {
1933                ctx.add_expression(Expression::Unary { op, expr: args[0] }, Span::default())?
1934            }
1935            MacroCall::Binary(op) => ctx.add_expression(
1936                Expression::Binary {
1937                    op,
1938                    left: args[0],
1939                    right: args[1],
1940                },
1941                Span::default(),
1942            )?,
1943            MacroCall::Mod(size) => {
1944                ctx.implicit_splat(&mut args[1], meta, size)?;
1945
1946                // x - y * floor(x / y)
1947
1948                let div = ctx.add_expression(
1949                    Expression::Binary {
1950                        op: BinaryOperator::Divide,
1951                        left: args[0],
1952                        right: args[1],
1953                    },
1954                    Span::default(),
1955                )?;
1956                let floor = ctx.add_expression(
1957                    Expression::Math {
1958                        fun: MathFunction::Floor,
1959                        arg: div,
1960                        arg1: None,
1961                        arg2: None,
1962                        arg3: None,
1963                    },
1964                    Span::default(),
1965                )?;
1966                let mult = ctx.add_expression(
1967                    Expression::Binary {
1968                        op: BinaryOperator::Multiply,
1969                        left: floor,
1970                        right: args[1],
1971                    },
1972                    Span::default(),
1973                )?;
1974                ctx.add_expression(
1975                    Expression::Binary {
1976                        op: BinaryOperator::Subtract,
1977                        left: args[0],
1978                        right: mult,
1979                    },
1980                    Span::default(),
1981                )?
1982            }
1983            MacroCall::Splatted(fun, size, i) => {
1984                ctx.implicit_splat(&mut args[i], meta, size)?;
1985
1986                ctx.add_expression(
1987                    Expression::Math {
1988                        fun,
1989                        arg: args[0],
1990                        arg1: args.get(1).copied(),
1991                        arg2: args.get(2).copied(),
1992                        arg3: args.get(3).copied(),
1993                    },
1994                    Span::default(),
1995                )?
1996            }
1997            MacroCall::MixBoolean => ctx.add_expression(
1998                Expression::Select {
1999                    condition: args[2],
2000                    accept: args[1],
2001                    reject: args[0],
2002                },
2003                Span::default(),
2004            )?,
2005            MacroCall::Clamp(size) => {
2006                ctx.implicit_splat(&mut args[1], meta, size)?;
2007                ctx.implicit_splat(&mut args[2], meta, size)?;
2008
2009                ctx.add_expression(
2010                    Expression::Math {
2011                        fun: MathFunction::Clamp,
2012                        arg: args[0],
2013                        arg1: args.get(1).copied(),
2014                        arg2: args.get(2).copied(),
2015                        arg3: args.get(3).copied(),
2016                    },
2017                    Span::default(),
2018                )?
2019            }
2020            MacroCall::BitCast(kind) => ctx.add_expression(
2021                Expression::As {
2022                    expr: args[0],
2023                    kind,
2024                    convert: None,
2025                },
2026                Span::default(),
2027            )?,
2028            MacroCall::Derivate(axis, ctrl) => ctx.add_expression(
2029                Expression::Derivative {
2030                    axis,
2031                    ctrl,
2032                    expr: args[0],
2033                },
2034                Span::default(),
2035            )?,
2036            MacroCall::Barrier => {
2037                ctx.emit_restart();
2038                ctx.body.push(
2039                    crate::Statement::ControlBarrier(crate::Barrier::all()),
2040                    meta,
2041                );
2042                return Ok(None);
2043            }
2044            MacroCall::SmoothStep { splatted } => {
2045                ctx.implicit_splat(&mut args[0], meta, splatted)?;
2046                ctx.implicit_splat(&mut args[1], meta, splatted)?;
2047
2048                ctx.add_expression(
2049                    Expression::Math {
2050                        fun: MathFunction::SmoothStep,
2051                        arg: args[0],
2052                        arg1: args.get(1).copied(),
2053                        arg2: args.get(2).copied(),
2054                        arg3: None,
2055                    },
2056                    Span::default(),
2057                )?
2058            }
2059        }))
2060    }
2061}
2062
2063fn texture_call(
2064    ctx: &mut Context,
2065    image: Handle<Expression>,
2066    level: SampleLevel,
2067    comps: CoordComponents,
2068    offset: Option<Handle<Expression>>,
2069    meta: Span,
2070) -> Result<Handle<Expression>> {
2071    if let Some(sampler) = ctx.samplers.get(&image).copied() {
2072        let mut array_index = comps.array_index;
2073
2074        if let Some(ref mut array_index_expr) = array_index {
2075            ctx.conversion(array_index_expr, meta, Scalar::I32)?;
2076        }
2077
2078        Ok(ctx.add_expression(
2079            Expression::ImageSample {
2080                image,
2081                sampler,
2082                gather: None, //TODO
2083                coordinate: comps.coordinate,
2084                array_index,
2085                offset,
2086                level,
2087                depth_ref: comps.depth_ref,
2088                clamp_to_edge: false,
2089            },
2090            meta,
2091        )?)
2092    } else {
2093        Err(Error {
2094            kind: ErrorKind::SemanticError("Bad call".into()),
2095            meta,
2096        })
2097    }
2098}
2099
2100/// Helper struct for texture calls with the separate components from the vector argument
2101///
2102/// Obtained by calling [`coordinate_components`](Frontend::coordinate_components)
2103#[derive(Debug)]
2104struct CoordComponents {
2105    coordinate: Handle<Expression>,
2106    depth_ref: Option<Handle<Expression>>,
2107    array_index: Option<Handle<Expression>>,
2108    used_extra: bool,
2109}
2110
2111impl Frontend {
2112    /// Helper function for texture calls, splits the vector argument into it's components
2113    fn coordinate_components(
2114        &mut self,
2115        ctx: &mut Context,
2116        image: Handle<Expression>,
2117        coord: Handle<Expression>,
2118        extra: Option<Handle<Expression>>,
2119        meta: Span,
2120    ) -> Result<CoordComponents> {
2121        if let TypeInner::Image {
2122            dim,
2123            arrayed,
2124            class,
2125        } = *ctx.resolve_type(image, meta)?
2126        {
2127            let image_size = match dim {
2128                Dim::D1 => None,
2129                Dim::D2 => Some(VectorSize::Bi),
2130                Dim::D3 => Some(VectorSize::Tri),
2131                Dim::Cube => Some(VectorSize::Tri),
2132            };
2133            let coord_size = match *ctx.resolve_type(coord, meta)? {
2134                TypeInner::Vector { size, .. } => Some(size),
2135                _ => None,
2136            };
2137            let (shadow, storage) = match class {
2138                ImageClass::Depth { .. } => (true, false),
2139                ImageClass::Storage { .. } => (false, true),
2140                ImageClass::Sampled { .. } => (false, false),
2141            };
2142
2143            let coordinate = match (image_size, coord_size) {
2144                (Some(size), Some(coord_s)) if size != coord_s => {
2145                    ctx.vector_resize(size, coord, Span::default())?
2146                }
2147                (None, Some(_)) => ctx.add_expression(
2148                    Expression::AccessIndex {
2149                        base: coord,
2150                        index: 0,
2151                    },
2152                    Span::default(),
2153                )?,
2154                _ => coord,
2155            };
2156
2157            let mut coord_index = image_size.map_or(1, |s| s as u32);
2158
2159            let array_index = if arrayed && !(storage && dim == Dim::Cube) {
2160                let index = coord_index;
2161                coord_index += 1;
2162
2163                Some(ctx.add_expression(
2164                    Expression::AccessIndex { base: coord, index },
2165                    Span::default(),
2166                )?)
2167            } else {
2168                None
2169            };
2170            let mut used_extra = false;
2171            let depth_ref = match shadow {
2172                true => {
2173                    let index = coord_index;
2174
2175                    if index == 4 {
2176                        used_extra = true;
2177                        extra
2178                    } else {
2179                        Some(ctx.add_expression(
2180                            Expression::AccessIndex { base: coord, index },
2181                            Span::default(),
2182                        )?)
2183                    }
2184                }
2185                false => None,
2186            };
2187
2188            Ok(CoordComponents {
2189                coordinate,
2190                depth_ref,
2191                array_index,
2192                used_extra,
2193            })
2194        } else {
2195            self.errors.push(Error {
2196                kind: ErrorKind::SemanticError("Type is not an image".into()),
2197                meta,
2198            });
2199
2200            Ok(CoordComponents {
2201                coordinate: coord,
2202                depth_ref: None,
2203                array_index: None,
2204                used_extra: false,
2205            })
2206        }
2207    }
2208}
2209
2210/// Helper function to cast a expression holding a sampled image to a
2211/// depth image.
2212pub fn sampled_to_depth(
2213    ctx: &mut Context,
2214    image: Handle<Expression>,
2215    meta: Span,
2216    errors: &mut Vec<Error>,
2217) {
2218    // Get the a mutable type handle of the underlying image storage
2219    let ty = match ctx[image] {
2220        Expression::GlobalVariable(handle) => &mut ctx.module.global_variables.get_mut(handle).ty,
2221        Expression::FunctionArgument(i) => {
2222            // Mark the function argument as carrying a depth texture
2223            ctx.parameters_info[i as usize].depth = true;
2224            // NOTE: We need to later also change the parameter type
2225            &mut ctx.arguments[i as usize].ty
2226        }
2227        _ => {
2228            // Only globals and function arguments are allowed to carry an image
2229            return errors.push(Error {
2230                kind: ErrorKind::SemanticError("Not a valid texture expression".into()),
2231                meta,
2232            });
2233        }
2234    };
2235
2236    match ctx.module.types[*ty].inner {
2237        // Update the image class to depth in case it already isn't
2238        TypeInner::Image {
2239            class,
2240            dim,
2241            arrayed,
2242        } => match class {
2243            ImageClass::Sampled { multi, .. } => {
2244                *ty = ctx.module.types.insert(
2245                    Type {
2246                        name: None,
2247                        inner: TypeInner::Image {
2248                            dim,
2249                            arrayed,
2250                            class: ImageClass::Depth { multi },
2251                        },
2252                    },
2253                    Span::default(),
2254                )
2255            }
2256            ImageClass::Depth { .. } => {}
2257            // Other image classes aren't allowed to be transformed to depth
2258            ImageClass::Storage { .. } => errors.push(Error {
2259                kind: ErrorKind::SemanticError("Not a texture".into()),
2260                meta,
2261            }),
2262        },
2263        _ => errors.push(Error {
2264            kind: ErrorKind::SemanticError("Not a texture".into()),
2265            meta,
2266        }),
2267    };
2268
2269    // Copy the handle to allow borrowing the `ctx` again
2270    let ty = *ty;
2271
2272    // If the image was passed through a function argument we also need to change
2273    // the corresponding parameter
2274    if let Expression::FunctionArgument(i) = ctx[image] {
2275        ctx.parameters[i as usize] = ty;
2276    }
2277}
2278
2279bitflags::bitflags! {
2280    /// Influences the operation [`texture_args_generator`]
2281    struct TextureArgsOptions: u32 {
2282        /// Generates multisampled variants of images
2283        const MULTI = 1 << 0;
2284        /// Generates shadow variants of images
2285        const SHADOW = 1 << 1;
2286        /// Generates standard images
2287        const STANDARD = 1 << 2;
2288        /// Generates cube arrayed images
2289        const CUBE_ARRAY = 1 << 3;
2290        /// Generates cube arrayed images
2291        const D2_MULTI_ARRAY = 1 << 4;
2292    }
2293}
2294
2295impl From<BuiltinVariations> for TextureArgsOptions {
2296    fn from(variations: BuiltinVariations) -> Self {
2297        let mut options = TextureArgsOptions::empty();
2298        if variations.contains(BuiltinVariations::STANDARD) {
2299            options |= TextureArgsOptions::STANDARD
2300        }
2301        if variations.contains(BuiltinVariations::CUBE_TEXTURES_ARRAY) {
2302            options |= TextureArgsOptions::CUBE_ARRAY
2303        }
2304        if variations.contains(BuiltinVariations::D2_MULTI_TEXTURES_ARRAY) {
2305            options |= TextureArgsOptions::D2_MULTI_ARRAY
2306        }
2307        options
2308    }
2309}
2310
2311/// Helper function to generate the image components for texture/image builtins
2312///
2313/// Calls the passed function `f` with:
2314/// ```text
2315/// f(ScalarKind, ImageDimension, arrayed, multi, shadow)
2316/// ```
2317///
2318/// `options` controls extra image variants generation like multisampling and depth,
2319/// see the struct documentation
2320fn texture_args_generator(
2321    options: TextureArgsOptions,
2322    mut f: impl FnMut(crate::ScalarKind, Dim, bool, bool, bool),
2323) {
2324    for kind in [Sk::Float, Sk::Uint, Sk::Sint].iter().copied() {
2325        for dim in [Dim::D1, Dim::D2, Dim::D3, Dim::Cube].iter().copied() {
2326            for arrayed in [false, true].iter().copied() {
2327                if dim == Dim::Cube && arrayed {
2328                    if !options.contains(TextureArgsOptions::CUBE_ARRAY) {
2329                        continue;
2330                    }
2331                } else if Dim::D2 == dim
2332                    && options.contains(TextureArgsOptions::MULTI)
2333                    && arrayed
2334                    && options.contains(TextureArgsOptions::D2_MULTI_ARRAY)
2335                {
2336                    // multisampling for sampler2DMSArray
2337                    f(kind, dim, arrayed, true, false);
2338                } else if !options.contains(TextureArgsOptions::STANDARD) {
2339                    continue;
2340                }
2341
2342                f(kind, dim, arrayed, false, false);
2343
2344                // 3D images can't be neither arrayed nor shadow
2345                // so we break out early, this way arrayed will always
2346                // be false and we won't hit the shadow branch
2347                if let Dim::D3 = dim {
2348                    break;
2349                }
2350
2351                if Dim::D2 == dim && options.contains(TextureArgsOptions::MULTI) && !arrayed {
2352                    // multisampling
2353                    f(kind, dim, arrayed, true, false);
2354                }
2355
2356                if Sk::Float == kind && options.contains(TextureArgsOptions::SHADOW) {
2357                    // shadow
2358                    f(kind, dim, arrayed, false, true);
2359                }
2360            }
2361        }
2362    }
2363}
2364
2365/// Helper functions used to convert from a image dimension into a integer representing the
2366/// number of components needed for the coordinates vector (1 means scalar instead of vector)
2367const fn image_dims_to_coords_size(dim: Dim) -> usize {
2368    match dim {
2369        Dim::D1 => 1,
2370        Dim::D2 => 2,
2371        _ => 3,
2372    }
2373}