godot
godot copied to clipboard
CPUParticles and MultiMesh are all drawn in the same transform on Android
Godot version
3.4, 3.5
System information
Andriod, Samsung galaxy s22 ultra
Issue description
CPUParticles (GLES 2) works fine in editor, on Linux / Windows desktop, and most Android phones, but on Samsung galaxy s22 ultra (and probably other phones from that CPU/GPU family), particles seem to be emitted all at the same time at the same position and rotation, so it always looks like one particle is used even if you set the number of particles to tens or hundreds.
Steps to reproduce
- make a scene with a simple CPUParticles instance using billboard mode
- use GLES2 renderer
- deploy to Samsung galaxy s22 ultra
Minimal reproduction project
Samsung galaxy s22 ultra
Does it have an Exynos (Europe) or Snapdragon (USA/China) SoC? There are two variants of the S22 series depending on the region you bought it in, with two different GPUs.
I think it's Exynos since it's bought in Europe, but I'll doublecheck with the owner (it's my friends' phone)
Here are the specs of the phone where the problem is detected

I've recently run into the issue described above (S22 series, GLES2) on Godot 3.5.1 and 3.5.2
It seems to be a problem with the S22 series from Samsung with Exynos CPU, most specifically: "SM-S901B", "SM-S901B/DS", "SM-S908B", "SM-S908B/DS", "SM-S906B", "SM-S906B/DS"
Please note that MultiMeshInstance3D and GridMap (which I think use MultiMeshInstance3D behind the scenes) also seem to be broken on this devices. More specifically only 1-2 instances are being rendered on screen.
Here's what the logs are showing:
D libEGL : dlopen (libGLESv2_samsung.so) success at 0x816debb3b52e92c3
V SVK : ------------------------------------------------------------------
V SVK : Samsung Vulkan version merge SHA1 = None
V SVK : Samsung Vulkan version revision number = e444125
V SVK : ------------------------------------------------------------------
V SVK : Entering: Create
V SVK : --------------------------------------------------------
V SVK : Application Name:
V SVK : Vulkan API 1.3 version
V SVK : --------------------------------------------------------
V SVK : Exiting: Create
E chromium: [ERROR:gpu_control_list.cc(136)] Cannot parse ANGLE GL_RENDERER: ANGLE (Samsung Xclipse 920) on Vulkan 1.1.179
D libEGL : ANGLE Info:Shader.cpp:443 (resolveCompile):
D libEGL : ERROR: 0:76: 'gl_FragDepth' : undeclared identifier
D libEGL : ERROR: 0:76: 'assign' : l-value required (can't modify a const)
I godot : 1 | #version 100
I godot : 2 | #define ANDROID_ENABLED
I godot : 3 |
I godot : 4 | #ifdef USE_GLES_OVER_GL
I godot : 5 | #define lowp
I godot : 6 | #define mediump
I godot : 7 | #define highp
I godot : 8 | #else
I godot : 9 | #if defined(USE_HIGHP_PRECISION)
I godot : 10 | precision highp float;
I godot : 11 | precision highp int;
I godot : 12 | #else
I godot : 13 | precision mediump float;
I godot : 14 | precision mediump int;
I godot : 15 | #endif
I godot : 16 | #endif
I godot : 17 |
I godot : 18 | uniform highp samplerCube source_cube; //texunit:0
I godot : 19 | /* clang-format on */
I godot : 20 | varying vec2 uv_interp;
I godot : 21 |
I godot : 22 | uniform bool z_flip;
I godot : 23 | uniform highp float z_far;
I godot : 24 | uniform highp float z_near;
I godot : 25 | uniform highp float bias;
I godot : 26 |
I godot : 27 | void main() {
I godot : 28 | highp vec3 normal = vec3(uv_interp * 2.0 - 1.0, 0.0);
I godot : 29 | /*
I godot : 30 | if (z_flip) {
I godot : 31 | normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
I godot : 32 | } else {
I godot : 33 | normal.z = -0.5 + 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
I godot : 34 | }
I godot : 35 | */
I godot : 36 |
I godot : 37 | //normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
I godot : 38 | //normal.xy *= 1.0 + normal.z;
I godot : 39 |
I godot : 40 | normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
I godot : 41 | normal = normalize(normal);
I godot : 42 | /*
I godot : 43 | normal.z = 0.5;
I godot : 44 | normal = normalize(normal);
I godot : 45 | */
I godot : 46 |
I godot : 47 | if (!z_flip) {
I godot : 48 | normal.z = -normal.z;
I godot : 49 | }
I godot : 50 |
I godot : 51 | //normal = normalize(vec3( uv_interp * 2.0 - 1.0, 1.0 ));
I godot : 52 | float depth = textureCube(source_cube, normal).r;
I godot : 53 |
I godot : 54 | // absolute values for direction cosines, bigger value equals closer to basis axis
I godot : 55 | vec3 unorm = abs(normal);
I godot : 56 |
I godot : 57 | if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
I godot : 58 | // x code
I godot : 59 | unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0);
I godot : 60 | } else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
I godot : 61 | // y code
I godot : 62 | unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0);
I godot : 63 | } else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
I godot : 64 | // z code
I godot : 65 | unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0);
I godot : 66 | } else {
I godot : 67 | // oh-no we messed up code
I godot : 68 | // has to be
I godot : 69 | unorm = vec3(1.0, 0.0, 0.0);
I godot : 70 | }
I godot : 71 |
I godot : 72 | float depth_fix = 1.0 / dot(normal, unorm);
I godot : 73 |
I godot : 74 | depth = 2.0 * depth - 1.0;
I godot : 75 | float linear_depth = 2.0 * z_near * z_far / (z_far + z_near - depth * (z_far - z_near));
I godot : 76 | gl_FragDepth = (linear_depth * depth_fix + bias) / z_far;
I godot : 77 | }
I godot : 78 |
E godot : ERROR: CubeToDpShaderGLES2: Fragment shader compilation failed:
E godot : ERROR: 0:76: 'gl_FragDepth' : undeclared identifier
E godot : ERROR: 0:76: 'assign' : l-value required (can't modify a const)
E godot :
E godot : at: _display_error_with_code (drivers/gles2/shader_gles2.cpp:126) - CubeToDpShaderGLES2: Fragment shader compilation failed:
E godot : ERROR: 0:76: 'gl_FragDepth' : undeclared identifier
E godot : ERROR: 0:76: 'assign' : l-value required (can't modify a const)
E godot :
E godot : ERROR: Method failed. Returning: nullptr
E godot : at: get_current_version (drivers/gles2/shader_gles2.cpp:372) - Method failed. Returning: nullptr
E godot : ERROR: Condition "!version" is true. Returned: false
E godot : at: bind (drivers/gles2/shader_gles2.cpp:87) - Condition "!version" is true. Returned: false
E godot : ERROR: Condition "!version" is true. Returned: -1
E godot : at: _get_uniform (drivers/gles2/shader_gles2.h:258) - Condition "!version" is true. Returned: -1
E godot : ERROR: Condition "!version" is true. Returned: -1
E godot : at: _get_uniform (drivers/gles2/shader_gles2.h:258) - Condition "!version" is true. Returned: -1
E godot : ERROR: Condition "!version" is true. Returned: -1
E godot : at: _get_uniform (drivers/gles2/shader_gles2.h:258) - Condition "!version" is true. Returned: -1
E godot : ERROR: Condition "!version" is true. Returned: -1
E godot : at: _get_uniform (drivers/gles2/shader_gles2.h:258) - Condition "!version" is true. Returned: -1
E godot : ERROR: Condition "!version" is true. Returned: -1
E godot : at: _get_uniform (drivers/gles2/shader_gles2.h:258) - Condition "!version" is true. Returned: -1
E godot : ERROR: Condition "!version" is true. Returned: -1
E godot : at: _get_uniform (drivers/gles2/shader_gles2.h:258) - Condition "!version" is true. Returned: -1
E godot : ERROR: Condition "!version" is true. Returned: -1
E godot : at: _get_uniform (drivers/gles2/shader_gles2.h:258) - Condition "!version" is true. Returned: -1
E godot : ERROR: Condition "!version" is true. Returned: -1
E godot : at: _get_uniform (drivers/gles2/shader_gles2.h:258) - Condition "!version" is true. Returned: -1
D libEGL : ANGLE Info:Debug.cpp:183 (insertMessage): GL performance: LOW: Clear effectively discarding previous draw call results. Suggest earlier Clear followed by masked color or depth/stencil draw calls instead
A quick google suggests gl_FragDepth
is not supported on GLES2 (at least in the base spec, it is with EXT_frag_depth
, which may be widely supported hence the problem occurring rarely).
Offending line appears to be: https://github.com/godotengine/godot/blob/b0c399ec8c9f5f71c64656e8463907153f8459ff/drivers/gles2/shaders/cube_to_dp.glsl#L99
And that has been there for at least 5 years.
The extension is tested for: https://github.com/godotengine/godot/blob/b0c399ec8c9f5f71c64656e8463907153f8459ff/drivers/gles2/rasterizer_storage_gles2.cpp#L6320
But presumably is not stopping this shader being compiled.
Might need @clayjohn to take a look, I'm not so familiar with this part of the scene shaders.
I'm facing the same CPUParticles2D bug in Godot 3.5.1 with GLES2. Users are only able to see a single particle. It's surprising this hasn't received more attention, given the importance of particle systems in game development. Are there any workarounds or fixes available for this issue?
Using GLES3 is problematic on mobile, so I'm stuck with GLES2 and CPUParticles2D.
Edit: I'm using C# for mobile development, which is why I'm using Godot 3.x
~~That gl_fragDepth
part is required for the cubemap-to-dual parabolid shader to work, which is used for radiance map rendering in 3D. If we disabled it on problematic hardware, it would break ambient and reflected light rendering in 3D. (You can try commenting out that line and recompiling, even on a desktop PC.)~~
Most engine contributors don't have access to Exynos devices (let alone specific models as this seems to be specific to the S22 series so far), so this may take a while to be resolved. The issue also hasn't been reproduced on 4.x with the Compatibility rendering method so far.
But presumably is not stopping this shader being compiled.
Might need @clayjohn to take a look, I'm not so familiar with this part of the scene shaders.
Looks like we need to enable the extension in the shader as well #extension GL_EXT_frag_depth : enable
and add a define #define gl_FragDepthEXT gl_FragDepth
Almost no mobile devices support the GL_EXT_frag_depth
extension. Its likely that this has always been broken and we just haven't been running into reports of this not working. And naturally we don't have test devices to confirm the fix on. Looking at the reports on GPUInfo.org, almost all devices that advertise support for it are desktop devices running GLES2, or specialty GPUs like those on the NVidia Shield tablet. Accordingly, it would be just as safe to not detect the extension at all and force all mobile devices to use Dual Paraboloid shadows.
That gl_fragDepth part is required for the cubemap-to-dual parabolid shader to work, which is used for radiance map rendering in 3D. If we disabled it on problematic hardware, it would break ambient and reflected light rendering in 3D. (You can try commenting out that line and recompiling, even on a desktop PC.)
Its only used for omnilight shadows when set to the "Cube" shadow mode.
That being said, I think the CubeToDual shader is not the cause of this problem. I think it is a separate problem that just happens to be reproducible on the same device. There is no reason the omnilight shadow shader would cause CPUParticles and MultiMesh to render incorrectly.
plz help how I upgraide s22 Ultra GPU?