《坦克世界》1.0+:使用 CPU 优化的图形和物理丰富用户体验

合著者: 《坦克世界》团队《坦克世界》渲染团队负责人 Bronislav Sviglo

简介

在过去十年,计算机游戏通过使用不断发展的 GPU 显著改善了视觉质量。现在,游戏使用堪比好莱坞电影中典型渲染的实时图形,高性能 GPU 在最终用户 PC 平台上已经屡见不鲜。由于游戏不断接近逼真的 GPU 加速图形,开发人员正在寻找下一个突破:接下来将丰富玩家的体验,提升性能和创建沉浸感更强的动态世界。

为此,开发人员应考虑现代 CPU。本文(基于旧金山 2018 年游戏开发者大会上英特尔和 Wargaming* 的联合演示)将探索最大的游戏开发商之一 - Wargaming 如何使用现代 CPU 的创新特性对他们的引擎进行重新架构,并将性能和游戏体验提升到新的水平。本文以《坦克世界》* 为例,介绍 Wargaming 使用 CPU 多核和 CPU 单指令多数据 (SIMD) 功能显著提升游戏沉浸式体验的创新方法。我们以英特尔® 线程构建模块(英特尔® TBB)的任务系统为基础,讨论它如何支持游戏引擎的多线程基础、Havok Destruction* 的使用以及新的物理校正坦克线程模拟和并发渲染。在本文中,我们将集中讨论 CPU 多核线程如何优化《坦克世界》

Wargaming* 和《坦克世界》*

Wargaming 于 1998 年在白俄罗斯明斯克成立,现已发展为世界最大的游戏开发商之一,这主要归功于《坦克世界》。《坦克世界》是一款免费的在线多人战争游戏,支持用户驾驶 20 世纪坦克、坦克歼击车和其他重型装甲车作战。自 2010 年首次大规模上市以来,这款游戏吸引了全球超过 1.4 亿的注册用户,公司通过定期更新不断推出新特性、地图、坦克和其他游戏内容,使游戏成为市场上排名靠前的大型多人在线 (MMO) 游戏。今年 3 月,Wargaming 发布了第一版 - 《坦克世界》1.0 以来最大的《坦克世界》更新,提供了全新的引擎、图形渲染器、完全重新设计的高分辨率地图和许多其他卓越的新特性。

多年来,英特尔和 Wargaming 在引擎架构到日常优化等问题上开展了密切的合作,以确保新版《坦克世界》针对广泛的计算机而优化,为未来的增强特性打下坚实的基础。

《坦克世界》1.0

开发《坦克世界》1.0 更新耗费了大约 4 年时间,工程团队在将 BigWorld 引擎重新编写到全新的专有 Core 引擎方面取得了重大进步,生成了强大、灵活的现代解决方案,可运行最热门的游戏。在全新的 Core 引擎中大幅调整了渲染和物理引擎中的架构,以支持设计人员使用更加具体的地图显著改进视觉质量,通过水力模拟和对象破坏增强逼真度,为更多特性做好准备,同时不会显著降低性能。这一切都需要大量的 GPU 和 CPU 优化工作。

World of Tanks in game terrain rendering

World of Tanks in game terrain rendering

图 1.《坦克世界》 0.9x 和 1.0 中相同的游戏位置。

现代 CPU

CPU 是每台计算设备的大脑和心脏。自问世以来,CPU 不断演变,并且这个过程永远不会停止。多代英特尔® 处理器带来了新特性并改进了性能、内存、高速缓存、延迟、指令集等。主要改进了并行计算,它包括两大部分:多核和向量指令集。

多核

如今,几乎每台计算设备均包括一颗多核 CPU,无论是智能手机、游戏机、台式机还是笔记本电脑。主要原因是:在目前的技术流程中,添加更多内核是提升性能的最有效的方法。这是未来的一个长期趋势。因此,对于包括游戏开发人员在内的程序员,利用所有可用的 CPU 内核来访问现代 CPU 的所有计算资源至关重要。

