Unity CPU优化卡顿

当我们打开一个包含大量数据的新场景或背包界面,往往会比较卡顿,因为你大概的时候要处理这样的数据↓

解决方案无非是缩小总数据量↓

排队(缩小单位时间内数据量)↓

场景1:打开背包等包含大量数据的界面(渲染模块)
场景2:场景切换等同一时间大量instantiate的场景(加载模块&分帧加载)

渲染模块(缩小数据量)

影响渲染效率的两个最基本的参数:DrawCallTriangle

DrawCall

DrawCall是一个命令,发起方是CPU,接收方是GPU。这个命令仅仅会指向一个需要被渲染的图元列表,不会包含任何材质信息。
每次CPU准备数据并通知GPU的过程就称之为一个DrawCall。

降低Draw Call 降低DrawCall主要的方式有动态合批、静态合批、GPU Instancing、SRP Batcher。批处理系统自动进行,我们只需注意批处理的规则。GPU Instancing是Shader开关,默认开启。
使用图集,尽量不使用实时光照和阴影效果
游戏性能并非Draw Call越小越好。这是因为,决定渲染模块性能的除了Draw Call之外,还有用于传输渲染数据的总线带宽。当我们使用Draw Call Batching将同种材质的网格模型拼合在一起时,可能会造成同一时间需要传输的数据(Texture、VB/IB等大大增加,以至于造成带宽“堵塞”,在资源无法及时传输过去的情况下,GPU只能等待,从而反倒降低了游戏的运行帧率

还可以通过分块加载,无限列表等方法降低同时存在的数据量来降低DrawCall。

Triangle

Triangle(三角面)面片数越高会导致渲染的耗时越高。一般建议通过LOD工具减少场景中的面片数,进而降低渲染的开销。
需要说明的是,此处的面片数量并不是当前帧场景模型的面片数,而是当前帧所渲染的面片数,其数值不仅与模型面片数有关,也和渲染次数相关。例如:场景中的网格模型面片数为1万,而其使用的Shader拥有2个渲染Pass,或者有2个相机对其同时渲染,那么此处所显示的Triangle数值将为2万。

多线程渲染
开启多线程渲染后,主线程的渲染耗时就会有很明显下降(//todo未验证)

加载模块(缩小数据量)

Instantiate的卡顿与三部分开销相关:相关资源加载、脚本组件的序列化和构造函数的执行,并且绝大部分原因均是相关资源加载导致。 资源加载是加载模块中最为耗时的部分,其CPU开销在Unity引擎中主要体现在Loading.UpdatePreloading和Loading.ReadObject两项中
资源优化
纹理
纹理 分辨率 纹理资源的分辨率对加载性能影响较大,分辨率越高,其加载越为耗时。建议512x512
格式 纹理资源的格式对加载性能影响较大,使用 ASTC 或 ETC->Android & PVRTC->iOS
Mipmap 开启Mipmap功能同样会增大一部分纹理大小,一般来说,其内存会增加至原始大小的1.33倍.开启Mipmap功能会导致资源加载更为耗时,且设备性能越差,其加载效率影响越大
1、严格控制RGBA32和ARGB32纹理的使用,在保证视觉效果的前提下,尽可能采用“够用就好”的原则,降低纹理资源的分辨率,以及使用硬件支持的纹理格式。 2、在ASTC或硬件格式(ETC、PVRTC)无法满足视觉效果时,RGBA16格式是一种较为理想的折中选择,既可以增加视觉效果,又可以保持较低的加载耗时。

ASTC格式纹理实践

3、严格检查纹理资源的Mipmap功能,特别注意UI纹理的Mipmap是否开启。在UI中,由于不存在元素相对于相机的位置不断变化的需求,一般情况下也不需要频繁缩放图片(特殊需求除外),所以对UI而言Mipmap的存在对性能提升其实是没有意义的。2D游戏或UI中,不建议开启Mipmap

4、ETC2对于支持OpenGL ES3.0的Android移动设备来说,是一个很好的处理半透明的纹理格式。但是,如果你的游戏需要在大量OpenGL ES2.0的设备上进行运行,那么我们不建议使用ETC2格式纹理。因为不仅会造成大量的内存占用(ETC2转成RGBA32),同时也增加一定的加载时间。建议研发团队在项目中谨慎使用ETC2格式纹理。
网格
网格 数据量 资源的数据量对加载性能影响较大,面片数越多,其加载越为耗时。设备性能越差,其耗时差别越为明显
顶点属性 顶点属性的增加对内存和AssetBundle包体大小影响较大。对于加载效率影响较大,且顶点数越多,影响越大
Read/Write 关闭Read/Write功能会降低AssetBundle的物理大小,其降低量与资源本身数据量相关。同时,关闭Read/Write功能会大幅度降低网格资源的内存占用;关闭Read/Write功能会略微提升该资源的加载效率。
1、保证视觉效果的前提下,尽可能采用“够用就好”的原则,即降低网格资源的顶点数量和面片数量;如场景的静态物品背面等看不到的可以简化网格 2、尽量降低定点数,顶点属性越多,则内存占用越高,加载时间越长; 3、如果在项目运行过程中对网格资源数据不进行读写操作(比如Morphing动画等),将Read/Write功能关闭,既可以提升加载效率,又可以大幅度降低内存占用
Shader
网格 Shader资源的物理体积与内存占用虽然很小,但其加载耗时开销的CPU占用很高,这主要是因为Shader的解析CPU开销很高,成为了Shader资源加载的性能瓶颈
Mobile/Particles Additive在解析方面的耗时远小于Mobile/Diffuse、Mobile/Bumped Diffsue甚至Mobile/VertexLit
除Mobile/Particles Additive外,其他三个主流Shader在加载时均会造成明显的降帧,甚至卡顿。应尽可能避免在非切换场景时刻进行Shader的加载操作。

如果当前项目使用AssetBundle

1、通过依赖关系打包,将项目中的所有Shader抽离并打成一个独立的AssetBundle文件,其他AssetBundle与其建立依赖; 2、Shader的AssetBundle文件在游戏启动后即进行加载并常驻内存,因为一款项目的Shader种类数量一般在50~100不等,且每个均很小,即便全部常驻内存,其内存总占用量也不会超过2MB; 3、后续Prefab加载和实例化后,Unity引擎会通过AssetBundle之间的依赖关系直接找到对应的Shader资源进行使用,而不会再进行加载和解析操作。

建议:
1、在保证渲染效果和项目需求的情况下,尽可能降低Shader的Keyword数量,以提升Shader的加载效率;

2、对于简单Shader,可尝试去除Fallback操作,该方法非常适合于目前正在大量使用的Mobile/Diffuse、Mobile/Bumped Diffuse等Built-in Shader;
3、尽可能对Shader进行单独、依赖关系打包并对其进行预加载,以降低后续不必要的加载开销
4、直接去除Shader中的Fallback选项。Fallback功能是对于无法使用当前Shader的硬件设备可以使用对硬件设备要求更低的Fallback Shader来进行渲染,以保证渲染的稳定性。但是,就目前的移动市场而言,不支持Mobile/Diffuse和Mobile/Bumped Diffuse的设备已经相当少(或者说,我们目前还没遇到不支持Mobile/Diffuse Shader的设备反馈)。因此,对于使用Mobile Shader的项目,可以尝试直接将其FallBack去掉来大幅降低Keyword的数量。在我们的测试项目中,去掉FallBack功能,Mobile/Bumped Shader的Keyword从原来的39下降到12,Mobile/Diffuse的Keyword从原来的27下降到12。

Prefab优化
不要在Awake和OnEnable里写太多逻辑 切换场景加载Prefabs时,如果挂载的脚本或AddComponent的脚本 Awake和OnEnable脚本里有另外10个Prefab的实例化。那么会等这10个Prefab全部加载完成才能完成当前prefabs加载显示。 可以在Start里实现类似需求,或分帧加载,分块加载

预加载&分帧加载(排队)

通过AssetBundle 依赖关系打包将资源预先加载,将 Instantiate 的总体耗时拆分,平摊到之前帧进行执行(比如切换场景处等),从而让 Instantiate 实例化操作的局部耗时更加平滑

猜你喜欢

转载自blog.csdn.net/hzhnzmyz/article/details/118115086