Fix Compatibility Rendering (GLES3) on old and low budget devices.
Fixes #86112 Fixes #86565
Tested devices:
- moto e5 play
(Adreno 3xx) - Huawei Mediapad T3 AGS-W09
(Adreno 3xx) - Redmi 4X
(Adreno 3xx) - Samsung Tab S7
(Adreno 650) - Nexus 7
(Adreno 320) - Samsung Tab A6 (2016) SM -T580
Mali-T830 - 18 devices on Firebase Test Lab (up to Android 9)
TODOs
Notes:
- I could only reproduce the issue
Unable to initialize engine native layer(comment) when OpenGL Debug is activated. Not all devices support the flags_EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR - @joined72 has found and fixed the issue in the file
drivers/gles3/shaders/scene.glslcomment. There are more shaders withpow(max(distance, 0.0001), -decay);.
- joined72 has found and fixed the issue in the file
drivers/gles3/shaders/scene.glslcomment. There are more shaders withpow(max(distance, 0.0001), -decay);.
All the shaders in your screenshot are in the RD renderer (meaning Vulkan or DirectX), so they won't have an effect on the GLES3 renderer.
Might be worth noting in a comment that this is to handle non-compliant devices so no one sees it and "fixes" the unnecessary divide
@Alex2782 about the "rotation bug" I have tested your gles3jni (https://github.com/Alex2782/ndk-samples) but its rotation is the same as on new devices, I tested on Honor Lite 9 and Redmi 12, same rotation. And about the old 3.0 rotation bug, I'm not able to reproduce it on my Old Andreno (TM) 308 Tablet (just works fine). So currently I need some help to can proceed with my investigation.
Until I'll receive some help about the possible "Wrong Rotation Bug" cause, I'll work on doing more 2D/3D tests.
Amazing work @Alex2782 and @joined72! I'm am extremely impressed with your dedication to figuring out these issues and finding workable solutions.
@joined72 Could you please test with 1.0?
return nd * (1.0 / pow(max(distance, 0.0001), decay));
On moto e5 play (Firebase Test Lab) freezes again. Maybe I compiled the APK incorrectly. I have switched back to 1 for now to debug orientation.
Shader debug/logging (I think there are no other possibilities, not for GLES3) https://www.shadertoy.com/view/csfXW7
@joined72: Can you also see this error?
01-19 16:08:06.236: E/godot(13166): USER ERROR: CanvasShaderGLES3: Fragment shader compilation failed:
01-19 16:08:06.236: E/godot(13166): Fragment shader compilation failed.
01-19 16:08:06.236: E/godot(13166): ERROR: 0:400: '' : Case label has to be a constant integer expression
01-19 16:08:06.236: E/godot(13166): ERROR: 0:403: '' : Case label has to be a constant integer expression
01-19 16:08:06.236: E/godot(13166): ERROR: 0:406: '' : Case label has to be a constant integer expression
01-19 16:08:06.236: E/godot(13166): ERROR: 3 compilation errors. No code generated.
@Alex2782 I'm currently at home so I can't do more tests here, but from my last investigations I found this...
return nd * (1.0 / pow(max(distance, 0.0001), decay));==> Crashed Shader / App Freezereturn nd / pow(max(distance, 0.0001), decay);==> Crashed Shader / App Freezereturn nd * (1 / pow(max(distance, 0.0001), decay));==> WORKS FINE!!!
It doesn't seem to be a problem directly linked to a negative exponent in the pow function, especially reading the error that the shader compilation gives you which seems to be linked to a "switch/case" code that is not present in our function. From a first impression it seems like a bug in the shader compiler of the Adreno (TM) 308 Driver, what do you think about?
PS: can you share the project used to generate the "USER ERROR: CanvasShaderGLES3: Fragment shader compilation failed:" error?
@joined72: ShaderTest.zip
After 3 seconds, a uniform shader is attached to some 2D nodes.
And a 3D scene is loaded after 6 seconds.
return nd * (1 / pow(max(distance, 0.0001), decay));==> WORKS FINE!!!
Unfortunately not, there is no 'freeze' because the shader code is invalid and glLinkProgram is not executed.
video, test 'moto e5 play', empty 3D scene
https://github.com/godotengine/godot/assets/41921395/3cdea592-9f60-4088-b0e5-ae7a8a058a2a
logcat, USER ERROR: SceneShaderGLES3 on Samsung Tab S7.
2024-01-21 02:57:32.166 25371-25419 godot com.godot.game E USER ERROR: SceneShaderGLES3: Fragment shader compilation failed:
ERROR: 0:804: '/' : wrong operand types no operation '/' exists that takes a left-hand operand of type 'const int' and a right operand of type 'float' (or there is no acceptable conversion)
ERROR: 0:804: '*' : wrong operand types no operation '*' exists that takes a left-hand operand of type 'float' and a right operand of type 'const int' (or there is no acceptable conversion)
ERROR: 2 compilation errors. No code generated.
2024-01-21 02:57:32.166 25371-25419 godot com.godot.game E at: _display_error_with_code (drivers/gles3/shader_gles3.cpp:252)
2024-01-21 02:57:32.166 25371-25419 godot com.godot.game E USER ERROR: Method/function failed.
2024-01-21 02:57:32.166 25371-25419 godot com.godot.game E at: _compile_specialization (drivers/gles3/shader_gles3.cpp:394)
The 3D scene is also not displayed as in the video. However, these logs are not output on 'moto e5 play', only USER ERROR: CanvasShaderGLES3
UPDATE: also found the error output USER ERROR: SceneShaderGLES3: Fragment shader compilation failed: on 'moto e5 play'.
Rotation Bug
void RasterizerGLES3::_blit_render_target_to_screen
I think if you set flip_y = false; then the image will be rotated correctly, but this is not a universal solution, only if you want to use it for your personal projects. 😃
UPDATE: I was also able to flip the 2D output using shaders.
/gles3/shaders/canvas.glsl
gl_Position = screen_transform * vec4(vertex, 0.0, 1.0);
gl_Position.y *= -1.0; //<--- new code
maybe canvas_transform or screen_transform is calculated incorrectly
@joined72: android_release-2024-01-22.apk (42 MB)
Can you see the 3D scene on your hardware? On 'moto e5 play' (Firebase Test Lab) the tests ended too early today. Maybe it is already too much, only 2 GB Ram and in parallel a video is recorded and a robo test is executed.
If you find time again...
/gles3/shaders/scene.glsl src diff
//TODO: Dev-Tests
//++++++++++++++++++++++++++++++++
DISABLE_LIGHTMAP = true
DISABLE_LIGHT_DIRECTIONAL = true
DISABLE_LIGHT_OMNI = true
DISABLE_LIGHT_SPOT = true
DISABLE_FOG = true
//--------------------------------
!!! and !!!
return nd * pow(max(distance, 0.0001), -decay);
If you find time again, set these flags to false. When exactly does it still work and when not? On my Samsung Tab I couldn't see any differences in the 3D Scene. On moto e5 play the 2D Scene was displayed for up to 5 seconds, so no 'freeze'.
@Alex2782 is this editor error relevant?
Cannot import custom .glsl shaders when using the gl_compatibility rendering_method. Please switch to the forward_plus or mobile rendering methods to use custom shaders. (User)
I think can be ignored, only works with Vulkan (button "Compute")
on moto e5 play
01-22 06:13:15.736: E/godot(20576): USER ERROR: CanvasShaderGLES3: Fragment shader compilation failed:
01-22 06:13:15.736: E/godot(20576): Fragment shader compilation failed.
01-22 06:13:15.736: E/godot(20576): ERROR: 0:405: '' : Case label has to be a constant integer expression
01-22 06:13:15.736: E/godot(20576): ERROR: 0:408: '' : Case label has to be a constant integer expression
01-22 06:13:15.736: E/godot(20576): ERROR: 0:411: '' : Case label has to be a constant integer expression
01-22 06:13:15.736: E/godot(20576): ERROR: 3 compilation errors. No code generated.
maybe here
01-22 06:13:15.460: I/godot(20576): 85: #define LIGHT_FLAGS_BLEND_MODE_ADD uint(0 << 16)
01-22 06:13:15.460: I/godot(20576): 86: #define LIGHT_FLAGS_BLEND_MODE_SUB uint(1 << 16)
01-22 06:13:15.460: I/godot(20576): 87: #define LIGHT_FLAGS_BLEND_MODE_MIX uint(2 << 16)
01-22 06:13:15.471: I/godot(20576): 403: switch (blend_mode) {
01-22 06:13:15.471: I/godot(20576): 404: case LIGHT_FLAGS_BLEND_MODE_ADD: {
01-22 06:13:15.471: I/godot(20576): 405: color.rgb += light_color.rgb * light_color.a;
01-22 06:13:15.471: I/godot(20576): 406: } break;
01-22 06:13:15.471: I/godot(20576): 407: case LIGHT_FLAGS_BLEND_MODE_SUB: {
01-22 06:13:15.471: I/godot(20576): 408: color.rgb -= light_color.rgb * light_color.a;
01-22 06:13:15.471: I/godot(20576): 409: } break;
01-22 06:13:15.471: I/godot(20576): 410: case LIGHT_FLAGS_BLEND_MODE_MIX: {
01-22 06:13:15.471: I/godot(20576): 411: color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
01-22 06:13:15.471: I/godot(20576): 412: } break;
01-22 06:13:15.471: I/godot(20576): 413: }
I have spent a lot of time trying to identify the real issue with the get_omni_spot_attenuation function return line.
I tested a lot of different situations and found differents solutions, but ALL required that the final calculation contain an INT (or best an UINT) value. If this value is used as FLOAT all STOP to works again.
To let you understand, these are some of the working solutions I found:
return nd * (1 / pow(max(distance, 0.0001), decay); // <== Just known solution!
nd *= 1; // <== This fix the issue!!!
return nd * pow(max(distance, 0.0001), -decay;
return float(uint(nd * pow(max(distance, 0.0001), -decay) * 10000.0) / 10000.0); // <= This solution WORKS!
As you can see, the latest solution converts the result into an unsigned integer and, after reconverts it to a float, loses precision after the 4th decimal digit. The latest solution let me think could be an internal Adreno (TM) 308 math processor-related bug.
I think I to continuing to use the first and more concise solution, only adding a detailed comment to prevent others to change it. What do you think about it?
PS: I'm continuing my tests...
maybe here
01-22 06:13:15.471: I/godot(20576): 403: switch (blend_mode) { 01-22 06:13:15.471: I/godot(20576): 404: case LIGHT_FLAGS_BLEND_MODE_ADD: { 01-22 06:13:15.471: I/godot(20576): 405: color.rgb += light_color.rgb * light_color.a; 01-22 06:13:15.471: I/godot(20576): 406: } break;
What is the .glsl file?
CanvasShaderGLES3 = /gles3/shaders/canvas.glsl
and #include "canvas_uniforms_inc.glsl" (I can find the #defines here)
return nd * (1 / pow(max(distance, 0.0001), decay); // <== Just known solution!
USER ERROR: SceneShaderGLES3: Fragment shader compilation failed:
https://github.com/godotengine/godot/pull/87352#issuecomment-1902487290. (check logcat + video hidden in the <details> tag)
#87352 (comment). (check logcat + video hidden in the
<details>tag)
I'll investigate on it.
I think to have find a viable workaround for the Adreno (TM) 308 "Rotation Bug", I just apply the following changes to the drivers/gles3/rasterizer_gles3.cpp file, function RasterizerGLES3::_blit_render_target_to_screen:
the following code...
glBlitFramebuffer(0, 0, rt->size.x, rt->size.y,
p_screen_rect.position.x, flip_y ? screen_rect_end.y : p_screen_rect.position.y, screen_rect_end.x, flip_y ? p_screen_rect.position.y : screen_rect_end.y,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
become...
bool flip_x = false;
bool landscape = screen_rect_end.x > screen_rect_end.y;
if (landscape && RenderingServer::get_singleton()->get_video_adapter_name() == "Adreno (TM) 308") {
flip_y = false;
flip_x = true;
}
glBlitFramebuffer(0, 0, rt->size.x, rt->size.y,
flip_x ? screen_rect_end.x : p_screen_rect.position.x, flip_y ? screen_rect_end.y : p_screen_rect.position.y,
flip_x ? p_screen_rect.position.x : screen_rect_end.x, flip_y ? p_screen_rect.position.y : screen_rect_end.y,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
On all my tests seems to works fine, try on moto e5 play and tell me your results.
@Alex2782
CanvasShaderGLES3=/gles3/shaders/canvas.glsland
#include "canvas_uniforms_inc.glsl"(I can find the#defines here)
return nd * (1 / pow(max(distance, 0.0001), decay); // <== Just known solution!
USER ERROR: SceneShaderGLES3: Fragment shader compilation failed:#87352 (comment). (check logcat + video hidden in the
<details>tag)
The unique working solution I found currently is set BASE_PASS = false in scene.glsl file... I'll work to improve it.
On all my tests seems to works fine, try on
moto e5 playand tell me your results.
get_video_adapter_name() == "Adreno (TM) 308"
I think that this code will find it very difficult to be included in the Master branch.
More devices may be affected: https://opengles.gpuinfo.org/
@clayjohn Are you familiar with all the limitations?
Example: vec4 are 4 UNIFORM_COMPONENTS? But as an array x 256 = 1024?
#define MAX_GLOBAL_SHADER_UNIFORMS 256
layout(std140) uniform GlobalShaderUniformData { //ubo:1
vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
};
https://opengles.gpuinfo.org/compare.php?reports=3901,4415
GL_MAX_FRAGMENT_UNIFORM_VECTORS: 224
GL_MAX_VERTEX_UNIFORM_VECTORS: 256
GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: 896
or more limits
DISABLE_LIGHTMAP, DISABLE_LIGHT_DIRECTIONAL, DISABLE_LIGHT_OMNI, DISABLE_LIGHT_SPOT, DISABLE_FOG
BASE_PASS
These flags control the uniform initialization, a 2D scene can be started as soon as less are initialized. Starting a 3D scene directly does not work ('Freeze'). Displaying a 2D scene first, then attaching a 3D scene and hiding the 2D scene does not work either. The test on Firebase is terminated immediately, maybe it is a crash.
@Alex2782 I have found a better solution, simply changing in the scene.glsl file...
mediump float cone_attenuation;
to
mediump float cone_attenuation = 0.0;
fix the issue (at least on 2D), now I'm working to set a default value for all the struct float fields.
mediump float cone_attenuation = 0.0;
You mean struct LightData? syntax error (also in C / C++ not possible)
struct LightData { // This structure needs to be as packed as possible.
...
mediump float cone_attenuation = 0.0;
...
};
If you change something in the scene.glsl, first test a 3D scene on modern hardware and check whether it still works.
If the scene shader is invalid, then there are no more problems on 'potato' devices, because fewer resources are required 😃
logcat (Samsung Tab S7)
2024-01-23 12:41:54.930 14264-14377 godot com.godot.game E USER ERROR: SceneShaderGLES3: Fragment shader compilation failed:
ERROR: 0:440: '=' : Syntax error: syntax error
INTERNAL ERROR: no main() function!
ERROR: 1 compilation errors. No code generated.
2024-01-23 12:41:54.930 14264-14377 godot com.godot.game E at: _display_error_with_code (drivers/gles3/shader_gles3.cpp:252)
2024-01-23 12:41:54.930 14264-14377 godot com.godot.game E USER ERROR: Method/function failed.
2024-01-23 12:41:54.930 14264-14377 godot com.godot.game E at: _compile_specialization (drivers/gles3/shader_gles3.cpp:398)
@Alex2782 changing:
spot_attenuation *= 1.0 - pow(spot_rim, spot_lights[idx].cone_attenuation);
to
spot_attenuation *= 1.0 - pow(spot_rim, abs(spot_lights[idx].cone_attenuation));
works fine also on 3D scenes, also if the 3D light are completely OFF.
I'm working on it, the cause seems to be canvas.glsl light_blend_compute function.
@Alex2782 I think to have fixed also the "CanvasShaderGLES3: Fragment shader compilation failed" bug simply changing the following canvas.glsl light_blend_compute function code;
from...
case LIGHT_FLAGS_BLEND_MODE_ADD: {
color.rgb += light_color.rgb * light_color.a;
} break;
case LIGHT_FLAGS_BLEND_MODE_SUB: {
color.rgb -= light_color.rgb * light_color.a;
} break;
case LIGHT_FLAGS_BLEND_MODE_MIX: {
color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
} break;
to...
case int(LIGHT_FLAGS_BLEND_MODE_ADD): {
color.rgb += light_color.rgb * light_color.a;
} break;
case int(LIGHT_FLAGS_BLEND_MODE_SUB): {
color.rgb -= light_color.rgb * light_color.a;
} break;
case int(LIGHT_FLAGS_BLEND_MODE_MIX): {
color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
} break;
I just tested it on a new device (Redmi 12) and on the old Adreno (TM) 308 Tablet, works on both also if light continue to be disabled on the Adreno 308 one, but without any logcat errors.
@clayjohn Are you familiar with all the limitations?
Example:
vec4are 4UNIFORM_COMPONENTS? But as an array x 256 = 1024?#define MAX_GLOBAL_SHADER_UNIFORMS 256 layout(std140) uniform GlobalShaderUniformData { //ubo:1 vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; };https://opengles.gpuinfo.org/compare.php?reports=3901,4415
GL_MAX_FRAGMENT_UNIFORM_VECTORS: 224 GL_MAX_VERTEX_UNIFORM_VECTORS: 256 GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: 896 or more limits
I had to look up these limits to fully understand them. I think you are on the right track. OpenGL implementations are only required to support arrays of up to 224 vec4s in the fragment shader.
In my opinion we should change the limit based on what is supported. See how it is defined for scene.glsl
https://github.com/godotengine/godot/blob/062e8802b7a5569f37d6a42e8a270c31b58ff6e6/drivers/gles3/rasterizer_scene_gles3.cpp#L3610-L3611
We should do something like we do for MAX_LIGHT_DATA_STRUCTS. In other words in config.cpp we can check GL_MAX_FRAGMENT_UNIFORM_VECTORS and GL_MAX_VERTEX_UNIFORM_VECTORS then create a new variable config->max_global_shader_array_len and use that to define MAX_GLOBAL_SHADER_UNIFORMS
We would also want to change how we calculate global_shader_uniforms.buffer_size in material.cpp since the maximum size is not actually the size of the uniform block, it is the max size of a single variable
https://github.com/godotengine/godot/blob/062e8802b7a5569f37d6a42e8a270c31b58ff6e6/drivers/gles3/storage/material_storage.cpp#L1111-L1115
case int(LIGHT_FLAGS_BLEND_MODE_ADD):
Yesterday I already switched to if / else if (diff), interesting to know that it seems to work that way too. I am now wondering what exactly the error means. Tomorrow I may have more time to check everything in more detail.
#define LIGHT_FLAGS_BLEND_MODE_ADD uint(0 << 16)
01-22 06:13:15.736: E/godot(20576): ERROR: 0:405: '' : Case label has to be a constant integer expression
case int(uint(0 << 16)): vs case uint(0 << 16): ?
@Alex2782 did you tested the spot_attenuation *= 1.0 - pow(spot_rim, abs(spot_lights[idx].cone_attenuation)); solution?
Here seems to works fine.
Yes, this also helps against the 'freeze' at app start. 👍 I have also highlighted this code to check it more closely.
On my Samsung Tab S7 I couldn't see any differences in my 3D Scene as long as the configuration is positive I think. Negative values can also be configured in the editor. On moto e5 play I have not yet been able to get a 3D Scene to run, I have reduced a lot of values.
I think the problem isn't related to limits but some other .glsl not full compliant issue. We have only to find it. ;)
Update: do you know if is possible in logcat show not only shader compilation errors but also warnings?
do you know if is possible in logcat show not only shader compilation errors but also warnings?
I don't know how, some errors / warnings are already issued by the driver. Under project settings there is also "Open GL Debug", but these flags do not work on old devices. (this change)
void ShaderGLES3::_compile_specialization
Compiler errors are output in this function (deactivate shader cache)