【程序】Unity 性能 Review

发表于2018-01-21
评论6 5.4k浏览

本文首发于知乎专栏:MACK的游戏开发笔记,欢迎各位关注。


年初的一次优化笔记,第一篇。每个版本一周,每个版本一篇已经6,7篇了。过早的优化可能并不是最好的,但是每个版本每个月都要上线测试。始终觉得对于实时多人竞技游戏来说,流畅的重要性超过画面。

使用Unity的Profile先简单分析

  • 角色更新过高,后面更详细的分析。
  • 屏幕边缘图标和Wifi过高。优化方案:频繁设置UI文字和贴了可以优化。
  • 放技能开销非常高,每个子弹有独立弹道逻辑,技能释放频率比价高。优化方案:类对象使用内存池,delegate优化减少gc,更完善的预加载机制,逻辑优化等等。CMT的timer使用内存池。
  • 部分非技能的CMT没有预加载。
  • 召唤物没有预加载。
  • 部分道具有些没有预加载。
  • 消息处理非常卡。解决方案:使用类对象内存池,优化序列化方式不使用protobuff的。
  • 帧操作增减人会卡。解决方案:推测玩家行为预加载。
  • 飞行道具开销很高,特别是Deactive上。解决方案:可见性使用layer,使用内存池
  • 地图脚本开销较高。
  • Camera.main直接调用开销较高。解决方案:因为Unity会遍历所有的Camera,是一个线性时间。在Update中调用的话,缓存一下main
    camera。


使用Unity的FrameDebugger简单分析

  • 俯视角开启了天空球渲染,其实没必要


使用Unity MemoryProfiler分析内存占用和内存泄漏

通过memoryprofile抓帧分析,内存占用不高,但是RenderTexture占用非常高63.5M

  • 大厅场景RenderTexture 63.5m。解决方案:因为使用了景深,扰动,屏幕校色,实时阴影,等后期处理效果所致,我们根据玩家手机配置做了自适应和限定,并且再图形配置选项允许玩家做一些调整。景深开销非常高因此关闭了实时景深效果使用其他方法代替。
  • 战斗场景Texture 33.7m Mesh 14.9m animationclip 3.7m Audiomanager 4.1M。解决方案:贴图压缩,使用shader减少换色贴图,修正一些贴图引用不释放导致的内存泄漏。
  • LockstepMnanager.FixedUpdate函数 每帧分配10kb以上内存。解决方案:各种GC相关的优化。
  • SceneLoader.update函数(315 bytes) MainLoop.Update函数(202bytes), NetManager.update函数(186bs) CharacterActor.LateUpdate函数(392 bytes)皆为每帧都分配。GC的分配每帧超过200b就算大了。解决方案:各种内存优化,消息使用内存池等等。
  • MainLoop.OnGUI函数每帧分配内存 300b。解决方案:删掉空函数。
  • 大厅场景Camera.Render cpu等待gnu渲染,造成时间帧率抖动 25ms-> 43ms 体现为Camera.Render self时间大幅变动 (优化方法 降低场景面数,拆分Gm_Cabin模型)
  • Loading贴图存在伪内存泄漏,因为图集和按钮放在一起了被按钮引用。
  • Unity技术支持提供了脚本插件可以分析代码中所有静态的引用。发现是lua的LuaScriptMgr中会对一些贴图有引用,需要注意使用Lua导致的内存问题。


使用XCode做详尽的性能优化

XCode也可以做GPU的性能分析。编译xcode工程需要Development版,启动,GUP的优化点需要在Run的面板里设置成metal否则有些堆栈看不到。点FPS,然后点相机Capture一帧,可以看到帧率,CPUGPU的占比,渲染时每个函数的开销。点Shader里面的看更详细堆栈,可以看到每个shader的每行代码的开销占比。

  • 这个版本大厅很简单但是非常非常卡,看到UI和景深开销占比比较高,UI占了25,景深占了30。看到UI费是采样一张贴图占了50但是这张贴图并不大,景深费是因为shader里lerp了很多次导致开销很高。在FrameDebugger里可以看到UI最后渲染了一个全屏的面,可以看到每次渲染了占用的时间比,这个面实际没用。关闭了这个UI的全屏面,关了景深和后处理,基本就没开销了,连UI的渲染也只有2ms了因为景深把总线带宽给占满了,其中最费的还是景深效果。
