【问题标题】:Opengl Unsynchronized/Non-blocking MapOpengl 非同步/非阻塞地图
【发布时间】:2011-10-21 11:30:23
【问题描述】:

我刚刚找到了以下OpenGL specification for ARB_map_buffer_range

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

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

glMapBuffer(target_, GL_READ_ONLY);  

但是,这样做的问题是它在传输数据时阻塞了渲染线程。

我可以通过流水线渲染来减少这个问题,但延迟是我的应用程序中的一个大问题。

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

例如

thread 1:

map();
render_next_frame();

thread 2:

wait_for_map

thread 1:

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

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

有什么想法吗?

【问题讨论】:

    标签: opengl synchronization nonblocking pbo


    【解决方案1】:

    一般来说,做“非阻塞映射”是不可能的,但是可以不阻塞映射。

    之所以没有“非阻塞映射”,是因为在函数调用返回的那一刻,您可以访问数据,因此驱动程序必须确保它确实存在。如果数据还没有传输,驱动除了阻塞还能做什么。
    线程并没有使这变得更好,并且可能使情况变得更糟(添加同步和上下文共享问题)。线程不能神奇地消除传输数据的需要。

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

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


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

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

    【讨论】:

    • 您能否进一步解释一下这个“查询对象”?听起来我可以用它来推迟对 map 的调用,如果它还没有准备好。
    • 指的是ARB_sync,它也在核心3.2中。这使您可以将同步查询(“栅栏”)插入命令流,您可以等待(客户端或客户端+服务器)或查询完成状态。如果同步查询返回它已完成,这意味着在您插入围栏之前发生的所有命令都已完成。因此,在您的情况下,您知道数据在您想要的位置,并且映射缓冲区不会阻塞。
    • 非常好,所以如果我理解正确的话,我应该在调用 glReadPixels(设备->主机传输)之后直接进行这样的同步查询。一旦我知道查询已准备好,那么对 map 的调用就不会阻塞(或者只是很快)?
    • -1:这个答案不正确。您可以使用 GL_MAP_UNSYNCHRONIZED_BIT 进行非阻塞映射操作。但是,您将立即对可能写入 GPU 正在读/写的内存的后果负责,等等。所以你仍然需要使用栅栏等来进行同步。
    • @ronag:确保您使用的是 svn 版本,而不是最新版本。几个月前我做了很多工作来更新 Glee,但作者还没有发布另一个版本。
    【解决方案2】:

    如果您使用GL_MAP_UNSYNCHRONIZED_BIT 映射缓冲区,驱动程序不会等到 OpenGL 处理完该内存后再为您映射。因此,您将或多或少地立即访问它。

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

    因此,为了实际使用非同步映射,您必须将您的行为与 OpenGL 对该缓冲区的访问同步。这将涉及使用ARB_sync objects(如果您只使用 NVIDIA 并且最近没有更新驱动程序,则使用 NV_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,否则您需要在阅读之前发出障碍)。

    【讨论】:

    猜你喜欢
    • 2018-04-06
    • 2023-03-14
    • 2013-07-20
    • 1970-01-01
    • 2013-05-07
    • 1970-01-01
    • 2016-12-14
    • 1970-01-01
    相关资源
    最近更新 更多