UE性能优化实践
内存(空间)/ 帧率(时间)
内存
IOS的内存要比Android小很多。例如,21年发布的IPhone13内存是4-6G,小米12内存是8-12G,相差一倍。近年的高端机则不再有这么大的差别,25年发布的IPhone17内存是8-12G,小米17内存是12-16G。
相比Android,IOS能够使用更小的内存实现系统的流畅运行,一方面是因为编程语言上的区别。IOS使用Objective-C、Swift,会被直接编译成在CPU上运行的原生代码,无需任何解释器和虚拟环境;Android使用Java、Kotlin,依赖JVM进行解释执行(Kotlin/Native支持将 Kotlin代码编译为无需虚拟机就可运行的原生代码,这个差距正在缩小),而使用JVM会增加app使用的内存。例如YouTube,在IOS/Andorid上的内存使用分别为176/282MB,差距明显。
对于游戏而言,比如Unity和UE,在IOS/Andorid上都是通过编译为原生代码运行,因此内存使用相差不大。例如原神,在IOS/Andorid上的内存使用分别为1270/1400MB。差距可能是因为纹理压缩方式、着色器编程语言的差异。
IPhone手机不仅内存更小,IOS的内存管理策略也更加严苛,系统在内存紧张时会通过Jetsam直接终止占用过多内存的前台进程。对于IOS,app的内存占用应在50%以下,才能避免闪退的风险。因此,游戏内存优化的需求主要来自于旧款IPhone手机。
首先,要避免内存泄漏,这是最基本的要求。减少内存使用大致有压缩、复用、牺牲表现、及时释放四种方法。通过UE自带的Insights、memreport、LLM,外部工具VMmap、keymob等,可以检查内存使用情况、发现可以优化的瓶颈。
GC
UE的gc.TimeBetweenPurgingPendingKillObjects默认是61.1秒,针对不同平台设置不同的内存阈值,当FPlatformMemory::GetStats().AvailablePhysical小于阈值时,调用ForceGarbageCollection进行强制GC。
物理
对于静态网格体,不需要碰撞时Collision Presets选择No Collision,有碰撞时Collision Complexity优先使用Use Simple Collision As Complex。简单碰撞优先使用简单几何碰撞体,然后再考虑凸包。如果CPU端不需要几何数据,关闭Allow CPUAccess。
对于骨骼网格体,如果CPU端不需要LOD数据,关闭Allow CPUAccess。
FaceRemap即碰撞体经过Cook之后依然保留的面重映射表,以在场景查询中支持物理材质。若不需要,在项目设置中开启Suppress Face Remap Table;即使没有开启,只有勾上了Support Physical Material Masks的静态网格体才会生成FaceRemap。
如果项目不依赖于通过碰撞检测结果来找回UV坐标的功能(FindCollisionUV),可以在项目设置中关闭Support UV From Hit Results。
帧率
通过UE自带的Insights,定位耗时高和引起帧率波动的函数进行优化。
GC
对于GC,设置gc.MaxObjectsNotConsideredByGC和gc.SizeOfPermanentObjectPool为合适的值,从而降低GC消耗。若要设置精准的值,从游戏日志中找到如下内容,将第一个数设置为gc.MaxObjectsNotConsideredByGC,第二个数设置为gc.SizeOfPermanentObjectPool。
1 | |
Game Hitches (GC Mark Time?) - Programming & Scripting / C++ - Epic Developer Community Forums
加载
对于副本中会用到的角色/AI资源,在副本加载时即进行预加载,在副本中战斗时便不会出现因为加载资源发生的卡顿。
对于不是频繁加载/创建且生命周期较长的特效(如关卡中的特效),避免在蓝图中直接使用特效节点,使用自定义的特效组件进行异步加载管理,以免引起同步阻塞。
物理
对于静态网格体的碰撞体,应尽量使用简单碰撞。简单碰撞中应优先使用简单几何碰撞体(球体USP_
> 胶囊体 UCP_ > 盒体
UBX_),然后再考虑使用凸包(UCX_)。
对于骨骼网格体对应的物理资产,针对不同平台制作不同级别的简化物理资产,同时保证在收到打击时(AddImpulse)的布娃娃表现一致。比如,若bVelChange为true,物理资产及其简化版应保证受击骨骼质量一致(动能一致)、总质量一致。
工具
Insights
通道gpu、bookmark、frame、cpu、log默认启用,其他通道可自行选择开启,追踪内存的话开启memory通道即可。
追踪编辑器独立进程游戏/游戏包体:
1 | |
编辑器cmd追踪:
1 | |
LLM
在独立进程游戏中运行时添加额外启动参数-llm,使用stat llm系列命令即可查看当前内存占用信息。llm即Low
Level
Memory,会统计UE中所有能被追踪到的内存占用,不走FMemory申请的内存无法被追踪到,比如第三方库。
memreport
命令行中运行memreport -full,会将当前帧的所有内存资源导出,如果开启了LLM则也一并导出LLM,位置在Game\Saved\Profiling\MemReports,后缀为.memreport,可以以文本格式打开。里面的信息包括物理/虚拟内存总计、UObject统计等。可以在BaseEngine.ini中添加[MemReportFullCommands]配置输出信息。
1 | |
VMmap
对于UE中无法追踪到的内存,可以借助这个工具进行追踪,比如代码段和静态数据段的内存占用。
keymob
下面是IPhone14Plus上不同开放世界手游的性能对比(默认设置,初始地图跑图,统计取平均值):
| 原神 Yuanshen | 鸣潮 Client | 无限暖暖 X6Game | 二重螺旋 EM | |
|---|---|---|---|---|
| FPS | 33 | 34 | 33 | 30 |
| 内存 GB | 1.76 | 1.77 | 2.12 | 2.24 |
| CPU利用率 % | 95.12 | 50.82 | 78.76 | 86.74 |
| GPU利用率 % | 33 | 53 | 70 | 71 |
CPU利用率=各核心使用率之和,IPhone14Plus的CPU为6核,单进程最大理论占用率为600%。
GPU利用率:所统计的设备利用率、渲染器利用率、Tile利用率基本一致,用GPU利用率概括。