复制纹理到纹理的最佳方法

27

如何从一张纹理复制像素到另一张纹理?

我找到了一些方法。 例如,有一个方法 glCopyImageSubData(),但我的目标版本是OpenGL 2.1,所以我不能使用它。 此外,由于性能非常重要, glGetTexImage2D()不是一个选择。 由于我正在将视频帧处理为纹理,因此我必须每秒进行30-60次复制。

我发现的可用选项如下:

  1. 为源纹理创建FBO,并使用 glCopyTexSubImage2D()将其复制到目标纹理。
  2. 为源纹理和目标纹理创建FBO,并将FBO合并。
  3. 为目标纹理创建FBO,并将源纹理渲染到FBO中。

您可以忽略创建FBO的成本,因为FBO只会被创建一次。

请不要只回答“这取决于情况,请自行测试”之类的内容。 我不仅针对一种GPU。如果情况有所不同,请告诉我依据是什么。

此外,由于很难测量OpenGL调用的时间,因此我想知道的不是定量结果。我需要一些关于哪种方法应该避免的建议。

如果您知道更好的复制纹理的方法,请让我也知道。

感谢您的阅读。


我认为考虑到GL2.1的限制(FBO在那里不是核心功能,但可能作为扩展广泛可用),除了你列出的选项,没有其他好的选择。我还认为所有三个选项之间的性能差异将非常小。 - derhass
在现代OpenGL中,计时GL调用实际上非常容易。如果你的驱动程序支持计时器查询(大多数2.1实现不支持),则可以在命令队列中插入一个查询,在命令之前完成查询,并在命令之后结束查询。等待几帧以获取该查询的结果,您将获得一种非常准确的测量所需时间的方法。虽然不能在旧的驱动程序上使用它,但是如果您只想要一个对比每个调用之间性能差异的概括,这很有帮助。 - Andon M. Coleman
如果你想立即获取计时器查询的结果,那么这将会强制进行管线停顿。因此,这取决于你长期以来使用计时器查询的目的是什么。如果你想在运行时对性能进行测量,并且对运行时性能的影响最小,请在结果可用后再查询结果。如果你想立即知道命令的成本,请在结果可用之前获取结果(换句话说,强制进行CPU/GPU同步)。在你的情况下,我认为第二种方法可以很好地解决问题。 - Andon M. Coleman
@derhass 很高兴知道我找到了所有的方法!谢谢。 - slyx
@AndonM.Coleman 我不知道这一点。事实上,我正在使用Qt,而Qt已经为此提供了一个包装类!我现在正在测试它。稍后我会更新结果。谢谢。 - slyx
2个回答

38

由于我不知道定时器查询,所以我没有考虑基准测试。 现在,我可以进行自己的基准测试。 我已经测量了每100次操作的时间,并重复了五次。 创建FBO的成本未包括在内。

- S=source texture, D=destination texture, SF=FBO of S, DF=FBO of D
- operation=copying texture to texture
- op/s = how many operations for one second(average), larger is better
  1. 使用简单的passthrough着色器创建DF并将S渲染到DF中

    • 945.656op/s(100次运算用时105.747ms)
    • 947.293op/s(100次运算用时105.564ms)
    • 949.099op/s(100次运算用时105.363ms)
    • 949.324op/s(100次运算用时105.338ms)
    • 948.215op/s(100次运算用时105.461ms)
  2. 创建SF并使用glCopyTexSubImage2D()复制到D上

    • 937.263op/s(100次运算用时106.694ms)
    • 940.941op/s(100次运算用时106.277ms)
    • 941.722op/s(100次运算用时106.188ms)
    • 941.145op/s(100次运算用时106.254ms)
    • 940.997op/s(100次运算用时106.270ms)
  3. 创建DF和SF并使用glBlitFramebuffer()

    • 828.172op/s(100次运算用时120.748ms)
    • 843.612op/s(100次运算用时118.538ms)
    • 845.377op/s(100次运算用时118.290ms)
    • 847.024op/s(100次运算用时118.060ms)
    • 843.303op/s(100次运算用时118.581ms)
  4. 创建DF和SF并使用glCopyPixels()

    • 525.711op/s(100次运算用时190.219ms)
    • 523.396op/s(100次运算用时191.060ms)
    • 537.605op/s(100次运算用时186.010ms)
    • 538.560op/s(100次运算用时185.680ms)
    • 553.059op/s(100次运算用时180.813ms)

性能比较

passthrough shader ~ glCopyTexSubImage2D > glBlitFramebuffer >> glCopyPixels

因此,简单的直通着色器表现最好,可以复制纹理。 glCopyTexSubImage2D略慢于直通着色器。 fbo-blitting足够快,但比着色器和glCopyTexSubImage2D差。 我没有期望到好结果的glCopyPixels表现最差,符合我的预期。


4
复制深度附件怎么样? - Tara
你使用哪种硬件? - Tomilov Anatoliy

2
我们最终选择了将四边形渲染到目标中;当使用最小化着色器、低精度等性能相近的不同使用GPU进行复制的方法时,该方法提供了最大的灵活性。
然而,如果您可以找到一种避免仅复制全部内容的操作的方法 - 如果您可以将一个改变其中一个副本的操作转换为在一个步骤中读取原始数据、应用改变并生成新副本的操作 - 那么这种方法肯定会更快。

1
我不确定我理解了你的回答。在第一段中,我可以认为你的意思是使用最小着色器的第三个选项最灵活、易于使用? - slyx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接