OpenGL快速读回缓冲区

时间:2015-06-08 03:53:46

标签: c++ opengl hook

我试图将后台缓冲区的内容读入我自己的缓冲区。 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);
}

因此,每次应用程序调用{​​{1}}时,我都会在交换之前读取后台缓冲区。

如何快速阅读后台缓冲区?我在上面遗漏了什么?

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

任何其他方式,我最终将后台缓冲区复制到我的内存块中,而且速度很慢。

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

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

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调用一起执行,因此它将包含您发出的所有绘制调用的结果。

次要术语点:您在此处所询问的内容未被称为&#34; PBuffer&#34;。全名是&#34; Pixel Buffer Object&#34;,通常以其简短形式使用&#34; PBO&#34;。 PBuffer是完全不同的东西。这是一种旧的屏幕外渲染机制,幸好这些日子已经过时了。

答案 1 :(得分:0)

  

有什么想法吗?

你不要滥用主帧缓冲区来做你不应该做的事情(渲染到窗口帧缓冲区并从中读取),而是使用帧缓冲区对象和渲染缓冲区来渲染。您仍然必须使用glReadPixels,但由于您使用的是屏幕外表面,因此您将避免与窗口系统进行所有同步。仍然建议使用PBO进行数据传输,因为它为OpenGL实现提供了更多调度操作的自由度。我建议如下:

  1. 渲染到FBO渲染缓冲区
  2. glReadPixels从renderbuffer到GL_PIXEL_PACK_BUFFER PBO
  3. 将渲染缓冲区blit到主帧缓冲区
  4. SwapBuffers
  5. 从PBO中检索数据
  6. 这种安排和操作顺序为OpenGL实现提供了足够的余地,可以异步地重叠那里发生的一些操作,而不会产生一些停顿同步点。例如,glReadPixels和将渲染缓冲区blitting到主帧缓冲区不会相互干扰(两者都只从渲染缓冲区读取)。 OpenGL驱动程序可以重新排列glReadPixels,以便在blit之后或同时执行。实际上,您可以交换2和3,在某些实现中,这可能会产生更好的性能。哎呀,你可以在4点之后移动2,但是你会松开一些重新排序自由的操作。