opengl - 绑定(bind) FBO(帧缓冲对象)的成本有多高

标签 opengl

绑定(bind)一个帧缓冲对象有多昂贵,即使用 glBindFramebuffer(GL_FRAMEBUFFER, ...)?我在谷歌上找不到任何关于每帧更改帧缓冲区的实际次数的任何信息。我问是因为我正在编写一个支持 C++ 后期处理的 2D 渲染器,并在每次绘制调用(多边形、线条、图像)开始时显式绑定(bind)帧缓冲区是最优雅的代码路径,但到目前为止我还没有足够的东西可以绘制来对其进行基准测试。在每次绘制调用开始时绑定(bind)帧缓冲区的另一种方法是一直保持绑定(bind),除非我需要绘制到屏幕上,但这将要求我不要将调用交错到两个不同的渲染器实例(这是可行,但有限制)。

我找到了 this question ,但它已经超过四年了(引用的文本已经五年了)并且它并没有完全回答同样的问题。由每帧多次绑定(bind)和解除绑定(bind) FBO 引起的性能下降是否足以保证手动管理绑定(bind)的帧缓冲区?

最佳答案

由于性能特征经常出现这种情况,因此没有简单的答案。它在很大程度上取决于硬件架构、驱动程序优化和使用条件。

首先给您 tl;dr:切换渲染表面可以在相当便宜和非常昂贵之间。我的建议如下:

  • 尝试各种方法,并在您关心的所有平台上对它们进行基准测试。
  • 如果您不能执行选项 1,并且您仍然希望确信您的代码将在各种架构中表现良好,请按渲染目标对渲染进行分组,并避免不必要的切换。

  • 我不愿给出每帧有多少开关是无害的数字。主要是因为我没有它们,而且我不喜欢猜测。因为这取决于很多因素。我从一个通常非常可靠的消息来源得知,至少在一个平台上,每帧仅 2 或 3 个交换机会对性能产生非常重大的负面影响。除了这个非常糟糕的情况,我的直觉会告诉我,我会尽量避免切换超过 10-100 次。但这实际上只是一个猜测,您绝对有可能获得更多,特别是如果您的目标是有限的硬件集。

    您的问题听起来像是涵盖了两种不同的情况。让我分别讨论一下:

    冗余绑定(bind)调用

    从您的描述来看,您似乎部分具有这种使用模式:
    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glDraw...(...);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glDraw...(...);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    

    在这种情况下,您制作 glBindFramebuffer()调用,但您的所有渲染都将转到同一个帧缓冲区。我希望大多数驱动程序检测到这些绑定(bind)调用是多余的,并且不会做任何认真的工作。尽管有时存在关于驾驶员是否应该检测冗余状态变化的哲学争论,但他们大多会这样做。

    在这种情况下,这取决于您对 GPU/驱动程序供应商的信任程度。除非我对它进行了基准测试,否则在这种情况下,我倾向于偏执。如果在我的软件架构中有任何合理的方法可以做到这一点,我会避免冗余调用。

    实际的帧缓冲开关

    正如我在介绍中提到的,这里发生的事情高度依赖 GPU 和驱动程序。只是切换状态以将渲染指向不同的目标很便宜。但还有更多。

    您通常有与事件渲染目标相关联的额外内存分配。典型示例包括用于早期深度测试的缓冲区和压缩的颜色缓冲区。当您切换到不同的渲染目标时,这些分配会发生什么取决于硬件架构、驱动程序实现以及可能的其他条件:
  • 只要有足够的空间,就可以为您循环的所有渲染表面保持这些分配事件,并随着实际渲染目标切换在它们之间切换。在这种情况下,开销很小。
  • 如果这些分配在片上存储器中,则空间可能非常有限。如果没有足够的空间将它们全部保留在芯片上,则旧表面的分配可能会被驱逐到视频内存(如果 GPU 有的话)或系统内存,然后重新加载新表面的分配。这可能相当昂贵。
  • GPU/驱动程序可能不支持驱逐和重新加载这些分配,并且可能必须将它们的内容解析到实际缓冲区(例如,扩展压缩颜色缓冲区的内容,并将其写回全色缓冲区)。这是昂贵的。

  • 平铺架构让事情变得更加有趣,它以各种形式在移动设备上得到了非常广泛的使用。 tiled 架构的主要卖点是每个像素只能运行一次片段着色器,并且每个 tile 只能写入帧缓冲区一次,这减少了写入帧缓冲区的整体内存带宽,也大大提高了这些写入的局部性因为一次写入整个图块。

    据我所知,用于存储将为每个图块渲染的三角形的图块内存通常是片上内存。因此,如果您切换帧缓冲区,则必须:
  • 执行渲染每个tile的整个过程,并将结果写回framebuffer。
  • 将旧表面的切片内存保存到系统内存中,并为新表面加载之前保存的切片内存。

  • 我不知道哪种方法最常用(如果我知道,我可能无法分享)。但是这两种方法听起来都非常昂贵,而且如果过于频繁地使用基于 tile 的架构的整个目的就会落空。

    关于opengl - 绑定(bind) FBO(帧缓冲对象)的成本有多高,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24593276/

    相关文章:

    c++ - OpenGL:如何控制我的窗口将使用哪种缓冲方法?

    c++ - 我找不到此函数中的内存泄漏 (SDL/OpenGL)

    linux - 无法在 Linux(Ubuntu)上使用 glew.h

    macos - 如何让OpenGlView的init函数运行?

    C++ OpenGL 相机运动

    opengl - glBufferData 中的提示标志实际上有什么作用吗?

    c++ - 使用 freeglut 保持纵横比和比例独立于窗口大小

    c++ - 如何在 OpenGL 中绘制单个像素?

    c++ - OpenGL 中的故障面部变形目标动画 (C++)

    c - 我如何链接 GLFW