如今,虚拟现实 (VR) 技术正日益受到欢迎,这主要得益于遵循摩尔定律的技术进步让这一全新体验在技术上成为可能。尽管虚拟现实能给用户带来身临其境般的超凡体验,但相比传统应用,其具有双目渲染、低延迟、高分辨率以及高帧率等严苛要求,因此极大地增加了 CPU 和 GPU 的计算工作负载。鉴于此,性能问题对于虚拟现实应用尤为重要,因为虚拟现实体验如果没有经过优化,会出现低帧率或高延迟的问题,让用户使用时出现眩晕的情况。在本文中,我们将介绍一种适用于所有底层引擎或虚拟现实运行时(VRruntime)的通用方法,分析基于 PC 的虚拟现实应用面临的瓶颈问题。我们以腾讯* 的一款 PC 虚拟现实游戏《盘古》* 为例展示分析流程。

虚拟现实游戏和传统游戏在渲染管线上的区别

在探讨分析详情之前,我们先来解释一下 CPU 在虚拟现实中发挥重要作用的原因及其对虚拟现实性能的影响。图 1 所示为传统游戏的渲染管线,其中 CPU 和 GPU 是并行处理的,以实现最高的硬件利用率。但此方案并不适用于虚拟现实,因为虚拟现实需要较低和稳定的渲染延迟,传统游戏的渲染管线无法满足此项要求。

以图 1 为例,帧 N+2 的渲染延迟远高于VR对延迟的最低要求,因为 GPU 必须先完成帧 N+1 的工作,再来处理帧 N+2 的工作,因而使得帧 N+2 产生了较高的延迟。此外,由于运行情况不同,帧 N、帧 N+1 和帧 N+2 的渲染延迟也会有所差异,这对虚拟现实的体验也是不利的,因为一直变动的延迟会让用户产生模拟疾病(simulation sickness)。

 1传统游戏的渲染管线。

因此,虚拟现实的渲染管线实际上如图2 所示,这样能确保每帧可以达到最短的延迟。在图 2中,CPU/GPU 未进行并行处理,这样虽然降低了效率,但可确保每帧实现较低和稳定的渲染延迟。在这种情况下,CPU 会成为虚拟现实的瓶颈,因为 GPU 必须等待 CPU 完成预渲染工作(绘制调用准备、动态阴影初始化、遮挡剔除等)。CPU 优化有助于减少 GPU闲置时间,提高性能。

 2拟现实游戏的渲染管线。

《盘古》* 虚拟现实游戏的背景

《盘古》* 是腾讯* 利用Unreal Engine* 4 开发的一款基于 PC 的 DirectX* 11 FPS 虚拟现实游戏,支持Oculus Rift* 和 HTC Vive*。为了使《盘古》* 在英特尔® 酷睿™ i7 处理器上实现最佳的游戏体验,我们与腾讯* 密切合作,努力提升该游戏的性能与用户体验。结果显示,在本文所述的开发阶段,帧率得到了显著提升,从早期测试时 Oculus Rift* DK2 (1920x1080) 上的每秒36.4 帧 (fps) 跃升至本文撰写时 HTC Vive* (2160x1200) 上的每秒 71.4帧 (fps)。以下为开发工作开始和本文撰写时使用的引擎和虚拟现实运行时:

  • 初始开发平台: Oculus v0.8 x64 运行时和 Unreal 4.10.2
  • 本文撰写时开发平台: SteamVR* v1463169981 和 Unreal 4.11.2

在开发阶段使用不同虚拟现实运行时的原因在于,《盘古》*最初是在 Oculus Rift DK2 上开发的,而当时Oculus Rift CV1 和 HTC Vive 尚未发布。在 HTC Vive 正式发布后,《盘古》* 便迁移至该设备。评估显示,采用不同的虚拟现实运行时在性能方面没有显著的差异,因为 Oculus 和 SteamVR 运行时采用了相同的虚拟现实渲染管线(如图 2 所示)。在此情况下,渲染性能主要由游戏引擎决定。这点可在图 5 和图14中得到验证,Oculus 和 SteamVR 运行时在每帧的GPU 渲染结束后才插入GPU 任务(用于镜头畸变校正),在渲染方面仅消耗了少量时间。

