作者:王文斓
虚拟现实 (VR) 能够带给用户前所未有的沉浸体验,但同时由于双目渲染、低延迟、高分辨率、强制垂直同步 (vsync) 等特性使 VR 对 CPU 渲染线程和逻辑线程,以及 GPU 的计算压力较大[1]。如何能有效分析 VR 应用的性能瓶颈,优化 CPU 线程提高工作的并行化程度,从而降低 GPU 等待时间提升利用率将成为 VR 应用是否流畅、会否眩晕、沉浸感是否足够的关键。Unreal* Engine 4 (UE4) 作为目前 VR 开发者主要使用的两大游戏引擎之一,了解 UE4 的 CPU 线程结构和相关优化工具能够帮助我们开发出更优质基于 UE4 的 VR 应用。本文将集中介绍 UE4 的 CPU 性能分析和调试指令、线程结构、优化方法和工具、以及如何在 UE4 里发挥闲置 CPU 核心的计算资源来增强 VR 内容的表现,为不同配置的玩家提供相应的视听表现和内容,优化 VR 的沉浸感。
当 VR 开发的过程中遇到 CPU 性能问题时,除了需要找出瓶颈所在,还要掌握 UE4 里能够帮助优化这些瓶颈的工具,熟知每个工具的用法、效果和差异才能选取最合适的优化策略,快速提高 VR 应用的性能。在这个章节我们会集中讲解和介绍 UE4 的这些优化工具。
渲染线程优化
由于性能、带宽和 MSAA 等考虑因素,目前 VR 应用多采用前向渲染 (Forward Rendering) 而非延迟渲染(Deferred Rendering)。但在 UE4 的前向渲染管线中,为了减少 GPU overdraw,在 basepass 前的 prepass 阶段会强制使用 early-z 来生成 depth buffer,导致 basepass 前的 GPU 工作量提交较少。加上目前主流的 DirectX* 11 基本上属于单线程渲染,多线程能力较差,一旦 VR 场景的 drawcalls 或者 primitives 数目较多,culling 计算时间较长,基本上在 basepass 前的计算阶段就很可能因为渲染线程瓶颈而产生 GPU 闲置(GPU bubbles),降低了 GPU 利用率而引发掉帧,所以渲染线程的优化在 VR 开发中至关重要。
由于性能、带宽和 MSAA 等考虑因素,目前 VR 应用多采用前向渲染 (Forward Rendering) 而非延迟渲染(Deferred Rendering)。但在 UE4 的前向渲染管线中,为了减少 GPU overdraw,在 basepass 前的 prepass 阶段会强制使用 early-z 来生成 depth buffer,导致 basepass 前的 GPU 工作量提交较少。加上目前主流的 DirectX* 11 基本上属于单线程渲染,多线程能力较差,一旦 VR 场景的 drawcalls 或者 primitives 数目较多,culling 计算时间较长,基本上在 basepass 前的计算阶段就很可能因为渲染线程瓶颈而产生 GPU 闲置(GPU bubbles),降低了 GPU 利用率而引发掉帧,所以渲染线程的优化在 VR 开发中至关重要。
如果我们用 GPUView 分析图 3 的场景,会得到图 4 的结果,图 4 中红色箭头指的位置就是 CPU 渲染线程开始的时间。由于 running start,第一个红色箭头在垂直同步前 3ms 就开始计算,但显然到了垂直同步时 GPU 还没工作可以做,一直到 3.5ms 后 GPU 短暂工作了一下又闲置了 1.2ms,然后 CPU 才把 prepass 工作提交到 CPU context queue,prepass 完成后又过了 2ms basepass 的工作才被提交到 CPU context queue 给 GPU 执行。图 4 中红圈圈起来的地方就是 GPU 闲置的时间段,加起来有接近 7ms 的 GPU bubbles,直接导致 GPU 渲染无法在 11.1ms 内完成而掉帧,需要 2 个垂直同步周期才能完成这帧的工作,实际上我们可以结合 Windows* Performance Analyzer(WPA) 分析 GPU bubbles 期间的渲染线程调用堆栈并找出瓶颈是由哪些函数引起的 [1]。另外第二个红色箭头指的位置是下一帧渲染线程开始的时间,由于这一帧出现掉帧,所以下一帧的渲染线程多了整整一个垂直同步周期作计算。等到下一帧的 GPU 在垂直同步后开始工作时,渲染线程已经把 CPU context queue 填满了,所以 GPU 有足够多的工作可以做而不会产生 GPU bubbles,只要没有 GPU bubbles 一帧的渲染在 9ms 内就能完成,于是下一帧就不会掉帧。3 个垂直同步周期完成 2 帧的渲染,这也是为什么平均帧率是 60fps 的原因。
从图 4 的分析结果可以发现在这例子中,GPU 实际上并不是性能瓶颈,只要把真正的 CPU 渲染线程瓶颈解决,该 VR 游戏就能够达到 90fps。而事实上我们发现大部分用 UE4 开发的 VR 应用都存在渲染线程瓶颈,因此熟练掌握下面几种 UE4 渲染线程优化工具可以大大提升 VR 应用的性能。
图 3. 一个存在 CPU 渲染线程瓶颈的 VR 游戏例子,上面显示了 SteamVR 对每帧 CPU 和 GPU 消耗时间的统计。
图 4. 图 3 例子的 GPUView 时间视图,可以看到 CPU 渲染线程瓶颈导致了 GPU 闲置,从而引发掉帧。
实例化立体渲染 (Instanced Stereo Rendering)
VR 由于双目渲染的原因导致 drawcall 数目增加一倍,容易引发渲染线程瓶颈。实例化立体渲染只要对对象提交一次 drawcall,然后由 GPU 分别对左右眼视角施加对应的变换矩阵,就能够把对象同时画到左右眼视角上,等于把这部分的 CPU 工作移到 GPU 处理,增加了 GPU vertex shader 的工作但可以节省一半 drawcall。因此,除非 VR 场景的 drawcall 数目较低 (<500),否则实例化立体渲染一般能够降低渲染线程负载,为 VR 应用带来约 20% 的性能提升。实例化立体渲染可以在项目设置中选择打开或关闭。
可见性剔除 (Visibility Culling)
在 VR 应用中渲染线程瓶颈通常由两大原因造成,一个是静态网格计算另一个是可见性剔除。静态网格计算可以通过合并 drawcall 或者 mesh 来优化,而可见性剔除则需要减少原语 (primitives) 或者动态遮挡剔除 (dynamic occlusion culling) 的数目。可见性剔除瓶颈在 VR 应用中尤其严重,因为 VR 为了减低延时强制每帧 CPU 渲染线程计算最多只能提前到垂直同步前 3ms(running start/queue ahead),而 UE4 里 InitViews(包括可见性剔除和设置动态阴影等)阶段是不会产生 GPU 工作的,一旦 InitViews 所花时间超过 3ms,就必定会产生 GPU bubbles 而降低 GPU 利用率,容易造成掉帧,所以可见性剔除在 VR 里需要重点优化。
来源: http://geek.csdn.net/news/detail/248107