diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index e0dea5b06c1..de0179c77f1 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -879,6 +879,8 @@ void ShaderLanguage::clear() { completion_class = SubClassTag::TAG_GLOBAL; completion_struct = StringName(); + unknown_varying_usages.clear(); + error_line = 0; tk_line = 1; char_idx = 0; @@ -2447,6 +2449,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) { @@ -2793,8 +2809,7 @@ bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, St p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT; } break; - case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT: - case ShaderNode::Varying::STAGE_VERTEX_TO_LIGHT: + case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT: case ShaderNode::Varying::STAGE_VERTEX: if (current_function == String("fragment")) { *r_message = RTR("Varyings which assigned in 'vertex' function may not be reassigned in 'fragment' or 'light'."); @@ -2817,13 +2832,14 @@ bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, St bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, String *r_message) { switch (p_varying.stage) { case ShaderNode::Varying::STAGE_UNKNOWN: - *r_message = RTR("Varying must be assigned before using!"); + VaryingUsage usage; + usage.var = &p_varying; + usage.line = tk_line; + unknown_varying_usages.push_back(usage); return false; case ShaderNode::Varying::STAGE_VERTEX: - if (current_function == String("fragment")) { - p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT; - } else if (current_function == String("light")) { - p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_LIGHT; + if (current_function == String("fragment") || current_function == String("light")) { + p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT; } break; case ShaderNode::Varying::STAGE_FRAGMENT: @@ -2831,24 +2847,25 @@ bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, Str p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT; } break; - case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT: - if (current_function == String("light")) { - *r_message = RTR("Varying must only be used in two different stages, which can be 'vertex' 'fragment' and 'light'"); - return false; - } - break; - case ShaderNode::Varying::STAGE_VERTEX_TO_LIGHT: - if (current_function == String("fragment")) { - *r_message = RTR("Varying must only be used in two different stages, which can be 'vertex' 'fragment' and 'light'"); - return false; - } - break; default: break; } return true; } +bool ShaderLanguage::_check_varying_usages(int *r_error_line, String *r_error_message) const { + for (const List::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::_validate_assign(Node *p_node, const Map &p_builtin_types, String *r_message) { if (p_node->type == Node::TYPE_OPERATOR) { OperatorNode *op = static_cast(p_node); @@ -3502,7 +3519,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons Token next_token = _get_token(); _set_tkpos(prev_pos); String error; - if (next_token.type == TK_OP_ASSIGN) { + + if (is_token_operator_assign(next_token.type)) { if (!_validate_varying_assign(shader->varyings[identifier], &error)) { _set_error(error); return nullptr; @@ -6347,6 +6365,14 @@ Error ShaderLanguage::_parse_shader(const Map &p_funct tk = _get_token(); } + 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; + } + return OK; } diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h index 992d0636b86..5b6bfd62479 100644 --- a/servers/visual/shader_language.h +++ b/servers/visual/shader_language.h @@ -587,10 +587,9 @@ public: struct Varying { enum Stage { STAGE_UNKNOWN, - STAGE_VERTEX, // transition stage to STAGE_VERTEX_TO_FRAGMENT or STAGE_VERTEX_TO_LIGHT, emits error if they are not used - STAGE_FRAGMENT, // transition stage to STAGE_FRAGMENT_TO_LIGHT, emits error if it's not used - STAGE_VERTEX_TO_FRAGMENT, - STAGE_VERTEX_TO_LIGHT, + STAGE_VERTEX, // transition stage to STAGE_VERTEX_TO_FRAGMENT_LIGHT, emits warning if it's not used + STAGE_FRAGMENT, // transition stage to STAGE_FRAGMENT_TO_LIGHT, emits warning if it's not used + STAGE_VERTEX_TO_FRAGMENT_LIGHT, STAGE_FRAGMENT_TO_LIGHT, }; @@ -702,6 +701,7 @@ public: static String get_datatype_name(DataType p_type); static bool is_token_nonvoid_datatype(TokenType p_type); static bool is_token_operator(TokenType p_type); + static bool is_token_operator_assign(TokenType p_type); static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value = nullptr); static DataType get_scalar_type(DataType p_type); @@ -752,6 +752,14 @@ private: StringName current_function; bool last_const = false; + struct VaryingUsage { + ShaderNode::Varying *var; + int line; + }; + List unknown_varying_usages; + + bool _check_varying_usages(int *r_error_line, String *r_error_message) const; + TkPos _get_tkpos() { TkPos tkp; tkp.char_idx = char_idx;