Opengl非同步/非阻塞映射

时间:2011-08-06 13:28:08

标签: opengl synchronization nonblocking pbo

我刚刚找到以下OpenGL specification for ARB_map_buffer_range

我想知道是否可以使用此扩展程序进行非阻塞地图调用?

目前在我的应用程序中我正在渲染到FBO,然后我将其映射到主机PBO缓冲区。

glMapBuffer(target_, GL_READ_ONLY);  

然而,问题在于它在传输数据时会阻塞渲染线程。

我可以通过管道渲染来减少此问题,但延迟是我的应用程序中的一个大问题。

我的问题是我是否可以将map_buffer_range与MAP_UNSYNCHRONIZED_BIT一起使用并等待地图操作在另一个线程上完成,或者在同一线程上推迟地图操作,同时渲染线程渲染下一帧。

e.g。

thread 1:

map();
render_next_frame();

thread 2:

wait_for_map

thread 1:

map();
while(!is_map_ready())
   do_some_rendering_for_next_frame();

我不确定的是我如何知道地图操作何时准备好,规范只提到“其他同步技术以确保正确操作”。

有什么想法吗?

2 个答案:

答案 0 :(得分:7)

如果使用GL_MAP_UNSYNCHRONIZED_BIT映射缓冲区,则在为您映射OpenGL之前,驱动程序不会等到OpenGL完成。因此,您可以或多或少地立即访问它。

问题在于,意味着你可以随便读/写那个内存。如果OpenGL正在读取或写入该缓冲区并且您更改它...欢迎使用未定义的行为。其中可能包括崩溃。

因此,为了实际使用非同步映射,必须将行为与OpenGL对该缓冲区的访问同步。这将涉及使用ARB_sync objects(如果您仅在NVIDIA上使用NV_fence,并且最近未更新您的驱动程序)。

话虽如此,如果您使用fence对象来同步对缓冲区的访问,那么您根本不需要GL_MAP_UNSYNCHRONIZED_BIT。完成围栏或检测到它已完成后,您可以正常映射缓冲区并立即完成(除非其他操作也在读/写)。

通常,非同步访问最适用于需要对缓冲区进行细粒度写访问的情况。在这种情况下,充分利用同步对象将获得您真正需要的东西(能够判断地图操作何时完成)。


附录:上述内容现已过时(取决于您的硬件)。感谢OpenGL 4.4 / ARB_buffer_storage,您现在不仅可以映射未​​同步,还可以无限期地保存缓冲区是的,您可以在使用缓冲区时映射

这是通过创建不可变存储并使用GL_MAP_PERSISTENT_BIT(以及其他内容)提供该存储来完成的。然后你glMapBufferRange,也提供相同的位。

从技术上讲,这几乎没有变化。您仍然需要使用OpenGL同步您的操作。如果您将内容写入缓冲区的某个区域,则需要issue a barrierflush that region of the buffer explicitly。如果您正在阅读,在阅读之前仍然需要使用fence sync object来确保数据实际上是那里(除非您也使用GL_MAP_COHERENT_BIT,在阅读之前需要发布障碍。

答案 1 :(得分:6)

一般情况下,无法执行“非阻止地图”,但您可以无阻塞地进行映射。

没有“非阻塞映射”的原因是函数调用返回的那一刻,你可以访问数据,所以驱动程序必须确保它在那里,积极。如果数据尚未传输,则驱动程序还可以阻止其他操作 线程不会使这更好,并可能使它变得更糟(添加同步和上下文共享问题)。线程无法神奇地消除传输数据的需要。

这导致如何不阻止映射:只有在确定传输完成时才进行映射。一种安全的方法是在翻转缓冲区后或glFinish之后或等待查询/ fence对象后映射缓冲区。如果你不能等到缓冲区被交换,使用栅栏是最好的方法。围栏不会阻止管道,但会告诉您转移是否已完成(glFinish可能会或可能不会,但可能停止)。 交换缓冲区后的读取也是100%安全的,但如果您需要同一帧内的数据(可以完美地用于屏幕截图或计算色调映射的直方图),则可能无法接受。

一种不太安全的方法是插入“其他一些东西”并希望转移已经完成。

<小时/> 关于以下评论:
这个答案不正确。在可用之后访问数据是不可能做得更好(这应该是显而易见的)。这意味着你必须同步/阻止,无论如何,没有选择 虽然从一个非常迂腐的角度来看,你当然可以使用GL_MAP_UNSYNCHRONIZED_BIT来获得非阻塞的地图操作,但这完全无关紧要,因为除非明确重现,否则它不起作用如上所述的隐式同步。您无法安全访问的映射是没有用的。

映射和访问OpenGL正在传输数据而没有同步/阻塞(隐式或显式)的缓冲区意味着“未定义的行为”,这只是“可能是垃圾结果,可能是崩溃”的更好的措辞。
另一方面,如果您明确地同步(例如,如上所述使用栅栏),那么您是否使用未同步标志是无关紧要的,因为无论如何都不需要进行隐式同步。