C++ – glUniformMatrix4fv fails with an error code of GL_INVALID_OPERATION

cglslopengl

I'm having some bad luck trying to bind a uniform 4×4 matrix. I'm targeting OpenGL 3.3 with this program, but my environment is OpenGL 4.2. I have a function that simply binds an identity matrix to a uniform in my vertex shader, but the call to glUniformMatrix4fv fails with GL_INVALID_OPERATION.

Here is my vertex shader:

#version 330
in vec4 in_vertex;
uniform mat4 mvMatrix;
void main(void) {
    gl_Position = mvMatrix * in_vertex;
}

I'm aware of the pitfalls of matrix transposition and left/right multiplying, but I figure that's a battle for when I can actually pass a uniform matrix.

Here is a simple function that's referenced in the function where I'm having problems. I'm only using this for now to try and pin down the place where the error occurs. Since the evaluation of glUniformMatrix4fv is all on the server side, I don't have a way to use breakpoints, etc.

inline void die_on_gl_error(const char* location) {
    GLenum error = GL_NO_ERROR;
    error = glGetError();
    if (GL_NO_ERROR != error) {
        printf("GL Error %x encountered in %s.\n", error, location);
        exit(1);
    }
}

The SDK docs say that there are a few reasons why glMatrixUniform4fv could set GL_INVALID_OPERATION:

  1. GL_INVALID_OPERATION is generated if there is no current program object.
  2. 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.
  3. 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.
  4. GL_INVALID_OPERATION is generated if location is an invalid uniform location for the current program object and location is not equal to -1.
  5. GL_INVALID_OPERATION is generated if count is greater than 1 and the indicated uniform variable is not an array variable.
  6. GL_INVALID_OPERATION is generated if a sampler is loaded using a command other than glUniform1i and glUniform1iv.
  7. GL_INVALID_OPERATION is generated if glUniform is executed between the execution of glBegin and the corresponding execution of glEnd.

For context, the object from which this function is called has a parameter called active_program that stores the number of the currently activated GLSL program. identity_matrix is declared as:

float identity_matrix[16];

and defined as:

identity_matrix = {
            1.0f, 0.0f, 0.0f, 0.0f,
            0.0f, 1.0f, 0.0f, 0.0f,
            0.0f, 0.0f, 1.0f, 0.0f,
            0.0f, 0.0f, 0.0f, 1.0f
};

Without further ado, here is what's giving me trouble:

void VSGL::load_identity_matrix() {
// GL_INVALID_OPERATION is generated if there is no current program object.
if (!glIsProgram(active_program)) {
    printf("Active program is not valid.\n");
    exit(1);
}

// ... active_program is a program, but is it valid?
GLint program_valid = 0;
glValidateProgram(active_program);
glGetProgramiv(active_program, GL_VALIDATE_STATUS, &program_valid);
if (GL_TRUE != program_valid) {
    printf("Program validation failed.\n");
    exit(1);
}
die_on_gl_error("GetProgram (Validate Status)");

// ... makes sure there is a program active, and the current program matches
// the value stored in active_program.
GLint current_program = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, &current_program);
if (0 == current_program) {
    printf("Error, no current program is set.\n");
    exit(1);
} else if (current_program != active_program) {
    printf("Error, current program doesn't match active_program!\n");
}
die_on_gl_error("GetInteger");

// ... ensures the program actually has an active uniform, as the docs
// say that uniforms can be optimized out if they don't contribute to
// out results.
GLint num_active_uniforms = 0;
glGetProgramiv(active_program, GL_ACTIVE_UNIFORMS, &num_active_uniforms);
if (0 == num_active_uniforms) {
    printf("There are 0 uniforms active in program %d.\n", active_program);
    exit(1);
} else {
    printf("There are %d uniform(s) active in program %d.\n", num_active_uniforms, active_program);
}
die_on_gl_error("GetProgram (Active Uniforms)");

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

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

// ... gets some basic information about the active uniforms, of which there
// should be only one, a FLOAT_MAT4 of size 1.
const GLchar *uniform_name = "mvMatrix";
GLint location = glGetUniformLocation(active_program, uniform_name);
die_on_gl_error("GetUniformLocation");

GLchar *message;
GLint max_uniform_length;
glGetProgramiv(active_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_length);
message = new GLchar[max_uniform_length];
GLint size;
GLenum type;
glGetActiveUniform(active_program, location, max_uniform_length, NULL, &size, &type, message);
printf("Uniform %s:\tType:%x\tSize:%d\n", message, type, size);
if (GL_FLOAT_MAT4 != type) {
    printf("Active uniform at location is not a 4x4 float matrix.\n");
}
die_on_gl_error("GetActiveUniform");

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

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

// GL_INVALID_OPERATION is generated if glUniform is executed between the execution
// of glBegin and the corresponding execution of glEnd.

// None of the above are true, and yet the following dies with GL_INVALID_OPERATION?
glUniformMatrix4fv(location, 1, false, identity_matrix);
die_on_gl_error("UniformMatrix4f");
}

After all that, here's the output:

There are 1 uniform(s) active in program 3.
Uniform mvMatrix:   Type:8b5c   Size:1
GL Error 502 encountered in UniformMatrix4f.

Type 8b5c is GL_FLOAT_MAT4, of course, and size is 1, of course, so I can't see which of the invalid operation conditions is biting me!

Edit:

Here's the loop in main where UseProgram and this function are called:

    while (wm->update()) {
        wm->poll_input();
        handle_input(viewingmatrix);
        if (!gl->use_program(program))
            exit(-1);
        gl->load_identity_matrix();
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glDrawArrays(GL_TRIANGLES, 0, bananaNumVerts);
        glFlush();
        usleep(16667);
    }

gl->use_program(program) is just a wrapper that checks the validity of the int passed in and updates the object's active_program parameter.

Edit 2: My thanks to luke for pointing me at gDEBugger, which detected the GL Error as well. In the call information in gDEBugger, I noted that only three arguments were listed. While I assume this might be because the fourth was a pointer to an array (does that reside on the client side, or is it passed to the server side every time glUniform is called?), it got me thinking of what else might be the cause.

In case it's probative, glUniformMatrix4fv is, of course, actually a function pointer that obtains its address like so:

Declaration:

PFNGLUNIFORMMATRIX4FV glUniformMatrix4fv;

Assignment:

glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)glXGetProcAddress((const GLubyte*)"glUniform4fv");

This is me avoiding GLEW for academic reasons. However, when I was looking through glext.h, I noted that there was also a PFNGLUNIFORMMATRIX4FVARBPROC, which I presume is just there for codebases written before this function was adopted into core. If that's not the case, please let me know.

Best Answer

Take a look at your glXGetProcAddress call:

glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)glXGetProcAddress((const GLubyte*)"glUniform4fv");

You're requesting glUniform4fv instead of glUniformMatrix4fv !

I know you said you aren't using extension wrapper libraries for academic reasons, but I still highly recommend them.

Related Topic