Browse Source

Add basic support to evaluate operator value in shader language

Yuri Rubinsky 1 year ago
parent
commit
af92fdb0ac

+ 2 - 2
drivers/gles3/storage/material_storage.cpp

@@ -348,7 +348,7 @@ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_
 	}
 }
 
-_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) {
+_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::Scalar> &value, uint8_t *data) {
 	switch (type) {
 		case ShaderLanguage::TYPE_BOOL: {
 			uint32_t *gui = (uint32_t *)data;
@@ -572,7 +572,7 @@ void ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_t
 Variant ShaderData::get_default_parameter(const StringName &p_parameter) const {
 	if (uniforms.has(p_parameter)) {
 		ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter];
-		Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
+		Vector<ShaderLanguage::Scalar> default_value = uniform.default_value;
 		return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint);
 	}
 	return Variant();

+ 2 - 2
servers/rendering/renderer_rd/storage_rd/material_storage.cpp

@@ -342,7 +342,7 @@ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_
 	}
 }
 
-_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) {
+_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::Scalar> &value, uint8_t *data) {
 	switch (type) {
 		case ShaderLanguage::TYPE_BOOL: {
 			uint32_t *gui = (uint32_t *)data;
@@ -566,7 +566,7 @@ void MaterialStorage::ShaderData::set_default_texture_parameter(const StringName
 Variant MaterialStorage::ShaderData::get_default_parameter(const StringName &p_parameter) const {
 	if (uniforms.has(p_parameter)) {
 		ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter];
-		Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
+		Vector<ShaderLanguage::Scalar> default_value = uniform.default_value;
 		return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint);
 	}
 	return Variant();

+ 1 - 1
servers/rendering/shader_compiler.cpp

@@ -185,7 +185,7 @@ static String f2sp0(float p_float) {
 	return num;
 }
 
-static String get_constant_text(SL::DataType p_type, const Vector<SL::ConstantNode::Value> &p_values) {
+static String get_constant_text(SL::DataType p_type, const Vector<SL::Scalar> &p_values) {
 	switch (p_type) {
 		case SL::TYPE_BOOL:
 			return p_values[0].boolean ? "true" : "false";

+ 505 - 102
servers/rendering/shader_language.cpp

@@ -1362,7 +1362,7 @@ void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, Iden
 }
 #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) {
+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, Vector<Scalar> *r_constant_values) {
 	if (is_shader_inc) {
 		for (int i = 0; i < RenderingServer::SHADER_MAX; i++) {
 			for (const KeyValue<StringName, FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(i))) {
@@ -1424,8 +1424,8 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
 			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_constant_values && !p_block->variables[p_identifier].values.is_empty()) {
+				*r_constant_values = p_block->variables[p_identifier].values;
 			}
 			if (r_type) {
 				*r_type = IDENTIFIER_LOCAL_VAR;
@@ -1507,13 +1507,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
 		if (r_struct_name) {
 			*r_struct_name = shader->constants[p_identifier].struct_name;
 		}
-		if (r_constant_value) {
-			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_constant_values) {
+			if (shader->constants[p_identifier].initializer && !shader->constants[p_identifier].initializer->get_values().is_empty()) {
+				*r_constant_values = shader->constants[p_identifier].initializer->get_values();
 			}
 		}
 		if (r_type) {
@@ -1544,7 +1540,7 @@ 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, int *r_ret_size) {
+bool ShaderLanguage::_validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) {
 	bool valid = false;
 	DataType ret_type = TYPE_VOID;
 	int ret_size = 0;
@@ -2007,9 +2003,384 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
 	if (r_ret_size) {
 		*r_ret_size = ret_size;
 	}
+
+	if (valid && (!p_block || p_block->use_op_eval)) {
+		// Need to be placed here and not in the `_reduce_expression` because otherwise expressions like `1 + 2 / 2` will not work correctly.
+		valid = _eval_operator(p_block, p_op);
+	}
+
 	return valid;
 }
 
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_get_node_values(const BlockNode *p_block, Node *p_node) {
+	Vector<Scalar> result;
+
+	switch (p_node->type) {
+		case Node::NODE_TYPE_VARIABLE: {
+			_find_identifier(p_block, false, FunctionInfo(), static_cast<VariableNode *>(p_node)->name, nullptr, nullptr, nullptr, nullptr, nullptr, &result);
+		} break;
+		default: {
+			result = p_node->get_values();
+		} break;
+	}
+
+	return result;
+}
+
+bool ShaderLanguage::_eval_operator(const BlockNode *p_block, OperatorNode *p_op) {
+	bool is_valid = true;
+
+	switch (p_op->op) {
+		case OP_EQUAL:
+		case OP_NOT_EQUAL:
+		case OP_LESS:
+		case OP_LESS_EQUAL:
+		case OP_GREATER:
+		case OP_GREATER_EQUAL:
+		case OP_AND:
+		case OP_OR:
+		case OP_ADD:
+		case OP_SUB:
+		case OP_MUL:
+		case OP_DIV:
+		case OP_MOD:
+		case OP_SHIFT_LEFT:
+		case OP_SHIFT_RIGHT:
+		case OP_BIT_AND:
+		case OP_BIT_OR:
+		case OP_BIT_XOR: {
+			DataType a = p_op->arguments[0]->get_datatype();
+			DataType b = p_op->arguments[1]->get_datatype();
+
+			bool is_op_vec_transform = false;
+			if (p_op->op == OP_MUL) {
+				DataType ta = a;
+				DataType tb = b;
+
+				if (ta > tb) {
+					SWAP(ta, tb);
+				}
+				if (ta == TYPE_VEC2 && tb == TYPE_MAT2) {
+					is_op_vec_transform = true;
+				} else if (ta == TYPE_VEC3 && tb == TYPE_MAT3) {
+					is_op_vec_transform = true;
+				} else if (ta == TYPE_VEC4 && tb == TYPE_MAT4) {
+					is_op_vec_transform = true;
+				}
+			}
+
+			Vector<Scalar> va = _get_node_values(p_block, p_op->arguments[0]);
+			Vector<Scalar> vb = _get_node_values(p_block, p_op->arguments[1]);
+
+			if (is_op_vec_transform) {
+				p_op->values = _eval_vector_transform(va, vb, a, b, p_op->get_datatype());
+			} else {
+				p_op->values = _eval_vector(va, vb, a, b, p_op->get_datatype(), p_op->op, is_valid);
+			}
+		} break;
+		case OP_NOT:
+		case OP_NEGATE:
+		case OP_BIT_INVERT: {
+			p_op->values = _eval_unary_vector(_get_node_values(p_block, p_op->arguments[0]), p_op->get_datatype(), p_op->op);
+		} break;
+		default: {
+		} break;
+	}
+
+	return is_valid;
+}
+
+ShaderLanguage::Scalar ShaderLanguage::_eval_unary_scalar(const Scalar &p_a, Operator p_op, DataType p_ret_type) {
+	Scalar scalar;
+
+	switch (p_op) {
+		case OP_NOT: {
+			scalar.boolean = !p_a.boolean;
+		} break;
+		case OP_NEGATE: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				scalar.sint = -p_a.sint;
+			} else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+				// Intentionally wrap the unsigned int value, because GLSL does.
+				scalar.uint = 0 - p_a.uint;
+			} else { // float types
+				scalar.real = -scalar.real;
+			}
+		} break;
+		case OP_BIT_INVERT: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				scalar.sint = ~p_a.sint;
+			} else { // uint types
+				scalar.uint = ~p_a.uint;
+			}
+		} break;
+		default: {
+		} break;
+	}
+
+	return scalar;
+}
+
+ShaderLanguage::Scalar ShaderLanguage::_eval_scalar(const Scalar &p_a, const Scalar &p_b, Operator p_op, DataType p_ret_type, bool &r_is_valid) {
+	Scalar scalar;
+
+	switch (p_op) {
+		case OP_EQUAL: {
+			scalar.boolean = p_a.boolean == p_b.boolean;
+		} break;
+		case OP_NOT_EQUAL: {
+			scalar.boolean = p_a.boolean != p_b.boolean;
+		} break;
+		case OP_LESS: {
+			if (p_ret_type == TYPE_INT) {
+				scalar.boolean = p_a.sint < p_b.sint;
+			} else if (p_ret_type == TYPE_UINT) {
+				scalar.boolean = p_a.uint < p_b.uint;
+			} else { // float type
+				scalar.boolean = p_a.real < p_b.real;
+			}
+		} break;
+		case OP_LESS_EQUAL: {
+			if (p_ret_type == TYPE_INT) {
+				scalar.boolean = p_a.sint <= p_b.sint;
+			} else if (p_ret_type == TYPE_UINT) {
+				scalar.boolean = p_a.uint <= p_b.uint;
+			} else { // float type
+				scalar.boolean = p_a.real <= p_b.real;
+			}
+		} break;
+		case OP_GREATER: {
+			if (p_ret_type == TYPE_INT) {
+				scalar.boolean = p_a.sint > p_b.sint;
+			} else if (p_ret_type == TYPE_UINT) {
+				scalar.boolean = p_a.uint > p_b.uint;
+			} else { // float type
+				scalar.boolean = p_a.real > p_b.real;
+			}
+		} break;
+		case OP_GREATER_EQUAL: {
+			if (p_ret_type == TYPE_INT) {
+				scalar.boolean = p_a.sint >= p_b.sint;
+			} else if (p_ret_type == TYPE_UINT) {
+				scalar.boolean = p_a.uint >= p_b.uint;
+			} else { // float type
+				scalar.boolean = p_a.real >= p_b.real;
+			}
+		} break;
+		case OP_AND: {
+			scalar.boolean = p_a.boolean && p_b.boolean;
+		} break;
+		case OP_OR: {
+			scalar.boolean = p_a.boolean || p_b.boolean;
+		} break;
+		case OP_ADD: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				scalar.sint = p_a.sint + p_b.sint;
+			} else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+				scalar.uint = p_a.uint + p_b.uint;
+			} else { // float + matrix types
+				scalar.real = p_a.real + p_b.real;
+			}
+		} break;
+		case OP_SUB: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				scalar.sint = p_a.sint - p_b.sint;
+			} else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+				scalar.uint = p_a.uint - p_b.uint;
+			} else { // float + matrix types
+				scalar.real = p_a.real - p_b.real;
+			}
+		} break;
+		case OP_MUL: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				scalar.sint = p_a.sint * p_b.sint;
+			} else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+				scalar.uint = p_a.uint * p_b.uint;
+			} else { // float + matrix types
+				scalar.real = p_a.real * p_b.real;
+			}
+		} break;
+		case OP_DIV: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				if (p_b.sint == 0) {
+					_set_error(RTR("Division by zero error."));
+					r_is_valid = false;
+					break;
+				}
+				scalar.sint = p_a.sint / p_b.sint;
+			} else if (p_ret_type == TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+				if (p_b.uint == 0U) {
+					_set_error(RTR("Division by zero error."));
+					r_is_valid = false;
+					break;
+				}
+				scalar.uint = p_a.uint / p_b.uint;
+			} else { // float + matrix types
+				scalar.real = p_a.real / p_b.real;
+			}
+		} break;
+		case OP_MOD: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				if (p_b.sint == 0) {
+					_set_error(RTR("Modulo by zero error."));
+					r_is_valid = false;
+					break;
+				}
+				scalar.sint = p_a.sint % p_b.sint;
+			} else { // uint types
+				if (p_b.uint == 0U) {
+					_set_error(RTR("Modulo by zero error."));
+					r_is_valid = false;
+					break;
+				}
+				scalar.uint = p_a.uint % p_b.uint;
+			}
+		} break;
+		case OP_SHIFT_LEFT: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				scalar.sint = p_a.sint << p_b.sint;
+			} else { // uint types
+				scalar.uint = p_a.uint << p_b.uint;
+			}
+		} break;
+		case OP_SHIFT_RIGHT: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				scalar.sint = p_a.sint >> p_b.sint;
+			} else { // uint types
+				scalar.uint = p_a.uint >> p_b.uint;
+			}
+		} break;
+		case OP_BIT_AND: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				scalar.sint = p_a.sint & p_b.sint;
+			} else { // uint types
+				scalar.uint = p_a.uint & p_b.uint;
+			}
+		} break;
+		case OP_BIT_OR: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				scalar.sint = p_a.sint | p_b.sint;
+			} else { // uint types
+				scalar.uint = p_a.uint | p_b.uint;
+			}
+		} break;
+		case OP_BIT_XOR: {
+			if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+				scalar.sint = p_a.sint ^ p_b.sint;
+			} else { // uint types
+				scalar.uint = p_a.uint ^ p_b.uint;
+			}
+		} break;
+		default: {
+		} break;
+	}
+
+	return scalar;
+}
+
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_unary_vector(const Vector<Scalar> &p_va, DataType p_ret_type, Operator p_op) {
+	uint32_t size = get_datatype_component_count(p_ret_type);
+	if (p_va.size() != p_ret_type) {
+		return Vector<Scalar>(); // Non-evaluable values should not be parsed further.
+	}
+	Vector<Scalar> value;
+	value.resize(size);
+
+	Scalar *w = value.ptrw();
+	for (uint32_t i = 0U; i < size; i++) {
+		w[i] = _eval_unary_scalar(p_va[i], p_op, p_ret_type);
+	}
+	return value;
+}
+
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_vector(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type, Operator p_op, bool &r_is_valid) {
+	uint32_t left_size = get_datatype_component_count(p_left_type);
+	uint32_t right_size = get_datatype_component_count(p_right_type);
+
+	if (p_va.size() != left_size || p_vb.size() != right_size) {
+		return Vector<Scalar>(); // Non-evaluable values should not be parsed further.
+	}
+
+	uint32_t ret_size = get_datatype_component_count(p_ret_type);
+	Vector<Scalar> value;
+	value.resize(ret_size);
+
+	Scalar *w = value.ptrw();
+	for (uint32_t i = 0U; i < ret_size; i++) {
+		w[i] = _eval_scalar(p_va[MIN(i, left_size - 1)], p_vb[MIN(i, right_size - 1)], p_op, p_ret_type, r_is_valid);
+		if (!r_is_valid) {
+			return value;
+		}
+	}
+	return value;
+}
+
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_vector_transform(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type) {
+	uint32_t left_size = get_datatype_component_count(p_left_type);
+	uint32_t right_size = get_datatype_component_count(p_right_type);
+
+	if (p_va.size() != left_size || p_vb.size() != right_size) {
+		return Vector<Scalar>(); // Non-evaluable values should not be parsed further.
+	}
+
+	uint32_t ret_size = get_datatype_component_count(p_ret_type);
+	Vector<Scalar> value;
+	value.resize_zeroed(ret_size);
+
+	Scalar *w = value.ptrw();
+	switch (p_ret_type) {
+		case TYPE_VEC2: {
+			if (left_size == 2) { // v * m
+				Vector2 v = Vector2(p_va[0].real, p_va[1].real);
+
+				w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y);
+				w[1].real = (p_vb[2].real * v.x + p_vb[3].real * v.y);
+			} else { // m * v
+				Vector2 v = Vector2(p_vb[0].real, p_vb[1].real);
+
+				w[0].real = (p_va[0].real * v.x + p_va[2].real * v.y);
+				w[1].real = (p_va[1].real * v.x + p_va[3].real * v.y);
+			}
+		} break;
+		case TYPE_VEC3: {
+			if (left_size == 3) { // v * m
+				Vector3 v = Vector3(p_va[0].real, p_va[1].real, p_va[2].real);
+
+				w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y + p_vb[2].real * v.z);
+				w[1].real = (p_vb[3].real * v.x + p_vb[4].real * v.y + p_vb[5].real * v.z);
+				w[2].real = (p_vb[6].real * v.x + p_vb[7].real * v.y + p_vb[8].real * v.z);
+			} else { // m * v
+				Vector3 v = Vector3(p_vb[0].real, p_vb[1].real, p_vb[2].real);
+
+				w[0].real = (p_va[0].real * v.x + p_va[3].real * v.y + p_va[6].real * v.z);
+				w[1].real = (p_va[1].real * v.x + p_va[4].real * v.y + p_va[7].real * v.z);
+				w[2].real = (p_va[2].real * v.x + p_va[5].real * v.y + p_va[8].real * v.z);
+			}
+		} break;
+		case TYPE_VEC4: {
+			if (left_size == 4) { // v * m
+				Vector4 v = Vector4(p_va[0].real, p_va[1].real, p_va[2].real, p_va[3].real);
+
+				w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y + p_vb[2].real * v.z + p_vb[3].real * v.w);
+				w[1].real = (p_vb[4].real * v.x + p_vb[5].real * v.y + p_vb[6].real * v.z + p_vb[7].real * v.w);
+				w[2].real = (p_vb[8].real * v.x + p_vb[9].real * v.y + p_vb[10].real * v.z + p_vb[11].real * v.w);
+				w[3].real = (p_vb[12].real * v.x + p_vb[13].real * v.y + p_vb[14].real * v.z + p_vb[15].real * v.w);
+			} else { // m * v
+				Vector4 v = Vector4(p_vb[0].real, p_vb[1].real, p_vb[2].real, p_vb[3].real);
+
+				w[0].real = (p_vb[0].real * v.x + p_vb[4].real * v.y + p_vb[8].real * v.z + p_vb[12].real * v.w);
+				w[1].real = (p_vb[1].real * v.x + p_vb[5].real * v.y + p_vb[9].real * v.z + p_vb[13].real * v.w);
+				w[2].real = (p_vb[2].real * v.x + p_vb[6].real * v.y + p_vb[10].real * v.z + p_vb[14].real * v.w);
+				w[3].real = (p_vb[3].real * v.x + p_vb[7].real * v.y + p_vb[11].real * v.z + p_vb[15].real * v.w);
+			}
+		} break;
+		default: {
+		} break;
+	}
+
+	return value;
+}
+
 const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
 	// Constructors.
 