下图所示为优化工作前后的游戏截图,请注意绘制调用次数在优化之后减少至原来的1/5,每帧的 GPU 执行时间平均从 15.1ms 缩短至 9.6ms,如图 12 和 13 所示:

 3优化前(左)后(右)的游戏截图

测试平台的规格:

  • 英特尔® 酷睿™ i7-6820HK 处理器(4 核,8 线程)@ 2.7GHz
  • NVIDIA GeForce* GTX980 16GB GDDR5
  • 图形驱动程序版本:  364.72
  • 16 GB DDR4 RAM
  • Windows* 10 RTM Build 10586.164

查找性能问题

为了更好地了解《盘古》*的性能瓶颈,我们先综合分析了该游戏的基本性能指标,见表 1。表中数据通过几种不同的工具收集,包括 GPU-Z、TypePerf 和 Unreal Frontend 等。将这些数据与系统空闲时的数据比较可得出以下几点结论:

  • 帧率低(36.4 fps)而且GPU利用率也低(GTX980 上为 49.64%)。如果能够提高 GPU 利用率,帧率也会提高。
  • 大量的绘制调用。 DirectX 11 中的渲染为单线程,相对于 DirectX 12,DirectX 11渲染线程具有相对较高的绘制调用开销。由于该游戏是在 DirectX 11 上开发的,并且为了达到低延迟和较短的MTP延迟(motion-to-photon latency,从用户运动开始到相应画面显示到屏幕上所花的时间),虚拟现实的渲染管线打破了CPU / GPU的并行处理,因此如果游戏的瓶颈在渲染线程,很容易出现CPU瓶颈导致性能显著降低。在这种情况下,较少的绘制调用有助于缓解渲染线程瓶颈。
  • 由表中可以看出,CPU 利用率似乎不是问题,因为其平均值只有 13.6%。但从下文更进一步的分析可以看出,《盘古》实际上存在CPU瓶颈的问题。
  系统空闲 《盘古》*运行在Oculus Rift* DK2上(优化前)
GPU核心频率(MHz) 135 1337.6
GPU内存频率(MHz) 162 1749.6
GPU已用内存(MB) 184 1727.71
GPU负载(%) 0 49.64
平均帧率(fps) 不适用 36.4
绘制调用次数(每帧) 0 4437
处理器(_Total)\处理器时间(%) 1.04 (5.73/0.93/0.49/0.29/ 0.7/0.37/0.24/0.2) 13.58 (30.20/10.54/26.72/3.76/ 12.72/8.16/12.27/4.29)
处理器信息_Total)\处理器频率(MHZ) 800 2700

 1:优化前游戏的基本性能指标。

下面,我们利用GPUView和Windows 评估和部署工具包(Windows Assessment and Deployment Kit, Windows ADK)[1] 中的 Windows 性能分析器(Windows Performance Analyzer, WPA)对《盘古》的性能瓶颈进行分析。

深入探查性能问题

GPUView [2] 工具可用于调查图形应用、 CPU 线程、图形驱动程序、Windows 图形内核等相互之间的性能和交互情况。该工具还可以在时间轴上显示程序是否存在CPU 或 GPU瓶颈。另外,Windows 性能分析器 [3] 可用于跟踪 Windows 事件(Event Tracing for Windows, ETW),并生成相应事件的数据和图表。WPA具有灵活的用户界面(UI),通过简单操作即可查看调用堆栈、CPU 热点、上下文切换热点等,它还可以用来查找性能问题的根本原因。 GPUView 和 Windows 性能分析器都可以用于分析由 Windows性能记录器(WPR)採集到的事件追踪日志(Event Trace Log, ETL)。Windows 性能记录器可通过用户界面或命令行运行,其内建的配置文件可用来选择要记录的事件。

对于虚拟现实应用,最好先确定其计算是否受限于CPU、GPU或二者。我们可以将优化工作的重心集中在对性能影响最大的瓶颈,以便最大限度提升性能。

