Esempio n. 1
0
        public static StandardFunction FromContext(SSLParser.StandardFunctionContext ctx, SSLVisitor vis)
        {
            var rtype = ReflectionUtils.TranslateTypeContext(ctx.type());
            var fname = ctx.Name.Text;

            List <Param> pars = new List <Param>();

            if (ctx.Params != null)
            {
                foreach (var pctx in ctx.Params._PList)
                {
                    var pname = pctx.Name.Text;
                    var aidx  = pctx.Access?.Type ?? SSLParser.KW_IN;
                    var acs   = (aidx == SSLParser.KW_OUT) ? Access.Out : (aidx == SSLParser.KW_INOUT) ? Access.InOut : Access.In;
                    var ptype = ReflectionUtils.TranslateTypeContext(pctx.type());
                    if (ptype == ShaderType.Void)
                    {
                        vis.Error(ctx, $"The parameter '{pname}' cannot have type 'void'.");
                    }
                    var fidx = pars.FindIndex(ep => ep.Name == pname);
                    if (fidx != -1)
                    {
                        vis.Error(ctx, $"Duplicate parameter name '{pname}' in the function parameter list.");
                    }
                    pars.Add(new Param {
                        Name = pname, Type = ptype.Value, Access = acs
                    });
                }
            }

            return(new StandardFunction(fname, rtype.Value, pars.ToArray()));
        }
 private static void EnsureMatchingComponents(SSLVisitor vis, IToken token, string name, ShaderType a1c, ShaderType a2c, ShaderType a3c)
 {
     if (!a2c.CanPromoteTo(a1c))
     {
         vis.Error(token, $"The built-in function '{name}' requires a '{a1c}' compatible type for argument 2 (actual is '{a2c}').");
     }
     if ((a3c != ShaderType.Void) && !a3c.CanPromoteTo(a1c))
     {
         vis.Error(token, $"The built-in function '{name}' requires a '{a1c}' compatible type for argument 3 (actual is '{a3c}').");
     }
 }
        internal static Variable FromContext(SSLParser.VariableDeclarationContext ctx, SSLVisitor vis, VariableScope scope)
        {
            var name = ctx.Name.Text;

            if (name[0] == '$')
            {
                vis.Error(ctx, "Cannot start a variable with the character '$', this is reserved for built-in variables.");
            }
            if (name.Length > 32)
            {
                vis.Error(ctx, "Variable names cannot be longer than 32 characters.");
            }

            if (ctx.KW_FLAT() != null && scope != VariableScope.Internal)
            {
                vis.Error(ctx, "'flat' qualifier is only valid for internals.");
            }

            var type = ReflectionUtils.TranslateTypeContext(ctx.type());

            if (!type.HasValue)
            {
                vis.Error(ctx, $"Unable to convert variable '{name}' to internal type.");
            }
            if (type.Value == ShaderType.Void)
            {
                vis.Error(ctx, $"The variable '{name}' cannot be of type 'void'.");
            }

            uint?asize = null;

            if (ctx.arrayIndexer() != null)
            {
                if (!vis.TryParseArrayIndexer(ctx.arrayIndexer(), (null, null), out var aidx, out var error))
                {
                    vis.Error(ctx.arrayIndexer(), error);
                }
                if (aidx.Index2 != null)
                {
                    vis.Error(ctx.arrayIndexer(), "Cannot declare multi-dimensional arrays.");
                }
                if (!aidx.Index1.GetIntegerLiteral().HasValue)                 // Must be a literal (OR A SPEC CONSTANT TODO)
                {
                    vis.Error(ctx.arrayIndexer(), "Must use integer literals when declaring arrays.");
                }
                asize = (uint)aidx.Index1.GetIntegerLiteral().Value;
                if (asize.Value > 255)
                {
                    vis.Error(ctx.arrayIndexer(), "Cannot declare arrays larger than 255.");
                }
            }

            return(new Variable(type.Value, name, scope, false, asize, flat: (ctx.KW_FLAT() != null)));
        }
 private static void EnsureVectorSizes(SSLVisitor vis, IToken token, string name, ShaderType a1t, ShaderType a2t, ShaderType a3t)
 {
     if ((a1t != ShaderType.Void) && (a2t != ShaderType.Void) && (a2t.GetComponentCount() != a1t.GetComponentCount()))
     {
         vis.Error(token, $"The built-in function '{name}' requires matching vector sizes for arguments 1 and 2.");
     }
     if ((a1t != ShaderType.Void) && (a3t != ShaderType.Void) && (a3t.GetComponentCount() != a1t.GetComponentCount()))
     {
         vis.Error(token, $"The built-in function '{name}' requires matching vector sizes for arguments 1 and 3.");
     }
     if ((a2t != ShaderType.Void) && (a3t != ShaderType.Void) && (a3t.GetComponentCount() != a2t.GetComponentCount()))
     {
         vis.Error(token, $"The built-in function '{name}' requires matching vector sizes for arguments 2 and 3.");
     }
 }
 private static void EnsureSizeIfNotScalar(SSLVisitor vis, IToken token, string name, ShaderType vtype, ShaderType ctype, int vp, int cp)
 {
     if (!ctype.IsScalarType() && (ctype.GetComponentCount() != vtype.GetComponentCount()))
     {
         vis.Error(token, $"The built-in function '{name}' requires argument {cp} to be a scalar, or a matching vector size to argument {vp}.");
     }
 }
