《天涯明月刀》手游引擎技术负责人:如何应用GPU Driven优化渲染效果?| TGDC 2020

来自 游戏葡萄 2020-12-10
深度

[ 游戏葡萄原创专稿,未经允许请勿转载 ]

《天涯明月刀》手游引擎技术负责人:如何应用GPU Driven优化渲染效果?| TGDC 2020

在TGDC系列报道第一篇中,我们讨论了工业化体系对于游戏产业的重要性。

而具体到手游产品的开发过程中,引擎技术的选择和使用,将直接决定产品画质、帧率和功耗三个维度的表现。而《天涯明月刀手游》(下文简称《天刀》)作为「腾讯旗舰·高沉浸感国风大世界之作」(官方宣传语),其对于引擎技术的选择和使用,在某种意义上代表着头部厂商对于通过工业化手段抬升游戏品质,优化用户体验的思考。

那么对于画质、帧率和功耗三个维度,《天刀》手游是如何选择并最终达成平衡的?

在此次TGDC活动中,腾讯互娱《天刀》手游引擎技术负责人刘冰啸分享了开发历程中他们对于几个关键点的技术决策,并详细阐述了他们应用GPU Driven优化渲染效果的一系列尝试。

以下为葡萄君整理的分享内容:

大家好,我是刘冰啸,在腾讯《天刀》手游项目组担任引擎技术负责人。这次演讲和大家一起回顾《天刀》手游开发历程中,几项关键点的技术决策和开发经历。 

《天刀》手游是一款基于真实物理渲染的国风MMORPG游戏。它延续了天刀端游画面表现力,在手游平台性能受限的环境下,实现这样的效果,甚至超越了端游的表现。

《天刀手游》场景实录

我们考虑手游引擎开发的几个重要要素:

首先是画质的选择。游戏要突出远景还是近景?画面表现是优先照顾室内还是室外?画面效果要鲜艳还是要更加自然?光影之间的取舍是优先光线的表达还是优先影子的效果?

第二是帧率。《天刀》手游属于强交互的MMORPG动作游戏,我们需要高且稳定的帧率。

通常开发PC游戏的时候,我们只需要考虑这两个方面。先达成特定画质效果表现,再通过优化不断提高帧率。帧率达到特定水平之后,我们再想办法引入更好的画质表现。两者不停相互迭代,提升整体画面效果,优化表现。

对于手游,我们引入了第三个维度——功耗。手机功耗不仅会导致发热,还会影响到玩家单次游玩的时间。我们对其中任一维度的调整,都将影响其他两个维度的表达。

112.png

我们开发引擎组采用了各种各样的技术栈,获取更好的优化效果。在GPU和CPU的遮挡剔除中,我们使用了不同粒度的算法;还启用了Vulkan API,对GPU内存进行粒度上的管理;并实现了GPU Driven的渲染管线,用Compute去辅助优化其他各项渲染技术。

113.png 

此外,我们使用了PBR材质,加上真实物理单位的Lighting,获得真实的HDR画面效果。 

手游每次测试后,我们都会进行一些重点优化或功能调优:

114.png

第一次测试中,我们对Unity 进行了多线程框架改动,把渲染线程和提交线程从主线程中剥离。因为在手游开发环境里,主线程持续工作会提升芯片功率,导致手机更容易发热。

在第二次测试中,我们引入了Vulkan API。它相对GLES,有着更轻量级的调用。通过测试,我们在提交线程实现了30%的CPU效率提升。 

而且因为Vulkan API是更加自由的开发方式,我们对于一些比较重度的Vulkan API操作,如采用一些Cache的方式去做Descriptor Set、Layout的绑定,这让我们在开发过程中可以获得更好的解法。

在第三次测试中,我们引入了GPU Driven技术,把大部分CPU上的工作转移到GPU上运行,不仅提升了GPU效率,也减少了从GPU到CPU之间的传递带宽。我们把这项技术用在了地形、草、植被,以及家园里。

