OpenGL GLSL ES 3.10 - Referencing a uniform variable, causes the vertex shader to not draw anything

Hello everyone,

The past few months I’ve started experimenting and learning more about OpenGL and GLSL and how to compile it for Android. I’ve had success for the most part, except from this issue.

I have this project, that has a default shader, that just draws models and textures. Recently I decided to add a second shader that does a fancy effect, and is used only on some of the objects drawn.

After compiling the project for Linux or Windows, it all works as expected. When compiling the project to Android, only on specific devices, the new shader doesn’t work, and draws nothing, while on other devices I tried, it draws the objects with the new effect as expected.

My shaders

Below is my default vertex shader specifically made for Android devices, this one works on all devices and draws everything without any editing or effect. As far as I understand, the fragment shaders work, so I’ll omit them.

    #version 310 es

    in vec4 position;
    in vec3 colour;
    in vec2 texCoord;

    uniform mat4 matrix;
    uniform mat4 matrixProjection;

    out vec2 outTexCoord;
    out vec4 outColour;

    void main() {
            gl_Position = matrixProjection *matrix *position;
            outTexCoord  = texCoord;
            outColour  = vec4(colour.rgb, 1);
    }

I hope this looks fairly straight-forward. matrixProjection is the projection matrix, and matrix is the model-view matrix. They both work as expected and I’m able to render a whole scene without issue.

Now here is a simplified version of my new shader:

    #version 310 es

    in vec4 position;
    in vec3 colour;
    in vec2 texCoord;

    uniform mat4 matrix;
    uniform mat4 matrixProjection;
    uniform float animationCurrent;

    out vec2 outTexCoord;
    out vec4 outColour;

    void main() {
            gl_Position = matrixProjection *matrix *position;

            if (animationCurrent > 0.0) {
                    gl_Position.y += 5.0;
            }

            outColour = vec4(colour.rgb, 1.0);
            outTexCoord  = texCoord;
    }

The only difference of the new shader is the new uniform animationCurrent, and an extra if statement that will modify the gl_Position.y of some vertices. Any object that is using this shader, is not drawn at all on the screen on some devices, but is drawn correctly on other devices.

What I’ve tried

From the new shader, if I remove the entire if statement, it all works and it displays objects as-is. If I replace the if statement with if (true) it still works, but it just displays all vertices of objects drawn with it slightly higher. If I replace it with if (false) it still works as expected, and draws the objects.

So for some reason, just referencing animationCurrent causes the object to not be drawn.

I also tried replacing the if statement with if (matrix[0][0] > 0.0) and it still draws the object, it looks like there’s something specifically wrong with animationCurrent. I tried adding another matrix uniform variable, and set its value the same way as I do matrix, but it wouldn’t draw the object either.

This should mean that the value of animationCurrent is not relevant, and the fact that it’s a uniform float doesn’t matter either.

Hardware

The problem occurs on an android phone with this hardware:

Device: Moto E (4) Plus - 7.1.1
Vendor graphic card: ARM
Renderer: Mali-T720
Version OpenGL: OpenGL ES 3.1 v1.r12p1-01alp0.62f282720426ab7712f1c6b996a6dc82
Version GLSL: OpenGL ES GLSL ES 3.10

And this android tablet with similar hardware:

Device: Kindle Fire 8
Vendor graphic card: ARM
Renderer: Mali-T720
Version GL: OpenGL ES 3.1 v1.r26p0-01rel0.526d936ea9da20486773a9aaceecd920
Version GLSL: OpenGL ES GLSL ES 3.10

This is an android tablet where everything works as expected:

Device: Lenovo TB-X505F - 10
Vendor graphic card: Qualcomm
Renderer: Adreno (TM) 504
Version GL: OpenGL ES 3.2 V@415.0 (GIT@f345350, I0760943699, 1580221225) (Date:01/28/20)
Version GLSL: OpenGL ES GLSL ES 3.20

And here is a slightly older device that works. I’ve modified the shader a bit to support an older glsl version, but the idea is the same:

Device: Kindle Fire 7
Vendor graphic card: ARM
Renderer: Mali-450 MP
Version GL: OpenGL ES 2.0
Version GLSL: OpenGL ES GLSL ES 1.00

Question

My primary goal, is to understand what is causing this. Have I missed something very obvious? Is this a very edge-case bug related to the hardware?

I’m still learning how to support different devices with different versions of glsl, so it’s very likely I’ve missed something.

