Browse Source

Implemented global const array to shaders

Yuri Roubinsky 5 years ago
parent
commit
636a9d9f50

+ 28 - 1
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: {

+ 237 - 19
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 Map<StringName, BuiltI
 	} else if (p_node->type == Node::TYPE_ARRAY) {
 		ArrayNode *arr = static_cast<ArrayNode *>(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 Map<StringName, Bui
 							}
 
 							DataType type2;
-							String struct_name2 = "";
+							StringName struct_name2 = "";
 
 							if (shader->structs.has(tk.text)) {
 								type2 = TYPE_STRUCT;
@@ -6445,38 +6448,253 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &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<StringName, BuiltInInfo>());
-							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<ConstantNode *>(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<StringName, BuiltInInfo>());
+											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<StringName, BuiltInInfo>());
+										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<ConstantNode *>(expr);
+							} else {
+								//variable created with assignment! must parse an expression
+								Node *expr = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>());
+								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<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)) + "'");
 									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;

+ 5 - 0
servers/rendering/shader_language.h

@@ -453,6 +453,8 @@ public:
 
 	struct ConstantNode : public Node {
 		DataType datatype = TYPE_VOID;
+		String struct_name = "";
+		int array_size = 0;
 
 		union Value {
 			bool boolean;
@@ -462,7 +464,9 @@ public:
 		};
 
 		Vector<Value> values;
+		Vector<ArrayDeclarationNode::Declaration> 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 @@ public:
 			StringName type_str;
 			DataPrecision precision;
 			ConstantNode *initializer;
+			int array_size;
 		};
 
 		struct Function {