diff --git a/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp index 32321164a1e2..79bb99064959 100644 --- a/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp +++ b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp @@ -711,6 +711,11 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge gcode += _typestr(cnode.type); } gcode += " " + _mkid(String(cnode.name)); + if (cnode.array_size > 0) { + gcode += "["; + gcode += itos(cnode.array_size); + gcode += "]"; + } gcode += "="; gcode += _dump_node_code(cnode.initializer, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); gcode += ";\n"; @@ -988,7 +993,29 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge } break; case SL::Node::TYPE_CONSTANT: { SL::ConstantNode *cnode = (SL::ConstantNode *)p_node; - return get_constant_text(cnode->datatype, cnode->values); + + if (cnode->array_size == 0) { + return get_constant_text(cnode->datatype, cnode->values); + } else { + if (cnode->get_datatype() == SL::TYPE_STRUCT) { + code += _mkid(cnode->struct_name); + } else { + code += _typestr(cnode->datatype); + } + code += "["; + code += itos(cnode->array_size); + code += "]"; + code += "("; + for (int i = 0; i < cnode->array_size; i++) { + if (i > 0) { + code += ","; + } else { + code += ""; + } + code += _dump_node_code(cnode->array_declarations[0].initializer[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + } + code += ")"; + } } break; case SL::Node::TYPE_OPERATOR: { diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 2ec65b7ea8ee..a3fef10cc027 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -1014,6 +1014,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea 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; } @@ -3064,7 +3067,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Maptype == Node::TYPE_ARRAY) { ArrayNode *arr = static_cast(p_node); - if (arr->is_const) { + if (shader->constants.has(arr->name) || arr->is_const) { if (r_message) { *r_message = RTR("Constants cannot be modified."); } @@ -4976,7 +4979,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Mapstructs.has(tk.text)) { type2 = TYPE_STRUCT; @@ -6445,38 +6448,253 @@ Error ShaderLanguage::_parse_shader(const Map &p_funct constant.type_str = struct_name; constant.precision = precision; constant.initializer = nullptr; + constant.array_size = 0; - if (tk.type == TK_OP_ASSIGN) { - if (!is_constant) { - _set_error("Expected 'const' keyword before constant definition"); + bool unknown_size = false; + + if (tk.type == TK_BRACKET_OPEN) { + if (RenderingServer::get_singleton()->is_low_end()) { + _set_error("Global const arrays are supported only on high-end platform!"); return ERR_PARSE_ERROR; } - //variable created with assignment! must parse an expression - Node *expr = _parse_and_reduce_expression(nullptr, Map()); - if (!expr) { + tk = _get_token(); + if (tk.type == TK_BRACKET_CLOSE) { + unknown_size = true; + tk = _get_token(); + } else if (tk.type == TK_INT_CONSTANT && ((int)tk.constant) > 0) { + constant.array_size = (int)tk.constant; + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); + } else { + _set_error("Expected integer constant > 0 or ']'"); return ERR_PARSE_ERROR; } - if (expr->type == Node::TYPE_OPERATOR && ((OperatorNode *)expr)->op == OP_CALL) { - _set_error("Expected constant expression after '='"); + } + + if (tk.type == TK_OP_ASSIGN) { + if (!is_constant) { + _set_error("Expected 'const' keyword before constant definition"); return ERR_PARSE_ERROR; } - constant.initializer = static_cast(expr); + if (constant.array_size > 0 || unknown_size) { + bool full_def = false; + + ArrayDeclarationNode::Declaration decl; + decl.name = name; + decl.size = constant.array_size; - if (is_struct) { - if (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 '" + struct_name + "'"); + tk = _get_token(); + + if (tk.type != TK_CURLY_BRACKET_OPEN) { + if (unknown_size) { + _set_error("Expected '{'"); + return ERR_PARSE_ERROR; + } + + full_def = true; + + DataPrecision precision2 = PRECISION_DEFAULT; + if (is_token_precision(tk.type)) { + precision2 = get_token_precision(tk.type); + tk = _get_token(); + if (!is_token_nonvoid_datatype(tk.type)) { + _set_error("Expected datatype after precision"); + return ERR_PARSE_ERROR; + } + } + + StringName struct_name2; + DataType type2; + + 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) { + array_size2 = constant.array_size; + tk = _get_token(); + } else { + _set_tkpos(pos2); + + Node *n = _parse_and_reduce_expression(NULL, Map()); + 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 (constant.precision != precision2 || constant.type != type2 || struct_name != struct_name2 || constant.array_size != array_size2) { + String error_str = "Cannot convert from '"; + if (type2 == TYPE_STRUCT) { + error_str += struct_name2; + } else { + if (precision2 != PRECISION_DEFAULT) { + error_str += get_precision_name(precision2); + error_str += " "; + } + 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 { + if (precision != PRECISION_DEFAULT) { + error_str += get_precision_name(precision); + error_str += " "; + } + error_str += get_datatype_name(type); + } + error_str += "["; + error_str += itos(constant.array_size); + error_str += "]'"; + _set_error(error_str); + return ERR_PARSE_ERROR; + } + } + + 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 '('"); + return ERR_PARSE_ERROR; + } + } + } + + if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization + while (true) { + Node *n = _parse_and_reduce_expression(NULL, Map()); + if (!n) { + return ERR_PARSE_ERROR; + } + + if (n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { + _set_error("Expected constant expression"); + 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)) + "'"); + 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 ','"); + else + _set_error("Expected ')' or ','"); + return ERR_PARSE_ERROR; + } + } + if (unknown_size) { + decl.size = decl.initializer.size(); + constant.array_size = decl.initializer.size(); + } else if (decl.initializer.size() != constant.array_size) { + _set_error("Array size mismatch"); + return ERR_PARSE_ERROR; + } + } + + ConstantNode *expr = memnew(ConstantNode); + + expr->datatype = constant.type; + + expr->struct_name = constant.type_str; + + expr->array_size = constant.array_size; + + expr->array_declarations.push_back(decl); + + constant.initializer = static_cast(expr); + } else { + //variable created with assignment! must parse an expression + Node *expr = _parse_and_reduce_expression(NULL, Map()); + 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; + } + + constant.initializer = static_cast(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)) + "'"); return ERR_PARSE_ERROR; } - } else if (type != expr->get_datatype()) { - _set_error("Invalid assignment of '" + get_datatype_name(expr->get_datatype()) + "' to '" + get_datatype_name(type) + "'"); - return ERR_PARSE_ERROR; } tk = _get_token(); } else { - _set_error("Expected initialization of constant"); - return ERR_PARSE_ERROR; + if (constant.array_size > 0 || unknown_size) { + _set_error("Expected array initialization"); + return ERR_PARSE_ERROR; + } else { + _set_error("Expected initialization of constant"); + return ERR_PARSE_ERROR; + } } shader->constants[name] = constant; diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 020a5e3e6fa4..bc8f03774a1b 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -453,6 +453,8 @@ class ShaderLanguage { struct ConstantNode : public Node { DataType datatype = TYPE_VOID; + String struct_name = ""; + int array_size = 0; union Value { bool boolean; @@ -462,7 +464,9 @@ class ShaderLanguage { }; Vector values; + Vector array_declarations; virtual DataType get_datatype() const { return datatype; } + virtual String get_datatype_name() const { return struct_name; } ConstantNode() : Node(TYPE_CONSTANT) {} @@ -573,6 +577,7 @@ class ShaderLanguage { StringName type_str; DataPrecision precision; ConstantNode *initializer; + int array_size; }; struct Function {