Browse Source

Merge pull request #62972 from Chaosus/shader_groups

Implement shader uniform groups/subgroups
Rémi Verschelde 3 years ago
parent
commit
2d2b85d400

+ 45 - 0
drivers/gles3/storage/material_storage.cpp

@@ -2900,7 +2900,22 @@ void CanvasShaderData::get_param_list(List<PropertyInfo> *p_param_list) const {
 		}
 	}
 
+	String last_group;
 	for (const KeyValue<int, StringName> &E : order) {
+		String group = uniforms[E.value].group;
+		if (!uniforms[E.value].subgroup.is_empty()) {
+			group += "::" + uniforms[E.value].subgroup;
+		}
+
+		if (group != last_group) {
+			PropertyInfo pi;
+			pi.usage = PROPERTY_USAGE_GROUP;
+			pi.name = group;
+			p_param_list->push_back(pi);
+
+			last_group = group;
+		}
+
 		PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]);
 		pi.name = E.value;
 		p_param_list->push_back(pi);
@@ -3123,7 +3138,22 @@ void SkyShaderData::get_param_list(List<PropertyInfo> *p_param_list) const {
 		}
 	}
 
+	String last_group;
 	for (const KeyValue<int, StringName> &E : order) {
+		String group = uniforms[E.value].group;
+		if (!uniforms[E.value].subgroup.is_empty()) {
+			group += "::" + uniforms[E.value].subgroup;
+		}
+
+		if (group != last_group) {
+			PropertyInfo pi;
+			pi.usage = PROPERTY_USAGE_GROUP;
+			pi.name = group;
+			p_param_list->push_back(pi);
+
+			last_group = group;
+		}
+
 		PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]);
 		pi.name = E.value;
 		p_param_list->push_back(pi);
@@ -3433,7 +3463,22 @@ void SceneShaderData::get_param_list(List<PropertyInfo> *p_param_list) const {
 		}
 	}
 
+	String last_group;
 	for (const KeyValue<int, StringName> &E : order) {
+		String group = uniforms[E.value].group;
+		if (!uniforms[E.value].subgroup.is_empty()) {
+			group += "::" + uniforms[E.value].subgroup;
+		}
+
+		if (group != last_group) {
+			PropertyInfo pi;
+			pi.usage = PROPERTY_USAGE_GROUP;
+			pi.name = group;
+			p_param_list->push_back(pi);
+
+			last_group = group;
+		}
+
 		PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]);
 		pi.name = E.value;
 		p_param_list->push_back(pi);

+ 92 - 1
scene/resources/material.cpp

@@ -202,7 +202,98 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const {
 
 void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
 	if (!shader.is_null()) {
-		shader->get_param_list(p_list);
+		List<PropertyInfo> list;
+		shader->get_param_list(&list, true);
+
+		HashMap<String, HashMap<String, List<PropertyInfo>>> groups;
+		{
+			HashMap<String, List<PropertyInfo>> none_subgroup;
+			none_subgroup.insert("<None>", List<PropertyInfo>());
+			groups.insert("<None>", none_subgroup);
+		}
+
+		String last_group = "<None>";
+		String last_subgroup = "<None>";
+
+		bool is_none_group_undefined = true;
+		bool is_none_group = true;
+
+		for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) {
+			if (E->get().usage == PROPERTY_USAGE_GROUP) {
+				if (!E->get().name.is_empty()) {
+					Vector<String> vgroup = E->get().name.split("::");
+					last_group = vgroup[0];
+					if (vgroup.size() > 1) {
+						last_subgroup = vgroup[1];
+					} else {
+						last_subgroup = "<None>";
+					}
+					is_none_group = false;
+
+					if (!groups.has(last_group)) {
+						PropertyInfo info;
+						info.usage = PROPERTY_USAGE_GROUP;
+						info.name = last_group;
+
+						List<PropertyInfo> none_subgroup;
+						none_subgroup.push_back(info);
+
+						HashMap<String, List<PropertyInfo>> subgroup_map;
+						subgroup_map.insert("<None>", none_subgroup);
+
+						groups.insert(last_group, subgroup_map);
+					}
+
+					if (!groups[last_group].has(last_subgroup)) {
+						PropertyInfo info;
+						info.usage = PROPERTY_USAGE_SUBGROUP;
+						info.name = last_subgroup;
+
+						List<PropertyInfo> subgroup;
+						subgroup.push_back(info);
+
+						groups[last_group].insert(last_subgroup, subgroup);
+					}
+				} else {
+					last_group = "<None>";
+					last_subgroup = "<None>";
+					is_none_group = true;
+				}
+				continue; // Pass group.
+			}
+
+			if (is_none_group_undefined && is_none_group) {
+				is_none_group_undefined = false;
+
+				PropertyInfo info;
+				info.usage = PROPERTY_USAGE_GROUP;
+				info.name = "Shader Param";
+				groups["<None>"]["<None>"].push_back(info);
+			}
+
+			PropertyInfo info = E->get();
+			info.name = info.name;
+			groups[last_group][last_subgroup].push_back(info);
+		}
+
+		// Sort groups alphabetically.
+		List<UniformProp> props;
+		for (HashMap<String, HashMap<String, List<PropertyInfo>>>::Iterator group = groups.begin(); group; ++group) {
+			for (HashMap<String, List<PropertyInfo>>::Iterator subgroup = group->value.begin(); subgroup; ++subgroup) {
+				for (List<PropertyInfo>::Element *item = subgroup->value.front(); item; item = item->next()) {
+					if (subgroup->key == "<None>") {
+						props.push_back({ group->key, item->get() });
+					} else {
+						props.push_back({ group->key + "::" + subgroup->key, item->get() });
+					}
+				}
+			}
+		}
+		props.sort_custom<UniformPropComparator>();
+
+		for (List<UniformProp>::Element *E = props.front(); E; E = E->next()) {
+			p_list->push_back(E->get().info);
+		}
 	}
 }
 