向量指令

向量指令或 SIMD 支持开发人员在单个指令(如向量加法或减法)内处理多个数据。PC 平台上有多个向量指令集,包括英特尔® SIMD 流指令扩展(英特尔® SSE)和英特尔® 高级向量扩展指令集(英特尔® AVX)、英特尔® 高级向量扩展指令集 2(英特尔® AVX2),它们也支持最新的游戏机。这些指令集可广泛应用于游戏开发,显著提升物理、渲染和人工智能引擎子系统的性能。

我们来看一下 2017(左)和 2016 的平均计算机系统,了解发生了哪些变化以及对开发人员和游戏意味着什么。

表 1.台式机处理器 SKU。

CPU英特尔® 酷睿™ i7-4790K 2017英特尔® 酷睿™  i7-4650U 2016
# 内核42
# 线程84
基础频率3.60 GHz1.70 GHz
最大频率4.00 GHz3.30 GHz
指令集扩展英特尔® SSE4.1
英特尔® SSE4.2
英特尔® AVX2
英特尔 SSE4.1
英特尔 SSE4.2
英特尔 AVX2

或者在很长一段时间内,笔记本电脑中的 CPU 主要有两个物理内核(4 个采用超线程的线程)。从 2017 年年底开始,许多移动 SKU 具有 4 个物理内核(8 个采用超线程的线程)。

表 2.笔记本电脑处理器 SKU。

CPU英特尔® 酷睿™ i7-8700K 2017英特尔® 酷睿™ i7-8650U 2016
# 内核64
# 线程128
基础频率3.70 GHz1.90 GHz
最大频率4.70 GHz4.20 GHz
指令集扩展英特尔 SSE4.1
英特尔 SSE4.2
英特尔 AVX2
英特尔 SSE4.1
英特尔 SSE4.2
英特尔 AVX2

对于台式机 PC SKU,2017 年底也见证了最热门游戏 SKU 的增长。英特尔® 酷睿™ i7 处理器现在具有 6 个内核。

内核数量的增加为开发人员提供了显著提升应用性能和添加功能(包括全新的视觉效果、物理和声音)的机会。

新老硬件配置对比

新硬件不断朝着更高性能和更多功能的方向发展,为全球各个年龄段的玩家提供了极为广泛的计算机配置选择。这为游戏开发人员带来了挑战,他们既要支持新特性,还要支持使用老旧电脑的用户。

开发人员开始构建自己的引擎,以充分利用多核新硬件,同时仍需支持现有的配置。对他们来说,开发或选择正确的线程框架至关重要。该子系统将支持轻松、高效地使用可用的计算设备。

英特尔® TBB 是一个编程 API,通过有效利用可用的计算资源,帮助开发人员充分利用应用中的多线程,甚至异构环境。经过 10 多年的演变,英特尔 TBB 库推出了专门用于并行编程软件执行的高级接口。有关英特尔 TBB 层次结构的简要介绍,请参见图 2。

Intel® Threading Building Blocks library components
图 2.英特尔® 线程构建模块库组件。左侧:高级和低级任务 API;右侧:各种并发容器和同步基元。

除了支持同时执行任意函数的任务 API,并发环境还包含几种实用的实体,如容器、同步基元和内存分配程序,它们不需要连接英特尔 TBB 库。如图 2 的右半部分所示。英特尔 TBB 库最实用的特性之一可能是对并行结构嵌套的支持。库在设计之初便考虑了嵌套支持,因此,它可以高效地执行。例如,用户可以从通用并行算法的另一个调用中调用一个通用并行算法(如 “parallel_for”),或调用流程图节点或库的其他任何结构,不用担心过度订购系统。图 3 展示了嵌套支持带来优势的示例。

