Notifications
Article
Unity制作人专场:《Unity AAA游戏深度优化》主题演讲
Updated a month ago
2.7 K
2
演讲人:Unity大中华区技术总监 张黎明
2019年,Unity在北京、成都、深圳分别举办了3场制作人专场活动,搭建优秀游戏制作人与Unity官方面对面交流的桥梁,内容涵盖项目优化、技术美术与解决方案等多个方面。其中深圳Unity制作人专场在2019未来商业生态链接大会暨第四届金陀螺奖颁奖典礼(FBEC2019)上特别呈现。Unity社区将陆续发布FBEC2019Unity制作人专场的精彩演说,将前沿技术传递给广大开发者、创作者,助力高品质MWU游戏的诞生。
演讲PPT下载
文末将分享演讲完整视频(建议点击链接进行观看)

以下为内容实录:

张黎明:大家下午好!
今天给大家分享的题目是Unity一款AAA游戏深度优化的经验。
首先给大家介绍一下Unity在中国提供的技术支持服务,我的团队是专门负责为中国客户提供各个方面的技术支持和解决方案。
我们提供的技术支持有不同的维度,有面向国内的大公司,比如腾讯、网易、盛大这些大公司的企业支持,我们可以按年提供技术支持,在现场帮他们优化游戏的工程,解决各种问题,或者在现场调试游戏各种崩溃问题。
另外我们也提供按天服务的形式,如果大家在游戏上线前游戏遇到性能瓶颈的话,我们也可以直接到你们办公室里做现场性能优化,并在完成后提供一个性能优化文档,给出游戏的性能数据和优化建议。
今天的案例,就是我们跟这家客户进行的一次非常深度的合作,他们在游戏上线之前遇到了比较大的性能问题,当然这个客户技术实力还是很强的,自己已经做了很多优化,这也是一个开放大场景的游戏。
当时这款游戏在高端的手机上可以跑的比较流畅,他们遇到的问题是在中低端手机上,这个游戏会出现帧率过低、卡顿的问题,我们就是要帮他们解决这些问题。
我们做性能优化的时候,首先会使用一些性能分析的工具,常见的工具会有Unity Profiler、安卓的Systrace、苹果的Instrument等工具进行分析。这款游戏我们分析之后发现是一款CPU消耗比较大的游戏,而且它的消耗基本都是在主线程里面。
我们经过分析之后,开始阶段判断处理要解决一个问题,就是CPU主线程和渲染线程的等待问题,这种现象是我们不喜欢的,因为这样会导致硬件不能得到充分的利用,帧率自然就会降下来。
另外,我们还分析了这款游戏的逻辑代码在哪里消耗比较多,还发现游戏内使用相机过多导致了额外的消耗,还有Jobsystem并行度不够高等问题,我们通过修改引擎代码的方式帮他们进行优化。
为什么会出现主线程和渲染线程等待的问题呢?因为这里有一个演示,Unity在主线程的Update完成后要等待渲染线程,有时候就会导致主线程等待的时间很久,多线程并行不够好。
后面,我们通过几种手段提升了并行度,我们2019.1之后提供了API可选择主线程同步的时间点,这款游戏是5.6上开发的,我们同过backport这个功能把这个问题解决了。
另外,我们发现这个游戏当时用了七个相机来进行渲染的,这些相机有渲染场景的、有渲染第一人称视角的手和枪的、有渲染UI的、有渲染后期处理的。Unity的相机有一个问题是Camera本身会有一定的固定消耗,不管这个相机渲染的对象有多少,都要进行相机剔除,耗时比较大。
虽然这个游戏里面有很多相机都是渲染数量很少的物体,但是还是导致主线程的消耗比较大。
为了解决这个问题,我们使用了一个方法,引擎默认的方式是每个相机独立执行,这样每个相机的job数量不多的情况下,多线程并行度不是很好。
我们当时的解决方法就是把相机这部分代码进行了修改,把所有相机的Culling都放在前期一起执行,这样就可以让多线程并行度更好一些。
多线程的数量、Job的数量、粒度都会影响性能,这里提一点,多线程的唤醒是我们这次优化的时候发现的一个重要因素,过去大家开发PC游戏的时候对这一点感受不会很深,因为在PC上一个线程唤醒的耗时很短,但是这次我们发现在一个中低端手机上唤醒的话,耗时比较大。
我们发现某些中、低端手机会出现主线程进入睡眠状态的情况。针对这种情况,我们可以考虑在一些比较低端的手机上把一些Job放在主线程执行。
另外,Unity其实后台开了很多线程,默认WorkerThread是64个,但是经过我们测试发现,线程数量不是越多越好,尤其是在中低端手机上,线程越多反而会导致性能越差,我们测试某些低端硬件上减少WorkerThread数量以后,测出来性能反而会更好一些。
我们在2019.3引擎提供了一个API,让你可以设置有多少Job Thread,当时这个游戏因为是5.6上,我们只能在引擎底层进行修改,backport这个功能到5.6版本,让他们也可以使用这个功能。

