Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


SSL builtins.rs   Sprache: unbekannt

 
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

use super::{
    ast::{
        BuiltinVariations, FunctionDeclaration, FunctionKind, Overload, ParameterInfo,
        ParameterQualifier,
    },
    context::Context,
    Error, ErrorKind, Frontend, Result,
};
use crate::{
    BinaryOperator, DerivativeAxis as Axis, DerivativeControl as Ctrl, Expression, Handle,
    ImageClass, ImageDimension as Dim, ImageQuery, MathFunction, Module, RelationalFunction,
    SampleLevel, Scalar, ScalarKind as Sk, Span, Type, TypeInner, UnaryOperator, VectorSize,
};

impl crate::ScalarKind {
    const fn dummy_storage_format(&self) -> crate::StorageFormat {
        match *self {
            Sk::Sint => crate::StorageFormat::R16Sint,
            Sk::Uint => crate::StorageFormat::R16Uint,
            _ => crate::StorageFormat::R16Float,
        }
    }
}

impl Module {
    /// Helper function, to create a function prototype for a builtin
    fn add_builtin(&mut self, args: Vec<TypeInner>, builtin: MacroCall) -> Overload {
        let mut parameters = Vec::with_capacity(args.len());
        let mut parameters_info = Vec::with_capacity(args.len());

        for arg in args {
            parameters.push(self.types.insert(
                Type {
                    name: None,
                    inner: arg,
                },
                Span::default(),
            ));
            parameters_info.push(ParameterInfo {
                qualifier: ParameterQualifier::In,
                depth: false,
            });
        }

        Overload {
            parameters,
            parameters_info,
            kind: FunctionKind::Macro(builtin),
            defined: false,
            internal: true,
            void: false,
        }
    }
}

const fn make_coords_arg(number_of_components: usize, kind: Sk) -> TypeInner {
    let scalar = Scalar { kind, width: 4 };

    match number_of_components {
        1 => TypeInner::Scalar(scalar),
        _ => TypeInner::Vector {
            size: match number_of_components {
                2 => VectorSize::Bi,
                3 => VectorSize::Tri,
                _ => VectorSize::Quad,
            },
            scalar,
        },
    }
}

