内存屏障无法通过CUDA在计算阶段和数据访问之间同步

时间:2016-06-01 12:45:48

标签: opengl cuda nvenc

我有以下管道:

  1. 渲染到自定义FBO的纹理附件。
  2. 将纹理附件绑定为图像。
  3. 使用imageLoad / Store运行计算着色器,从上图中采样。
  4. 将结果写入SSBO或图像。
  5. 将SSBO(或图像)映射为CUDA CUgraphicsResource,并使用CUDA处理该缓冲区中的数据。
  6. 现在,问题在于在阶段4和5之间同步数据。以下是我尝试过的同步解决方案。

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

    glFinish - 这个有效。但不建议这样做,因为它最终确定了提交给驱动程序的所有命令。

    ARB_sync Here it is said建议不要这样做,因为它会严重影响性能。

    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. 如果答案为“是”,那该怎么办?

1 个答案:

答案 0 :(得分:2)

我知道这是一个古老的问题,但是是否有可怜的灵魂偶然发现了这个问题...

首先,原因glMemoryBarrier不起作用:它需要 OpenGL驱动程序将障碍插入管线。 CUDA根本不关心OpenGL管道。

第二,glFinish之外的另一种方法是将glFenceSyncglClientWaitSync结合使用:

....
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);
...

这将导致CPU阻塞,直到所有命令执行完GPU为止。这包括内存传输和计算操作。

不幸的是,无法告诉CUDA在OpenGL内存屏障/栅栏上等待。如果您确实需要额外的异步性,则必须切换到DirectX 12,CUDA为此支持DirectX 12导入篱笆/信号量并等待并通过cuImportExternalSemaphore,{{1 }}和cuWaitExternalSemaphoresAsync