Esempio n. 6
0
        public Variable AddLocal(SSLParser.VariableDefinitionContext ctx, SSLVisitor vis)
        {
            var v = Variable.FromContext(ctx, vis, VariableScope.Local);

            if (!_scopes.Peek().TryAddVariable(v, out var error))
            {
                vis.Error(ctx, error);
            }
            return(v);
        }
        internal static Variable FromConstant(SSLParser.ConstantStatementContext ctx, SSLVisitor vis)
        {
            var name = ctx.Name.Text;

            if (name[0] == '$')
            {
                vis.Error(ctx, "Cannot start a variable with the character '$', this is reserved for built-in variables.");
            }
            if (name.Length > 32)
            {
                vis.Error(ctx, "Variable names cannot be longer than 32 characters.");
            }

            uint?cidx = null;

            if (ctx.SpecIndex != null)
            {
                var ival = SSLVisitor.ParseIntegerLiteral(ctx.SpecIndex.Text, out var isus, out var perror);
                if (!ival.HasValue)
                {
                    vis.Error(ctx, "Could not parse integer literal.");
                }
                if (ival.Value < 0)
                {
                    vis.Error(ctx, "Specialization constant index cannot be less than 0.");
                }
                if (ival.Value > 255)
                {
                    vis.Error(ctx, "Specialization constant index cannot be greater than 255.");
                }
                cidx = (uint)ival.Value;
            }

            var type = ReflectionUtils.TranslateTypeContext(ctx.type());

            if (!type.HasValue)
            {
                vis.Error(ctx, $"Unable to convert constant '{name}' to internal type.");
            }
            if (type == ShaderType.Void)
            {
                vis.Error(ctx, $"The variable '{name}' cannot be of type 'void'.");
            }
            if (!type.Value.IsValueType())
            {
                vis.Error(ctx, "Globals constants must be a value type.");
            }

            return(new Variable(type.Value, name, VariableScope.Constant, true, null, cidx: cidx));
        }
Esempio n. 8
0
        public Variable AddAttribute(SSLParser.VariableDeclarationContext ctx, SSLVisitor vis)
        {
            var v = Variable.FromContext(ctx, vis, VariableScope.Attribute);

            var pre = FindGlobal(v.Name);

            if (pre != null)
            {
                vis.Error(ctx, $"A variable with the name '{v.Name}' already exists in the global {v.Scope} context.");
            }

            _attributes.Add(v.Name, v);
            return(v);
        }
Esempio n. 9
0
        public StandardFunction AddFunction(SSLParser.StandardFunctionContext ctx, SSLVisitor vis)
        {
            var func = StandardFunction.FromContext(ctx, vis);

            var pre = FindFunction(func.Name);

            if (pre != null)
            {
                vis.Error(ctx, $"A function with the name '{func.Name}' already exists in the shader.");
            }

            _functions.Add(func.Name, func);
            return(func);
        }
