分析顶点格式

您可以通过使用帧性能分析来诊断一些可能与顶点相关的性能问题。您可以使用 Commands 窗格查看游戏在给定帧中执行的所有绘制调用,以及每次绘制调用绘制的基元计数。它可以估算在单个帧中提交的顶点总数。

glDrawElements 调用的帧性能分析视图,悬停鼠标可查看绘制调用参数的详细信息
图 1. 单个 glDrawElements 调用的帧性能分析视图,显示已绘制 2,718 个三角形基元

顶点属性压缩

您的游戏可能会面临的一个常见问题是平均顶点过大。如果提交的大量顶点具有较高的平均顶点大小,会导致 GPU 在读取时占用较大的顶点内存读取带宽。

如需观察给定绘制调用的顶点格式,请完成以下步骤:

  1. 选择感兴趣的绘制调用。

    该调用可以是场景的典型绘制调用、具有大量顶点的绘制调用、复杂角色模型的绘制调用,也可以是其他类型的绘制调用。

  2. 前往 Pipeline 窗格,然后点击 IA 以进行输入组装。 这定义了进入 GPU 的顶点的顶点格式。

  3. 观察一系列属性及其格式;例如,R32G32B32_SFLOAT 是一个 3 分量 32 位有符号浮点数。

绘图调用的输入组件的帧性能分析视图,包含未压缩的顶点属性
图 2. 绘制调用的输入组件,包含未压缩的属性,导致顶点大小为 56 字节

通常,可以压缩顶点属性,同时最大限度地降低所绘制模型的质量。具体来说,我们建议您:

  • 将顶点位置压缩为半精度 16 位浮点数
  • 将 UV 纹理坐标压缩为 16 位无符号整数 ushort
  • 通过使用四元数对法线、正切和双法线向量进行编码来压缩切线空间

对于低精度类型,可能还要根据具体情况考虑其他属性。

顶点流拆分

您还可以调查顶点属性流是否进行了适当的拆分。在移动 GPU 等平铺渲染架构中,首先在分箱传递中使用顶点位置,以创建在每个图块中处理的基元的分箱。如果顶点属性交错到单个缓冲区中,则所有顶点数据都会被读入缓存以进行分箱,即使只使用了顶点位置也是如此。

为了减少顶点读取内存带宽并提高缓存效率,从而缩短分箱传递所花费的时间,顶点数据应拆分为两个单独的流,一个用于顶点位置,另一个用于所有其他顶点属性。

如需调查是否适当拆分顶点属性,请执行以下操作:

  1. 选择感兴趣的绘制调用,并记下绘制调用编号。

    该调用可以是场景的典型绘制调用、具有大量顶点的绘制调用、复杂角色模型的绘制调用,也可以是其他类型的绘制调用。

  2. 转到 Pipeline 窗格,然后点击 IA 以进行输入组装。这定义了进入 GPU 的顶点的顶点格式。

  3. 观察顶点属性的绑定;这些绑定通常可能会线性增加(0、1、2、3 等),但也并非总是如此。顶点位置通常是列出的第一个顶点属性。

  4. State 窗格中,找到 LastDrawInfos 并展开匹配的绘制调用编号。然后,展开此绘制调用的 BoundVertexBuffers

  5. 观察给定绘制调用期间绑定的顶点缓冲区,其中索引与之前的顶点属性绑定匹配。

  6. 扩展绘制调用的顶点属性的绑定,并扩展缓冲区。

  7. 观察缓冲区的 VulkanHandle,缓冲区表示顶点数据源所在的底层内存。如果 VulkanHandle 不同,则表示属性来自不同的底层缓冲区。如果 VulkanHandle 相同但偏移量较大(例如,大于 100),则这些属性可能仍然来自不同的子缓冲区,但这需要进一步调查。

绘图调用的输入组合和状态的帧性能分析视图,显示了绑定的顶点缓冲区
图 3. 绘制调用的输入组件,右侧状态面板显示绑定 0 和 1、顶点位置和法线的属性共享单个底层缓冲区

如需详细了解顶点流拆分以及如何在各种游戏引擎上对其进行解析,请参阅有关该主题的博文