【问题标题】:Understanding binding/activating texture performance penalty in OpenGL了解 OpenGL 中的绑定/激活纹理性能损失
【发布时间】:2017-02-25 00:42:33
【问题描述】:

我不确定我所做的是否是最佳的。

我有三个纹理(其中两个是堆叠有大量信息的数组纹理),我做了三个渲染通道:

  • 具有纹理 1 的关卡几何体

  • 具有纹理 2 的世界实体

  • 具有纹理 3 的 HUD 内容

因此,我的代码通常会执行类似这样的伪代码:

bindTexture(tex1);
drawLevel();
unbindTexture(tex1);  // Is this necessary if I just bind after anyways?

bindTexture(tex2);
drawEntities();
unbindTexture(tex2);

bindTexture(tex3);
drawHUD();
unbindTexture(tex3);

因为我记得看到在 GL_TEXTURE0、GL_TEXTURE1 等上最多允许 16 个(或更多)单独的纹理,这是否意味着我可以将我的世界纹理写入 GL_TEXTURE0,将实体写入 GL_TEXTURE1,将 HUD 写入 GL_TEXTURE2 和没有性能损失,或者很少? 或者改变活动纹理是否会带来明显的损失?

我问是因为我记得看到纹理交换是一项繁重的操作。这是否意味着即使我改变了我的活动纹理,它仍然很费力?还是仅当您拥有超过 16 个左右的纹理并不断将它们推送到 GPU 时才会产生负担?

这是错误的还是慢的?

我只需要 3 个纹理,所以如果有最优化的方法,我会很高兴学习它。

假设我使用的是 core 3.3 而不是 4.5(但如果在 4.2 或更高版本中有更好的方法,我也很乐意听到这些)。它也应该适用于 nvidia 和 AMD 或过去 4-5 年发布的任何 GPU。

编辑:如果您发现我所说的任何奇怪的概念是错误的,请纠正我,因为我是新手。出于这个原因,我更希望有人在他们的回答中冗长。

【问题讨论】:

  • This blog link 关于glActiveTexture 的性能注意事项可能也会有所帮助。

标签: c++ opengl


【解决方案1】:

我认为关于 OpenGL 中的活动纹理存在少量混淆。 OpenGL 允许您同时拥有多达 16 个活动纹理(编辑:每个着色器阶段)。因此,您可以一次拥有最多 16 个纹理的单个绘制调用引用(并且某些实现允许超过 16 个)。

您可以在程序中创建和激活的纹理数量没有限制(物理 VRAM 限制除外)。更重要的是,大多数 OpenGL 驱动程序都围绕您将为每个绘制调用绑定一个新纹理(或可能多个)的假设进行优化。

更改活动纹理是一个相对昂贵的过程,但不是在您使用它的环境中。例如,如果每个实体都有多个要引用的纹理,并且每个实体都是一个很小的对象,否则就不会花费太多精力来渲染,这将很重要。当您实际上将所有纹理捆绑到 3 个“巨型纹理”中并且仅在从一组对象移动到下一组对象时切换时,它的重要性要小得多。在这种情况下,切换纹理的成本是微不足道的,不值得关注。

我会说这看起来有点像过早优化的情况,因为努力将所有内容捆绑在一起可能代表工作负载占用量可能超过您从最终结果中获得的收益,但这是一个决心你必须自己做。

但是,TL;DR:您的代码在功能或性能方面没有任何问题。

【讨论】:

  • "OpenGL 允许您同时拥有多达 16 个活动纹理。" 不,OpenGL 允许您拥有更多。
  • @NicolBolas 大多数 OpenGL 实现允许您拥有更多,但 OpenGL 规范本身仅保证最多可以使用 16 个在单个着色器调用中。这是一个重要的区别。
  • 但是您没有说“在单个着色器调用中”。你说“同时”。 OpenGL 3.3要求实现允许您每阶段有 16 个纹理,这意味着在 3.3 中“同时”最多有 48 个纹理。
  • @NicolBolas 我的“Active Texture”措辞暗示了这一点。我不知道你可以在阶段之间绑定不同的纹理,所以如果“有效”限制高于 16,那么我的答案可能值得修正。