Esempio n. 10
0
        public Variable AddConstant(SSLParser.ConstantStatementContext ctx, SSLVisitor vis)
        {
            var v = Variable.FromConstant(ctx, vis);

            var pre = FindGlobal(v.Name);

            if (pre != null)
            {
                vis.Error(ctx, $"A variable with the name '{v.Name}' already exists in the global {v.Scope} context.");
            }

            _constants.Add(v.Name, v);
            return(v);
        }
        // Creates an expression result by applying array indexers and swizzles, if present
        public static ExprResult ApplyModifiers(SSLVisitor vis, ExprResult res, SSLParser.ArrayIndexerContext actx, ITerminalNode swizzle)
        {
            bool hasa = (actx != null);
            bool hass = (swizzle != null);

            // This prevents duplication in the event that there is an ssa already, and there is no array indexer or swizzle
            if (res.HasSSA)
            {
                res = new ExprResult(res.Type, null, res.SSA.Name);
            }

            if (hasa)
            {
                var lim =
                    res.IsArray ? res.ArraySize : res.Type.IsVectorType() ? res.Type.GetComponentCount() :
                    ((res.Type == ShaderType.Mat2) ? 2u : (res.Type == ShaderType.Mat3) ? 3u : 4u);
                if (!vis.TryParseArrayIndexer(actx, (lim - 1, res.Type.IsMatrixType() ? lim - 1 : (uint?)null), out var aidx, out var error))
                {
                    vis.Error(actx, error);
                }
                if (res.IsArray)
                {
                    if (aidx.Index2 != null)
                    {
                        vis.Error(actx, "Multi-dimensional arrays are not supported.");
                    }
                    res = new ExprResult(res.Type, null, $"{res.RefText}[{aidx.Index1.RefText}]");
                }
                else if (res.Type.IsVectorType())
                {
                    if (aidx.Index2 != null)
                    {
                        vis.Error(actx, "Vectors cannot have more than one array indexer.");
                    }
                    res = new ExprResult(res.Type.GetComponentType(), null, $"{res.RefText}[{aidx.Index1.RefText}]");
                }
                else if (res.Type.IsMatrixType())
                {
                    if (aidx.Index2 == null)
                    {
                        vis.Error(actx, "Matrices must have two array indexers to access their members.");
                    }
                    res = new ExprResult(ShaderType.Float, null, $"{res.RefText}[{aidx.Index1.RefText}][{aidx.Index2.RefText}]");
                }
                else
                {
                    vis.Error(actx, "The preceeding expression cannot have array indexers applied to it.");
                }
            }

            if (hass)
            {
                if (!res.Type.IsVectorType())
                {
                    vis.Error(swizzle.Symbol, "Cannot apply a swizzle to a non-vector type.");
                }
                var stxt = swizzle.Symbol.Text.Substring(1);
                if (stxt.Length > 4)
                {
                    vis.Error(swizzle.Symbol, "A swizzle cannot have more than four components.");
                }
                foreach (var swc in stxt)
                {
                    if (!ReflectionUtils.IsSwizzleValid(res.Type, swc))
                    {
                        vis.Error(swizzle.Symbol, $"The swizzle character '{swc}' is not valid for this type.");
                    }
                }
                res = new ExprResult(res.Type.ToVectorType((uint)stxt.Length).Value, null, $"{res.RefText}.{stxt}");
            }

            return(res);
        }
        internal static Variable FromContext(SSLParser.UniformVariableContext ctx, SSLVisitor vis)
        {
            var name = ctx.Name.Text;

            if (name[0] == '$')
            {
                vis.Error(ctx, "Cannot start a variable with the character '$', this is reserved for built-in variables.");
            }
            if (name.Length > 32)
            {
                vis.Error(ctx, "Uniform names cannot be longer than 32 characters.");
            }

            var type = ReflectionUtils.TranslateTypeContext(ctx.type());

            if (!type.HasValue)
            {
                vis.Error(ctx, $"Unable to convert variable '{name}' to internal type.");
            }
            if (type == ShaderType.Void)
            {
                vis.Error(ctx, $"The variable '{name}' cannot be of type 'void'.");
            }

            ImageFormat?ifmt = null;
            uint?       si   = null;

            if (type.Value.IsImageType())
            {
                if (ctx.Qualifier?.imageLayoutQualifier() == null)
                {
                    vis.Error(ctx, "Storage image types must have a format qualifier.");
                }
                ifmt = ReflectionUtils.TranslateImageFormat(ctx.Qualifier.imageLayoutQualifier());
            }
            else if (type.Value.IsSubpassInput())
            {
                if (ctx.Qualifier?.INTEGER_LITERAL() == null)
                {
                    vis.Error(ctx, "Subpass inputs must have an index qualifier.");
                }
                var pv = SSLVisitor.ParseIntegerLiteral(ctx.Qualifier.INTEGER_LITERAL().GetText(), out var isus, out var error);
                if (!pv.HasValue)
                {
                    vis.Error(ctx, error);
                }
                if (pv.Value < 0)
                {
                    vis.Error(ctx, "Subpass input index cannot be less than 0.");
                }
                if (pv.Value > 255)
                {
                    vis.Error(ctx, "Subpass input index cannot be greater than 255.");
                }
                si = (uint)pv.Value;
            }
            else
            {
                if (ctx.Qualifier?.imageLayoutQualifier() != null || ctx.Qualifier?.INTEGER_LITERAL() != null)
                {
                    vis.Error(ctx, $"The handle type '{type}' cannot have qualifiers.");
                }
            }

            return(new Variable(type.Value, name, VariableScope.Uniform, false, null, ifmt: ifmt, si: si));
        }
        // Checks the arugments and return types for built in functions
        // Note: the visitor ensures that the correct number of args are present, we dont need to check that in this function
        public static ShaderType CheckBuiltinCall(SSLVisitor vis, IToken token, string name, int type, ExprResult[] args)
        {
            var btidx = Array.FindIndex(args, a => a.IsArray);

            if (btidx != -1)
            {
                vis.Error(token, $"Arguments to built-in functions cannot be arrays (arg {btidx+1}).");
            }

            ShaderType a1t = args[0].Type,
                       a2t = (args.Length > 1) ? args[1].Type : ShaderType.Void,
                       a3t = (args.Length > 2) ? args[2].Type : ShaderType.Void;
            ShaderType a1c = a1t.GetComponentType(),
                       a2c = a2t.GetComponentType(),
                       a3c = a3t.GetComponentType();

            if (type >= SSLParser.BIF_TEXSIZE && type <= SSLParser.BIF_TEXFETCH)             // Functions that deal with texture handles
            {
                if (!a1t.IsTextureType())
                {
                    vis.Error(token, $"The built-in function '{name}' requires a texture handle as argument 1.");
                }
                var tdim = a1t.GetTexelDim();

                if (type == SSLParser.BIF_TEXSIZE)
                {
                    return(ShaderType.Int.ToVectorType(tdim).Value);
                }
                else if (type == SSLParser.BIF_TEXTURE)
                {
                    if (!a2c.CanPromoteTo(ShaderType.Float))
                    {
                        vis.Error(token, $"The built-in function '{name}' requires a floating-point vector or scalar as argument 2.");
                    }
                    if (a2t.GetComponentCount() != tdim)
                    {
                        vis.Error(token, "The size of the texture access coordinates does not match the size of the texture.");
                    }
                }
                else
                {
                    if (a2c != ShaderType.Int)
                    {
                        vis.Error(token, $"The built-in function '{name}' requires integer texture coordinates.");
                    }
                    if (a2t.GetComponentCount() != tdim)
                    {
                        vis.Error(token, "The size of the texture access coordinates does not match the size of the texture.");
                    }
                }
                return(ShaderType.Float4);
            }
            else if (type >= SSLParser.BIF_IMAGESIZE && type <= SSLParser.BIF_IMAGESTORE)             // Functions that deal with image handles
            {
                if (!a1t.IsImageType())
                {
                    vis.Error(token, $"The built-in function '{name}' requires an image handle as argument 1.");
                }
                var tdim = a1t.GetTexelDim();

                if (type == SSLParser.BIF_IMAGESIZE)
                {
                    return(ShaderType.Int.ToVectorType(tdim).Value);
                }

                if (a2c != ShaderType.Int)
                {
                    vis.Error(token, $"The built-in function '{name}' requires integer texture coordinates.");
                }
                if (a2t.GetComponentCount() != tdim)
                {
                    vis.Error(token, "The size of the texture access coordinates does not match the size of the texture.");
                }

                var ttype = args[0].ImageFormat.Value;
                if ((type == SSLParser.BIF_IMAGESTORE) && !a3t.CanPromoteTo(ttype.GetTexelType()))
                {
                    vis.Error(token, $"The built-in function '{name}' requires the type {ttype.GetTexelType()} for argument 3.");
                }
                return((type == SSLParser.BIF_IMAGESTORE) ? ShaderType.Void : ttype.GetTexelType());
            }
            else if (type >= SSLParser.BIF_MATCOMPMUL && type <= SSLParser.BIF_DETERMINANT)             // Functions that deal with matrices
            {
                btidx = Array.FindIndex(args, a => !a.Type.IsMatrixType());
                if (btidx != -1)
                {
                    vis.Error(token, $"The built-in function '{name}' does not support non-matrix arguments (arg {btidx+1}).");
                }

                if (type == SSLParser.BIF_MATCOMPMUL)                 // 'matCompMul' function
                {
                    if (a1t != a2t)
                    {
                        vis.Error(token, $"The built-in function '{name}' expects two identically sized matrix types for both arguments.");
                    }
                    return(a1t);
                }
                else if (type == SSLParser.BIF_TRANSPOSE || type == SSLParser.BIF_INVERSE)                 // 'transpose' and 'inverse' functions
                {
                    return(a1t);
                }
                else if (type == SSLParser.BIF_DETERMINANT)                 // 'determinant' function
                {
                    return(ShaderType.Float);
                }
            }
            else if (type == SSLParser.BIF_SUBPASSLOAD)             // 'subpassLoad' function
            {
                if (!a1t.IsSubpassInput())
                {
                    vis.Error(token, "The 'subpassLoad' function requires a subpass input as an argument.");
                }
                return(ShaderType.Float4);
            }
            else             // Functions that deal with scalar and vector types
            {
                btidx = Array.FindIndex(args, a => !(a.Type.IsScalarType() || a.Type.IsVectorType()));
                if (btidx != -1)
                {
                    vis.Error(token, $"The built-in function '{name}' does not support handle or matrix arguments (arg {btidx+1}).");
                }

                if (type >= SSLParser.BIF_DEG2RAD && type <= SSLParser.BIF_FRACT)                 // trig, exponential, and 1-arg common functions
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    EnsureVectorSizes(vis, token, name, a1t, a2t, a3t);
                    return(ShaderType.Float.ToVectorType(a1t.GetComponentCount()).Value);
                }
                else if (type == SSLParser.BIF_MOD)                 // 'mod' function
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    EnsureSizeIfNotScalar(vis, token, name, a1t, a2t, 1, 2);
                    return(ShaderType.Float.ToVectorType(a1t.GetComponentCount()).Value);
                }
                else if (type == SSLParser.BIF_MIN || type == SSLParser.BIF_MAX)                 // 'min' and 'max' functions
                {
                    EnsureCastableComponents(vis, token, name, a1c, ShaderType.Void, ShaderType.Void, ShaderType.Float);
                    EnsureMatchingComponents(vis, token, name, a1c, a2c, a3c);
                    EnsureSizeIfNotScalar(vis, token, name, a1t, a2t, 1, 2);
                    return(a1t);
                }
                else if (type == SSLParser.BIF_CLAMP)                 // 'clamp' function
                {
                    EnsureCastableComponents(vis, token, name, a1c, ShaderType.Void, ShaderType.Void, ShaderType.Float);
                    EnsureMatchingComponents(vis, token, name, a1c, a2c, a3c);
                    EnsureVectorSizes(vis, token, name, ShaderType.Void, a2t, a3t);
                    EnsureSizeIfNotScalar(vis, token, name, a1t, a2t, 1, 2);
                    return(a1t);
                }
                else if (type == SSLParser.BIF_MIX)                 // 'mix' function, unlike GLSL this only supports floating points
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    EnsureVectorSizes(vis, token, name, a1t, a2t, ShaderType.Void);
                    EnsureSizeIfNotScalar(vis, token, name, a1t, a3t, 1, 3);
                    return(ShaderType.Float.ToVectorType(a1t.GetComponentCount()).Value);
                }
                else if (type == SSLParser.BIF_STEP)                 // 'step' function
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    EnsureSizeIfNotScalar(vis, token, name, a2t, a1t, 2, 1);
                    return(ShaderType.Float.ToVectorType(a1t.GetComponentCount()).Value);
                }
                else if (type == SSLParser.BIF_SSTEP)                 // 'smoothstep' function
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    EnsureVectorSizes(vis, token, name, a1t, a2t, a3t);
                    EnsureSizeIfNotScalar(vis, token, name, a2t, a1t, 2, 1);
                    return(ShaderType.Float.ToVectorType(a3t.GetComponentCount()).Value);
                }
                else if (type == SSLParser.BIF_LENGTH)                 // 'length' function
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    return(ShaderType.Float);
                }
                else if (type == SSLParser.BIF_DISTANCE || type == SSLParser.BIF_DOT)                 // 'distance' and 'dot' functions
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    EnsureVectorSizes(vis, token, name, a1t, a2t, a3t);
                    return(ShaderType.Float);
                }
                else if (type == SSLParser.BIF_CROSS)                 // 'cross' function
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    if (a1t.GetComponentCount() != 3 || a2t.GetComponentCount() != 3)
                    {
                        vis.Error(token, $"The built-in function '{name}' expects 3-component floating-point vector arguments.");
                    }
                    return(ShaderType.Float3);
                }
                else if (type == SSLParser.BIF_NORMALIZE)                 // 'normalize' function
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    return(ShaderType.Float.ToVectorType(a1t.GetComponentCount()).Value);
                }
                else if (type == SSLParser.BIF_FFORWARD)                 // 'faceforward' function
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    EnsureVectorSizes(vis, token, name, a1t, a2t, a3t);
                    return(ShaderType.Float.ToVectorType(a1t.GetComponentCount()).Value);
                }
                else if (type == SSLParser.BIF_REFLECT)                 // 'reflect' function
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    EnsureVectorSizes(vis, token, name, a1t, a2t, a3t);
                    return(ShaderType.Float.ToVectorType(a1t.GetComponentCount()).Value);
                }
                else if (type == SSLParser.BIF_REFRACT)                 // 'refract' function
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    EnsureVectorSizes(vis, token, name, a1t, a2t, ShaderType.Void);
                    if (!a3t.CanPromoteTo(ShaderType.Float))
                    {
                        vis.Error(token, $"The built-in function '{name}' expects a floating-point scalar for argument 3.");
                    }
                    return(ShaderType.Float.ToVectorType(a1t.GetComponentCount()).Value);
                }
                else if (type >= SSLParser.BIF_VECLT && type <= SSLParser.BIF_VECGE)                 // 2-arg relational vector functions
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Float);
                    EnsureVectorSizes(vis, token, name, a1t, a2t, a3t);
                    return(ShaderType.Bool.ToVectorType(a1t.GetComponentCount()).Value);
                }
                else if (type == SSLParser.BIF_VECEQ || type == SSLParser.BIF_VECNE)                 // 2-arg logical vector functions
                {
                    if ((a1c == ShaderType.Bool) || a1c.CanPromoteTo(ShaderType.Float))
                    {
                        EnsureMatchingComponents(vis, token, name, a1c, a2c, a3c);
                    }
                    else
                    {
                        vis.Error(token, $"The built-in function '{name}' expects floating-point or boolean vectors for all arguments.");
                    }
                    EnsureVectorSizes(vis, token, name, a1t, a2t, a3t);
                    return(ShaderType.Bool.ToVectorType(a1t.GetComponentCount()).Value);
                }
                else if (type >= SSLParser.BIF_VECANY && type <= SSLParser.BIF_VECNOT)                 // 1-arg logical vector functions
                {
                    EnsureCastableComponents(vis, token, name, a1c, a2c, a3c, ShaderType.Bool);
                    return(ShaderType.Bool);
                }
            }

            // Error
            vis.Error(token, $"The built-in function '{name}' was not understood.");
            return(ShaderType.Void);
        }