@@ -3271,34 +3642,15 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
 								int max = builtin_func_const_args[constarg_idx].max;
 
 								bool error = false;
-								if (p_func->arguments[arg]->type == Node::NODE_TYPE_VARIABLE) {
-									const VariableNode *vn = static_cast<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) {
+								Vector<Scalar> values = _get_node_values(p_block, p_func->arguments[arg]);
+								if (p_func->arguments[arg]->get_datatype() == TYPE_INT && !values.is_empty()) {
+									if (values[0].sint < min || values[0].sint > max) {
 										error = true;
 									}
 								} else {
-									if (p_func->arguments[arg]->type == Node::NODE_TYPE_CONSTANT) {
-										const ConstantNode *cn = static_cast<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;
-									}
+									error = true;
 								}
+
 								if (error) {
 									_set_error(vformat(RTR("Expected integer constant within [%d..%d] range."), min, max));
 									return false;
@@ -3760,7 +4112,7 @@ bool ShaderLanguage::is_token_hint(TokenType p_type) {
 	return int(p_type) > int(TK_RENDER_MODE) && int(p_type) < int(TK_SHADER_TYPE);
 }
 
-bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value) {
+bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value) {
 	if (p_constant->datatype == p_to_type) {
 		if (p_value) {
 			for (int i = 0; i < p_constant->values.size(); i++) {
@@ -3828,7 +4180,7 @@ bool ShaderLanguage::is_sampler_type(DataType p_type) {
 	return p_type > TYPE_MAT4 && p_type < TYPE_STRUCT;
 }
 
-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) {
+Variant ShaderLanguage::constant_value_to_variant(const Vector<Scalar> &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) {
@@ -4437,6 +4789,52 @@ uint32_t ShaderLanguage::get_datatype_size(ShaderLanguage::DataType p_type) {
 	ERR_FAIL_V(0);
 }
 
+uint32_t ShaderLanguage::get_datatype_component_count(ShaderLanguage::DataType p_type) {
+	switch (p_type) {
+		case TYPE_BOOL:
+			return 1U;
+		case TYPE_BVEC2:
+			return 2U;
+		case TYPE_BVEC3:
+			return 3U;
+		case TYPE_BVEC4:
+			return 4U;
+		case TYPE_INT:
+			return 1U;
+		case TYPE_IVEC2:
+			return 2U;
+		case TYPE_IVEC3:
+			return 3U;
+		case TYPE_IVEC4:
+			return 4U;
+		case TYPE_UINT:
+			return 1U;
+		case TYPE_UVEC2:
+			return 2U;
+		case TYPE_UVEC3:
+			return 3U;
+		case TYPE_UVEC4:
+			return 4U;
+		case TYPE_FLOAT:
+			return 1U;
+		case TYPE_VEC2:
+			return 2U;
+		case TYPE_VEC3:
+			return 3U;
+		case TYPE_VEC4:
+			return 4U;
+		case TYPE_MAT2:
+			return 4U;
+		case TYPE_MAT3:
+			return 9U;
+		case TYPE_MAT4:
+			return 16U;
+		default:
+			break;
+	}
+	return 0U;
+}
+
 void ShaderLanguage::get_keyword_list(List<String> *r_keywords) {
 	HashSet<String> kws;
 
@@ -4929,45 +5327,30 @@ Error ShaderLanguage::_parse_array_size(BlockNode *p_block, const FunctionInfo &
 			*r_unknown_size = true;
 		}
 	} else {
-		int array_size = 0;
+		_set_tkpos(pos);
 
-		if (!tk.is_integer_constant() || ((int)tk.constant) <= 0) {
-			_set_tkpos(pos);
-			Node *n = _parse_and_reduce_expression(p_block, p_function_info);
-			if (n) {
-				if (n->type == Node::NODE_TYPE_VARIABLE) {
-					VariableNode *vn = static_cast<VariableNode *>(n);
-					if (vn) {
-						ConstantNode::Value v;
-						DataType data_type;
-						bool is_const = false;
+		int array_size = 0;
+		Node *expr = _parse_and_reduce_expression(p_block, p_function_info);
 
-						_find_identifier(p_block, false, p_function_info, vn->name, &data_type, nullptr, &is_const, nullptr, nullptr, &v);
+		if (expr) {
+			Vector<Scalar> values = _get_node_values(p_block, expr);
 
-						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::NODE_TYPE_OPERATOR) {
-					_set_error(vformat(RTR("Array size expressions are not supported.")));
-					return ERR_PARSE_ERROR;
-				}
-				if (r_size_expression != nullptr) {
-					*r_size_expression = n;
+			if (!values.is_empty()) {
+				switch (expr->get_datatype()) {
+					case TYPE_INT: {
+						array_size = values[0].sint;
+					} break;
+					case TYPE_UINT: {
+						array_size = (int)values[0].uint;
+					} break;
+					default: {
+					} break;
 				}
 			}
-		} else if (((int)tk.constant) > 0) {
-			array_size = (uint32_t)tk.constant;
+
+			if (r_size_expression != nullptr) {
+				*r_size_expression = expr;
+			}
 		}
 
 		if (array_size <= 0) {
@@ -5064,7 +5447,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
 			idx++;
 		}
 		if (!auto_size && !undefined_size && an->initializer.size() != array_size) {
-			_set_error(RTR("Array size mismatch."));
+			_set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), array_size, an->initializer.size()));
 			return nullptr;
 		}
 	} else {
@@ -5185,7 +5568,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
 			}
 		}
 		if (an->initializer.size() != p_array_size) {
-			_set_error(RTR("Array size mismatch."));
+			_set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), p_array_size, an->initializer.size()));
 			return nullptr;
 		}
 	} else {
@@ -5230,7 +5613,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 
 		} else if (tk.type == TK_FLOAT_CONSTANT) {
 			ConstantNode *constant = alloc_node<ConstantNode>();
-			ConstantNode::Value v;
+			Scalar v;
 			v.real = tk.constant;
 			constant->values.push_back(v);
 			constant->datatype = TYPE_FLOAT;
@@ -5238,7 +5621,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 
 		} else if (tk.type == TK_INT_CONSTANT) {
 			ConstantNode *constant = alloc_node<ConstantNode>();
-			ConstantNode::Value v;
+			Scalar v;
 			v.sint = tk.constant;
 			constant->values.push_back(v);
 			constant->datatype = TYPE_INT;
@@ -5246,7 +5629,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 
 		} else if (tk.type == TK_UINT_CONSTANT) {
 			ConstantNode *constant = alloc_node<ConstantNode>();
-			ConstantNode::Value v;
+			Scalar v;
 			v.uint = tk.constant;
 			constant->values.push_back(v);
 			constant->datatype = TYPE_UINT;
@@ -5255,7 +5638,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 		} else if (tk.type == TK_TRUE) {
 			//handle true constant
 			ConstantNode *constant = alloc_node<ConstantNode>();
-			ConstantNode::Value v;
+			Scalar v;
 			v.boolean = true;
 			constant->values.push_back(v);
 			constant->datatype = TYPE_BOOL;
@@ -5264,7 +5647,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 		} else if (tk.type == TK_FALSE) {
 			//handle false constant
 			ConstantNode *constant = alloc_node<ConstantNode>();
-			ConstantNode::Value v;
+			Scalar v;
 			v.boolean = false;
 			constant->values.push_back(v);
 			constant->datatype = TYPE_BOOL;
@@ -6527,7 +6910,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, &op->return_array_size)) {
+				if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
 					_set_error(RTR("Invalid base type for increment/decrement operator."));
 					return nullptr;
 				}
@@ -6876,7 +7259,11 @@ 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, &op->return_array_size)) {
+				if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
+					if (error_set) {
+						return nullptr;
+					}
+
 					String at;
 					for (int j = 0; j < op->arguments.size(); j++) {
 						if (j > 0) {
@@ -6914,7 +7301,11 @@ 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, &op->return_array_size)) {
+			if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
+				if (error_set) {
+					return nullptr;
+				}
+
 				String at;
 				for (int i = 0; i < op->arguments.size(); i++) {
 					if (i > 0) {
@@ -6950,6 +7341,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 			}
 
 			if (_is_operator_assign(op->op)) {
+				if (p_block && expression[next_op - 1].node->type == Node::NODE_TYPE_VARIABLE) {
+					VariableNode *vn = static_cast<VariableNode *>(expression[next_op - 1].node);
+					p_block->use_op_eval = vn->is_const;
+				}
+
 				String assign_message;
 				if (!_validate_assign(expression[next_op - 1].node, p_function_info, &assign_message)) {
 					_set_error(assign_message);
@@ -6972,7 +7368,11 @@ 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, &op->return_array_size)) {
+			if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
+				if (error_set) {
+					return nullptr;
+				}
+
 				String at;
 				for (int i = 0; i < op->arguments.size(); i++) {
 					if (i > 0) {
@@ -6998,6 +7398,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 		}
 	}
 
+	if (p_block) {
+		p_block->use_op_eval = true;
+	}
+
 	if (p_previous_expression_info != nullptr) {
 		p_previous_expression_info->expression->push_back(expression[0]);
 	}
@@ -7020,7 +7424,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
 		DataType base = get_scalar_type(type);
 		int cardinality = get_cardinality(type);
 
-		Vector<ConstantNode::Value> values;
+		Vector<Scalar> values;
 
 		for (int i = 1; i < op->arguments.size(); i++) {
 			op->arguments.write[i] = _reduce_expression(p_block, op->arguments[i]);
@@ -7032,7 +7436,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
 						values.push_back(cn->values[j]);
 					}
 				} else if (get_scalar_type(cn->datatype) == cn->datatype) {
-					ConstantNode::Value v;
+					Scalar v;
 					if (!convert_constant(cn, base, &v)) {
 						return p_node;
 					}
@@ -7048,8 +7452,8 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
 
 		if (values.size() == 1) {
 			if (type >= TYPE_MAT2 && type <= TYPE_MAT4) {
-				ConstantNode::Value value = values[0];
-				ConstantNode::Value zero;
+				Scalar value = values[0];
+				Scalar zero;
 				zero.real = 0.0f;
 				int size = 2 + (type - TYPE_MAT2);
 
@@ -7060,7 +7464,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
 					}
 				}
 			} else {
-				ConstantNode::Value value = values[0];
+				Scalar value = values[0];
 				for (int i = 1; i < cardinality; i++) {
 					values.push_back(value);
 				}
@@ -7081,10 +7485,10 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
 
 			DataType base = get_scalar_type(cn->datatype);
 
-			Vector<ConstantNode::Value> values;
+			Vector<Scalar> values;
 
 			for (int i = 0; i < cn->values.size(); i++) {
-				ConstantNode::Value nv;
+				Scalar nv;
 				switch (base) {
 					case TYPE_BOOL: {
 						nv.boolean = !cn->values[i].boolean;
@@ -7515,7 +7919,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
 									decl.size = decl.initializer.size();
 									var.array_size = decl.initializer.size();
 								} else if (decl.initializer.size() != var.array_size) {
-									_set_error(RTR("Array size mismatch."));
+									_set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), var.array_size, decl.initializer.size()));
 									return ERR_PARSE_ERROR;
 								}
 								tk = _get_token();
@@ -7537,7 +7941,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
 
 					array_size = var.array_size;
 				} else if (tk.type == TK_OP_ASSIGN) {
-					//variable created with assignment! must parse an expression
+					p_block->use_op_eval = is_const;
+
+					// Variable created with assignment! Must parse an expression.
 					Node *n = _parse_and_reduce_expression(p_block, p_function_info);
 					if (!n) {
 						return ERR_PARSE_ERROR;
@@ -7552,11 +7958,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
 						}
 					}
 
-					if (n->type == Node::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 (is_const) {
+						var.values = n->get_values();
 					}
 
 					if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) {
@@ -7734,13 +8137,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
 									if (!vn) {
 										return ERR_PARSE_ERROR;
 									}
-									ConstantNode::Value v;
+									Vector<Scalar> v = { Scalar() };
 									_find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, nullptr, nullptr, nullptr, &v);
-									if (constants.has(v.sint)) {
-										_set_error(vformat(RTR("Duplicated case label: %d."), v.sint));
+									if (constants.has(v[0].sint)) {
+										_set_error(vformat(RTR("Duplicated case label: %d."), v[0].sint));
 										return ERR_PARSE_ERROR;
 									}
-									constants.insert(v.sint);
+									constants.insert(v[0].sint);
 								}
 							} else if (flow->flow_op == FLOW_OP_DEFAULT) {
 								continue;
@@ -7801,7 +8204,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
 				vn->name = tk.text;
 				n = vn;
 			} else {
-				ConstantNode::Value v;
+				Scalar v;
 				if (tk.type == TK_UINT_CONSTANT) {
 					v.uint = (uint32_t)tk.constant;
 				} else {
@@ -9678,7 +10081,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
 										decl.size = decl.initializer.size();
 										constant.array_size = decl.initializer.size();
 									} else if (decl.initializer.size() != constant.array_size) {
-										_set_error(RTR("Array size mismatch."));
+										_set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), constant.array_size, decl.initializer.size()));
 										return ERR_PARSE_ERROR;
 									}
 								} else {

+ 30 - 14
servers/rendering/shader_language.h

@@ -355,6 +355,13 @@ public:
 		}
 	};
 
