C++ 缓存友好顶点定义

C++ 缓存友好顶点定义,c++,opengl,caching,C++,Opengl,Caching,我正在编写一个opengl应用程序,对于顶点、法线和颜色,我使用单独的缓冲区,如下所示: GLuint顶点缓冲区、法线缓冲区、颜色缓冲区 我的主管告诉我,如果我定义一个结构,如: struct vertex { glm::vec3 pos; glm::vec3 normal; glm::vec3 color; }; GLuint vertex_buffer; 然后定义这些顶点的缓冲区,我的应用程序会更快,因为当位置被缓存时,法线和颜色将在缓存线中 我认为,定义这样的结构

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

GLuint顶点缓冲区、法线缓冲区、颜色缓冲区

我的主管告诉我,如果我定义一个结构,如:

struct vertex {
    glm::vec3 pos;
    glm::vec3 normal;
    glm::vec3 color;
};
GLuint vertex_buffer;
然后定义这些顶点的缓冲区,我的应用程序会更快,因为当位置被缓存时,法线和颜色将在缓存线中


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

取决于GPU体系结构

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

此外,当顶点着色器接近完成时,GPU可以将下一组属性预取到缓存中。因此,当顶点着色器完成时,下一个属性就在那里,可以加载到寄存器中

tl;dr不必为这些“拇指法则”操心,除非你真的对其进行了分析或了解GPU的实际架构。

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

那么你真的分析了差异吗

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


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

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

这里非常重要的因素是GPU架构。大多数(尤其是现代)GPU都有多个缓存线(用于输入汇编程序阶段、制服、纹理的数据),但从多个VBO获取输入属性可能效率低下(始终是概要文件!)。以交错格式定义它们有助于提高性能:

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

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

但总的来说:

交错数据格式:

  • 导致GPU缓存压力减小,因为单个顶点的顶点坐标和属性不会分散在内存中。 它们连续地装入几个缓存线,而分散地装入 属性可能会导致更多缓存更新,从而导致逐出。这个 最坏的情况可能是每个缓存线至少有一个(属性)元素 一段时间,因为内存位置遥远,而顶点会被拉扯 以不确定/不连续的方式,其中可能没有 预测和预取开始起作用。GPU在许多方面与CPU非常相似 这件事

  • 对于各种外部格式也非常有用,这些格式满足不推荐的交错格式,其中包含兼容数据的数据集 源可以直接读取到映射的GPU内存中。我最后 使用当前的API重新实现这些交错格式 正是这些原因

  • 应该像简单数组一样友好地对齐布局。混合具有不同大小/对齐要求的各种数据类型 可能需要填充GPU和CPU友好。这是唯一的缺点 据我所知,执行起来比较困难

  • 不要阻止您指向其中的单个attrib数组进行共享

进一步阅读


那么,你是说GPU可以同时读取不同的缓存线?例如顶点位置,从不同的缓冲区同时读取normal和derivs?@mmostajab读取属性是非常可预测的,因此预取将被更多地使用,效果会更好。@mmostajab:是的,现代GPU可以同时访问它们的缓存线。@datenwolf我没有分析它,但在这两种情况下,我得到的fps几乎相同。@mmostajab:这将是一个配置文件,你得到的结果是预期的。事实上,在现代GPU上,不交错数据实际上可以获得(非常小的)性能提升。从openGL wiki(您的最后一个链接):“交错属性对渲染性能有多大帮助还不清楚。需要分析数据。由于对齐需要,交错的顶点数据可能比未交错的顶点数据占用更多空间。“当wiki没有对其发表任何意见(赞成或反对)时,您提倡交错。
[…]然后按顺序存储这些交错的顶点块,再次将所有顶点属性合并到一个缓冲区中。[…]最佳布局取决于特定的GPU和驱动程序(加上OpenGL实现).
在单个缓冲区中使用交错属性与在每个属性中使用单独缓冲区相比的另一个优点是,需要绑定的缓冲区更少。因此,它可以减少设置状态的CPU开销。@RetoKoradi是的,只在开始时需要绑定的东西可以节省大量的CPU时间……这样可以节省更多的CPU时间仅仅因为你们看的教程并没有解释,而是告诉你们复制粘贴,所以并没有对每一帧解除绑定/重新绑定相同的缓冲区。