我的目标是读取默认OpenGL帧缓冲区的内容并将像素数据存储在a中cv::Mat.显然有两种不同的方法可以实现这一目标:
1)同步:使用FBO和 glRealPixels
cv::Mat a = cv::Mat::zeros(cv::Size(1920, 1080), CV_8UC3);
glReadPixels(0, 0, 1920, 1080, GL_BGR, GL_UNSIGNED_BYTE, a.data);
Run Code Online (Sandbox Code Playgroud)
2)异步:使用PBO和 glReadPixels
cv::Mat b = cv::Mat::zeros(cv::Size(1920, 1080), CV_8UC3);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo_userImage);
glReadPixels(0, 0, 1920, 1080, GL_BGR, GL_UNSIGNED_BYTE, 0);
unsigned char* ptr = static_cast<unsigned char*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY));
std::copy(ptr, ptr + 1920 * 1080 * 3 * sizeof(unsigned char), b.data);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
Run Code Online (Sandbox Code Playgroud)
根据我在此主题上收集的所有信息,异步版本2)应该更快.但是,比较两个版本的经过时间会产生差异通常是最小的,有时版本1)事件优于PBO变体.
对于性能检查,我插入了以下代码(基于此答案):
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
....
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::cout << "Time difference = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << std::endl;
Run Code Online (Sandbox Code Playgroud)
我在创建PBO时也尝试了使用提示:我GL_DYNAMIC_COPY和GL_STREAM_READ这里之间没有发现太大的区别.
我很高兴有关如何进一步提高帧缓冲区的像素读取操作速度的建议.
您的第二个版本根本不是异步的,因为您在触发副本后立即映射缓冲区.然后地图调用将阻塞,直到缓冲区的内容可用,实际上变为同步.
或者:取决于驱动程序,它会在实际读取时阻止.换句话说,驱动程序可以以导致页面故障和后续同步的方式实现映射.在您的情况下,这并不重要,因为您仍然会立即访问该数据std::copy.
这样做的正确方法是使用同步对象和围栏.
保持您的PBO设置,但在发布glReadPixels到PBO后,将同步对象插入流中glFenceSync.然后,一段时间后,通过调查该栅栏同步对象完成(或者只是等待它)glClientWaitSync.
如果glClientWaitSync返回栅栏前的命令完成,您现在可以从缓冲区读取而无需昂贵的CPU/GPU同步.(如果驱动程序特别愚蠢并且尚未将缓冲区内容移动到可映射的地址,尽管您在PBO上使用了提示,您可以使用另一个线程来执行映射.glGetBufferSubData因此可能更便宜,因为数据没有'需要处于可映射的范围内.)
如果您需要逐帧进行此操作,您会注意到您很可能需要多个PBO,即拥有一小部分PBO.这是因为在下一帧,前一帧数据的回读尚未完成,并且相应的栅栏未发出信号.(是的,这些天GPU大规模流水线化,它们将是你提交队列后面的一些框架).
| 归档时间: |
|
| 查看次数: |
923 次 |
| 最近记录: |