如何在不同的线程上调用 glReadPixels?

Posted

技术标签:

【中文标题】如何在不同的线程上调用 glReadPixels?【英文标题】:How to call glReadPixels on different thread? 【发布时间】:2011-09-09 13:33:21 【问题描述】:

当我在另一个线程上调用 glReadPixels 时,它不会返回任何数据。我在某处读到建议我需要在调用线程中创建一个新上下文并复制内存。我该怎么做?

这是我使用的 glReadPixels 代码:

pixels = new BYTE[ 3 * width * height];
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
image = FreeImage_ConvertFromRawBits(pixels, width, height, 3 * width, 24, 0xFF0000, 0x00FF00, 0x0000FF, false);
FreeImage_Save(FIF_PNG, image, pngpath.c_str() , 0);

另外,我从thread 中读到他们建议使用另一段代码(见最后),但我不明白什么是 origX、origY、srcOrigX、srcOrigY?

【问题讨论】:

【参考方案1】:

您可以创建共享上下文,这将按您的预期工作。请参阅wglShareLists(名称选择不当,它共享的不仅仅是列表)。或者,使用WGL_ARB_create_context,它也直接支持共享上下文(您已将问题标记为“windows”,但非 WGL 也存在类似的功能)。

但是,使用像素缓冲区对象要容易得多,它与多线程具有相同的净效果(传输将异步运行而不会阻塞渲染线程),而且它的复杂性要低很多倍。

【讨论】:

【参考方案2】:

你有不同的选择。

您调用 ReadPixel 与渲染线程进行流水线化。在这种情况下,返回的数据应存储在缓冲区中,该缓冲区可以排队到专用于保存图片的线程中。这可以通过缓冲区队列、互斥体和信号量轻松完成:渲染线程使用 ReadPixel 获取数据,锁定互斥体,将(系统内存)像素缓冲区入队,解锁互斥体,增加信号量;工作线程(锁定在信号量上)将由渲染线程发出信号,锁定互斥体,使像素缓冲区出列,解锁互斥体并保存图像。

否则,您可以将当前帧缓冲区复制到纹理或像素缓冲区对象上。在这种情况下,您必须有两个不同的线程,每个线程都有一个当前的 OpenGL 上下文(通过 MakeCurrent),彼此共享它们的对象空间(如 user771921 所建议的那样)。当第一个渲染线程调用ReadPixels(或CopyPixels)时,通知第二个线程该操作(例如使用信号量);第二个渲染线程将映射像素缓冲区对象(或获取纹理数据)。 这种方法的优点是允许驱动程序将第一个线程读取操作流水线化,但它实际上通过引入额外的支持缓冲区使内存复制操作加倍。此外,当第二个线程映射缓冲区时,ReadPixel 操作被刷新,该缓冲区在第二个线程发出信号后立即执行(很可能)。

我会建议第一个选项,因为它更清洁和简单。第二个过于复杂,我怀疑你能从使用它获得好处:图像保存操作比 ReadPixel 慢很多。

即使 ReadPixel 没有流水线化,您的 FPS 真的会变慢吗?在进行分析之前不要进行优化。

您链接的示例使用与 OpenGL 无关的 GDI 函数。我认为代码会导致 repaint 表单事件,然后捕获窗口客户区内容。与 ReadPixel 相比,它似乎要慢得多,即使我实际上并未对此问题执行任何分析。

【讨论】:

【参考方案3】:

好吧,在多线程程序中使用 opengl 是个坏主意 - 特别是如果您在没有创建上下文的线程中使用 opengl 函数。

除此之外,您的代码示例没有任何问题。

【讨论】:

谢谢。你会建议我怎么做? @huy 最简单的方法是从您创建 opengl 上下文的线程中读取像素。最好不要在使用 opengl 库的程序中拥有超过 1 个线程。 @JVo 谢谢我去看看。 @VJo:对使用 OpenGL 的程序进行多线程处理并没有错。应该只将所有 OpenGL 操作保留在一个线程中。 @datewolf 否。实际上每个线程可以有一个当前 OpenGL 上下文,而不是每个进程(来自 MSDN:“一个进程可以通过多线程有多个渲染上下文。一个线程必须设置一个当前渲染调用任何 OpenGL 函数之前的上下文。否则,所有 OpenGL 调用都将被忽略。")。

以上是关于如何在不同的线程上调用 glReadPixels?的主要内容,如果未能解决你的问题,请参考以下文章

在 Opengl ES 中在 FBO 上调用多个 glReadPixels 和 glDrawArrays

PixelBuffer对象和Android上的glReadPixel(ARCore)阻塞

将 WriteableBitmap 线程化到图像控件

glReadPixels 为空时如何生成屏幕截图?

glReadPixels 函数返回错误 1282 (GL_INVALID_OPERATION)

使用 OpenCV 处理来自 glReadPixels 的图像并将其作为纹理返回