+	union Scalar {
+		bool boolean = false;
+		float real;
+		int32_t sint;
+		uint32_t uint;
+	};
+
 	struct Node {
 		Node *next = nullptr;
 
@@ -379,6 +386,7 @@ public:
 		virtual String get_datatype_name() const { return ""; }
 		virtual int get_array_size() const { return 0; }
 		virtual bool is_indexed() const { return false; }
+		virtual Vector<Scalar> get_values() const { return Vector<Scalar>(); }
 
 		Node(Type t) :
 				type(t) {}
@@ -402,11 +410,13 @@ public:
 		Operator op = OP_EQUAL;
 		StringName struct_name;
 		Vector<Node *> arguments;
+		Vector<Scalar> values;
 
 		virtual DataType get_datatype() const override { return return_cache; }
 		virtual String get_datatype_name() const override { return String(struct_name); }
 		virtual int get_array_size() const override { return return_array_size; }
 		virtual bool is_indexed() const override { return op == OP_INDEX; }
+		virtual Vector<Scalar> get_values() const override { return values; }
 
 		OperatorNode() :
 				Node(NODE_TYPE_OPERATOR) {}
@@ -485,19 +495,15 @@ public:
 		String struct_name = "";
 		int array_size = 0;
 
-		union Value {
-			bool boolean = false;
-			float real;
-			int32_t sint;
-			uint32_t uint;
-		};
-
-		Vector<Value> values;
+		Vector<Scalar> values;
 		Vector<VariableDeclarationNode::Declaration> array_declarations;
 
 		virtual DataType get_datatype() const override { return datatype; }
 		virtual String get_datatype_name() const override { return struct_name; }
 		virtual int get_array_size() const override { return array_size; }
+		virtual Vector<Scalar> get_values() const override {
+			return values;
+		}
 
 		ConstantNode() :
 				Node(NODE_TYPE_CONSTANT) {}
@@ -529,13 +535,14 @@ public:
 			int line; //for completion
 			int array_size;
 			bool is_const;
-			ConstantNode::Value value;
+			Vector<Scalar> values;
 		};
 
 		HashMap<StringName, Variable> variables;
 		List<Node *> statements;
 		bool single_statement = false;
 		bool use_comma_between_statements = false;
