OpenGL PBO映射缓冲区:多线程解压慢,memcpy快

4
我们正在使用一台工作站 Core i7 和 AMD FirePro 8000。对于视频解码(8K,7680x4320 视频帧 ~ 66MB hapq 编解码器),我们尝试使用以下显而易见的循环:
1. 从流中获取帧 2. 映射缓冲区 3. 多线程解码帧切片到映射的缓冲区 4. 取消映射缓冲区 5. 从绑定的 PBO 将 Texsubimage 图像子集插入纹理
但是步骤3. 多线程解码切片到映射的缓冲区非常慢 - 至少需要40毫秒才能完成。
当我们将其拆分为两个步骤时,
3a. 多线程解码帧切片到 malloced 内存 3b. 从 malloced 内存复制到映射的缓冲区
这两个步骤都需要8+9 ~ 17毫秒才能完成。现在我们有了一个相当可接受的解决方案,但额外的复制步骤仍然很烦人。
为什么多线程解压到映射内存会特别慢?如何避免额外的复制步骤?
编辑1: 这是生成、定义和映射缓冲区的方法:
glGenBuffers(1, &hdf.m_pbo_id);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, hdf.m_pbo_id);
glBufferData(GL_PIXEL_UNPACK_BUFFER, m_compsize, nullptr, GL_STREAM_DRAW);
hdf.mapped_buffer = (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);

编辑2:

有人提出了一个问题,关于时间的测量方式。只有非gl代码才被测量。伪代码如下:

情况1(非常慢,t2-t1约为40ms):

gl_map();
t1 = elapse_time();
unpack_multithreaded_multiple_snappy_slices_into_mapped_buffer();
t2 = elapse_time();
gl_unmap();

案例2(中等缓慢,t3-t2〜9毫秒,t2-t1〜8毫秒):
gl_map();
malloc_sys_buffer();
t1 = elapse_time();
unpack_multithreaded_multiple_snappy_slices_into_sys_buffer();
t2 = elapse_time();
memcpy_sys_buffer_into_mapped_buffer();
t3 = elapse_time();
gl_unmap();

在测量的代码块中没有涉及OpenGL代码。可能是写穿/ CPU缓存问题。


1
缓冲区是否持久映射?缓冲区是否使用不可变存储分配?设置了哪些标志? - BDL
我在上面添加了缓冲区生成/定义/映射代码,请看一下。这就是你所要求的吗? - Heiner
1
你如何测量时间?测量中包括什么内容?问题在于由于同步问题,根据编写代码的方式可能会有很大的差异。例如:map - long calculation - unmap - draw - repeat。在这种情况下,map 可能需要很长时间,因为它要等到 draw 完成后才能开始。但这并不意味着长时间计算更慢。它只是稍后开始。如果没有看到 [MCVE],我认为无法回答这个问题。 - BDL
提供一个完整可编译的示例是不可能的。但是关于测量经过的时间,就像上面所述一样。我添加了伪代码以达到最大的清晰度。 - Heiner
也许这是一个写通/ CPU 缓存问题。是的,这可能是一个可能的解释。您应该查看实际映射,以了解在您特定的实现中发生了什么。在 Windows 上,Sysinternals 套件中的某些工具(忘记了其中哪一个)可以向您显示映射信息。您可以尝试切换到持久映射缓冲区,它可能会导致不同的映射(并且还避免了映射/取消映射开销)。 - derhass
1个回答

1

将数据解压到映射内存中会很慢,因为这种内存是写组合的。对于这种类型的内存,每次写入时都会将完整的高速缓存线路通过总线传输到GPU。与此内存交互的最佳方式是以尽可能大的块大小写入数据。为了避免额外的复制步骤,您可能需要修改解码器以写入大块连续内存。同时,尝试使用不同的编写线程也是有益的。 这里https://fgiesen.wordpress.com/2013/01/29/write-combining-is-not-your-friend/提供了一个很好的概述。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接