VAO和元素数组缓冲区状态

the*_*ine 22 opengl nvidia intel vertex-array opengl-3

我最近写一些的OpenGL 3.3代码与顶点数组对象(VAO)并测试它以后英特尔图形适配器,我发现,我感到失望,该元件阵列缓冲器结合显然不是VAO状态的一部分,作为呼叫:

glBindVertexArray(my_vao);
glDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_INTEGER, 0);
Run Code Online (Sandbox Code Playgroud)

没有效果,而:

glBindVertexArray(my_vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, my_index_buffer); // ?
glDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_INTEGER, 0);
Run Code Online (Sandbox Code Playgroud)

渲染几何体.我认为这是在英特尔执行的OpenGL(因为它是在GL_ARB_vertex_array_object即使在GL_OES_vertex_array_object明确提出(和)该元素阵列的一个单纯的错误保存状态的一部分),但随后在移动的NVIDIA Quadro 4200发生这不好玩.

它是我的代码中的驱动程序错误,规格错误或错误吗?代码在GeForce 260和480上完美运行.

有没有类似的经历?

什么也奇怪的是,GL_EXT_direct_state_access不具有结合元件阵列缓冲器到VAO的函数(但是它确实有功能来指定顶点ATTRIB阵列,因此阵列缓冲器).GPU制造商是否违反规范并欺骗我们,或者是什么?

编辑:

我原本不打算显示任何源代码,因为我认为这里没有必要.但是根据要求,这是重现问题的最小测试用例:

static GLuint n_vertex_buffer_object, p_index_buffer_object_list[3];
static GLuint p_vao[2];

bool InitGLObjects()
{
    const float p_quad_verts_colors[] = {
        1, 0, 0, -1, 1, 0,
        1, 0, 0, 1, 1, 0,
        1, 0, 0, 1, -1, 0,
        1, 0, 0, -1, -1, 0, // red quad
        0, 0, 1, -1, 1, 0,
        0, 0, 1, 1, 1, 0,
        0, 0, 1, 1, -1, 0,
        0, 0, 1, -1, -1, 0, // blue quad
        0, 0, 0, -1, 1, 0,
        0, 0, 0, 1, 1, 0,
        0, 0, 0, 1, -1, 0,
        0, 0, 0, -1, -1, 0 // black quad
    };
    const unsigned int p_quad_indices[][6] = {
        {0, 1, 2, 0, 2, 3},
        {4, 5, 6, 4, 6, 7},
        {8, 9, 10, 8, 10, 11}
    };
    glGenBuffers(1, &n_vertex_buffer_object);
    glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
    glBufferData(GL_ARRAY_BUFFER, sizeof(p_quad_verts_colors), p_quad_verts_colors, GL_STATIC_DRAW);
    glGenBuffers(3, p_index_buffer_object_list);
    for(int n = 0; n < 3; ++ n) {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[n]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(p_quad_indices[n]), p_quad_indices[n], GL_STATIC_DRAW);
    }

    glGenVertexArrays(2, p_vao);
    glBindVertexArray(p_vao[0]);
    {
        glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0));
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(3 * sizeof(float)));
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[0]); // red
    }
    glBindVertexArray(0);

    glBindVertexArray(p_vao[1]);
    {
        glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0));
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(3 * sizeof(float)));
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[1]); // blue
    }
    glBindVertexArray(0);

#ifdef BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[2]);
    // bind the buffer with the black quad (not inside VAO, should NOT be seen)
#endif // BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER

    // [compile shaders here]

    return true; // success
}
Run Code Online (Sandbox Code Playgroud)

上面的代码创建了一个顶点缓冲区,包含三个四边形,红色,蓝色和黑色.然后它创建三个指向各个四边形的索引缓冲区.然后创建并设置两个VAO,一个应包含红色四边形索引,另一个应包含蓝色四边形索引.黑四不应该在所有的(假设BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER呈现定义).

