OpenGL:从许多帧缓冲区纹理中读取稀疏像素数据的有效方法?

OpenGL: efficient way to read sparce pixel data from many framebuffer textures?

我正在编写一个使用 GPU 计算内容的程序,我想从帧缓冲区读取数据以用于我的客户端代码。我使用的帧缓冲区大约有 40 个纹理,大小均为 1024x1024,所有这些都包含需要读取的数据,但非常少,例如每个纹理的任意 x/y 坐标中的 50 左右像素。对每个纹理、每个帧使用 glReadPixels 对我来说成本太高了……

我只需要从每个纹理中读取几个 select 像素,有没有一种方法可以快速收集它们的数据而不需要从 GPU 下载每个完整的纹理?

如果您需要将数据从 GPU 复制到 CPU 内存,没有办法(据我所知)使用 glReadPixels。

根据您使用的平台和程序的具体情况,您可以使用 FBO 尝试多种优化:

  • 假设您知道像素的位置,只复制纹理的一部分。请注意,在大多数情况下,复制整个纹理而不是发出几个小读取仍然更快

  • 如果不需要 32 位纹理,可以渲染到较低的颜色分辨率。具体要看你的平台扩展了。

  • 也许您真的不需要复制像素,因为您打算将它们用作下一阶段的纹理输入?在这种情况下,您可以使用 glCopyTexImage2D

  • 直接在 GPU 上复制像素

不管你怎么切,这听起来都相当昂贵。我想到了几种方法:

  • 我首先要尝试的是 glReadPixels(),但要使用 PBO。绑定一个足够大的缓冲区以将所有像素保存到 GL_PIXEL_PACK_BUFFER 目标,然后提交 glReadPixels() 调用,并使用偏移量将结果放置在缓冲区的不同部分。然后调用 glMapBufferRange() 读回值。

  • 另一种方法是将要读取的所有像素复制到单个纹理中。您可以使用 glBlitFramebuffer()glCopyTexSubImage2D()。然后使用单个 glReadPixels()glGetTexImage() 调用从该纹理中获取所有数据。

这两种方法的工作量和同步开销应该大致相同。但其中一个可能更有效,具体取决于驱动程序中的哪些路径得到了更好的优化。

正如前面的回答所建议的那样,我会非常确定您确实需要它,并且没有任何方法可以在 GPU 上保存和处理数据。任何时候回读数据,都会在 GPU 和 CPU 之间引入同步,这对性能最有害。

您对可以使用的 OpenGL 版本有任何限制吗?如果没有,听起来您应该研究一下计算着色器。你说你在计算数据,所以我假设你是 "abusing" 应用程序的渲染管道,尤其是片段着色器,并将片段数据存储在被解释为颜色以外的其他东西的帧缓冲区中。

如果是这种情况,那么您只需要一个着色器存储缓冲区和一个原子计数器。在某个时候,您正在决定片段(x,y,z [z 是纹理索引])应该具有值 v。因此在您的计算着色器中,您可以像在片段着色器中一样进行计算,但作为输出,您存储一个元组 (x, y, z, v)。您将此元组存储在着色器存储缓冲区中原子计数器的索引处,该计数器在每个写入元素后递增。最后,您将数据紧凑地存储在缓冲区中,只需要读回这些元素。确切的数字是原子计数器在终止后保持的值。使用 glGetBufferSubData 将缓冲区下载到位置值对数组中,遍历它并施展你的 CPU 魔法。