这篇文章不长篇分析代码了,因为部分工作和想法由于时间、成本关系我也没完整验证,开个短文章讨论,或许有相同问题解决经验的朋友也能带来一些想法(之前也确实听其它公司的同学聊过一些他们遇到的问题)。
这里主要考虑业务代码大体合理,由于引起引擎设计本身的卡顿,业务代码自身问题带来的卡顿另做考虑
1.注意SetActorLabel,编辑器中很多项目可能有在Outline中重命名物体显示各种id的需求(刚需),比如服务器下发一个对象后需要将这个对象命名中包含它的服务器id、配置id等方便策划以及debug。
而UE的SetActorLabel会引起flush加载的,意思是只要你重命名,这个时候在异步加载的Package均会被flush,卡顿也就是显而易见的了。但实际发现在PIE中重命名时flush并不是必须的,所以可以考虑扩展一个不flush的函数,供gameplay中创建服务器对象设置名称这种情况使用。
2.UE Editor Package加载机制原因:Editor下Package加载就是“落后于”移动端(Cook平台):任何同步加载会flush异步加载,本身Tick效率也更低。个人觉得这是比较关键的导致差异性的原因,可能很多人会想这种和平台硬件没关系的代码为什么不能做到PC和移动端代码一致?这样对业务开发、debug、性能内存分析各种地方都有巨大好处。但可惜UE4设计下就是不行,不是简单改一两行代码就能做到的,它背后涉及太多可以让你放弃这个想法。
UE4 Package加载的分析可以阅读一下别人的文章
UE4加载模块分析笔记(一)
UE4加载模块分析笔记(二)
其实评论区就不止一个人有疑问,为什么Editor设计成这样,这也是我刚看到时的最大疑问,后面我专门去问了Epic官方,官方给出的回答
概括:
EDL:需要Cook,为什么一定需要Cook才行,主要考虑是蓝图
ALT:实际上是和EDL代码一一对应的(我本人也尝试过在代码中强行开启多线程的开关,确实如此,UE的LoadPackageInternal就写死了check主线程),在Editor下这个配置实际上是假的:
那么可不可以改?Editor下可不可以同步加载时只flush所依赖的包?我觉得理论上是可行的,我确实也做过一些尝试能大体正确地跑起来,但因为改得太底层怕有一些没考虑完全的地方,所以一直只当做实验性的工作,有问题长期发现。
想要修改这个机制的话可以考虑只Tick需要的AsyncPackage及依赖的PendingPackage,以及在UObject层面防止重复加载,注意需要测试有的包在包一级循环依赖等各种复杂的情况(你可能会发现CreateImport、LoadPackageInternal在一个异步包的任意阶段都会被调用)
不过反正这里UE5也在重构,官方说他们也在修改,所以也可以考虑等一下官方吧(截止目前UE5.1 Editor同步加载还是会flush)
3.强制垃圾回收,UE4地形加载中部分代码会主动垃圾回收,Epic这样做或许有其它考虑,但实践发现这就是导致了很多的卡顿,可以根据情况去掉(地图比较大的项目,分地块用到了流式地形加载或许会比较容易遇到)。
4.部分机器下电脑管家会影响,不一定所有人的机器都会遇到,但我确实遇到过,开启和关闭电脑管家立马就能感觉到了。
具体分析会发现UE的AsyncLoad,RequestLevel,RenderData Streaming,DDC背后都有涉及文件操作、简单调用操作系统API的卡顿。