为什么glGetProgramiv GL_ACTIVE_UNIFORMS偶尔会返回垃圾并导致程序崩溃?

时间:2015-03-07 21:32:07

标签: c++ c opengl driver undefined-behavior

我已经修改了Anton在这里找到的opengl教程伴随代码中的一些代码: https://github.com/capnramses/antons_opengl_tutorials_book/blob/master/02_shaders/main.c

在测试print_all或dump_all函数以打印着色器的所有内容时,我在日志中注意到glGetProgramiv(shader_program, GL_ACTIVE_UNIFORMS, &params)会产生非常消极的垃圾值(并且由于以下情况而无法执行任何操作for循环检查)或非常积极,并在glGetActiveUniform(shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name)崩溃我的程序,因为它只应处理iNUM_ACTIVE_UNIFORMS - 1的值;我相信它不会超过100万。我在调用之后还检查了glGetError的结果,并且没有设置错误标志。

这是我的转储功能和我日志的图片:

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);
}

当值为负时我的日志:http://i.stack.imgur.com/Xu9nq.png

当值很大并且崩溃程序时我的日志:http://i.stack.imgur.com/QBP1o.png

注意:我从无法找到文件的目录中运行应用程序。另外,我还没有在任何地方调用glUseProgram()。因此,我正在转储未成功链接的着色器的内容,或者甚至连接了任何成功编译的着色器。

这是一个间歇性的问题;大多数情况下,日志将正确打印0表示活动制服。到目前为止,我已经看到了三个结果; 1.)数字太大而且崩溃了。 2.)这个数字是疯狂的负面而且没有崩溃。 3.)数字很大但不是太大,并且在崩溃调用下方的循环中花费了大量时间,只是打印出垃圾。

这是一个驱动程序错误,还是我在做一些固有的未定义的东西?

编辑1:

如果对glGetProgramiv(shader_program, GL_ACTIVE_UNIFORMS, &params)的调用返回一个庞大的数字,则glGetActiveUniform (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name)的调用会在其下方for循环的第一次迭代中崩溃,i的值为零。但是,当glGetProgramiv为活动制服返回0时,对glGetActiveUniform的调用i为0并不会崩溃(我对for循环进行了硬编码)一旦)。这让我觉得这里还有更多的内容,而不仅仅是未经初始化的数据会返回给我。

编辑2 根据要求,这是一个提供奇怪值的最小示例程序:

#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;
}

2 个答案:

答案 0 :(得分:1)

查询无法链接的程序的制服(或任何其他状态)的数量在GL中有效。第7.3.1节中的OpenGL 4.3 core profile specification状态:

  

如果程序链接失败,则链接可能失败了   原因的数量,包括程序需要更多的情况   资源比实施支持的资源。实施是   允许但不要求记录可能的资源列表   如果程序成功链接,则被认为是活跃的。如果   实现不记录任何给定接口的信息,   相应的活动资源列表被视为空。如果一个   程序从未被链接,所有活动资源列表都是   被认为是空的。

与第7.13节一起讨论glGetProgramiv():

  

如果pnam e是ACTIVE_ATTRIBUTES,则活动属性的数量(请参阅   程序中的第7.3.1节返回。如果不存在活动属性,   返回零。如果pname是ACTIVE_UNIFORMS,则为活动数   程序中的制服被退回。如果没有活动的制服,则为零   返回。

请注意,第7.3.1节仅在此引文中引用一次,但它定义了&#34;资源&#34;包括制服和属性(以及其他)。

一般情况下,GL永远不会向您返回未初始化的数据 - 除非您查询自己负责填充的内容(如缓冲区,帧缓冲区等)。 glGet*()调用成功(并返回有效数据),或者因GL错误而失败(并且不会向参数指向的内存写入任何内容)。

如果你看到一些未初始化的GL状态,那很可能是一个驱动程序错误。请注意,查询无法链接的程序的资源是一个特别不寻常(实际上也是无用的)操作(我从未尝试过这样做),所以这可能是一个没有经过良好测试的代码路径,我可以想象你可能会遇到一些驱动程序错误。

由于您有一个能够重现问题的最小测试程序,我可以给出的唯一建议是:更新您的驱动程序。如果这没有帮助,请提交错误报告。

答案 1 :(得分:0)

在无法链接的程序上调用glGetProgramiv(GL_ACTIVE_UNIFORMS)是一个非常糟糕的主意。

可能的情况是,驱动程序从未达到设置该值的过程中的点,并且只是将未初始化的内存返回给您。

由于程序处于无效状态,因此构成驱动程序错误的问题含糊不清。