图 4 为优化前《盘古》在 GPUView 中的时间线视图,包括 GPU 工作队列、CPU 上下文队列和 CPU 线程。根据图表我们可以看出:

  • 帧率大约为 37 fps。
  • GPU利用率大约为 50%。
  • 此VR应用的用户体验很差,因为帧率远低于 90 fps,易于让最终用户出现眩晕的感觉。
  • 如GPU 工作队列所示,只有两个进程向 GPU 提交了任务: Oculus 虚拟现实运行时和《盘古》。Oculus VR运行时在帧渲染的最后阶段进行了后处理,包括畸变校正、色彩校正和时间扭曲等。
  • 从图中可以看出《盘古》同时存在CPU和GPU瓶颈:
    • 在 CPU 瓶颈方面,GPU有大约50%的时间都处于空闲状态,而且受到了一些 CPU 线程的影响而导致GPU工作没法及时被提交,只有这些线程中的CPU任务完成后GPU 任务才能被执行。这种情况下如果对 CPU 任务进行优化,将能够极大地提升 GPU 的利用率,使 GPU 能执行更多的任务,从而提高帧率。
    • 在GPU 瓶颈方面,我们可以看出,即使所有GPU空闲时间都能够被消除,单帧的 GPU 执行时间仍然大于 11.1ms(在《盘古》的情况下约为 14.7ms),因此,如果不对 GPU 进一步优化,此应用的帧率不可能达到 Oculus Rift* CV1 和 HTC Vive* 等虚拟现实头盔要求的帧率 - 90 fps。

 4GPUView 盘古》*时间线视图

改善帧率和 GPU 利用率的初步建议:

  • 物理和 AI 等非紧急的 CPU 任务可以延后处理,使图形渲染工作能够尽早被提交,以缩短CPU瓶颈时间。
  • 有效应用多线程技术可增加并行性,减少游戏中的 CPU 瓶颈。
  • 减少会导致 CPU 瓶颈的任务,如绘制调用、动态阴影预处理、布料模拟、物理和AI等。
  • 提前提交下一帧的CPU 任务以提高 GPU 利用率。尽管MTP延迟可能会略有增加,但性能与效率会显著提高。
  • DirectX 11 具有高绘制调用和驱动程序开销。绘制调用过多时渲染线程会造成严重的 CPU 瓶颈。如果可以的话,考虑迁移至 DirectX 12。
  • 此外还必须优化 GPU 工作负载(如过度绘制、带宽、纹理填充率等),因为单帧的GPU处理时间大于一个垂直同步周期,所以会发生丢帧。

为了深入探查瓶颈问题,我们使用 Windows 性能分析器来研究从GPUView发现的CPU瓶颈(通过分析同一个ETL文件)。 Windows 性能分析器也可用于发现CPU热点函数或上下文切换的性能热点;对该主题有兴趣的读者可以参考 [4] 了解更多详情。以下介绍CPU瓶颈分析和优化的主要方法。

首先,让我们分析一下《盘古》出现性能问题的区间。在GPU完成一帧的渲染后,当前画面会通过显示桌面内容(Present)函数被提交到显示缓存,两个显示桌面内容(Present)函数之间的时间段为一帧的周期,如图 5 所示(26.78 ms,相当于 37.34 fps)。

 5GPUView盘古》*时间线视图(单帧)。注意导致 GPU閒置 CPU 线程。

请注意,在 GPU 工作队列中有不少时间GPU是闲置的(例如,一帧开头的 7.37 ms),这实际上是由工作负载的 CPU 线程瓶颈所造成,如红框所圈起来的部分。原因在于绘制调用准备、剔除等 CPU 任务必须在GPU渲染命令提交之前完成。

如果使用 Windows 性能分析器分析GPUView所示的 CPU 瓶颈,我们就能找出导致GPU无法马上执行工作的对应CPU热点函数。图 6-11 所示为与GPUView为同一时间段下,Windows 性能分析器中各CPU线程的利用率和的调用堆栈。

 6Windows 性能分析器《盘古》*时间线视图,与图 5 为同一时间段

接下来让我们仔细分析每个 CPU 线程的瓶颈。

 7渲染线程 T1864 调用堆栈。

由调用堆栈可以看出,渲染线程中最主要的三个瓶颈是

  1. 静态网格的基本通道渲染(50%)
  2. 动态阴影初始化(17%)
  3. 计算视图可视性(17%)

