【问题标题】:Non blocking glReadPixels of depth values with PBO具有 PBO 的深度值的非阻塞 glReadPixels
【发布时间】:2019-09-23 10:48:45
【问题描述】:

我正在从帧缓冲区读取单个像素的深度以实现拾取。最初我的 glReadPixels() 需要很长时间(5ms 左右),而在 nVidia 上它甚至会在这段时间内burn 100% CPU。在 Intel 上它也很慢,但是 CPU 空闲。

从那时起,我使用 PixelBufferObject 功能 PBO 来使 glReadPixels 异步并且还使用 this well known example 进行双缓冲。

这种方法效果很好,让我进行 glReadPixels() 异步调用但前提是我读取 RGBA 值。如果我使用相同的 PBO 方法来读取深度值,则 glReadPixels() 会再次阻塞。

读取 RGBA:glReadPixels() 需要 12µs。

读取深度:glReadPixels() 需要 5ms。

我在 nVidia 和 Intel 驱动程序上试过这个。具有不同的格式/类型组合。我试过了:

glReadPixels( srcx, srcy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, 0 );

和:

glReadPixels( srcx, srcy, 1, 1, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0 );

和:

glReadPixels( srcx, srcy, 1, 1, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, 0 );

这些都不会导致异步 glReadPixels() 调用。但是,如果我通过以下调用读取 RGBA 值:

glReadPixels( srcx, srcy, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0 );

然后 glReadPixels() 立即返回,因此不再阻塞。

在读取单个像素之前,我会这样做:

glReadBuffer( GL_FRONT );
glBindBuffer( GL_PIXEL_PACK_BUFFER, pboid );

我创建了双缓冲 PBO:

glGenBuffers( NUMPBO, pboids );
for ( int i=0; i<NUMPBO; ++i )
{
    const int pboid = pboids[i];
    glBindBuffer( GL_PIXEL_PACK_BUFFER, pboid );
    glBufferData( GL_PIXEL_PACK_BUFFER, DATA_SIZE, 0, GL_STREAM_READ );
    ...

我使用深度大小为 24、模板大小为 8 和默认双缓冲区的 SDL2 创建帧缓冲区。

我在 Ubuntu LTS 上使用 OpenGL Core Profile 3.3。

【问题讨论】:

  • 深度缓冲区的实际格式是什么?如果渲染到具有特定 OpenGL 图像格式的 FBO 会发生什么?
  • @nicol 我怎么知道?我可以查询吗?我从 SDL2 请求 24b 深度和 8b 模板,所以可能期待 GL_UNSIGNED_INT_24_8?
  • "在读取单个像素之前,我会做glReadBuffer( GL_FRONT );" 那只影响颜色缓冲区,深度缓冲区没有双缓冲,所以它实际上可以解释差异。很难说到底发生了什么,因为渲染循环的其余部分是未知的,但即使在 PBO 代码路径中也有可能强制同步(可能只是使用一些以前的帧而不是最近的帧)。
  • @derhass 我实际上并没有读取像素深度(通过 glMapBuffer)直到下一帧,所以没有同步进行。 glReadPixel 应该触发了异步操作并立即返回(就像 RGBA 一样)。但它没有,为了阅读深度。
  • 好吧,对于 Intel 案例,mesa 可以很好地告诉您发生了什么(使用调试回调!):intel_texsubimage_blorp: GL_DEPTH_COMPONENT not supported; intelReadPixels: fallback to CPU mapping in PBO case; CPU mapping a busy "isl-miptree" BO stalled and took 33.192 ms.。这将引导您到this bug report。英伟达是做什么的,我不知道。

标签: opengl depth-buffer glreadpixels pbo


【解决方案1】:

直到下一帧我才真正读取像素深度(通过 glMapBuffer),因此没有同步进行。 glReadPixel 应该触发了异步操作并立即返回(就像 RGBA 一样)。但它没有,为了阅读深度。

这需要有两个深度缓冲区。但是没有。多缓冲是指颜色缓冲区的数量,因为这些是实际显示的内容。实现几乎不会为您提供多个深度缓冲区。

为了服务从深度缓冲区读取,读取必须在“下一帧”发生之前发生。所以需要同步。

一般来说,最好从您自己的图像中读取。这样,您就可以完全控制格式、重用时间等内容,从而控制同步问题。如果您需要两个深度缓冲区,以便在使用另一个时可以读取其中一个,那么您需要创建它。

仅供参考:由于pixel ownership issues 等原因,从默认帧缓冲区中读取数据是可疑的。但是从 front 缓冲区读取几乎总是错误的。

【讨论】:

  • 谢谢。这是有道理的,但我不同意一件事:“所以需要同步。” 是的,但不是 glReadPixels() 调用,OpenGL 不知道 何时 我将访问 PBO。这种同步最多可以在 glMapBuffer() 调用中强制执行。但我观察到 glReadPixels() 调用中的同步。我正在考虑将 8 位深度写入 ALPHA 通道并改用它,因为读取异步帧缓冲区深度似乎是不可能的?
  • @Bram:停止从默认帧缓冲区读取。这就是答案。只是停止这样做。我不知道为什么您宁愿使用 8 位来完全破坏深度缓冲区,而不是像其他人一样创建 FBO。 “这种同步最多可以在 glMapBuffer() 调用中强制执行。” 不,它必须在此之前。您自己说过您将在地图调用之前渲染一帧。由于该渲染将覆盖深度缓冲区,因此必须在任何覆盖发生之前进行读取。
  • 谢谢。我将改为研究离屏渲染。好的,同步会在 mapbuffer 之前发生,但也会在 readpixels 之后发生,而不是在 readpixels 中。可能在呈现帧缓冲区时?关于 alpha 的注意事项:我仍然会使用正常的 24b 深度进行渲染,但片段着色器只是将片段的 z 复制到 alpha 通道中以进行选择。 z 测试的高精度深度,拾取的低精度副本。
猜你喜欢
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-24
  • 2011-03-03
  • 1970-01-01
  • 2021-12-29
  • 2011-03-20
相关资源
最近更新 更多