开发

Meta Quest 的绘制调用成本分析

当材质和网格提交给 GPU 进行绘制时,便会发生绘制调用。VR 游戏的绘制调用往往高于 2D 游戏,再加上运行时的帧率更高,所以 CPU 资源会被大量占用。这会导致绘制所有对象时,每一帧可用的 CPU 时间缩短,然而对象数量却会翻倍。
注意:虽然单通立体渲染(Unity 版)有助于消除 CPU 的部分负担,但您仍应了解更改各种参数对绘制调用所产生的影响。
绘制调用时间会因此前的绘制调用状态而不同。对此前调用的材质、网格或纹理进行更改,会增加绘制调用时间。
本文档主要介绍各种调用参数的测试结果,以及它们对 CPU 和 GPU 绘制调用在时间方面的影响。
这些测试均在使用 Unity 2018.1.6f1 的 Meta Quest 1 上执行,其结果适用于任意应用的开发。

测试方法

  • Unity 测试应用:
  • 测试设置:
    • 生成 500 个正方形对象,显示几帧让着色器进行编译,然后加以禁用
  • 测试流程:
    • 启用 10 个对象,等待 5 帧并记录 OnPreRenderOnPostRender 之间的平均时间。
  • 禁用异步时间扭曲 (ATW),防止 ATW 线程阻止主渲染线程。每次原始 GPU 测量中都包含时间扭曲。
  • 禁用 VrPowerManager,防止设备休眠
渲染测试如下图中所示:
Rendered test scene used for draw call performance measurement.

CPU 成本分析

第一组测试结果显示了更改着色器、材质、网格、纹理和颜色对 CPU 的影响。
如要优化性能,您应该:
  • 避免切换着色器程序
  • 避免切换材质
  • 注意网格变化和纹理计数的影响
随后几个部分会提供图表结果和这些参数影响的更多详情。

在绘制调用之间更改材质

测试表明,使用相同的着色器但切换材质(不改变任何其他属性)导致绘制调用的时间增加了 64%。
切换着色器程序会导致结果变得更糟糕。测试表明,切换着色器使绘制调用时间成本增加了 175%。
下图表示的是切换着色器和材质对于渲染无纹理和共享网格的对象所花费时间的影响。
为帮助缩短更改材质的时间,您可以按材质对您的绘图调用进行排序。
Impact of switching materials and/or shaders

在绘制调用之间更改网格

尽量避免在绘制调用之间更改网格。该图表示的是使用相同材质时,在绘制调用之间切换网格所增加的时间。
Mesh switching between draw calls

更改材质对比更改网格

更改材质的影响大于更改网格。下图是对二者的比较。
Changing materials vs. changing meshes

纹理计数的影响

如果要更改材质和使用多种纹理,那么当您增加纹理数量时,进行绘制调用会付出更昂贵的成本。
而如果重复使用具有多个纹理的材质,那么在绘制调用之间对其加以更改,仍会产生少量成本。
下图表示的是增加纹理对更改材质和重复使用材质所产生的影响。
Effect of increasing textures

低影响的更改

对纹理、材质颜色、纹理大小、滤镜或压缩算法的更改对于绘制调用时间的影响最小。

在绘制调用之间更改纹理

在 Unity 2018 中,更改纹理产生的成本不会高于更改材质所产生的成本。下图展示的正是这一点。
Changing textures between draw calls

在绘制调用之间更改材质颜色

在绘制调用之间更改材质的颜色不存在显著成本差异,所以如果要更换材质,可以不必担心更换颜色的问题。但如果只需要更改颜色,应使用相同的材质,并更改网格颜色即可。
Changing material colors between draw calls

使用不同大小的纹理进行绘制调用的成本

纹理大小不会对绘制调用成本产生重大影响。下图展示的正是这一点。
Texture size

纹理滤镜和压缩算法的影响

更改滤镜算法或压缩方式不会对绘制调用成本产生任何影响。
下图表示更改滤镜算法的影响。
Changing filter algorithms
下图表示压缩算法的影响。
Compression algorithms

更改网格复杂性

与纹理大小类似,网格的大小对于绘制调用来说并不会显著增加 CPU 成本,而是会影响 GPU。下图展示了纹理大小对 CPU 的影响。
Impact of texture size on CPU

每次绘制调用的 CPU 时间

如果知道各种排列下一帧的总渲染时间,就能估算出每次绘制调用的时间。为了计算测试中的绘制时间,我们观察了渲染不同对象计数之间的平均增量。
这些测试表明,重新绘制相同对象的成本约为绘制不同对象成本的 25%。
所以要缩短绘制时间,可考虑以下几点:
  • 给对象进行分类
  • 采用 Atlas 纹理
  • 考虑不用纹理。使用合适的着色器,您仍然可以制作出效果极佳的游戏。