Any information you have, let me know. I’m willing to try a few things on different devices to find more about this issue.

1 Like

First, try using

            vec4 pos = matrixProjection *matrix *position;

            if (animationCurrent > 0.0) {
                    pos.y += 5.0;
            }
            gl_Position = pos;

It’s possible that the compiler treats gl_Position specially in a way that doesn’t work with compound assignment and/or non-uniform control flow.

Also: are you examining the compilation and linking logs (glGetShaderInfoLog and glGetProgramInfoLog respectively)? Even if no error is indicated, there may be relevant warnings in the log.

Hi GClements,

It’s possible that the compiler treats gl_Position specially in a way that doesn’t work with compound assignment and/or non-uniform control flow.

This is very good advice which I’ll keep in mind for future reference. I tried it on this case, and unfortunately the behavior remained the same. I wouldn’t see anything being drawn, until I commented out the if statement.

I also tried with your example, to change the if statement to if (true) {...}, and that would draw the objects slightly elevated, as expected (because of the pos.y += 5.0;), but with if (animationCurrent > 0.0) {...} it doesn’t draw anything.

Also: are you examining the compilation and linking logs (glGetShaderInfoLog and glGetProgramInfoLog respectively)? Even if no error is indicated, there may be relevant warnings in the log.

I was only checking that if the compilation of the shaders failed. I checked the results of both glGetShaderInfoLog and glGetProgramInfoLog after compiling both the shaders and the program itself, but it would return an empty log.

I really appreciate the help as I’ve been stuck on this for a while. If you think providing any extra details will help, I’m happy to post it here, including how shaders are initialized, and how uniform variables are set.

The setting of uniform variables might be useful, but there isn’t really much you could do that would conceivably result in nothing drawing. If the result with that condition differs from both if (false) ... and if (true) ..., it isn’t a case of using the wrong value.

You are using glGetUniformLocation and not assuming locations, right? Because adding new uniforms can change the location of existing uniforms.

The setting of uniform variables might be useful, but there isn’t really much you could do that would conceivably result in nothing drawing. If the result with that condition differs from both if (false) ... and if (true) ..., it isn’t a case of using the wrong value.

I just tried, if (false) ... draws the object in its place, and if (true) ... draws the object on the same place, just slightly elevated. This is what’s confusing me. I’m still learning how shaders work and how to support different glsl versions and hardware, but I never expected an error like that.

You are using glGetUniformLocation and not assuming locations, right? Because adding new uniforms can change the location of existing uniforms.

Yes, so my current setup for the shaders looks like this:

  • During initialization, compile shaders and link the 2 programs (default, and the new one).

  • When drawing an object with the new shader, I do the following steps:

    glUseProgram(newProgram);
    GLuint MatrixID2 = glGetUniformLocation(newProgram, "matrixProjection");
    glUniformMatrix4fv(MatrixID2, 1, GL_FALSE, (float *)&matPerspective);
    GLuint MatrixID = glGetUniformLocation(newProgram, "matrix");
    glUniformMatrix4fv(MatrixID , 1, GL_FALSE, (float *)getModelViewMatrix());
    
    m->animationCurrentLocation = glGetUniformLocation(newProgram, "animationCurrent");
    glUniform1f(m->animationCurrentLocation, m->animationCurrent);
    // draw object
    glUniform1f(m->animationCurrentLocation, 40);
    
    glUseProgram(defaultProgram);
    

I’m aware this is not very optimal, I know of a few optimizations, like drawing all objects that use that shader in bulk, and using glGetUniformLocation only once after the program is linked, but I’m now focusing on this specific bug. This is also how I set matrix and matrixProjection, and they work exactly as expected.

Although come to think of it, since the same compiled project works on some devices and doesn’t work on others, that would suggest the OpenGL part is for the most part correct, right? As only the shaders are compiled at runtime.

The most confusing thing for me is that even if I add another uniform matrix, similar to matrix, the same bug will appear, and I can’t reference that variable either. I checked online with limits on how many uniforms can a shader have, but everything I’ve found suggest shaders are very generous, and offer 100+ uniforms, while I’m only trying to use less than 10.

Could it be something specific to GLSL ES 3.10 ? I can’t help but notice both of my devices that have this issue use that version, that includes a Kindle Fire 8, while an older device, Kindle Fire 7, using GLSL ES 1.00 works as expected. The main difference being that I changed all in to attribute and all out to varying for it.