/// Inject builtins into the declaration
///
/// This is done to not add a large startup cost and not increase memory
/// usage if it isn't needed.
pub fn inject_builtin(
    declaration: &mut FunctionDeclaration,
    module: &mut Module,
    name: &str,
    mut variations: BuiltinVariations,
) {
    log::trace!(
        "{} variations: {:?} {:?}",
        name,
        variations,
        declaration.variations
    );
    // Don't regeneate variations
    variations.remove(declaration.variations);
    declaration.variations |= variations;

    if variations.contains(BuiltinVariations::STANDARD) {
        inject_standard_builtins(declaration, module, name)
    }

    if variations.contains(BuiltinVariations::DOUBLE) {
        inject_double_builtin(declaration, module, name)
    }

    match name {
        "texture"
        | "textureGrad"
        | "textureGradOffset"
        | "textureLod"
        | "textureLodOffset"
        | "textureOffset"
        | "textureProj"
        | "textureProjGrad"
        | "textureProjGradOffset"
        | "textureProjLod"
        | "textureProjLodOffset"
        | "textureProjOffset" => {
            let f = |kind, dim, arrayed, multi, shadow| {
                for bits in 0..=0b11 {
                    let variant = bits & 0b1 != 0;
                    let bias = bits & 0b10 != 0;

                    let (proj, offset, level_type) = match name {
                        // texture(gsampler, gvec P, [float bias]);
                        "texture" => (false, false, TextureLevelType::None),
                        // textureGrad(gsampler, gvec P, gvec dPdx, gvec dPdy);
                        "textureGrad" => (false, false, TextureLevelType::Grad),
                        // textureGradOffset(gsampler, gvec P, gvec dPdx, gvec dPdy, ivec offset);
                        "textureGradOffset" => (false, true, TextureLevelType::Grad),
                        // textureLod(gsampler, gvec P, float lod);
                        "textureLod" => (false, false, TextureLevelType::Lod),
                        // textureLodOffset(gsampler, gvec P, float lod, ivec offset);
                        "textureLodOffset" => (false, true, TextureLevelType::Lod),
                        // textureOffset(gsampler, gvec+1 P, ivec offset, [float bias]);
                        "textureOffset" => (false, true, TextureLevelType::None),
                        // textureProj(gsampler, gvec+1 P, [float bias]);
                        "textureProj" => (true, false, TextureLevelType::None),
                        // textureProjGrad(gsampler, gvec+1 P, gvec dPdx, gvec dPdy);
                        "textureProjGrad" => (true, false, TextureLevelType::Grad),
                        // textureProjGradOffset(gsampler, gvec+1 P, gvec dPdx, gvec dPdy, ivec offset);
                        "textureProjGradOffset" => (true, true, TextureLevelType::Grad),
                        // textureProjLod(gsampler, gvec+1 P, float lod);
                        "textureProjLod" => (true, false, TextureLevelType::Lod),
                        // textureProjLodOffset(gsampler, gvec+1 P, gvec dPdx, gvec dPdy, ivec offset);
                        "textureProjLodOffset" => (true, true, TextureLevelType::Lod),
                        // textureProjOffset(gsampler, gvec+1 P, ivec offset, [float bias]);
                        "textureProjOffset" => (true, true, TextureLevelType::None),
                        _ => unreachable!(),
                    };

                    let builtin = MacroCall::Texture {
                        proj,
                        offset,
                        shadow,
                        level_type,
                    };

                    // Parse out the variant settings.
                    let grad = level_type == TextureLevelType::Grad;
                    let lod = level_type == TextureLevelType::Lod;

                    let supports_variant = proj && !shadow;
                    if variant && !supports_variant {
                        continue;
                    }

                    if bias && !matches!(level_type, TextureLevelType::None) {
                        continue;
                    }

                    // Proj doesn't work with arrayed or Cube
                    if proj && (arrayed || dim == Dim::Cube) {
                        continue;
                    }

                    // texture operations with offset are not supported for cube maps
                    if dim == Dim::Cube && offset {
                        continue;
                    }

                    // sampler2DArrayShadow can't be used in textureLod or in texture with bias
                    if (lod || bias) && arrayed && shadow && dim == Dim::D2 {
                        continue;
                    }

                    // TODO: glsl supports using bias with depth samplers but naga doesn't
                    if bias && shadow {
                        continue;
                    }

                    let class = match shadow {
                        true => ImageClass::Depth { multi },
                        false => ImageClass::Sampled { kind, multi },
                    };

                    let image = TypeInner::Image {
                        dim,
                        arrayed,
                        class,
                    };

                    let num_coords_from_dim = image_dims_to_coords_size(dim).min(3);
                    let mut num_coords = num_coords_from_dim;

                    if shadow && proj {
                        num_coords = 4;
                    } else if dim == Dim::D1 && shadow {
                        num_coords = 3;
                    } else if shadow {
                        num_coords += 1;
                    } else if proj {
                        if variant && num_coords == 4 {
                            // Normal form already has 4 components, no need to have a variant form.
                            continue;
                        } else if variant {
                            num_coords = 4;
                        } else {
                            num_coords += 1;
                        }
                    }

                    if !(dim == Dim::D1 && shadow) {
                        num_coords += arrayed as usize;
                    }

                    // Special case: texture(gsamplerCubeArrayShadow) kicks the shadow compare ref to a separate argument,
                    // since it would otherwise take five arguments. It also can't take a bias, nor can it be proj/grad/lod/offset
                    // (presumably because nobody asked for it, and implementation complexity?)
                    if num_coords >= 5 {
                        if lod || grad || offset || proj || bias {
                            continue;
                        }
                        debug_assert!(dim == Dim::Cube && shadow && arrayed);
                    }
                    debug_assert!(num_coords <= 5);

                    let vector = make_coords_arg(num_coords, Sk::Float);
                    let mut args = vec![image, vector];

                    if num_coords == 5 {
                        args.push(TypeInner::Scalar(Scalar::F32));
                    }

                    match level_type {
                        TextureLevelType::Lod => {
                            args.push(TypeInner::Scalar(Scalar::F32));
                        }
                        TextureLevelType::Grad => {
                            args.push(make_coords_arg(num_coords_from_dim, Sk::Float));
                            args.push(make_coords_arg(num_coords_from_dim, Sk::Float));
                        }
                        TextureLevelType::None => {}
                    };

                    if offset {
                        args.push(make_coords_arg(num_coords_from_dim, Sk::Sint));
                    }

                    if bias {
                        args.push(TypeInner::Scalar(Scalar::F32));
                    }

                    declaration
                        .overloads
                        .push(module.add_builtin(args, builtin));
                }
            };

            texture_args_generator(TextureArgsOptions::SHADOW | variations.into(), f)
        }
        "textureSize" => {
            let f = |kind, dim, arrayed, multi, shadow| {
                let class = match shadow {
                    true => ImageClass::Depth { multi },
                    false => ImageClass::Sampled { kind, multi },
                };

                let image = TypeInner::Image {
                    dim,
                    arrayed,
                    class,
                };

                let mut args = vec![image];

                if !multi {
                    args.push(TypeInner::Scalar(Scalar::I32))
                }

                declaration
                    .overloads
                    .push(module.add_builtin(args, MacroCall::TextureSize { arrayed }))
            };

            texture_args_generator(
                TextureArgsOptions::SHADOW | TextureArgsOptions::MULTI | variations.into(),
                f,
            )
        }
        "textureQueryLevels" => {
            let f = |kind, dim, arrayed, multi, shadow| {
                let class = match shadow {
                    true => ImageClass::Depth { multi },
                    false => ImageClass::Sampled { kind, multi },
                };

                let image = TypeInner::Image {
                    dim,
                    arrayed,
                    class,
                };

                declaration
                    .overloads
                    .push(module.add_builtin(vec![image], MacroCall::TextureQueryLevels))
            };

            texture_args_generator(TextureArgsOptions::SHADOW | variations.into(), f)
        }
        "texelFetch" | "texelFetchOffset" => {
            let offset = "texelFetchOffset" == name;
            let f = |kind, dim, arrayed, multi, _shadow| {
                // Cube images aren't supported
                if let Dim::Cube = dim {
                    return;
                }

                let image = TypeInner::Image {
                    dim,
                    arrayed,
                    class: ImageClass::Sampled { kind, multi },
                };

                let dim_value = image_dims_to_coords_size(dim);
                let coordinates = make_coords_arg(dim_value + arrayed as usize, Sk::Sint);

                let mut args = vec![image, coordinates, TypeInner::Scalar(Scalar::I32)];

                if offset {
                    args.push(make_coords_arg(dim_value, Sk::Sint));
                }

                declaration
                    .overloads
                    .push(module.add_builtin(args, MacroCall::ImageLoad { multi }))
            };

            // Don't generate shadow images since they aren't supported
            texture_args_generator(TextureArgsOptions::MULTI | variations.into(), f)
        }
        "imageSize" => {
            let f = |kind: Sk, dim, arrayed, _, _| {
                // Naga doesn't support cube images and it's usefulness
                // is questionable, so they won't be supported for now
                if dim == Dim::Cube {
                    return;
                }

                let image = TypeInner::Image {
                    dim,
                    arrayed,
                    class: ImageClass::Storage {
                        format: kind.dummy_storage_format(),
                        access: crate::StorageAccess::empty(),
                    },
                };

                declaration
                    .overloads
                    .push(module.add_builtin(vec![image], MacroCall::TextureSize { arrayed }))
            };

            texture_args_generator(variations.into(), f)
        }
        "imageLoad" => {
            let f = |kind: Sk, dim, arrayed, _, _| {
                // Naga doesn't support cube images and it's usefulness
                // is questionable, so they won't be supported for now
                if dim == Dim::Cube {
                    return;
                }

                let image = TypeInner::Image {
                    dim,
                    arrayed,
                    class: ImageClass::Storage {
                        format: kind.dummy_storage_format(),
                        access: crate::StorageAccess::LOAD,
                    },
                };

                let dim_value = image_dims_to_coords_size(dim);
                let mut coord_size = dim_value + arrayed as usize;
                // > Every OpenGL API call that operates on cubemap array
                // > textures takes layer-faces, not array layers
                //
                // So this means that imageCubeArray only takes a three component
                // vector coordinate and the third component is a layer index.
                if Dim::Cube == dim && arrayed {
                    coord_size = 3
                }
                let coordinates = make_coords_arg(coord_size, Sk::Sint);

                let args = vec![image, coordinates];

                declaration
                    .overloads
                    .push(module.add_builtin(args, MacroCall::ImageLoad { multi: false }))
            };

            // Don't generate shadow nor multisampled images since they aren't supported
            texture_args_generator(variations.into(), f)
        }
        "imageStore" => {
            let f = |kind: Sk, dim, arrayed, _, _| {
                // Naga doesn't support cube images and it's usefulness
                // is questionable, so they won't be supported for now
                if dim == Dim::Cube {
                    return;
                }

                let image = TypeInner::Image {
                    dim,
                    arrayed,
                    class: ImageClass::Storage {
                        format: kind.dummy_storage_format(),
                        access: crate::StorageAccess::STORE,
                    },
                };

                let dim_value = image_dims_to_coords_size(dim);
                let mut coord_size = dim_value + arrayed as usize;
                // > Every OpenGL API call that operates on cubemap array
                // > textures takes layer-faces, not array layers
                //
                // So this means that imageCubeArray only takes a three component
                // vector coordinate and the third component is a layer index.
                if Dim::Cube == dim && arrayed {
                    coord_size = 3
                }
                let coordinates = make_coords_arg(coord_size, Sk::Sint);

                let args = vec![
                    image,
                    coordinates,
                    TypeInner::Vector {
                        size: VectorSize::Quad,
                        scalar: Scalar { kind, width: 4 },
                    },
                ];

                let mut overload = module.add_builtin(args, MacroCall::ImageStore);
                overload.void = true;
                declaration.overloads.push(overload)
            };

            // Don't generate shadow nor multisampled images since they aren't supported
            texture_args_generator(variations.into(), f)
        }
        _ => {}
    }
}

