缓存友好的顶点定义

Posted

技术标签:

【中文标题】缓存友好的顶点定义【英文标题】:Cache Friendly Vertex Definition 【发布时间】:2015-04-14 09:36:47 【问题描述】:

我正在编写一个 opengl 应用程序,对于顶点、法线和颜色,我使用单独的缓冲区,如下所示:

GLuint vertex_buffer, normal_buffer, color_buffer;

我的主管告诉我,如果我定义这样的结构:

struct vertex 
    glm::vec3 pos;
    glm::vec3 normal;
    glm::vec3 color;
;
GLuint vertex_buffer;

然后定义这些顶点的缓冲区,我的应用程序会变得更快,因为当位置被缓存时,法线和颜色将在缓存行中。

我认为定义这样的结构不会对性能产生太大影响,因为像结构一样定义顶点会导致缓存线中的顶点更少,同时将它们定义为单独的缓冲区,将导致有 3 个不同的缓存线用于缓存中的位置、法线和颜色。所以,什么都没有改变。真的吗?

【问题讨论】:

【参考方案1】:

首先,为不同的顶点属性使用单独的缓冲区可能不是一个好的技术。

这里非常重要的因素是 GPU 架构。大多数(尤其是现代)GPU 有多个缓存行(输入汇编器阶段的数据、制服、纹理),但是从多个 VBO 获取输入属性无论如何都可能是低效的(总是配置文件!)。以交错格式定义它们有助于提高性能:

如果你使用这样的结构,这就是你会得到的。

然而,这并不总是正确的(同样,总是配置文件!) - 虽然交错数据对 GPU 更友好,但它需要正确对齐,并且可能会占用更多的内存空间。

但是,一般来说:

交错数据格式:

减少 GPU 缓存压力,因为单个顶点的顶点坐标和属性不会分散在内存中。 它们连续地适合几个缓存行,而分散 属性可能会导致更多的缓存更新,从而导致驱逐。这 最坏的情况可能是每个高速缓存行一个(属性)元素 一个时间,因为遥远的记忆位置,而顶点被拉 以非确定性/非连续的方式,可能没有 预测和预取开始了。GPU 与 CPU 非常相似 这件事。

对于各种外部格式也非常有用,它们满足不推荐使用的交错格式,其中兼容数据的数据集 源代码可以直接读入映射的 GPU 内存。我结束了 使用当前 API 重新实现这些交错格式 正是这些原因。

应该像简单数组一样布局对齐友好。混合具有不同大小/对齐要求的各种数据类型 可能需要填充以对 GPU 和 CPU 友好。这是唯一的缺点 我知道,除了更困难的实现。

不要阻止您指向其中的单个属性数组进行共享。

Source

进一步阅读

Best Practices for Working with Vertex Data

Vertex Specification Best Practices

【讨论】:

来自 openGL wiki(您的最后一个链接):“交错属性对渲染性能有多大帮助尚不清楚。需要分析数据。交错的顶点数据可能比未交错的数据占用更多空间对齐需求。” 当 wiki 没有说任何(赞成或反对)关于它的内容时,您提倡交错。 [...] and then store each of these interleaved vertex blocks sequentially, again combining all the vertex attributes into a single buffer. [...] The optimal layout depends on the specific GPU and driver (plus OpenGL implementation). 在单个缓冲区中使用交错属性而不是为每个属性使用单独的缓冲区的另一个优点是需要绑定的缓冲区更少。所以它可以减少设置状态的 CPU 开销。 @RetoKoradi 是的,您节省了很多只需要在开始时绑定的东西......您可以通过简单地不取消绑定/重新绑定每帧相同的缓冲区来节省更多的 CPU 时间,因为教程你看了没解释,叫你复制粘贴代替。【参考方案2】:

取决于 GPU 架构。

大多数 GPU 会有多个缓存线(一些用于制服,另一些用于顶点属性,其他用于纹理采样)

此外,当顶点着色器快要完成时,GPU 可以将下一组属性预取到缓存中。这样当顶点着色器完成时,下一个属性就可以加载到寄存器中了。

tl;dr 不要理会这些“经验法则”,除非您实际分析它或了解 GPU 的实际架构。

【讨论】:

那么,你的意思是GPU可以同时读取不同的缓存行?例如顶点位置,法线和同时来自不同缓冲区的派生? @mmostajab 读取属性是非常可预测的,因此预取将被更多地使用并且效果更好。 @mmostajab:是的,现代 GPU 可以同时访问它们的缓存行。【参考方案3】:

告诉你的主管“过早的优化是万恶之源” – Donald E. Knuth。但是不要忘记下一句“但这并不意味着我们不应该优化热点”。

那么你真的描述了这些差异吗?

无论如何,顶点数据的布局对于现代 GPU 上的缓存效率并不重要。它曾经在旧的 GPU 上(约 2000 年),这就是为什么有用于交错顶点数据的函数的原因。但这些天来,这几乎不是问题。

这与现代 GPU 访问内存的方式有关,事实上现代 GPU 的缓存行不是按内存地址索引,而是按访问模式(即着色器中的第一个不同内存访问获取第一个缓存行,第二个,第二个缓存行,依此类推)。

【讨论】:

@datenwolf 我没有对其进行分析,但在这两种情况下我几乎得到相同的 fps。 @mmostajab:这将是一个配置文件,你得到的结果是预期的。事实上,在现代 GPU 上,您实际上可能通过交错数据而获得(非常小的)性能提升。

以上是关于缓存友好的顶点定义的主要内容,如果未能解决你的问题,请参考以下文章

java 哪个SOQL for循环更加友好的顶点?

DirectX学习笔记--索引缓存绘图

顶点动画存储在 FBX 文件中而不使用点缓存?

Opengl_On_Vs_2_顶点缓存对象

如何在 Blender 导出脚本中导出每个顶点的 UV 坐标

opengl算法学习---消隐