If you have any other suggestions, or ideas, I’m happy to try them. Thanks a lot for the effort.

That suggest that it may be an implementation bug. Or it could be that your code is triggering undefined behaviour.

It’s possible that the ES 3.1 devices can’t quite support that version, the driver is trying to fudge the issue, and failing. Or that the driver is fairly new and is effectively still “beta”. Or it could just be that the code does something that isn’t entirely valid in 3.1. Given that the situation appears to be “works on everything except Mali-T720”, I’d lean towards assuming a driver bug.

Unfortunately, I don’t have enough concrete experience with ES to suggest common problems. Nothing in your code stands out. Are you checking the result of glGetError periodically?

It’s possible that the ES 3.1 devices can’t quite support that version, the driver is trying to fudge the issue, and failing. Or that the driver is fairly new and is effectively still “beta”. Or it could just be that the code does something that isn’t entirely valid in 3.1. Given that the situation appears to be “works on everything except Mali-T720”, I’d lean towards assuming a driver bug.

Considering how unusual this bug is, I’m leaning there too.

Are you checking the result of glGetError periodically?

I did use it once I found out about this bug. I just tried it, but running it directly after every opengl function call when compiling the shaders and using them, and checking for GL_NO_ERROR, which was always the case.

I seem to be getting some inconsistent results after adding some extra uniforms, and messing around a bit. I’ll experiment with it and see if I can understand what is happening behind the scenes, and if I can figure a workaround that will give the desired results. I’ll update this thread if I find anything worth mentioning.

I’ve just found what the problem is, and the workaround for it.

On those specific devices, it seems that declaring the uniforms in a specific order matters. I’m not sure if that’s a bug with Mali-T720 (since both devices use it) or with GLSL 3.10, as I don’t have enough devices to test this.

My solution was changing from this:

uniform mat4 matrix;
uniform mat4 matrixProjection;
uniform float animationCurrent;

To this:

uniform float animationCurrent;
uniform mat4 matrix;
uniform mat4 matrixProjection;

The shader compiles successfully and renders all the special effects modified by animationCurrent. I don’t know what’s the order of all different uniform types, but hopefully this can help someone having a similar issue in the future.

If anybody knows why this happens, and if it’s indeed a bug with the hardware, or something mentioned by a specific version of OpenGL, let me know, as I’d still like to know why this behaves this way.

The order in which uniforms are declared shouldn’t affect anything but their locations (which are allocated by the linker if you don’t have layout(location=...) qualifiers on the declarations).

Have you tried printing the locations returned from glGetUniformLocation? That may give a clue as to what’s going on. Try specifying the location in the shader (unlike attributes, uniform variables always use a single location, even for matrices).

Are you sure that you’re querying locations correctly? If you’re storing locations, be aware that re-linking a program can change them. When setting uniforms, the target program must be the current program (there’s no mechanism to set a default-block uniform variable for a program other than the current program).

You aren’t using multiple threads, right? Default-block uniforms are part of the program object so you can’t use the program in multiple threads concurrently (unless you use the same uniform values for all threads).

1 Like

From what I’ve read, I agree. I’ve never seen anything being mentioned about their order.

I tried mentioning the location manually, but the shader stops drawing objects again. I tried layout(location = 0) and layout(location = 1) just in case, but it breaks in both cases.

I just tried that, and these are the results:

when animationCurrent is mentioned first in order (the issue is fixed)

matrixProjection: 1
matrix: 2
animationCurrent: 3

when animationCurrent is mentioned last in order (the issue is not fixed and the shader draws nothing)

matrixProjection: 2
matrix: 3
animationCurrent: 1

The order seems to be in reverse, as in, when animationCurrent is mentioned early, it gets the location 3, while when mentioned last, it gets the location 1. I’m not sure if that gives us any clue on what might be happening.

I’ve made sure to link the program only once at startup, I use the code I mentioned above to draw objects, which uses glUseProgram then glGetUniformLocation and glUniform*, and then draws the object (using glDrawArrays).

Come to think of it, I am using multiple threads. However I’ve made sure to have only one thread have access to the OpenGL context, while the other only updates some non-OpenGL values over time (it’s for a video game).
For this thread, you can think of it like, one thread decides what the value of animationCurrent is, while the other thread takes that value and passes it to the shader to draw. I’ve made sure the threads don’t use the same objects at the same time.

I hope this information helps.

All things considered, I’d assume that this is a driver bug.