Opengl 非同步/非阻塞映射
我刚刚找到了以下ARB_map_buffer_range
的OpenGL规范。
我想知道是否可以使用此扩展进行非阻塞地图调用?
目前,在我的应用程序中,我正在渲染到 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();
我不确定的是我如何知道映射操作何时准备就绪,规范仅提到“确保正确操作的其他同步技术”。
有什么想法吗?
I just found the following OpenGL specification for ARB_map_buffer_range
.
I'm wondering if it is possible to do non-blocking map calls using this extension?
Currently in my application im rendering to an FBO which I then map to a host PBO buffer.
glMapBuffer(target_, GL_READ_ONLY);
However, the problem with this is that it blocks the rendering thread while transferring the data.
I could reduce this issue by pipelining the rendering, but latency is a big issue in my application.
My question is whether i can use map_buffer_range with MAP_UNSYNCHRONIZED_BIT and wait for the map operation to finish on another thread, or defer the map operation on the same thread, while the rendering thread renders the next frame.
e.g.
thread 1:
map();
render_next_frame();
thread 2:
wait_for_map
or
thread 1:
map();
while(!is_map_ready())
do_some_rendering_for_next_frame();
What I'm unsure of is how I know when the map operation is ready, the specification only mentions "other synchronization techniques to ensure correct operation".
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您使用 GL_MAP_UNSYNCHRONIZED_BIT 映射缓冲区,驱动程序将不会等到 OpenGL 处理完该内存后再为您映射。所以你或多或少会立即访问它。
问题是,这并不意味着您可以随意读/写该内存。如果 OpenGL 正在读取或写入该缓冲区并且您更改了它...欢迎未定义的行为。其中可能包括崩溃。
因此,为了实际使用非同步映射,您必须将您的行为与 OpenGL 对该缓冲区的访问同步。这将涉及使用 ARB_sync 对象(或 NV_fence,如果您只使用 NVIDIA 并且没有使用最近没有更新您的驱动程序)。
话虽这么说,如果您使用栅栏对象来同步对缓冲区的访问,那么您实际上根本不需要
GL_MAP_UNSYNCHRONIZED_BIT
。一旦完成栅栏,或检测到它已完成,您就可以正常映射缓冲区,并且它应该立即完成(除非其他操作也在读/写)。一般来说,当您需要对缓冲区进行细粒度的写访问时,最好使用非同步访问。在这种情况下,充分利用同步对象将获得您真正需要的东西(能够判断映射操作何时完成)。
附录:以上内容现已过时(取决于您的硬件)。感谢 OpenGL 4.4/ARB_buffer_storage,您现在不仅可以进行不同步的贴图,您可以无限期地保持缓冲区映射。是的,您可以在使用时映射缓冲区。
这是通过创建不可变存储并为该存储提供(除其他外)
GL_MAP_PERSISTENT_BIT
。然后你glMapBufferRange
,也提供相同的位。现在从技术上来说,这几乎没有改变。您仍然需要将您的操作与 OpenGL 同步。如果您向缓冲区的某个区域写入内容,则需要发出屏障 或显式刷新缓冲区的该区域。如果您正在阅读,您仍然需要使用 fence 同步对象 来确保数据实际上在读取之前已经存在(除非您也使用 GL_MAP_COHERENT_BIT,否则您需要在读取之前发出屏障)。
If you map a buffer with
GL_MAP_UNSYNCHRONIZED_BIT
, the driver will not wait until OpenGL is done with that memory before mapping it for you. So you will get more or less immediate access to it.The problem is that this does not mean that you can just read/write that memory willy-nilly. If OpenGL is reading from or writing to that buffer and you change it... welcome to undefined behavior. Which can include crashing.
Therefore, in order to actually use unsynchronized mapping, you must synchronize your behavior to OpenGL's access of that buffer. This will involve the use of ARB_sync objects (or NV_fence if you're only on NVIDIA and haven't updated your drivers recently).
That being said, if you're using a fence object to synchronize access to the buffer, then you really don't need
GL_MAP_UNSYNCHRONIZED_BIT
at all. Once you finish the fence, or detect that it has completed, you can map the buffer normally and it should complete immediately (unless some other operation is reading/writing too).In general, unsynchronized access is best used for when you need fine-grained write access to the buffer. In this case, good use of sync objects will get you what you really need (the ability to tell when the map operation is finished).
Addendum: The above is now outdated (depending on your hardware). Thanks to OpenGL 4.4/ARB_buffer_storage, you can now not only map unsynchronized, you can keep a buffer mapped indefinitely. Yes, you can have a buffer mapped while it is in use.
This is done by creating immutable storage and providing that storage with (among other things) the
GL_MAP_PERSISTENT_BIT
. Then youglMapBufferRange
, also providing the same bit.Now technically, that changes pretty much nothing. You still need to synchronize your actions with OpenGL. If you write stuff to a region of the buffer, you'll need to either issue a barrier or flush that region of the buffer explicitly. And if you're reading, you still need to use a fence sync object to make sure that the data is actually there before reading it (and unless you use
GL_MAP_COHERENT_BIT
too, you'll need to issue a barrier before reading).一般来说,不可能做到“无阻塞映射”,但可以无阻塞映射。
不能有“非阻塞映射”的原因是,函数调用返回时,您就可以访问数据,因此驱动程序必须确保它存在。如果数据还没有传输,驱动程序除了阻塞还能做什么。
线程不会让情况变得更好,甚至可能让情况变得更糟(增加同步和上下文共享问题)。线程无法神奇地消除传输数据的需要。
这就导致了如何在映射时不阻塞:只有在确定传输完成时才进行映射。一种安全的方法是在翻转缓冲区后或在
glFinish
后或在等待查询/栅栏对象后映射缓冲区。如果您不能等到缓冲区被交换,那么使用栅栏是更好的方法。栅栏不会停止管道,但会告诉您传输是否完成(glFinish
可能会也可能不会,但可能会停止)。交换缓冲区后读取也是 100% 安全的,但如果您需要同一帧内的数据,则可能无法接受(不过,非常适合屏幕截图或计算色调映射的直方图)。
一种不太安全的方法是插入“一些其他内容”并希望同时传输完成。
In respect of below comment:
This answer is not incorrect. It isn't possible to do any better than access data after it's available (this should be obvious). Which means that you must sync/block, one way or the other, there is no choice.
Although, from a very pedantic point of view, you can of course use
GL_MAP_UNSYNCHRONIZED_BIT
to get a non-blocking map operation, this is entirely irrelevant, as it does not work unless you explicitly reproduce the implicit sync as described above. A mapping that you can't safely access is good for nothing.映射和访问 OpenGL 将数据传输到的缓冲区而不同步/阻塞(隐式或显式)意味着“未定义的行为”,这只是“可能是垃圾结果,可能崩溃”的更好的措辞。
另一方面,如果您显式同步(例如,使用如上所述的栅栏),则无论您是否使用未同步标志都无关紧要,因为无论如何都不需要发生更多隐式同步。
In general, it is not possible to do a "nonblocking map", but you can map without blocking.
The reason why there can be no "nonblocking map" is that the moment the function call returns, you could access the data, so the driver must make sure it is there, positively. If the data has not been transferred, what else can the driver do but block.
Threads don't make this any better, and possibly make it worse (adding synchronisation and context sharing issues). Threads cannot magically remove the need to transfer data.
And this leads to how to not block on mapping: Only map when you are sure that the transfer is finished. One safe way to do this is to map the buffer after flipping buffers or after
glFinish
or after waiting on a query/fence object. Using a fence is the preferrable way if you can't wait until buffers have been swapped. A fence won't stall the pipeline, but will tell you whether or not your transfer is done (glFinish
may or may not, but will probably stall).Reading after swapping buffers is also 100% safe, but may not be acceptable if you need the data within the same frame (works perfectly for screenshots or for calculating a histogram for tonemapping, though).
A less safe way is to insert "some other stuff" and hope that in the mean time the transfer has completed.
In respect of below comment:
This answer is not incorrect. It isn't possible to do any better than access data after it's available (this should be obvious). Which means that you must sync/block, one way or the other, there is no choice.
Although, from a very pedantic point of view, you can of course use
GL_MAP_UNSYNCHRONIZED_BIT
to get a non-blocking map operation, this is entirely irrelevant, as it does not work unless you explicitly reproduce the implicit sync as described above. A mapping that you can't safely access is good for nothing.Mapping and accessing a buffer that OpenGL is transferring data to without synchronizing/blocking (implicitly or explicitly) means "undefined behavior", which is only a nicer wording for "probably garbage results, maybe crash".
If, on the other hand, you explicitly synchronize (say, with a fence as described above), then it's irrelevant whether or not you use the unsynchronized flag, since no more implicit sync needs to happen anyway.