(转整)UE4游戏优化 多人大地型游戏的优化渲染线程的优化

Posted baipao-xd

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(转整)UE4游戏优化 多人大地型游戏的优化渲染线程的优化相关的知识,希望对你有一定的参考价值。

施主分享随缘,评论随心,@author:白袍小道

   

小道暗语:

1、因为小道这里博客目录没自己整,暂时就用随笔目录结构,所以二级目录那啥就忽略了。标题格式大致都是(原or转) 二级目录 (标题)

   

2、因为所看和以前记录太过杂乱,所以只能手动一点点搬移(回忆,整理)。欢迎讨论,知识和能力总是被问出来了不是(嘿嘿,这样才能成长), 若有不对别喷就好哈哈。

   

引言:

文章四方面包括了从游戏线程渲染线程GPU内存等的优化,提升游戏技术底子。

原作者:王祢,Epic Games 资深开发者技术支持,管理虚幻引擎技术支持的程序员团队,拥有近15年虚幻引擎使用经验。

   

正文:

优化肯定是有个前提和需求背景的,本文的前提:在移动设备上做大地型的多人游戏。

需求背景:

1、开放地图:视野宽,视距远,地图大

2、场景:风格变化多

3、同屏人不少

4、交互也不少

(看到这里对吧,UE +上述 就直接说堡垒之夜就好了呗)

   

渲染线程

 

技术分享图片

   

1.1

场景遍历

   

场景的复杂度(渲染前有场景遍历哦,其他地方详细说了比如关卡,primitive搜索,叉树,等等等)嘿嘿

渲染线程的第一个开销取决于场景的复杂度,即使实际绘制出来的内容很少,但是场景遍历的开销却是正比于场景

在内存里的Primitive数量的。

   

如果我这个遍历时间很长,那么实际绘制调用发出的时间就会比较晚。这个时候,我们就要利用好Streaming Level来最小化Scene Tranversal的开销。另外,动态的对象每一幀重新获取要绘制的渲染数据,也会有不小的开销,同时也会降低静态对象的渲染状态排序的优势。这也是上面提到过的加入了特殊的Static Render Path的优化手段的原因。

   

   

1.2

裁剪

技术分享图片

   

   

1.3

DrawCall

   

(刷,HLOD来合并,实例)

技术分享图片

剔除完就到了最终头的开销来源:Draw Calls,减少DC的手段多种多样,譬如引擎提供了刷foliage的工具,对于石头、树之类大量复用的对象,用这种方式刷出的HISCM,会做gpu instancing大大减少DC数。

   

然后一个有用的方案是HLOD,可以把一组Mesh甚至是一个关卡合并成一个Proxy Mesh,在最低级LOD后,可以切换到这个合并的Mesh,大大的减少远处物件的Draw Call并依然保持很远的视距。HLOD依然可以做多级的LOD帮助进一步减少DrawCall和减少面数,这些工具都是引擎内建,可以很方便部署自动化。

   

Dynamic Instancing,我们有一些特殊的方案,也做了一些整合,接下来的引擎版本会有非常大的渲染pipeline的重构(信不信),会对这个有更天然支持,甚至支持带光照烘焙的Dynamic Instancing,在光照图计算的时候就把可以instancing到一起的对象优先并到一张光照图上。

   

   

1.4

Drawing Policies

另外一个和DrawCall开销息息相关的是渲染状态切换的数量,引擎里有个接近的概念叫Drawing Policies,刚才说静态的对象我们会按Drawing Policies分组排序,现在的版本中,我们针对这个分组排序的规则做了一些改进,可以更好的减少渲染线程的渲染绘制调用的状态切换,同时也一定程度兼顾gpu的overdraw

   

刚才说到的新的mesh draw command pipeline要到今年年底,明年年初才上线,在目前的测试场景中,对于渲染线程的优化,可能有近十倍的改善,当然最终在移动端上表现如何还不能下定论。整个新管线的思路是尽可能使得渲染线程在cpu端没有什么开销的,场景资源管理等的开销都在GPU上。

   

1.5

RHI Thread