Diagram of Feature detection Intel® TBB Flow Graph
图 3.特征检测英特尔® TBB Flow Graph,将 3 种模式表达为并行性:管线化(绿色方框)、功能并行性(红色方框)和数据并行性(蓝色方框)。

如欲了解有关英特尔 TBB 的更多信息,包括产品新闻、库论坛和历史消息,请访问 Intel.cn 的产品页面或访问专门站点线程构建模块 (TBB)。有关库的用途以及如何用它解决各种多核编程问题的文章,详见 《The Parallel Universe》杂志。2016 年 6 月发行的杂志特刊专门介绍了英特尔 TBB 库及其演变过程。

cover of Parallel Universe magazine -bird on it

cover of Parallel Universe magazine -conceptual motherboard

图 4.《The Parallel Universe》数字杂志。

现代 CPU

对于 Wargaming 工程团队,功耗、灵活性、易于使用和维护是选择英特尔 TBB 作为《坦克世界》 1.0 主线程 API 的关键因素。自 2016 以来,英特尔和 Wargaming 共同致力于将 API 集成至《坦克世界》的 core 引擎。通过重新思考引擎结构和执行模型,选择正确的算法为未来的游戏版本提供可扩展的高效解决方案和支持的硬件平台。

Havok Destruction*

创建更加动态的游戏世界是游戏开发人员最重要的任务之一。即使进行了逼真的渲染,如果物理效果和环境看起来是静态的,游戏也会让人感觉不自然。破坏效果可以增强游戏环境的动态性,因此,《坦克世界》是检验该功能的理想示例。

更新 1.0 之前,可破坏的物理效果非常简单,可通过简单的粒子系统实现,需要对模型进行以下更改。《坦克世界》 1.0 的破坏工作由 Havok Destruction 完成,后者是强大又流行的游戏物理中间件 Havok Physics* 的一部分。

团队开始集成默认的 Havok* 线程化解决方案 - Havok 自己的任务管理器:

Diagram of Havok threading solution
图 5.Havok Destruction 线程化。

游戏还包括一套特殊的碰撞几何集,旨在提升性能:

example of different geometry set for game map
图 6.用于碰撞和破坏的不同几何集。

该解决方案效果卓越,该方案在《坦克世界》 1.0 中发布。添加更多多核导向型特性引发了一个潜在问题:拥有两个不同的多线程任务管理器。Havok Destruction 的内部系统需要自己的线程池分配,这些线程与英特尔 TBB 的线程池竞争,导致过度订购系统。

由于英特尔 TBB 被选为主要的线程框架,将游戏中的所有效果和子系统打包到英特尔 TBB 实体是合理的,这样英特尔 TBB 便能管理可用内核上的调度。这促成了 Wargaming、英特尔和 Havok 的合作,以支持英特尔 TBB 替代 Havok 线程层。这样,英特尔 TBB 可将 Havok Destruction 任务映射到英特尔 TBB 线程,不会引起任何可组合性问题,如系统的过度订购,这对 CPU 内核数量相对较少的 PC 而言尤为重要。

增强的坦克履带

坦克是重型机动车,履带是坦克的独特特征。履带是两个及以上车轮驱动的连续轨道,这使它们成为物理模拟的重要课题。如果处理得当,将改进玩家的沉浸式体验,对于硬核粉丝尤为如此,他们对坦克每个部分的外形和运转方式都十分熟悉。

《坦克世界》团队决定开发自己的履带模拟技术,这是一项复杂的可扩展技术,利用弹簧链物理模型生成真实的视觉效果(见图 7)。

Animated gif of tank thread in motion
图 7.2D 坦克履带模拟。

之前,《坦克世界》具有两种不同的模拟履带:

  • 蒙皮网格。最简单、最快速的技术,但是看起来是静态的。动态效果通过移动的纹理创建。
  • 样条模拟,通过样条控制点模拟履带,然后移动它们来模拟与地形的交互,从而实现移动和集成。视觉效果很好,但是过程却很复杂,图形工作者需要针对所有不同的坦克模型设置履带。此外,无法真实地模拟与地形和游戏对象的碰撞。

