介绍

Unity 中的特效在制作过程中往往会只注重效果,而忽视性能。往往做了很多特效之后才发现真机上同时播放多个特效时卡顿。

特效与其他原始资源(模型、纹理、动画等等)不同,前者是动态的,而后者是静态的。因此不能使用传统的分析方法只看一些静态的数据是否正确,而必须记录动态的数据。

搜索了一下,发现有人已经写好了一个工具:sunbrando/ParticleEffectProfiler: Unity 特效性能分析工具

看简介发现支持的功能很多:贴图内存占用、DrawCall 数量、粒子数量、特效原填充像素点、特效实际填充像素点、OverDraw 比率等等。

环境

  • Unity 5.6.6f2

需求

由于文档中并未对功能进行详细的说明,因此需要阅读源码确认:

  1. 确定所有功能含义以及获取的数值是否正确,如有错误需修正。
  2. 修正在检查过程中发现的问题。
  3. 补充缺失功能,如显示贴图数量、ParticleSystem 数量等等。
  4. Unity 版本兼容问题。
  5. 整合进项目后,打包时会报错UnityEditor找不到 · Issue #1 · sunbrando/ParticleEffectProfiler

问题

兼容问题

ProjectSettings/ProjectVersion.txt 显示的版本号是 Unity 2017.2.2f1,因此在目标版本 Unity 5.6.6f2 上有一些 API 不存在,需要手动加入版本宏定义进行处理。

例如粒子数量是通过反射调用 Unity 2017.2.2f1 中的 ParticleSystem.CalculateEffectUIData,需要修改为 Unity 5.6.6f2 的 ParticleSystem.CountSubEmitterParticles

获取信息不正确

检查源代码发现获取贴图内存使用的是 Profiler.GetRuntimeMemorySizeLong API,在编辑器下显示的是编辑器所在的平台内存占用,而不是目标平台的内存占用。

因此修改为通过反射调用的 Unity 私有 API UnityEditor.TextureUtil.GetStorageMemorySize,这是预览窗口中显示目标平台内存大小的 API。

DrawCall 数值为什么比实际大 2 倍?

Stats 视图中显示的 DrawCall 数值,使用工具时的数值是未使用工具时的大概 2 倍。

阅读源码发现 Camera 实际上渲染了两次,一次用作取样,一次用作显示。取样是用于计算 OverDraw 比率。

但是实际上还是会少算一次,因为默认情况下根据不同的 Unity 版本可能会有一次 Blit(Unity 5.6.6f2 默认强制开启 Blit),用于将当前渲染的结果送到屏幕上,以解决分辨率不同的问题。

其他参数是什么意思?

信息面板中以下属性是什么意思?

  1. 特效原填充像素点
  2. 特效实际填充像素点
  3. 平均每像素overdraw率

阅读代码发现原理是将相机渲染到一张 512x512 的 RenderTexture,然后计算有颜色的像素数量,以及有颜色的像素重复渲染次数,取平均值显示,同时计算比率。

打包时有编译错误

因为运行时脚本中存在 using UnityEditor; 声明,理论上来说此工具并不需要打包到最终版本中,因此将使用到的运行时脚本使用 UNITY_EDITOR 宏定义完全包围,这样就不会影响打包。

缺失功能

ParticleSystem 数量

使用 GetComponentsInChildren(true) 获取数组,然后直接打印组件的数量即可。

注意:一定要包含未启用的 ParticleSystem 组件,防止统计有误。

贴图 数量

在遍历获取贴图大小时,可以一并将其计数返回。

注意:考虑到特效中使用的不同 ParticleSystem 可能会引用相同的贴图,因此在遍历时必须去除重复。

操作优化

实际使用工具时是先添加脚本后再运行的,这样导致 Prefab 上增加了一个无用的脚本。 不过如果未去除 ParticleEffectScript 脚本就点击 Prefab 的 Apply 按钮保存则工具会报错。 但是实际上美术在使用工具时偶尔会遗忘,从而导致 Prefab 资源被无用脚本污染。

解决方案是使用播放状态变化事件以及编辑器自身的 Update 事件去检查是否进入到播放状态,决定是否添加测试脚本。

注意:InitializeOnLoad 会在播放后重新触发,因此无法通过静态变量的方式保存测试开始的请求状态,因此使用 EditorPref 保存请求状态。

注意事项

  1. 需要新建特效性能评估专用场景,调整好相机的视角、距离与 Clear Flags,用作基准,在之后的测试中尽量不发生变化,否则结果不便比较。
  2. 只能在场景中放置一个要测试的特效,不能放入其他特效导致结果被污染。

优化

以下列出了一些比较好但未实现的需求,如果时间允许或其他人有兴趣可以考虑实现:

  1. README 文档更新,详细介绍工具功能以及背后原理,方便他人使用。
  2. 每一项数据如果有超过推荐值则应显示为红色。
  3. 导致无法裁剪的列表中只显示特效名字而未显示路径,其实最好显示 ObjectField,这样可以直接点击跳转到对应的 GameObject
  4. RenderTextureTexture 需要缓存起来,不要每帧都分配。
  5. 使用 string.FormatStringBuilder 格式化字符串,减少内存消耗。
  6. 浮点数比较去除直接比较,防止丢失精度与产生警告。
  7. 字体需要修改为系统自带的字体,同时调整字号。
  8. 使用命名空间重新组织代码,重新命名脚本。
  9. 项目结构不太合理,可以考虑不作为一整个 Unity 项目,而作为 Unity 中的一个单独插件目录管理,例如 NGUI。
  10. 应该正确设置屏幕分辨率,需要与目标设备的最流行分辨率一致。

工具

更新 20190729

Pull Request 已合并到上游