同样在这次测试中,我们修改、提升了整体的光照表达,引入了自动曝光,提升了Tonemapping效果,解决了引入真实物理单位之后,不同光照环境Lighting体现的细节颜色丢失问题。我们还重新对Sky Lighting进行定义,使得整个场景的室外表现更丰富、更具有对比度。 

这里我主要讲一下,第三次测试中,我们用GPU Driven技术对渲染进行优化的要点。

我们在开发过程中经历了多线程的优化,发现手机平台的一个优化甜区。现在很多安卓手机都是4+4的多核架构,有点像上一代主机平台,如Xbox 360或PS3。这些架构能让开发者更多使用辅助计算单元提升计算效率。 

我们采用了两个方向,第一个方向是尽可能剥离主线程的计算,将其Dispatch到小核以及其他线程上,提升主线程计算效率;另一个方向是把计算转入GPU Compute。为什么说GPU Compute如此重要?

115.png 

首先,我认为它是现代渲染框架的一个基石。大家都了解,除了利用GPU进行光栅化处理的部分外,Compute能够完成大量GPU渲染流程上的操作,包括计算光照、材质等等

Compute的渲染语言完全能和GPU本身的硬件对应,例如Local Data Storage机制和Thread利用率,都能很好的通过Compute语言表现出来。

其次,使用Compute能发挥Vulkan更大的潜力,为什么这么说?因为我们可以使用Vulkan有效控制GPU的同步行为,而Compute作为一个独立单元,我们可以把Compute计算和GPU的其他计算并行,如Compute可以和一个带宽优先的Shadow Pass并行。

Compute是一个单独的Queue,对比Vs和Ps的整套Pipeline。它是一个非常简化的单元,非常容易摆放。

再者,采用Compute可以给GPU Driven和Bindless,打开更广阔的优化空间。我们甚至可以使用Async Compute方式做更进一步的并行 

对于《天刀》手游,打开Compute的关键突破点是GPU Driven的地形系统。我们看看使用Compute作为GPU Driven的一些研究方向。

116.png

最近几年,GPU Driven是一个相对比较热门的研发方向。育碧在SIGGRAPH 2015发表过一篇文章《GPU Driven的渲染管线》,主要讲《刺客信条:大革命》的开发。在GDC 2016上,EA的寒霜引擎也提到了GPU Driven的Pipeline。2018年的《孤岛惊魂5》,2019年的《特技摩托:聚变》,都实现了相关功能,但已经上线的手游产品很少体现相关的技术。

因此,我们先讲讲什么是GPU驱动的渲染管线。

由GPU掌握实际渲染控制,可以提供更细致的渲染力度。例如我们做渲染剔除时,CPU只能控制在Object Level,使用Object Bounding做剔除,而在GPU层面可以做到更进一步控制,在Mesh Cluster级别上通过切分Mesh获得更好的力度控制。

它的另一个好处是不需要GPU和CPU之间来回传递数据。在理想情况下,GPU Driven甚至都可以使用一个Drawcall绘制完整场景,当然这需要Compute Shader的支持以及Indirect Drawing提供相关API。在Vulkan 1.0的情况下,我们都可以拿到相关支持。

我们看一下《天刀》手游里GPU Driven地形实施后的结果。

117.png

首先介绍一下CPU地形常用的算法。我们上个版本里的地形算法是CPU端的Geometry Clipmap算法。它采用视锥的裁剪剔除方式,根据距离计算LOD,和GPU Driven相比少了Depth剔除,而GPU Driven可以根据距离和地形块的复杂度来计算送入Clipmap的顶点数。 

因为有两个Pass,其中一个要通过Virtual Texture使用,这样它需要21万乘2个顶点数,而通过GPU Driven,因为剔除效果比较好,只需要8万6个顶点数。 

我们看一下最后的GPU时间。

在GPU Driven的情况下,我们在Iphone 8Plus上可以获得2.9毫秒的时间开销,比通过CPU的方式节省了将近四分之一的成本。

在CPU端收益更大,我们提交线程和渲染线程,每个都可以获得五毫秒以上的收益。

从GPU Driven流程来讲,它主要包含GPU Driven和Virtual Texture两个算法,