[可以看看UERender模块,队列命令和线程

   

技术分享图片

在OpenGL ES上,GraphicAPI的调用必须和glcontext在一个线程,于是,我们把所有的gl command都enqueue到了一个叫RHI Thread的线程,这样一来,实际渲染驱动的开销和引擎渲染线程的工作就可以有一部分并行化,减少整个渲染的frame time,以及变向降低渲染线程所在核的主频,这样可能在部分设备上还能减少一些功耗开销。

Hitches

1

Loading

   

技术分享图片

当启用streaming level异步加载以后,如果游戏逻辑发生了阻塞加载,由于引擎并不知道加载数据的依赖性,所以会导致引擎Flush异步线程,造成卡顿。其中普通游戏逻辑触发的加载我们可以比较容易的察觉并改正,但是另一个情况是在网络同步的时候,当服务器第一次同步回来一个新的Actor时,客户端会创建Actor Channel(UE网络提出了同步的有关的几个Channel),并需要实际Spawn Actor,可能会依赖阻塞加载的数据,进而导致flush造成卡顿。

      

我们可以通过打开net.AllowAsyncLoadingEnabled,使得触发的加载变成一个异步加载,并且这个Actor Channel的创建过程,也会加入一个pending的队列,等到加载资源都到了以后的那帧才可以实际的创建。

   

   

2

CompileShader

   

技术分享图片

由于OPGLES没有固定的shadercache标准,引擎提供了ShaderCache,在新版本中改进成了ShaderPipelineCache的功能,该系统可以在离线环境下先跑一遍游戏,在这个过程中用到的Shader,绘制的状态记录都会在Log文件中。Runtime的时候,我们会先读log,分一些批次预先Compile完以减少runtime发生compile的情况。

   

另外,一旦compile,可以配合另一个ProgramBinaryCache的功能,引擎会把link完的program保存下来,以后再需要加载Shader的时候,如果发现这个link program存在,会直接加载program。

   

这样不但能省去compile和link的过程,还跳过了shader code的加载过程和节省了内存。除了compile,这个cache系统还会做warmup,也就是预先绘制,以减少第一次使用的额外开销。

   

   

   

3

Spawing

Cook,池,C++组件,蓝图组件(序列化优化)

技术分享图片

降低spawn的开销一个是减少每个components的数量,再者,尽可能用C++的Component。如果你是BP components,引擎项目设置中有一个选项,可以在cook的时候把components的序列化,初始化的结果存下来,spawn的时候直接拿这个数据做实例化就行了。

   

然后Component注册到游戏线程可以做分时。当然最常规的减少spawn卡顿的方法还是做pooling,如果有大量同类型Actor的Spawn,建议这样做。

   

   

   

4

GC

引用分析,Destroy会分幀去做

技术分享图片

GC主要分为两步,先是引用分析,然后分析完标记可以destruct的对象会在这时开始发出BeginDestroy,而实际的Destroy会分幀去做,因为有些对象渲染线程的资源还在访问,不能当场删掉,所以只是发出一个render fence,渲染线程回收掉,我们才在下一帧主线程purge的阶段把对象删掉。

   

在整个GC过程中最费的,是引用分析,因为这个必须在当前这幀做完,新版本中我们把标记和引用分析都做了多线程并行,利用所有的核计算,可以比较好的提高引用分析的效率。还有一种手段是可以跳过大量的常驻内存的对象,我这里列了一个参数,MaxObjectNotConsideredByGC,设置这个参数范围内的对象是不会在引用分析的时候做检测的。

   

再有一点是Clustering,一组对象永远是共生的,可以规划在Clustering里面,这样的场景下GC效率可能提升十几倍。最后新版本中,我们把BeginDestroy也放到的发生GC的后一帧去做。

以上是关于(转整)UE4游戏优化 多人大地型游戏的优化渲染线程的优化的主要内容,如果未能解决你的问题,请参考以下文章

(转整)UE4游戏优化 多人大地型游戏的优化内存的优化

(转整)UE4游戏优化 多人大地型游戏的优化GPU的优化

VR游戏制作渲染优化方法

游戏常用图形渲染优化手段介绍!

cocos2d-x ios游戏开发初认识 渲染的优化

ue4 c++代码怎么获取场景中的一个actor