以上瓶颈是由于渲染线程中存在太多的绘制调用、状态变换和阴影图渲染所造成。优化渲染线程性能的几点建议如下:

  • 在 Unity* 中应用批处理或在 Unreal 中应用actor融合以减少静态网格绘制。将相近对象组合在一起,并使用细节层次(LOD)。使用更少的材质,以及将不同的纹理融入较大的纹理集都有助于提升性能。
  • 在 Unity 中使用双宽渲染 (Double Wide Rendering) 或在 Unreal 中使用实例化立体渲染 (Instanced Stereo Rendering),减少立体渲染的绘制调用提交开销。
  • 减少或关闭实时阴影。接收动态阴影的对象将不会进行批处理,从而造成严重的绘制调用问题。
  • 避免使用会导致对象被多次渲染的特效(反射,逐像素光照,透明或多材质对象)。

 8戏线程 T8292 调用堆栈

游戏线程最主要的三个瓶颈是

  1. 设置动画评估并行处理的前置工作(36.4%)
  2. 重绘视口(view port)(21.2%)
  3. 处理鼠标移动事件(21.2%)

以上三大问题可以通过减少视口数量,以及降低CPU并行动画评估的开销来解决。如果只使用了几个动画节点,则实施单线程处理,并且检查CPU 方面的鼠标控制使用情况。

工作线程(T8288,T4672,T8308):

 9工作线程 T8288 调用堆栈。

 10工作线程 T4672 调用堆栈。

 11工作线程 T8308 调用堆栈。

这些工作线程的瓶颈主要在于物理相关模拟,比如布料模拟、动画评估和粒子系统更新。

表 2 列出了GPU闲置时的所有 CPU 热点(时钟周期的百分比)。

线程 功能 时钟周期百分比
渲染线程 静态网格的基本通道渲染 13.1% 22.1%
动态阴影初始化 4.5%
计算视图可视性 4.5%
游戏线程 设置动画评估并行处理的前置工作 7.7% 16.7%
重绘视口 4.5%
处理鼠标移动事件 4.5%
物理 布料模拟 13.5% 22%
动画评估 4%
粒子系统
4.5%
驱动程序 4.4%

表 2:优化前GPU闲置时的 CPU 热点。

优化

在实施了包括细节层次、实体化立体渲染、动态阴影消除、延迟CPU任务以及优化物理等优化措施后,帧率从 Oculus Rift* DK2(1920x1080)上的 36.4 fps 提升至 HTC Vive*(2160x1200)上的 71.4 fps;同时由于 CPU 瓶颈减少,GPU 的利用率从 54.7% 提升至 74.3%。

图 12 和图 13 分别显示优化前后《盘古》* 的 GPU 利用率,如 GPU 工作队列所示。

 12优化前《盘古》 GPU 利用率。

 13优化后《盘古》 GPU 利用率。

 14优化后 GPUView 盘古》*时间线视图。

图 14 所示为优化后《盘古》*的GPUView视图。优化后 CPU 瓶颈期从 7.37 ms 降至 2.62 ms,所用的优化措施包括:

  • 提前运行渲染线程(一种通过产生额外的 MTP 延迟来减少 CPU 瓶颈的方法)[5]
  • 减少绘制调用次数和开销,包括采用细节层次、实体化立体渲染和移除动态阴影。
  • 延迟处理游戏线程和工作线程的任务。

图15所示为 CPU 瓶颈期的 CPU 渲染线程的调用堆栈,即图14的红框标记起来的部分。

 15渲染线程 T10404 调用堆栈。

表 3 列出了优化后 GPU闲置时的所有 CPU 热点(时钟周期的百分比)。请注意相对于表 2,许多热点和线程已从 CPU 瓶颈中移除。

线程 功能 时钟周期百分比
渲染线程 静态网格的基本通道渲染 44.3% 52.2%
渲染遮挡 7.9%
驱动程序 38.5%

 3优化后 GPU 闲置时的 CPU 热点。

更多的优化措施,比如actor融合或者使用更少的材质,都可以优化渲染线程中的静态网络渲染,进一步提高帧率。假若能对 CPU 任务进行充分的优化,单帧的处理时间能进一步减少 2.62 ms(单帧的 CPU 瓶颈期),达到 11.38 ms,相当于平均帧率 87.8 fps。

表 4 所示为优化前后的性能指标。

  系统空闲 《盘古》*运行在Oculus Rift* DK2上(优化前) 《盘古》*运行在 HTC Vive* 上(优化后) 
