顶点缓冲区-索引或直接、交错或分离



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

我正在寻找一些常见的陈词滥调——我发现了一些情况,其中一个或相反的情况更适合,但并不是所有的情况都很容易解决。在追求性能时,选择顶点缓冲区格式应该考虑什么?

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

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

索引渲染与直接渲染

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

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

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

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

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

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

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

索引与直接

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

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

交错与分离

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

最新更新