+		bool use_op_eval = true;
 
 		BlockNode() :
 				Node(NODE_TYPE_BLOCK) {}
@@ -657,7 +664,7 @@ public:
 			DataType type = TYPE_VOID;
 			DataPrecision precision = PRECISION_DEFAULT;
 			int array_size = 0;
-			Vector<ConstantNode::Value> default_value;
+			Vector<Scalar> default_value;
 			Scope scope = SCOPE_LOCAL;
 			Hint hint = HINT_NONE;
 			bool use_color = false;
@@ -803,15 +810,16 @@ public:
 	static bool is_token_operator_assign(TokenType p_type);
 	static bool is_token_hint(TokenType p_type);
 
-	static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value = nullptr);
+	static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value = nullptr);
 	static DataType get_scalar_type(DataType p_type);
 	static int get_cardinality(DataType p_type);
 	static bool is_scalar_type(DataType p_type);
 	static bool is_float_type(DataType p_type);
 	static bool is_sampler_type(DataType p_type);
-	static Variant constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE);
+	static Variant constant_value_to_variant(const Vector<Scalar> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE);
 	static PropertyInfo uniform_to_property_info(const ShaderNode::Uniform &p_uniform);
 	static uint32_t get_datatype_size(DataType p_type);
+	static uint32_t get_datatype_component_count(DataType p_type);
 
 	static void get_keyword_list(List<String> *r_keywords);
 	static bool is_control_flow_keyword(String p_keyword);
@@ -1070,13 +1078,21 @@ private:
 
 	IdentifierType last_type = IDENTIFIER_MAX;
 
-	bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, ConstantNode::Value *r_constant_value = nullptr);
+	bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, Vector<Scalar> *r_constant_values = nullptr);
 #ifdef DEBUG_ENABLED
 	void _parse_used_identifier(const StringName &p_identifier, IdentifierType p_type, const StringName &p_function);
 #endif // DEBUG_ENABLED
 	bool _is_operator_assign(Operator p_op) const;
 	bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr);
-	bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr);
+	bool _validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr);
+
+	Vector<Scalar> _get_node_values(const BlockNode *p_block, Node *p_node);
+	bool _eval_operator(const BlockNode *p_block, OperatorNode *p_op);
+	Scalar _eval_unary_scalar(const Scalar &p_a, Operator p_op, DataType p_ret_type);
+	Scalar _eval_scalar(const Scalar &p_a, const Scalar &p_b, Operator p_op, DataType p_ret_type, bool &r_is_valid);
+	Vector<Scalar> _eval_unary_vector(const Vector<Scalar> &p_va, DataType p_ret_type, Operator p_op);
+	Vector<Scalar> _eval_vector(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type, Operator p_op, bool &r_is_valid);
+	Vector<Scalar> _eval_vector_transform(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type);
 
 	struct BuiltinEntry {
 		const char *name;