void onDraw()
{
    glClearColor(.5f, .5f, .5f, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    glDisable(GL_DEPTH_TEST);

    glUseProgram(n_program_object);

    static int n_last_color = -1;
    int n_color = (clock() / 2000) % 2;
    if(n_last_color != n_color) {
        printf("now drawing %s quad\n", (n_color)? "blue" : "red");
        n_last_color = n_color;
    }

    glBindVertexArray(p_vao[n_color]);
#ifdef VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[n_color]); // fixes the problem
#endif // VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}
Run Code Online (Sandbox Code Playgroud)

这会将视口清除为灰色并以重复方式呈现蓝色或红色四边形(它还会打印哪一个).虽然这适用于台式机GPU,它不会对笔记本GPU工作(除非VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER宏定义的黑色方形的呈现方式.取消定义BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER宏使得四蓝色,因为蓝色的索引缓存是最后的约束.但事实并非如此无论如何渲染红色四边形.

所以我看待它的方式,在我对VAO应该如何工作,我的代码中的错误或驱动程序错误的理解中要么是致命的误解.

完整源
二进制文件(windows,32位)

the*_*ine 25

过了一段时间,我发现这实际上是我的坏事.配备移动NVIDIA Quadro 4200显卡的笔记本电脑设置为默认情况下所有应用程序都可以在英特尔显卡上运行,即使笔记本电脑处于性能模式.我不明白为什么有人想这样做,因为任何应用程序都无法使用OpenGL中更强大的GPU(它仍然可以用于OpenCL,因为有明确的设备选择,也可能是DirectX - 这可以解释为什么有些游戏运行顺利).

尽管如此,所描述的错误行为只是英特尔驱动程序中的一个错误,这就是它的全部内容.英特尔驱动程序不保存ELEMENT_ARRAY_BUFFER_BINDING.那里.

我真的很抱歉提出这个问题,因为在不知情的情况下无法给出一个好的答案.

  • 即使它对你没有帮助,在SO上记录驱动程序错误对我有帮助. (7认同)

Nic*_*las 18

我实际上相信ARB VAO缺少元素数组缓冲区绑定(或任何其他缓冲区绑定)状态.

信仰不是必需的; 规范说明了事实.

ARB_vertex_array_object规范:

命令

void GenVertexArrays(sizei n, uint *arrays);
Run Code Online (Sandbox Code Playgroud)

返回以前未使用的顶点数组对象名称.这些名称仅标记为已使用,仅用于GenVertexArrays,并使用表6.6(CLIENT_ACTIVE_TEXTURE选择器状态除外),6.7和6.8(ARRAY_BUFFER_BINDING状态除外)中列出的状态进行初始化.

所以我们拥有它:VAO所包含的整个州是这三个表的内容,但有例外.

扩展名是针对OpenGL图形规范2.1版(PDF)编写的.因此,相对于该规范引用任何页码,节标签或表号.

我不打算在这里复制这三张桌子.但如果您查看第273页(按规格页数)/第287页(按物理页数),您将找到表6.8.在该表上有以下内容:

  • ELEMENT_ARRAY_BUFFER_BINDING

这里没有歧义.可能没有明确说明这些信息.但毫无疑问,它就在那里.ELEMENT_ARRAY_BUFFER_BINDING是VAO状态的一部分.

因此,您的问题可能来自以下两个来源之一:

  1. 驱动程序bug.正如我在评论中所说,驱动程序错误似乎不太可能.并非不可能,只是不太可能 对于不同的硬件,NVIDIA的驱动程序非常类似,VAO几乎不会在硬件中反映出来.除非您使用不同版本的驱动程序,否则没有理由期望错误是由于驱动程序错误造成的.

  2. 用户错误.我知道你声称你的代码有效,所以没关系.每个人都对某些代码做出了声明.并且有很多次我会发誓说某些代码工作得很好.然而它被打破了; 它刚刚过去了.它发生了.如果你发布你的代码,那么至少我们可以打折这种可能性.否则,我们只有你的话.考虑到人类对此的错误,这不值得.