【问题标题】:What happens if you call glBufferData on a mapped buffer?如果在映射缓冲区上调用 glBufferData 会发生什么?
【发布时间】:2019-07-27 00:58:33
【问题描述】:

如果在当前使用 glMapBufferRange 映射的缓冲区上调用 glBufferData 会发生什么?我认为这是非法的,但我在规范中找不到任何内容:

https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glBufferData.xhtml

在 glDrawArrays 规范中是非法的。

好的,额外的挑战:

如果我们有上下文资源共享,并且缓冲区当前在线程 A 中与上下文 A 映射,那么上下文 B 上的线程 B 会在其上调用 glBufferData 怎么办?

【问题讨论】:

    标签: opengl-es opengl-es-3.0


    【解决方案1】:

    对于单个上下文场景,当调用 glBufferData() 然后删除现有缓冲区对象时,该上下文中该资源的任何活动绑定都将被解除绑定,并且任何活动映射都将被删除。如果您在同一上下文中在glBufferData() 之后调用glUnmapBuffer(),那么您将收到GL_INVALID_OPERATION 错误,因为最初未映射新版本缓冲区的状态。

    对于多上下文场景,它变得更加复杂。 OpenGL ES 定义了一个弱连贯的状态管理模型(以避免对性能关键调用路径的昂贵锁定要求)。

    • 属于上下文的渲染状态(例如绑定信息、启用位)永远不能被另一个上下文修改(可以实现无锁)。
    • 属于对象(例如缓冲区、采样器、纹理)的资源状态是弱连贯的。上下文会立即看到自己的更改,但只会在绑定资源时获取另一个上下文写入的更改(只需要锁定绑定更改)。
    • 资源数据负载完全不一致。如果您想确保数据在另一个上下文中可用,则必须包括线程之间的手动同步。

    调用glMapBuffer() 的线程A 将创建缓冲区“版本1”的主状态副本,包括缓冲区“映射”的本地状态设置。

    线程 B 调用 glBufferData() 将创建缓冲区资源“版本 2”的新版本,但这不会影响线程 A 持有的状态,它将继续反映绑定缓冲区时的状态线程 A(版本 1)。

    调用 glUnmapBuffer() 的线程 A 可以正常工作,因为它将取消映射缓冲区“版本 1”(映射状态是线程 A 上下文的本地状态,并且仍然表示缓冲区是“映射的”)。

    注意,线程B调用glBufferData()后,线程A看到的缓冲区的数据内容是不可预测的(可能是旧数据,也可能是新数据),符合数据不连贯的设计一点也不。如果没有挂起的绘制操作,那么驱动程序可以简单地重用支持“版本 1”缓冲区的内存来包含为“版本 2”上传的内容。如果您想保证跨上下文的数据一致性,那么您需要手动同步(这在概念上就像让两个线程同时在同一个缓冲区上调用 glBufferData())。

    我建议阅读 OpenGL ES 3.2 规范的第 5 章。

    【讨论】:

    • 好的,假设线程A调用glMapBufferRange,假设映射区域有一定的大小。然后线程 B 调用 glBufferData,但大小比以前小。这是否意味着来自线程 A 的映射指针现在指向的区域小于请求的区域,从而导致访问时出现段错误?
    • 不,页面应该被映射操作引用。
    • ...但是如果您最终遇到这种情况,您已经陷入未定义的行为,所以它不会崩溃,但它可能会呈现垃圾..
    • 我们究竟是什么时候进入了未定义的行为?其中一个函数会抛出错误代码吗?
    • "注意线程B调用glBufferData()后线程A看到的缓冲区的数据内容是不可预测的(可能是旧数据,也可能是新数据),按照设计数据完全不连贯”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-04
    • 1970-01-01
    • 2020-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多