Unity性能优化-UI

与3D场景渲染机制不一样,UI渲染以Canvas为单位进行,CPU负责把UI界面的逻辑结构进行更新,汇总,并负责把这些数据准备好,最后把这些信息传给GPU。

渲染开销建议控制在2ms以内,对于一个3D游戏来说一般UI Drawcall控制在50,对于比较重UI的游戏,也尽量不要超过100。

UI影响CPU的因素:

  • 界面结构复杂度
  • 界面结构变化频率
  • 动画复杂度

UI影响GPU的因素:

  • Drawcall
  • 图片最终在屏幕所展现的面积
  • 图片是否透明
  • Shader的复杂度
  • Overrdraw,单位像素的重新绘制次数

其中,特别值得注意的是Drawcall和Overdraw。

常用的优化措施有:

Profiler分析

Canvas.Sorting() 耗时过高
减少Canvas使用数量,减少动态排序逻辑。

Canvas.SendWillRenderCanvases() 耗时过高
UI全部都在一个canvas下,层级很深并且里面有很多的UI效果,导致UI更新耗时较长,建议对UI动态和静态进行拆分(多个canvas,但不能太多,否则Canvas.Sort()会阻塞主线程)减少UI的层级。

具体一个界面应该限制在多少层级,官方并没有一个标准的推荐值,这不是单一维度可以衡量的,因为每个界面的材质纹理复杂度,Canvas Render的裁剪合并结果都不一样,所以最终需要项目组做具体的测试。

PS:如果单个UI比较复杂,还可能因为引用的资源量较多,导致IO时间过长

批处理

UI批处理:对UI的批处理比较复杂,它受UI布局的影响:

  • 是否透明
  • 是否遮挡
  • 是否深度相邻
  • 是否Desc一致 即相同状态

参考

加载Load

使用异步接口。

加载-分帧

复杂界面

拆分成多个子部分,优先加载背景的部分。

列表数据

分页加载。

动静分离

优化UGUI.UpdateBatches时间

  • 特别由于低端机的核数较少,导致UpdateBatches的Job很难得到调度
  • 将会动的UI元素单独放到⼀个Canvas⾥,这样每帧UpdateBatches的 开销会减少

OnGui()

GUI.Repaint() 说明有使用原生的IMGUI(OnGUI),建议不要在正式上线版本中使用。

SetActive

根据UI界面使用频率的不同区别处理:

  • 使用频率很低,直接Instantiate/Destroy处理
  • 使用较为频繁,可缓存并通过SetActive管理可见性
  • 如果使用频率非常高,比如BattleHud界面,则可通过改变UI界面Alpha = 1/0(同时开关blocksRaycasts值)的方式来处理

PS:Why SetActive 导致卡顿

  • 会导致网格重建,会导致内存分配,触发GC
  • 会遍历这个实体上所有继承MonoBehaviour的脚本的OnEnable和OnDisanle调用
  • 对于复杂结构的物体,父物体在使用SetActive时 也会调用子物体的GameObject.Activate和GameObject.Deactivate
  • 有初始化逻辑的可能重新做一些初始化的工作

填充率

  • UGUI只产生quad(简单的顶点),所以顶点着色器不太可能给GPU Tiler流水线产生压力。着色器中的任何问题都应归结于填充率 Fillrate
  • 在移动设备上,FillRate 的压力主要来自半透明物体
  • 注意绘制像 alpha=0 这种实际上不会产生效果的颜色上去,也同样有 Blend 操作,这是一种极大的浪费
  • UI渲染过程中 谨慎 全屏透明的渲染
  • 隐藏不可见的对象

Alpha Test

渲染设置,避免使用Alpha Test,因为非常耗时,性价比很低。

全屏问题

UI覆盖全屏的情况下

  • 可以不渲染后面的3D场景,可能导致发热降频、耗电量高
  • 避免Overdraw
  • ⻓时间不动的界⾯可以实现OndemandedRendering动态降频

Shader

留意到里面有discard语句,可能带来性能影响,应编写无clip版本的Shader

拼接

超大尺寸的图片 考虑拼接方式

RenderTarget

2D项目所有相机都没有⽤到RT的Depth,可以在创建RT的时候不创建Depth和Stencil,降低内存和显存带宽消耗。

组件

Mask组件

规则遮罩时,Mask组件建议换成RectMask2D,更高效。

LayoutGroup组件

  • 在UI没有增删的时候设置Enable = false, 否则会导致UI Layout时间大幅增加
  • 不需要进行事件接收的组件,取消勾选Raycaster Target

Text组件

  • 不使用富文本的Text,取消勾选Rich Text,不使用Best Fit
  • 用TextMashPro代替OutLine,Shadow组件

UI-Particle

含有大量的BakeMesh操作,建议低端机去掉该功能。

图集

  • 不需要的图集不进内存(不要存放在Resources目录下,打包成AssetBundle)
  • 不使⽤的图集及时卸载
  • 提高图集的利⽤率:如果⼤的图集中UI元素很少,且存在⼤量的透明区域时,需要进行合理的规划

不使用Built-in资源

UI引⽤了Unity⾃带的⼀部分Texture,建议批量替换成项⽬组⾃⼰的

Fonts

合理规划字体数量,并通过工具裁剪字体字库。

九宫格

对于一些规则的图,比如背景框。

贴图格式压缩

ASTC

Texture 2D Import Settings

优化Sprite填充率

  • 尽量用双线性过滤,不用三线性插值
  • 可读写没必要开启

MipMap

Sprite类型的mipmap,UI没必要,因为固定分辨率大小,UI图片很少有缩小的情况出现。

PS:纹理贴图载入内存时,根据屏幕空间大小来计算多层mipmap。渲染系统会根据各个mip层的大小和几何图形在屏幕上的面积来决定哪一个mip层被选择。

取消勾选Generate Physics Shape

勾选该选项会生成物理碰撞的形状。

Alpha Source

  • 针对于没有Alpha通道的图可以直接设置为None。(None:无论输入纹理是否有 Alpha 通道,导入的纹理都没有 Alpha 通道)
  • 针对于Alpha通道不是0 就是 1的情况下,选择Input Texture Alpha。注意同时选中Alpha is Transparency 选项

Texture Type

如果图像仅有⼀个通道,则将Texture Type设置为 Single Channel。