CPU Time per draw call

GPU 成本分析

第二组测试仔细检测了更改材质、复杂网格等内容对 GPU 的影响。
若要缩短 GPU 绘制时间,可遵循以下准则:
  • 避免使用复杂网格
  • 在较小程度上,避免材质更改
  • 避免更改着色器
以下部分包含各种参数更改的图表结果。

材质更改

材质更改的 CPU 成本最为昂贵,但它对于 GPU 成本又有何影响呢?结果表明,更改材质的确会影响 GPU 时间,尤其是在新材质采用不同的着色器时。下图表示更改材质对 GPU 的影响:

复杂网格

材质更改有影响,但要注意的是,顶点/三角形计数高的复杂网格对 GPU 绘制时间的影响高于材质更改。下图展示了使用更高的多边形网格会快速消耗您的 GPU 预算。
Complex meshes

额外纹理参考

注意在着色器中对额外纹理进行采样的成本。使用一个简单着色器的话,可在图中看出额外纹理样本的成本增加情况。纹理增多会提高内存成本,但较之更改着色器的成本,避免增加额外纹理不算太过重要。
Additional texture references

低影响的更改

有三个方面不会影响绘制调用的 GPU 成本,它们分别是纹理压缩、纹理滤镜和纹理大小。

纹理压缩的 GPU 成本

下图是重复使用着色器与更改着色器对比下的各种纹理压缩级别。
Texture compression levels

纹理大小

下图是重复使用着色器与更改着色器对比下的各种纹理大小级别。
Texture size levels

纹理滤镜

下图是重复使用着色器与更改着色器对比下的各种纹理滤镜技术。
Texture filter techniques

每次绘制调用的 GPU 时间

下图展示了使用简单着色器绘制小四边形时,每个绘制调用所需的实际时间。该测试采用受控的方式执行,因此只适合用来对比调用之间的相对成本差异,不适合对比测量时间。
GPU time per call

不同着色器的 GPU 成本

下一组测试研究的是更改着色器对 GPU 绘制成本的影响。

采样立方体与 Texture2D

一般认为,从立方体采样比从 Texture2D 采样要更昂贵。虽然这是事实,但其实差别很小,如果您的应用需要立方体,尽管使用即可。下图表示的是立方体与 Texture2D 的对比。
Cubemap vs. Texture2D

依赖型与独立型纹理读取

业界认为纹理读取成本更高,应当避免使用。然而,在 Quest 上读取依赖型纹理只比读取独立型纹理略贵一点。所以可以考虑对查找表 (LUT) 取样,代替更加昂贵的着色器操作。
Dependent vs. Independent Texture Reads

漫射与 PBR 着色器

着色器复杂性很可能是 GPU 耗时最高的操作。请看简单漫射着色器与 PBR 着色器(Unity 标准着色器)之间的对比。逻辑操作的数量和复杂性通常比采样额外纹理花费的时间更多。
Diffuse vs. PBR shader

对比着色器参数

最后,下图表示的是组合不同纹理参数得到的结果。该图表明,在几乎所有情况下,着色器的复杂性都会导致绘制调用时间加长。
Comparing shader parameters
我们也可以用相同的推导来计算不同参数的“实际 GPU时间”成本。与之前的说明一样,这种计算只适用于相对比较。结果表明,着色器复杂性是测量 GPU 时间时需要考虑在内的因素。

本测试中未涵盖的项目

测试和结果数据表明了更改参数对于绘制调用长度的部分影响。在设计应用程序时,还有许多其他事项需要考虑。
  • 网格的形状和(屏幕空间)大小
    • 多边形出现在多个 GPU 磁贴中可能产生额外成本
  • 不透明度与透明度
    • Alpha 值混合处理并不便宜,如果您使用线性或 sRGB 色彩空间,成本也会增加。在线性空间中,所有颜色在采样时都会应用伽玛曲线,并在写入时进行反向应用。这使得混合操作的成本比所有内容保留在 sRGB 空间时要高得多。
  • MSAA 级别
    • 有传言称 MSAA 没有成本,或者说成本极低,因此可以一直开启。然而结果并非如此,它会实际产生成本。不过对于 VR 来说,使用 MSAA 能够获得视觉改善,所以可谓是一项实质性的需求。这意味着增加的成本只能从其他方面给予补偿。
  • 帧缓冲区提取
    • Unity 大大简化了使用 GL_EXT_shader_framebuffer_fetch 的方式(只需要用一个 inout 着色器参数写入您的片元着色器,无需返回最终颜色)。然而这种方式却隐藏了一个事实,那就是它用起来可能相当昂贵,而且成本还会随着 MSAA 级别的增加而提高(帧缓冲区提取要在每个样本基础上进行处理,而不是每个片元)。