分析顶点格式

您可以通过使用帧分析来诊断一些可能的与顶点相关的性能问题。使用**命令**面板查看游戏在给定帧中执行的所有绘图调用以及每个绘图调用绘制的图元计数。这可以近似估计单帧中提交的顶点总数。

Frame profiling view for a glDrawElements call, hovered for detail
            on the draw call parameters
图1. 单个glDrawElements调用的帧分析视图,显示绘制了2,718个三角形图元

顶点属性压缩

您的游戏可能面临的一个常见问题是平均顶点大小很大。使用高平均顶点大小提交的大量顶点会导致GPU读取时产生大量的顶点内存读取带宽。

要观察给定绘图调用的顶点格式,请完成以下步骤

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

    这可以是场景的典型绘图调用、具有大量顶点的绘图调用、复杂角色模型的绘图调用或其他类型的绘图调用。

  2. 导航到**管道**面板,然后单击输入汇编的**IA**。这定义了进入GPU的顶点的顶点格式。

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

Frame profiling view for a draw call's input assembly, with
            uncompressed vertex attributes
图2. 绘图调用的输入汇编,未压缩的属性导致顶点大小为56字节

通常,可以压缩顶点属性,而不会显着降低绘制模型的质量。特别是,我们建议

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

也可以根据具体情况考虑其他杂项属性的较低精度类型。

顶点流分割

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

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

要调查顶点属性是否被适当地分割

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

    这可以是场景的典型绘图调用、具有大量顶点的绘图调用、复杂角色模型的绘图调用或其他类型的绘图调用。

  2. 导航到**管道**面板,然后单击输入汇编的**IA**。这定义了进入GPU的顶点的顶点格式。

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

  4. 在**状态**面板中,找到LastDrawInfos并展开匹配的绘图调用编号。然后,展开此绘图调用的BoundVertexBuffers

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

  6. 展开绘图调用的顶点属性的绑定,然后展开缓冲区。

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

Frame profiling view for a draw call's input assembly and state showing the bound vertex buffer
图3. 绘图调用的输入汇编,右侧的状态面板显示绑定 0 和 1(顶点位置和法线)处的属性共享单个底层缓冲区

有关顶点流分割以及如何在各种游戏引擎上解决它的更多详细信息,请参阅我们关于此主题的博文