简体   繁体   中英

Why does glGetProgramiv GL_ACTIVE_UNIFORMS occasionally return garbage and crash my program?

I have adapted some code from Anton's opengl tutorial companion code found here: https://github.com/capnramses/antons_opengl_tutorials_book/blob/master/02_shaders/main.c

In testing the print_all or dump_all function to print everything about a shader, I have noticed in my log that glGetProgramiv(shader_program, GL_ACTIVE_UNIFORMS, &params) will put a garbage value that is either extremely negative(and doesn't do anything due to the for loop check) or extremely positive and crashes my program at glGetActiveUniform(shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name) because it is only supposed to handle values of i up to NUM_ACTIVE_UNIFORMS - 1 ; and I am sure it isn't expecting a number over 1 million. I have also checked for the result of glGetError after the call and no error flags have been set.

Here is my dump function and a picture of my log:

void dump_all(FILE* file, GLuint shader_program)
{
    int params = -1;

    fprintf(file, "--------------------\nshader program %i info:\n", shader_program);
    glGetProgramiv (shader_program, GL_LINK_STATUS, &params);
    fprintf(file, "GL_LINK_STATUS = %i\n", params);

    glGetProgramiv (shader_program, GL_ATTACHED_SHADERS, &params);
    fprintf(file,"GL_ATTACHED_SHADERS = %i\n", params);

    glGetProgramiv (shader_program, GL_ACTIVE_ATTRIBUTES, &params);
    fprintf(file, "GL_ACTIVE_ATTRIBUTES = %i\n", params);

    const int MAX_LENGTH = 64;
    int i;
    for (i = 0; i < params; i++)
    {
        char name[MAX_LENGTH];
        int actual_length = 0; // not used atm.
        int size = 0;
        GLenum type;
        glGetActiveAttrib (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name);
        if (size > 1)
        {
            int j;
            for (j = 0; j < size; j++)
            {
                char long_name[MAX_LENGTH];
                int location;

                sprintf (long_name, "%s[%i]", name, j);
                location = glGetAttribLocation (shader_program, long_name);
                fprintf (file, "  %i) type:%s name:%s location:%i\n",
                    i, gl_type_to_string(type), long_name, location);
            }
        }
        else
        {
            int location = glGetAttribLocation (shader_program, name);
            fprintf(file, "  %i) type:%s name:%s location:%i\n", i, gl_type_to_string (type), name, location);
        }
    }

    printf("\nbefore the active uniform call\n");
    glGetProgramiv (shader_program, GL_ACTIVE_UNIFORMS, &params);
    GLenum error = glGetError();
    printf("\nThere is an error (0 for no error and 1 for an error): %d\n", error != GL_NO_ERROR);
    printf("\nglGetError: %d\n", error);
    printf("\nafter the get active uniform call\n");
    fprintf(file, "GL_ACTIVE_UNIFORMS = %i\n", params);
    for (i = 0; i < params; i++)
    {
        char name[MAX_LENGTH];
        int actual_length = 0; // not used atm.
        int size = 0;
        GLenum type;
        printf("\nright before the thing\n");
        glGetActiveUniform (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name);
        printf("\nright after the thign\n");
        if (size > 1)
        {
            printf("\nin the if block\n");
            int j;
            for (j = 0; j < size; j++)
            {
                char long_name[MAX_LENGTH];
                int location;

                sprintf (long_name, "%s[%i]", name, j);
                location = glGetUniformLocation (shader_program, long_name);
                fprintf(file, "  %i) type:%s name:%s location:%i\n",
                    i, gl_type_to_string (type), long_name, location);
            }
        }
        else
        {
            printf("\nin the else block\n");
            int location = glGetUniformLocation (shader_program, name);
            fprintf(file, "  %i) type:%s name:%s location:%i\n",
                    i, gl_type_to_string (type), name, location);
        }
    }

    print_program_info_log(file, shader_program);
}

My log when the value is negative: http://i.stack.imgur.com/Xu9nq.png

My log when the value is huge and crashes the program : http://i.stack.imgur.com/QBP1o.png

Note: I am running the application from a directory where the files cannot be found. Also, I have yet to make a call to glUseProgram() anywhere. So, I am dumping the contents of a shader that hasn't been successfully linked or even has any successfully compiled shaders attached to it.

This is an intermittent problem; most of the time, the log will correctly print out 0 for active uniforms. So far, I have seen three outcomes; 1.) The number is too big and it crashes. 2.) The number is crazily negative and doesn't crash . 3.) The number is big but not too big and spends a lot of time in the loops below the crashing call, just printing out garbage.

