OpenGL glReadPixels 性能

Posted

技术标签:

【中文标题】OpenGL glReadPixels 性能【英文标题】:OpenGL glReadPixels Performance 【发布时间】:2015-09-30 03:59:42 【问题描述】:

我正在尝试为 HDR 色调映射实现自动曝光,并且我正在尝试降低查找场景平均亮度的成本,我似乎遇到了 glReadPixels 的瓶颈。这是我的设置:

1:我创建了一个下采样的 FBO,以减少在使用 glReadPixels 时仅使用 GL_RED 值和 GL_BYTE 格式的读取成本。

private void CreateDownSampleExposure() 
        DownFrameBuffer = glGenFramebuffers();
        DownTexture = GL11.glGenTextures();
        glBindFramebuffer(GL_FRAMEBUFFER, DownFrameBuffer);
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, DownTexture);
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RED, 1600/8, 1200/8,
                0, GL11.GL_RED, GL11.GL_BYTE, (ByteBuffer) null);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                GL11.GL_TEXTURE_2D, DownTexture, 0);
        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 
            System.err.println("error");
         else 
            System.err.println("success");
        
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

    

2:设置ByteBuffers并读取FBO贴图的贴图。

Setup()
byte[] testByte = new byte[1600/8*1000/8];
ByteBuffer testByteBuffer = BufferUtils.createByteBuffer(testByte.length);
testByteBuffer.put(testByte);
testByteBuffer.flip();

MainLoop()
  //Render scene and store result into downSampledFBO texture

   GL11.glBindTexture(GL11.GL_TEXTURE_2D, DeferredFBO.getDownTexture());

  //GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RED, GL11.GL_BYTE,       
  //testByteBuffer); <- This is slower than readPixels. 

  GL11.glReadPixels(0, 0, DisplayManager.Width/8, DisplayManager.Height/8, 
  GL11.GL_RED, GL11.GL_BYTE, testByteBuffer);
  int x = 0;

  for(int i = 0; i <testByteBuffer.capacity(); i++)
        x+= testByteBuffer.get(i);
        
    System.out.println(x); <-Print out accumulated value of brightness. 
    
  //Adjust exposure depending on brightness. 

问题是,我可以将我的 FBO 纹理降低 100 倍,所以我的glReadPixels读取只有 16x10 像素,几乎没有性能提升。没有下采样可以显着提高性能,但是一旦我将宽度和高度除以 8,它似乎就会下降。似乎仅调用此函数就有如此巨大的开销。致电glReadPixels 时是否有我做错或没有考虑的事情?

【问题讨论】:

显然 readPixels 很慢,并且来自 GameDev 上的 Darth Melkor “您读取的像素数量与摊位无关紧要,即使在 1x1 区域和最快的现代硬件上,您仍然可以获得- 最好使用不涉及回读的方法”。我想我会尝试找到更好更有效的方法。我会试试像素缓冲区对象。 【参考方案1】:

glReadPixels 很慢,因为 CPU 必须等到 GPU 完成所有渲染后才能为您提供结果。可怕的同步点。

使 glReadPixels 快速的一种方法是使用某种双/三缓冲方案,以便您只在您期望 GPU 已经完成的渲染到纹理上调用 glReadPixels。仅当在您的应用程序中可以接受之前等待几帧接收 glReadPixels 的结果时,这才是可行的。例如,在视频游戏中,延迟可以被证明是模拟学生对照明条件变化的响应时间。

但是,对于您的特定色调映射示例,您可能只想计算平均亮度,以便将该信息反馈回 GPU 以进行另一次渲染。而不是 glReadPixels,通过将图像复制到具有线性过滤(盒式过滤器)的连续一半大小的渲染目标来计算平均值,直到您下降到 1x1 目标。

该 1x1 目标现在是包含平均亮度的纹理,并且可以在色调映射渲染过程中使用该纹理。没有同步点。

【讨论】:

以上是关于OpenGL glReadPixels 性能的主要内容,如果未能解决你的问题,请参考以下文章

opengl glReadPixels

CGContextRef 作为 OpenGL 上下文

glReadPixels 的 OpenGL DSA 等效项

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

OpenGL ES glReadPixels exc_bad_access

glReadPixels 保存到 BMP - 颜色失真