全新弹簧链物理解决了大多数问题,提供了出色的视觉效果:

game tank rolling on rough terrain

game tank rolling on rough terrain

图 8.坦克履带模拟:展示详细地形碰撞(左侧)和重力(右侧)的截图。

现在,履带被分为 4 个部分:前部和后部,顶部和底部:

Profile view of game tank details
图 9.坦克履带模拟:4 部分设置。

引擎对坦克下方的区域进行光线投射,并使用动态创建的高度场模拟碰撞。

每个履带部分的物理模拟可显著改进视觉效果,模拟的履带可与车辙、地形和坦克几何正确地交互。履带将根据坦克的移动做出正确反应,使玩家感受到物理材料和机制。它还会大幅减少图形工作者设置每个坦克模型的工作量,因为新履带可立即应用于每个坦克,无需手动调整。

英特尔 TBB 为每条履带创建了自己的任务,稍后在一个可用的工作线程上执行:

Simlulation tasks on Intel TBB worker threads
图 10.将模拟任务映射至英特尔 TBB 工作线程。

图 10 显示英特尔 TBB 花费约 250 微秒,每个任务的工作量是巨大的。因此,根据算法的可扩展性,将这些任务划分为更多任务,以提高 CPU 的利用率和整体模拟性能。

并发渲染

主线程的引擎性能限制是 CPU 端最重要的性能问题之一,主线程执行主要的游戏逻辑,为渲染线程准备工作或自己发出渲染调用。最终,即使 CPU 具有多个内核,并且游戏能为不同的子系统或任务创建多个线程,主要的瓶颈仍是大多数工作运行的单核频率。

因此,DirectX* 12、Vulkan* 等现代渲染 API 在构建时就考虑了多核性质。这支持 3D 应用从多个线程准备与发出渲染调用,将工作有效分配至所有可用的 CPU 内核,最大限度地减少主线程上的瓶颈。目前,《坦克世界》使用 DirectX* 11 API,但是,工程团队提出了使用现有 API 实现这一点的创新方法。

《坦克世界》的初始版本使用 BigWorld 引擎,它的渲染管道是一个单线程 DirectX 3D* 9 应用:

Map of a rendering pipeline circa 2010
图 11.初始渲染管道,c.2010。

第一步是将渲染和其余的游戏分离并引入线程化。团队提出了抽象渲染接口 (ARI) 方法,并将所有 API 特定的渲染代码分为单独的渲染线程。补丁 0.9.15 和 Core 引擎 3.0(2016 年 8 月):

Map of a rendering pipeline 2016
图 12.渲染管道 v0.9.15,2016 年 8 月。

在这项工作进行期间,全新的渲染 API - 如 Mantle、DirectX 12 和 Vulkan - 开始出现,团队最初使用相同的方法构建了 ARI。基本上,ARI 将渲染命令收集至软件命令缓冲区。然后,收集的命令缓冲区被提交给渲染线程,后者负责处理它们并直接调用相应的图形 API(在那时,只能是 DirectX* 9 或 DirectX 11)。

更快速的多核感知型 Wgfx 中间编译器是 ARI 的重要组成部分,它包含大量的后台优化和一个自定义 API,是 DirectX 3D 效果框架的替代选项。尽管引擎现在拥有单独的渲染线程来处理软件命令缓冲区,但是 ARI 渲染命令只在主线程中发出。即使如此,性能仍提升了高达 30%。

可以通过以下术语快速解释 ARI:

  • 命令表。明确描述了渲染线程应该做什么。命令表包含一系列渲染命令,如绘制、清除、更新、复制资源,稍后将在渲染线程上执行这些命令。
  • 设备接口。ARI 前端和图形 API(如 DirectX 9、DirectX 11 或 DirectX 12)之间的“驱动程序”。
  • 资源。任何资源类的缓冲区:纹理、查询、图形管道状态等。

