Browse Source

Improve ubershader compatibility

- Prevent debug enforced use of ubershader on shaders not supporting it
- Use unsigned integer for ubershader flags
- Add project setting for disabling async shader compilation on mobile
- Stop sampling some textures through different kinds of samplers at the same time
Pedro J. Estébanez 3 years ago
parent
commit
0d1ec9a7cf

+ 4 - 0
doc/classes/ProjectSettings.xml

@@ -1294,6 +1294,10 @@
 			To reduce loading times after the project has been launched at least once, you can use [code]Asynchronous + Cache[/code]. This also causes the ubershaders to be cached into storage so they can be ready faster next time they are used (provided the platform provides support for it).
 			[b]Note:[/b] Asynchronous compilation is currently only supported for spatial (3D) and particle materials/shaders. CanvasItem (2D) shaders will not use asynchronous compilation even if this setting is set to [code]Asynchronous[/code] or [code]Asynchronous + Cache[/code].
 		</member>
+		<member name="rendering/gles3/shaders/shader_compilation_mode.mobile" type="int" setter="" getter="" default="0">
+			An override for [code]rendering/gles3/shaders/shader_compilation_mode[/code], so asynchronous compilation can be disabled for mobile.
+			You may want to do that since mobile GPUs generally won't support ubershaders due to their complexity.
+		</member>
 		<member name="rendering/limits/buffers/blend_shape_max_buffer_size_kb" type="int" setter="" getter="" default="4096">
 			Max buffer size for blend shapes. Any blend shape bigger than this will not work.
 		</member>

+ 13 - 2
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -1819,7 +1819,14 @@ void RasterizerSceneGLES3::_setup_light(RenderList::Element *e, const Transform
 		GIProbeInstance *gipi = gi_probe_instance_owner.getptr(ridp[0]);
 
 		float bias_scale = e->instance->baked_light ? 1 : 0;
-		glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 10);
+		// Normally, lightmapping uses the same texturing units than the GI probes; however, in the case of the ubershader
+		// that's not a good idea because some hardware/drivers (Android/Intel) may fail to render if a single texturing unit
+		// is used through multiple kinds of samplers in the same shader.
+		if (state.scene_shader.is_version_ubershader()) {
+			glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 12);
+		} else {
+			glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 10);
+		}
 		glBindTexture(GL_TEXTURE_3D, gipi->tex_cache);
 		state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_XFORM1, gipi->transform_to_data * p_view_transform);
 		state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_BOUNDS1, gipi->bounds);
@@ -1831,7 +1838,11 @@ void RasterizerSceneGLES3::_setup_light(RenderList::Element *e, const Transform
 		if (gi_probe_count > 1) {
 			GIProbeInstance *gipi2 = gi_probe_instance_owner.getptr(ridp[1]);
 
-			glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 11);
+			if (state.scene_shader.is_version_ubershader()) {
+				glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 13);
+			} else {
+				glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 11);
+			}
 			glBindTexture(GL_TEXTURE_3D, gipi2->tex_cache);
 			state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_XFORM2, gipi2->transform_to_data * p_view_transform);
 			state.scene_shader.set_uniform(SceneShaderGLES3::GI_PROBE_BOUNDS2, gipi2->bounds);

+ 5 - 5
drivers/gles3/shader_gles3.cpp

