OpenGL:在单独的线程上使用glClientWaitSync

5
我正在使用GL_MAP_UNSYNCHRONIZED_BIT和glMapBufferRange来映射一个缓冲对象。然后将返回的指针传递给工作线程以异步计算新顶点。对象是双缓冲的,因此我可以在写入一个对象时渲染另一个对象。使用GL_MAP_UNSYNCHRONIZED_BIT可以显著提高性能(主要是因为glUnmapBuffer更快地返回),但是我会出现一些视觉伪影(尽管进行了双缓冲),所以我认为GPU开始渲染时DMA上传仍在进行中,或者工作线程开始过早地写入顶点。
如果我正确理解glFenceSync、glWaitSync和glClientWaitSync,则应该按照以下方式解决这些问题:
A: 避免GPU在DMA处理完成之前渲染缓冲对象:在glUnmapBufferRange之后直接在主线程上调用
GLsync uploadSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
glWaitSync(uploadSync, 0, GL_TIMEOUT_IGNORED);

B: 在GPU完成渲染之前,避免在工作线程中向缓冲区写入内容:在glDrawElements调用后,直接在主线程上调用。

GLsync renderSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);

在工作线程上,在开始向先前从glMapBufferRange返回的指针写入数据之前

glClientWaitSync(renderSync,0,100000000);
...start writing to the mapped pointer

1:我的显式同步方法正确吗?

2:如何处理第二种情况?我想在工作线程中等待(不想使主线程停滞),但是我无法从工作线程发出glCommands。除了GL调用之外,是否有另一种方法来检查GLsync是否已被标记为已信号?


你可以为上传线程使用一个单独的GL上下文,该上下文与主上下文共享所有绘图对象。在这种情况下,您还可以共享同步对象,并以此方式同步线程。 - derhass
我理解GLsync对象的方式是它们不绑定到上下文,所以我猜上下文甚至不需要共享。如果可能的话,我想避免为工作线程使用单独的上下文。在Android上,似乎可以通过eglDupNativeFenceFDANDROID从GLsync生成本地fence - 如果Windows也有类似的东西或者跨平台的话,那就太好了。 - matthias_buehlmann
同步对象是与GL上下文(或多个共享上下文)关联的对象,就像任何其他GL对象一样,因此您不能在没有共享上下文的情况下摆脱它们。 - derhass
opengl.org的同步对象(https://www.opengl.org/wiki/Sync_Object)维基页面指出:“同步对象从未绑定到上下文,也不像普通GL对象那样封装状态。这些不是OpenGL对象。” - matthias_buehlmann
看起来维基百科上有一个错误(也许是来自旧版本的同步对象?)扩展文档非常清楚地说明它的工作方式类似于其他gl对象:https://www.opengl.org/registry/specs/ARB/sync.txt - Jerem
1个回答

2
你可以在工作线程中创建一个OpenGL上下文,然后与主线程共享它。接下来:

Run on the main thread:

GLsync renderSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();

那么

Run on the worker thread:

glClientWaitSync(renderSync,0,100000000);

主线程上的 glFlush 很重要,否则您可能会无限等待。 请参阅OpenGL文档:

4.1.2 信号

脚注3: SYNC_FLUSH_COMMANDS_BIT 定义的简单刷新行为在等待另一个上下文命令流中发出的围栏命令完成时无法帮助。 阻塞围栏同步对象的应用程序必须采取其他措施,以确保发出相应围栏命令的上下文已将该命令刷新到图形管道。


1
我想避免为工作线程单独创建上下文,但似乎无法避免。 - matthias_buehlmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接