diff options
Diffstat (limited to 'servers/rendering/shader_language.cpp')
-rw-r--r-- | servers/rendering/shader_language.cpp | 4958 |
1 files changed, 3408 insertions, 1550 deletions
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 6c835fcadf..d803d5fcf8 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,9 +30,11 @@ #include "shader_language.h" #include "core/os/os.h" -#include "core/print_string.h" +#include "core/string/print_string.h" #include "servers/rendering_server.h" +#define HAS_WARNING(flag) (warning_flags & flag) + static bool _is_text_char(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } @@ -223,7 +225,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { String ShaderLanguage::get_token_text(Token p_token) { String name = token_names[p_token.type]; - if (p_token.type == TK_INT_CONSTANT || p_token.type == TK_REAL_CONSTANT) { + if (p_token.type == TK_INT_CONSTANT || p_token.type == TK_FLOAT_CONSTANT) { name += "(" + rtos(p_token.constant) + ")"; } else if (p_token.type == TK_IDENTIFIER) { name += "(" + String(p_token.text) + ")"; @@ -558,13 +560,13 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { return _make_token(TK_ERROR, "Invalid numeric constant"); } hexa_found = true; - } else if (GETCHAR(i) == 'e') { - if (hexa_found || exponent_found || float_suffix_found) { + } else if (GETCHAR(i) == 'e' && !hexa_found) { + if (exponent_found || float_suffix_found) { return _make_token(TK_ERROR, "Invalid numeric constant"); } exponent_found = true; - } else if (GETCHAR(i) == 'f') { - if (hexa_found || exponent_found) { + } else if (GETCHAR(i) == 'f' && !hexa_found) { + if (exponent_found) { return _make_token(TK_ERROR, "Invalid numeric constant"); } float_suffix_found = true; @@ -629,7 +631,7 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { if (!_is_number(last_char)) { return _make_token(TK_ERROR, "Invalid (integer) numeric constant"); } - if (!str.is_valid_integer()) { + if (!str.is_valid_int()) { return _make_token(TK_ERROR, "Invalid numeric constant"); } } @@ -637,13 +639,13 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { char_idx += str.length(); Token tk; if (period_found || exponent_found || float_suffix_found) { - tk.type = TK_REAL_CONSTANT; + tk.type = TK_FLOAT_CONSTANT; } else { tk.type = TK_INT_CONSTANT; } if (hexa_found) { - tk.constant = (double)str.hex_to_int(true); + tk.constant = (double)str.hex_to_int(); } else { tk.constant = str.to_float(); } @@ -901,6 +903,8 @@ bool ShaderLanguage::is_token_nonvoid_datatype(TokenType p_type) { void ShaderLanguage::clear() { current_function = StringName(); + last_name = StringName(); + last_type = IDENTIFIER_MAX; completion_type = COMPLETION_NONE; completion_block = nullptr; @@ -908,11 +912,24 @@ void ShaderLanguage::clear() { completion_class = SubClassTag::TAG_GLOBAL; completion_struct = StringName(); + unknown_varying_usages.clear(); + +#ifdef DEBUG_ENABLED + used_constants.clear(); + used_varyings.clear(); + used_uniforms.clear(); + used_functions.clear(); + used_structs.clear(); + used_local_vars.clear(); + warnings.clear(); +#endif // DEBUG_ENABLED + error_line = 0; tk_line = 1; char_idx = 0; error_set = false; error_str = ""; + last_const = false; while (nodes) { Node *n = nodes; nodes = nodes->next; @@ -920,7 +937,43 @@ void ShaderLanguage::clear() { } } -bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name) { +#ifdef DEBUG_ENABLED +void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, IdentifierType p_type, const StringName &p_function) { + switch (p_type) { + case IdentifierType::IDENTIFIER_CONSTANT: + if (HAS_WARNING(ShaderWarning::UNUSED_CONSTANT_FLAG) && used_constants.has(p_identifier)) { + used_constants[p_identifier].used = true; + } + break; + case IdentifierType::IDENTIFIER_VARYING: + if (HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG) && used_varyings.has(p_identifier)) { + if (shader->varyings[p_identifier].stage != ShaderNode::Varying::STAGE_VERTEX && shader->varyings[p_identifier].stage != ShaderNode::Varying::STAGE_FRAGMENT) { + used_varyings[p_identifier].used = true; + } + } + break; + case IdentifierType::IDENTIFIER_UNIFORM: + if (HAS_WARNING(ShaderWarning::UNUSED_UNIFORM_FLAG) && used_uniforms.has(p_identifier)) { + used_uniforms[p_identifier].used = true; + } + break; + case IdentifierType::IDENTIFIER_FUNCTION: + if (HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG) && used_functions.has(p_identifier)) { + used_functions[p_identifier].used = true; + } + break; + case IdentifierType::IDENTIFIER_LOCAL_VAR: + if (HAS_WARNING(ShaderWarning::UNUSED_LOCAL_VARIABLE_FLAG) && used_local_vars.has(p_function) && used_local_vars[p_function].has(p_identifier)) { + used_local_vars[p_function][p_identifier].used = true; + } + break; + default: + break; + } +} +#endif // DEBUG_ENABLED + +bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name, ConstantNode::Value *r_constant_value) { if (p_function_info.built_ins.has(p_identifier)) { if (r_data_type) { *r_data_type = p_function_info.built_ins[p_identifier].type; @@ -931,7 +984,6 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_type) { *r_type = IDENTIFIER_BUILTIN_VAR; } - return true; } @@ -945,7 +997,6 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_type) { *r_type = IDENTIFIER_FUNCTION; } - return true; } @@ -962,13 +1013,15 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_array_size) { *r_array_size = p_block->variables[p_identifier].array_size; } - if (r_type) { - *r_type = IDENTIFIER_LOCAL_VAR; - } if (r_struct_name) { *r_struct_name = p_block->variables[p_identifier].struct_name; } - + if (r_constant_value) { + *r_constant_value = p_block->variables[p_identifier].value; + } + if (r_type) { + *r_type = IDENTIFIER_LOCAL_VAR; + } return true; } @@ -990,15 +1043,18 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_data_type) { *r_data_type = function->arguments[i].type; } - if (r_type) { - *r_type = IDENTIFIER_FUNCTION_ARGUMENT; - } if (r_struct_name) { *r_struct_name = function->arguments[i].type_str; } + if (r_array_size) { + *r_array_size = function->arguments[i].array_size; + } if (r_is_const) { *r_is_const = function->arguments[i].is_const; } + if (r_type) { + *r_type = IDENTIFIER_FUNCTION_ARGUMENT; + } return true; } } @@ -1021,6 +1077,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_data_type) { *r_data_type = shader->uniforms[p_identifier].type; } + if (r_array_size) { + *r_array_size = shader->uniforms[p_identifier].array_size; + } if (r_type) { *r_type = IDENTIFIER_UNIFORM; } @@ -1028,18 +1087,26 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea } if (shader->constants.has(p_identifier)) { + if (r_is_const) { + *r_is_const = true; + } if (r_data_type) { *r_data_type = shader->constants[p_identifier].type; } if (r_array_size) { *r_array_size = shader->constants[p_identifier].array_size; } - if (r_type) { - *r_type = IDENTIFIER_CONSTANT; - } if (r_struct_name) { *r_struct_name = shader->constants[p_identifier].type_str; } + if (r_constant_value) { + if (shader->constants[p_identifier].initializer && shader->constants[p_identifier].initializer->values.size() == 1) { + *r_constant_value = shader->constants[p_identifier].initializer->values[0]; + } + } + if (r_type) { + *r_type = IDENTIFIER_CONSTANT; + } return true; } @@ -1052,6 +1119,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_data_type) { *r_data_type = shader->functions[i].function->return_type; } + if (r_array_size) { + *r_array_size = shader->functions[i].function->return_array_size; + } if (r_type) { *r_type = IDENTIFIER_FUNCTION; } @@ -1062,13 +1132,18 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea return false; } -bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type) { +bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) { bool valid = false; DataType ret_type = TYPE_VOID; + int ret_size = 0; switch (p_op->op) { case OP_EQUAL: case OP_NOT_EQUAL: { + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); valid = na == nb; @@ -1078,6 +1153,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type case OP_LESS_EQUAL: case OP_GREATER: case OP_GREATER_EQUAL: { + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1087,6 +1166,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } break; case OP_AND: case OP_OR: { + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1095,6 +1178,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } break; case OP_NOT: { + if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); valid = na == TYPE_BOOL; ret_type = TYPE_BOOL; @@ -1105,6 +1192,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type case OP_POST_INCREMENT: case OP_POST_DECREMENT: case OP_NEGATE: { + if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); valid = na > TYPE_BOOL && na < TYPE_MAT2; ret_type = na; @@ -1113,6 +1204,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type case OP_SUB: case OP_MUL: case OP_DIV: { + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1181,6 +1276,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type * component-wise. */ + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1233,6 +1332,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type case OP_ASSIGN_SHIFT_RIGHT: case OP_SHIFT_LEFT: case OP_SHIFT_RIGHT: { + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1281,6 +1384,18 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } } break; case OP_ASSIGN: { + int sa = 0; + int sb = 0; + if (!p_op->arguments[0]->is_indexed()) { + sa = p_op->arguments[0]->get_array_size(); + } + if (!p_op->arguments[1]->is_indexed()) { + sb = p_op->arguments[1]->get_array_size(); + } + if (sa != sb) { + break; // don't accept arrays if their sizes are not equal + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na == TYPE_STRUCT || nb == TYPE_STRUCT) { @@ -1289,16 +1404,29 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type valid = na == nb; } ret_type = na; + ret_size = sa; } break; case OP_ASSIGN_ADD: case OP_ASSIGN_SUB: case OP_ASSIGN_MUL: case OP_ASSIGN_DIV: { + int sa = 0; + int sb = 0; + if (!p_op->arguments[0]->is_indexed()) { + sa = p_op->arguments[0]->get_array_size(); + } + if (!p_op->arguments[1]->is_indexed()) { + sb = p_op->arguments[1]->get_array_size(); + } + if (sa > 0 || sb > 0) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na == nb) { - valid = (na > TYPE_BOOL && na < TYPE_MAT2) || (p_op->op == OP_ASSIGN_MUL && na >= TYPE_MAT2 && na <= TYPE_MAT4); + valid = (na > TYPE_BOOL && na <= TYPE_MAT4); ret_type = na; } else if (na == TYPE_IVEC2 && nb == TYPE_INT) { valid = true; @@ -1361,6 +1489,18 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type * must match. */ + int sa = 0; + int sb = 0; + if (!p_op->arguments[0]->is_indexed()) { + sa = p_op->arguments[0]->get_array_size(); + } + if (!p_op->arguments[1]->is_indexed()) { + sb = p_op->arguments[1]->get_array_size(); + } + if (sa > 0 || sb > 0) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1415,17 +1555,34 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } } break; case OP_BIT_INVERT: { //unaries + if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); valid = na >= TYPE_INT && na < TYPE_FLOAT; ret_type = na; } break; case OP_SELECT_IF: { + int sa = 0; + int sb = 0; + if (!p_op->arguments[1]->is_indexed()) { + sa = p_op->arguments[1]->get_array_size(); + } + if (!p_op->arguments[2]->is_indexed()) { + sb = p_op->arguments[2]->get_array_size(); + } + if (sa != sb) { + break; // don't accept arrays if their sizes are not equal + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); DataType nc = p_op->arguments[2]->get_datatype(); valid = na == TYPE_BOOL && (nb == nc); ret_type = nb; + ret_size = sa; } break; default: { ERR_FAIL_V(false); @@ -1435,735 +1592,1066 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type if (r_ret_type) { *r_ret_type = ret_type; } + if (r_ret_size) { + *r_ret_size = ret_size; + } return valid; } const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { - //constructors - { "bool", TYPE_BOOL, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec2", TYPE_BVEC2, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec2", TYPE_BVEC2, { TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_BVEC2, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BVEC2, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BVEC2, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BOOL, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BVEC3, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "float", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec2", TYPE_VEC2, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec2", TYPE_VEC2, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_VEC2, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "int", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec2", TYPE_IVEC2, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec2", TYPE_IVEC2, { TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_INT, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "uint", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec2", TYPE_UVEC2, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec2", TYPE_UVEC2, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UVEC2, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UINT, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - - { "mat2", TYPE_MAT2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat3", TYPE_MAT3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat4", TYPE_MAT4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "mat2", TYPE_MAT2, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat3", TYPE_MAT3, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat4", TYPE_MAT4, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - //conversion scalars - - { "int", TYPE_INT, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "int", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "int", TYPE_INT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "int", TYPE_INT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "float", TYPE_FLOAT, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "float", TYPE_FLOAT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "float", TYPE_FLOAT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "float", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "uint", TYPE_UINT, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, true }, - { "uint", TYPE_UINT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uint", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uint", TYPE_UINT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "bool", TYPE_BOOL, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bool", TYPE_BOOL, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "bool", TYPE_BOOL, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "bool", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - //conversion vectors - - { "ivec2", TYPE_IVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec2", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec2", TYPE_IVEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec2", TYPE_IVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "vec2", TYPE_VEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec2", TYPE_VEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec2", TYPE_VEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "vec2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "uvec2", TYPE_UVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec2", TYPE_UVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec2", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec2", TYPE_UVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - - { "bvec2", TYPE_BVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec2", TYPE_BVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec2", TYPE_BVEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "bvec2", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "ivec3", TYPE_IVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "ivec3", TYPE_IVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - - { "vec3", TYPE_VEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "vec3", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - - { "uvec3", TYPE_UVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - - { "bvec3", TYPE_BVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "bvec3", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - - { "ivec4", TYPE_IVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "ivec4", TYPE_IVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "vec4", TYPE_VEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "vec4", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "uvec4", TYPE_UVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "bvec4", TYPE_BVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "bvec4", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - //conversion between matrixes - - { "mat2", TYPE_MAT2, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat2", TYPE_MAT2, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat3", TYPE_MAT3, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat3", TYPE_MAT3, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat4", TYPE_MAT4, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat4", TYPE_MAT4, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, - - //builtins - trigonometry - - { "radians", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "radians", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "radians", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "radians", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "degrees", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "degrees", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "degrees", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "degrees", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "sin", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "sin", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "sin", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "sin", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "cos", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "cos", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "cos", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "cos", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "tan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "tan", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "tan", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "tan", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "asin", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "asin", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "asin", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "asin", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "acos", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "acos", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "acos", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "acos", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "atan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "sinh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "sinh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "sinh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "sinh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "cosh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "cosh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "cosh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "cosh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "tanh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "tanh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "tanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "tanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "asinh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "asinh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "asinh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "asinh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "acosh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "acosh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "acosh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "acosh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "atanh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "atanh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "atanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "atanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - //builtins - exponential - { "pow", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "pow", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "pow", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "pow", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "log", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "log", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "log", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "log", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "log2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "log2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "log2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "log2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "sqrt", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "sqrt", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "sqrt", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "sqrt", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "inversesqrt", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "inversesqrt", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "inversesqrt", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "inversesqrt", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - //builtins - common - { "abs", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "abs", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "sign", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "sign", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "floor", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "floor", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "floor", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "floor", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "trunc", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "trunc", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "trunc", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "trunc", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "round", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "round", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "round", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "round", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "roundEven", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "roundEven", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "roundEven", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "roundEven", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "ceil", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ceil", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ceil", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "ceil", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "fract", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "fract", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "fract", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "fract", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "mod", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "modf", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "modf", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "modf", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "modf", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "min", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "min", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "min", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "max", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "max", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "max", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "clamp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "clamp", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "clamp", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "mix", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "step", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC2, { TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC3, { TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC2, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC3, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "isnan", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "isnan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "isnan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "isnan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "isinf", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "isinf", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "isinf", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "isinf", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "floatBitsToInt", TYPE_INT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToInt", TYPE_IVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToInt", TYPE_IVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToInt", TYPE_IVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "floatBitsToUint", TYPE_UINT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToUint", TYPE_UVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToUint", TYPE_UVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToUint", TYPE_UVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "intBitsToFloat", TYPE_FLOAT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "intBitsToFloat", TYPE_VEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "intBitsToFloat", TYPE_VEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "intBitsToFloat", TYPE_VEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "uintBitsToFloat", TYPE_FLOAT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uintBitsToFloat", TYPE_VEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uintBitsToFloat", TYPE_VEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "uintBitsToFloat", TYPE_VEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - //builtins - geometric - { "length", TYPE_FLOAT, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "length", TYPE_FLOAT, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "length", TYPE_FLOAT, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "distance", TYPE_FLOAT, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "distance", TYPE_FLOAT, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "distance", TYPE_FLOAT, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "dot", TYPE_FLOAT, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "dot", TYPE_FLOAT, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "dot", TYPE_FLOAT, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "cross", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "normalize", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "normalize", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "normalize", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "reflect", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "refract", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "faceforward", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "faceforward", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "faceforward", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "matrixCompMult", TYPE_MAT2, { TYPE_MAT2, TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "matrixCompMult", TYPE_MAT3, { TYPE_MAT3, TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, - { "matrixCompMult", TYPE_MAT4, { TYPE_MAT4, TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "outerProduct", TYPE_MAT2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "outerProduct", TYPE_MAT3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "outerProduct", TYPE_MAT4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "transpose", TYPE_MAT2, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "transpose", TYPE_MAT3, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, - { "transpose", TYPE_MAT4, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "determinant", TYPE_FLOAT, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "determinant", TYPE_FLOAT, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, - { "determinant", TYPE_FLOAT, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "inverse", TYPE_MAT2, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "inverse", TYPE_MAT3, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, - { "inverse", TYPE_MAT4, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "lessThan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "lessThan", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThan", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThan", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "lessThan", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "lessThan", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "lessThan", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "greaterThan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "greaterThan", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThan", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThan", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "greaterThan", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "greaterThan", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "greaterThan", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "lessThanEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThanEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThanEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "lessThanEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThanEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThanEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "lessThanEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "lessThanEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "lessThanEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "greaterThanEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThanEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThanEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "greaterThanEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThanEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThanEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "greaterThanEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "greaterThanEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "greaterThanEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "equal", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "equal", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "equal", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "equal", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "equal", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "equal", TYPE_BVEC2, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC3, { TYPE_BVEC3, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC4, { TYPE_BVEC4, TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "notEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "notEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "notEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "notEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "notEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "notEqual", TYPE_BVEC2, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC3, { TYPE_BVEC3, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC4, { TYPE_BVEC4, TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "any", TYPE_BOOL, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "any", TYPE_BOOL, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "any", TYPE_BOOL, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "all", TYPE_BOOL, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "all", TYPE_BOOL, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "all", TYPE_BOOL, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "not", TYPE_BVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "not", TYPE_BVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "not", TYPE_BVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - //builtins - texture - { "textureSize", TYPE_IVEC2, { TYPE_SAMPLER2D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC2, { TYPE_ISAMPLER2D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC2, { TYPE_USAMPLER2D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER3D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER3D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER3D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC2, { TYPE_SAMPLERCUBE, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC2, { TYPE_SAMPLERCUBEARRAY, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "texture", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "textureLod", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - - { "dFdx", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdx", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdx", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdx", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "dFdy", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdy", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdy", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdy", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "fwidth", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "fwidth", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "fwidth", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "fwidth", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - //sub-functions - - //array - { "length", TYPE_INT, { TYPE_VOID }, TAG_ARRAY, true }, - - // modern functions - - { "fma", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "fma", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "fma", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "fma", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { nullptr, TYPE_VOID, { TYPE_VOID }, TAG_GLOBAL, false } + // Constructors. + + { "bool", TYPE_BOOL, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BVEC2, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BVEC2, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BVEC2, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BOOL, TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BVEC3, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "float", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_VEC2, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "int", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_INT, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_INT, TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_IVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "uint", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC2, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UINT, TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + + { "mat2", TYPE_MAT2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "mat2", TYPE_MAT2, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + // Conversion scalars. + + { "int", TYPE_INT, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "int", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "int", TYPE_INT, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "int", TYPE_INT, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "float", TYPE_FLOAT, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "float", TYPE_FLOAT, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "float", TYPE_FLOAT, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "float", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "uint", TYPE_UINT, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uint", TYPE_UINT, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uint", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uint", TYPE_UINT, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + + { "bool", TYPE_BOOL, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bool", TYPE_BOOL, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bool", TYPE_BOOL, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "bool", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + // Conversion vectors. + + { "ivec2", TYPE_IVEC2, { TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "vec2", TYPE_VEC2, { TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "vec2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "uvec2", TYPE_UVEC2, { TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + + { "bvec2", TYPE_BVEC2, { TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "bvec2", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "ivec3", TYPE_IVEC3, { TYPE_BVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_UVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "ivec3", TYPE_IVEC3, { TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "vec3", TYPE_VEC3, { TYPE_BVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_IVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_UVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "vec3", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "uvec3", TYPE_UVEC3, { TYPE_BVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_IVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + + { "bvec3", TYPE_BVEC3, { TYPE_BVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_IVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_UVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "bvec3", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "ivec4", TYPE_IVEC4, { TYPE_BVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_UVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "ivec4", TYPE_IVEC4, { TYPE_VEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "vec4", TYPE_VEC4, { TYPE_BVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_IVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_UVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "vec4", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "uvec4", TYPE_UVEC4, { TYPE_BVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_IVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_VEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + + { "bvec4", TYPE_BVEC4, { TYPE_BVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_IVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_UVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "bvec4", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + // Conversion between matrixes. + + { "mat2", TYPE_MAT2, { TYPE_MAT3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat2", TYPE_MAT2, { TYPE_MAT4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_MAT2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_MAT4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_MAT2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_MAT3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + // Built-ins - trigonometric functions. + // radians + + { "radians", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "degrees" }, TAG_GLOBAL, false }, + { "radians", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "degrees" }, TAG_GLOBAL, false }, + { "radians", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "degrees" }, TAG_GLOBAL, false }, + { "radians", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "degrees" }, TAG_GLOBAL, false }, + + // degrees + + { "degrees", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "radians" }, TAG_GLOBAL, false }, + { "degrees", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "radians" }, TAG_GLOBAL, false }, + { "degrees", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "radians" }, TAG_GLOBAL, false }, + { "degrees", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "radians" }, TAG_GLOBAL, false }, + + // sin + + { "sin", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "sin", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "sin", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "sin", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + + // cos + + { "cos", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "cos", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "cos", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "cos", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + + // tan + + { "tan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "tan", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "tan", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "tan", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + + // asin + + { "asin", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asin", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asin", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asin", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // acos + + { "acos", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acos", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acos", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acos", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // atan + + { "atan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "y_over_x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "y_over_x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "y_over_x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "y_over_x" }, TAG_GLOBAL, false }, + { "atan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "y", "x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "y", "x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "y", "x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "y", "x" }, TAG_GLOBAL, false }, + + // sinh + + { "sinh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sinh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sinh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sinh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // cosh + + { "cosh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "cosh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "cosh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "cosh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // tanh + + { "tanh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "tanh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "tanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "tanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // asinh + + { "asinh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asinh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asinh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asinh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // acosh + + { "acosh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acosh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acosh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acosh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // atanh + + { "atanh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "atanh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "atanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "atanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // Builtins - exponential functions. + // pow + + { "pow", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "pow", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "pow", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "pow", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + + // exp + + { "exp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // log + + { "log", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // exp2 + + { "exp2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // log2 + + { "log2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // sqrt + + { "sqrt", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sqrt", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sqrt", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sqrt", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // inversesqrt + + { "inversesqrt", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "inversesqrt", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "inversesqrt", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "inversesqrt", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // Built-ins - common functions. + // abs + + { "abs", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "abs", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // sign + + { "sign", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "sign", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // floor + + { "floor", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "floor", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "floor", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "floor", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // trunc + + { "trunc", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "trunc", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "trunc", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "trunc", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // round + + { "round", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "round", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "round", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "round", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // roundEven + + { "roundEven", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "roundEven", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "roundEven", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "roundEven", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // ceil + + { "ceil", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "ceil", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "ceil", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "ceil", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // fract + + { "fract", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "fract", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "fract", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "fract", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // mod + + { "mod", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + + // modf + + { "modf", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "i" }, TAG_GLOBAL, true }, + { "modf", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "x", "i" }, TAG_GLOBAL, true }, + { "modf", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "x", "i" }, TAG_GLOBAL, true }, + { "modf", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "x", "i" }, TAG_GLOBAL, true }, + + // min + + { "min", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "min", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "min", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + // max + + { "max", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "max", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "max", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + // clamp + + { "clamp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + + { "clamp", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + + { "clamp", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + + // mix + + { "mix", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_BVEC2, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_BVEC3, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_BVEC4, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + + // step + + { "step", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC2, { TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC3, { TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC4, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + + // smoothstep + + { "smoothstep", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC2, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC3, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC4, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + + // isnan + + { "isnan", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isnan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isnan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isnan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // isinf + + { "isinf", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isinf", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isinf", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isinf", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // floatBitsToInt + + { "floatBitsToInt", TYPE_INT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToInt", TYPE_IVEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToInt", TYPE_IVEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToInt", TYPE_IVEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + + // floatBitsToUint + + { "floatBitsToUint", TYPE_UINT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToUint", TYPE_UVEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToUint", TYPE_UVEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToUint", TYPE_UVEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + + // intBitsToFloat + + { "intBitsToFloat", TYPE_FLOAT, { TYPE_INT, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "intBitsToFloat", TYPE_VEC2, { TYPE_IVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "intBitsToFloat", TYPE_VEC3, { TYPE_IVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "intBitsToFloat", TYPE_VEC4, { TYPE_IVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + + // uintBitsToFloat + + { "uintBitsToFloat", TYPE_FLOAT, { TYPE_UINT, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "uintBitsToFloat", TYPE_VEC2, { TYPE_UVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "uintBitsToFloat", TYPE_VEC3, { TYPE_UVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "uintBitsToFloat", TYPE_VEC4, { TYPE_UVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + + // Built-ins - geometric functions. + // length + + { "length", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "length", TYPE_FLOAT, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "length", TYPE_FLOAT, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "length", TYPE_FLOAT, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // distance + + { "distance", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "distance", TYPE_FLOAT, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "distance", TYPE_FLOAT, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "distance", TYPE_FLOAT, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + // dot + + { "dot", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "dot", TYPE_FLOAT, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "dot", TYPE_FLOAT, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "dot", TYPE_FLOAT, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + // cross + + { "cross", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + // normalize + + { "normalize", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "v" }, TAG_GLOBAL, false }, + { "normalize", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "v" }, TAG_GLOBAL, false }, + { "normalize", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "v" }, TAG_GLOBAL, false }, + { "normalize", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "v" }, TAG_GLOBAL, false }, + + // reflect + + { "reflect", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "I", "N" }, TAG_GLOBAL, false }, + + // refract + + { "refract", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "I", "N", "eta" }, TAG_GLOBAL, false }, + + // faceforward + + { "faceforward", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "N", "I", "Nref" }, TAG_GLOBAL, false }, + { "faceforward", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "N", "I", "Nref" }, TAG_GLOBAL, false }, + { "faceforward", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "N", "I", "Nref" }, TAG_GLOBAL, false }, + + // matrixCompMult + + { "matrixCompMult", TYPE_MAT2, { TYPE_MAT2, TYPE_MAT2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "matrixCompMult", TYPE_MAT3, { TYPE_MAT3, TYPE_MAT3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "matrixCompMult", TYPE_MAT4, { TYPE_MAT4, TYPE_MAT4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + // outerProduct + + { "outerProduct", TYPE_MAT2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "c", "r" }, TAG_GLOBAL, false }, + { "outerProduct", TYPE_MAT3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "c", "r" }, TAG_GLOBAL, false }, + { "outerProduct", TYPE_MAT4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "c", "r" }, TAG_GLOBAL, false }, + + // transpose + + { "transpose", TYPE_MAT2, { TYPE_MAT2, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "transpose", TYPE_MAT3, { TYPE_MAT3, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "transpose", TYPE_MAT4, { TYPE_MAT4, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + + // determinant + + { "determinant", TYPE_FLOAT, { TYPE_MAT2, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "determinant", TYPE_FLOAT, { TYPE_MAT3, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "determinant", TYPE_FLOAT, { TYPE_MAT4, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + + // inverse + + { "inverse", TYPE_MAT2, { TYPE_MAT2, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "inverse", TYPE_MAT3, { TYPE_MAT3, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "inverse", TYPE_MAT4, { TYPE_MAT4, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + + // lessThan + + { "lessThan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "lessThan", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "lessThan", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "lessThan", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "lessThan", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + // greaterThan + + { "greaterThan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "greaterThan", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "greaterThan", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "greaterThan", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "greaterThan", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + // lessThanEqual + + { "lessThanEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "lessThanEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "lessThanEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "lessThanEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "lessThanEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + // greaterThanEqual + + { "greaterThanEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "greaterThanEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "greaterThanEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "greaterThanEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "greaterThanEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + // equal + + { "equal", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "equal", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "equal", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "equal", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "equal", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + { "equal", TYPE_BVEC2, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC3, { TYPE_BVEC3, TYPE_BVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC4, { TYPE_BVEC4, TYPE_BVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + // notEqual + + { "notEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "notEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "notEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "notEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "notEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + { "notEqual", TYPE_BVEC2, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC3, { TYPE_BVEC3, TYPE_BVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC4, { TYPE_BVEC4, TYPE_BVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + // any + + { "any", TYPE_BOOL, { TYPE_BVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "any", TYPE_BOOL, { TYPE_BVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "any", TYPE_BOOL, { TYPE_BVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // all + + { "all", TYPE_BOOL, { TYPE_BVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "all", TYPE_BOOL, { TYPE_BVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "all", TYPE_BOOL, { TYPE_BVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // not + + { "not", TYPE_BVEC2, { TYPE_BVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "not", TYPE_BVEC3, { TYPE_BVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "not", TYPE_BVEC4, { TYPE_BVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + // Built-ins: texture functions. + // textureSize + + { "textureSize", TYPE_IVEC2, { TYPE_SAMPLER2D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_ISAMPLER2D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_USAMPLER2D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER3D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER3D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER3D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_SAMPLERCUBE, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_SAMPLERCUBEARRAY, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + + // texture + + { "texture", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, + + // textureProj + + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + + // textureLod + + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, false }, + { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, false }, + { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, false }, + { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, false }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, false }, + + // texelFetch + + { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + + // textureProjLod + + { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + + // textureGrad + + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + + // textureGather + + { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + + // dFdx + + { "dFdx", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdx", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdx", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdx", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + + // dFdy + + { "dFdy", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdy", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdy", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdy", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + + // fwidth + + { "fwidth", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidth", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidth", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidth", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + + // Sub-functions. + // array + + { "length", TYPE_INT, { TYPE_VOID }, { "" }, TAG_ARRAY, true }, + + // Modern functions. + // fma + + { "fma", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "a", "b", "c" }, TAG_GLOBAL, false }, + { "fma", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b", "c" }, TAG_GLOBAL, false }, + { "fma", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b", "c" }, TAG_GLOBAL, false }, + { "fma", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b", "c" }, TAG_GLOBAL, false }, + + // Packing/Unpacking functions. + + { "packHalf2x16", TYPE_UINT, { TYPE_VEC2, TYPE_VOID }, { "v" }, TAG_GLOBAL, true }, + { "packUnorm2x16", TYPE_UINT, { TYPE_VEC2, TYPE_VOID }, { "v" }, TAG_GLOBAL, true }, + { "packSnorm2x16", TYPE_UINT, { TYPE_VEC2, TYPE_VOID }, { "v" }, TAG_GLOBAL, true }, + { "packUnorm4x8", TYPE_UINT, { TYPE_VEC4, TYPE_VOID }, { "v" }, TAG_GLOBAL, true }, + { "packSnorm4x8", TYPE_UINT, { TYPE_VEC4, TYPE_VOID }, { "v" }, TAG_GLOBAL, true }, + + { "unpackHalf2x16", TYPE_VEC2, { TYPE_UINT, TYPE_VOID }, { "v" }, TAG_GLOBAL, true }, + { "unpackUnorm2x16", TYPE_VEC2, { TYPE_UINT, TYPE_VOID }, { "v" }, TAG_GLOBAL, true }, + { "unpackSnorm2x16", TYPE_VEC2, { TYPE_UINT, TYPE_VOID }, { "v" }, TAG_GLOBAL, true }, + { "unpackUnorm4x8", TYPE_VEC4, { TYPE_UINT, TYPE_VOID }, { "v" }, TAG_GLOBAL, true }, + { "unpackSnorm4x8", TYPE_VEC4, { TYPE_UINT, TYPE_VOID }, { "v" }, TAG_GLOBAL, true }, + + // bitfieldExtract + + { "bitfieldExtract", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "value", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldExtract", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, { "value", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldExtract", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_INT, TYPE_VOID }, { "value", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldExtract", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_INT, TYPE_VOID }, { "value", "offset", "bits" }, TAG_GLOBAL, true }, + + { "bitfieldExtract", TYPE_UINT, { TYPE_UINT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "value", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldExtract", TYPE_UVEC2, { TYPE_UVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, { "value", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldExtract", TYPE_UVEC3, { TYPE_UVEC3, TYPE_INT, TYPE_INT, TYPE_VOID }, { "value", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldExtract", TYPE_UVEC4, { TYPE_UVEC4, TYPE_INT, TYPE_INT, TYPE_VOID }, { "value", "offset", "bits" }, TAG_GLOBAL, true }, + + // bitfieldInsert + + { "bitfieldInsert", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "base", "insert", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldInsert", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, { "base", "insert", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldInsert", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_INT, TYPE_INT, TYPE_VOID }, { "base", "insert", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldInsert", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_INT, TYPE_INT, TYPE_VOID }, { "base", "insert", "offset", "bits" }, TAG_GLOBAL, true }, + + { "bitfieldInsert", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "base", "insert", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldInsert", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, { "base", "insert", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldInsert", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_INT, TYPE_INT, TYPE_VOID }, { "base", "insert", "offset", "bits" }, TAG_GLOBAL, true }, + { "bitfieldInsert", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_INT, TYPE_INT, TYPE_VOID }, { "base", "insert", "offset", "bits" }, TAG_GLOBAL, true }, + + // bitfieldReverse + + { "bitfieldReverse", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitfieldReverse", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitfieldReverse", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitfieldReverse", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + + { "bitfieldReverse", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitfieldReverse", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitfieldReverse", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitfieldReverse", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + + // bitCount + + { "bitCount", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitCount", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitCount", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitCount", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + + { "bitCount", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitCount", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitCount", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "bitCount", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + + // findLSB + { "findLSB", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findLSB", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findLSB", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findLSB", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + + { "findLSB", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findLSB", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findLSB", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findLSB", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + + // findMSB + + { "findMSB", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findMSB", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findMSB", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findMSB", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + + { "findMSB", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findMSB", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findMSB", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + { "findMSB", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + + // umulExtended + + { "umulExtended", TYPE_VOID, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "umulExtended", TYPE_VOID, { TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "umulExtended", TYPE_VOID, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "umulExtended", TYPE_VOID, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + + // imulExtended + + { "imulExtended", TYPE_VOID, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "imulExtended", TYPE_VOID, { TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "imulExtended", TYPE_VOID, { TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "imulExtended", TYPE_VOID, { TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + + // uaddCarry + + { "uaddCarry", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "y", "carry" }, TAG_GLOBAL, true }, + { "uaddCarry", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "x", "y", "carry" }, TAG_GLOBAL, true }, + { "uaddCarry", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "y", "carry" }, TAG_GLOBAL, true }, + { "uaddCarry", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "y", "carry" }, TAG_GLOBAL, true }, + + // usubBorrow + + { "usubBorrow", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true }, + { "usubBorrow", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true }, + { "usubBorrow", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true }, + { "usubBorrow", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true }, + + // ldexp + + { "ldexp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_INT, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "ldexp", TYPE_VEC2, { TYPE_VEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "ldexp", TYPE_VEC3, { TYPE_VEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "ldexp", TYPE_VEC4, { TYPE_VEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + + // frexp + + { "frexp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_INT, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "frexp", TYPE_VEC2, { TYPE_VEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "frexp", TYPE_VEC3, { TYPE_VEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "frexp", TYPE_VEC4, { TYPE_VEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + + { nullptr, TYPE_VOID, { TYPE_VOID }, { "" }, TAG_GLOBAL, false } }; const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] = { - //constructors - { "modf", 1 }, - { nullptr, 0 } + { "modf", { 1, -1 } }, + { "umulExtended", { 2, 3 } }, + { "imulExtended", { 2, 3 } }, + { "uaddCarry", { 2, -1 } }, + { "usubBorrow", { 2, -1 } }, + { "ldexp", { 1, -1 } }, + { "frexp", { 1, -1 } }, + { nullptr, { 0, -1 } } +}; + +const ShaderLanguage::BuiltinFuncConstArgs ShaderLanguage::builtin_func_const_args[] = { + { "textureGather", 2, 0, 3 }, + { nullptr, 0, 0, 0 } }; bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) { @@ -2171,6 +2659,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI Vector<DataType> args; Vector<StringName> args2; + Vector<int> args3; ERR_FAIL_COND_V(p_func->arguments[0]->type != Node::TYPE_VARIABLE, false); @@ -2179,6 +2668,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI for (int i = 1; i < p_func->arguments.size(); i++) { args.push_back(p_func->arguments[i]->get_datatype()); args2.push_back(p_func->arguments[i]->get_datatype_name()); + args3.push_back(p_func->arguments[i]->get_array_size()); } int argcount = args.size(); @@ -2225,6 +2715,13 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI failed_builtin = true; bool fail = false; for (int i = 0; i < argcount; i++) { + if (p_func->arguments[i + 1]->type == Node::TYPE_ARRAY) { + const ArrayNode *anode = static_cast<const ArrayNode *>(p_func->arguments[i + 1]); + if (anode->call_expression == nullptr && !anode->is_indexed()) { + fail = true; + break; + } + } if (get_scalar_type(args[i]) == args[i] && p_func->arguments[i + 1]->type == Node::TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[i + 1]), builtin_func_defs[idx].args[i])) { //all good, but needs implicit conversion later } else if (args[i] != builtin_func_defs[idx].args[i]) { @@ -2248,96 +2745,153 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI } if (!fail) { - //make sure its not an out argument used in the wrong way - int outarg_idx = 0; - while (builtin_func_out_args[outarg_idx].name) { - if (String(name) == builtin_func_out_args[outarg_idx].name) { - int arg_idx = builtin_func_out_args[outarg_idx].argument; - - if (arg_idx < argcount) { - if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) { - _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member."); - return false; + { + int constarg_idx = 0; + while (builtin_func_const_args[constarg_idx].name) { + if (String(name) == builtin_func_const_args[constarg_idx].name) { + int arg = builtin_func_const_args[constarg_idx].arg + 1; + if (p_func->arguments.size() <= arg) { + break; } - if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { - ArrayNode *mn = static_cast<ArrayNode *>(p_func->arguments[arg_idx + 1]); - if (mn->is_const) { - fail = true; - } - } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { - MemberNode *mn = static_cast<MemberNode *>(p_func->arguments[arg_idx + 1]); - if (mn->basetype_const) { - fail = true; + int min = builtin_func_const_args[constarg_idx].min; + int max = builtin_func_const_args[constarg_idx].max; + + bool error = false; + if (p_func->arguments[arg]->type == Node::TYPE_VARIABLE) { + const VariableNode *vn = (VariableNode *)p_func->arguments[arg]; + + bool is_const = false; + ConstantNode::Value value; + value.sint = -1; + + _find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, &is_const, nullptr, nullptr, &value); + if (!is_const || value.sint < min || value.sint > max) { + error = true; } - } else { // TYPE_VARIABLE - VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg_idx + 1]); - if (vn->is_const) { - fail = true; - } else { - StringName varname = vn->name; - if (shader->uniforms.has(varname)) { - fail = true; - } else { - if (p_function_info.built_ins.has(varname)) { - BuiltInInfo info = p_function_info.built_ins[varname]; - if (info.constant) { - fail = true; - } + } else { + if (p_func->arguments[arg]->type == Node::TYPE_CONSTANT) { + ConstantNode *cn = (ConstantNode *)p_func->arguments[arg]; + + if (cn->get_datatype() == TYPE_INT && cn->values.size() == 1) { + int value = cn->values[0].sint; + + if (value < min || value > max) { + error = true; } + } else { + error = true; } + } else { + error = true; } } - if (fail) { - _set_error(vformat("Constant value cannot be passed for '%s' parameter!", "out")); + if (error) { + _set_error(vformat("Expected integer constant within %s..%s range.", min, max)); return false; } + } + constarg_idx++; + } + } - StringName var_name; - if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { - var_name = static_cast<const ArrayNode *>(p_func->arguments[arg_idx + 1])->name; - } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { - Node *n = static_cast<const MemberNode *>(p_func->arguments[arg_idx + 1])->owner; - while (n->type == Node::TYPE_MEMBER) { - n = static_cast<const MemberNode *>(n)->owner; - } - if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) { + //make sure its not an out argument used in the wrong way + int outarg_idx = 0; + while (builtin_func_out_args[outarg_idx].name) { + if (String(name) == builtin_func_out_args[outarg_idx].name) { + for (int arg = 0; arg < BuiltinFuncOutArgs::MAX_ARGS; arg++) { + int arg_idx = builtin_func_out_args[outarg_idx].arguments[arg]; + if (arg_idx == -1) { + break; + } + if (arg_idx < argcount) { + if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) { _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member."); return false; } - if (n->type == Node::TYPE_VARIABLE) { - var_name = static_cast<const VariableNode *>(n)->name; - } else { // TYPE_ARRAY - var_name = static_cast<const ArrayNode *>(n)->name; + + if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { + ArrayNode *mn = static_cast<ArrayNode *>(p_func->arguments[arg_idx + 1]); + if (mn->is_const) { + fail = true; + } + } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { + MemberNode *mn = static_cast<MemberNode *>(p_func->arguments[arg_idx + 1]); + if (mn->basetype_const) { + fail = true; + } + } else { // TYPE_VARIABLE + VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg_idx + 1]); + if (vn->is_const) { + fail = true; + } else { + StringName varname = vn->name; + if (shader->uniforms.has(varname)) { + fail = true; + } else { + if (shader->varyings.has(varname)) { + _set_error(vformat("Varyings cannot be passed for '%s' parameter!", "out")); + return false; + } + if (p_function_info.built_ins.has(varname)) { + BuiltInInfo info = p_function_info.built_ins[varname]; + if (info.constant) { + fail = true; + } + } + } + } } - } else { // TYPE_VARIABLE - var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name; - } - const BlockNode *b = p_block; - bool valid = false; - while (b) { - if (b->variables.has(var_name) || p_function_info.built_ins.has(var_name)) { - valid = true; - break; + if (fail) { + _set_error(vformat("Constant value cannot be passed for '%s' parameter!", "out")); + return false; } - if (b->parent_function) { - for (int i = 0; i < b->parent_function->arguments.size(); i++) { - if (b->parent_function->arguments[i].name == var_name) { - valid = true; - break; + + StringName var_name; + if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { + var_name = static_cast<const ArrayNode *>(p_func->arguments[arg_idx + 1])->name; + } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { + Node *n = static_cast<const MemberNode *>(p_func->arguments[arg_idx + 1])->owner; + while (n->type == Node::TYPE_MEMBER) { + n = static_cast<const MemberNode *>(n)->owner; + } + if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) { + _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member."); + return false; + } + if (n->type == Node::TYPE_VARIABLE) { + var_name = static_cast<const VariableNode *>(n)->name; + } else { // TYPE_ARRAY + var_name = static_cast<const ArrayNode *>(n)->name; + } + } else { // TYPE_VARIABLE + var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name; + } + const BlockNode *b = p_block; + bool valid = false; + while (b) { + if (b->variables.has(var_name) || p_function_info.built_ins.has(var_name)) { + valid = true; + break; + } + if (b->parent_function) { + for (int i = 0; i < b->parent_function->arguments.size(); i++) { + if (b->parent_function->arguments[i].name == var_name) { + valid = true; + break; + } } } + b = b->parent_block; } - b = b->parent_block; - } - if (!valid) { - _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable, array or member."); - return false; + if (!valid) { + _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable, array or member."); + return false; + } } } } - outarg_idx++; } //implicitly convert values if possible @@ -2397,6 +2951,11 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI } else { arg_name = get_datatype_name(args[i]); } + if (args3[i] > 0) { + arg_name += "["; + arg_name += itos(args3[i]); + arg_name += "]"; + } err += arg_name; } err += ")"; @@ -2446,6 +3005,11 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI } else { func_arg_name = get_datatype_name(pfunc->arguments[j].type); } + if (pfunc->arguments[j].array_size > 0) { + func_arg_name += "["; + func_arg_name += itos(pfunc->arguments[j].array_size); + func_arg_name += "]"; + } arg_list += func_arg_name; } } @@ -2458,21 +3022,32 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI bool fail = false; for (int j = 0; j < args.size(); j++) { - if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) { + if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && args3[j] == 0 && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) { //all good, but it needs implicit conversion later - } else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str)) { + } else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str) || args3[j] != pfunc->arguments[j].array_size) { String func_arg_name; if (pfunc->arguments[j].type == TYPE_STRUCT) { func_arg_name = pfunc->arguments[j].type_str; } else { func_arg_name = get_datatype_name(pfunc->arguments[j].type); } + if (pfunc->arguments[j].array_size > 0) { + func_arg_name += "["; + func_arg_name += itos(pfunc->arguments[j].array_size); + func_arg_name += "]"; + } String arg_name; if (args[j] == TYPE_STRUCT) { arg_name = args2[j]; } else { arg_name = get_datatype_name(args[j]); } + if (args3[j] > 0) { + arg_name += "["; + arg_name += itos(args3[j]); + arg_name += "]"; + } + _set_error(vformat("Invalid argument for \"%s(%s)\" function: argument %s should be %s but is %s.", String(name), arg_list, j + 1, func_arg_name, arg_name)); fail = true; break; @@ -2518,16 +3093,45 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI return false; } -bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) const { - if (a->get_datatype() != b->get_datatype()) { - return false; +bool ShaderLanguage::_compare_datatypes(DataType p_datatype_a, String p_datatype_name_a, int p_array_size_a, DataType p_datatype_b, String p_datatype_name_b, int p_array_size_b) { + bool result = true; + + if (p_datatype_a == TYPE_STRUCT || p_datatype_b == TYPE_STRUCT) { + if (p_datatype_name_a != p_datatype_name_b) { + result = false; + } + } else { + if (p_datatype_a != p_datatype_b) { + result = false; + } } - if (a->get_datatype() == TYPE_STRUCT || b->get_datatype() == TYPE_STRUCT) { - if (a->get_datatype_name() != b->get_datatype_name()) { - return false; + + if (p_array_size_a != p_array_size_b) { + result = false; + } + + if (!result) { + String type_name = p_datatype_a == TYPE_STRUCT ? p_datatype_name_a : get_datatype_name(p_datatype_a); + if (p_array_size_a > 0) { + type_name += "["; + type_name += itos(p_array_size_a); + type_name += "]"; + } + + String type_name2 = p_datatype_b == TYPE_STRUCT ? p_datatype_name_b : get_datatype_name(p_datatype_b); + if (p_array_size_b > 0) { + type_name2 += "["; + type_name2 += itos(p_array_size_b); + type_name2 += "]"; } + + _set_error("Invalid assignment of '" + type_name2 + "' to '" + type_name + "'"); } - return true; + return result; +} + +bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) { + return _compare_datatypes(a->get_datatype(), a->get_datatype_name(), a->get_array_size(), b->get_datatype(), b->get_datatype_name(), b->get_array_size()); } bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg) { @@ -2612,6 +3216,20 @@ bool ShaderLanguage::is_token_operator(TokenType p_type) { p_type == TK_COLON); } +bool ShaderLanguage::is_token_operator_assign(TokenType p_type) { + return (p_type == TK_OP_ASSIGN || + p_type == TK_OP_ASSIGN_ADD || + p_type == TK_OP_ASSIGN_SUB || + p_type == TK_OP_ASSIGN_MUL || + p_type == TK_OP_ASSIGN_DIV || + p_type == TK_OP_ASSIGN_MOD || + p_type == TK_OP_ASSIGN_SHIFT_LEFT || + p_type == TK_OP_ASSIGN_SHIFT_RIGHT || + p_type == TK_OP_ASSIGN_BIT_AND || + p_type == TK_OP_ASSIGN_BIT_OR || + p_type == TK_OP_ASSIGN_BIT_XOR); +} + bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value) { if (p_constant->datatype == p_to_type) { if (p_value) { @@ -2655,100 +3273,329 @@ bool ShaderLanguage::is_scalar_type(DataType p_type) { return p_type == TYPE_BOOL || p_type == TYPE_INT || p_type == TYPE_UINT || p_type == TYPE_FLOAT; } +bool ShaderLanguage::is_float_type(DataType p_type) { + switch (p_type) { + case TYPE_FLOAT: + case TYPE_VEC2: + case TYPE_VEC3: + case TYPE_VEC4: + case TYPE_MAT2: + case TYPE_MAT3: + case TYPE_MAT4: + case TYPE_SAMPLER2D: + case TYPE_SAMPLER2DARRAY: + case TYPE_SAMPLER3D: + case TYPE_SAMPLERCUBE: + case TYPE_SAMPLERCUBEARRAY: { + return true; + } + default: { + return false; + } + } +} bool ShaderLanguage::is_sampler_type(DataType p_type) { return p_type == TYPE_SAMPLER2D || - p_type == TYPE_ISAMPLER2D || - p_type == TYPE_USAMPLER2D || - p_type == TYPE_SAMPLER2DARRAY || - p_type == TYPE_ISAMPLER2DARRAY || - p_type == TYPE_USAMPLER2DARRAY || - p_type == TYPE_SAMPLER3D || - p_type == TYPE_ISAMPLER3D || - p_type == TYPE_USAMPLER3D || - p_type == TYPE_SAMPLERCUBE || - p_type == TYPE_SAMPLERCUBEARRAY; + p_type == TYPE_ISAMPLER2D || + p_type == TYPE_USAMPLER2D || + p_type == TYPE_SAMPLER2DARRAY || + p_type == TYPE_ISAMPLER2DARRAY || + p_type == TYPE_USAMPLER2DARRAY || + p_type == TYPE_SAMPLER3D || + p_type == TYPE_ISAMPLER3D || + p_type == TYPE_USAMPLER3D || + p_type == TYPE_SAMPLERCUBE || + p_type == TYPE_SAMPLERCUBEARRAY; } -Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) { +Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) { + int array_size = p_array_size; + if (p_value.size() > 0) { Variant value; switch (p_type) { case ShaderLanguage::TYPE_BOOL: - value = Variant(p_value[0].boolean); + if (array_size > 0) { + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].boolean); + } + value = Variant(array); + } else { + value = Variant(p_value[0].boolean); + } break; case ShaderLanguage::TYPE_BVEC2: + array_size *= 2; + + if (array_size > 0) { + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].boolean); + } + value = Variant(array); + } else { + value = Variant(p_value[0].boolean); + } + break; case ShaderLanguage::TYPE_BVEC3: + array_size *= 3; + + if (array_size > 0) { + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].boolean); + } + value = Variant(array); + } else { + value = Variant(p_value[0].boolean); + } + break; case ShaderLanguage::TYPE_BVEC4: + array_size *= 4; + + if (array_size > 0) { + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].boolean); + } + value = Variant(array); + } else { + value = Variant(p_value[0].boolean); + } + break; case ShaderLanguage::TYPE_INT: - value = Variant(p_value[0].sint); + if (array_size > 0) { + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].sint); + } + value = Variant(array); + } else { + value = Variant(p_value[0].sint); + } break; case ShaderLanguage::TYPE_IVEC2: - value = Variant(Vector2(p_value[0].sint, p_value[1].sint)); + if (array_size > 0) { + array_size *= 2; + + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].sint); + } + value = Variant(array); + } else { + value = Variant(Vector2(p_value[0].sint, p_value[1].sint)); + } break; case ShaderLanguage::TYPE_IVEC3: - value = Variant(Vector3(p_value[0].sint, p_value[1].sint, p_value[2].sint)); + if (array_size > 0) { + array_size *= 3; + + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].sint); + } + value = Variant(array); + } else { + value = Variant(Vector3(p_value[0].sint, p_value[1].sint, p_value[2].sint)); + } break; case ShaderLanguage::TYPE_IVEC4: - value = Variant(Plane(p_value[0].sint, p_value[1].sint, p_value[2].sint, p_value[3].sint)); + if (array_size > 0) { + array_size *= 4; + + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].sint); + } + value = Variant(array); + } else { + value = Variant(Plane(p_value[0].sint, p_value[1].sint, p_value[2].sint, p_value[3].sint)); + } break; case ShaderLanguage::TYPE_UINT: - value = Variant(p_value[0].uint); + if (array_size > 0) { + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].uint); + } + value = Variant(array); + } else { + value = Variant(p_value[0].uint); + } break; case ShaderLanguage::TYPE_UVEC2: - value = Variant(Vector2(p_value[0].uint, p_value[1].uint)); + if (array_size > 0) { + array_size *= 2; + + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].uint); + } + value = Variant(array); + } else { + value = Variant(Vector2(p_value[0].uint, p_value[1].uint)); + } break; case ShaderLanguage::TYPE_UVEC3: - value = Variant(Vector3(p_value[0].uint, p_value[1].uint, p_value[2].uint)); + if (array_size > 0) { + array_size *= 3; + + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].uint); + } + value = Variant(array); + } else { + value = Variant(Vector3(p_value[0].uint, p_value[1].uint, p_value[2].uint)); + } break; case ShaderLanguage::TYPE_UVEC4: - value = Variant(Plane(p_value[0].uint, p_value[1].uint, p_value[2].uint, p_value[3].uint)); + if (array_size > 0) { + array_size *= 4; + + PackedInt32Array array = PackedInt32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].uint); + } + value = Variant(array); + } else { + value = Variant(Plane(p_value[0].uint, p_value[1].uint, p_value[2].uint, p_value[3].uint)); + } break; case ShaderLanguage::TYPE_FLOAT: - value = Variant(p_value[0].real); + if (array_size > 0) { + PackedFloat32Array array = PackedFloat32Array(); + for (int i = 0; i < array_size; i++) { + array.push_back(p_value[i].real); + } + value = Variant(array); + } else { + value = Variant(p_value[0].real); + } break; case ShaderLanguage::TYPE_VEC2: - value = Variant(Vector2(p_value[0].real, p_value[1].real)); + if (array_size > 0) { + array_size *= 2; + + PackedVector2Array array = PackedVector2Array(); + for (int i = 0; i < array_size; i += 2) { + array.push_back(Vector2(p_value[i].real, p_value[i + 1].real)); + } + value = Variant(array); + } else { + value = Variant(Vector2(p_value[0].real, p_value[1].real)); + } break; case ShaderLanguage::TYPE_VEC3: - value = Variant(Vector3(p_value[0].real, p_value[1].real, p_value[2].real)); + if (array_size > 0) { + array_size *= 3; + + PackedVector3Array array = PackedVector3Array(); + for (int i = 0; i < array_size; i += 3) { + array.push_back(Vector3(p_value[i].real, p_value[i + 1].real, p_value[i + 2].real)); + } + value = Variant(array); + } else { + value = Variant(Vector3(p_value[0].real, p_value[1].real, p_value[2].real)); + } break; case ShaderLanguage::TYPE_VEC4: - if (p_hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { - value = Variant(Color(p_value[0].real, p_value[1].real, p_value[2].real, p_value[3].real)); + if (array_size > 0) { + array_size *= 4; + + if (p_hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { + PackedColorArray array = PackedColorArray(); + for (int i = 0; i < array_size; i += 4) { + array.push_back(Color(p_value[i].real, p_value[i + 1].real, p_value[i + 2].real, p_value[i + 3].real)); + } + value = Variant(array); + } else { + PackedFloat32Array array = PackedFloat32Array(); + for (int i = 0; i < array_size; i += 4) { + array.push_back(p_value[i].real); + array.push_back(p_value[i + 1].real); + array.push_back(p_value[i + 2].real); + array.push_back(p_value[i + 3].real); + } + value = Variant(array); + } } else { - value = Variant(Plane(p_value[0].real, p_value[1].real, p_value[2].real, p_value[3].real)); + if (p_hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { + value = Variant(Color(p_value[0].real, p_value[1].real, p_value[2].real, p_value[3].real)); + } else { + value = Variant(Plane(p_value[0].real, p_value[1].real, p_value[2].real, p_value[3].real)); + } } break; case ShaderLanguage::TYPE_MAT2: - value = Variant(Transform2D(p_value[0].real, p_value[2].real, p_value[1].real, p_value[3].real, 0.0, 0.0)); + if (array_size > 0) { + array_size *= 4; + + PackedFloat32Array array = PackedFloat32Array(); + for (int i = 0; i < array_size; i += 4) { + array.push_back(p_value[i].real); + array.push_back(p_value[i + 1].real); + array.push_back(p_value[i + 2].real); + array.push_back(p_value[i + 3].real); + } + value = Variant(array); + } else { + value = Variant(Transform2D(p_value[0].real, p_value[2].real, p_value[1].real, p_value[3].real, 0.0, 0.0)); + } break; case ShaderLanguage::TYPE_MAT3: { - Basis p; - p[0][0] = p_value[0].real; - p[0][1] = p_value[1].real; - p[0][2] = p_value[2].real; - p[1][0] = p_value[3].real; - p[1][1] = p_value[4].real; - p[1][2] = p_value[5].real; - p[2][0] = p_value[6].real; - p[2][1] = p_value[7].real; - p[2][2] = p_value[8].real; - value = Variant(p); + if (array_size > 0) { + array_size *= 9; + + PackedFloat32Array array = PackedFloat32Array(); + for (int i = 0; i < array_size; i += 9) { + for (int j = 0; j < 9; j++) { + array.push_back(p_value[i + j].real); + } + } + value = Variant(array); + } else { + Basis p; + p[0][0] = p_value[0].real; + p[0][1] = p_value[1].real; + p[0][2] = p_value[2].real; + p[1][0] = p_value[3].real; + p[1][1] = p_value[4].real; + p[1][2] = p_value[5].real; + p[2][0] = p_value[6].real; + p[2][1] = p_value[7].real; + p[2][2] = p_value[8].real; + value = Variant(p); + } break; } case ShaderLanguage::TYPE_MAT4: { - Basis p; - p[0][0] = p_value[0].real; - p[0][1] = p_value[1].real; - p[0][2] = p_value[2].real; - p[1][0] = p_value[4].real; - p[1][1] = p_value[5].real; - p[1][2] = p_value[6].real; - p[2][0] = p_value[8].real; - p[2][1] = p_value[9].real; - p[2][2] = p_value[10].real; - Transform t = Transform(p, Vector3(p_value[3].real, p_value[7].real, p_value[11].real)); - value = Variant(t); + if (array_size > 0) { + array_size *= 16; + + PackedFloat32Array array = PackedFloat32Array(); + for (int i = 0; i < array_size; i += 16) { + for (int j = 0; j < 16; j++) { + array.push_back(p_value[i + j].real); + } + } + value = Variant(array); + } else { + Basis p; + p[0][0] = p_value[0].real; + p[0][1] = p_value[1].real; + p[0][2] = p_value[2].real; + p[1][0] = p_value[4].real; + p[1][1] = p_value[5].real; + p[1][2] = p_value[6].real; + p[2][0] = p_value[8].real; + p[2][1] = p_value[9].real; + p[2][2] = p_value[10].real; + Transform3D t = Transform3D(p, Vector3(p_value[3].real, p_value[7].real, p_value[11].real)); + value = Variant(t); + } break; } case ShaderLanguage::TYPE_ISAMPLER2DARRAY: @@ -2784,31 +3631,50 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform pi.type = Variant::NIL; break; case ShaderLanguage::TYPE_BOOL: - pi.type = Variant::BOOL; + if (p_uniform.array_size > 0) { + pi.type = Variant::PACKED_INT32_ARRAY; + } else { + pi.type = Variant::BOOL; + } break; case ShaderLanguage::TYPE_BVEC2: - pi.type = Variant::INT; - pi.hint = PROPERTY_HINT_FLAGS; - pi.hint_string = "x,y"; + if (p_uniform.array_size > 0) { + pi.type = Variant::PACKED_INT32_ARRAY; + } else { + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y"; + } break; case ShaderLanguage::TYPE_BVEC3: - pi.type = Variant::INT; - pi.hint = PROPERTY_HINT_FLAGS; - pi.hint_string = "x,y,z"; + if (p_uniform.array_size > 0) { + pi.type = Variant::PACKED_INT32_ARRAY; + } else { + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y,z"; + } break; case ShaderLanguage::TYPE_BVEC4: - pi.type = Variant::INT; - pi.hint = PROPERTY_HINT_FLAGS; - pi.hint_string = "x,y,z,w"; + if (p_uniform.array_size > 0) { + pi.type = Variant::PACKED_INT32_ARRAY; + } else { + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y,z,w"; + } break; case ShaderLanguage::TYPE_UINT: case ShaderLanguage::TYPE_INT: { - pi.type = Variant::INT; - if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { - pi.hint = PROPERTY_HINT_RANGE; - pi.hint_string = rtos(p_uniform.hint_range[0]) + "," + rtos(p_uniform.hint_range[1]) + "," + rtos(p_uniform.hint_range[2]); + if (p_uniform.array_size > 0) { + pi.type = Variant::PACKED_INT32_ARRAY; + } else { + pi.type = Variant::INT; + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { + pi.hint = PROPERTY_HINT_RANGE; + pi.hint_string = rtos(p_uniform.hint_range[0]) + "," + rtos(p_uniform.hint_range[1]) + "," + rtos(p_uniform.hint_range[2]); + } } - } break; case ShaderLanguage::TYPE_IVEC2: case ShaderLanguage::TYPE_IVEC3: @@ -2819,59 +3685,106 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform pi.type = Variant::PACKED_INT32_ARRAY; } break; case ShaderLanguage::TYPE_FLOAT: { - pi.type = Variant::FLOAT; - if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { - pi.hint = PROPERTY_HINT_RANGE; - pi.hint_string = rtos(p_uniform.hint_range[0]) + "," + rtos(p_uniform.hint_range[1]) + "," + rtos(p_uniform.hint_range[2]); + if (p_uniform.array_size > 0) { + pi.type = Variant::PACKED_FLOAT32_ARRAY; + } else { + pi.type = Variant::FLOAT; + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { + pi.hint = PROPERTY_HINT_RANGE; + pi.hint_string = rtos(p_uniform.hint_range[0]) + "," + rtos(p_uniform.hint_range[1]) + "," + rtos(p_uniform.hint_range[2]); + } } - } break; case ShaderLanguage::TYPE_VEC2: - pi.type = Variant::VECTOR2; + if (p_uniform.array_size > 0) { + pi.type = Variant::PACKED_VECTOR2_ARRAY; + } else { + pi.type = Variant::VECTOR2; + } break; case ShaderLanguage::TYPE_VEC3: - pi.type = Variant::VECTOR3; + if (p_uniform.array_size > 0) { + pi.type = Variant::PACKED_VECTOR3_ARRAY; + } else { + pi.type = Variant::VECTOR3; + } break; case ShaderLanguage::TYPE_VEC4: { - if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { - pi.type = Variant::COLOR; + if (p_uniform.array_size > 0) { + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { + pi.type = Variant::PACKED_COLOR_ARRAY; + } else { + pi.type = Variant::PACKED_FLOAT32_ARRAY; + } } else { - pi.type = Variant::PLANE; + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { + pi.type = Variant::COLOR; + } else { + pi.type = Variant::PLANE; + } } } break; case ShaderLanguage::TYPE_MAT2: - pi.type = Variant::TRANSFORM2D; + if (p_uniform.array_size > 0) { + pi.type = Variant::PACKED_FLOAT32_ARRAY; + } else { + pi.type = Variant::TRANSFORM2D; + } break; case ShaderLanguage::TYPE_MAT3: - pi.type = Variant::BASIS; + if (p_uniform.array_size > 0) { + pi.type = Variant::PACKED_FLOAT32_ARRAY; + } else { + pi.type = Variant::BASIS; + } break; case ShaderLanguage::TYPE_MAT4: - pi.type = Variant::TRANSFORM; + if (p_uniform.array_size > 0) { + pi.type = Variant::PACKED_FLOAT32_ARRAY; + } else { + pi.type = Variant::TRANSFORM3D; + } break; case ShaderLanguage::TYPE_SAMPLER2D: case ShaderLanguage::TYPE_ISAMPLER2D: case ShaderLanguage::TYPE_USAMPLER2D: { - pi.type = Variant::OBJECT; + if (p_uniform.array_size > 0) { + pi.type = Variant::ARRAY; + } else { + pi.type = Variant::OBJECT; + } pi.hint = PROPERTY_HINT_RESOURCE_TYPE; pi.hint_string = "Texture2D"; } break; case ShaderLanguage::TYPE_SAMPLER2DARRAY: case ShaderLanguage::TYPE_ISAMPLER2DARRAY: case ShaderLanguage::TYPE_USAMPLER2DARRAY: { - pi.type = Variant::OBJECT; + if (p_uniform.array_size > 0) { + pi.type = Variant::ARRAY; + } else { + pi.type = Variant::OBJECT; + } pi.hint = PROPERTY_HINT_RESOURCE_TYPE; pi.hint_string = "TextureLayered"; } break; case ShaderLanguage::TYPE_SAMPLER3D: case ShaderLanguage::TYPE_ISAMPLER3D: case ShaderLanguage::TYPE_USAMPLER3D: { - pi.type = Variant::OBJECT; + if (p_uniform.array_size > 0) { + pi.type = Variant::ARRAY; + } else { + pi.type = Variant::OBJECT; + } pi.hint = PROPERTY_HINT_RESOURCE_TYPE; pi.hint_string = "Texture3D"; } break; case ShaderLanguage::TYPE_SAMPLERCUBE: case ShaderLanguage::TYPE_SAMPLERCUBEARRAY: { - pi.type = Variant::OBJECT; + if (p_uniform.array_size > 0) { + pi.type = Variant::ARRAY; + } else { + pi.type = Variant::OBJECT; + } pi.hint = PROPERTY_HINT_RESOURCE_TYPE; pi.hint_string = "TextureLayered"; } break; @@ -2958,6 +3871,20 @@ void ShaderLanguage::get_keyword_list(List<String> *r_keywords) { } } +bool ShaderLanguage::is_control_flow_keyword(String p_keyword) { + return p_keyword == "break" || + p_keyword == "case" || + p_keyword == "continue" || + p_keyword == "default" || + p_keyword == "do" || + p_keyword == "else" || + p_keyword == "for" || + p_keyword == "if" || + p_keyword == "return" || + p_keyword == "switch" || + p_keyword == "while"; +} + void ShaderLanguage::get_builtin_funcs(List<String> *r_keywords) { Set<String> kws; @@ -3091,6 +4018,106 @@ bool ShaderLanguage::_is_operator_assign(Operator p_op) const { return false; } +bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, String *r_message) { + if (current_function != String("vertex") && current_function != String("fragment")) { + *r_message = vformat(RTR("Varying may not be assigned in the '%s' function."), current_function); + return false; + } + switch (p_varying.stage) { + case ShaderNode::Varying::STAGE_UNKNOWN: // first assign + if (current_function == varying_function_names.vertex) { + p_varying.stage = ShaderNode::Varying::STAGE_VERTEX; + } else if (current_function == varying_function_names.fragment) { + p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT; + } + break; + case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT: + case ShaderNode::Varying::STAGE_VERTEX: + if (current_function == varying_function_names.fragment) { + *r_message = RTR("Varyings which assigned in 'vertex' function may not be reassigned in 'fragment' or 'light'."); + return false; + } + break; + case ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT: + case ShaderNode::Varying::STAGE_FRAGMENT: + if (current_function == varying_function_names.vertex) { + *r_message = RTR("Varyings which assigned in 'fragment' function may not be reassigned in 'vertex' or 'light'."); + return false; + } + break; + default: + break; + } + return true; +} + +bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, String *r_message) { + switch (p_varying.stage) { + case ShaderNode::Varying::STAGE_UNKNOWN: + VaryingUsage usage; + usage.var = &p_varying; + usage.line = tk_line; + unknown_varying_usages.push_back(usage); + break; + case ShaderNode::Varying::STAGE_VERTEX: + if (current_function == varying_function_names.fragment || current_function == varying_function_names.light) { + p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT; + } + break; + case ShaderNode::Varying::STAGE_FRAGMENT: + if (current_function == varying_function_names.light) { + p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT; + } + break; + default: + break; + } + return true; +} + +bool ShaderLanguage::_check_varying_usages(int *r_error_line, String *r_error_message) const { + for (const List<ShaderLanguage::VaryingUsage>::Element *E = unknown_varying_usages.front(); E; E = E->next()) { + ShaderNode::Varying::Stage stage = E->get().var->stage; + if (stage != ShaderNode::Varying::STAGE_UNKNOWN && stage != ShaderNode::Varying::STAGE_VERTEX && stage != ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT) { + *r_error_line = E->get().line; + *r_error_message = RTR("Fragment-stage varying could not been accessed in custom function!"); + return false; + } + } + + return true; +} + +bool ShaderLanguage::_check_node_constness(const Node *p_node) const { + switch (p_node->type) { + case Node::TYPE_OPERATOR: { + OperatorNode *op_node = (OperatorNode *)p_node; + for (int i = int(op_node->op == OP_CALL); i < op_node->arguments.size(); i++) { + if (!_check_node_constness(op_node->arguments[i])) { + return false; + } + } + } break; + case Node::TYPE_CONSTANT: + break; + case Node::TYPE_VARIABLE: { + VariableNode *varn = (VariableNode *)p_node; + if (!varn->is_const) { + return false; + } + } break; + case Node::TYPE_ARRAY: { + ArrayNode *arrn = (ArrayNode *)p_node; + if (!arrn->is_const) { + return false; + } + } break; + default: + return false; + } + return true; +} + bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message) { if (p_node->type == Node::TYPE_OPERATOR) { OperatorNode *op = static_cast<OperatorNode *>(p_node); @@ -3131,13 +4158,6 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi return false; } - if (shader->varyings.has(var->name) && current_function != String("vertex")) { - if (r_message) { - *r_message = RTR("Varyings can only be assigned in vertex function."); - } - return false; - } - if (shader->constants.has(var->name) || var->is_const) { if (r_message) { *r_message = RTR("Constants cannot be modified."); @@ -3158,13 +4178,6 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi return false; } - if (shader->varyings.has(arr->name) && current_function != String("vertex")) { - if (r_message) { - *r_message = RTR("Varyings can only be assigned in vertex function."); - } - return false; - } - return true; } @@ -3175,7 +4188,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi } bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat) { - for (int i = 0; shader->functions.size(); i++) { + for (int i = 0; i < shader->functions.size(); i++) { if (shader->functions[i].name == p_name) { ERR_FAIL_INDEX_V(p_argument, shader->functions[i].function->arguments.size(), false); FunctionNode::Argument *arg = &shader->functions[i].function->arguments.write[p_argument]; @@ -3194,9 +4207,9 @@ bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(StringNam arg->tex_argument_check = true; arg->tex_argument_filter = p_filter; arg->tex_argument_repeat = p_repeat; - for (Map<StringName, Set<int>>::Element *E = arg->tex_argument_connect.front(); E; E = E->next()) { - for (Set<int>::Element *F = E->get().front(); F; F = F->next()) { - if (!_propagate_function_call_sampler_uniform_settings(E->key(), F->get(), p_filter, p_repeat)) { + for (KeyValue<StringName, Set<int>> &E : arg->tex_argument_connect) { + for (Set<int>::Element *F = E.value.front(); F; F = F->next()) { + if (!_propagate_function_call_sampler_uniform_settings(E.key, F->get(), p_filter, p_repeat)) { return false; } } @@ -3209,7 +4222,7 @@ bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(StringNam } bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin) { - for (int i = 0; shader->functions.size(); i++) { + for (int i = 0; i < shader->functions.size(); i++) { if (shader->functions[i].name == p_name) { ERR_FAIL_INDEX_V(p_argument, shader->functions[i].function->arguments.size(), false); FunctionNode::Argument *arg = &shader->functions[i].function->arguments.write[p_argument]; @@ -3228,9 +4241,9 @@ bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringNa arg->tex_builtin_check = true; arg->tex_builtin = p_builtin; - for (Map<StringName, Set<int>>::Element *E = arg->tex_argument_connect.front(); E; E = E->next()) { - for (Set<int>::Element *F = E->get().front(); F; F = F->next()) { - if (!_propagate_function_call_sampler_builtin_reference(E->key(), F->get(), p_builtin)) { + for (KeyValue<StringName, Set<int>> &E : arg->tex_argument_connect) { + for (Set<int>::Element *F = E.value.front(); F; F = F->next()) { + if (!_propagate_function_call_sampler_builtin_reference(E.key, F->get(), p_builtin)) { return false; } } @@ -3242,6 +4255,376 @@ bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringNa ERR_FAIL_V(false); //bug? function not found } +ShaderLanguage::Node *ShaderLanguage::_parse_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, int &r_array_size) { + int array_size = 0; + + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + if (n) { + if (n->type == Node::TYPE_VARIABLE) { + VariableNode *vn = static_cast<VariableNode *>(n); + if (vn) { + ConstantNode::Value v; + DataType data_type; + bool is_const = false; + + _find_identifier(p_block, false, p_function_info, vn->name, &data_type, nullptr, &is_const, nullptr, nullptr, &v); + + if (is_const) { + if (data_type == TYPE_INT) { + int32_t value = v.sint; + if (value > 0) { + array_size = value; + } + } else if (data_type == TYPE_UINT) { + uint32_t value = v.uint; + if (value > 0U) { + array_size = value; + } + } + } + } + } else if (n->type == Node::TYPE_OPERATOR) { + _set_error("Array size expressions are not yet implemented."); + return nullptr; + } + } + + r_array_size = array_size; + return n; +} + +Error ShaderLanguage::_parse_global_array_size(int &r_array_size) { + if (r_array_size > 0) { + _set_error("Array size is already defined!"); + return ERR_PARSE_ERROR; + } + TkPos pos = _get_tkpos(); + Token tk = _get_token(); + + int array_size = 0; + + if (tk.type != TK_INT_CONSTANT || ((int)tk.constant) <= 0) { + _set_tkpos(pos); + Node *n = _parse_array_size(nullptr, FunctionInfo(), array_size); + if (!n) { + return ERR_PARSE_ERROR; + } + } else if (((int)tk.constant) > 0) { + array_size = (uint32_t)tk.constant; + } + + if (array_size <= 0) { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + + r_array_size = array_size; + return OK; +} + +Error ShaderLanguage::_parse_local_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, ArrayDeclarationNode *p_node, ArrayDeclarationNode::Declaration *p_decl, int &r_array_size, bool &r_is_unknown_size) { + TkPos pos = _get_tkpos(); + Token tk = _get_token(); + + if (tk.type == TK_BRACKET_CLOSE) { + r_is_unknown_size = true; + } else { + if (tk.type != TK_INT_CONSTANT || ((int)tk.constant) <= 0) { + _set_tkpos(pos); + int array_size = 0; + Node *n = _parse_array_size(p_block, p_function_info, array_size); + if (!n) { + return ERR_PARSE_ERROR; + } + p_decl->size = array_size; + p_node->size_expression = n; + } else if (((int)tk.constant) > 0) { + p_decl->size = (uint32_t)tk.constant; + } + + if (p_decl->size <= 0) { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + + r_array_size = p_decl->size; + } + + return OK; +} + +ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info) { + DataType type = TYPE_VOID; + String struct_name = ""; + int array_size = 0; + bool auto_size = false; + bool undefined_size = false; + Token tk = _get_token(); + + if (tk.type == TK_CURLY_BRACKET_OPEN) { + auto_size = true; + } else { + if (shader->structs.has(tk.text)) { + type = TYPE_STRUCT; + struct_name = tk.text; + } else { + if (!is_token_variable_datatype(tk.type)) { + _set_error("Invalid data type for array"); + return nullptr; + } + type = get_token_datatype(tk.type); + } + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + TkPos pos = _get_tkpos(); + tk = _get_token(); + if (tk.type == TK_BRACKET_CLOSE) { + undefined_size = true; + tk = _get_token(); + } else { + _set_tkpos(pos); + + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { + _set_error("Expected single integer constant > 0"); + return nullptr; + } + + ConstantNode *cnode = (ConstantNode *)n; + if (cnode->values.size() == 1) { + array_size = cnode->values[0].sint; + if (array_size <= 0) { + _set_error("Expected single integer constant > 0"); + return nullptr; + } + } else { + _set_error("Expected single integer constant > 0"); + return nullptr; + } + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return nullptr; + } else { + tk = _get_token(); + } + } + } else { + _set_error("Expected '['"); + return nullptr; + } + } + + ArrayConstructNode *an = alloc_node<ArrayConstructNode>(); + + if (tk.type == TK_PARENTHESIS_OPEN || auto_size) { // initialization + int idx = 0; + while (true) { + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + if (!n) { + return nullptr; + } + + // define type by using the first member + if (auto_size && idx == 0) { + type = n->get_datatype(); + if (type == TYPE_STRUCT) { + struct_name = n->get_datatype_name(); + } + } else { + if (!_compare_datatypes(type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) { + return nullptr; + } + } + + tk = _get_token(); + if (tk.type == TK_COMMA) { + an->initializer.push_back(n); + } else if (!auto_size && tk.type == TK_PARENTHESIS_CLOSE) { + an->initializer.push_back(n); + break; + } else if (auto_size && tk.type == TK_CURLY_BRACKET_CLOSE) { + an->initializer.push_back(n); + break; + } else { + if (auto_size) { + _set_error("Expected '}' or ','"); + } else { + _set_error("Expected ')' or ','"); + } + return nullptr; + } + idx++; + } + if (!auto_size && !undefined_size && an->initializer.size() != array_size) { + _set_error("Array size mismatch"); + return nullptr; + } + } else { + _set_error("Expected array initialization!"); + return nullptr; + } + + an->datatype = type; + an->struct_name = struct_name; + return an; +} + +ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size) { + DataType type = TYPE_VOID; + String struct_name = ""; + int array_size = 0; + bool auto_size = false; + TkPos prev_pos = _get_tkpos(); + Token tk = _get_token(); + + if (tk.type == TK_CURLY_BRACKET_OPEN) { + auto_size = true; + } else { + if (shader->structs.has(tk.text)) { + type = TYPE_STRUCT; + struct_name = tk.text; + } else { + if (!is_token_variable_datatype(tk.type)) { + _set_tkpos(prev_pos); + + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + + if (!n) { + _set_error("Invalid data type for array"); + return nullptr; + } + + if (!_compare_datatypes(p_type, p_struct_name, p_array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) { + return nullptr; + } + return n; + } + type = get_token_datatype(tk.type); + } + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + TkPos pos = _get_tkpos(); + tk = _get_token(); + if (tk.type == TK_BRACKET_CLOSE) { + array_size = p_array_size; + tk = _get_token(); + } else { + _set_tkpos(pos); + + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { + _set_error("Expected single integer constant > 0"); + return nullptr; + } + + ConstantNode *cnode = (ConstantNode *)n; + if (cnode->values.size() == 1) { + array_size = cnode->values[0].sint; + if (array_size <= 0) { + _set_error("Expected single integer constant > 0"); + return nullptr; + } + } else { + _set_error("Expected single integer constant > 0"); + return nullptr; + } + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return nullptr; + } else { + tk = _get_token(); + } + } + } else { + _set_error("Expected '['"); + return nullptr; + } + + if (type != p_type || struct_name != p_struct_name || array_size != p_array_size) { + String error_str = "Cannot convert from '"; + if (type == TYPE_STRUCT) { + error_str += struct_name; + } else { + error_str += get_datatype_name(type); + } + error_str += "["; + error_str += itos(array_size); + error_str += "]'"; + error_str += " to '"; + if (type == TYPE_STRUCT) { + error_str += p_struct_name; + } else { + error_str += get_datatype_name(p_type); + } + error_str += "["; + error_str += itos(p_array_size); + error_str += "]'"; + _set_error(error_str); + return nullptr; + } + } + + ArrayConstructNode *an = alloc_node<ArrayConstructNode>(); + an->datatype = p_type; + an->struct_name = p_struct_name; + + if (tk.type == TK_PARENTHESIS_OPEN || auto_size) { // initialization + while (true) { + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + if (!n) { + return nullptr; + } + + if (!_compare_datatypes(p_type, p_struct_name, 0, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) { + return nullptr; + } + + tk = _get_token(); + if (tk.type == TK_COMMA) { + an->initializer.push_back(n); + } else if (!auto_size && tk.type == TK_PARENTHESIS_CLOSE) { + an->initializer.push_back(n); + break; + } else if (auto_size && tk.type == TK_CURLY_BRACKET_CLOSE) { + an->initializer.push_back(n); + break; + } else { + if (auto_size) { + _set_error("Expected '}' or ','"); + } else { + _set_error("Expected ')' or ','"); + } + return nullptr; + } + } + if (an->initializer.size() != p_array_size) { + _set_error("Array size mismatch"); + return nullptr; + } + } else { + _set_error("Expected array initialization!"); + return nullptr; + } + + return an; +} + ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info) { Vector<Expression> expression; @@ -3270,7 +4653,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons return nullptr; } - } else if (tk.type == TK_REAL_CONSTANT) { + } else if (tk.type == TK_FLOAT_CONSTANT) { ConstantNode *constant = alloc_node<ConstantNode>(); ConstantNode::Value v; v.real = tk.constant; @@ -3308,50 +4691,73 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons //make sure void is not used in expression _set_error("Void value not allowed in Expression"); return nullptr; - } else if (is_token_nonvoid_datatype(tk.type)) { - //basic type constructor + } else if (is_token_nonvoid_datatype(tk.type) || tk.type == TK_CURLY_BRACKET_OPEN) { + if (tk.type == TK_CURLY_BRACKET_OPEN) { + //array constructor - OperatorNode *func = alloc_node<OperatorNode>(); - func->op = OP_CONSTRUCT; + _set_tkpos(prepos); + expr = _parse_array_constructor(p_block, p_function_info); + } else { + DataType datatype; + DataPrecision precision; + bool precision_defined = false; - if (is_token_precision(tk.type)) { - func->return_precision_cache = get_token_precision(tk.type); + if (is_token_precision(tk.type)) { + precision = get_token_precision(tk.type); + precision_defined = true; + tk = _get_token(); + } + + datatype = get_token_datatype(tk.type); tk = _get_token(); - } - VariableNode *funcname = alloc_node<VariableNode>(); - funcname->name = get_datatype_name(get_token_datatype(tk.type)); - func->arguments.push_back(funcname); + if (tk.type == TK_BRACKET_OPEN) { + //array constructor - tk = _get_token(); - if (tk.type != TK_PARENTHESIS_OPEN) { - _set_error("Expected '(' after type name"); - return nullptr; - } + _set_tkpos(prepos); + expr = _parse_array_constructor(p_block, p_function_info); + } else { + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after type name"); + return nullptr; + } + //basic type constructor - int carg = -1; + OperatorNode *func = alloc_node<OperatorNode>(); + func->op = OP_CONSTRUCT; - bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg); + if (precision_defined) { + func->return_precision_cache = precision; + } - if (carg >= 0) { - completion_type = COMPLETION_CALL_ARGUMENTS; - completion_line = tk_line; - completion_block = p_block; - completion_function = funcname->name; - completion_argument = carg; - } + VariableNode *funcname = alloc_node<VariableNode>(); + funcname->name = get_datatype_name(datatype); + func->arguments.push_back(funcname); - if (!ok) { - return nullptr; - } + int carg = -1; - if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) { - _set_error("No matching constructor found for: '" + String(funcname->name) + "'"); - return nullptr; - } + bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg); + + if (carg >= 0) { + completion_type = COMPLETION_CALL_ARGUMENTS; + completion_line = tk_line; + completion_block = p_block; + completion_function = funcname->name; + completion_argument = carg; + } + + if (!ok) { + return nullptr; + } - expr = _reduce_expression(p_block, func); + if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) { + _set_error("No matching constructor found for: '" + String(funcname->name) + "'"); + return nullptr; + } + expr = _reduce_expression(p_block, func); + } + } } else if (tk.type == TK_IDENTIFIER) { _set_tkpos(prepos); @@ -3364,6 +4770,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (shader->structs.has(identifier)) { pstruct = shader->structs[identifier].shader_struct; +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_STRUCT_FLAG) && used_structs.has(identifier)) { + used_structs[identifier].used = true; + } +#endif // DEBUG_ENABLED struct_init = true; } @@ -3385,152 +4796,16 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons Node *nexpr; if (pstruct->members[i]->array_size != 0) { - DataType type = pstruct->members[i]->get_datatype(); - String struct_name = pstruct->members[i]->struct_name; - int array_size = pstruct->members[i]->array_size; - - DataType type2; - String struct_name2 = ""; - int array_size2 = 0; - - bool auto_size = false; - - tk = _get_token(); - - if (tk.type == TK_CURLY_BRACKET_OPEN) { - auto_size = true; - } else { - if (shader->structs.has(tk.text)) { - type2 = TYPE_STRUCT; - struct_name2 = tk.text; - } else { - if (!is_token_variable_datatype(tk.type)) { - _set_error("Invalid data type for array"); - return nullptr; - } - type2 = get_token_datatype(tk.type); - } - - tk = _get_token(); - if (tk.type == TK_BRACKET_OPEN) { - TkPos pos2 = _get_tkpos(); - tk = _get_token(); - if (tk.type == TK_BRACKET_CLOSE) { - array_size2 = array_size; - tk = _get_token(); - } else { - _set_tkpos(pos2); - - Node *n = _parse_and_reduce_expression(p_block, p_function_info); - if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { - _set_error("Expected single integer constant > 0"); - return nullptr; - } - - ConstantNode *cnode = (ConstantNode *)n; - if (cnode->values.size() == 1) { - array_size2 = cnode->values[0].sint; - if (array_size2 <= 0) { - _set_error("Expected single integer constant > 0"); - return nullptr; - } - } else { - _set_error("Expected single integer constant > 0"); - return nullptr; - } - - tk = _get_token(); - if (tk.type != TK_BRACKET_CLOSE) { - _set_error("Expected ']'"); - return nullptr; - } else { - tk = _get_token(); - } - } - } else { - _set_error("Expected '['"); - return nullptr; - } - - if (type != type2 || struct_name != struct_name2 || array_size != array_size2) { - String error_str = "Cannot convert from '"; - if (type2 == TYPE_STRUCT) { - error_str += struct_name2; - } else { - error_str += get_datatype_name(type2); - } - error_str += "["; - error_str += itos(array_size2); - error_str += "]'"; - error_str += " to '"; - if (type == TYPE_STRUCT) { - error_str += struct_name; - } else { - error_str += get_datatype_name(type); - } - error_str += "["; - error_str += itos(array_size); - error_str += "]'"; - _set_error(error_str); - return nullptr; - } - } - - ArrayConstructNode *an = alloc_node<ArrayConstructNode>(); - an->datatype = type; - an->struct_name = struct_name; - - if (tk.type == TK_PARENTHESIS_OPEN || auto_size) { // initialization - while (true) { - Node *n = _parse_and_reduce_expression(p_block, p_function_info); - if (!n) { - return nullptr; - } - - if (type != n->get_datatype() || struct_name != n->get_datatype_name()) { - _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (type == TYPE_STRUCT ? struct_name : get_datatype_name(type)) + "'"); - return nullptr; - } - - tk = _get_token(); - if (tk.type == TK_COMMA) { - an->initializer.push_back(n); - continue; - } else if (!auto_size && tk.type == TK_PARENTHESIS_CLOSE) { - an->initializer.push_back(n); - break; - } else if (auto_size && tk.type == TK_CURLY_BRACKET_CLOSE) { - an->initializer.push_back(n); - break; - } else { - if (auto_size) { - _set_error("Expected '}' or ','"); - } else { - _set_error("Expected ')' or ','"); - } - return nullptr; - } - } - if (an->initializer.size() != array_size) { - _set_error("Array size mismatch"); - return nullptr; - } - } else { - _set_error("Expected array initialization!"); + nexpr = _parse_array_constructor(p_block, p_function_info, pstruct->members[i]->get_datatype(), pstruct->members[i]->struct_name, pstruct->members[i]->array_size); + if (!nexpr) { return nullptr; } - - nexpr = an; } else { nexpr = _parse_and_reduce_expression(p_block, p_function_info); if (!nexpr) { return nullptr; } - Node *node = pstruct->members[i]; if (!_compare_datatypes_in_nodes(pstruct->members[i], nexpr)) { - String type_name = nexpr->get_datatype() == TYPE_STRUCT ? nexpr->get_datatype_name() : get_datatype_name(nexpr->get_datatype()); - String type_name2 = node->get_datatype() == TYPE_STRUCT ? node->get_datatype_name() : get_datatype_name(node->get_datatype()); - _set_error("Invalid assignment of '" + type_name + "' to '" + type_name2 + "'"); return nullptr; } } @@ -3552,7 +4827,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expr = func; - } else { //a function + } else { //a function call const StringName &name = identifier; @@ -3617,6 +4892,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons FunctionNode *call_function = shader->functions[function_index].function; if (call_function) { + func->return_cache = call_function->get_datatype(); + func->struct_name = call_function->get_datatype_name(); + func->return_array_size = call_function->get_array_size(); + //get current base function FunctionNode *base_function = nullptr; { @@ -3658,6 +4937,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (shader->uniforms.has(varname)) { error = true; } else { + if (shader->varyings.has(varname)) { + _set_error(vformat("Varyings cannot be passed for '%s' parameter!", _get_qualifier_str(call_function->arguments[i].qualifier))); + return nullptr; + } if (p_function_info.built_ins.has(varname)) { BuiltInInfo info = p_function_info.built_ins[varname]; if (info.constant) { @@ -3719,16 +5002,30 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } } expr = func; +#ifdef DEBUG_ENABLED + if (check_warnings) { + StringName func_name; + + if (p_block && p_block->parent_function) { + func_name = p_block->parent_function->name; + } + + _parse_used_identifier(name, IdentifierType::IDENTIFIER_FUNCTION, func_name); + } +#endif // DEBUG_ENABLED } } else { //an identifier + last_name = identifier; + last_type = IDENTIFIER_MAX; _set_tkpos(pos); DataType data_type; IdentifierType ident_type; int array_size = 0; StringName struct_name; + bool is_local = false; if (p_block && p_block->block_tag != SubClassTag::TAG_GLOBAL) { int idx = 0; @@ -3750,25 +5047,63 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons _set_error("Unknown identifier in expression: " + String(identifier)); return nullptr; } + if (ident_type == IDENTIFIER_VARYING) { + TkPos prev_pos = _get_tkpos(); + Token next_token = _get_token(); + + // An array of varyings. + if (next_token.type == TK_BRACKET_OPEN) { + _get_token(); // Pass constant. + _get_token(); // Pass TK_BRACKET_CLOSE. + next_token = _get_token(); + } + _set_tkpos(prev_pos); + + String error; + if (is_token_operator_assign(next_token.type)) { + if (!_validate_varying_assign(shader->varyings[identifier], &error)) { + _set_error(error); + return nullptr; + } + } else { + if (!_validate_varying_using(shader->varyings[identifier], &error)) { + _set_error(error); + return nullptr; + } + } + } if (ident_type == IDENTIFIER_FUNCTION) { _set_error("Can't use function as identifier: " + String(identifier)); return nullptr; } + if (is_const) { + last_type = IDENTIFIER_CONSTANT; + } else { + last_type = ident_type; + } + + is_local = ident_type == IDENTIFIER_LOCAL_VAR || ident_type == IDENTIFIER_FUNCTION_ARGUMENT; } Node *index_expression = nullptr; Node *call_expression = nullptr; + Node *assign_expression = nullptr; if (array_size > 0) { + prepos = _get_tkpos(); tk = _get_token(); - if (tk.type != TK_BRACKET_OPEN && tk.type != TK_PERIOD) { - _set_error("Expected '[' or '.'"); - return nullptr; - } - - if (tk.type == TK_PERIOD) { + if (tk.type == TK_OP_ASSIGN) { + if (is_const) { + _set_error("Constants cannot be modified."); + return nullptr; + } + assign_expression = _parse_array_constructor(p_block, p_function_info, data_type, struct_name, array_size); + if (!assign_expression) { + return nullptr; + } + } else if (tk.type == TK_PERIOD) { completion_class = TAG_ARRAY; p_block->block_tag = SubClassTag::TAG_ARRAY; call_expression = _parse_and_reduce_expression(p_block, p_function_info); @@ -3777,22 +5112,21 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons return nullptr; } data_type = call_expression->get_datatype(); - } else { // indexing - + } else if (tk.type == TK_BRACKET_OPEN) { // indexing index_expression = _parse_and_reduce_expression(p_block, p_function_info); if (!index_expression) { return nullptr; } - if (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT) { - _set_error("Only integer expressions are allowed for indexing"); + if (index_expression->get_array_size() != 0 || (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT)) { + _set_error("Only integer expressions are allowed for indexing."); return nullptr; } if (index_expression->type == Node::TYPE_CONSTANT) { ConstantNode *cnode = (ConstantNode *)index_expression; if (cnode) { - if (!cnode->values.empty()) { + if (!cnode->values.is_empty()) { int value = cnode->values[0].sint; if (value < 0 || value >= array_size) { _set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1)); @@ -3807,6 +5141,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons _set_error("Expected ']'"); return nullptr; } + } else { + _set_tkpos(prepos); } ArrayNode *arrname = alloc_node<ArrayNode>(); @@ -3815,17 +5151,31 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons arrname->struct_name = struct_name; arrname->index_expression = index_expression; arrname->call_expression = call_expression; + arrname->assign_expression = assign_expression; arrname->is_const = is_const; + arrname->array_size = array_size; + arrname->is_local = is_local; expr = arrname; - } else { VariableNode *varname = alloc_node<VariableNode>(); varname->name = identifier; varname->datatype_cache = data_type; varname->is_const = is_const; varname->struct_name = struct_name; + varname->is_local = is_local; expr = varname; } +#ifdef DEBUG_ENABLED + if (check_warnings) { + StringName func_name; + + if (p_block && p_block->parent_function) { + func_name = p_block->parent_function->name; + } + + _parse_used_identifier(identifier, ident_type, func_name); + } +#endif // DEBUG_ENABLED } } else if (tk.type == TK_OP_ADD) { continue; //this one does nothing @@ -3864,8 +5214,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons ERR_FAIL_COND_V(!expr, nullptr); /* OK now see what's NEXT to the operator.. */ - /* OK now see what's NEXT to the operator.. */ - /* OK now see what's NEXT to the operator.. */ while (true) { TkPos pos2 = _get_tkpos(); @@ -3878,6 +5226,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons DataType dt = expr->get_datatype(); String st = expr->get_datatype_name(); + if (!expr->is_indexed() && expr->get_array_size() > 0) { + completion_class = TAG_ARRAY; + p_block->block_tag = SubClassTag::TAG_ARRAY; + Node *call_expression = _parse_and_reduce_expression(p_block, p_function_info); + p_block->block_tag = SubClassTag::TAG_GLOBAL; + if (!call_expression) { + return nullptr; + } + expr = call_expression; + break; + } + StringName identifier; if (_get_completable_identifier(p_block, dt == TYPE_STRUCT ? COMPLETION_STRUCT : COMPLETION_INDEX, identifier)) { if (dt == TYPE_STRUCT) { @@ -3911,12 +5271,12 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons String member_name = String(ident.ptr()); if (shader->structs.has(st)) { StructNode *n = shader->structs[st].shader_struct; - for (List<MemberNode *>::Element *E = n->members.front(); E; E = E->next()) { - if (String(E->get()->name) == member_name) { - member_type = E->get()->datatype; - array_size = E->get()->array_size; + for (const MemberNode *E : n->members) { + if (String(E->name) == member_name) { + member_type = E->datatype; + array_size = E->array_size; if (member_type == TYPE_STRUCT) { - member_struct_name = E->get()->struct_name; + member_struct_name = E->struct_name; } ok = true; break; @@ -4154,25 +5514,43 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons mn->has_swizzling_duplicates = repeated; if (array_size > 0) { + TkPos prev_pos = _get_tkpos(); tk = _get_token(); - if (tk.type == TK_PERIOD) { - _set_error("Nested array length() is not yet implemented"); - return nullptr; + if (tk.type == TK_OP_ASSIGN) { + if (last_type == IDENTIFIER_CONSTANT) { + _set_error("Constants cannot be modified."); + return nullptr; + } + Node *assign_expression = _parse_array_constructor(p_block, p_function_info, member_type, member_struct_name, array_size); + if (!assign_expression) { + return nullptr; + } + mn->assign_expression = assign_expression; + } else if (tk.type == TK_PERIOD) { + completion_class = TAG_ARRAY; + p_block->block_tag = SubClassTag::TAG_ARRAY; + Node *call_expression = _parse_and_reduce_expression(p_block, p_function_info); + p_block->block_tag = SubClassTag::TAG_GLOBAL; + if (!call_expression) { + return nullptr; + } + mn->datatype = call_expression->get_datatype(); + mn->call_expression = call_expression; } else if (tk.type == TK_BRACKET_OPEN) { Node *index_expression = _parse_and_reduce_expression(p_block, p_function_info); if (!index_expression) { return nullptr; } - if (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT) { - _set_error("Only integer expressions are allowed for indexing"); + if (index_expression->get_array_size() != 0 || (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT)) { + _set_error("Only integer expressions are allowed for indexing."); return nullptr; } if (index_expression->type == Node::TYPE_CONSTANT) { ConstantNode *cnode = (ConstantNode *)index_expression; if (cnode) { - if (!cnode->values.empty()) { + if (!cnode->values.is_empty()) { int value = cnode->values[0].sint; if (value < 0 || value >= array_size) { _set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1)); @@ -4188,13 +5566,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons return nullptr; } mn->index_expression = index_expression; - } else { - _set_error("Expected '[' or '.'"); - return nullptr; + _set_tkpos(prev_pos); } } - expr = mn; //todo @@ -4213,123 +5588,138 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons return nullptr; } - if (index->get_datatype() != TYPE_INT && index->get_datatype() != TYPE_UINT) { - _set_error("Only integer datatypes are allowed for indexing"); + if (index->get_array_size() != 0 || (index->get_datatype() != TYPE_INT && index->get_datatype() != TYPE_UINT)) { + _set_error("Only integer expressions are allowed for indexing."); return nullptr; } DataType member_type = TYPE_VOID; + String member_struct_name; - switch (expr->get_datatype()) { - case TYPE_BVEC2: - case TYPE_VEC2: - case TYPE_IVEC2: - case TYPE_UVEC2: - case TYPE_MAT2: - if (index->type == Node::TYPE_CONSTANT) { - uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; - if (index_constant >= 2) { - _set_error("Index out of range (0-1)"); - return nullptr; - } + if (expr->get_array_size() > 0) { + if (index->type == Node::TYPE_CONSTANT) { + uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; + if (index_constant >= (uint32_t)expr->get_array_size()) { + _set_error(vformat("Index [%s] out of range [%s..%s]", index_constant, 0, expr->get_array_size() - 1)); + return nullptr; } + } + member_type = expr->get_datatype(); + if (member_type == TYPE_STRUCT) { + member_struct_name = expr->get_datatype_name(); + } + } else { + switch (expr->get_datatype()) { + case TYPE_BVEC2: + case TYPE_VEC2: + case TYPE_IVEC2: + case TYPE_UVEC2: + case TYPE_MAT2: + if (index->type == Node::TYPE_CONSTANT) { + uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; + if (index_constant >= 2) { + _set_error("Index out of range (0-1)"); + return nullptr; + } + } - switch (expr->get_datatype()) { - case TYPE_BVEC2: - member_type = TYPE_BOOL; - break; - case TYPE_VEC2: - member_type = TYPE_FLOAT; - break; - case TYPE_IVEC2: - member_type = TYPE_INT; - break; - case TYPE_UVEC2: - member_type = TYPE_UINT; - break; - case TYPE_MAT2: - member_type = TYPE_VEC2; - break; - default: - break; - } + switch (expr->get_datatype()) { + case TYPE_BVEC2: + member_type = TYPE_BOOL; + break; + case TYPE_VEC2: + member_type = TYPE_FLOAT; + break; + case TYPE_IVEC2: + member_type = TYPE_INT; + break; + case TYPE_UVEC2: + member_type = TYPE_UINT; + break; + case TYPE_MAT2: + member_type = TYPE_VEC2; + break; + default: + break; + } - break; - case TYPE_BVEC3: - case TYPE_VEC3: - case TYPE_IVEC3: - case TYPE_UVEC3: - case TYPE_MAT3: - if (index->type == Node::TYPE_CONSTANT) { - uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; - if (index_constant >= 3) { - _set_error("Index out of range (0-2)"); - return nullptr; + break; + case TYPE_BVEC3: + case TYPE_VEC3: + case TYPE_IVEC3: + case TYPE_UVEC3: + case TYPE_MAT3: + if (index->type == Node::TYPE_CONSTANT) { + uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; + if (index_constant >= 3) { + _set_error("Index out of range (0-2)"); + return nullptr; + } } - } - switch (expr->get_datatype()) { - case TYPE_BVEC3: - member_type = TYPE_BOOL; - break; - case TYPE_VEC3: - member_type = TYPE_FLOAT; - break; - case TYPE_IVEC3: - member_type = TYPE_INT; - break; - case TYPE_UVEC3: - member_type = TYPE_UINT; - break; - case TYPE_MAT3: - member_type = TYPE_VEC3; - break; - default: - break; - } - break; - case TYPE_BVEC4: - case TYPE_VEC4: - case TYPE_IVEC4: - case TYPE_UVEC4: - case TYPE_MAT4: - if (index->type == Node::TYPE_CONSTANT) { - uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; - if (index_constant >= 4) { - _set_error("Index out of range (0-3)"); - return nullptr; + switch (expr->get_datatype()) { + case TYPE_BVEC3: + member_type = TYPE_BOOL; + break; + case TYPE_VEC3: + member_type = TYPE_FLOAT; + break; + case TYPE_IVEC3: + member_type = TYPE_INT; + break; + case TYPE_UVEC3: + member_type = TYPE_UINT; + break; + case TYPE_MAT3: + member_type = TYPE_VEC3; + break; + default: + break; + } + break; + case TYPE_BVEC4: + case TYPE_VEC4: + case TYPE_IVEC4: + case TYPE_UVEC4: + case TYPE_MAT4: + if (index->type == Node::TYPE_CONSTANT) { + uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; + if (index_constant >= 4) { + _set_error("Index out of range (0-3)"); + return nullptr; + } } - } - switch (expr->get_datatype()) { - case TYPE_BVEC4: - member_type = TYPE_BOOL; - break; - case TYPE_VEC4: - member_type = TYPE_FLOAT; - break; - case TYPE_IVEC4: - member_type = TYPE_INT; - break; - case TYPE_UVEC4: - member_type = TYPE_UINT; - break; - case TYPE_MAT4: - member_type = TYPE_VEC4; - break; - default: - break; + switch (expr->get_datatype()) { + case TYPE_BVEC4: + member_type = TYPE_BOOL; + break; + case TYPE_VEC4: + member_type = TYPE_FLOAT; + break; + case TYPE_IVEC4: + member_type = TYPE_INT; + break; + case TYPE_UVEC4: + member_type = TYPE_UINT; + break; + case TYPE_MAT4: + member_type = TYPE_VEC4; + break; + default: + break; + } + break; + default: { + _set_error("Object of type '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' can't be indexed"); + return nullptr; } - break; - default: { - _set_error("Object of type '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' can't be indexed"); - return nullptr; } } - OperatorNode *op = alloc_node<OperatorNode>(); op->op = OP_INDEX; op->return_cache = member_type; + op->struct_name = member_struct_name; op->arguments.push_back(expr); op->arguments.push_back(index); expr = op; @@ -4345,7 +5735,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons op->op = tk.type == TK_OP_DECREMENT ? OP_POST_DECREMENT : OP_POST_INCREMENT; op->arguments.push_back(expr); - if (!_validate_operator(op, &op->return_cache)) { + if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { _set_error("Invalid base type for increment/decrement operator"); return nullptr; } @@ -4496,9 +5886,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons bool unary = false; bool ternary = false; + Operator op = expression[i].op; int priority; - switch (expression[i].op) { + switch (op) { case OP_EQUAL: priority = 8; break; @@ -4619,6 +6010,14 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons ERR_FAIL_V(nullptr); //unexpected operator } +#if DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::FLOAT_COMPARISON_FLAG) && (op == OP_EQUAL || op == OP_NOT_EQUAL) && + (!expression[i - 1].is_op && !expression[i + 1].is_op) && + (expression[i - 1].node->get_datatype() == TYPE_FLOAT && expression[i + 1].node->get_datatype() == TYPE_FLOAT)) { + _add_line_warning(ShaderWarning::FLOAT_COMPARISON); + } +#endif // DEBUG_ENABLED + if (priority < min_priority) { // < is used for left to right (default) // <= is used for right to left @@ -4632,7 +6031,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons ERR_FAIL_COND_V(next_op == -1, nullptr); // OK! create operator.. - // OK! create operator.. if (is_unary) { int expr_pos = next_op; while (expression[expr_pos].is_op) { @@ -4657,13 +6055,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.write[i].is_op = false; expression.write[i].node = op; - if (!_validate_operator(op, &op->return_cache)) { + if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { String at; for (int j = 0; j < op->arguments.size(); j++) { if (j > 0) { at += " and "; } at += get_datatype_name(op->arguments[j]->get_datatype()); + if (!op->arguments[j]->is_indexed() && op->arguments[j]->get_array_size() > 0) { + at += "["; + at += itos(op->arguments[j]->get_array_size()); + at += "]"; + } } _set_error("Invalid arguments to unary operator '" + get_operator_text(op->op) + "' :" + at); return nullptr; @@ -4690,13 +6093,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.write[next_op - 1].is_op = false; expression.write[next_op - 1].node = op; - if (!_validate_operator(op, &op->return_cache)) { + if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { String at; for (int i = 0; i < op->arguments.size(); i++) { if (i > 0) { at += " and "; } at += get_datatype_name(op->arguments[i]->get_datatype()); + if (!op->arguments[i]->is_indexed() && op->arguments[i]->get_array_size() > 0) { + at += "["; + at += itos(op->arguments[i]->get_array_size()); + at += "]"; + } } _set_error("Invalid argument to ternary ?: operator: " + at); return nullptr; @@ -4743,7 +6151,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons //replace all 3 nodes by this operator and make it an expression - if (!_validate_operator(op, &op->return_cache)) { + if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { String at; for (int i = 0; i < op->arguments.size(); i++) { if (i > 0) { @@ -4754,6 +6162,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else { at += get_datatype_name(op->arguments[i]->get_datatype()); } + if (!op->arguments[i]->is_indexed() && op->arguments[i]->get_array_size() > 0) { + at += "["; + at += itos(op->arguments[i]->get_array_size()); + at += "]"; + } } _set_error("Invalid arguments to operator '" + get_operator_text(op->op) + "' :" + at); return nullptr; @@ -4964,11 +6377,46 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun Node *vardecl = nullptr; while (true) { - if (tk.type != TK_IDENTIFIER) { - _set_error("Expected identifier after type"); + bool unknown_size = false; + int array_size = 0; + + ArrayDeclarationNode *anode = nullptr; + ArrayDeclarationNode::Declaration adecl; + + if (tk.type != TK_IDENTIFIER && tk.type != TK_BRACKET_OPEN) { + _set_error("Expected identifier or '[' after type."); return ERR_PARSE_ERROR; } + if (tk.type == TK_BRACKET_OPEN) { + anode = alloc_node<ArrayDeclarationNode>(); + + if (is_struct) { + anode->struct_name = struct_name; + anode->datatype = TYPE_STRUCT; + } else { + anode->datatype = type; + } + + anode->precision = precision; + anode->is_const = is_const; + vardecl = (Node *)anode; + + adecl.size = 0U; + adecl.single_expression = false; + + Error error = _parse_local_array_size(p_block, p_function_info, anode, &adecl, array_size, unknown_size); + if (error != OK) { + return error; + } + tk = _get_token(); + + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier!"); + return ERR_PARSE_ERROR; + } + } + StringName name = tk.text; ShaderLanguage::IdentifierType itype; if (_find_identifier(p_block, true, p_function_info, name, (ShaderLanguage::DataType *)nullptr, &itype)) { @@ -4978,254 +6426,283 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun } } + adecl.name = name; + +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_LOCAL_VARIABLE_FLAG)) { + if (p_block && p_block->parent_function) { + StringName func_name = p_block->parent_function->name; + + if (!used_local_vars.has(func_name)) { + used_local_vars.insert(func_name, Map<StringName, Usage>()); + } + + used_local_vars[func_name].insert(name, Usage(tk_line)); + } + } +#endif // DEBUG_ENABLED + BlockNode::Variable var; var.type = type; var.precision = precision; var.line = tk_line; - var.array_size = 0; + var.array_size = array_size; var.is_const = is_const; var.struct_name = struct_name; tk = _get_token(); if (tk.type == TK_BRACKET_OPEN) { - bool unknown_size = false; + if (var.array_size > 0 || unknown_size) { + _set_error("Array size is already defined!"); + return ERR_PARSE_ERROR; + } if (RenderingServer::get_singleton()->is_low_end() && is_const) { _set_error("Local const arrays are supported only on high-end platform!"); return ERR_PARSE_ERROR; } - ArrayDeclarationNode *node = alloc_node<ArrayDeclarationNode>(); + anode = alloc_node<ArrayDeclarationNode>(); if (is_struct) { - node->struct_name = struct_name; - node->datatype = TYPE_STRUCT; + anode->struct_name = struct_name; + anode->datatype = TYPE_STRUCT; } else { - node->datatype = type; + anode->datatype = type; } - node->precision = precision; - node->is_const = is_const; - vardecl = (Node *)node; - - ArrayDeclarationNode::Declaration decl; - decl.name = name; - decl.size = 0U; - - tk = _get_token(); - - if (tk.type == TK_BRACKET_CLOSE) { - unknown_size = true; - } else { - if (tk.type != TK_INT_CONSTANT || ((int)tk.constant) <= 0) { - _set_error("Expected integer constant > 0 or ']'"); - return ERR_PARSE_ERROR; - } + anode->precision = precision; + anode->is_const = is_const; + vardecl = (Node *)anode; - decl.size = ((uint32_t)tk.constant); - tk = _get_token(); + adecl.size = 0U; + adecl.single_expression = false; - if (tk.type != TK_BRACKET_CLOSE) { - _set_error("Expected ']'"); - return ERR_PARSE_ERROR; - } - var.array_size = decl.size; + Error error = _parse_local_array_size(p_block, p_function_info, anode, &adecl, var.array_size, unknown_size); + if (error != OK) { + return error; } + tk = _get_token(); + } + if (var.array_size > 0 || unknown_size) { bool full_def = false; - tk = _get_token(); if (tk.type == TK_OP_ASSIGN) { if (RenderingServer::get_singleton()->is_low_end()) { _set_error("Array initialization is supported only on high-end platform!"); return ERR_PARSE_ERROR; } + TkPos prev_pos = _get_tkpos(); tk = _get_token(); - if (tk.type != TK_CURLY_BRACKET_OPEN) { - if (unknown_size) { - _set_error("Expected '{'"); - return ERR_PARSE_ERROR; - } - - full_def = true; + if (tk.type == TK_IDENTIFIER) { // a function call array initialization + _set_tkpos(prev_pos); + Node *n = _parse_and_reduce_expression(p_block, p_function_info); - DataPrecision precision2 = PRECISION_DEFAULT; - if (is_token_precision(tk.type)) { - precision2 = get_token_precision(tk.type); - tk = _get_token(); - if (shader->structs.has(tk.text)) { - _set_error("Precision modifier cannot be used on structs."); - return ERR_PARSE_ERROR; + if (!n) { + _set_error("Expected correct array initializer!"); + return ERR_PARSE_ERROR; + } else { + if (unknown_size) { + adecl.size = n->get_array_size(); + var.array_size = n->get_array_size(); } - if (!is_token_nonvoid_datatype(tk.type)) { - _set_error("Expected datatype after precision"); + + if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) { return ERR_PARSE_ERROR; } - } - DataType type2; - StringName struct_name2 = ""; + adecl.single_expression = true; + adecl.initializer.push_back(n); + } - if (shader->structs.has(tk.text)) { - type2 = TYPE_STRUCT; - struct_name2 = tk.text; - } else { - if (!is_token_variable_datatype(tk.type)) { - _set_error("Invalid data type for array"); + tk = _get_token(); + } else { + if (tk.type != TK_CURLY_BRACKET_OPEN) { + if (unknown_size) { + _set_error("Expected '{'"); return ERR_PARSE_ERROR; } - type2 = get_token_datatype(tk.type); - } - int array_size2 = 0; + full_def = true; - tk = _get_token(); - if (tk.type == TK_BRACKET_OPEN) { - TkPos pos2 = _get_tkpos(); - tk = _get_token(); - if (tk.type == TK_BRACKET_CLOSE) { - array_size2 = var.array_size; + DataPrecision precision2 = PRECISION_DEFAULT; + if (is_token_precision(tk.type)) { + precision2 = get_token_precision(tk.type); tk = _get_token(); - } else { - _set_tkpos(pos2); - - Node *n = _parse_and_reduce_expression(p_block, p_function_info); - if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { - _set_error("Expected single integer constant > 0"); + if (shader->structs.has(tk.text)) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } + if (!is_token_nonvoid_datatype(tk.type)) { + _set_error("Expected datatype after precision"); return ERR_PARSE_ERROR; } + } - ConstantNode *cnode = (ConstantNode *)n; - if (cnode->values.size() == 1) { - array_size2 = cnode->values[0].sint; - if (array_size2 <= 0) { - _set_error("Expected single integer constant > 0"); - return ERR_PARSE_ERROR; - } - } else { - _set_error("Expected single integer constant > 0"); + DataType type2; + StringName struct_name2 = ""; + + if (shader->structs.has(tk.text)) { + type2 = TYPE_STRUCT; + struct_name2 = tk.text; + } else { + if (!is_token_variable_datatype(tk.type)) { + _set_error("Invalid data type for array"); return ERR_PARSE_ERROR; } + type2 = get_token_datatype(tk.type); + } + + int array_size2 = 0; + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + TkPos pos2 = _get_tkpos(); tk = _get_token(); - if (tk.type != TK_BRACKET_CLOSE) { - _set_error("Expected ']'"); - return ERR_PARSE_ERROR; + if (tk.type == TK_BRACKET_CLOSE) { + array_size2 = var.array_size; + tk = _get_token(); } else { + _set_tkpos(pos2); + + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + + ConstantNode *cnode = (ConstantNode *)n; + if (cnode->values.size() == 1) { + array_size2 = cnode->values[0].sint; + if (array_size2 <= 0) { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + } else { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } else { + tk = _get_token(); + } } - } - } else { - _set_error("Expected '['"); - return ERR_PARSE_ERROR; - } - - if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) { - String error_str = "Cannot convert from '"; - if (precision2 != PRECISION_DEFAULT) { - error_str += get_precision_name(precision2); - error_str += " "; - } - if (type2 == TYPE_STRUCT) { - error_str += struct_name2; } else { - error_str += get_datatype_name(type2); - } - error_str += "["; - error_str += itos(array_size2); - error_str += "]'"; - error_str += " to '"; - if (precision != PRECISION_DEFAULT) { - error_str += get_precision_name(precision); - error_str += " "; + _set_error("Expected '['"); + return ERR_PARSE_ERROR; } - if (type == TYPE_STRUCT) { - error_str += struct_name; - } else { - error_str += get_datatype_name(type); + + if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) { + String error_str = "Cannot convert from '"; + if (precision2 != PRECISION_DEFAULT) { + error_str += get_precision_name(precision2); + error_str += " "; + } + if (type2 == TYPE_STRUCT) { + error_str += struct_name2; + } else { + error_str += get_datatype_name(type2); + } + error_str += "["; + error_str += itos(array_size2); + error_str += "]'"; + error_str += " to '"; + if (precision != PRECISION_DEFAULT) { + error_str += get_precision_name(precision); + error_str += " "; + } + if (type == TYPE_STRUCT) { + error_str += struct_name; + } else { + error_str += get_datatype_name(type); + } + error_str += "["; + error_str += itos(var.array_size); + error_str += "]'"; + _set_error(error_str); + return ERR_PARSE_ERROR; } - error_str += "["; - error_str += itos(var.array_size); - error_str += "]'"; - _set_error(error_str); - return ERR_PARSE_ERROR; } - } - bool curly = tk.type == TK_CURLY_BRACKET_OPEN; + bool curly = tk.type == TK_CURLY_BRACKET_OPEN; - if (unknown_size) { - if (!curly) { - _set_error("Expected '{'"); - return ERR_PARSE_ERROR; - } - } else { - if (full_def) { - if (curly) { - _set_error("Expected '('"); + if (unknown_size) { + if (!curly) { + _set_error("Expected '{'"); return ERR_PARSE_ERROR; } + } else { + if (full_def) { + if (curly) { + _set_error("Expected '('"); + return ERR_PARSE_ERROR; + } + } } - } - if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization - while (true) { - Node *n = _parse_and_reduce_expression(p_block, p_function_info); - if (!n) { - return ERR_PARSE_ERROR; - } + if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization + while (true) { + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + if (!n) { + return ERR_PARSE_ERROR; + } - if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { - _set_error("Expected constant expression"); - return ERR_PARSE_ERROR; - } + if (anode->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { + _set_error("Expected constant expression"); + return ERR_PARSE_ERROR; + } - if (var.type != n->get_datatype() || struct_name != n->get_datatype_name()) { - _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? struct_name : get_datatype_name(var.type)) + "'"); - return ERR_PARSE_ERROR; - } + if (!_compare_datatypes(var.type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) { + return ERR_PARSE_ERROR; + } - tk = _get_token(); - if (tk.type == TK_COMMA) { - decl.initializer.push_back(n); - continue; - } else if (!curly && tk.type == TK_PARENTHESIS_CLOSE) { - decl.initializer.push_back(n); - break; - } else if (curly && tk.type == TK_CURLY_BRACKET_CLOSE) { - decl.initializer.push_back(n); - break; - } else { - if (curly) { - _set_error("Expected '}' or ','"); + tk = _get_token(); + if (tk.type == TK_COMMA) { + adecl.initializer.push_back(n); + continue; + } else if (!curly && tk.type == TK_PARENTHESIS_CLOSE) { + adecl.initializer.push_back(n); + break; + } else if (curly && tk.type == TK_CURLY_BRACKET_CLOSE) { + adecl.initializer.push_back(n); + break; } else { - _set_error("Expected ')' or ','"); + if (curly) { + _set_error("Expected '}' or ','"); + } else { + _set_error("Expected ')' or ','"); + } + return ERR_PARSE_ERROR; } + } + if (unknown_size) { + adecl.size = adecl.initializer.size(); + var.array_size = adecl.initializer.size(); + } else if (adecl.initializer.size() != var.array_size) { + _set_error("Array size mismatch"); return ERR_PARSE_ERROR; } + tk = _get_token(); } - if (unknown_size) { - decl.size = decl.initializer.size(); - var.array_size = decl.initializer.size(); - } else if (decl.initializer.size() != var.array_size) { - _set_error("Array size mismatch"); - return ERR_PARSE_ERROR; - } - tk = _get_token(); } } else { if (unknown_size) { _set_error("Expected array initialization"); return ERR_PARSE_ERROR; } - if (is_const) { + if (anode->is_const) { _set_error("Expected initialization of constant"); return ERR_PARSE_ERROR; } } - node->declarations.push_back(decl); + anode->declarations.push_back(adecl); } else if (tk.type == TK_OP_ASSIGN) { VariableDeclarationNode *node = alloc_node<VariableDeclarationNode>(); if (is_struct) { @@ -5248,13 +6725,24 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun return ERR_PARSE_ERROR; } if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { - _set_error("Expected constant expression after '='"); - return ERR_PARSE_ERROR; + OperatorNode *op = ((OperatorNode *)n); + for (int i = 1; i < op->arguments.size(); i++) { + if (!_check_node_constness(op->arguments[i])) { + _set_error("Expected constant expression for argument '" + itos(i - 1) + "' of function call after '='"); + return ERR_PARSE_ERROR; + } + } } decl.initializer = n; - if (var.type == TYPE_STRUCT ? (var.struct_name != n->get_datatype_name()) : (var.type != n->get_datatype())) { - _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? String(var.struct_name) : get_datatype_name(var.type)) + "'"); + if (n->type == Node::TYPE_CONSTANT) { + ConstantNode *const_node = static_cast<ConstantNode *>(n); + if (const_node && const_node->values.size() == 1) { + var.value = const_node->values[0]; + } + } + + if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) { return ERR_PARSE_ERROR; } tk = _get_token(); @@ -5284,7 +6772,6 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun p_block->statements.push_back(vardecl); p_block->variables[name] = var; - if (tk.type == TK_COMMA) { if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR) { _set_error("Multiple declarations in 'for' loop are not implemented yet."); @@ -5421,18 +6908,29 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun ControlFlowNode *flow = (ControlFlowNode *)switch_block->statements[i]; if (flow) { if (flow->flow_op == FLOW_OP_CASE) { - ConstantNode *n2 = static_cast<ConstantNode *>(flow->expressions[0]); - if (!n2) { - return ERR_PARSE_ERROR; - } - if (n2->values.empty()) { - return ERR_PARSE_ERROR; - } - if (constants.has(n2->values[0].sint)) { - _set_error("Duplicated case label: '" + itos(n2->values[0].sint) + "'"); - return ERR_PARSE_ERROR; + if (flow->expressions[0]->type == Node::TYPE_CONSTANT) { + ConstantNode *cn = static_cast<ConstantNode *>(flow->expressions[0]); + if (!cn || cn->values.is_empty()) { + return ERR_PARSE_ERROR; + } + if (constants.has(cn->values[0].sint)) { + _set_error("Duplicated case label: '" + itos(cn->values[0].sint) + "'"); + return ERR_PARSE_ERROR; + } + constants.insert(cn->values[0].sint); + } else if (flow->expressions[0]->type == Node::TYPE_VARIABLE) { + VariableNode *vn = static_cast<VariableNode *>(flow->expressions[0]); + if (!vn) { + return ERR_PARSE_ERROR; + } + ConstantNode::Value v; + _find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, nullptr, nullptr, nullptr, &v); + if (constants.has(v.sint)) { + _set_error("Duplicated case label: '" + itos(v.sint) + "'"); + return ERR_PARSE_ERROR; + } + constants.insert(v.sint); } - constants.insert(n2->values[0].sint); } else if (flow->flow_op == FLOW_OP_DEFAULT) { continue; } else { @@ -5468,12 +6966,38 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun tk = _get_token(); } + Node *n = nullptr; + if (tk.type != TK_INT_CONSTANT) { - _set_error("Expected integer constant"); - return ERR_PARSE_ERROR; - } + bool correct_constant_expression = false; + DataType data_type; + + if (tk.type == TK_IDENTIFIER) { + bool is_const; + _find_identifier(p_block, false, p_function_info, tk.text, &data_type, nullptr, &is_const); + if (is_const) { + if (data_type == TYPE_INT) { + correct_constant_expression = true; + } + } + } + if (!correct_constant_expression) { + _set_error("Expected integer constant"); + return ERR_PARSE_ERROR; + } - int constant = (int)tk.constant * sign; + VariableNode *vn = alloc_node<VariableNode>(); + vn->name = tk.text; + n = vn; + } else { + ConstantNode::Value v; + v.sint = (int)tk.constant * sign; + + ConstantNode *cn = alloc_node<ConstantNode>(); + cn->values.push_back(v); + cn->datatype = TYPE_INT; + n = cn; + } tk = _get_token(); @@ -5485,12 +7009,6 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun ControlFlowNode *cf = alloc_node<ControlFlowNode>(); cf->flow_op = FLOW_OP_CASE; - ConstantNode *n = alloc_node<ConstantNode>(); - ConstantNode::Value v; - v.sint = constant; - n->values.push_back(v); - n->datatype = TYPE_INT; - BlockNode *case_block = alloc_node<BlockNode>(); case_block->block_type = BlockNode::BLOCK_TYPE_CASE; case_block->parent_block = p_block; @@ -5670,7 +7188,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun //check return type BlockNode *b = p_block; - if (b && b->parent_function && (b->parent_function->name == "vertex" || b->parent_function->name == "fragment" || b->parent_function->name == "light")) { + if (b && b->parent_function && p_function_info.main_function) { _set_error(vformat("Using 'return' in '%s' processor function results in undefined behavior!", b->parent_function->name)); return ERR_PARSE_ERROR; } @@ -5684,6 +7202,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun return ERR_BUG; } + String return_struct_name = String(b->parent_function->return_struct_name); + String array_size_string; + + if (b->parent_function->return_array_size > 0) { + array_size_string = "[" + itos(b->parent_function->return_array_size) + "]"; + } + ControlFlowNode *flow = alloc_node<ControlFlowNode>(); flow->flow_op = FLOW_OP_RETURN; @@ -5692,18 +7217,19 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (tk.type == TK_SEMICOLON) { //all is good if (b->parent_function->return_type != TYPE_VOID) { - _set_error("Expected return with expression of type '" + get_datatype_name(b->parent_function->return_type) + "'"); + _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string + "'"); return ERR_PARSE_ERROR; } } else { _set_tkpos(pos); //rollback, wants expression + Node *expr = _parse_and_reduce_expression(p_block, p_function_info); if (!expr) { return ERR_PARSE_ERROR; } - if (b->parent_function->return_type != expr->get_datatype()) { - _set_error("Expected return expression of type '" + get_datatype_name(b->parent_function->return_type) + "'"); + if (b->parent_function->return_type != expr->get_datatype() || b->parent_function->return_array_size != expr->get_array_size() || return_struct_name != expr->get_datatype_name()) { + _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string + "'"); return ERR_PARSE_ERROR; } @@ -5747,15 +7273,15 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun pos = _get_tkpos(); tk = _get_token(); if (tk.type != TK_SEMICOLON) { - //all is good _set_error("Expected ';' after discard"); + return ERR_PARSE_ERROR; } p_block->statements.push_back(flow); } else if (tk.type == TK_CF_BREAK) { if (!p_can_break) { - //all is good - _set_error("Breaking is not allowed here"); + _set_error("'break' is not allowed outside of a loop or 'switch' statement"); + return ERR_PARSE_ERROR; } ControlFlowNode *flow = alloc_node<ControlFlowNode>(); @@ -5764,8 +7290,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun pos = _get_tkpos(); tk = _get_token(); if (tk.type != TK_SEMICOLON) { - //all is good _set_error("Expected ';' after break"); + return ERR_PARSE_ERROR; } p_block->statements.push_back(flow); @@ -5780,8 +7306,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun } else if (tk.type == TK_CF_CONTINUE) { if (!p_can_continue) { - //all is good - _set_error("Continuing is not allowed here"); + _set_error("'continue' is not allowed outside of a loop"); + return ERR_PARSE_ERROR; } ControlFlowNode *flow = alloc_node<ControlFlowNode>(); @@ -5792,6 +7318,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (tk.type != TK_SEMICOLON) { //all is good _set_error("Expected ';' after continue"); + return ERR_PARSE_ERROR; } p_block->statements.push_back(flow); @@ -5877,6 +7404,7 @@ Error ShaderLanguage::_validate_datatype(DataType p_type) { Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types) { Token tk = _get_token(); + TkPos prev_pos; if (tk.type != TK_SHADER_TYPE) { _set_error("Expected 'shader_type' at the beginning of shader. Valid types are: " + _get_shader_type_list(p_shader_types)); @@ -5898,20 +7426,24 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct _set_error("Invalid shader type. Valid types are: " + _get_shader_type_list(p_shader_types)); return ERR_PARSE_ERROR; } - + prev_pos = _get_tkpos(); tk = _get_token(); if (tk.type != TK_SEMICOLON) { + _set_tkpos(prev_pos); _set_error("Expected ';' after 'shader_type <type>'."); + return ERR_PARSE_ERROR; } tk = _get_token(); int texture_uniforms = 0; - int uniforms = 0; + int texture_binding = 0; int instance_index = 0; ShaderNode::Uniform::Scope uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL; + stages = &p_functions; + while (tk.type != TK_EOF) { switch (tk.type) { case TK_RENDER_MODE: { @@ -5954,6 +7486,10 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct tk = _get_token(); if (tk.type == TK_IDENTIFIER) { st.name = tk.text; + if (shader->structs.has(st.name)) { + _set_error("Redefinition of '" + String(st.name) + "'"); + return ERR_PARSE_ERROR; + } tk = _get_token(); if (tk.type != TK_CURLY_BRACKET_OPEN) { _set_error("Expected '{'"); @@ -6012,8 +7548,23 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct _set_error("void datatype not allowed here"); return ERR_PARSE_ERROR; } - tk = _get_token(); + + if (tk.type != TK_IDENTIFIER && tk.type != TK_BRACKET_OPEN) { + _set_error("Expected identifier or '['."); + return ERR_PARSE_ERROR; + } + + int array_size = 0; + + if (tk.type == TK_BRACKET_OPEN) { + Error error = _parse_global_array_size(array_size); + if (error != OK) { + return error; + } + tk = _get_token(); + } + if (tk.type != TK_IDENTIFIER) { _set_error("Expected identifier!"); return ERR_PARSE_ERROR; @@ -6024,41 +7575,29 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct member->datatype = type; member->struct_name = struct_name; member->name = tk.text; + member->array_size = array_size; if (member_names.has(member->name)) { _set_error("Redefinition of '" + String(member->name) + "'"); return ERR_PARSE_ERROR; } member_names.insert(member->name); - tk = _get_token(); - if (tk.type == TK_BRACKET_OPEN) { - tk = _get_token(); - if (tk.type == TK_INT_CONSTANT && tk.constant > 0) { - member->array_size = (int)tk.constant; - tk = _get_token(); - if (tk.type == TK_BRACKET_CLOSE) { - tk = _get_token(); - if (tk.type != TK_SEMICOLON) { - _set_error("Expected ';'"); - return ERR_PARSE_ERROR; - } - } else { - _set_error("Expected ']'"); - return ERR_PARSE_ERROR; - } - } else { - _set_error("Expected single integer constant > 0"); - return ERR_PARSE_ERROR; + if (tk.type == TK_BRACKET_OPEN) { + Error error = _parse_global_array_size(member->array_size); + if (error != OK) { + return error; } + tk = _get_token(); } - st_node->members.push_back(member); if (tk.type != TK_SEMICOLON) { - _set_error("Expected ']' or ';'"); + _set_error("Expected ';'"); return ERR_PARSE_ERROR; } + + st_node->members.push_back(member); member_count++; } } @@ -6074,7 +7613,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } shader->structs[st.name] = st; shader->vstructs.push_back(st); // struct's order is important! - +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_STRUCT_FLAG)) { + used_structs.insert(st.name, Usage(tk_line)); + } +#endif // DEBUG_ENABLED } break; case TK_GLOBAL: { tk = _get_token(); @@ -6101,28 +7644,49 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct bool uniform = tk.type == TK_UNIFORM; if (!uniform) { - if (shader_type_identifier == "particles" || shader_type_identifier == "sky") { + if (shader_type_identifier == "particles" || shader_type_identifier == "sky" || shader_type_identifier == "fog") { _set_error(vformat("Varyings cannot be used in '%s' shaders!", shader_type_identifier)); return ERR_PARSE_ERROR; } } + bool precision_defined = false; DataPrecision precision = PRECISION_DEFAULT; DataInterpolation interpolation = INTERPOLATION_SMOOTH; DataType type; StringName name; + int array_size = 0; tk = _get_token(); if (is_token_interpolation(tk.type)) { + if (uniform) { + _set_error("Interpolation qualifiers are not supported for uniforms!"); + return ERR_PARSE_ERROR; + } interpolation = get_token_interpolation(tk.type); tk = _get_token(); } if (is_token_precision(tk.type)) { precision = get_token_precision(tk.type); + precision_defined = true; tk = _get_token(); } + if (shader->structs.has(tk.text)) { + if (uniform) { + if (precision_defined) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } + _set_error("struct datatype is not yet supported for uniforms!"); + return ERR_PARSE_ERROR; + } else { + _set_error("struct datatype not allowed here"); + return ERR_PARSE_ERROR; + } + } + if (!is_token_datatype(tk.type)) { _set_error("Expected datatype. "); return ERR_PARSE_ERROR; @@ -6141,11 +7705,26 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } tk = _get_token(); + + if (tk.type != TK_IDENTIFIER && tk.type != TK_BRACKET_OPEN) { + _set_error("Expected identifier or '['."); + return ERR_PARSE_ERROR; + } + + if (tk.type == TK_BRACKET_OPEN) { + Error error = _parse_global_array_size(array_size); + if (error != OK) { + return error; + } + tk = _get_token(); + } + if (tk.type != TK_IDENTIFIER) { _set_error("Expected identifier!"); return ERR_PARSE_ERROR; } + prev_pos = _get_tkpos(); name = tk.text; if (_find_identifier(nullptr, false, FunctionInfo(), name)) { @@ -6174,12 +7753,32 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } ShaderNode::Uniform uniform2; + uniform2.type = type; + uniform2.scope = uniform_scope; + uniform2.precision = precision; + uniform2.array_size = array_size; + + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + Error error = _parse_global_array_size(uniform2.array_size); + if (error != OK) { + return error; + } + tk = _get_token(); + } + if (is_sampler_type(type)) { if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE) { _set_error("Uniforms with 'instance' qualifiers can't be of sampler type."); return ERR_PARSE_ERROR; } uniform2.texture_order = texture_uniforms++; + uniform2.texture_binding = texture_binding; + if (uniform2.array_size > 0) { + texture_binding += uniform2.array_size; + } else { + ++texture_binding; + } uniform2.order = -1; if (_validate_datatype(type) != OK) { return ERR_PARSE_ERROR; @@ -6189,17 +7788,19 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct _set_error("Uniforms with 'instance' qualifiers can't be of matrix type."); return ERR_PARSE_ERROR; } - uniform2.texture_order = -1; - uniform2.order = uniforms++; } - uniform2.type = type; - uniform2.scope = uniform_scope; - uniform2.precision = precision; - - //todo parse default value - tk = _get_token(); + if (uniform2.array_size > 0) { + if (uniform_scope == ShaderNode::Uniform::SCOPE_GLOBAL) { + _set_error("'SCOPE_GLOBAL' qualifier is not yet supported for uniform array!"); + return ERR_PARSE_ERROR; + } + if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE) { + _set_error("'SCOPE_INSTANCE' qualifier is not yet supported for uniform array!"); + return ERR_PARSE_ERROR; + } + } int custom_instance_index = -1; @@ -6207,6 +7808,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct //hint do { tk = _get_token(); + + if (uniform2.array_size > 0) { + if (tk.type != TK_HINT_COLOR) { + _set_error("This hint is not yet supported for uniform arrays!"); + return ERR_PARSE_ERROR; + } + } + if (tk.type == TK_HINT_WHITE_TEXTURE) { uniform2.hint = ShaderNode::Uniform::HINT_WHITE; } else if (tk.type == TK_HINT_BLACK_TEXTURE) { @@ -6259,7 +7868,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct tk = _get_token(); } - if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { + if (tk.type != TK_FLOAT_CONSTANT && tk.type != TK_INT_CONSTANT) { _set_error("Expected integer constant"); return ERR_PARSE_ERROR; } @@ -6283,7 +7892,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct tk = _get_token(); } - if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { + if (tk.type != TK_FLOAT_CONSTANT && tk.type != TK_INT_CONSTANT) { _set_error("Expected integer constant after ','"); return ERR_PARSE_ERROR; } @@ -6296,7 +7905,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct if (tk.type == TK_COMMA) { tk = _get_token(); - if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { + if (tk.type != TK_FLOAT_CONSTANT && tk.type != TK_INT_CONSTANT) { _set_error("Expected integer constant after ','"); return ERR_PARSE_ERROR; } @@ -6397,6 +8006,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct //reset scope for next uniform if (tk.type == TK_OP_ASSIGN) { + if (uniform2.array_size > 0) { + _set_error("Setting default value to a uniform array is not yet supported!"); + return ERR_PARSE_ERROR; + } + Node *expr = _parse_and_reduce_expression(nullptr, FunctionInfo()); if (!expr) { return ERR_PARSE_ERROR; @@ -6418,6 +8032,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } shader->uniforms[name] = uniform2; +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_UNIFORM_FLAG)) { + used_uniforms.insert(name, Usage(tk_line)); + } +#endif // DEBUG_ENABLED + //reset scope for next uniform uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL; @@ -6425,19 +8045,29 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct _set_error("Expected ';'"); return ERR_PARSE_ERROR; } - } else { + } else { // varying ShaderNode::Varying varying; varying.type = type; varying.precision = precision; varying.interpolation = interpolation; + varying.tkpos = prev_pos; + varying.array_size = array_size; tk = _get_token(); if (tk.type != TK_SEMICOLON && tk.type != TK_BRACKET_OPEN) { - _set_error("Expected ';' or '['"); + if (array_size == 0) { + _set_error("Expected ';' or '['"); + } else { + _set_error("Expected ';'"); + } return ERR_PARSE_ERROR; } if (tk.type == TK_BRACKET_OPEN) { + if (array_size > 0) { + _set_error("Array size is already defined!"); + return ERR_PARSE_ERROR; + } tk = _get_token(); if (tk.type == TK_INT_CONSTANT && tk.constant > 0) { varying.array_size = (int)tk.constant; @@ -6454,12 +8084,17 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct return ERR_PARSE_ERROR; } } else { - _set_error("Expected single integer constant > 0"); + _set_error("Expected integer constant > 0"); return ERR_PARSE_ERROR; } } shader->varyings[name] = varying; +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG)) { + used_varyings.insert(name, Usage(tk_line)); + } +#endif // DEBUG_ENABLED } } break; @@ -6472,6 +8107,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct DataPrecision precision = PRECISION_DEFAULT; DataType type; StringName name; + int return_array_size = 0; if (tk.type == TK_CONST) { is_constant = true; @@ -6507,12 +8143,35 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } else { type = get_token_datatype(tk.type); } - TkPos prev_pos = _get_tkpos(); + prev_pos = _get_tkpos(); tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { - _set_error("Cannot use arrays as return types"); - return ERR_PARSE_ERROR; + bool error = false; + tk = _get_token(); + + if (tk.type == TK_INT_CONSTANT) { + return_array_size = (int)tk.constant; + if (return_array_size > 0) { + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + } else { + error = true; + } + } else { + error = true; + } + if (error) { + _set_error("Expected integer constant > 0"); + return ERR_PARSE_ERROR; + } + + prev_pos = _get_tkpos(); } + _set_tkpos(prev_pos); _get_completable_identifier(nullptr, COMPLETION_MAIN_FUNCTION, name); @@ -6627,15 +8286,15 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct tk = _get_token(); if (tk.type == TK_BRACKET_OPEN) { - TkPos pos2 = _get_tkpos(); + prev_pos = _get_tkpos(); tk = _get_token(); if (tk.type == TK_BRACKET_CLOSE) { array_size2 = constant.array_size; tk = _get_token(); } else { - _set_tkpos(pos2); + _set_tkpos(prev_pos); - Node *n = _parse_and_reduce_expression(NULL, FunctionInfo()); + Node *n = _parse_and_reduce_expression(nullptr, FunctionInfo()); if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { _set_error("Expected single integer constant > 0"); return ERR_PARSE_ERROR; @@ -6716,7 +8375,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization while (true) { - Node *n = _parse_and_reduce_expression(NULL, FunctionInfo()); + Node *n = _parse_and_reduce_expression(nullptr, FunctionInfo()); if (!n) { return ERR_PARSE_ERROR; } @@ -6726,8 +8385,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct return ERR_PARSE_ERROR; } - if (constant.type != n->get_datatype() || n->get_datatype_name() != struct_name) { - _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (is_struct ? String(struct_name) : get_datatype_name(constant.type)) + "'"); + if (!_compare_datatypes(constant.type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) { return ERR_PARSE_ERROR; } @@ -6742,10 +8400,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct decl.initializer.push_back(n); break; } else { - if (curly) + if (curly) { _set_error("Expected '}' or ','"); - else + } else { _set_error("Expected ')' or ','"); + } return ERR_PARSE_ERROR; } } @@ -6771,18 +8430,23 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct constant.initializer = static_cast<ConstantNode *>(expr); } else { //variable created with assignment! must parse an expression - Node *expr = _parse_and_reduce_expression(NULL, FunctionInfo()); - if (!expr) + Node *expr = _parse_and_reduce_expression(nullptr, FunctionInfo()); + if (!expr) { return ERR_PARSE_ERROR; + } if (expr->type == Node::TYPE_OPERATOR && ((OperatorNode *)expr)->op == OP_CALL) { - _set_error("Expected constant expression after '='"); - return ERR_PARSE_ERROR; + OperatorNode *op = ((OperatorNode *)expr); + for (int i = 1; i < op->arguments.size(); i++) { + if (!_check_node_constness(op->arguments[i])) { + _set_error("Expected constant expression for argument '" + itos(i - 1) + "' of function call after '='"); + return ERR_PARSE_ERROR; + } + } } constant.initializer = static_cast<ConstantNode *>(expr); - if (type != expr->get_datatype() || expr->get_datatype_name() != struct_name) { - _set_error("Invalid assignment of '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' to '" + (is_struct ? String(struct_name) : get_datatype_name(type)) + "'"); + if (!_compare_datatypes(type, struct_name, 0, expr->get_datatype(), expr->get_datatype_name(), 0)) { return ERR_PARSE_ERROR; } } @@ -6799,6 +8463,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct shader->constants[name] = constant; shader->vconstants.push_back(constant); +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_CONSTANT_FLAG)) { + used_constants.insert(name, Usage(tk_line)); + } +#endif // DEBUG_ENABLED if (tk.type == TK_COMMA) { tk = _get_token(); @@ -6837,8 +8506,8 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } if (p_functions.has("global")) { // Adds global variables: 'TIME' - for (Map<StringName, BuiltInInfo>::Element *E = p_functions["global"].built_ins.front(); E; E = E->next()) { - builtins.built_ins.insert(E->key(), E->value()); + for (const KeyValue<StringName, BuiltInInfo> &E : p_functions["global"].built_ins) { + builtins.built_ins.insert(E.key, E.value); } } @@ -6857,9 +8526,16 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct func_node->return_type = type; func_node->return_struct_name = struct_name; func_node->return_precision = precision; + func_node->return_array_size = return_array_size; if (p_functions.has(name)) { func_node->can_discard = p_functions[name].can_discard; + } else { +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG)) { + used_functions.insert(name, Usage(tk_line)); + } +#endif // DEBUG_ENABLED } func_node->body = alloc_node<BlockNode>(); @@ -6904,6 +8580,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct StringName param_struct_name; DataPrecision pprecision = PRECISION_DEFAULT; bool use_precision = false; + int array_size = 0; if (is_token_precision(tk.type)) { pprecision = get_token_precision(tk.type); @@ -6950,8 +8627,29 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct tk = _get_token(); if (tk.type == TK_BRACKET_OPEN) { - _set_error("Arrays as parameters are not implemented yet"); - return ERR_PARSE_ERROR; + bool error = false; + tk = _get_token(); + + if (tk.type == TK_INT_CONSTANT) { + array_size = (int)tk.constant; + + if (array_size > 0) { + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + } else { + error = true; + } + } else { + error = true; + } + if (error) { + _set_error("Expected integer constant > 0"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); } if (tk.type != TK_IDENTIFIER) { _set_error("Expected identifier for argument name"); @@ -6985,14 +8683,41 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct arg.tex_argument_repeat = REPEAT_DEFAULT; arg.is_const = is_const; - func_node->arguments.push_back(arg); - tk = _get_token(); if (tk.type == TK_BRACKET_OPEN) { - _set_error("Arrays as parameters are not implemented yet"); - return ERR_PARSE_ERROR; + if (array_size > 0) { + _set_error("Array size is already defined!"); + return ERR_PARSE_ERROR; + } + bool error = false; + tk = _get_token(); + + if (tk.type == TK_INT_CONSTANT) { + array_size = (int)tk.constant; + + if (array_size > 0) { + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + } else { + error = true; + } + } else { + error = true; + } + + if (error) { + _set_error("Expected integer constant > 0"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); } + arg.array_size = array_size; + func_node->arguments.push_back(arg); + if (tk.type == TK_COMMA) { tk = _get_token(); //do none and go on @@ -7042,30 +8767,38 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct tk = _get_token(); } - return OK; -} + int uniforms = 0; -bool ShaderLanguage::has_builtin(const Map<StringName, ShaderLanguage::FunctionInfo> &p_functions, const StringName &p_name) { - if (p_functions.has("vertex")) { - if (p_functions["vertex"].built_ins.has(p_name)) { - return true; + // Need to push arrays to first place in a uniform buffer in order to correct work. + for (Map<StringName, ShaderNode::Uniform>::Element *E = shader->uniforms.front(); E; E = E->next()) { + if (E->get().texture_order == -1 && E->get().scope != ShaderNode::Uniform::SCOPE_INSTANCE && E->get().array_size > 0) { + E->get().order = uniforms++; } } - if (p_functions.has("fragment")) { - if (p_functions["fragment"].built_ins.has(p_name)) { - return true; + for (Map<StringName, ShaderNode::Uniform>::Element *E = shader->uniforms.front(); E; E = E->next()) { + if (E->get().texture_order == -1 && E->get().scope != ShaderNode::Uniform::SCOPE_INSTANCE && E->get().array_size == 0) { + E->get().order = uniforms++; } } - if (p_functions.has("light")) { - if (p_functions["light"].built_ins.has(p_name)) { - return true; - } + + int error_line; + String error_message; + if (!_check_varying_usages(&error_line, &error_message)) { + _set_tkpos({ 0, error_line }); + _set_error(error_message); + return ERR_PARSE_ERROR; } - if (p_functions.has("compute")) { - if (p_functions["compute"].built_ins.has(p_name)) { + + return OK; +} + +bool ShaderLanguage::has_builtin(const Map<StringName, ShaderLanguage::FunctionInfo> &p_functions, const StringName &p_name) { + for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : p_functions) { + if (E.value.built_ins.has(p_name)) { return true; } } + return false; } @@ -7199,27 +8932,71 @@ String ShaderLanguage::get_shader_type(const String &p_code) { return String(); } -Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) { +#ifdef DEBUG_ENABLED +void ShaderLanguage::_check_warning_accums() { + for (const KeyValue<ShaderWarning::Code, Map<StringName, Map<StringName, Usage>> *> &E : warnings_check_map2) { + for (Map<StringName, Map<StringName, Usage>>::Element *T = (*E.value).front(); T; T = T->next()) { + for (const KeyValue<StringName, Usage> &U : T->get()) { + if (!U.value.used) { + _add_warning(E.key, U.value.decl_line, U.key); + } + } + } + } + for (const KeyValue<ShaderWarning::Code, Map<StringName, Usage> *> &E : warnings_check_map) { + for (const Map<StringName, Usage>::Element *U = (*E.value).front(); U; U = U->next()) { + if (!U->get().used) { + _add_warning(E.key, U->get().decl_line, U->key()); + } + } + } +} +List<ShaderWarning>::Element *ShaderLanguage::get_warnings_ptr() { + return warnings.front(); +} +void ShaderLanguage::enable_warning_checking(bool p_enabled) { + check_warnings = p_enabled; +} +bool ShaderLanguage::is_warning_checking_enabled() const { + return check_warnings; +} +void ShaderLanguage::set_warning_flags(uint32_t p_flags) { + warning_flags = p_flags; +} +uint32_t ShaderLanguage::get_warning_flags() const { + return warning_flags; +} +#endif // DEBUG_ENABLED + +Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) { clear(); code = p_code; global_var_get_type_func = p_global_variable_type_func; + varying_function_names = p_varying_function_names; nodes = nullptr; shader = alloc_node<ShaderNode>(); Error err = _parse_shader(p_functions, p_render_modes, p_shader_types); +#ifdef DEBUG_ENABLED + if (check_warnings) { + _check_warning_accums(); + } +#endif // DEBUG_ENABLED + if (err != OK) { return err; } return OK; } -Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint) { +Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint) { clear(); code = p_code; + varying_function_names = p_varying_function_names; nodes = nullptr; global_var_get_type_func = p_global_variable_type_func; @@ -7252,8 +9029,8 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct return OK; } break; case COMPLETION_MAIN_FUNCTION: { - for (const Map<StringName, FunctionInfo>::Element *E = p_functions.front(); E; E = E->next()) { - ScriptCodeCompletionOption option(E->key(), ScriptCodeCompletionOption::KIND_FUNCTION); + for (const KeyValue<StringName, FunctionInfo> &E : p_functions) { + ScriptCodeCompletionOption option(E.key, ScriptCodeCompletionOption::KIND_FUNCTION); r_options->push_back(option); } @@ -7269,9 +9046,9 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct if (completion_class == TAG_GLOBAL) { while (block) { if (comp_ident) { - for (const Map<StringName, BlockNode::Variable>::Element *E = block->variables.front(); E; E = E->next()) { - if (E->get().line < completion_line) { - matches.insert(E->key(), ScriptCodeCompletionOption::KIND_VARIABLE); + for (const KeyValue<StringName, BlockNode::Variable> &E : block->variables) { + if (E.value.line < completion_line) { + matches.insert(E.key, ScriptCodeCompletionOption::KIND_VARIABLE); } } } @@ -7289,30 +9066,30 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct if (comp_ident) { if (p_functions.has("global")) { - for (Map<StringName, BuiltInInfo>::Element *E = p_functions["global"].built_ins.front(); E; E = E->next()) { + for (const KeyValue<StringName, BuiltInInfo> &E : p_functions["global"].built_ins) { ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_MEMBER; - if (E->get().constant) { + if (E.value.constant) { kind = ScriptCodeCompletionOption::KIND_CONSTANT; } - matches.insert(E->key(), kind); + matches.insert(E.key, kind); } } if (skip_function != StringName() && p_functions.has(skip_function)) { - for (Map<StringName, BuiltInInfo>::Element *E = p_functions[skip_function].built_ins.front(); E; E = E->next()) { + for (const KeyValue<StringName, BuiltInInfo> &E : p_functions[skip_function].built_ins) { ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_MEMBER; - if (E->get().constant) { + if (E.value.constant) { kind = ScriptCodeCompletionOption::KIND_CONSTANT; } - matches.insert(E->key(), kind); + matches.insert(E.key, kind); } } - for (const Map<StringName, ShaderNode::Varying>::Element *E = shader->varyings.front(); E; E = E->next()) { - matches.insert(E->key(), ScriptCodeCompletionOption::KIND_VARIABLE); + for (const KeyValue<StringName, ShaderNode::Varying> &E : shader->varyings) { + matches.insert(E.key, ScriptCodeCompletionOption::KIND_VARIABLE); } - for (const Map<StringName, ShaderNode::Uniform>::Element *E = shader->uniforms.front(); E; E = E->next()) { - matches.insert(E->key(), ScriptCodeCompletionOption::KIND_MEMBER); + for (const KeyValue<StringName, ShaderNode::Uniform> &E : shader->uniforms) { + matches.insert(E.key, ScriptCodeCompletionOption::KIND_MEMBER); } } @@ -7326,6 +9103,12 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct int idx = 0; bool low_end = RenderingServer::get_singleton()->is_low_end(); + if (stages && stages->has(skip_function)) { + for (const KeyValue<StringName, StageFunctionInfo> &E : (*stages)[skip_function].stage_functions) { + matches.insert(String(E.key), ScriptCodeCompletionOption::KIND_FUNCTION); + } + } + while (builtin_func_defs[idx].name) { if (low_end && builtin_func_defs[idx].high_end) { idx++; @@ -7351,9 +9134,9 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct } } - for (Map<String, ScriptCodeCompletionOption::Kind>::Element *E = matches.front(); E; E = E->next()) { - ScriptCodeCompletionOption option(E->key(), E->value()); - if (E->value() == ScriptCodeCompletionOption::KIND_FUNCTION) { + for (const KeyValue<String, ScriptCodeCompletionOption::Kind> &E : matches) { + ScriptCodeCompletionOption option(E.key, E.value); + if (E.value == ScriptCodeCompletionOption::KIND_FUNCTION) { option.insert_text += "("; } r_options->push_back(option); @@ -7362,6 +9145,16 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct return OK; } break; case COMPLETION_CALL_ARGUMENTS: { + StringName block_function; + BlockNode *block = completion_block; + + while (block) { + if (block->parent_function) { + block_function = block->parent_function->name; + } + block = block->parent_block; + } + for (int i = 0; i < shader->functions.size(); i++) { if (!shader->functions[i].callable) { continue; @@ -7370,6 +9163,13 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct String calltip; calltip += get_datatype_name(shader->functions[i].function->return_type); + + if (shader->functions[i].function->return_array_size > 0) { + calltip += "["; + calltip += itos(shader->functions[i].function->return_array_size); + calltip += "]"; + } + calltip += " "; calltip += shader->functions[i].name; calltip += "("; @@ -7401,6 +9201,12 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct calltip += " "; calltip += shader->functions[i].function->arguments[j].name; + if (shader->functions[i].function->arguments[j].array_size > 0) { + calltip += "["; + calltip += itos(shader->functions[i].function->arguments[j].array_size); + calltip += "]"; + } + if (j == completion_argument) { calltip += char32_t(0xFFFF); } @@ -7421,6 +9227,45 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct String calltip; bool low_end = RenderingServer::get_singleton()->is_low_end(); + if (stages && stages->has(block_function)) { + for (const KeyValue<StringName, StageFunctionInfo> &E : (*stages)[block_function].stage_functions) { + if (completion_function == E.key) { + calltip += get_datatype_name(E.value.return_type); + calltip += " "; + calltip += E.key; + calltip += "("; + + for (int i = 0; i < E.value.arguments.size(); i++) { + if (i > 0) { + calltip += ", "; + } else { + calltip += " "; + } + + if (i == completion_argument) { + calltip += char32_t(0xFFFF); + } + + calltip += get_datatype_name(E.value.arguments[i].type); + calltip += " "; + calltip += E.value.arguments[i].name; + + if (i == completion_argument) { + calltip += char32_t(0xFFFF); + } + } + + if (E.value.arguments.size()) { + calltip += " "; + } + calltip += ")"; + + r_call_hint = calltip; + return OK; + } + } + } + while (builtin_func_defs[idx].name) { if (low_end && builtin_func_defs[idx].high_end) { idx++; @@ -7428,10 +9273,16 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct } int idx2 = 0; - int out_arg = -1; + Set<int> out_args; while (builtin_func_out_args[idx2].name != nullptr) { if (builtin_func_out_args[idx2].name == builtin_func_defs[idx].name) { - out_arg = builtin_func_out_args[idx2].argument; + for (int i = 0; i < BuiltinFuncOutArgs::MAX_ARGS; i++) { + int arg = builtin_func_out_args[idx2].arguments[i]; + if (arg == -1) { + break; + } + out_args.insert(arg); + } break; } idx2++; @@ -7453,7 +9304,7 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct calltip += "("; bool found_arg = false; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < BuiltinFuncDef::MAX_ARGS - 1; i++) { if (builtin_func_defs[idx].args[i] == TYPE_VOID) { break; } @@ -7468,12 +9319,18 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct calltip += char32_t(0xFFFF); } - if (out_arg >= 0 && i == out_arg) { + if (out_args.has(i)) { calltip += "out "; } calltip += get_datatype_name(builtin_func_defs[idx].args[i]); + String arg_name = (String)builtin_func_defs[idx].args_names[i]; + if (!arg_name.is_empty()) { + calltip += " "; + calltip += arg_name; + } + if (i == completion_argument) { calltip += char32_t(0xFFFF); } @@ -7523,15 +9380,6 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct limit = 4; } break; - case TYPE_MAT2: - limit = 2; - break; - case TYPE_MAT3: - limit = 3; - break; - case TYPE_MAT4: - limit = 4; - break; default: { } } @@ -7563,6 +9411,16 @@ ShaderLanguage::ShaderNode *ShaderLanguage::get_shader() { ShaderLanguage::ShaderLanguage() { nodes = nullptr; completion_class = TAG_GLOBAL; + +#if DEBUG_ENABLED + warnings_check_map.insert(ShaderWarning::UNUSED_CONSTANT, &used_constants); + warnings_check_map.insert(ShaderWarning::UNUSED_FUNCTION, &used_functions); + warnings_check_map.insert(ShaderWarning::UNUSED_STRUCT, &used_structs); + warnings_check_map.insert(ShaderWarning::UNUSED_UNIFORM, &used_uniforms); + warnings_check_map.insert(ShaderWarning::UNUSED_VARYING, &used_varyings); + + warnings_check_map2.insert(ShaderWarning::UNUSED_LOCAL_VARIABLE, &used_local_vars); +#endif // DEBUG_ENABLED } ShaderLanguage::~ShaderLanguage() { |