这两个算法的实现有相互交叉。

出于简化,我们只讲一下GPU Driven相关流程的算法。

118.png

首先是一个深度的MIPS生成,这部分算法是在Compute里面实现的; 

其次,我们在Compute里面使用上一步制作的深度缓冲Buffer,实现了GPU的遮挡剔除,主要是通过计算你送入的Patches尺寸,看它处在哪一级的深度缓冲,对比这一级深度缓冲的深度和你的深度,决定是否被Depth剔除。通过这个流程,我们可以获得可视的Patches数目。

第三,根据可视的Patches数目,我们仍然通过Compute来生成Indirect Arguments的Buffer。

第四,把这些准备好的Indirect Buffer绘制出去。

这就是整个地形GPU Driven的渲染流程。

减少了Binding Memory Pingpong操作。

它的应用范围主要在于对你的Buffer做Filter,比如我们经常在渲染管线中提到的Bloom、高斯模糊、自动曝光,这些对区域进行Filter的操作都可以采用这种方法优化。

我们看看它具体怎么应用在《天刀》手游的地形体系里。

119.png

第一步,我们会做第一次Dispatch,产生16 X 16的线程组,每组128条线程。我们把这128条线程的读入深度放入MIPS 里

第二步,通过同步的方式把上一级MIPS相邻的四个点取出来,合并选择最深的单位写到第二级MIPS里......以此类推完成四级写出。 

在第二个Dispatch里,我们用同样的方法Dispatch一个线程组,128条线程,同样把后面32 X 16的MIPS写入完成在GPU Driven的地形系统里。

我们还有另一个机制优化,这个机制是LOD体系。《孤岛惊魂》的实现里主要采用的是CPU 四叉树的方式组织LOD的Patches,根据距离更新四叉树上的节点,再根据相机的距离,选择CPU 四叉树上的节点送入GPU进行Indirect Buffer和GPU Culling。

1110.png

而对于《天刀》手游,我们把这些Patch信息先通过Offline的方式Bake出来,在每一个相机发生位置移动的时候,更新这些Bake出来的信息。根据这些信息过滤改变的Patch,生成新的Indirect Buffer。 

我们看一下差异。

第一步,我们读到了所有的Patches的属性。

第二步,根据视锥裁剪获得结果。

第三步,再用Hiz产生的Depth产生一次Depth裁剪

1111.png

完成这三步之后,我们就可以生成Indirect Draw Arguments的Buffer,再Dispatch出去。完成GPU Driven地形后,我们引擎组会把所有流程重新梳理一遍,根据梳理结果选择可以应用GPU Driven的其他渲染模块。

1112.png

其中一个比较重要的渲染模块是场景的植被管理。有经验的渲染程序可能会直接意识到这一点。

草的Geometry、地形的Patch,和Sector的管理是非常类似的概念。它们都有LOD,都有不同的Geometry表现。我们绘制这些Geometry LOD时,最好的方法是通过Multi Draw Instanced Indirect的方案做。

另外一点是每种类型的草的Texture对应在地形上,更像是一个地形的Virtual Texture机制。我们也可以用Bindless的方式去做绑定。可是在Vulkan 1.0的平台情况下,这两个API的Multi Draw Instanced Indirect或是Bindless都没办法获得更好的支持。

因此,在《天刀》手游的实现里,我们只能将草的每种类型完成一次GPU Driven的Culling和Draw Indirect Buffer的生成。

另外,《天刀》手游里家园的渲染也比较采用适合GPU Driven。

家园玩法可以让玩家尽可能多地定制整个家园的地形、地表、墙壁、物件、地板等等。这个层面需要非常多的Geometry类型;而且家园区域相对比较小,只有128米 X 128米的自定义空间,这意味着遮挡剔除必须要做得非常好。

从这两点来看,GPU Driven非常非常适合应用在家园的渲染中。问题在于家园里面的物件其实和草的类型一样,都非常是依赖于Multi Draw Instanced Indirect和Bindless这两种API的实现。

1113.png