+ 11 - 0
scene/resources/material.h

@@ -84,6 +84,17 @@ class ShaderMaterial : public Material {
 
 	HashMap<StringName, Variant> param_cache;
 
+	struct UniformProp {
+		String str;
+		PropertyInfo info;
+	};
+
+	struct UniformPropComparator {
+		bool operator()(const UniformProp &p_a, const UniformProp &p_b) const {
+			return p_a.str.naturalnocasecmp_to(p_b.str) < 0;
+		}
+	};
+
 protected:
 	bool _set(const StringName &p_name, const Variant &p_value);
 	bool _get(const StringName &p_name, Variant &r_ret) const;

+ 9 - 5
scene/resources/shader.cpp

@@ -103,7 +103,7 @@ String Shader::get_code() const {
 	return code;
 }
 
-void Shader::get_param_list(List<PropertyInfo> *p_params) const {
+void Shader::get_param_list(List<PropertyInfo> *p_params, bool p_get_groups) const {
 	_update_shader();
 
 	List<PropertyInfo> local;
@@ -112,12 +112,16 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const {
 	params_cache_dirty = false;
 
 	for (PropertyInfo &pi : local) {
-		if (default_textures.has(pi.name)) { //do not show default textures
+		bool is_group = pi.usage == PROPERTY_USAGE_GROUP || pi.usage == PROPERTY_USAGE_SUBGROUP;
+		if (!p_get_groups && is_group) {
 			continue;
 		}
-		String original_name = pi.name;
-		pi.name = "shader_param/" + pi.name;
-		params_cache[pi.name] = original_name;
+		if (!is_group) {
+			if (default_textures.has(pi.name)) { //do not show default textures
+				continue;
+			}
+			params_cache[pi.name] = pi.name;
+		}
 		if (p_params) {
 			//small little hack
 			if (pi.type == Variant::RID) {

+ 1 - 1
scene/resources/shader.h

@@ -78,7 +78,7 @@ public:
 	void set_code(const String &p_code);
 	String get_code() const;
 
-	void get_param_list(List<PropertyInfo> *p_params) const;
+	void get_param_list(List<PropertyInfo> *p_params, bool p_get_groups = false) const;
 	bool has_param(const StringName &p_param) const;
 
 	void set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index = 0);

+ 15 - 0
servers/rendering/renderer_rd/environment/fog.cpp

@@ -393,7 +393,22 @@ void Fog::FogShaderData::get_param_list(List<PropertyInfo> *p_param_list) const
 		}
 	}
 
+	String last_group;
 	for (const KeyValue<int, StringName> &E : order) {
+		String group = uniforms[E.value].group;
+		if (!uniforms[E.value].subgroup.is_empty()) {
+			group += "::" + uniforms[E.value].subgroup;
+		}
+
+		if (group != last_group) {
+			PropertyInfo pi;
+			pi.usage = PROPERTY_USAGE_GROUP;
+			pi.name = group;
+			p_param_list->push_back(pi);
+
+			last_group = group;
+		}
+
 		PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]);
 		pi.name = E.value;
 		p_param_list->push_back(pi);

+ 15 - 1
servers/rendering/renderer_rd/environment/sky.cpp

@@ -178,8 +178,22 @@ void SkyRD::SkyShaderData::get_param_list(List<PropertyInfo> *p_param_list) cons
 			order[E.value.order] = E.key;
 		}
 	}
-
+	String last_group;
 	for (const KeyValue<int, StringName> &E : order) {
+		String group = uniforms[E.value].group;
+		if (!uniforms[E.value].subgroup.is_empty()) {
+			group += "::" + uniforms[E.value].subgroup;
+		}
+
+		if (group != last_group) {
+			PropertyInfo pi;
+			pi.usage = PROPERTY_USAGE_GROUP;
+			pi.name = group;
+			p_param_list->push_back(pi);
+
+			last_group = group;
+		}
+
 		PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]);
 		pi.name = E.value;
 		p_param_list->push_back(pi);

+ 15 - 0
servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp

@@ -407,7 +407,22 @@ void SceneShaderForwardClustered::ShaderData::get_param_list(List<PropertyInfo>
 		}
 	}
 
