Kazade
Kazade

Reputation: 1327

Unexplainable GL_INVALID_OPERATION from glUniform1i (OpenGL thinks an int is a float?)

Update: Mystery solved... sort of!

I used glGetActiveUniform to investigate what type of variable GLSL thinks my uniform is.

This was the output:

I/KGLT    (21149): 2005266104: UNIFORM active_texture_count with size 1 and type 5126 (//kglt/kglt/gpu_program.cpp:368)

Type 5126 is GL_FLOAT!?! And indeed using glUniform1f solves the problem, but, why? When the definition in the shader is definitely int?

Original question below

I'm porting my game engine to Android. On the desktop I'm using OpenGL 3.x and GLSL 1.2, on Android I'm using GLES 2.0.

For some reason on my Android phone I'm getting a GL_INVALID_OPERATION error from glUniform1i, but I can't figure out what the reason is.

I've added some logging to show the issue:

E/KGLT    (28500): 2006330776: Attempting to set uniform value of 0 at location 0 for name active_texture_count (//kglt/kglt/gpu_program.cpp:50)
W/Adreno-ES20(28500): <__load_uniform_int:351>: GL_INVALID_OPERATION
E/KGLT    (28500): 2006330776: An OpenGL error occurred: void kglt::UniformManager::set_int(const unicode &, const int32_t) - GL_INVALID_OPERATION (//kglt/kglt/utils/gl_error.cpp:41)

You'll notice that Adreno-ES20 logs the problem just before I do, and also just before I call glUniform1i I log the value I'm setting, the location returned from glGetUniformLocation, and the uniform name (not that that's relevant really). I've verified that the GL calls are being made in the main thread, so there should be no threading issue at work here.

According to the documentation, glUniformX can raise an INVALID_OPERATION in the following circumstances, I'll comment inline:

GL_INVALID_OPERATION is generated if there is no current program object.

I've checked, there definitely is a program object and it's the one I'm expecting it to be. Checked with glGetIntegerv(GL_CURRENT_PROGRAM, ...)

GL_INVALID_OPERATION is generated if the size of the uniform variable declared in the shader does not match the size indicated by the glUniform command.

The uniform declaration in the GLSL is this:

    uniform int active_texture_count;

Definitely looks like an integer to me, so using glUniform1i is correct right? This is the one I'm most suspicious of, is there some kind of packing issue involved here?

GL_INVALID_OPERATION is generated if one of the integer variants of this function is used to load a uniform variable of type float, vec2, vec3, vec4, or an array of these, or if one of the floating-point variants of this function is used to load a uniform variable of type int, ivec2, ivec3, or ivec4, or an array of these.

See above, I don't think I'm doing this. I'm definitely using glUniform1i, and the value is definitely an integer, and the shader variable is definitely defined as an integer.

GL_INVALID_OPERATION is generated if location is an invalid uniform location for the current program object and location is not equal to -1.

I'm calling glGetUniformLocation immediately before the call to glUniform1i, with the same bound program ID. It returns zero which is a valid location (as -1 is the error value). Is there some way I can verify that the location refers to the variable name I think it does?

GL_INVALID_OPERATION is generated if count is greater than 1 and the indicated uniform variable is not an array variable.

count isn't an argument to the 1i variant of glUniform so not applicable.

GL_INVALID_OPERATION is generated if a sampler is loaded using a command other than glUniform1i and glUniform1iv.

I'm not setting a sampler value, and even if I was, I'm using the right variant anyway.

I'm at a total loss here, I've stared at this code for hours and Google'd around an can't find anything that might indicate what the problem is. I'm also unsure what my next step should be to debug this.

If it's at all helpful, here's the fragment shader in its entirety:

    uniform sampler2D textures[2];
    uniform vec4 global_ambient;
    uniform int active_texture_count;
    uniform vec4 material_ambient;

    varying vec2 frag_texcoord0;
    varying vec2 frag_texcoord1;
    varying vec4 frag_diffuse;

    void main() {
        if(active_texture_count == 0) {
            gl_FragColor = frag_diffuse * global_ambient * material_ambient;
        } else {
            vec4 t1 = texture2D(textures[0], frag_texcoord0.st);
            vec4 t2 = texture2D(textures[1], frag_texcoord1.st);
            if(active_texture_count < 2) {
                t2 = vec4(1.0, 1.0, 1.0, 0.0);
            }
            gl_FragColor = (((1.0 - t2.a) * t1) + (t2.a * t2)) * frag_diffuse * global_ambient * material_ambient;
        }
    }

EDIT: I've updated the code to be very explicit, so this is the current code

if(glGetError() != GL_NO_ERROR) {
    L_DEBUG("There was an error before setting the uniform");
}
glUseProgram(program_.program_object_);

if(glGetError() != GL_NO_ERROR) {
    L_DEBUG("There was an error after using the program");
}

GLint loc = glGetUniformLocation(program_.program_object_, name.c_str());//locate(uniform_name);

if(glGetError() != GL_NO_ERROR) {
    L_DEBUG("There was an error after getting the uniform location");
}

L_DEBUG(_u("Setting value {0} for uniform at location {1}").format(value, loc));
glUniform1i(loc, value);

if(glGetError() != GL_NO_ERROR) {
    L_DEBUG("There was an error after setting the uniform");
} else {
    L_DEBUG("Uniform set sucessfully");
}

And here is the logging output

I/KGLT    (12770): 2005266248: Setting value 0 for uniform at location 0 (//kglt/kglt/gpu_program.cpp:64)
W/Adreno-ES20(12770): <__load_uniform_int:351>: GL_INVALID_OPERATION
I/KGLT    (12770): 2005266248: There was an error after setting the uniform (//kglt/kglt/gpu_program.cpp:68)

As you can see, there is no error before, the program is active, the location is the one returned directly from GL... any ideas? :(

Upvotes: 1

Views: 4714

Answers (1)

Kazade
Kazade

Reputation: 1327

This is something to do with either the version declaration, or the precision declaration in the shader.

Previously, I simply had

#version 120

...because this shader was written for desktop GL. When I tried to test on an older device, the shader compiler complained that version 120 wasn't supported, so I was forced to change this to

#version 100
precision mediump float;

With this change, the variable is now correctly reported as an integer on both devices. I believe this is a driver bug when using version 120 on the Nexus 5. That's my best guess anyway!

Upvotes: 1

Related Questions