本帖最后由 清风 于 2015-8-3 10:05 编辑
在今年的CJ CGDC 中国游戏开发者大会上,来自 unity 大中华区的技术支持经理张鑫带来了关于《全新的Unity 移动游戏 优化解决方案》的精彩主题演讲。本次演讲分享的内容,包括从渲染模块、物理模块、动画模块的CPU优化;如何对堆内存的管理以及面对内存泄露和资源冗余的解决方案;以及对代码的优化处理。
CPU优化
首先通过Profiler来找到具体的瓶颈,通过Profiler可以看到每一帧里每个函数的具体开销。
如果我们的代码逻辑很复杂,上万行,那么可以借助Begin/EndSample 来拆分,得到真正开销大的代码块。
在移动设备上,半透明渲染的开销是需要特别注意的(像花,树,草等),因为在移动设备上会造成更多的overdraw
不同的设备对Drawcall的敏感度不同,因此可以针对设备做一个“LOD”,在高端机上允许更多的drawcall(即可以开启更多的特效等)。
MeshSkinning.Update 是蒙皮计算的开销,Animator.Update 是骨骼动画的更新开销。OptimizeGameObject的优化选项默认是关闭的。在红米上,一个100人的测试案例中,开启之后,前者可以提高70%的效率,而后者可以提高30%的效率。
另一个测试也是在红米上,200人同屏,单个角色400个顶点,560面左右,动画的总时长为6秒,骨骼数量平均为12个。通过序列帧的形式实现skinnedMeshRenderer到MeshRenderer 的转换。可以跑到25帧。
BakeMesh (SkinnedMeshRenderer.BakeMesh)
上图是一个Ui的例子,由一位工程师在两周左右的时间完成,美术除外(来自scaleform在Assetstore上的资源)。
UI系统的渲染顺序是由UI元素在Hierarchy中的顺序决定的。而Ui系统会通过重排UI元素的渲染顺序来减少drawcall,但前提是不改变渲染结果。因此发生重叠之后,drawcall可能会上升。
内存管理
游戏制作的中后期通常会开始遇到内存上的 问题 。
Mono内存有80%的团队都不太关心,但却是很重要,会影响游戏的流畅性。
StackTraceUtility.PostprocessStacktrace ()
StackTraceUtility.ExtractStackTrace()
Log的输出不仅会消耗CPU,同时也会引起较大的堆内存分配。
内存泄露
代码优化
游戏优化深度检测:
这部分耗时的避免,可以通过将shader打入单独的assetbundle包,并在进入场景时就进行预加载。
同一时间Load过多的非“基础”AB,且不及时Unload,会导致过多的内存消耗,如WebStream 和Serialized File 的内存。
而PersistentManager.Remapper 的意义是:PersistentManager维护资源的持久化存储功能,Remapper保存的是加载到内存的资源heapID与源数据FileID的映射关系,但Remapper是memory
pool,只增不减,因此建议不要一次性加载过多的assetbundle,而是以流式的方式来加载,以减小其峰值。
小结:性能优化是没有定式的,它需要“因时因地”制宜。
在今年的CJ CGDC 中国游戏开发者大会上,来自 unity 大中华区的技术支持经理张鑫带来了关于《全新的Unity 移动游戏 优化解决方案》的精彩主题演讲。本次演讲分享的内容,包括从渲染模块、物理模块、动画模块的CPU优化;如何对堆内存的管理以及面对内存泄露和资源冗余的解决方案;以及对代码的优化处理。
CPU优化
首先通过Profiler来找到具体的瓶颈,通过Profiler可以看到每一帧里每个函数的具体开销。
![](http://www.manew.com/data/attachment/forum/201508/03/095405zze6l6nqjn6sn6uv.jpg.thumb.jpg)
自身逻辑代码效率
![](http://www.manew.com/data/attachment/forum/201508/03/095405ifstlxcl44razvol.jpg.thumb.jpg)
如果我们的代码逻辑很复杂,上万行,那么可以借助Begin/EndSample 来拆分,得到真正开销大的代码块。
渲染模块效率
![](http://www.manew.com/data/attachment/forum/201508/03/095405i1jsjkj6ukjj5cyc.jpg.thumb.jpg)
在移动设备上,半透明渲染的开销是需要特别注意的(像花,树,草等),因为在移动设备上会造成更多的overdraw
![](http://www.manew.com/data/attachment/forum/201508/03/095405te4jcxg99xeaxxat.jpg.thumb.jpg)
不同的设备对Drawcall的敏感度不同,因此可以针对设备做一个“LOD”,在高端机上允许更多的drawcall(即可以开启更多的特效等)。
动画模块效率
![](http://www.manew.com/data/attachment/forum/201508/03/095406rl81dvl11v116d8l.jpg.thumb.jpg)
MeshSkinning.Update 是蒙皮计算的开销,Animator.Update 是骨骼动画的更新开销。OptimizeGameObject的优化选项默认是关闭的。在红米上,一个100人的测试案例中,开启之后,前者可以提高70%的效率,而后者可以提高30%的效率。
另一个测试也是在红米上,200人同屏,单个角色400个顶点,560面左右,动画的总时长为6秒,骨骼数量平均为12个。通过序列帧的形式实现skinnedMeshRenderer到MeshRenderer 的转换。可以跑到25帧。
BakeMesh (SkinnedMeshRenderer.BakeMesh)
- 200+
- ~400 顶点
- ~500
- + 60 面
- ~6 s 动画
- ~12 骨骼
UI模块效率
![](http://www.manew.com/data/attachment/forum/201508/03/095406t0h9d2srd2f98fff.jpg.thumb.jpg)
上图是一个Ui的例子,由一位工程师在两周左右的时间完成,美术除外(来自scaleform在Assetstore上的资源)。
UI系统的渲染顺序是由UI元素在Hierarchy中的顺序决定的。而Ui系统会通过重排UI元素的渲染顺序来减少drawcall,但前提是不改变渲染结果。因此发生重叠之后,drawcall可能会上升。
内存管理
游戏制作的中后期通常会开始遇到内存上的 问题 。
- 总体内存:
![](http://www.manew.com/data/attachment/forum/201508/03/095407s89bvi8yvjbshvjv.jpg.thumb.jpg)
- Used Total:当前帧的Unity内存、Mono内存、GfxDriver内存、Profiler内存的总和。
- Mono内存:
- 记录代码堆内存的分配情况,由Mono控制
- 内存只增不减
- 建议<
- 40MB
![](http://www.manew.com/data/attachment/forum/201508/03/095407jb4abknoi4z0kry4.jpg.thumb.jpg)
Mono内存有80%的团队都不太关心,但却是很重要,会影响游戏的流畅性。
- 常见问题:Log输出
StackTraceUtility.PostprocessStacktrace ()
StackTraceUtility.ExtractStackTrace()
![](http://www.manew.com/data/attachment/forum/201508/03/095407zbe89rnzm7sy4rdv.jpg.thumb.jpg)
Log的输出不仅会消耗CPU,同时也会引起较大的堆内存分配。
内存泄露
- 资源被强行Hold无法释放
- 表现症状:Profiler中内存增长趋势明显,且资源无法回收
- Detailed
- Memory Profiler
- 资源冗余
- 资源通过AB加载,且资源在AB建立时存在多份
- 对AB打包机制进行详细排查
- 依赖关系打包
![](http://www.manew.com/data/attachment/forum/201508/03/095407xd2aldd4k4ee2445.jpg.thumb.jpg)
代码优化
游戏优化深度检测:
- 通过Profiler逐帧查看和分析CPU占用较高的函数
![](http://www.manew.com/data/attachment/forum/201508/03/095408xrlbrcbc2acltrvu.jpg.thumb.jpg)
- GC.Alloc
![](http://www.manew.com/data/attachment/forum/201508/03/095408lxzppiz1dthp1155.jpg.thumb.jpg)
- Shader.Parse
- Shader加载时的解析耗时
这部分耗时的避免,可以通过将shader打入单独的assetbundle包,并在进入场景时就进行预加载。
同一时间Load过多的非“基础”AB,且不及时Unload,会导致过多的内存消耗,如WebStream 和Serialized File 的内存。
而PersistentManager.Remapper 的意义是:PersistentManager维护资源的持久化存储功能,Remapper保存的是加载到内存的资源heapID与源数据FileID的映射关系,但Remapper是memory
pool,只增不减,因此建议不要一次性加载过多的assetbundle,而是以流式的方式来加载,以减小其峰值。
小结:性能优化是没有定式的,它需要“因时因地”制宜。