+	String last_group;
 	for (const KeyValue<int, StringName> &E : order) {
+		String group = uniforms[E.value].group;
+		if (!uniforms[E.value].subgroup.is_empty()) {
+			group += "::" + uniforms[E.value].subgroup;
+		}
+
+		if (group != last_group) {
+			PropertyInfo pi;
+			pi.usage = PROPERTY_USAGE_GROUP;
+			pi.name = group;
+			p_param_list->push_back(pi);
+
+			last_group = group;
+		}
+
 		PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]);
 		pi.name = E.value;
 		p_param_list->push_back(pi);

+ 15 - 0
servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp

@@ -364,7 +364,22 @@ void SceneShaderForwardMobile::ShaderData::get_param_list(List<PropertyInfo> *p_
 		}
 	}
 
+	String last_group;
 	for (const KeyValue<int, StringName> &E : order) {
+		String group = uniforms[E.value].group;
+		if (!uniforms[E.value].subgroup.is_empty()) {
+			group += "::" + uniforms[E.value].subgroup;
+		}
+
+		if (group != last_group) {
+			PropertyInfo pi;
+			pi.usage = PROPERTY_USAGE_GROUP;
+			pi.name = group;
+			p_param_list->push_back(pi);
+
+			last_group = group;
+		}
+
 		PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]);
 		pi.name = E.value;
 		p_param_list->push_back(pi);

+ 15 - 0
servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp

@@ -2185,7 +2185,22 @@ void RendererCanvasRenderRD::CanvasShaderData::get_param_list(List<PropertyInfo>
 		}
 	}
 
+	String last_group;
 	for (const KeyValue<int, StringName> &E : order) {
+		String group = uniforms[E.value].group;
+		if (!uniforms[E.value].subgroup.is_empty()) {
+			group += "::" + uniforms[E.value].subgroup;
+		}
+
+		if (group != last_group) {
+			PropertyInfo pi;
+			pi.usage = PROPERTY_USAGE_GROUP;
+			pi.name = group;
+			p_param_list->push_back(pi);
+
+			last_group = group;
+		}
+
 		PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]);
 		pi.name = E.value;
 		p_param_list->push_back(pi);

+ 15 - 0
servers/rendering/renderer_rd/storage_rd/particles_storage.cpp

@@ -1612,7 +1612,22 @@ void ParticlesStorage::ParticlesShaderData::get_param_list(List<PropertyInfo> *p
 		}
 	}
 
+	String last_group;
 	for (const KeyValue<int, StringName> &E : order) {
+		String group = uniforms[E.value].group;
+		if (!uniforms[E.value].subgroup.is_empty()) {
+			group += "::" + uniforms[E.value].subgroup;
+		}
+
+		if (group != last_group) {
+			PropertyInfo pi;
+			pi.usage = PROPERTY_USAGE_GROUP;
+			pi.name = group;
+			p_param_list->push_back(pi);
+
+			last_group = group;
+		}
+
 		PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]);
 		pi.name = E.value;
 		p_param_list->push_back(pi);