/// Injects the builtins into declaration that don't need any special variations
fn inject_standard_builtins(
    declaration: &mut FunctionDeclaration,
    module: &mut Module,
    name: &str,
) {
    // Some samplers (sampler1D, etc...) can be float, int, or uint
    let anykind_sampler = if name.starts_with("sampler") {
        Some((name, Sk::Float))
    } else if name.starts_with("usampler") {
        Some((&name[1..], Sk::Uint))
    } else if name.starts_with("isampler") {
        Some((&name[1..], Sk::Sint))
    } else {
        None
    };
    if let Some((sampler, kind)) = anykind_sampler {
        match sampler {
            "sampler1D" | "sampler1DArray" | "sampler2D" | "sampler2DArray" | "sampler2DMS"
            | "sampler2DMSArray" | "sampler3D" | "samplerCube" | "samplerCubeArray" => {
                declaration.overloads.push(module.add_builtin(
                    vec![
                        TypeInner::Image {
                            dim: match sampler {
                                "sampler1D" | "sampler1DArray" => Dim::D1,
                                "sampler2D" | "sampler2DArray" | "sampler2DMS"
                                | "sampler2DMSArray" => Dim::D2,
                                "sampler3D" => Dim::D3,
                                _ => Dim::Cube,
                            },
                            arrayed: matches!(
                                sampler,
                                "sampler1DArray"
                                    | "sampler2DArray"
                                    | "sampler2DMSArray"
                                    | "samplerCubeArray"
                            ),
                            class: ImageClass::Sampled {
                                kind,
                                multi: matches!(sampler, "sampler2DMS" | "sampler2DMSArray"),
                            },
                        },
                        TypeInner::Sampler { comparison: false },
                    ],
                    MacroCall::Sampler,
                ));
                return;
            }
            _ => (),
        }
    }

    match name {
        // Shadow sampler can only be of kind `Sk::Float`
        "sampler1DShadow"
        | "sampler1DArrayShadow"
        | "sampler2DShadow"
        | "sampler2DArrayShadow"
        | "samplerCubeShadow"
        | "samplerCubeArrayShadow" => {
            let dim = match name {
                "sampler1DShadow" | "sampler1DArrayShadow" => Dim::D1,
                "sampler2DShadow" | "sampler2DArrayShadow" => Dim::D2,
                _ => Dim::Cube,
            };
            let arrayed = matches!(
                name,
                "sampler1DArrayShadow" | "sampler2DArrayShadow" | "samplerCubeArrayShadow"
            );

            for i in 0..2 {
                let ty = TypeInner::Image {
                    dim,
                    arrayed,
                    class: match i {
                        0 => ImageClass::Sampled {
                            kind: Sk::Float,
                            multi: false,
                        },
                        _ => ImageClass::Depth { multi: false },
                    },
                };

                declaration.overloads.push(module.add_builtin(
                    vec![ty, TypeInner::Sampler { comparison: true }],
                    MacroCall::SamplerShadow,
                ))
            }
        }
        "sin" | "exp" | "exp2" | "sinh" | "cos" | "cosh" | "tan" | "tanh" | "acos" | "asin"
        | "log" | "log2" | "radians" | "degrees" | "asinh" | "acosh" | "atanh"
        | "floatBitsToInt" | "floatBitsToUint" | "dFdx" | "dFdxFine" | "dFdxCoarse" | "dFdy"
        | "dFdyFine" | "dFdyCoarse" | "fwidth" | "fwidthFine" | "fwidthCoarse" => {
            // bits layout
            // bit 0 through 1 - dims
            for bits in 0..0b100 {
                let size = match bits {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };
                let scalar = Scalar::F32;

                declaration.overloads.push(module.add_builtin(
                    vec![match size {
                        Some(size) => TypeInner::Vector { size, scalar },
                        None => TypeInner::Scalar(scalar),
                    }],
                    match name {
                        "sin" => MacroCall::MathFunction(MathFunction::Sin),
                        "exp" => MacroCall::MathFunction(MathFunction::Exp),
                        "exp2" => MacroCall::MathFunction(MathFunction::Exp2),
                        "sinh" => MacroCall::MathFunction(MathFunction::Sinh),
                        "cos" => MacroCall::MathFunction(MathFunction::Cos),
                        "cosh" => MacroCall::MathFunction(MathFunction::Cosh),
                        "tan" => MacroCall::MathFunction(MathFunction::Tan),
                        "tanh" => MacroCall::MathFunction(MathFunction::Tanh),
                        "acos" => MacroCall::MathFunction(MathFunction::Acos),
                        "asin" => MacroCall::MathFunction(MathFunction::Asin),
                        "log" => MacroCall::MathFunction(MathFunction::Log),
                        "log2" => MacroCall::MathFunction(MathFunction::Log2),
                        "asinh" => MacroCall::MathFunction(MathFunction::Asinh),
                        "acosh" => MacroCall::MathFunction(MathFunction::Acosh),
                        "atanh" => MacroCall::MathFunction(MathFunction::Atanh),
                        "radians" => MacroCall::MathFunction(MathFunction::Radians),
                        "degrees" => MacroCall::MathFunction(MathFunction::Degrees),
                        "floatBitsToInt" => MacroCall::BitCast(Sk::Sint),
                        "floatBitsToUint" => MacroCall::BitCast(Sk::Uint),
                        "dFdxCoarse" => MacroCall::Derivate(Axis::X, Ctrl::Coarse),
                        "dFdyCoarse" => MacroCall::Derivate(Axis::Y, Ctrl::Coarse),
                        "fwidthCoarse" => MacroCall::Derivate(Axis::Width, Ctrl::Coarse),
                        "dFdxFine" => MacroCall::Derivate(Axis::X, Ctrl::Fine),
                        "dFdyFine" => MacroCall::Derivate(Axis::Y, Ctrl::Fine),
                        "fwidthFine" => MacroCall::Derivate(Axis::Width, Ctrl::Fine),
                        "dFdx" => MacroCall::Derivate(Axis::X, Ctrl::None),
                        "dFdy" => MacroCall::Derivate(Axis::Y, Ctrl::None),
                        "fwidth" => MacroCall::Derivate(Axis::Width, Ctrl::None),
                        _ => unreachable!(),
                    },
                ))
            }
        }
        "intBitsToFloat" | "uintBitsToFloat" => {
            // bits layout
            // bit 0 through 1 - dims
            for bits in 0..0b100 {
                let size = match bits {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };
                let scalar = match name {
                    "intBitsToFloat" => Scalar::I32,
                    _ => Scalar::U32,
                };

                declaration.overloads.push(module.add_builtin(
                    vec![match size {
                        Some(size) => TypeInner::Vector { size, scalar },
                        None => TypeInner::Scalar(scalar),
                    }],
                    MacroCall::BitCast(Sk::Float),
                ))
            }
        }
        "pow" => {
            // bits layout
            // bit 0 through 1 - dims
            for bits in 0..0b100 {
                let size = match bits {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };
                let scalar = Scalar::F32;
                let ty = || match size {
                    Some(size) => TypeInner::Vector { size, scalar },
                    None => TypeInner::Scalar(scalar),
                };

                declaration.overloads.push(
                    module
                        .add_builtin(vec![ty(), ty()], MacroCall::MathFunction(MathFunction::Pow)),
                )
            }
        }
        "abs" | "sign" => {
            // bits layout
            // bit 0 through 1 - dims
            // bit 2 - float/sint
            for bits in 0..0b1000 {
                let size = match bits & 0b11 {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };
                let scalar = match bits >> 2 {
                    0b0 => Scalar::F32,
                    _ => Scalar::I32,
                };

                let args = vec![match size {
                    Some(size) => TypeInner::Vector { size, scalar },
                    None => TypeInner::Scalar(scalar),
                }];

                declaration.overloads.push(module.add_builtin(
                    args,
                    MacroCall::MathFunction(match name {
                        "abs" => MathFunction::Abs,
                        "sign" => MathFunction::Sign,
                        _ => unreachable!(),
                    }),
                ))
            }
        }
        "bitCount" | "bitfieldReverse" | "bitfieldExtract" | "bitfieldInsert" | "findLSB"
        | "findMSB" => {
            let fun = match name {
                "bitCount" => MathFunction::CountOneBits,
                "bitfieldReverse" => MathFunction::ReverseBits,
                "bitfieldExtract" => MathFunction::ExtractBits,
                "bitfieldInsert" => MathFunction::InsertBits,
                "findLSB" => MathFunction::FirstTrailingBit,
                "findMSB" => MathFunction::FirstLeadingBit,
                _ => unreachable!(),
            };

            let mc = match fun {
                MathFunction::ExtractBits => MacroCall::BitfieldExtract,
                MathFunction::InsertBits => MacroCall::BitfieldInsert,
                _ => MacroCall::MathFunction(fun),
            };

            // bits layout
            // bit 0 - int/uint
            // bit 1 through 2 - dims
            for bits in 0..0b1000 {
                let scalar = match bits & 0b1 {
                    0b0 => Scalar::I32,
                    _ => Scalar::U32,
                };
                let size = match bits >> 1 {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };

                let ty = || match size {
                    Some(size) => TypeInner::Vector { size, scalar },
                    None => TypeInner::Scalar(scalar),
                };

                let mut args = vec![ty()];

                match fun {
                    MathFunction::ExtractBits => {
                        args.push(TypeInner::Scalar(Scalar::I32));
                        args.push(TypeInner::Scalar(Scalar::I32));
                    }
                    MathFunction::InsertBits => {
                        args.push(ty());
                        args.push(TypeInner::Scalar(Scalar::I32));
                        args.push(TypeInner::Scalar(Scalar::I32));
                    }
                    _ => {}
                }

                // we need to cast the return type of findLsb / findMsb
                let mc = if scalar.kind == Sk::Uint {
                    match mc {
                        MacroCall::MathFunction(MathFunction::FirstTrailingBit) => {
                            MacroCall::FindLsbUint
                        }
                        MacroCall::MathFunction(MathFunction::FirstLeadingBit) => {
                            MacroCall::FindMsbUint
                        }
                        mc => mc,
                    }
                } else {
                    mc
                };

                declaration.overloads.push(module.add_builtin(args, mc))
            }
        }
        "packSnorm4x8" | "packUnorm4x8" | "packSnorm2x16" | "packUnorm2x16" | "packHalf2x16" => {
            let fun = match name {
                "packSnorm4x8" => MathFunction::Pack4x8snorm,
                "packUnorm4x8" => MathFunction::Pack4x8unorm,
                "packSnorm2x16" => MathFunction::Pack2x16unorm,
                "packUnorm2x16" => MathFunction::Pack2x16snorm,
                "packHalf2x16" => MathFunction::Pack2x16float,
                _ => unreachable!(),
            };

            let ty = match fun {
                MathFunction::Pack4x8snorm | MathFunction::Pack4x8unorm => TypeInner::Vector {
                    size: VectorSize::Quad,
                    scalar: Scalar::F32,
                },
                MathFunction::Pack2x16unorm
                | MathFunction::Pack2x16snorm
                | MathFunction::Pack2x16float => TypeInner::Vector {
                    size: VectorSize::Bi,
                    scalar: Scalar::F32,
                },
                _ => unreachable!(),
            };

            let args = vec![ty];

            declaration
                .overloads
                .push(module.add_builtin(args, MacroCall::MathFunction(fun)));
        }
        "unpackSnorm4x8" | "unpackUnorm4x8" | "unpackSnorm2x16" | "unpackUnorm2x16"
        | "unpackHalf2x16" => {
            let fun = match name {
                "unpackSnorm4x8" => MathFunction::Unpack4x8snorm,
                "unpackUnorm4x8" => MathFunction::Unpack4x8unorm,
                "unpackSnorm2x16" => MathFunction::Unpack2x16snorm,
                "unpackUnorm2x16" => MathFunction::Unpack2x16unorm,
                "unpackHalf2x16" => MathFunction::Unpack2x16float,
                _ => unreachable!(),
            };

            let args = vec![TypeInner::Scalar(Scalar::U32)];

            declaration
                .overloads
                .push(module.add_builtin(args, MacroCall::MathFunction(fun)));
        }
        "atan" => {
            // bits layout
            // bit 0 - atan/atan2
            // bit 1 through 2 - dims
            for bits in 0..0b1000 {
                let fun = match bits & 0b1 {
                    0b0 => MathFunction::Atan,
                    _ => MathFunction::Atan2,
                };
                let size = match bits >> 1 {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };
                let scalar = Scalar::F32;
                let ty = || match size {
                    Some(size) => TypeInner::Vector { size, scalar },
                    None => TypeInner::Scalar(scalar),
                };

                let mut args = vec![ty()];

                if fun == MathFunction::Atan2 {
                    args.push(ty())
                }

                declaration
                    .overloads
                    .push(module.add_builtin(args, MacroCall::MathFunction(fun)))
            }
        }
        "all" | "any" | "not" => {
            // bits layout
            // bit 0 through 1 - dims
            for bits in 0..0b11 {
                let size = match bits {
                    0b00 => VectorSize::Bi,
                    0b01 => VectorSize::Tri,
                    _ => VectorSize::Quad,
                };

                let args = vec![TypeInner::Vector {
                    size,
                    scalar: Scalar::BOOL,
                }];

                let fun = match name {
                    "all" => MacroCall::Relational(RelationalFunction::All),
                    "any" => MacroCall::Relational(RelationalFunction::Any),
                    "not" => MacroCall::Unary(UnaryOperator::LogicalNot),
                    _ => unreachable!(),
                };

                declaration.overloads.push(module.add_builtin(args, fun))
            }
        }
        "lessThan" | "greaterThan" | "lessThanEqual" | "greaterThanEqual" => {
            for bits in 0..0b1001 {
                let (size, scalar) = match bits {
                    0b0000 => (VectorSize::Bi, Scalar::F32),
                    0b0001 => (VectorSize::Tri, Scalar::F32),
                    0b0010 => (VectorSize::Quad, Scalar::F32),
                    0b0011 => (VectorSize::Bi, Scalar::I32),
                    0b0100 => (VectorSize::Tri, Scalar::I32),
                    0b0101 => (VectorSize::Quad, Scalar::I32),
                    0b0110 => (VectorSize::Bi, Scalar::U32),
                    0b0111 => (VectorSize::Tri, Scalar::U32),
                    _ => (VectorSize::Quad, Scalar::U32),
                };

                let ty = || TypeInner::Vector { size, scalar };
                let args = vec![ty(), ty()];

                let fun = MacroCall::Binary(match name {
                    "lessThan" => BinaryOperator::Less,
                    "greaterThan" => BinaryOperator::Greater,
                    "lessThanEqual" => BinaryOperator::LessEqual,
                    "greaterThanEqual" => BinaryOperator::GreaterEqual,
                    _ => unreachable!(),
                });

                declaration.overloads.push(module.add_builtin(args, fun))
            }
        }
        "equal" | "notEqual" => {
            for bits in 0..0b1100 {
                let (size, scalar) = match bits {
                    0b0000 => (VectorSize::Bi, Scalar::F32),
                    0b0001 => (VectorSize::Tri, Scalar::F32),
                    0b0010 => (VectorSize::Quad, Scalar::F32),
                    0b0011 => (VectorSize::Bi, Scalar::I32),
                    0b0100 => (VectorSize::Tri, Scalar::I32),
                    0b0101 => (VectorSize::Quad, Scalar::I32),
                    0b0110 => (VectorSize::Bi, Scalar::U32),
                    0b0111 => (VectorSize::Tri, Scalar::U32),
                    0b1000 => (VectorSize::Quad, Scalar::U32),
                    0b1001 => (VectorSize::Bi, Scalar::BOOL),
                    0b1010 => (VectorSize::Tri, Scalar::BOOL),
                    _ => (VectorSize::Quad, Scalar::BOOL),
                };

                let ty = || TypeInner::Vector { size, scalar };
                let args = vec![ty(), ty()];

                let fun = MacroCall::Binary(match name {
                    "equal" => BinaryOperator::Equal,
                    "notEqual" => BinaryOperator::NotEqual,
                    _ => unreachable!(),
                });

                declaration.overloads.push(module.add_builtin(args, fun))
            }
        }
        "min" | "max" => {
            // bits layout
            // bit 0 through 1 - scalar kind
            // bit 2 through 4 - dims
            for bits in 0..0b11100 {
                let scalar = match bits & 0b11 {
                    0b00 => Scalar::F32,
                    0b01 => Scalar::I32,
                    0b10 => Scalar::U32,
                    _ => continue,
                };
                let (size, second_size) = match bits >> 2 {
                    0b000 => (None, None),
                    0b001 => (Some(VectorSize::Bi), None),
                    0b010 => (Some(VectorSize::Tri), None),
                    0b011 => (Some(VectorSize::Quad), None),
                    0b100 => (Some(VectorSize::Bi), Some(VectorSize::Bi)),
                    0b101 => (Some(VectorSize::Tri), Some(VectorSize::Tri)),
                    _ => (Some(VectorSize::Quad), Some(VectorSize::Quad)),
                };

                let args = vec![
                    match size {
                        Some(size) => TypeInner::Vector { size, scalar },
                        None => TypeInner::Scalar(scalar),
                    },
                    match second_size {
                        Some(size) => TypeInner::Vector { size, scalar },
                        None => TypeInner::Scalar(scalar),
                    },
                ];

                let fun = match name {
                    "max" => MacroCall::Splatted(MathFunction::Max, size, 1),
                    "min" => MacroCall::Splatted(MathFunction::Min, size, 1),
                    _ => unreachable!(),
                };

                declaration.overloads.push(module.add_builtin(args, fun))
            }
        }
        "mix" => {
            // bits layout
            // bit 0 through 1 - dims
            // bit 2 through 4 - types
            //
            // 0b10011 is the last element since splatted single elements
            // were already added
            for bits in 0..0b10011 {
                let size = match bits & 0b11 {
                    0b00 => Some(VectorSize::Bi),
                    0b01 => Some(VectorSize::Tri),
                    0b10 => Some(VectorSize::Quad),
                    _ => None,
                };
                let (scalar, splatted, boolean) = match bits >> 2 {
                    0b000 => (Scalar::I32, false, true),
                    0b001 => (Scalar::U32, false, true),
                    0b010 => (Scalar::F32, false, true),
                    0b011 => (Scalar::F32, false, false),
                    _ => (Scalar::F32, true, false),
                };

                let ty = |scalar| match size {
                    Some(size) => TypeInner::Vector { size, scalar },
                    None => TypeInner::Scalar(scalar),
                };
                let args = vec![
                    ty(scalar),
                    ty(scalar),
                    match (boolean, splatted) {
                        (true, _) => ty(Scalar::BOOL),
                        (_, false) => TypeInner::Scalar(scalar),
                        _ => ty(scalar),
                    },
                ];

                declaration.overloads.push(module.add_builtin(
                    args,
                    match boolean {
                        true => MacroCall::MixBoolean,
                        false => MacroCall::Splatted(MathFunction::Mix, size, 2),
                    },
                ))
            }
        }
        "clamp" => {
            // bits layout
            // bit 0 through 1 - float/int/uint
            // bit 2 through 3 - dims
            // bit 4 - splatted
            //
            // 0b11010 is the last element since splatted single elements
            // were already added
            for bits in 0..0b11011 {
                let scalar = match bits & 0b11 {
                    0b00 => Scalar::F32,
                    0b01 => Scalar::I32,
                    0b10 => Scalar::U32,
                    _ => continue,
                };
                let size = match (bits >> 2) & 0b11 {
                    0b00 => Some(VectorSize::Bi),
                    0b01 => Some(VectorSize::Tri),
                    0b10 => Some(VectorSize::Quad),
                    _ => None,
                };
                let splatted = bits & 0b10000 == 0b10000;

                let base_ty = || match size {
                    Some(size) => TypeInner::Vector { size, scalar },
                    None => TypeInner::Scalar(scalar),
                };
                let limit_ty = || match splatted {
                    true => TypeInner::Scalar(scalar),
                    false => base_ty(),
                };

                let args = vec![base_ty(), limit_ty(), limit_ty()];

                declaration
                    .overloads
                    .push(module.add_builtin(args, MacroCall::Clamp(size)))
            }
        }
        "barrier" => declaration
            .overloads
            .push(module.add_builtin(Vec::new(), MacroCall::Barrier)),
        // Add common builtins with floats
        _ => inject_common_builtin(declaration, module, name, 4),
    }
}