ARI 使我们开始考虑并发渲染以及将工作提交分配在多个线程的位置,以最大限度地减少主线程上的工作量。

命令表必须包含 plain, old data (POD) 命令,彼此独立并且具有稍后在渲染线程中发布所需的所有信息。由于命令表彼此不依赖,因此,可以以并行方式准备它们。将命令表提交至单独的渲染线程的顺序可保证正确的结果。

命令的示例包括绘制、清除或查询等。

但是,某些命令(如更新或复制资源)要求手动内存管理和对用户提供的内容进行生命周期控制。

因此,在 GPU 上上传纹理时,开发人员通常不想将整个纹理内容复制到命令。相反,他们只在内存上提供一个用于复制的指针或链接,并确保在帧结束前,该内存始终有效。

command list structure
图 13.软件“命令表”结构。

设备访问的 ARI 接口可按照访问模式分为 3 类:

  • 自由线程或独立于线程的接口,用于在任何时间从任何线程执行的操作。借助该接口,我们可以执行资源创建与删除、适配器状态接收等。
  • 单线程接口主要与命令表操作相关,如用命令填充列表,在 DirectX 12 中将其编译为原生命令等。
  • 创建仅限线程的接口是一个传统术语,关于现有老旧图形 API(如 DirectX 9 和 DirectX 11)执行特殊操作的限制,如渲染设备的显示与重置。

应用已经集成的英特尔 TBB API 后,线程模型如下所示:

  • 英特尔 TBB 流图,管理渲染任务之间的依赖性并定义将收集的命令表提交至渲染线程的实际顺序。
  • 渲染任务,代表引擎的每个渲染子系统的独立命令表。
  • 分隔使用软件命令缓冲区的渲染线程并对图形 API 执行原生调用。

在高级别,渲染帧由英特尔 TBB 流图管理,后者用作渲染任务调度程序。

  1. 流图包括节点和边缘的数量。
  2. 流图的每个节点都是一个子系统内的大型渲染工作块。
  3. 边缘组成不同子系统或关键渲染器阶段之间的依赖性。

以下渲染节点列表展示了各种渲染节点示例,因此,您可以了解它们是什么。

节点

  • 遮挡解析
  • 动态模型
  • 静态模型
  • 透明模型
  • 坦克
  • 植被
  • 地形
  • 阴影
  • 光照
  • 大气
  • 后期处理
  • GUI

下图显示了一个高级帧渲染图示例。此类高级帧架构对团队中的每个人都非常实用,他们可以快速了解实际的流程或执行顺序以及哪些子系统相互依赖。此外,该代码易于修改和优化,因为有关粗粒度任务的所有高级信息均位于同一个位置。基于任务的并行性或功能并行性可以使用英特尔 TBB 流图轻松表达,因为我们只需将每个任务打包到流图节点并添加它们之间的关联性。

High level render graph
图 14.高级帧渲染图。

如前所述,当前的 core 引擎使用英特尔 TBB 流图以并行方式收集各个子系统的命令表,然后以预定义的顺序将它们刷新到渲染线程。遗憾的是,如果我们每帧只向渲染线程刷新一次,将使渲染线程闲置。因此,我们需要执行中间刷新,为渲染线程分配一些工作。

介绍完 ARI 和并发渲染模型后,我们来看一下它如何在游戏中运行以及如何改变性能。

原始帧如图 15 所示(该测试在 4 核英特尔® 酷睿™ i5 处理器上执行)。

telemetry for rendering frame
图 15.Telemetry*- 在 4 核英特尔® 酷睿™ i5 处理器上渲染帧,未启用并发渲染。

