【问题标题】:Binding to GL_ELEMENT_ARRAY_BUFFER with no VAO bound绑定到没有 VAO 绑定的 GL_ELEMENT_ARRAY_BUFFER
【发布时间】:2013-12-05 05:09:13
【问题描述】:

当前绑定到 OpenGL 中GL_ELEMENT_ARRAY_BUFFER 目标的缓冲区是顶点数组对象(从这里开始的 VAO)中包含的状态的一部分。根据 OpenGL 4.4 核心配置文件规范,在没有绑定 VAO 的情况下尝试更改或访问 GL_ELEMENT_ARRAY_BUFFER 似乎是一个错误:

10.4 顶点数组对象

...INVALID_OPERATION 错误由任何命令生成 没有顶点数组时修改、绘制或查询顶点数组状态 是绑定的。这发生在初始 GL 状态,并且可能发生 BindVertexArray 的结果或 DeleteVertexArrays 的副作用。

OpenGL wiki 的 Buffer Object 页面支持此功能:

GL_ELEMENT_ARRAY_BUFFER​

gl*Draw*Elements*​ 形式的所有渲染函数都将使用指针 字段作为从绑定到 this 的缓冲区对象开始的字节偏移量 目标。用于索引渲染的索引将从缓冲区中获取 目的。 请注意,此绑定目标是顶点数组对象状态的一部分, 因此在此处绑定缓冲区之前必须绑定 VAO。

现在,如果不是这样就好了。它将使创建和管理与任何特定 VAO 分开的索引缓冲区变得容易。但是,如果在没有绑定 VAO 时仅将缓冲区绑定到 GL_ELEMENT_ARRAY_BUFFER 是禁止的,那么唯一的选择是表示索引缓冲区的类在创建/更新/等时绑定虚拟 VAO。

Nicol Bolas 的出色 OpenGL tutorial 表示这种使用方式实际上是有效的:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER): 在没有 VAO 绑定的情况下调用它不会失败。

这似乎与标准和 opengl.org wiki 相矛盾。标准中是否有我遗漏的支持这一点的内容,还是仅指不需要使用 VAO 的兼容性配置文件上下文?

【问题讨论】:

    标签: c++ opengl vao


    【解决方案1】:

    如果您有 AMD 或 NV GPU,您始终可以使用 EXT_direct_state_access 扩展来操作缓冲区对象而无需绑定它(这纯粹是驱动程序功能,不需要任何特殊类别的硬件)。遗憾的是,尽管这个扩展已经存在 5 年,但英特尔、Mesa 和 Apple 都懒得实施——懒惰的懒鬼。


    看看下面的函数,它们会让你描述的更容易:

    • glNamedBufferDataEXT (...)
    • glNamedBufferSubDataEXT (...)
    • glMapNamedBufferEXT (...)
    • glUnmapNamedBufferEXT (...)

    现在,由于很少采用 DSA,因此您可能必须为不支持它的系统编写一些备用代码。您可以通过编写具有相同函数签名的函数来重现 DSA 的功能,这些函数使用虚拟 VAO 来绑定 VBO 和 IBO,以便在不支持扩展的系统上进行数据操作。您必须在使用之前跟踪绑定的 VAO,并在所述函数返回之前恢复它以消除副作用。

    在一个好的引擎中,您应该隐藏 VAO 绑定状态,而不必从 GL 查询它。也就是说,不是直接使用glBindVertexArray (...),而是实现了一个包装该调用的系统,因此总是知道什么VAO绑定到特定上下文。最后,这使得在不存在驱动程序支持的情况下模拟 DSA 功能很多更加高效。如果您尝试这样的操作,您需要注意glDelete (...) 函数会隐式地解除绑定(通过绑定 0)被删除的对象(如果它绑定在当前上下文中)。

    【讨论】:

    • 我已经有一个系统来跟踪当前的 GL 状态并防止需要 glGet* 类型的调用。因此,只要看到像bind(0) 这样的请求,就可以很容易地添加一个绑定的虚拟 VAO。另一方面,通过直接状态访问,几乎所有东西都需要实现两次以支持不暴露它的硬件,从而增加了错误的机会。我可以看到 DSA 比具有适当客户端状态跟踪的 glBind* 实现略快,但我认为所有额外的条件代码都会抵消这种轻微的性能优势。
    • 在我看来,EXT_direct_state_access 更注重的是便利性而不是性能,如果你还必须实现回退,那么便利性是没有意义的。
    【解决方案2】:

    但是,如果在没有绑定 VAO 时仅将缓冲区绑定到 GL_ELEMENT_ARRAY_BUFFER 是禁止的,则唯一的替代方法是表示索引缓冲区的类在创建/更新/等时绑定虚拟 VAO。

    对旧问题的新答复,但在不需要 VAO(或干扰当前绑定的 VAO)的情况下使用索引缓冲区的简单方法是将缓冲区绑定到 GL_ELEMENT_ARRAY_BUFFER 以外的目标。

    例如,而不是这个:

    glBindVertexArray(vaoID);                               // ...Do I even have a VAO yet?
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);   // <- Alters VAO state!
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, ...);
    

    -- 可以改为这样写:

    glBindBuffer(GL_COPY_WRITE_BUFFER, indexBufferID);
    glBufferSubData(GL_COPY_WRITE_BUFFER, ...);
    

    (这里我随意使用了GL_COPY_WRITE_BUFFER,它的存在是为了提供一个临时目标,使缓冲区之间的复制更容易,但大多数其他目标也可以。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多