/// Injects the builtins into declaration that need doubles
fn inject_double_builtin(declaration: &mut FunctionDeclaration, module: &mut Module, name: &str) {
    match name {
        "abs" | "sign" => {
            // bits layout
            // bit 0 through 1 - dims
            for bits in 0..0b100 {
                let size = match bits {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };
                let scalar = Scalar::F64;

                let args = vec![match size {
                    Some(size) => TypeInner::Vector { size, scalar },
                    None => TypeInner::Scalar(scalar),
                }];

                declaration.overloads.push(module.add_builtin(
                    args,
                    MacroCall::MathFunction(match name {
                        "abs" => MathFunction::Abs,
                        "sign" => MathFunction::Sign,
                        _ => unreachable!(),
                    }),
                ))
            }
        }
        "min" | "max" => {
            // bits layout
            // bit 0 through 2 - dims
            for bits in 0..0b111 {
                let (size, second_size) = match bits {
                    0b000 => (None, None),
                    0b001 => (Some(VectorSize::Bi), None),
                    0b010 => (Some(VectorSize::Tri), None),
                    0b011 => (Some(VectorSize::Quad), None),
                    0b100 => (Some(VectorSize::Bi), Some(VectorSize::Bi)),
                    0b101 => (Some(VectorSize::Tri), Some(VectorSize::Tri)),
                    _ => (Some(VectorSize::Quad), Some(VectorSize::Quad)),
                };
                let scalar = Scalar::F64;

                let args = vec![
                    match size {
                        Some(size) => TypeInner::Vector { size, scalar },
                        None => TypeInner::Scalar(scalar),
                    },
                    match second_size {
                        Some(size) => TypeInner::Vector { size, scalar },
                        None => TypeInner::Scalar(scalar),
                    },
                ];

                let fun = match name {
                    "max" => MacroCall::Splatted(MathFunction::Max, size, 1),
                    "min" => MacroCall::Splatted(MathFunction::Min, size, 1),
                    _ => unreachable!(),
                };

                declaration.overloads.push(module.add_builtin(args, fun))
            }
        }
        "mix" => {
            // bits layout
            // bit 0 through 1 - dims
            // bit 2 through 3 - splatted/boolean
            //
            // 0b1010 is the last element since splatted with single elements
            // is equal to normal single elements
            for bits in 0..0b1011 {
                let size = match bits & 0b11 {
                    0b00 => Some(VectorSize::Quad),
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => None,
                };
                let scalar = Scalar::F64;
                let (splatted, boolean) = match bits >> 2 {
                    0b00 => (false, false),
                    0b01 => (false, true),
                    _ => (true, false),
                };

                let ty = |scalar| match size {
                    Some(size) => TypeInner::Vector { size, scalar },
                    None => TypeInner::Scalar(scalar),
                };
                let args = vec![
                    ty(scalar),
                    ty(scalar),
                    match (boolean, splatted) {
                        (true, _) => ty(Scalar::BOOL),
                        (_, false) => TypeInner::Scalar(scalar),
                        _ => ty(scalar),
                    },
                ];

                declaration.overloads.push(module.add_builtin(
                    args,
                    match boolean {
                        true => MacroCall::MixBoolean,
                        false => MacroCall::Splatted(MathFunction::Mix, size, 2),
                    },
                ))
            }
        }
        "clamp" => {
            // bits layout
            // bit 0 through 1 - dims
            // bit 2 - splatted
            //
            // 0b110 is the last element since splatted with single elements
            // is equal to normal single elements
            for bits in 0..0b111 {
                let scalar = Scalar::F64;
                let size = match bits & 0b11 {
                    0b00 => Some(VectorSize::Bi),
                    0b01 => Some(VectorSize::Tri),
                    0b10 => Some(VectorSize::Quad),
                    _ => None,
                };
                let splatted = bits & 0b100 == 0b100;

                let base_ty = || match size {
                    Some(size) => TypeInner::Vector { size, scalar },
                    None => TypeInner::Scalar(scalar),
                };
                let limit_ty = || match splatted {
                    true => TypeInner::Scalar(scalar),
                    false => base_ty(),
                };

                let args = vec![base_ty(), limit_ty(), limit_ty()];

                declaration
                    .overloads
                    .push(module.add_builtin(args, MacroCall::Clamp(size)))
            }
        }
        "lessThan" | "greaterThan" | "lessThanEqual" | "greaterThanEqual" | "equal"
        | "notEqual" => {
            let scalar = Scalar::F64;
            for bits in 0..0b11 {
                let size = match bits {
                    0b00 => VectorSize::Bi,
                    0b01 => VectorSize::Tri,
                    _ => VectorSize::Quad,
                };

                let ty = || TypeInner::Vector { size, scalar };
                let args = vec![ty(), ty()];

                let fun = MacroCall::Binary(match name {
                    "lessThan" => BinaryOperator::Less,
                    "greaterThan" => BinaryOperator::Greater,
                    "lessThanEqual" => BinaryOperator::LessEqual,
                    "greaterThanEqual" => BinaryOperator::GreaterEqual,
                    "equal" => BinaryOperator::Equal,
                    "notEqual" => BinaryOperator::NotEqual,
                    _ => unreachable!(),
                });

                declaration.overloads.push(module.add_builtin(args, fun))
            }
        }
        // Add common builtins with doubles
        _ => inject_common_builtin(declaration, module, name, 8),
    }
}