Is this a Driver bug, or am I doing something that is inherently undefined?

EDIT 1:

In the case that the call to glGetProgramiv(shader_program, GL_ACTIVE_UNIFORMS, &params) returns a huge number, the call to glGetActiveUniform (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name) crashes in the first iteration of the for loop below it with the value of zero for i . However, when glGetProgramiv returns 0 for the active uniforms like it should, a call to glGetActiveUniform with 0 for i doesn't crash(I hard coded the for loop to go around once). This makes me feel like there is more going on here than just uninitialized data being returned to me.

EDIT 2 As requested, here is a minimal example program that gives weird values:

#include <stdio.h>

#undef main
int main(int argc, char ** argv)
{
    SDL_Init(SDL_INIT_EVERYTHING);

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

    SDL_Window *window = SDL_CreateWindow("Example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL);
    if(!window)
    {
        printf("no window created : %s\n", SDL_GetError());
    }
    SDL_GLContext context = SDL_GL_CreateContext(window);
    SDL_GL_MakeCurrent(window, context);
    SDL_GL_SetSwapInterval(1);

    glewExperimental = GL_TRUE;
    GLenum err = glewInit();
    if(err != GLEW_OK)
    {
        printf("glew failed to init: %s", glewGetErrorString(err));
        return -1;
    }

    GLuint program = glCreateProgram();

    glLinkProgram(program); // I can create and attach shaders, compile them, or whatever; I get the same result.

    GLint num;
    glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &num);

    printf("NUM UNIFORMS: %d", num);

    int run_loop = 1;
    while(run_loop)
    {
        SDL_Event event;
        while(SDL_PollEvent(&event))
        {
            switch(event.type)
            {
                case SDL_QUIT:
                    SDL_Quit();
                    run_loop = 0;
                    break;
                default:
                    break;
            }
        }
        SDL_GL_SwapWindow(window);
    }   
    return 0;
}

Querying the number of uniforms (or any other state) of a program that failed to link is valid in the GL. The OpenGL 4.3 core profile specification states in section 7.3.1:

If a program is linked unsuccessfully, the link may have failed for a number of reasons, including cases where the program required more resources than supported by the implementation. Implementations are permitted, but not required, to record lists of resources that would have been considered active had the program linked successfully. If an implementation does not record information for any given interface, the corresponding list of active resources is considered empty. If a program has never been linked, all lists of active resources are considered empty.

together with section 7.13 about glGetProgramiv():

If pnam e is ACTIVE_ATTRIBUTES , the number of active attributes (see section 7.3.1) in program is returned. If no active attributes exist, zero is returned. If pname is ACTIVE_UNIFORMS , the number of active uniforms in program is returned. If no active uniforms exist, zero is returned.

Note that section 7.3.1 is only referenced once in this quotation, but it defines "resources" to include uniforms as well as attributes (among others).

In general, the GL will never return uninitialized data to you - except when you query things for which you yourself were responsible for filling (like buffers, framebuffers and so on). A glGet*() call either succeeds (and returns valid data), or fails with some GL error (and does not write anything to the memory pointed to by the arguments).

If you see some uninitialized GL state, that is very likely a driver bug. Note that querying the ressources of a program which failed to link is a particularily unusual (and in effect also quite useless) operation (I never attempted to do this), so this is proably a not well-tested code path and I can imagine that you might hit some driver bugs.

Since you have a minimal test program which is able to reproduce the issue, the only recommendation I can give is: update your drivers. If that doesn't help, file a bug report.

Calling glGetProgramiv(GL_ACTIVE_UNIFORMS) on a program that failed to link is a very bad idea.

Odds are that the driver never reached the point in the process where that value is set, and is just returning uninitialized memory to you.

Wether this constitutes a driver bug is vaguely debatable, since the program is in an invalid state.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM