Sfoglia il codice sorgente

Merge pull request #36176 from Chaosus/shader_fix_const_crash

Fix shader crash if pass const argument to 'out/inout' parameter
Yuri Roubinsky 5 anni fa
parent
commit
ef51726ff3
2 ha cambiato i file con 90 aggiunte e 28 eliminazioni
  1. 87 28
      servers/visual/shader_language.cpp
  2. 3 0
      servers/visual/shader_language.h

+ 87 - 28
servers/visual/shader_language.cpp

@@ -903,6 +903,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<String
 		if (r_data_type) {
 			*r_data_type = p_builtin_types[p_identifier].type;
 		}
+		if (r_is_const) {
+			*r_is_const = p_builtin_types[p_identifier].constant;
+		}
 		if (r_type) {
 			*r_type = IDENTIFIER_BUILTIN_VAR;
 		}
@@ -3066,6 +3069,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 		Token tk = _get_token();
 		TkPos pos = _get_tkpos();
 
+		bool is_const = false;
+
 		if (tk.type == TK_PARENTHESIS_OPEN) {
 			//handle subexpression
 
@@ -3457,40 +3462,82 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 
 							for (int i = 0; i < call_function->arguments.size(); i++) {
 								int argidx = i + 1;
-								if (argidx < func->arguments.size() && is_sampler_type(call_function->arguments[i].type)) {
-									//let's see where our argument comes from
-									Node *n = func->arguments[argidx];
-									ERR_CONTINUE(n->type != Node::TYPE_VARIABLE); //bug? this should always be a variable
-									VariableNode *vn = static_cast<VariableNode *>(n);
-									StringName varname = vn->name;
-									if (shader->uniforms.has(varname)) {
-										//being sampler, this either comes from a uniform
-										ShaderNode::Uniform *u = &shader->uniforms[varname];
-										ERR_CONTINUE(u->type != call_function->arguments[i].type); //this should have been validated previously
-										//propagate
-										if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat)) {
-											return NULL;
+								if (argidx < func->arguments.size()) {
+									if (call_function->arguments[i].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_OUT || call_function->arguments[i].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT) {
+										bool error = false;
+										Node *n = func->arguments[argidx];
+										if (n->type == Node::TYPE_CONSTANT || n->type == Node::TYPE_OPERATOR) {
+											error = true;
+										} else if (n->type == Node::TYPE_ARRAY) {
+											ArrayNode *an = static_cast<ArrayNode *>(n);
+											if (an->call_expression != NULL) {
+												error = true;
+											}
+										} else if (n->type == Node::TYPE_VARIABLE) {
+											VariableNode *vn = static_cast<VariableNode *>(n);
+											if (vn->is_const) {
+												error = true;
+											} else {
+												StringName varname = vn->name;
+												if (shader->uniforms.has(varname)) {
+													error = true;
+												} else {
+													if (p_builtin_types.has(varname)) {
+														BuiltInInfo info = p_builtin_types[varname];
+														if (info.constant) {
+															error = true;
+														}
+													}
+												}
+											}
+										} else if (n->type == Node::TYPE_MEMBER) {
+											MemberNode *mn = static_cast<MemberNode *>(n);
+											if (mn->basetype_const) {
+												error = true;
+											}
 										}
-									} else if (p_builtin_types.has(varname)) {
-										//a built-in
-										if (!_propagate_function_call_sampler_builtin_reference(name, i, varname)) {
+										if (error) {
+											_set_error(vformat("Constant value cannot be passed for '%s' parameter!", _get_qualifier_str(call_function->arguments[i].qualifier)));
 											return NULL;
 										}
-									} else {
-										//or this comes from an argument, but nothing else can be a sampler
-										bool found = false;
-										for (int j = 0; j < base_function->arguments.size(); j++) {
-											if (base_function->arguments[j].name == varname) {
-												if (!base_function->arguments[j].tex_argument_connect.has(call_function->name)) {
-													base_function->arguments.write[j].tex_argument_connect[call_function->name] = Set<int>();
+									}
+									if (is_sampler_type(call_function->arguments[i].type)) {
+										//let's see where our argument comes from
+										Node *n = func->arguments[argidx];
+										ERR_CONTINUE(n->type != Node::TYPE_VARIABLE); //bug? this should always be a variable
+										VariableNode *vn = static_cast<VariableNode *>(n);
+										StringName varname = vn->name;
+										if (shader->uniforms.has(varname)) {
+											//being sampler, this either comes from a uniform
+											ShaderNode::Uniform *u = &shader->uniforms[varname];
+											ERR_CONTINUE(u->type != call_function->arguments[i].type); //this should have been validated previously
+											//propagate
+											if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat)) {
+												return NULL;
+											}
+										} else if (p_builtin_types.has(varname)) {
+											//a built-in
+											if (!_propagate_function_call_sampler_builtin_reference(name, i, varname)) {
+												return NULL;
+											}
+										} else {
+											//or this comes from an argument, but nothing else can be a sampler
+											bool found = false;
+											for (int j = 0; j < base_function->arguments.size(); j++) {
+												if (base_function->arguments[j].name == varname) {
+													if (!base_function->arguments[j].tex_argument_connect.has(call_function->name)) {
+														base_function->arguments.write[j].tex_argument_connect[call_function->name] = Set<int>();
+													}
+													base_function->arguments.write[j].tex_argument_connect[call_function->name].insert(i);
+													found = true;
+													break;
 												}
-												base_function->arguments.write[j].tex_argument_connect[call_function->name].insert(i);
-												found = true;
-												break;
 											}
+											ERR_CONTINUE(!found);
 										}
-										ERR_CONTINUE(!found);
 									}
+								} else {
+									break;
 								}
 							}
 						}
@@ -3504,7 +3551,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 
 				DataType data_type;
 				IdentifierType ident_type;
-				bool is_const = false;
 				int array_size = 0;
 				StringName struct_name;
 
@@ -3812,6 +3858,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 
 				MemberNode *mn = alloc_node<MemberNode>();
 				mn->basetype = dt;
+				mn->basetype_const = is_const;
 				mn->datatype = member_type;
 				mn->base_struct_name = st;
 				mn->struct_name = member_struct_name;
@@ -5359,6 +5406,18 @@ String ShaderLanguage::_get_shader_type_list(const Set<String> &p_shader_types)
 	return valid_types;
 }
 
+String ShaderLanguage::_get_qualifier_str(ArgumentQualifier p_qualifier) const {
+	switch (p_qualifier) {
+		case ArgumentQualifier::ARGUMENT_QUALIFIER_IN:
+			return "in";
+		case ArgumentQualifier::ARGUMENT_QUALIFIER_OUT:
+			return "out";
+		case ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT:
+			return "inout";
+	}
+	return "";
+}
+
 Error ShaderLanguage::_validate_datatype(DataType p_type) {
 	if (VisualServer::get_singleton()->is_low_end()) {
 		bool invalid_type = false;

+ 3 - 0
servers/visual/shader_language.h

@@ -529,6 +529,7 @@ public:
 
 	struct MemberNode : public Node {
 		DataType basetype;
+		bool basetype_const;
 		StringName base_struct_name;
 		DataPrecision precision;
 		DataType datatype;
@@ -544,6 +545,7 @@ public:
 		MemberNode() :
 				Node(TYPE_MEMBER),
 				basetype(TYPE_VOID),
+				basetype_const(false),
 				datatype(TYPE_VOID),
 				array_size(0),
 				owner(NULL),
@@ -866,6 +868,7 @@ private:
 	Node *_parse_and_reduce_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types);
 	Error _parse_block(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, bool p_just_one = false, bool p_can_break = false, bool p_can_continue = false);
 	String _get_shader_type_list(const Set<String> &p_shader_types) const;
+	String _get_qualifier_str(ArgumentQualifier p_qualifier) const;
 
 	Error _parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types);