7

我有以下管道:

  1. 渲染到自定义 FBO 的纹理附件。
  2. 将该纹理附件绑定为图像。
  3. 运行计算着色器,使用 imageLoad/Store 从上面的图像中采样。
  4. 将结果写入 SSBO 或图像。
  5. 将 SSBO(或图像)映射为 CUDA CUgraphicsResource并使用 CUDA 处理来自该缓冲区的数据。

现在,问题在于在第 4 阶段和第 5 阶段之间同步数据。这是我尝试过的同步解决方案。

glFlush - 并不能真正起作用,因为它不能保证所有命令执行的完整性。

glFinish - 这个工作。但不建议这样做,因为它会完成所有提交给驱动程序的命令。

ARB_sync 这里据说不推荐,因为它严重影响性能。

glMemoryBarrier这个很有趣。但这根本行不通。

以下是代码示例:

glMemoryBarrier(GL_ALL_BARRIER_BITS);

并且还尝试过:

glTextureBarrierNV()

代码执行如下:

 //rendered into the fbo...
  glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
  glBindImageTexture(imageUnit1, fboTex, 0, GL_FALSE, 0, GL_READ_ONLY,GL_RGBA8); 
  glBindImageTexture(imageUnit2, imageTex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8));
  glDispatchCompute(16, 16, 1);

  glFinish(); // <-- must sync here,otherwise cuda buffer doesn't receive all the data

 //cuda maps the image to CUDA buffer here..

此外,我尝试在启动计算之前从上下文中解除绑定 FBO 和纹理,我什至尝试glMemoryBarrier在它们之间启动一个计算,然后将目标图像从第一次计算启动获取到 CUDA。仍然没有同步。(嗯,这是有道理的,因为两个计算也彼此不同步)

在计算着色器阶段之后。不同步!只有当我替换为glFinish, 或任何其他完全停止管道的操作时。比如glMapBuffer(),例如。

那么我应该只使用glFinish(),还是我在这里遗漏了什么?为什么 glMemoryBarrier() 在 CUDA 接管控制之前不同步计算着色器工作?

更新

我想稍微重构一下这个问题,因为原来的问题已经很老了。尽管如此,即使使用最新的 CUDA 和视频编解码器 SDK (NVENC),问题仍然存在。所以,我不在乎为什么glMemoryBarrier不同步。我想知道的是:

  1. 如果可以将 OpenGL 计算着色器执行完成与 CUDA 对该共享资源的使用同步,而不会停止整个渲染管道,在我的情况下是 OpenGL 图像。

  2. 如果答案是“是”,那该怎么做?

4

1 回答 1

2

I know this is an old question, but if any poor soul stumbles upon this...

First, the reason glMemoryBarrier does not work: it requires the OpenGL driver to insert a barrier into the pipeline. CUDA does not care about the OpenGL pipeline at all.

Second, the only other way outside of glFinish is to use glFenceSync in combination with glClientWaitSync:

....
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindImageTexture(imageUnit1, fboTex, 0, GL_FALSE, 0, GL_READ_ONLY,GL_RGBA8); 
glBindImageTexture(imageUnit2, imageTex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8));
glDispatchCompute(16, 16, 1);
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
... other work you might want to do that does not impact the buffer...
GLenum res = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, timeoutInNs);
if(res == GL_TIMEOUT_EXPIRED || res == GL_WAIT_FAILED) {
    ...handle timeouts and failures
}
cudaGraphicsMapResources(1, &gfxResource, stream);
...

This will cause the CPU to block until the GPU is done with all commands until the fence. This includes memory transfers and compute operations.

Unfortunately, there is no way to tell CUDA to wait on an OpenGL memory barrier/fence. If you really require the extra bit of asynchronicity, you'll have to switch to DirectX 12, for which CUDA supports importing fences/semaphores and waiting on as well as signaling them from a CUDA stream via cuImportExternalSemaphore, cuWaitExternalSemaphoresAsync, and cuSignalExternalSemaphoresAsync.

于 2019-11-11T09:16:11.917 回答