godot icon indicating copy to clipboard operation
godot copied to clipboard

Fix Compatibility Rendering (GLES3) on old and low budget devices.

Open Alex2782 opened this issue 1 year ago • 112 comments

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

  • [x] Rotation Bug comment
  • [x] fix lost old callback when continuous call requestRenderAndNotify src diff
  • [x] glLinkProgram shader freeze bug
  • [x] CanvasShaderGLES3: Fragment shader compilation failed
  • [ ] VK-GL-CTS
  • [ ] more tests (2d, 3d)

Alex2782 avatar Jan 18 '24 23:01 Alex2782

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.glsl comment. There are more shaders with pow(max(distance, 0.0001), -decay);.

image

Alex2782 avatar Jan 18 '24 23:01 Alex2782

  • joined72 has found and fixed the issue in the file drivers/gles3/shaders/scene.glsl comment. There are more shaders with pow(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.

dsnopek avatar Jan 19 '24 02:01 dsnopek

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

AThousandShips avatar Jan 19 '24 10:01 AThousandShips

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

joined72 avatar Jan 19 '24 17:01 joined72

Amazing work @Alex2782 and @joined72! I'm am extremely impressed with your dedication to figuring out these issues and finding workable solutions.

clayjohn avatar Jan 19 '24 17:01 clayjohn

@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

Alex2782 avatar Jan 20 '24 00:01 Alex2782

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

Bildschirmfoto 2024-01-20 um 02 01 11

Alex2782 avatar Jan 20 '24 01:01 Alex2782

@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 Freeze
  • return nd / pow(max(distance, 0.0001), decay); ==> Crashed Shader / App Freeze
  • return 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 avatar Jan 20 '24 11:01 joined72

@joined72: ShaderTest.zip After 3 seconds, a uniform shader is attached to some 2D nodes.

And a 3D scene is loaded after 6 seconds. image


  • 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

Alex2782 avatar Jan 21 '24 03:01 Alex2782

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

image

Alex2782 avatar Jan 22 '24 00:01 Alex2782

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

joined72 avatar Jan 22 '24 10:01 joined72

I think can be ignored, only works with Vulkan (button "Compute")

Alex2782 avatar Jan 22 '24 12:01 Alex2782

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: 	}

Alex2782 avatar Jan 22 '24 14:01 Alex2782

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

joined72 avatar Jan 22 '24 15:01 joined72

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?

joined72 avatar Jan 22 '24 15:01 joined72

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)

Alex2782 avatar Jan 22 '24 16:01 Alex2782

#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.

joined72 avatar Jan 22 '24 17:01 joined72

@Alex2782

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:

#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.

joined72 avatar Jan 22 '24 17:01 joined72

On all my tests seems to works fine, try on moto e5 play and 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/

Alex2782 avatar Jan 22 '24 18:01 Alex2782

@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 avatar Jan 22 '24 19:01 Alex2782

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

joined72 avatar Jan 23 '24 11:01 joined72

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 avatar Jan 23 '24 11:01 Alex2782

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

joined72 avatar Jan 23 '24 15:01 joined72

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

joined72 avatar Jan 23 '24 16:01 joined72

@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

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

clayjohn avatar Jan 23 '24 21:01 clayjohn

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 avatar Jan 23 '24 23:01 Alex2782

@Alex2782 did you tested the spot_attenuation *= 1.0 - pow(spot_rim, abs(spot_lights[idx].cone_attenuation)); solution?

Here seems to works fine.

joined72 avatar Jan 24 '24 15:01 joined72

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. image

Alex2782 avatar Jan 24 '24 21:01 Alex2782

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?

joined72 avatar Jan 24 '24 21:01 joined72

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)

Alex2782 avatar Jan 24 '24 22:01 Alex2782