对于《天刀》手游,我们只能退而求其次,利用地形步骤算出来的Hiz的Buffer做遮挡剔除。我们送入一套做遮挡剔除Buffer的内容,通过CPU从Readback的方案来获得这些Buffer的遮挡剔除结果,在CPU端组织尽量多的Instance对象。即便采用这种方式,我们在家园渲染中,也获得了比较好的渲染效率。 

完成了Compute的基础机制后,我们也做了各种尝试。其中一个尝试是完成了在ASTC和PVRTC的GPU实时压缩。这也是通过GPU Compute来实现的,可以用在角色的妆容系统上。

1114.png

《天刀》手游的妆容系统需要完成多个Feature的绘制,如果不能很好地Baking到一张贴图上,那么在实时渲染时会产生更多的开销。

我们希望它能尽量压缩,减少内存使用,就测试了一些Compute Compression的效果,其PSNR和效率表现都比较好。

另一个尝试是对VirtualTexture的压缩。前面提到地形的VirtualTexture技术,需要更新大量地形块数据。数据越多,更新频率才能变低。

在《天刀》手游里,我们尝试使用过三张2048 X 2048、不经压缩的材质,通过调整贴图的尺寸、数量、压缩方式,基本上能把大张贴图的时间控制在四毫秒以下。玩家从远处看,其实很难观察到地表材质的差异。

1115.png

1116.png

但我们对比这种算法的细节表现,仍然看到它在开启压缩的情况下,有一些Blocking的瑕疵。我们不能接受这种瑕疵,只能放弃这种方案。

1117.png

在Compute应用上,我们尝试了Cluster Deferred,主要应用于家园室内场景。家园的室内场景是一个封闭空间,有大量的动态物体和动态光照效果。 

这幅图显示室内场景里大概有55盏灯。左上角的蓝色背景图可以看到这个区域被多少光源照亮,最多可能是8盏灯以上。

1118.png

这个方案实现后,我们发现它仍然解决不了一些问题:

第一个问题是Deferred本身固有的问题,带宽问题,设计更好的Gbuffer搁置需要应用一些API,如Subpass,或一些更好的Pass Combine的操作。

第二个问题在于材质的复杂度。一个Deferred材质和Forward材质在整个大世界中很难做兼容融合,这增大了Shader的复杂度和工作量。

这种方向也许未来会是一个比较好的技术点,但是在现有架构下,我认为还是不够成熟,所以放弃了这套方案。

回到主题,在Compute技术落地的过程中,我认为有几个决策非常重要:

1119.png

第一,我们需要有非常好的基础。我们从多线程开始,就意识到整个计算体系应该往Compute方向或往其他线程方向使用,通过减少主线程开销成本,提升整个游戏的性能效率。 

第二,我们让技术功底比较好的同事完成了GPU Driven地形的突破,在此基础上 

第三点是技术落地和产品需求之间的选择。有很多技术可能比较先进,但它的质量品质达不到产品需求。那这些技术只能忍痛割爱。 

以上是我今天的分享内容。接下来是Q&A环节。

Q:性能优化有什么技术难点,听说你们能以更低规格实现更高标准,是怎么做到的? 

刘冰啸:我们在实现过程中,没有特别顾虑高规格和所谓的低规格,一般是设定一个标准,在标准之上寻找平衡。

举个例子,刚才提到的光照或阴影表达。我们认为阴影表达是弱于光照效果的,所以在大世界里面采用了较为传统的、用Lightmap Bake Shadow的方式,并没有使用实时阴影。

Q:将《天刀》这类经典IP从端游移植到手游,从引擎技术角度如何更好地还原端游品质?

刘冰啸:首先我们得确定需要还原端游的哪些特点?对于《天刀》手游,首先我们更加注重远景的表现,其次我们要有更好的角色表现。对于一些特定的技术点,我们要做到比较好的还原,比如云海效果。

确定这些特点之后,我们再去制定关键的技术要素,比如我们延续了端游的PBR管线,但其实是使用了比端游更好的基于物理光照单位的Lighting的方式。这使得《天刀》手游在暗光下的表现甚至某种程度上优于端游。

文章评论
游戏葡萄订阅号