UWA本地资源检测更新,助你严守项目性能的每个角落!

UWA本地资源检测更新,助你严守项目性能的每个角落!

在上一期的文章中,我们为大家简单讲解了本地资源检测2.1版本更新中与项目资源相关的7条规则。今天,我们继续介绍此次更新中,C#代码扫描检测(新增6条)、 Editor设置检测(新增3条)以及场景检测(新增3条)中新添加的各项规则。

一、C#代码扫描检测

1.该类的Update中存在引用类型的new操作

C#堆内存的持续分配会导致Used Mono持续上涨从而导致GC的触发,而GC的调用会暂停主线程从而造成卡顿。堆内存分配越频繁,GC的调用频率也就越高。因此在实际使用中我们应该尽量减少堆内存分配,特别是要避免在频繁调用的函数当中分配堆内存,以及在性能关键的时刻(比如战斗过程中)分配堆内存。

在“Update, LateUpdate, FixedUpdate”此类需要持续、高频调用的函数中,如果存在较多的堆内存分配,则会对性能造成较大的影响。对引用类型(Reference Type)进行实例化属于典型的造成堆内存分配的操作。因此,本条规则会检测出“Update类型函数”中实例化引用类型的操作,建议在高频调用的函数中避免创建引用类型的对象。具体的优化方式包括:使用值类型(Value Type)来替代,使用缓存池来复用对象等。

2.该类中存在造成堆内存分配的字符串操作

对字符串的操作是常见的堆内存分配行为。本条规则会检查的字符串操作包括:

对于字符串的操作,我们有以下优化建议:

  • 尽量避免在频繁调用的函数中进行造成堆内存分配的字符串操作。
  • 可以通过复用StringBuilder的方式在一定程度上降低字符串操作造成的堆内存分配。
  • 使用开源库中一些针对堆内存进行优化的字符串工具,如ZString、gstring等。

开源库ZString的链接:https://lab.uwa4d.com/lab/5e3dd6c08bab6aaf02417646

3.该类中存在对UnityEngine.Debug类的Log函数的调用

Debug.Log是Unity中最消耗资源的操作之一,会频繁分配堆内存。Unity也不会在正式发版时自动禁用Debug.Log。如果线上的版本当中存在对该函数进行调用,就会导致不必要的性能开销。

建议对Debug类的Log函数进行集中管理,可以使用条件编译符号对Log函数是否开启进行控制,在正式发布的版本当中避免对Log函数进行调用。

4.该类的方法中存在 Find类函数调用

Find类的函数会依据一定的条件对目标进行筛选,所以会产生相应的遍历操作,每一次的调用都是对一定范围的资源进行遍历查找,从而造成额外的计算量,导致CPU的耗时和mono堆内存的占用。

本条规则会查找“Find类型”的函数,具体的函数以及造成的性能开销如下:

项目团队可根据检测结果,减少不必要的Find类调用。

5.该类中调用的Unity函数可改为无堆内存分配版

在Unity的API中,此类函数会造成堆内存分配。而Unity同时又为这些功能提供了相应的“无分配版本”。UWA建议,如果要高频调用此类API,可将其改为NonAlloc版本的函数。以下表格中列出了本条规则会检测的函数以及对应的“无分配版本”:

需要指出的是:NonAlloc的函数需要预先创建容器来存放射线检测的结果,需要对容器的大小进行预先设置。如果预设容器的size太小会导致数据丢失,需要小心考虑预设的size。

例如使用无堆内存分配的射线检测碰撞:

6.该类中存在对特定容器的foreach遍历

使用foreach对特定容器进行遍历,可能会产生GC,对项目性能造成影响。该现象在Unity 5.2及以前的版本中体现得较为明显。Unity在5.3及以后的版本中进行了一定程度的优化,对于一部分容器,可以避免或降低使用foreach遍历造成的堆内存分配。