渲染线程开销也较高
  • 版本机出的版本在XCode中会报错APPLE MACH-O Linker Error,无法做性能分析。解决方法:改成il2cpp了。
  • 修复字符串拼接的量级,使用StringBuilder等。
  • OnGUI,FixedUpdate,Update等空函数也会有gc开销,因为会产生从C++到C#层调用的开销。最好都去掉。
  • FMOD音效模块开销过高。我们没使用官方的FMOD使用了FMODStudio,测试版资源为了效果和开发方便使用了一些事件内的效果器。优化后音效占用10-15%。
  • 动画开销较高,使用了动作融合,动画状态机较复杂。解决方案:不可见的角色不更新动画,一些顶点受骨骼影响数可以设置很小,小怪等简单角色使用Animation,动画的一些优化等。
  • UI,粒子,动作开销较高,并且多线程开销也高。
  • 可以看到每个模块开销比较平均,已经做了很多优化了没有明显瓶颈,只能一个模块一个模块的抠了


其他一些测试以及和Unity官方支持的问答:

  • Unity的物理比射线省,因为会优化做空间划分,只计算较小范围。Navmesh最优化。
  • 迷雾和可以性检测可见性预缓存,预缓存分块加载,迷雾进行分块划分优化,分担计算量到多帧执行等等
  • Resource目录资源多会导致Unity进游戏时间特别长,因为会把Resource下所有资源做一次检索,导致启动慢。建议使用Assertbundle,也支持压缩。另外提升进入游戏速度的就是,第一个场景足够简单足够小。解决方案:开发了热更的资源管理,每个资源有一个唯一ID,可以存在Resource下也可以存在Assertbundle中。
  • Assetbundle的新算法是按Chunk加载,不会把整个Assertbundle都加载进来但是压缩比小,压缩的时候要选择ChunkBase。如果bundle加载好之后资源的引用时放在bundle上,如果a界面打开bundle包读取贴图,a界面关闭则贴图会释放,如果a没关闭,b界面读取贴图b界面会对应到同一张贴图上。
  • 建议bundle放在StreamingAssets目录下。
  • Profile分析建议:Overhead,是Unity没统计到的时间,用总计时间减去剩余的时间,一般包括C++到C#层的调用开销,场景复杂度,垂直同步等。大厅因为Overhead卡是因为CPU在等GPU,建议用XCode看。
  • 通过Profile.BeginSample自己插代码对怀疑的代码进行内存和性能调试。
  • Unity5.3.5p8修改了Foreach装箱拆箱导致的内存开销。但是没有修改mono的gc。实际测试可以用Foreach了。
  • 部分Android设备总线太烂导致GC会特别卡
  • OnGUI,FixedUpdate,Update等空函数也会有gc开销,因为会产生从C++到C#层调用的开销。最好都去掉
  • 部分3G的Android设备,实际可用内存可能只有500M。Unity启动时最高能占到80M,一般在40-50M
  • unload切场景自动调,gc也会自动调
  • 编辑器下看到的资源引用计数不准确,得看真机。实际测试的时候老发现很多资源释放不掉,但是到真机测试发现实际已经释放掉了,因为编辑器模式会缓存一些资源,所有的测试都应该以真机为准。
  • lua导致gc太多,没有比较好的解决办法,把lua的代码放到C# 层
  • IOS上如果限45帧对IOS来说就是限30帧,低于30帧才是真实帧率
  • CPU优化:ulua的计时器开销很大。其中PreloadManager是Unity的各个模块的预加载。
  • GPU优化:Mask会很费。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论