GPU核心频率(MHZ) 135 1337.6 1316.8
GPU显存频率(MHZ) 162 1749.6 1749.6
GPU已用内存(MB) 184 1727.71 2253.03
GPU负载(%) 0 49.64 78.29
平均帧率(fps) 不适用 36.4 71.4
绘制调用次数(每帧) 0 4437 845
处理器(_Total)\处理器时间(%) 1.04 (5.73/0.93/0.49/0.29/ 0.7/0.37/0.24/0.2) 13.58 (30.20/10.54/26.72/3.76/ 12.72/8.16/12.27/4.29) 31.37 (46.63/27.72/33.34/18.42/ 39.77/19.04/46.29/19.76)
处理器信息(_Total)\处理器频率(MHZ) 800 2700 2700

 4优化前后游戏的基本性能指标。

结论

本文介绍了我们与腾讯通力合作,分析与优化运行于高端VR头盔上的《盘古》* 虚拟现实工作负载,从而使该游戏在英特尔®酷睿™ i7 处理器上实现 90 fps 的帧率。在实施我们的部分优化建议后,帧率从Oculus Rift* DK2(1920x1080)上的 36.4 fps 提升至 HTC Vive*(2160x1200)上的 71.4 fps,同时由于 CPU 瓶颈减少,GPU 的平均利用率从 54.7% 提升至 74.3%。单帧的 CPU 瓶颈期也从 7.37 ms 减至 2.62 ms。其他的优化措施,如角色合并和纹理集都可以进一步优化性能。

利用多种工具分析虚拟现实应用可以帮助我们了解该应用的性能表现和瓶颈所在,该步骤对于优化虚拟现实性能尤其重要,因为单凭性能指标可能无法真正反映瓶颈问题在那。本文中讨论的方法与工具可用于分析使用任何游戏引擎或虚拟现实运行时开发的Windows虚拟现实应用,以及确定工作负载是否受到 CPU 或GPU 的限制。由于绘制调用准备、物理模拟、光照或阴影等因素的影响,有时 CPU 对虚拟现实应用性能的影响比 GPU 更大。通过分析多个存在性能问题的虚拟现实工作负载,我们发现其中许多存在CPU瓶颈,这意味着优化 CPU 可以提升它们的 GPU 利用率、性能及用户体验。

参考

[1] https://developer.microsoft.com/en-us/windows/hardware/windows-assessment-deployment-kit

[2] http://graphics.stanford.edu/~mdfisher/GPUView.html

[3] https://msdn.microsoft.com/en-us/library/windows/hardware/hh162981.aspx

[4] https://randomascii.wordpress.com/2015/09/24/etw-central/

[5] http://www.gdcvault.com/play/1021771/Advanced-VR

作者介绍

Finn Wong 目前担任英特尔® 软件和解决方案事业部(SSG)、开发人员关系部门(DRD)、高级图形支持团队(AGE 团队)的高级应用工程师。从 2012 年加入英特尔公司起,他一直积极支持在公司 PC 产品中使用第三方媒体、图形和感知计算应用。在加入英特尔之前,Finn 有七年视频编码、数字图像处理、计算机视觉、算法和性能优化领域的专业工作经验,发表过多篇学术论文。 Finn 拥有国立台湾大学电气工程学士学位和通信工程硕士学位。

