13

选择顶点缓冲区类型有哪些常见准则?我们什么时候应该对顶点数据使用交错缓冲区,什么时候应该使用单独的缓冲区?我们什么时候应该使用索引数组,什么时候直接使用顶点数据?

我正在寻找一些常见的准则-我在某些情况下,其中一个或相反的情况更合适,但并非所有情况都可以轻松解决。以性能为目标时,在选择顶点缓冲区格式时应该注意什么?

也欢迎链接到有关该主题的网络资源。

4

2 回答 2

17

首先,您可以在 OpenGL wiki 上找到一些有用的信息。其次,如果有疑问,配置文件,有一些关于这个的经验法则,但经验可能会因数据集、硬件、驱动程序等而异。

索引与直接渲染

我几乎总是默认使用顶点缓冲区的索引方法。主要原因是所谓的转换后缓存。它是在图形管道的顶点处理阶段之后保留的缓存。本质上,这意味着如果您多次使用一个顶点,您很有可能会命中该缓存并能够跳过顶点计算。有一个条件甚至可以命中此缓存,那就是您需要使用索引缓冲区,没有它们它将无法工作,因为索引是此缓存键的一部分。

此外,您可能会节省存储空间,索引可以尽可能小(1 字节,2 字节),并且您可以重用完整的顶点规范。假设一个顶点和所有属性总共有大约 30 个字节的数据,并且您在假设 2 个多边形上共享这个顶点。使用索引渲染(2 字节索引),这将花费您2*index_size+attribute_size = 34 byte。使用非索引渲染,这将花费您 60 个字节。通常您的顶点将被共享两次以上。

基于索引的渲染总是更好吗?不,可能会有更糟的情况。对于非常简单的应用程序,可能不值得花费代码开销来设置基于索引的数据模型。此外,当您的属性未在多边形上共享时(例如,正常的每个多边形而不是每个顶点)可能根本没有顶点共享,IBO 不会带来好处,只会产生开销。

除此之外,虽然它启用了转换后缓存,但它确实使通用内存缓存性能更差。因为您访问属性相对随机,您可能会有更多的缓存未命中和内存预取(如果这将在 GPU 上完成)将无法正常工作。因此,如果您有足够的内存并且您的顶点着色器非常简单,那么可能(但可以衡量)非索引版本的性能优于索引版本。

交错与非交错与每个属性的缓冲区

这个故事有点微妙,我认为它归结为权衡你的一些属性。

  1. 交错可能会更好,因为所有属性将靠近在一起,并且可能位于几个内存缓存行中(甚至可能是单个缓存行)。显然,这可能意味着更好的性能。但是,结合基于索引的渲染,您的内存访问无论如何都是非常随机的,并且好处可能比您预期的要小。
  2. 知道哪些属性是静态的,哪些是动态的。如果您有 5 个属性,其中 2 个是完全静态的,1 个每 15 分钟更改一次,2 个每 10 秒更改一次,请考虑将它们放在 2 或 3 个单独的缓冲区中。您不想在这 2 个最频繁更改时重新上传所有 5 个属性。
  3. 考虑属性应该在 4 个字节上对齐。因此,您可能希望不时地进一步进行交错。假设您有一个 vec3 1 字节属性和一些标量 1 字节属性,天真这将需要 8 个字节。将它们放在一个 vec4 中可能会收获很多,这应该会将使用量减少到 4 个字节。
  4. 使用缓冲区大小、太大的缓冲区或太多的小缓冲区可能会影响性能。但这可能非常依赖于硬件、驱动程序和 OpenGL 实现。
于 2013-07-08T16:44:57.737 回答
5

索引与直接

让我们看看你通过索引得到了什么。每个重复的顶点,即具有“平滑”中断的顶点将花费更少。每个单一的“边缘”顶点都会花费你更多。对于基于现实世界且相对密集的数据,一个顶点将属于多个三角形,因此索引会加快速度。对于程序生成的任意数据,直接模式通常会更好。

索引缓冲区还给代码增加了额外的复杂性。

交错与分离

这里的主要区别实际上是基于一个问题“我只想更新一个组件吗? ”。如果答案是肯定的,那么你不应该交错,因为任何更新都会非常昂贵。如果不是,则使用交错缓冲区应该可以提高引用的局部性,并且通常在大多数硬件上更快。

于 2013-07-08T16:51:03.747 回答