图 16 显示主线程将所有组件依次收集到独立命令表。这需要很长时间(约 17 毫秒)。图 16 还显示渲染线程正在处理之前的渲染帧。您可能还注意到渲染线程在大多数时间闲置,这是因为主线程生成渲染命令的速度不够快。请记得及时为渲染线程分配工作。

启用并发渲染并为解析可见性、静态模型、坦克、光照、水、植被、后期处理、粒子和阴影添加并行执行后,如下所示:

telemetry for rendering frame
图 16.Telemetry* - 在 4 核英特尔® 酷睿™ i5 处理器上渲染帧,启用并发渲染。

整体帧速率提高了一倍,现在每帧只需 8 毫秒。但是该图有些奇怪:在一个工作线程上产生了一个没有工作的气泡。该气泡表示我们应该注意以下两点:

  1. 所有单个渲染任务的大小
  2. 该任务与其他任务之间的依赖性

忽视这些可能导致效率低下。

我们通常会发现,功能并行性很容易实施。编写与支持这些代码对工程团队来说轻而易举。由于他们可以获得渲染管道的高级视图,因此能够轻松进行修改以提高管道的并行性。

遗憾的是,高级帧渲染图要求我们拥有大型工作块,这并不总是最佳方法,因为并非所有任务均拥有相同的大小或相同的执行时间。不是所有任务都能并行执行,因为,某些任务相互依赖。因此,该方法很快导致关键执行路径。您可能考虑最大限度地降低任务之间的依赖性,或将任务细分为更小的任务,但是这可能极大地降低代码的可读性。我们如何提升性能,同时保证代码的可读性?

之前,我们只使用功能并行性,但是也要考虑数据并行性。我们可以单独利用每个子系统内的数据并行性,确保英特尔 TBB 将完成剩下的任务。这样,我们在外层上仍有易于读取和维护的帧渲染图,并且每个子系统能在内层生成更多任务,以更好地利用所有可用的 CPU 内核。

例如,我们来看一下并发渲染如何在更强大的 CPU 上运行,如包含 6 个物理内核的英特尔® 酷睿™ i7 处理器。

此处,我们禁用了并发渲染,平均帧时间为 12 毫秒:

telemetry for rendering frame
图 17.1 Telemetry* - 在 6 核英特尔® 酷睿™ i7 处理器上渲染帧,未启用并发渲染。

启用并发渲染后:

telemetry for rendering frame
图 17.2 Telemetry* - 在 6 核英特尔® 酷睿™ i7 处理器上渲染帧,启用并发渲染。

现在,帧时间为 4 毫秒,这表示性能提升了接近 3 倍!在某些线程上仍存在气泡,但是 CPU 利用率、CPU 利用率和整体应用性能均明显提高。

总之,并发渲染可显著减少命令生成花费的时间。除了添加并行命令生成外,代码仍很简单,并且易于读取与修改。释放的 CPU 时间可用于其他绝佳的特性,如坦克履带、在场景中添加更多对象甚至更多破坏效果。

还有很多工作等着我们去完成,渲染器可能被细分为更小的任务,每个任务还可以用并行算法划分为子任务。此外,需要特别注意过时图形 API 上的命令提交模式,并且始终为渲染线程分配足够多的工作,以避免其处于闲置状态。

DirectX 12 和 Vulkan 支持在生成线程内处理命令,《坦克世界》团队计划将来用它来减轻渲染线程的压力。

结论

本文展示了使用 CPU 和 GPU 提高视觉保真度的多个示例,这个聪明的方法将为玩家带来直观、有趣的全新特性。

CPU 和 GPU 都在不断演变,为开发人员提供了更多计算资源。开发人员在努力改进视觉效果及提升渲染性能时,必须考虑 CPU 和 GPU 。《坦克世界》工程团队继续探索与开发全新的特性。游戏的发行版本已提供 Havok Destruction 和坦克履带特性,并发渲染应用于开发的最后阶段。

资源

《坦克世界》

有关编译器优化的更完整信息,请参阅优化通知