+ 44 - 0
servers/rendering/shader_language.cpp

@@ -310,6 +310,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
 	// global space keywords
 
 	{ TK_UNIFORM, "uniform", CF_GLOBAL_SPACE | CF_UNIFORM_KEYWORD, {}, {} },
+	{ TK_UNIFORM_GROUP, "group_uniforms", CF_GLOBAL_SPACE, {}, {} },
 	{ TK_VARYING, "varying", CF_GLOBAL_SPACE, { "particles", "sky", "fog" }, {} },
 	{ TK_CONST, "const", CF_BLOCK | CF_GLOBAL_SPACE | CF_CONST_KEYWORD, {}, {} },
 	{ TK_STRUCT, "struct", CF_GLOBAL_SPACE, {}, {} },
@@ -1146,6 +1147,8 @@ void ShaderLanguage::clear() {
 	current_function = StringName();
 	last_name = StringName();
 	last_type = IDENTIFIER_MAX;
+	current_uniform_group_name = "";
+	current_uniform_subgroup_name = "";
 
 	completion_type = COMPLETION_NONE;
 	completion_block = nullptr;
@@ -8298,6 +8301,8 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
 					uniform.scope = uniform_scope;
 					uniform.precision = precision;
 					uniform.array_size = array_size;
+					uniform.group = current_uniform_group_name;
+					uniform.subgroup = current_uniform_subgroup_name;
 
 					tk = _get_token();
 					if (tk.type == TK_BRACKET_OPEN) {
@@ -8724,6 +8729,45 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
 				}
 
 			} break;
+			case TK_UNIFORM_GROUP: {
+				tk = _get_token();
+				if (tk.type == TK_IDENTIFIER) {
+					current_uniform_group_name = tk.text;
+					tk = _get_token();
+					if (tk.type == TK_PERIOD) {
+						tk = _get_token();
+						if (tk.type == TK_IDENTIFIER) {
+							current_uniform_subgroup_name = tk.text;
+							tk = _get_token();
+							if (tk.type != TK_SEMICOLON) {
+								_set_expected_error(";");
+								return ERR_PARSE_ERROR;
+							}
+						} else {
+							_set_error(RTR("Expected an uniform subgroup identifier."));
+							return ERR_PARSE_ERROR;
+						}
+					} else if (tk.type != TK_SEMICOLON) {
+						_set_expected_error(";", ".");
+						return ERR_PARSE_ERROR;
+					}
+				} else {
+					if (tk.type != TK_SEMICOLON) {
+						if (current_uniform_group_name.is_empty()) {
+							_set_error(RTR("Expected an uniform group identifier."));
+						} else {
+							_set_error(RTR("Expected an uniform group identifier or `;`."));
+						}
+						return ERR_PARSE_ERROR;
+					} else if (tk.type == TK_SEMICOLON && current_uniform_group_name.is_empty()) {
+						_set_error(RTR("Group needs to be opened before."));
+						return ERR_PARSE_ERROR;
+					} else {
+						current_uniform_group_name = "";
+						current_uniform_subgroup_name = "";
+					}
+				}
+			} break;
 			case TK_SHADER_TYPE: {
 				_set_error(RTR("Shader type is already defined."));
 				return ERR_PARSE_ERROR;

+ 6 - 0
servers/rendering/shader_language.h

@@ -154,6 +154,7 @@ public:
 		TK_SEMICOLON,
 		TK_PERIOD,
 		TK_UNIFORM,
+		TK_UNIFORM_GROUP,
 		TK_INSTANCE,
 		TK_GLOBAL,
 		TK_VARYING,
@@ -687,6 +688,8 @@ public:
 			TextureRepeat repeat = REPEAT_DEFAULT;
 			float hint_range[3];
 			int instance_index = 0;
+			String group;
+			String subgroup;
 
 			Uniform() {
 				hint_range[0] = 0.0f;
@@ -938,6 +941,9 @@ private:
 	StringName last_name;
 	bool is_shader_inc = false;
 
+	String current_uniform_group_name;
+	String current_uniform_subgroup_name;
+
 	VaryingFunctionNames varying_function_names;
 
 	TkPos _get_tkpos() {