summaryrefslogtreecommitdiffstats
path: root/servers/rendering/shader_language.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'servers/rendering/shader_language.cpp')
-rw-r--r--servers/rendering/shader_language.cpp399
1 files changed, 351 insertions, 48 deletions
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 9aa54d0bb7..4eaf7fcb55 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -39,6 +39,8 @@
#define HAS_WARNING(flag) (warning_flags & flag)
+int ShaderLanguage::instance_counter = 0;
+
String ShaderLanguage::get_operator_text(Operator p_op) {
static const char *op_names[OP_MAX] = { "==",
"!=",
@@ -94,6 +96,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = {
"FLOAT_CONSTANT",
"INT_CONSTANT",
"UINT_CONSTANT",
+ "STRING_CONSTANT",
"TYPE_VOID",
"TYPE_BOOL",
"TYPE_BVEC2",
@@ -210,6 +213,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = {
"HINT_ANISOTROPY_TEXTURE",
"HINT_SOURCE_COLOR",
"HINT_RANGE",
+ "HINT_ENUM",
"HINT_INSTANCE_INDEX",
"HINT_SCREEN_TEXTURE",
"HINT_NORMAL_ROUGHNESS_TEXTURE",
@@ -363,6 +367,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
{ TK_HINT_SOURCE_COLOR, "source_color", CF_UNSPECIFIED, {}, {} },
{ TK_HINT_RANGE, "hint_range", CF_UNSPECIFIED, {}, {} },
+ { TK_HINT_ENUM, "hint_enum", CF_UNSPECIFIED, {}, {} },
{ TK_HINT_INSTANCE_INDEX, "instance_index", CF_UNSPECIFIED, {}, {} },
// sampler hints
@@ -510,7 +515,54 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
return _make_token(TK_OP_NOT);
} break;
- //case '"' //string - no strings in shader
+ case '"': {
+ String _content = "";
+ bool _previous_backslash = false;
+
+ while (true) {
+ bool _ended = false;
+ char32_t c = GETCHAR(0);
+ if (c == 0) {
+ return _make_token(TK_ERROR, "EOF reached before string termination.");
+ }
+ switch (c) {
+ case '"': {
+ if (_previous_backslash) {
+ _content += '"';
+ _previous_backslash = false;
+ } else {
+ _ended = true;
+ }
+ break;
+ }
+ case '\\': {
+ if (_previous_backslash) {
+ _content += '\\';
+ }
+ _previous_backslash = !_previous_backslash;
+ break;
+ }
+ case '\n': {
+ return _make_token(TK_ERROR, "Unexpected end of string.");
+ }
+ default: {
+ if (!_previous_backslash) {
+ _content += c;
+ } else {
+ return _make_token(TK_ERROR, "Only \\\" and \\\\ escape characters supported.");
+ }
+ break;
+ }
+ }
+
+ char_idx++;
+ if (_ended) {
+ break;
+ }
+ }
+
+ return _make_token(TK_STRING_CONSTANT, _content);
+ } break;
//case '\'' //string - no strings in shader
case '{':
return _make_token(TK_CURLY_BRACKET_OPEN);
@@ -896,6 +948,13 @@ bool ShaderLanguage::_lookup_next(Token &r_tk) {
return false;
}
+ShaderLanguage::Token ShaderLanguage::_peek() {
+ TkPos pre_pos = _get_tkpos();
+ Token tk = _get_token();
+ _set_tkpos(pre_pos);
+ return tk;
+}
+
String ShaderLanguage::token_debug(const String &p_code) {
clear();
@@ -1118,6 +1177,9 @@ String ShaderLanguage::get_uniform_hint_name(ShaderNode::Uniform::Hint p_hint) {
case ShaderNode::Uniform::HINT_RANGE: {
result = "hint_range";
} break;
+ case ShaderNode::Uniform::HINT_ENUM: {
+ result = "hint_enum";
+ } break;
case ShaderNode::Uniform::HINT_SOURCE_COLOR: {
result = "source_color";
} break;
@@ -1238,6 +1300,7 @@ void ShaderLanguage::clear() {
include_positions.push_back(FilePosition());
include_markers_handled.clear();
+ calls_info.clear();
#ifdef DEBUG_ENABLED
keyword_completion_context = CF_UNSPECIFIED;
@@ -1445,8 +1508,12 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
*r_struct_name = shader->constants[p_identifier].struct_name;
}
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 (shader->constants[p_identifier].initializer && shader->constants[p_identifier].initializer->type == Node::NODE_TYPE_CONSTANT) {
+ ConstantNode *cnode = static_cast<ConstantNode *>(shader->constants[p_identifier].initializer);
+
+ if (cnode->values.size() == 1) {
+ *r_constant_value = cnode->values[0];
+ }
}
}
if (r_type) {
@@ -1542,7 +1609,7 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
}
DataType na = p_op->arguments[0]->get_datatype();
- valid = na > TYPE_BOOL && na < TYPE_MAT2;
+ valid = na > TYPE_BVEC4 && na < TYPE_MAT2;
ret_type = na;
} break;
case OP_ADD:
@@ -1562,7 +1629,7 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
}
if (na == nb) {
- valid = (na > TYPE_BOOL && na <= TYPE_MAT4);
+ valid = (na > TYPE_BVEC4 && na <= TYPE_MAT4);
ret_type = na;
} else if (na == TYPE_INT && nb == TYPE_IVEC2) {
valid = true;
@@ -1771,7 +1838,7 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
DataType nb = p_op->arguments[1]->get_datatype();
if (na == nb) {
- valid = (na > TYPE_BOOL && na <= TYPE_MAT4);
+ valid = (na > TYPE_BVEC4 && na <= TYPE_MAT4);
ret_type = na;
} else if (na == TYPE_IVEC2 && nb == TYPE_INT) {
valid = true;
@@ -3085,6 +3152,19 @@ const ShaderLanguage::BuiltinFuncConstArgs ShaderLanguage::builtin_func_const_ar
{ nullptr, 0, 0, 0 }
};
+const ShaderLanguage::BuiltinEntry ShaderLanguage::frag_only_func_defs[] = {
+ { "dFdx" },
+ { "dFdxCoarse" },
+ { "dFdxFine" },
+ { "dFdy" },
+ { "dFdyCoarse" },
+ { "dFdyFine" },
+ { "fwidth" },
+ { "fwidthCoarse" },
+ { "fwidthFine" },
+ { nullptr }
+};
+
bool ShaderLanguage::is_const_suffix_lut_initialized = false;
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, bool *r_is_custom_function) {
@@ -3964,12 +4044,9 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::C
}
value = Variant(array);
} else {
- PackedFloat32Array array;
+ PackedVector4Array array;
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);
+ array.push_back(Vector4(p_value[i].real, p_value[i + 1].real, p_value[i + 2].real, p_value[i + 3].real));
}
value = Variant(array);
}
@@ -4122,6 +4199,11 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform
if (p_uniform.array_size > 0) {
pi.type = Variant::PACKED_INT32_ARRAY;
// TODO: Handle range and encoding for for unsigned values.
+ } else if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_ENUM) {
+ pi.type = Variant::INT;
+ pi.hint = PROPERTY_HINT_ENUM;
+ String hint_string;
+ pi.hint_string = String(",").join(p_uniform.hint_enum_names);
} else {
pi.type = Variant::INT;
pi.hint = PROPERTY_HINT_RANGE;
@@ -4201,7 +4283,7 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform
if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SOURCE_COLOR) {
pi.type = Variant::PACKED_COLOR_ARRAY;
} else {
- pi.type = Variant::PACKED_FLOAT32_ARRAY;
+ pi.type = Variant::PACKED_VECTOR4_ARRAY;
}
} else {
if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SOURCE_COLOR) {
@@ -4610,6 +4692,67 @@ bool ShaderLanguage::_check_node_constness(const Node *p_node) const {
return true;
}
+bool ShaderLanguage::_check_restricted_func(const StringName &p_name, const StringName &p_current_function) const {
+ int idx = 0;
+
+ while (frag_only_func_defs[idx].name) {
+ if (StringName(frag_only_func_defs[idx].name) == p_name) {
+ if (is_supported_frag_only_funcs) {
+ if (p_current_function == "vertex" && stages->has(p_current_function)) {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ break;
+ }
+ idx++;
+ }
+
+ return false;
+}
+
+bool ShaderLanguage::_validate_restricted_func(const StringName &p_name, const CallInfo *p_func_info, bool p_is_builtin_hint) {
+ const bool is_in_restricted_function = p_func_info->name == "vertex";
+
+ // No need to check up the hierarchy if it's a built-in.
+ if (!p_is_builtin_hint) {
+ for (const CallInfo *func_info : p_func_info->calls) {
+ if (is_in_restricted_function && func_info->name != p_name) {
+ // Skips check for non-called method.
+ continue;
+ }
+
+ if (!_validate_restricted_func(p_name, func_info)) {
+ return false;
+ }
+ }
+ }
+
+ if (!p_func_info->uses_restricted_items.is_empty()) {
+ const Pair<StringName, CallInfo::Item> &first_element = p_func_info->uses_restricted_items.get(0);
+
+ if (first_element.second.type == CallInfo::Item::ITEM_TYPE_VARYING) {
+ const ShaderNode::Varying &varying = shader->varyings[first_element.first];
+
+ if (varying.stage == ShaderNode::Varying::STAGE_VERTEX) {
+ return true;
+ }
+ }
+
+ _set_tkpos(first_element.second.pos);
+
+ if (is_in_restricted_function) {
+ _set_error(vformat(RTR("'%s' cannot be used within the '%s' processor function."), first_element.first, "vertex"));
+ } else {
+ _set_error(vformat(RTR("'%s' cannot be used here, because '%s' is called by the '%s' processor function (which is not allowed)."), first_element.first, p_func_info->name, "vertex"));
+ }
+ return false;
+ }
+
+ return true;
+}
+
bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message) {
if (p_node->type == Node::NODE_TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(p_node);
@@ -4683,7 +4826,16 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi
return false;
}
-bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(const StringName &p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat) {
+ShaderLanguage::ShaderNode::Uniform::Hint ShaderLanguage::_sanitize_hint(ShaderNode::Uniform::Hint p_hint) {
+ if (p_hint == ShaderNode::Uniform::HINT_SCREEN_TEXTURE ||
+ p_hint == ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE ||
+ p_hint == ShaderNode::Uniform::HINT_DEPTH_TEXTURE) {
+ return p_hint;
+ }
+ return ShaderNode::Uniform::HINT_NONE;
+}
+
+bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(const StringName &p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat, ShaderNode::Uniform::Hint p_hint) {
for (int i = 0; i < shader->vfunctions.size(); i++) {
if (shader->vfunctions[i].name == p_name) {
ERR_FAIL_INDEX_V(p_argument, shader->vfunctions[i].function->arguments.size(), false);
@@ -4692,20 +4844,21 @@ bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(const Str
_set_error(vformat(RTR("Sampler argument %d of function '%s' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other)."), p_argument, String(p_name)));
return false;
} else if (arg->tex_argument_check) {
- //was checked, verify that filter and repeat are the same
- if (arg->tex_argument_filter == p_filter && arg->tex_argument_repeat == p_repeat) {
+ // Was checked, verify that filter, repeat, and hint are the same.
+ if (arg->tex_argument_filter == p_filter && arg->tex_argument_repeat == p_repeat && arg->tex_hint == _sanitize_hint(p_hint)) {
return true;
} else {
- _set_error(vformat(RTR("Sampler argument %d of function '%s' called more than once using textures that differ in either filter or repeat setting."), p_argument, String(p_name)));
+ _set_error(vformat(RTR("Sampler argument %d of function '%s' called more than once using textures that differ in either filter, repeat, or texture hint setting."), p_argument, String(p_name)));
return false;
}
} else {
arg->tex_argument_check = true;
arg->tex_argument_filter = p_filter;
arg->tex_argument_repeat = p_repeat;
+ arg->tex_hint = _sanitize_hint(p_hint);
for (KeyValue<StringName, HashSet<int>> &E : arg->tex_argument_connect) {
for (const int &F : E.value) {
- if (!_propagate_function_call_sampler_uniform_settings(E.key, F, p_filter, p_repeat)) {
+ if (!_propagate_function_call_sampler_uniform_settings(E.key, F, p_filter, p_repeat, p_hint)) {
return false;
}
}
@@ -5043,7 +5196,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
return an;
}
-ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info) {
+ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info, const ExpressionInfo *p_previous_expression_info) {
Vector<Expression> expression;
//Vector<TokenType> operators;
@@ -5266,6 +5419,36 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
const StringName &name = identifier;
+ if (name != current_function) { // Recursion is not allowed.
+ // Register call.
+ if (calls_info.has(name)) {
+ calls_info[current_function].calls.push_back(&calls_info[name]);
+ }
+
+ int idx = 0;
+ bool is_builtin = false;
+
+ while (frag_only_func_defs[idx].name) {
+ if (frag_only_func_defs[idx].name == name) {
+ // If a built-in function not found for the current shader type, then it shouldn't be parsed further.
+ if (!is_supported_frag_only_funcs) {
+ _set_error(vformat(RTR("Built-in function '%s' is not supported for the '%s' shader type."), name, shader_type_identifier));
+ return nullptr;
+ }
+ // Register usage of the restricted function.
+ calls_info[current_function].uses_restricted_items.push_back(Pair<StringName, CallInfo::Item>(name, CallInfo::Item(CallInfo::Item::ITEM_TYPE_BUILTIN, _get_tkpos())));
+ is_builtin = true;
+ break;
+ }
+ idx++;
+ }
+
+ // Recursively checks for the restricted function call.
+ if (is_supported_frag_only_funcs && current_function == "vertex" && stages->has(current_function) && !_validate_restricted_func(name, &calls_info[current_function], is_builtin)) {
+ return nullptr;
+ }
+ }
+
OperatorNode *func = alloc_node<OperatorNode>();
func->op = OP_CALL;
VariableNode *funcname = alloc_node<VariableNode>();
@@ -5370,10 +5553,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (shader->varyings.has(varname)) {
switch (shader->varyings[varname].stage) {
- case ShaderNode::Varying::STAGE_UNKNOWN: {
- _set_error(vformat(RTR("Varying '%s' must be assigned in the 'vertex' or 'fragment' function first."), varname));
- return nullptr;
- }
+ case ShaderNode::Varying::STAGE_UNKNOWN:
+ if (is_out_arg) {
+ error = true;
+ }
+ break;
case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT:
[[fallthrough]];
case ShaderNode::Varying::STAGE_VERTEX:
@@ -5449,10 +5633,16 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
}
if (is_sampler_type(call_function->arguments[i].type)) {
- //let's see where our argument comes from
- ERR_CONTINUE(n->type != Node::NODE_TYPE_VARIABLE); //bug? this should always be a variable
- VariableNode *vn = static_cast<VariableNode *>(n);
- StringName varname = vn->name;
+ // Let's see where our argument comes from.
+ StringName varname;
+ if (n->type == Node::NODE_TYPE_VARIABLE) {
+ VariableNode *vn = static_cast<VariableNode *>(n);
+ varname = vn->name;
+ } else if (n->type == Node::NODE_TYPE_ARRAY) {
+ ArrayNode *an = static_cast<ArrayNode *>(n);
+ varname = an->name;
+ }
+
if (shader->uniforms.has(varname)) {
//being sampler, this either comes from a uniform
ShaderNode::Uniform *u = &shader->uniforms[varname];
@@ -5468,7 +5658,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
//propagate
- if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat)) {
+ if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat, u->hint)) {
return nullptr;
}
} else if (p_function_info.built_ins.has(varname)) {
@@ -5567,6 +5757,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
_set_tkpos(prev_pos);
ShaderNode::Varying &var = shader->varyings[identifier];
+ calls_info[current_function].uses_restricted_items.push_back(Pair<StringName, CallInfo::Item>(identifier, CallInfo::Item(CallInfo::Item::ITEM_TYPE_VARYING, prev_pos)));
+
String error;
if (is_token_operator_assign(next_token.type)) {
if (!_validate_varying_assign(shader->varyings[identifier], &error)) {
@@ -6359,6 +6551,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
pos = _get_tkpos();
tk = _get_token();
+ if (p_previous_expression_info != nullptr && tk.type == p_previous_expression_info->tt_break && !p_previous_expression_info->is_last_expr) {
+ break;
+ }
+
if (is_token_operator(tk.type)) {
Expression o;
o.is_op = true;
@@ -6465,6 +6661,31 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expression.push_back(o);
+ if (o.op == OP_SELECT_IF) {
+ ExpressionInfo info;
+ info.expression = &expression;
+ info.tt_break = TK_COLON;
+
+ expr = _parse_and_reduce_expression(p_block, p_function_info, &info);
+ if (!expr) {
+ return nullptr;
+ }
+
+ expression.push_back({ true, { OP_SELECT_ELSE } });
+
+ if (p_previous_expression_info != nullptr) {
+ info.is_last_expr = p_previous_expression_info->is_last_expr;
+ } else {
+ info.is_last_expr = true;
+ }
+
+ expr = _parse_and_reduce_expression(p_block, p_function_info, &info);
+ if (!expr) {
+ return nullptr;
+ }
+
+ break;
+ }
} else {
_set_tkpos(pos); //something else, so rollback and end
break;
@@ -6777,6 +6998,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
}
+ if (p_previous_expression_info != nullptr) {
+ p_previous_expression_info->expression->push_back(expression[0]);
+ }
+
return expression[0].node;
}
@@ -6889,8 +7114,8 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
return p_node;
}
-ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info) {
- ShaderLanguage::Node *expr = _parse_expression(p_block, p_function_info);
+ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info, const ExpressionInfo *p_previous_expression_info) {
+ ShaderLanguage::Node *expr = _parse_expression(p_block, p_function_info, p_previous_expression_info);
if (!expr) { //errored
return nullptr;
}
@@ -7294,6 +7519,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
tk = _get_token();
+ } else {
+ _set_expected_error("(");
+ return ERR_PARSE_ERROR;
}
}
} else {
@@ -7931,7 +8159,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (!expr) {
return ERR_PARSE_ERROR;
}
- is_condition = expr->type == Node::NODE_TYPE_OPERATOR && expr->get_datatype() == TYPE_BOOL;
+ is_condition = expr->get_datatype() == TYPE_BOOL;
if (expr->type == Node::NODE_TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(expr);
@@ -7947,7 +8175,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION) {
if (tk.type == TK_COMMA) {
if (!is_condition) {
- _set_error(RTR("The middle expression is expected to be a boolean operator."));
+ _set_error(RTR("The middle expression is expected to have a boolean data type."));
+ return ERR_PARSE_ERROR;
+ }
+ tk = _peek();
+ if (tk.type == TK_SEMICOLON) {
+ _set_error(vformat(RTR("Expected expression, found: '%s'."), get_token_text(tk)));
return ERR_PARSE_ERROR;
}
continue;
@@ -7958,6 +8191,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
} else if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_EXPRESSION) {
if (tk.type == TK_COMMA) {
+ tk = _peek();
+ if (tk.type == TK_PARENTHESIS_CLOSE) {
+ _set_error(vformat(RTR("Expected expression, found: '%s'."), get_token_text(tk)));
+ return ERR_PARSE_ERROR;
+ }
continue;
}
if (tk.type != TK_PARENTHESIS_CLOSE) {
@@ -7976,7 +8214,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION && !is_condition) {
- _set_error(RTR("The middle expression is expected to be a boolean operator."));
+ _set_error(RTR("The middle expression is expected to have a boolean data type."));
return ERR_PARSE_ERROR;
}
}
@@ -8081,6 +8319,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
int texture_binding = 0;
int uniforms = 0;
int instance_index = 0;
+ int prop_index = 0;
#ifdef DEBUG_ENABLED
uint64_t uniform_buffer_size = 0;
uint64_t max_uniform_buffer_size = 0;
@@ -8099,6 +8338,8 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
ShaderNode::Uniform::Scope uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL;
stages = &p_functions;
+ is_supported_frag_only_funcs = shader_type_identifier == "canvas_item" || shader_type_identifier == "spatial" || shader_type_identifier == "sky";
+
const FunctionInfo &constants = p_functions.has("constants") ? p_functions["constants"] : FunctionInfo();
HashMap<String, String> defined_modes;
@@ -8633,6 +8874,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
++texture_binding;
}
uniform.order = -1;
+ uniform.prop_order = prop_index++;
} else {
if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE && (type == TYPE_MAT2 || type == TYPE_MAT3 || type == TYPE_MAT4)) {
_set_error(vformat(RTR("The '%s' qualifier is not supported for matrix types."), "SCOPE_INSTANCE"));
@@ -8641,6 +8883,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
uniform.texture_order = -1;
if (uniform_scope != ShaderNode::Uniform::SCOPE_INSTANCE) {
uniform.order = uniforms++;
+ uniform.prop_order = prop_index++;
#ifdef DEBUG_ENABLED
if (check_device_limit_warnings) {
if (uniform.array_size > 0) {
@@ -8836,6 +9079,40 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
new_hint = ShaderNode::Uniform::HINT_RANGE;
} break;
+ case TK_HINT_ENUM: {
+ if (type != TYPE_INT) {
+ _set_error(vformat(RTR("Enum hint is for '%s' only."), "int"));
+ return ERR_PARSE_ERROR;
+ }
+
+ tk = _get_token();
+ if (tk.type != TK_PARENTHESIS_OPEN) {
+ _set_expected_after_error("(", "hint_enum");
+ return ERR_PARSE_ERROR;
+ }
+
+ while (true) {
+ tk = _get_token();
+
+ if (tk.type != TK_STRING_CONSTANT) {
+ _set_error(RTR("Expected a string constant."));
+ return ERR_PARSE_ERROR;
+ }
+
+ uniform.hint_enum_names.push_back(tk.text);
+
+ tk = _get_token();
+
+ if (tk.type == TK_PARENTHESIS_CLOSE) {
+ break;
+ } else if (tk.type != TK_COMMA) {
+ _set_error(RTR("Expected ',' or ')' after string constant."));
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ new_hint = ShaderNode::Uniform::HINT_ENUM;
+ } break;
case TK_HINT_INSTANCE_INDEX: {
if (custom_instance_index != -1) {
_set_error(vformat(RTR("Can only specify '%s' once."), "instance_index"));
@@ -8930,7 +9207,9 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
default:
break;
}
- if (((new_filter != FILTER_DEFAULT || new_repeat != REPEAT_DEFAULT) || (new_hint != ShaderNode::Uniform::HINT_NONE && new_hint != ShaderNode::Uniform::HINT_SOURCE_COLOR && new_hint != ShaderNode::Uniform::HINT_RANGE)) && !is_sampler_type(type)) {
+
+ bool is_sampler_hint = new_hint != ShaderNode::Uniform::HINT_NONE && new_hint != ShaderNode::Uniform::HINT_SOURCE_COLOR && new_hint != ShaderNode::Uniform::HINT_RANGE && new_hint != ShaderNode::Uniform::HINT_ENUM;
+ if (((new_filter != FILTER_DEFAULT || new_repeat != REPEAT_DEFAULT) || is_sampler_hint) && !is_sampler_type(type)) {
_set_error(RTR("This hint is only for sampler types."));
return ERR_PARSE_ERROR;
}
@@ -9085,6 +9364,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
tk = _get_token();
if (tk.type == TK_IDENTIFIER) {
current_uniform_group_name = tk.text;
+ current_uniform_subgroup_name = "";
tk = _get_token();
if (tk.type == TK_PERIOD) {
tk = _get_token();
@@ -9401,6 +9681,9 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
_set_error(RTR("Array size mismatch."));
return ERR_PARSE_ERROR;
}
+ } else {
+ _set_expected_error("(");
+ return ERR_PARSE_ERROR;
}
array_size = constant.array_size;
@@ -9443,7 +9726,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
}
}
- constant.initializer = static_cast<ConstantNode *>(expr);
+ constant.initializer = expr;
if (!_compare_datatypes(type, struct_name, 0, expr->get_datatype(), expr->get_datatype_name(), expr->get_array_size())) {
return ERR_PARSE_ERROR;
@@ -9505,6 +9788,11 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
break;
}
+ if (is_constant) {
+ _set_error(vformat(RTR("'%s' qualifier cannot be used with a function return type."), "const"));
+ return ERR_PARSE_ERROR;
+ }
+
FunctionInfo builtins;
if (p_functions.has(name)) {
builtins = p_functions[name];
@@ -9541,6 +9829,11 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
shader->functions.insert(name, function);
shader->vfunctions.push_back(function);
+ CallInfo call_info;
+ call_info.name = name;
+
+ calls_info.insert(name, call_info);
+
func_node->name = name;
func_node->return_type = type;
func_node->return_struct_name = struct_name;
@@ -10325,10 +10618,11 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
}
while (builtin_func_defs[idx].name) {
- if (low_end && builtin_func_defs[idx].high_end) {
+ if ((low_end && builtin_func_defs[idx].high_end) || _check_restricted_func(builtin_func_defs[idx].name, skip_function)) {
idx++;
continue;
}
+
matches.insert(String(builtin_func_defs[idx].name), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
idx++;
}
@@ -10490,7 +10784,7 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
}
while (builtin_func_defs[idx].name) {
- if (low_end && builtin_func_defs[idx].high_end) {
+ if ((low_end && builtin_func_defs[idx].high_end) || _check_restricted_func(builtin_func_defs[idx].name, block_function)) {
idx++;
continue;
}
@@ -10623,15 +10917,21 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
}
} else if ((completion_base == DataType::TYPE_INT || completion_base == DataType::TYPE_FLOAT) && !completion_base_array) {
if (current_uniform_hint == ShaderNode::Uniform::HINT_NONE) {
- ScriptLanguage::CodeCompletionOption option("hint_range", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ Vector<String> options;
if (completion_base == DataType::TYPE_INT) {
- option.insert_text = "hint_range(0, 100, 1)";
+ options.push_back("hint_range(0, 100, 1)");
+ options.push_back("hint_enum(\"Zero\", \"One\", \"Two\")");
} else {
- option.insert_text = "hint_range(0.0, 1.0, 0.1)";
+ options.push_back("hint_range(0.0, 1.0, 0.1)");
}
- r_options->push_back(option);
+ for (const String &option_text : options) {
+ String hint_name = option_text.substr(0, option_text.find_char(char32_t('(')));
+ ScriptLanguage::CodeCompletionOption option(hint_name, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ option.insert_text = option_text;
+ r_options->push_back(option);
+ }
}
} else if ((int(completion_base) > int(TYPE_MAT4) && int(completion_base) < int(TYPE_STRUCT))) {
Vector<String> options;
@@ -10707,17 +11007,16 @@ ShaderLanguage::ShaderLanguage() {
nodes = nullptr;
completion_class = TAG_GLOBAL;
- int idx = 0;
- while (builtin_func_defs[idx].name) {
- if (builtin_func_defs[idx].tag == SubClassTag::TAG_GLOBAL) {
- const StringName &name = StringName(builtin_func_defs[idx].name);
-
- if (!global_func_set.has(name)) {
- global_func_set.insert(name);
+ if (instance_counter == 0) {
+ int idx = 0;
+ while (builtin_func_defs[idx].name) {
+ if (builtin_func_defs[idx].tag == SubClassTag::TAG_GLOBAL) {
+ global_func_set.insert(builtin_func_defs[idx].name);
}
+ idx++;
}
- idx++;
}
+ instance_counter++;
#ifdef DEBUG_ENABLED
warnings_check_map.insert(ShaderWarning::UNUSED_CONSTANT, &used_constants);
@@ -10732,4 +11031,8 @@ ShaderLanguage::ShaderLanguage() {
ShaderLanguage::~ShaderLanguage() {
clear();
+ instance_counter--;
+ if (instance_counter == 0) {
+ global_func_set.clear();
+ }
}