介绍

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. RenderTexture 与 Texture 需要缓存起来,不要每帧都分配。
  5. 使用 string.Format 与 StringBuilder 格式化字符串,减少内存消耗。
  6. 浮点数比较去除直接比较,防止丢失精度与产生警告。
  7. 字体需要修改为系统自带的字体,同时调整字号。
  8. 使用命名空间重新组织代码,重新命名脚本。
  9. 项目结构不太合理,可以考虑不作为一整个 Unity 项目,而作为 Unity 中的一个单独插件目录管理,例如 NGUI。
  10. 应该正确设置屏幕分辨率,需要与目标设备的最流行分辨率一致。

工具

更新 20190729

Pull Request 已合并到上游