另外,关于JobSystem,还要考虑它的粒度,如果粒度太大,利用多线程进行计算的话,在中低端手机上性能会比较差。我们希望Job的粒度尽量小一点,这样可以均匀的分配到各个线程上执行。
刚才提到了对比较大粒度的Job可以进行切分,比如在Profiler中看到Shadows.CullShadowCastersWithoutUmbra和CullSceneDynamicObjects粒度过大,进行切分之后性能提高了很多。
刚才我们提到的方法,不一定对各个游戏都适用,需要经过分析之后根据自己的情况,来决定使用哪种策略。
刚才主要讲的是多线程并行的优化,这款游戏是开放大世界,也有可能在当前屏幕里面看到很多角色,这些角色需要同时更新。骨骼动画的更新都是非常耗时的,我们当时进行了几个方面的优化,首先考虑开放大世界的话,有的角色离的很远,可能在几百米、一公里以外,有的角色却很近,就在你眼前,如果都每秒30帧刷新的话,是没有必要的。
如果想修改更新频率的话,有一个简单的做法就是可以选择关闭Animator自动更新选项,然后通过脚本调用,但是这个办法有一个缺陷,如果通过脚本调用的话,会导致Animator内部的多线程运算被禁用。
我们分析之后,认为还是要通过引擎代码修改的方法,当时我们实现了一个功能,根据Animator占屏幕面积的比例来决定更新频率。比如一个角色占屏幕面积的5%,就可以按5帧去更新,如果占屏幕面积20%按照30帧去更新。
经过测试,我们看到Animator整体消耗比优化前提高了20%。
对于这款游戏的内存消耗,我们从几个方面进行了优化,比如Mesh内存优化、纹理内存优化、Shader内存优化。
Unity提供了一个功能叫做Shader Variant Collection,可以加载一部分Shader的变体,但是无法控制Shader变体的卸载,也不能只加载一部分Shader LOD。如果只用SVC这个功能的话,还是不能解决Shader内存爆炸的问题。
当时我们改了Shader加载的这部分代码,通过API来决定加载或卸载某一个级别的Shader LOD或者Shader变体,这样可以减少中低端手机上Shader内存的消耗。
另外,对Mesh的话,因为这是一个开发世界,如果都用高模的话,会导致内存耗时比较大,现在常见的做法是适用LOD Group。
但是LOD Group存在一个问题,假如有一个房子你给他三级LOD的Mesh,引擎这部分会把三级Mesh都加载到内存,导致内存消耗过大。
针对这个问题,我们有非常完整的开放世界解决方案。当时因为这个项目是在5.6开发的,还用不了这个方案。我们就提供了一个简化的方案,让他们在运行的时候可以卸载某些用不到的Mesh LOD,这样在中低端手机上就可以只适用一个级别的LOD。
另外,大家可以关注的就是碰撞体,可能有的模型会直接拿渲染模型来做碰撞计算,但是Unity物理引擎保存两份碰撞体内存,消耗比较大。
对纹理的话我们有几个建议,首先在中低端手机上,可以选择使用Quality Setting里的Texture Quality选项,适用降低一般分辨率的纹理,纹理内存消耗可以减少到之前的四分之一。
另外,引擎在2017之后的版本,提供了一个功能,叫做Texture Mipmap Streaming, 可以动态加载某个Mipmap级别的纹理,如果有个模型离相机比较近的话,可以加载分辨率比较高的,如果离相机有一定距离的话,就加载比较低级别的Mipmap,这样也可以减少内存的消耗。
因为要做纹理动态加载的时候,有时候会出现一些卡顿,2017版以后,我们也加了一个功能叫做Texture Async Upload,可以减少纹理加载的卡顿,建议大家可以把这两个功能都用上。
Profiler里也可以看到Asset Bundle的Serialized File内存,每个AB会有14KB的内存消耗,实际测试发现,可以把这个14KB得cache内存调整为4KB,加载性能也没有明显差别。这样我们把AB内存的消耗减小了十几兆,另外,Asset Bundle也可以考虑把Type Tree关掉进一步减小内存消耗。
还有一个小的改进,是优化Postprocess Volume的计算消耗,因为它每帧都会计算多个Volume的插值。我们实现了一个基于Trigger的后处理Volume,只在触发Trigger的时候会有计算的消耗。
另外再介绍一下我们新推出的UPR性能分析工具,可以通过这个链接进行访问upr.unity.cn
这些工具的背影是这样的,首先UPR是基于Unity自带的Profiler数据进行分析,我们通过一些手段把这些数据发送到UPR的服务器上面,然后在服务器端对性能数据进行分析,并生成性能优化报告。
这里跟大家解释一下,既然我们有了Profiler这个工具,为什么还要用UPR进行性能分析呢?URP有几个优点,首先Profiler能保留的数据时长很小,不能做长时间的性能数据分析。
UPR可以解决这个问题,有用户测试了四个小时的游戏性能数据,我们也可以正常的分析出最终的结果。
另外,UPR支持截屏的功能,如果长时间测试的话,回头看数据的话可能不知道某个性能峰值是玩到了游戏哪个阶段,所以我们可以通过截图来回忆当时在什么阶段出现的问题。
Unity Profiler只能看某一帧的数据,不能统计一段时间的数据,如果我们想看几十分钟内所有函数平均消耗时间的排名,Profiler里面是看不到的,但是现在在UPR里面就可以看到。
现在我们正在开发一个功能,是提供优化建议,跟过去我们性能优化的经验,总结出当某个函数多的时候,给出建议应该怎么优化。
我们提供了几种方式使用来适用UPR,首先我们有UPR APP,现在只有安卓版,大家下载以后可以通过这个APP启动你的游戏,然后游戏的性能数据会经过这个APP转发到我们的服务器上。
第二种使用方式是我们开发了 winUPR的工具, 它是一个PC端的应用数据,可以接受各种平台上Unity应用的Profiler数据,winUPR再把这些数据转发到我们的服务器上。
我相信,大部分大规模的游戏都会有CI系统,每天定时出版本。如果我可以每天出的版本自动跑一些性能测试,这样就能对不同时间的性能数据进行对比,如果某一天性能变差的话,我们就可以及时发现,更容易发现性能变差的原因。winUPR就可以和你的CI系统结合,通过命令行执行。
第三种方式就是Unity中国版,刚才俊波也给大家介绍了,在中国版里面,我们不需要用APP和winUPR。只需要使用中国版Unity Editor,就可以转发Profiler数据到UPR服务器。
上面所说的一些性能优化未来会加入到中国版Unity里面。
UPR未来有什么开发计划呢?首先我们会重点做优化建议,提供更多的自动化性能优化的建议。
另外,也会在中国版Unity里面会收集更多性能数据类型,比如前面提到的Shader性能数据。
还有我们正在开发一套分布式Build Assetbundle, Build Player,Bake Lightmap的工具,最终会整合到一个CI系统里面,它也可以和UPR进行整合,同时也会引入第三方自动化测试框架。
未来,我们也会做更多的数据挖掘,可以在UPR里面对比某一个游戏的性能与其他同类型游戏的数据差异。
这就是我分享的全部内容,谢谢大家。
温馨提示:在Unite Shanghai 2020,我们将邀请经典的Made with Unity作品制作人与开发者现场分享经验心得。Unite Shanghai 2020暖冬特惠票正在火热销售中,热爱游戏的开发者一定要抓住最优惠的时刻哦。
点击抢购暖冬特惠票:
https://connect.unity.com/p/unite-shanghai-2020zheng-shi-qi-dong
Unity AAA游戏深度优化》主题演讲 完整视频
https://v.qq.com/x/page/c3040zvtpm9.html
Tags:
Unity China
737
Comments
B
BlusWen
a month ago
奥里给
0
Alien X
a month ago
给博主点赞!!!有干货的
0