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
This commit is contained in:
Pedro J. Estébanez 2021-12-11 14:25:38 +01:00
parent 22166639ed
commit 0d1ec9a7cf
7 changed files with 38 additions and 11 deletions

View File

@ -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>

View File

@ -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;
// 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]);
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);

View File

@ -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());

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);