OpenGL读回缓冲区快

OpenGL reading back buffer quickly

本文关键字:缓冲区 OpenGL      更新时间:2023-10-16

我正试图将后缓冲区的内容读取到我自己的缓冲区中。glReadPixels本身太慢了,使我的FPS从50下降到30。

所以我决定尝试使用PBuffer进行"异步"读取,但它崩溃了。

我的代码如下:

如果缓冲区不存在,创建它们。否则,将返回缓冲区读入指定的内存位置:

static int readIndex = 0;
static int writeIndex = 1;
static GLuint pbo[2] = {0};

void FastCaptureBackBuffer()
{
    //Create PBOs:
    if (!initBuffers)
    {
        initBuffers = true;
        glGenBuffers(2, pbo);
        glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[0]);
        glBufferData(GL_PIXEL_PACK_BUFFER, width * height * 1.0f, 0, GL_STREAM_READ);
        glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[1]);
        glBufferData(GL_PIXEL_PACK_BUFFER, width * height * 1.0f, 0, GL_STREAM_READ);
        glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    }
    //swap read and write.
    writeIndex = (writeIndex + 1) % 2;
    readIndex = (writeIndex + 1) % 2;
    //read back-buffer.
    glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[writeIndex]);
    glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);
    glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[readIndex]);
    void* data = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
    if (data)
    {
        memcpy(myBuffer, data, width * height * 4);
        data = nullptr;
        glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
    }
    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}

然后我做:

BOOL __stdcall HookSwapBuffers(HDC DC)
{
    FastCaptureBackBufferPBO();
    return CallFunction<BOOL>(GetOriginalAddress(353), DC);
}

所以每次应用程序调用wglSwapBuffers时,我在交换之前读取回缓冲区。

如何快速读取回缓冲区?我在上面遗漏了什么?

理想情况下,我想:指定一个游戏可以直接渲染的指针,而不是屏幕,然后我可以手动渲染内存的内容。

任何其他方式,我最终复制回缓冲区到我的内存块,它很慢。

任何想法?

您没有在缓冲区中保留足够的内存:

glBufferData(GL_PIXEL_PACK_BUFFER, width * height * 1.0f, 0, GL_STREAM_READ);

由于您使用GL_RGBA作为格式,您将需要每像素4字节,这也匹配您在memcpy()调用中使用的内容:

memcpy(myBuffer, data, width * height * 4);

所以glBufferData()调用应该是:

glBufferData(GL_PIXEL_PACK_BUFFER, width * height * 4, 0, GL_STREAM_READ);

此外,从你的问题中并不完全清楚你为什么使用HookSwapBuffers()。我相信人们使用它来拦截SwapBuffers()调用,如果他们没有源代码。如果您想在自己的代码中捕获渲染,您可以在完成渲染帧后立即调用glReadPixels()。它将与所有其他OpenGL调用一起按顺序执行,因此它将包含您发出的所有draw调用的结果。

小术语点:你在这里问的不叫"PBuffer"。它的全称是"像素缓冲对象",通常简称为"PBO"。PBuffer是完全不同的东西。

这是一种用于屏幕外渲染的旧机制,值得庆幸的是,现在几乎已经过时了。

任何想法?

如果你不滥用主framebuffer你不应该做的事情(渲染到窗口framebuffer并从中读取),而是使用framebuffer对象和renderbuffer来渲染。您仍然必须使用glReadPixels,但由于您使用的是离屏幕表面,因此可以避免与窗口系统的所有同步。使用PBO进行数据传输仍然是值得推荐的,因为它使OpenGL实现在调度操作方面更自由。我的建议如下:

  1. 渲染到FBO渲染缓冲区
  2. glReadPixels from renderbuffer到GL_PIXEL_PACK_BUFFER PBO
  3. 将渲染缓冲区Blit到主帧缓冲区
  4. SwapBuffers
  5. 从PBO中检索数据

这种操作的安排和顺序给了OpenGL实现足够的余地来异步重叠一些发生在那里的操作,而不会强加一些延迟同步点。例如,glReadPixels和将renderbuffer置位到主framebuffer不会相互干扰(都只从renderbuffer读取)。OpenGL驱动程序可能会重新安排glReadPixels在blit之后执行,或者同时执行。实际上,您可能会交换2和3,在某些实现中,这可能会产生更好的性能。哎呀,你甚至可以在4之后移动2,但这样你就失去了一些操作重新排序的自由。