我们在此处引用技术博主Jackson Dunstan所作的测试结果(https://www.jacksondunstan.com/articles/3805)供大家参考,该博主分别使用Unity5.6版本和5.2版本进行了实验,如下图所示:

表格来源:https://www.jacksondunstan.com/articles/3805

UWA基于上表中会产生GC的容器类型,在Unity 2020.3.15f上,进行了进一步的验证:

依据上述内容,项目团队可以对本条规则筛选出来的代码,进行进一步判断,可以采用for循环替代,或者换用其他合适的容器。


二、Editor设置检测

1.全局各向异性设置为forcedOn

关于纹理的各向异性,我们在之前的一篇文章《特效优化2:效果与性能的博弈》中,对纹理的Aniso Level做过简单的讲解。总的来说,纹理各向异性的使用与否,是对纹理表现效果和GPU性能开销的权衡考量。

在Unity的Project Settings-Quality的全局各项异性(Anisotropic Textures)内有三个选项:

Disabled——项目内禁用Texture的各向异性设置。
Per Texture——项目内遵循各个Texture自身的各向异性设置。
Forced On——强制开启各个Texture的各向异性设置,其中各项异性等级为1~9的Texture的等级会被提高成9,各向异性等级为9以上的Texture的等级会被固定在9-16之间。其中,各向异性设置为0的Texture不受影响,依然为0。

当以倾斜的角度观察物体(特别是地面)时,各向异性过滤方案会提升显示效果。但该方案会造成一定的GPU开销, 特别是对移动端来说,会有很明显的性能消耗。

Forced On的全局各向异性设置,将所有开启各向异性的Texture的Aniso Level都进行强制性的设置,很有可能造成不必要的性能压力,所以UWA建议在有各向异性需求的项目内,选择Per Texture以更好地达到性能与表现效果的平衡。

2.ProjectSettings中没有勾选Optimize Mesh Data

勾选Optimize Mesh Data,Unity在Build时会剔除没有被当前Material所使用的各项顶点属性(法线、UV通道等),所以UWA建议开启此选项。

需要注意的是,如果在项目运行时改变了相关Mesh所使用的Material,那么对应的资源可能会因为新的Material使用了之前被剔除掉的有关属性,从而导致渲染错误,对此项目团队需要进一步关注。

3.代码剔除设置未达到最优

在Unity项目中,引擎本身以及对第三方插件的引用等,都会使得项目中存在相当部分对工程没有用的代码。这时候可以使用Unity 2018.3之后提供的代码剔除功能。

通过对Project Settings->Player->Other Settings->Managed Stripping Level的设置来调整代码的裁剪程度,Unity会对场景内未使用的代码进行相关的裁剪,减少代码量,缩减包体大小。

需要注意的是,Unity无法识别代码中涉及到的反射调用,所以当项目在实际运行时使用到了被裁剪掉的相关代码,就可能导致运行出错以及其他各种突发情况。

Unity提供了两种方式用以在开启代码裁剪的同时,保留目标代码:
1、使用[Preserve]标签,可以对Assembly、Type、Field、Properties、Method使用。
2、使用Link.xml文件,在文件中写明需要保留的代码,使用范围同上,还可以标记保留整个Namespace。

UWA建议选择Medium或者High以最大程度裁剪代码, 并使用[Preserve]标签保留必要的代码。但需要注意如果Project Setting -> Player -> Configuration->Scripting Runtime Verision设置为.NET 3.5,Medium或者High等级的裁剪都不会生效

详情可参考以下文献:https://docs.unity3d.com/cn/current/Manual/ManagedCodeStripping.html


三、场景检测

1.场景中带阴影的实时光数量超过阈值

对移动端而言,使用带有阴影渲染的实时光照在性能上是十分“昂贵”的。特别是在使用“前向渲染”(Forward Rendering)时,对于带有阴影的实时光,Unity会对所有设置了Cast Shadow 的Object进行实时阴影渲染,每一个开启阴影的实时光照都会产生较高的渲染开销。如果在同一个场景下使用多个实时光照,那么对CPU和GPU都会带来较大的压力。

一般情况下,场景中只存在1个开启了Shadow的实时光照,基本上就可以满足需求。如需要多个光照,UWA建议关闭其他光照的阴影渲染。

2.场景中有开启了Deferred Rendering的相机

相比于前向渲染,延迟渲染(Deferred Rendering)所耗费的运算资源几乎不受场景中物体复杂度的影响,在复杂场景、多光源渲染中具有一定的性能优势。但该技术方案会造成较高的GPU带宽开销,这一开销对于移动设备的性能影响是难以忽视的。因此,本条规则会检测出场景中将渲染路径设置为Deferred的相机,研发团队可根据项目的实际情况进行判断,考虑Deferred路径是否为误开启。

3.场景开启了RealtimeGI

如果开启RealtimeGI(实时全局光照技术),Unity的实时光照渲染会同时对直接光照和间接光照进行计算,这会导致项目在运行时有较大的性能压力。如果关闭RealtimeGI,Unity在运行时,只会对直接光照进行实时计算。通常而言,我们会将间接光照的效果通过预计算的方式烘焙到光照贴图当中,避免在运行时实时计算间接光。出于对项目性能表现的考虑,我们不建议开启RealtimeGI。

以上,便是此次本地资源检测更新中关于C#代码扫描检测、 Editor设置检测以及场景检测中新增的各项规则,为大家对项目的静态资源检测,提供了更多更全面的检测规范。

此外,本次2.1版本的更新,我们还对原有的部分功能和规则进行了进一步的拓展:

1、本地资源检测的特效检测功能支持URP项目;

2、规则“引用纹理尺寸大于阀值的粒子系统”开启阈值设置,现在可以检测特定纹理尺寸范围的粒子系统;

3、规则“引用网格面片数超过阀值的粒子系统”开启阈值设置,现在可以检测特定网格面片数的粒子系统;

4、规则“粒子数上限超过阈值的粒子系统”开启阈值设置,现在可以检测特定粒子数的粒子系统;


疫情当下,个人健康防护仍需严防死守,大家的项目也是如此。即刻登录UWA官网,下载最新的SDK文件,用本地资源检测,为项目的静态资源提供全面检测保护!