PC虚拟现实应用的性能分析与优化:从CPU角度切入的更多相关文章

  1. MYSQL索引结构原理、性能分析与优化

    [转]MYSQL索引结构原理.性能分析与优化 第一部分:基础知识 索引 官方介绍索引是帮助MySQL高效获取数据的数据结构.笔者理解索引相当于一本书的目录,通过目录就知道要的资料在哪里, 不用一页一页 ...

  2. 1.linux服务器的性能分析与优化

    [教程主题]:1.linux服务器的性能分析与优化 [课程录制]: 创E [主要内容] [1]影响Linux服务器性能的因素 操作系统级 CPU 目前大部分CPU在同一时间只能运行一个线程,超线程的处 ...

  3. JDBC性能分析与优化

    JDBC性能分析与优化V1.0http://www.docin.com/p-758600080.html

  4. JVM性能分析与优化

    JVM性能分析与优化: http://www.docin.com/p-757199232.html

  5. 高性能Linux服务器 第10章 基于Linux服务器的性能分析与优化

    高性能Linux服务器 第10章    基于Linux服务器的性能分析与优化 作为一名Linux系统管理员,最主要的工作是优化系统配置,使应用在系统上以最优的状态运行.但硬件问题.软件问题.网络环境等 ...

  6. linux服务器的性能分析与优化(十三)

    [教程主题]:1.linux服务器的性能分析与优化 [主要内容] [1]影响Linux服务器性能的因素 操作系统级 Ø CPU 目前大部分CPU在同一时间只能运行一个线程,超线程的处理器可以在同一时间 ...

  7. Hive性能分析和优化方法

    Hive性能分析和优化方法 http://wenku.baidu.com/link?url=LVrnj-mD0OB69-eUH-0b2LGzc2SN76hjLVsGfCdYjV8ogyyN-BSja5 ...

  8. 【转】由浅入深探究mysql索引结构原理、性能分析与优化

    摘要: 第一部分:基础知识 第二部分:MYISAM和INNODB索引结构 1.简单介绍B-tree B+ tree树 2.MyisAM索引结构 3.Annode索引结构 4.MyisAM索引与Inno ...

  9. PostgreSQL CPU满(100%)性能分析及优化(转)

    PostgreSQL CPU满(100%)性能分析及优化 转自:https://help.aliyun.com/knowledge_detail/43562.html    在数据库运维当中,一个DB ...

随机推荐

  1. 如何一步一步用DDD设计一个电商网站(二)—— 项目架构

    阅读目录 前言 六边形架构 终于开始建项目了 DDD中的3个臭皮匠 CQRS(Command Query Responsibility Segregation) 结语 一.前言 上一篇我们讲了DDD的 ...

  2. 移动web基本知识

    1.pixel像素基础 1.px:csspixel 逻辑像素,浏览器所使用的抽象单位 2.dp,pt:设备无关像素 3.devicePixelPatio 设备像素缩放比例 2.viewport 1. ...

  3. 前端学HTTP之重定向和负载均衡

    前面的话 HTTP并不是独自运行在网上的.很多协议都会在HTTP报文的传输过程中对其数据进行管理.HTTP只关心旅程的端点(发送者和接收者),但在包含有镜像服务器.Web代理和缓存的网络世界中,HTT ...

  4. 协议森林16 小美的桌号(DHCP协议)

    作者:Vamei 出处:http://www.cnblogs.com/vamei 转载请先与我联系. DHCP协议用于动态的配置电脑的网络相关参数,如主机的IP地址,路由器出口地址.DNS域名服务器地 ...

  5. JS的内建函数reduce

    @(js) reduce函数,是ECMAScript5规范中出现的数组方法.在平时的工作中,相信大家使用的场景并不多,一般而言,可以通过reduce方法实现的逻辑都可以通过forEach方法来变相的实 ...

  6. 编译器开发系列--Ocelot语言4.类型定义的检查

    这里主要介绍一下检查循环定义的结构体.联合体.是对成员中包含自己本身的结构体.联合体进行检查.所谓"成员中包含自己本身",举例来说,就是指下面这样的定义. struct point ...

  7. 设置Hyper-V和VMware多个服务之间共存

    这个方法是解决多个服务之间不能共存,下面相当于是以Hyper-V和VMware做例子,其他的也适用. 今天准备安装VMware Workstation 10,然后玩玩MAC OS. 没想到,淡定的我双 ...

  8. SpringMVC初步

    SpringMVC框架介绍 1) Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面. Spring 框架提供了构建 Web 应用程序的全功 ...

  9. 基于Node.js实现一个小小的爬虫

    以前一直听说有爬虫这种东西,稍微看了看资料,貌似不是太复杂. 正好了解过node.js,那就基于它来个简单的爬虫. 1.本次爬虫目标: 从拉钩招聘网站中找出“前端开发”这一类岗位的信息,并作相应页面分 ...

  10. IOS开发之—— 在AFN基础上进行的网络请求的封装

    网络请求的思路:如果请求成功的话AFN的responseObject就是解析好的. 1发送网络请求:get/post/或者别的 带上URL,需要传的参数 2判断后台网络状态码有没有请求成功: 3 请求 ...