Search code examples
androidopengl-eslibgdxglsl

LibGDX shader working everywhere but my Android device


As a continuation from my previous question (GLSL : Accessing an array in a for-loop hinders performance), I have encountered an entirely new and annoying problem.

So, I have a shader that performs a black hole effect.

The shader works perfectly on my computer, the android emulator, and ShaderToy – but for some reason, even though the code is exactly the same, does not work on my Android device.

The problem occurs when I zoom in too far. For whatever reason, when my zoom reaches a certain point – the whole background zooms in and then zooms out and goes crazy. Like this :

What is happening

When it should look like this :

What should happen

However, it does work on my device if I change this :

#ifdef GL_ES
precision mediump float;
#endif

to this :

#ifdef GL_ES
precision highp float;
#endif

The problem with this is that it also decreases my FPS from 60 down to ~40.

I believe the problem is that my Android device's OpenGL version is "OpenGL ES 3.0" according to Gdx.gl.glGetString(GL20.GL_VERSION).

But I cannot figure out how to set my version to OpenGL 2.0 since the AndroidApplicationConfiguration class is giving me little to no options.

I've tried putting <uses-feature android:glEsVersion="0x00020000" android:required="true" /> in the manifest, but it still prints "OpenGL ES 3.0".

And I still don't even know if this is actually the cause of the problem or not, so that's why I'm asking here. Thank you for taking the time to read/answer my question :).

P.S. Here's the Shader code:

#ifdef GL_ES
precision mediump float;
#endif

const int MAX_HOLES = 4;

uniform sampler2D u_sampler2D;

varying vec2 vTexCoord0;

struct BlackHole {
    vec2 position;
    float radius;
    float deformRadius;
};

uniform vec2 screenSize;
uniform vec2 cameraPos;
uniform float cameraZoom;

uniform BlackHole blackHole[MAX_HOLES];

void main() {
    vec2 pos = vTexCoord0;

    float black = 0.0;
    for (int i = 0; i < MAX_HOLES; i++) {
        BlackHole hole = blackHole[i];
        vec2 position = (hole.position - cameraPos.xy) / cameraZoom + screenSize*0.5;
        float radius = hole.radius / cameraZoom;
        float deformRadius = hole.deformRadius / cameraZoom;

        vec2 deltaPos = vec2(position.x - gl_FragCoord.x, position.y - gl_FragCoord.y);
        float dist = length(deltaPos);
        float distToEdge = max(deformRadius - dist, 0.0);

        float dltR = max(sign(radius - dist), 0.0);
        black = min(black+dltR, 1.0);

        pos += (distToEdge * normalize(deltaPos) / screenSize);
    }

    gl_FragColor = (1.0 - black) * texture2D(u_sampler2D, pos) + black * vec4(0, 0, 0, 1);
}

Solution

  • As you have found the issue is down to a lack of precision in fp16 (mediump), which is fixed by using fp32 (highp). Most maths units will have double the throughput for fp16 vs fp32, which also explains the drop in performance.

    Querying the driver GLES version will return maximum supported version, not the version of the current EGL context, so what you are seeing is expected.

    Also please note that "highp" is optional in OpenGL ES 2.0 fragment shaders, so there is no guarantee that your shader will work on some GPUs in an OpenGL ES 2.0 context. The Mali-4xx series only support fp16 fragment shaders, for example (I think also some of the OpenGL ES 2.0 Vivante GPUs based on past experience).

    In OpenGL ES 3.0 highp is mandatory in fragment shaders, so it would be guaranteed to work there.