【问题标题】:Why is glReadPixels so slow and are there any alternative?为什么 glReadPixels 这么慢,还有其他选择吗?
【发布时间】:2016-10-02 21:23:45
【问题描述】:

我需要在每一帧拍摄截图,并且我需要非常高的性能(我正在使用 freeGlut)。我发现可以在glutIdleFunc(thisCallbackFunction)内部这样完成

GLubyte *data = (GLubyte *)malloc(3 * m_screenWidth * m_screenHeight);
glReadPixels(0, 0, m_screenWidth, m_screenHeight, GL_RGB, GL_UNSIGNED_BYTE, data);
// and I can access pixel values like this: data[3*(x*512 + y) + color] or whatever

它确实有效,但我有一个很大的问题,它真的很慢。当我的窗口为 512x512 时,它的运行速度不超过每秒 90 帧,而仅渲染立方体时,如果没有这两条线,它的运行速度为 6500 FPS!如果我们将其与 irrlicht 图形引擎进行比较,我可以做到这一点

// irrlicht code
video::IImage *screenShot = driver->createScreenShot();
const uint8_t *data = (uint8_t*)screenShot->lock();
// I can access pixel values from data in a similar manner here

即使加载了巨大的网格(Quake 3 地图),512x512 窗口也能以 400 FPS 的速度运行!考虑到我在 irrlicht 中使用 openGL 作为驱动程序。在我没有经验的眼中,glReadPixels 似乎将每个像素数据从一个地方复制到另一个地方,而(uint8_t*)screenShot->lock() 只是复制一个指向已经存在的数组的指针。我可以使用 freeGlut 做类似于后者的事情吗?我希望它比 irrlicht 更快。

请注意,irrlicht 也使用 openGL(它也提供了 directX 和其他选项,但在我上面给出的示例中,我使用了 openGL,顺便说一下,与其他选项相比,它是最快的)

【问题讨论】:

  • 查找像素缓冲区对象,让您异步执行。
  • 如果您将该代码放在空闲函数中,它可能每帧调用不止一次。此外,如果你从不释放你分配的数据,你就会泄漏内存。
  • @RetoKoradi 好吧,不,我不会在每次调用 idle 时都执行这些行。 data 在构造函数中只分配一次,我一遍又一遍地使用它。现在我正在研究 PBO....

标签: c++ performance opengl screenshot freeglut


【解决方案1】:

OpenGL 方法用于管理渲染管道。本质上,当图形卡向观看者显示图像时,正在完成下一帧的计算。当您拨打glReadPixels时;显卡等待当前帧完成,读取像素,然后开始计算下一帧。因此流水线变得停滞并变成连续的。

如果你能容纳两个缓冲区并告诉显卡将数据读入这些缓冲区,交换每一帧;然后您可以延迟 1 帧从缓冲区中回读,但不会停止流水线。这称为双缓冲。您还可以使用 2 帧延迟回读等进行三重缓冲。

这里有一个比较老的网页描述这个现象和实现:http://www.songho.ca/opengl/gl_pbo.html

还有很多关于帧缓冲和渲染到网络上的纹理的教程。其中之一在这里:http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/

【讨论】:

  • 所以,我想出了如何渲染到纹理,但我不知道如何将此纹理移动到主缓冲区以便它显示在屏幕上,最重要的是如何访问像素值从这个纹理使用索引(如我在问题中所示)。我该怎么办?
  • 您可以通过将后台缓冲区绑定为DRAW_BUFFER 并将纹理帧缓冲区绑定为READ_BUFFER 先验来使用glBlitFramebuffer。这是为了将图像放到屏幕上。要读取像素值,您应该使用像素缓冲区对象(又名 PBO)。
  • 我确实设法将图像从帧缓冲区对象(我渲染场景的位置)移动到主缓冲区,以便它显示在屏幕上,但我仍然无法访问像素值。我不太明白 PBO 应该放在哪里。本教程 (songho.ca/opengl/gl_pbo.html) 对我来说似乎不太容易理解,源代码有 600 行长,看起来非常复杂。我该怎么办?专门问另一个问题?我什至不知道具体要问什么。任何建议将不胜感激。
  • 遗憾的是我没有可用的示例代码。 songho.ca/opengl/gl_pbo.html 背后的基本思想是您为 GL_PIXEL_PACK_BUFFER 绑定 2 个 PBO 之一,并且您的 glReadPixels 在客户端(您的代码)上变为即时,但如果之前对缓冲区的操作已完成,则您只能从缓冲区读取服务器端(显卡)。因此,您几乎可以立即阅读前一帧。
  • 顺便说一句,我使用的是集成GPU(Intel HD graphics 4000),据我所知CPU和GPU共享相同的内存,为什么我需要下载它?为什么无法得到指针?
猜你喜欢
  • 2013-03-27
  • 2011-12-19
  • 1970-01-01
  • 2011-01-18
  • 2017-01-25
  • 1970-01-01
  • 2012-03-28
  • 2011-03-30
  • 2021-10-31
相关资源
最近更新 更多