c++ - 为什么 glGetProgramiv GL_ACTIVE_UNIFORMS 偶尔会返回垃圾并使我的程序崩溃?

标签 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, ¶ms) 将放置一个垃圾值,该值要么非常负(并且由于 for 循环检查而没有做任何事情)或非常积极并使我的程序在 glGetActiveUniform(shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name) 崩溃,因为它只应该处理 i 最多 NUM_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, ¶ms) 返回一个巨大的数字,调用 glGetActiveUniform (shader_program, i, MAX_LENGTH, &actual_length, &size, &type, name) 在它下面的 for 循环的第一次迭代中崩溃,i 的值为零。但是,当 glGetProgramiv 像它应该的那样为事件制服返回 0 时,用 0 为 i 调用 glGetActiveUniform 不会崩溃(我很难将 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;
}

最佳答案

查询链接失败的程序的统一数(或任何其他状态)在 GL 中有效。 OpenGL 4.3 core profile specification 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.

连同关于 glGetProgramiv() 的 7.13 节:

If pname 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.

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

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

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

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

关于c++ - 为什么 glGetProgramiv GL_ACTIVE_UNIFORMS 偶尔会返回垃圾并使我的程序崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28920238/

相关文章:

opengl - 你能计算每个实例而不是每个顶点的东西吗?

opengl - GBUFFER 贴花投影和缩放

javascript - WebGL - 沿行进方向旋转对象

java - 将 Java float 转换为 C

c++ - noexcept 依赖于成员函数的 noexcept

c++ - 如何打印两个数的公倍数?

c++ - 用于防止重复和语法错误问题的 boolean 数组?

c++ - boost 线程错误< Unresolved 重载函数类型>

c - 无法弄清楚这个 c 语法是什么

c - 在 C 中解读字符串