@@ -123,7 +123,7 @@ bool ShaderGLES3::_bind(bool p_binding_fallback) {
 
 #ifdef DEBUG_ENABLED
 	if (ready) {
-		if (VS::get_singleton()->is_force_shader_fallbacks_enabled() && !must_be_ready_now) {
+		if (VS::get_singleton()->is_force_shader_fallbacks_enabled() && !must_be_ready_now && get_ubershader_flags_uniform() != -1) {
 			ready = false;
 		}
 	}
@@ -160,7 +160,7 @@ bool ShaderGLES3::_bind_ubershader() {
 	ERR_FAIL_COND_V(conditionals_uniform == -1, false);
 #endif
 	new_conditional_version.version &= ~VersionKey::UBERSHADER_FLAG;
-	glUniform1i(conditionals_uniform, new_conditional_version.version);
+	glUniform1ui(conditionals_uniform, new_conditional_version.version);
 	return bound;
 }
 
@@ -454,11 +454,11 @@ static CharString _prepare_ubershader_chunk(const CharString &p_chunk) {
 			} else if (l.begins_with("#ifdef")) {
 				Vector<String> pieces = l.split_spaces();
 				CRASH_COND(pieces.size() != 2);
-				s += "if ((ubershader_flags & FLAG_" + pieces[1] + ") != 0) {\n";
+				s += "if ((ubershader_flags & FLAG_" + pieces[1] + ") != 0u) {\n";
 			} else if (l.begins_with("#ifndef")) {
 				Vector<String> pieces = l.split_spaces();
 				CRASH_COND(pieces.size() != 2);
-				s += "if ((ubershader_flags & FLAG_" + pieces[1] + ") == 0) {\n";
+				s += "if ((ubershader_flags & FLAG_" + pieces[1] + ") == 0u) {\n";
 			} else {
 				CRASH_NOW_MSG("The shader template is using too complex syntax in a line marked with ubershader-runtime.");
 			}
@@ -532,7 +532,7 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version(bool &r_async_forbidden)
 	if (build_ubershader) {
 		strings_common.push_back("#define IS_UBERSHADER\n");
 		for (int i = 0; i < conditional_count; i++) {
-			String s = vformat("#define FLAG_%s (1 << %d)\n", String(conditional_defines[i]).strip_edges().trim_prefix("#define "), i);
+			String s = vformat("#define FLAG_%s (1u << %du)\n", String(conditional_defines[i]).strip_edges().trim_prefix("#define "), i);
 			CharString cs = s.ascii();
 			flag_macros.push_back(cs);
 			strings_common.push_back(cs.ptr());

+ 1 - 0
drivers/gles3/shader_gles3.h

@@ -399,6 +399,7 @@ public:
 	void free_custom_shader(uint32_t p_code_id);
 
 	uint32_t get_version() const { return new_conditional_version.version; }
+	bool is_version_ubershader() const { return (new_conditional_version.version & VersionKey::UBERSHADER_FLAG); }
 	_FORCE_INLINE_ bool is_version_valid() const { return version && version->compile_status == Version::COMPILE_STATUS_OK; }
 
 	virtual void init() = 0;

+ 2 - 2
drivers/gles3/shaders/particles.glsl

@@ -2,7 +2,7 @@
 [vertex]
 
 #if defined(IS_UBERSHADER)
-uniform highp int ubershader_flags;
+uniform highp uint ubershader_flags;
 #endif
 
 layout(location = 0) in highp vec4 color;
@@ -222,7 +222,7 @@ VERTEX_SHADER_CODE
 [fragment]
 
 #if defined(IS_UBERSHADER)
-uniform highp int ubershader_flags;
+uniform highp uint ubershader_flags;
 #endif
 
 // any code here is never executed, stuff is filled just so it works

+ 12 - 2
drivers/gles3/shaders/scene.glsl

@@ -2,7 +2,7 @@
 [vertex]
 
 #if defined(IS_UBERSHADER)
-uniform highp int ubershader_flags;
+uniform highp uint ubershader_flags;
 #endif
 
 #define M_PI 3.14159265359
@@ -645,7 +645,7 @@ VERTEX_SHADER_CODE
 [fragment]
 
 #if defined(IS_UBERSHADER)
-uniform highp int ubershader_flags;
+uniform highp uint ubershader_flags;
 // These are more performant and make the ubershaderification simpler
 #define VCT_QUALITY_HIGH
 #define USE_LIGHTMAP_FILTER_BICUBIC
@@ -1649,7 +1649,12 @@ uniform mediump vec4[12] lightmap_captures;
 
 #ifdef USE_GI_PROBES //ubershader-skip
 
+#if !defined(IS_UBERSHADER)
 uniform mediump sampler3D gi_probe1; //texunit:-10
+#else
+uniform mediump sampler3D gi_probe1_uber; //texunit:-12
+#define gi_probe1 gi_probe1_uber
+#endif
 uniform highp mat4 gi_probe_xform1;
 uniform highp vec3 gi_probe_bounds1;
 uniform highp vec3 gi_probe_cell_size1;
@@ -1658,7 +1663,12 @@ uniform highp float gi_probe_bias1;
 uniform highp float gi_probe_normal_bias1;
 uniform bool gi_probe_blend_ambient1;
 
+#if !defined(IS_UBERSHADER)
 uniform mediump sampler3D gi_probe2; //texunit:-11
+#else
+uniform mediump sampler3D gi_probe2_uber; //texunit:-13
+#define gi_probe2 gi_probe2_uber
+#endif
 uniform highp mat4 gi_probe_xform2;
 uniform highp vec3 gi_probe_bounds2;
 uniform highp vec3 gi_probe_cell_size2;

+ 1 - 0
servers/visual_server.cpp

@@ -2727,6 +2727,7 @@ VisualServer::VisualServer() {
 #endif
 	GLOBAL_DEF("rendering/gles3/shaders/shader_compilation_mode", 0);
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/gles3/shaders/shader_compilation_mode", PropertyInfo(Variant::INT, "rendering/gles3/shaders/shader_compilation_mode", PROPERTY_HINT_ENUM, "Synchronous,Asynchronous,Asynchronous + Cache"));
+	GLOBAL_DEF("rendering/gles3/shaders/shader_compilation_mode.mobile", 0);
 	GLOBAL_DEF("rendering/gles3/shaders/max_simultaneous_compiles", 2);
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/gles3/shaders/max_simultaneous_compiles", PropertyInfo(Variant::INT, "rendering/gles3/shaders/max_simultaneous_compiles", PROPERTY_HINT_RANGE, "1,8,1"));
 	GLOBAL_DEF("rendering/gles3/shaders/max_simultaneous_compiles.mobile", 1);