即使要将输出数据发送到OpenGL而不是CPU,是否也应该等待CUDA流完成?

3xiyfsfu  于 2022-11-04  发布在  其他
关注(0)|答案(2)|浏览(280)

这是一个一般性的问题,尽管我使用OpenCV作为框架,但这个问题比OpenCV的领域更广泛。
我正在开发一个图像处理工具,它可以有效地从网络摄像头获取图像(产生一个位于cv::Mat的主机内存),将其上传到CUDA中的GPU设备内存(即cv::GpuMat),使用CUDA进行一些处理并得到一个结果finalCudaMat,最后将结果发送到OpenGL(即cv::ogl::Buffer::mapDevice + finalCudaMat.copyTo(mappedOglBuffer))。
由于整个过程涉及多个步骤,因此我使用了CUDA流对象(cv::cuda::Stream)能够异步调用CUDA,而无需等待CPU端完成每个操作。现在,如果有人最终将结果复制到CPU矩阵(即finalCudaMat.download(finalCpuMat)),如在通常情况下,通常需要对流进行等待(cudaStream.waitForCompletion())以确保在使用CPU侧矩阵之前结果已准备好。
在我的例子中,结果永远不会回到CPU,因为它继续在屏幕上渲染(还涉及到一点OpenGL操作和着色器)。

  • 一种方法是,在开始将GpuMat复制到OpenGL缓冲区之前,等待CUDA工作完成可能是合适的。因此,如果我添加流等待,一切都工作正常,CUDA操作需要大约2.5ms。
  • 换句话说,我感觉我不需要等待流的完成(所有的结果都会被GPU使用-- CPU不会再被调用)。因此,我可以在执行finalCudaMat.copyTo(mappedOglBuffer)之前删除cudaStream.waitForCompletion()调用,所有的东西看起来都很好整个CUDA处理操作(基本上任何GPU任务减去OpenGL相关)显然需要约1.8毫秒。

在过去,如果涉及两个不同的API,我有过不正确同步GPU工作的糟糕经历(例如,在Direct 3D 9上做一些事情,不要等待它完成,然后将生成的纹理复制到Direct 3D 10纹理,显然在某些帧上图像会变为空或撕裂)。
在这一点上,差异很小,不会影响我的60 FPS吞吐量。但我想知道,从技术上讲,我是否在做一个正确的工作,删除等待流操作。对此有什么想法吗?或者可能是一个关于OpenGL/CUDA互操作的文档,可以帮助我?

42fyovps

42fyovps1#

本文档中定义了这些规则:https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#graphics-interoperability
特别是它说
在Map资源时,通过OpenGL、Direct3D或其他CUDA上下文访问该资源会产生未定义的结果。
这是一个很强的暗示,说明所需的同步是由cudaGraphicsUnmapResources执行的,这一点在其文档中得到了证实:
此函数提供了同步保证,即在cudaGraphicsUnmapResources()之前在 stream 中发出的任何CUDA工作将在任何后续发出的图形工作开始之前完成。
因此,您不需要让CPU等待CUDA完成,但必须调用cudaGraphicsUnmapResources,这将在异步指令流中放置适当的屏障。请注意,与CPU传输代码不同,此调用在CUDA将数据复制到OpenGL缓冲区 * 之后 * 进行。

tzxcd3kk

tzxcd3kk2#

正如Ben Voigt所指出的,CUDA需要与OpenGL(或任何其他与之互操作的图形API)进行显式同步。现在,这是一种繁琐的工作,必须向计算流提交回调,并使用它们手动处理OpenGL围栏等。
然而由于Vulkan的出现以及随之而来的对外部资源的支持(以及OpenGL扩展)事实上,您可以通过让CUDA和OpenGL命令流导入平台原生信号量,在两者之间实现同步(cudaImportExternalSemaphoreGL_EXT_semaphore),并使用它们进行相互同步。它通常仍然涉及通过CPU端驱动程序的整个往返行程,但由于该部分无论如何都必须管理命令流,因此这实际上不是效率问题。

相关问题