/// Injects the builtins into declaration that can used either float or doubles
fn inject_common_builtin(
    declaration: &mut FunctionDeclaration,
    module: &mut Module,
    name: &str,
    float_width: crate::Bytes,
) {
    let float_scalar = Scalar {
        kind: Sk::Float,
        width: float_width,
    };
    match name {
        "ceil" | "round" | "roundEven" | "floor" | "fract" | "trunc" | "sqrt" | "inversesqrt"
        | "normalize" | "length" | "isinf" | "isnan" => {
            // bits layout
            // bit 0 through 1 - dims
            for bits in 0..0b100 {
                let size = match bits {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };

                let args = vec![match size {
                    Some(size) => TypeInner::Vector {
                        size,
                        scalar: float_scalar,
                    },
                    None => TypeInner::Scalar(float_scalar),
                }];

                let fun = match name {
                    "ceil" => MacroCall::MathFunction(MathFunction::Ceil),
                    "round" | "roundEven" => MacroCall::MathFunction(MathFunction::Round),
                    "floor" => MacroCall::MathFunction(MathFunction::Floor),
                    "fract" => MacroCall::MathFunction(MathFunction::Fract),
                    "trunc" => MacroCall::MathFunction(MathFunction::Trunc),
                    "sqrt" => MacroCall::MathFunction(MathFunction::Sqrt),
                    "inversesqrt" => MacroCall::MathFunction(MathFunction::InverseSqrt),
                    "normalize" => MacroCall::MathFunction(MathFunction::Normalize),
                    "length" => MacroCall::MathFunction(MathFunction::Length),
                    "isinf" => MacroCall::Relational(RelationalFunction::IsInf),
                    "isnan" => MacroCall::Relational(RelationalFunction::IsNan),
                    _ => unreachable!(),
                };

                declaration.overloads.push(module.add_builtin(args, fun))
            }
        }
        "dot" | "reflect" | "distance" | "ldexp" => {
            // bits layout
            // bit 0 through 1 - dims
            for bits in 0..0b100 {
                let size = match bits {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };
                let ty = |scalar| match size {
                    Some(size) => TypeInner::Vector { size, scalar },
                    None => TypeInner::Scalar(scalar),
                };

                let fun = match name {
                    "dot" => MacroCall::MathFunction(MathFunction::Dot),
                    "reflect" => MacroCall::MathFunction(MathFunction::Reflect),
                    "distance" => MacroCall::MathFunction(MathFunction::Distance),
                    "ldexp" => MacroCall::MathFunction(MathFunction::Ldexp),
                    _ => unreachable!(),
                };

                let second_scalar = match fun {
                    MacroCall::MathFunction(MathFunction::Ldexp) => Scalar::I32,
                    _ => float_scalar,
                };

                declaration
                    .overloads
                    .push(module.add_builtin(vec![ty(float_scalar), ty(second_scalar)], fun))
            }
        }
        "transpose" => {
            // bits layout
            // bit 0 through 3 - dims
            for bits in 0..0b1001 {
                let (rows, columns) = match bits {
                    0b0000 => (VectorSize::Bi, VectorSize::Bi),
                    0b0001 => (VectorSize::Bi, VectorSize::Tri),
                    0b0010 => (VectorSize::Bi, VectorSize::Quad),
                    0b0011 => (VectorSize::Tri, VectorSize::Bi),
                    0b0100 => (VectorSize::Tri, VectorSize::Tri),
                    0b0101 => (VectorSize::Tri, VectorSize::Quad),
                    0b0110 => (VectorSize::Quad, VectorSize::Bi),
                    0b0111 => (VectorSize::Quad, VectorSize::Tri),
                    _ => (VectorSize::Quad, VectorSize::Quad),
                };

                declaration.overloads.push(module.add_builtin(
                    vec![TypeInner::Matrix {
                        columns,
                        rows,
                        scalar: float_scalar,
                    }],
                    MacroCall::MathFunction(MathFunction::Transpose),
                ))
            }
        }
        "inverse" | "determinant" => {
            // bits layout
            // bit 0 through 1 - dims
            for bits in 0..0b11 {
                let (rows, columns) = match bits {
                    0b00 => (VectorSize::Bi, VectorSize::Bi),
                    0b01 => (VectorSize::Tri, VectorSize::Tri),
                    _ => (VectorSize::Quad, VectorSize::Quad),
                };

                let args = vec![TypeInner::Matrix {
                    columns,
                    rows,
                    scalar: float_scalar,
                }];

                declaration.overloads.push(module.add_builtin(
                    args,
                    MacroCall::MathFunction(match name {
                        "inverse" => MathFunction::Inverse,
                        "determinant" => MathFunction::Determinant,
                        _ => unreachable!(),
                    }),
                ))
            }
        }
        "mod" | "step" => {
            // bits layout
            // bit 0 through 2 - dims
            for bits in 0..0b111 {
                let (size, second_size) = match bits {
                    0b000 => (None, None),
                    0b001 => (Some(VectorSize::Bi), None),
                    0b010 => (Some(VectorSize::Tri), None),
                    0b011 => (Some(VectorSize::Quad), None),
                    0b100 => (Some(VectorSize::Bi), Some(VectorSize::Bi)),
                    0b101 => (Some(VectorSize::Tri), Some(VectorSize::Tri)),
                    _ => (Some(VectorSize::Quad), Some(VectorSize::Quad)),
                };

                let mut args = Vec::with_capacity(2);
                let step = name == "step";

                for i in 0..2 {
                    let maybe_size = match i == step as u32 {
                        true => size,
                        false => second_size,
                    };

                    args.push(match maybe_size {
                        Some(size) => TypeInner::Vector {
                            size,
                            scalar: float_scalar,
                        },
                        None => TypeInner::Scalar(float_scalar),
                    })
                }

                let fun = match name {
                    "mod" => MacroCall::Mod(size),
                    "step" => MacroCall::Splatted(MathFunction::Step, size, 0),
                    _ => unreachable!(),
                };

                declaration.overloads.push(module.add_builtin(args, fun))
            }
        }
        // TODO: https://github.com/gfx-rs/naga/issues/2526
        // "modf" | "frexp" => { ... }
        "cross" => {
            let args = vec![
                TypeInner::Vector {
                    size: VectorSize::Tri,
                    scalar: float_scalar,
                },
                TypeInner::Vector {
                    size: VectorSize::Tri,
                    scalar: float_scalar,
                },
            ];

            declaration
                .overloads
                .push(module.add_builtin(args, MacroCall::MathFunction(MathFunction::Cross)))
        }
        "outerProduct" => {
            // bits layout
            // bit 0 through 3 - dims
            for bits in 0..0b1001 {
                let (size1, size2) = match bits {
                    0b0000 => (VectorSize::Bi, VectorSize::Bi),
                    0b0001 => (VectorSize::Bi, VectorSize::Tri),
                    0b0010 => (VectorSize::Bi, VectorSize::Quad),
                    0b0011 => (VectorSize::Tri, VectorSize::Bi),
                    0b0100 => (VectorSize::Tri, VectorSize::Tri),
                    0b0101 => (VectorSize::Tri, VectorSize::Quad),
                    0b0110 => (VectorSize::Quad, VectorSize::Bi),
                    0b0111 => (VectorSize::Quad, VectorSize::Tri),
                    _ => (VectorSize::Quad, VectorSize::Quad),
                };

                let args = vec![
                    TypeInner::Vector {
                        size: size1,
                        scalar: float_scalar,
                    },
                    TypeInner::Vector {
                        size: size2,
                        scalar: float_scalar,
                    },
                ];

                declaration
                    .overloads
                    .push(module.add_builtin(args, MacroCall::MathFunction(MathFunction::Outer)))
            }
        }
        "faceforward" | "fma" => {
            // bits layout
            // bit 0 through 1 - dims
            for bits in 0..0b100 {
                let size = match bits {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };

                let ty = || match size {
                    Some(size) => TypeInner::Vector {
                        size,
                        scalar: float_scalar,
                    },
                    None => TypeInner::Scalar(float_scalar),
                };
                let args = vec![ty(), ty(), ty()];

                let fun = match name {
                    "faceforward" => MacroCall::MathFunction(MathFunction::FaceForward),
                    "fma" => MacroCall::MathFunction(MathFunction::Fma),
                    _ => unreachable!(),
                };

                declaration.overloads.push(module.add_builtin(args, fun))
            }
        }
        "refract" => {
            // bits layout
            // bit 0 through 1 - dims
            for bits in 0..0b100 {
                let size = match bits {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };

                let ty = || match size {
                    Some(size) => TypeInner::Vector {
                        size,
                        scalar: float_scalar,
                    },
                    None => TypeInner::Scalar(float_scalar),
                };
                let args = vec![ty(), ty(), TypeInner::Scalar(Scalar::F32)];
                declaration
                    .overloads
                    .push(module.add_builtin(args, MacroCall::MathFunction(MathFunction::Refract)))
            }
        }
        "smoothstep" => {
            // bit 0 - splatted
            // bit 1 through 2 - dims
            for bits in 0..0b1000 {
                let splatted = bits & 0b1 == 0b1;
                let size = match bits >> 1 {
                    0b00 => None,
                    0b01 => Some(VectorSize::Bi),
                    0b10 => Some(VectorSize::Tri),
                    _ => Some(VectorSize::Quad),
                };

                if splatted && size.is_none() {
                    continue;
                }

                let base_ty = || match size {
                    Some(size) => TypeInner::Vector {
                        size,
                        scalar: float_scalar,
                    },
                    None => TypeInner::Scalar(float_scalar),
                };
                let ty = || match splatted {
                    true => TypeInner::Scalar(float_scalar),
                    false => base_ty(),
                };
                declaration.overloads.push(module.add_builtin(
                    vec![ty(), ty(), base_ty()],
                    MacroCall::SmoothStep { splatted: size },
                ))
            }
        }
        // The function isn't a builtin or we don't yet support it
        _ => {}
    }
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum TextureLevelType {
    None,
    Lod,
    Grad,
}

/// A compiler defined builtin function
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum MacroCall {
    Sampler,
    SamplerShadow,
    Texture {
        proj: bool,
        offset: bool,
        shadow: bool,
        level_type: TextureLevelType,
    },
    TextureSize {
        arrayed: bool,
    },
    TextureQueryLevels,
    ImageLoad {
        multi: bool,
    },
    ImageStore,
    MathFunction(MathFunction),
    FindLsbUint,
    FindMsbUint,
    BitfieldExtract,
    BitfieldInsert,
    Relational(RelationalFunction),
    Unary(UnaryOperator),
    Binary(BinaryOperator),
    Mod(Option<VectorSize>),
    Splatted(MathFunction, Option<VectorSize>, usize),
    MixBoolean,
    Clamp(Option<VectorSize>),
    BitCast(Sk),
    Derivate(Axis, Ctrl),
    Barrier,
    /// SmoothStep needs a separate variant because it might need it's inputs
    /// to be splatted depending on the overload
    SmoothStep {
        /// The size of the splat operation if some
        splatted: Option<VectorSize>,
    },
}

impl MacroCall {
    /// Adds the necessary expressions and statements to the passed body and
    /// finally returns the final expression with the correct result
    pub fn call(
        &self,
        frontend: &mut Frontend,
        ctx: &mut Context,
        args: &mut [Handle<Expression>],
        meta: Span,
    ) -> Result<Option<Handle<Expression>>> {
        Ok(Some(match *self {
            MacroCall::Sampler => {
                ctx.samplers.insert(args[0], args[1]);
                args[0]
            }
            MacroCall::SamplerShadow => {
                sampled_to_depth(ctx, args[0], meta, &mut frontend.errors);
                ctx.invalidate_expression(args[0], meta)?;
                ctx.samplers.insert(args[0], args[1]);
                args[0]
            }
            MacroCall::Texture {
                proj,
                offset,
                shadow,
                level_type,
            } => {
                let mut coords = args[1];

                if proj {
                    let size = match *ctx.resolve_type(coords, meta)? {
                        TypeInner::Vector { size, .. } => size,
                        _ => unreachable!(),
                    };
                    let mut right = ctx.add_expression(
                        Expression::AccessIndex {
                            base: coords,
                            index: size as u32 - 1,
                        },
                        Span::default(),
                    )?;
                    let left = if let VectorSize::Bi = size {
                        ctx.add_expression(
                            Expression::AccessIndex {
                                base: coords,
                                index: 0,
                            },
                            Span::default(),
                        )?
                    } else {
                        let size = match size {
                            VectorSize::Tri => VectorSize::Bi,
                            _ => VectorSize::Tri,
                        };
                        right = ctx.add_expression(
                            Expression::Splat { size, value: right },
                            Span::default(),
                        )?;
                        ctx.vector_resize(size, coords, Span::default())?
                    };
                    coords = ctx.add_expression(
                        Expression::Binary {
                            op: BinaryOperator::Divide,
                            left,
                            right,
                        },
                        Span::default(),
                    )?;
                }

                let extra = args.get(2).copied();
                let comps = frontend.coordinate_components(ctx, args[0], coords, extra, meta)?;

                let mut num_args = 2;

                if comps.used_extra {
                    num_args += 1;
                };

                // Parse out explicit texture level.
                let mut level = match level_type {
                    TextureLevelType::None => SampleLevel::Auto,

                    TextureLevelType::Lod => {
                        num_args += 1;

                        if shadow {
                            log::warn!("Assuming LOD {:?} is zero", args[2],);

--> --------------------

--> maximum size reached

--> --------------------

[ Dauer der Verarbeitung: 0.54 Sekunden  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge