使用 OpenGL 中的持久映射缓冲区存储,仅在绘制之前和交换之后触及,是否真的需要任何进一步的同步?
With persistently mapped buffer storage in OpenGL, touched only before draw and after swap, is any further synchronization really needed?
我编写了一个简单的 C 程序 (on github),它使用 OpenGL 从分配给 glBufferStorage
的缓冲区中绘制一堆三角形,如下所示:
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
GLbitfield bufferStorageFlags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
glBufferStorage(GL_ARRAY_BUFFER, vboSize, 0, bufferStorageFlags);
vert *triData = glMapBufferRange(GL_ARRAY_BUFFER, 0, vboSize, bufferStorageFlags);
我知道在将 glBufferStorage
与 MAP_PERSISTENT_BIT
一起使用时,同步是我的责任,但我不确定我到底需要防止什么。
我唯一一次触摸 triData
是在调用 glDrawArrays
之前,调用 SDL_GL_SwapWindow
之后,所以我知道最后一帧的绘制已经完成,我还没有调用此框架尚未开始。
这似乎完美地工作,即使禁用了 vsync。
The wiki 说:
Swapping the back and front buffers on the Default Framebuffer may
cause some form of synchronization ... if there are still commands
affecting the default framebuffer that have not yet completed.
Swapping buffers only technically needs to sync to the last command
that affects the default framebuffer, but it may perform a full
glFinish.
但我读过的每篇关于该主题的文章都广泛使用了 GLsync
指针,尽管也许他们只是假设我可能想以更复杂的方式使用缓冲区?
现在,我是否相信 SDL_GL_SwapWindow
提供了足够的同步?
I am aware that synchronization is my responsibility when using glBufferStorage,
不,不一定。使用 glBufferStorage
创建的缓冲区与使用 glBuffer
创建的缓冲区没有什么不同,只是您不能重新指定它。
您只需要在使用 MAP_PERSISTENT_BIT
映射时进行手动同步(它包含在与 glBufferStorage
相同的扩展中,ARB_buffer_storage
)。
这是我关于任何 multithreaded/asynchronous 代码的 favorite advice:
If multithreaded code isn't immediately, obviously, provably correct then it is almost certainly wrong.
您无法证明 OpenGL 不会读取您正在写入的值。因此,这是错误的,即使没有明显的问题。
是的,您需要进行显式同步。即使您连贯地映射了缓冲区,您仍然无法在 OpenGL 可能正在读取它们时更改其中的值。您必须等到最后一次读取该数据的调用之后才能再次写入它。 OpenGL 必须等待它完成的唯一方法是 glFinish
或 glClientWaitSync
.
前面的回答是正确的,即使在使用交换后也确实需要同步。但我想说得更清楚一点,这不仅仅是一个理论上的问题。
交换操作通常不同步。让渲染比显示提前 1-2 帧是很常见的。这样做是为了减少 GPU 暂时进入空闲状态的 "bubbles"。如果您的交换调用是同步的,GPU 将不可避免地在 return 秒时处于空闲状态,因为所有先前提交的工作都已完成。即使您立即重新开始渲染,该工作也需要一点时间才能真正到达 GPU 执行。因此,有时 GPU 什么都不做,这至少会损害性能,只要您的渲染完全受 GPU 限制。
现在,您显然不希望渲染比显示提前太多。不良的副作用会增加响应用户输入的延迟(这对游戏来说是个大问题),以及排队渲染命令的过多内存使用。因此,在这种情况发生之前需要进行节流。这种限制通常作为交换操作的一部分应用,但几乎任何地方都可能发生。
因此,如果您测量对 return 的交换调用所花费的挂钟时间,很常见的是它足够长以表明它正在阻塞。但这并不表示调用本身是同步的。它可能只是阻塞,直到前一帧完成,以防止渲染超出显示太远。
我编写了一个简单的 C 程序 (on github),它使用 OpenGL 从分配给 glBufferStorage
的缓冲区中绘制一堆三角形,如下所示:
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
GLbitfield bufferStorageFlags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
glBufferStorage(GL_ARRAY_BUFFER, vboSize, 0, bufferStorageFlags);
vert *triData = glMapBufferRange(GL_ARRAY_BUFFER, 0, vboSize, bufferStorageFlags);
我知道在将 glBufferStorage
与 MAP_PERSISTENT_BIT
一起使用时,同步是我的责任,但我不确定我到底需要防止什么。
我唯一一次触摸 triData
是在调用 glDrawArrays
之前,调用 SDL_GL_SwapWindow
之后,所以我知道最后一帧的绘制已经完成,我还没有调用此框架尚未开始。
这似乎完美地工作,即使禁用了 vsync。
The wiki 说:
Swapping the back and front buffers on the Default Framebuffer may cause some form of synchronization ... if there are still commands affecting the default framebuffer that have not yet completed. Swapping buffers only technically needs to sync to the last command that affects the default framebuffer, but it may perform a full glFinish.
但我读过的每篇关于该主题的文章都广泛使用了 GLsync
指针,尽管也许他们只是假设我可能想以更复杂的方式使用缓冲区?
现在,我是否相信 SDL_GL_SwapWindow
提供了足够的同步?
I am aware that synchronization is my responsibility when using glBufferStorage,
不,不一定。使用 glBufferStorage
创建的缓冲区与使用 glBuffer
创建的缓冲区没有什么不同,只是您不能重新指定它。
您只需要在使用 MAP_PERSISTENT_BIT
映射时进行手动同步(它包含在与 glBufferStorage
相同的扩展中,ARB_buffer_storage
)。
这是我关于任何 multithreaded/asynchronous 代码的 favorite advice:
If multithreaded code isn't immediately, obviously, provably correct then it is almost certainly wrong.
您无法证明 OpenGL 不会读取您正在写入的值。因此,这是错误的,即使没有明显的问题。
是的,您需要进行显式同步。即使您连贯地映射了缓冲区,您仍然无法在 OpenGL 可能正在读取它们时更改其中的值。您必须等到最后一次读取该数据的调用之后才能再次写入它。 OpenGL 必须等待它完成的唯一方法是 glFinish
或 glClientWaitSync
.
前面的回答是正确的,即使在使用交换后也确实需要同步。但我想说得更清楚一点,这不仅仅是一个理论上的问题。
交换操作通常不同步。让渲染比显示提前 1-2 帧是很常见的。这样做是为了减少 GPU 暂时进入空闲状态的 "bubbles"。如果您的交换调用是同步的,GPU 将不可避免地在 return 秒时处于空闲状态,因为所有先前提交的工作都已完成。即使您立即重新开始渲染,该工作也需要一点时间才能真正到达 GPU 执行。因此,有时 GPU 什么都不做,这至少会损害性能,只要您的渲染完全受 GPU 限制。
现在,您显然不希望渲染比显示提前太多。不良的副作用会增加响应用户输入的延迟(这对游戏来说是个大问题),以及排队渲染命令的过多内存使用。因此,在这种情况发生之前需要进行节流。这种限制通常作为交换操作的一部分应用,但几乎任何地方都可能发生。
因此,如果您测量对 return 的交换调用所花费的挂钟时间,很常见的是它足够长以表明它正在阻塞。但这并不表示调用本身是同步的。它可能只是阻塞,直到前一帧完成,以防止渲染超出显示太远。