我是标题
1.前言
UGUI优化核心在于减少渲染状态的开销。
那么怎么减少渲染开销呢? 减少渲染过程中的Setpasscall次数(从一个材质的渲染状态切换到另一个材质的不同渲染状态,或者启用 / 禁用某些渲染特性)。明明大头不在DrawCall为什么还要关心合批来减少drawcall? 因为如果更换批次大概率会增加setpasscall,unity不能完全的帮我们处理好渲染的批次分配,我们需要通过一些人为的干预从而减少setpasscall。
详细看我之前的这篇文章
链接跳转
2.批次处理(batching)
当UI颜色,材质等产生变化的时候会调用Rebuild,重构这个批次内的所有顶点信息,颜色,文本等。然而每个批次不应该因为单个物体的频繁改变而导致整体的Rebuild。所以引出第一点:
动静分离
- 频繁改变的UI应该自己处于一个Canvas中,不频繁改变的UI放在同一个Canvas中,类似Unity自带的静态合批也是在做这个事情。
为了尽可能减少材质改变,可以通过合并图集信息,让某些物体的材质相同,UV映射信息不同。所以引出第二点:
合并图集
- 尽可能充分利用图集空间,最好是能大图+小图把整个图集拼满,这样没有一个UV坐标信息是浪费的。
- 需要注意的是:1.图集大小不要超过4096 * 4096否则对内存是很大的开销。2.按照频率来拼图,使用频率高的拼在一起,低的拼在一起。
然而上述只是理想状态能做的优化,放在同一个Canvas里只会帮助Unity进行合批,但是实际的渲染顺序和Drawcall数量还要看你的UI到底是如何摆放的。
所以引出第三点:
避免夹层(不线性的UI摆放)
Unity UI是优先渲染Hierarchy中靠下的元素和普通物体渲染时优先渲染Z轴靠前的情况不一样,即使你调整了UI在场景中的Z位置,渲染仍然按照Hierarchy中靠下的顺序。
- 尽量避免某个UI打断某些材质相同本能处在同一个批次的UI,从而分批造成多次Rebuild。
- 详细看某乎这篇文章:kong66
3.Rebuild
- 由于使用Disable() 和 SetActive()给一个Canvas下的子物体,会被标记为dirty引起Rebuild,所以需要频繁隐藏的物体要另外放在一个Canvas里面,以免标记为dirty。
- 引用上述文章的一段话“canvas不是划分的越多越好,不同canvas的UI元素是不可能batching 的, 所以canvas多,draw calls就会多; Canvas 太少,每个canvas上的元素太多,任何元素变化频繁, 都将导致canvas频繁dirty, 频繁rebuild, 而且rebuild计算量大. 总之,要在最低的rebuild成本 和 最少的draw calls之间做出权衡.”
- 使用Canvas Group来管理某个父物体下的子UI对象,作用范围是所有子UI,在可调整选项中调整UI属性可以避免标记为dirty,可调整选项有:
-
- Alpha 此组中的 UI 元素的不透明度。该值介于 0 和 1 之间,其中 0 表示完全透明,1 表示完全不透明。请注意,UI 元素也会保留自己的透明度,因此画布组的 Alpha 值将与各个元素的 Alpha 值彼此相乘。
- Interactable 确定此组件是否接受输入。当设置为 false 时,禁用交互。
- Block Raycasts 此组件是否作为射线投射的碰撞体?需要在连接到画布的图形射线投射器上调用 RayCast 函数。这_不_适用于 Physics.Raycast。
- Ignore Parent Groups 此组还会受到游戏对象层级视图中更上层的画布组 (Canvas Group) 组件中的设置所影响,还是会忽略并因此覆盖这些设置?
- 使用CanvasRenderer.Cull = true 可以手动设置在某个不想渲染的UI,也就是人为的剔除某个UI元素。
4.OverDraw
引用kong66文章的话“因为Canvas的所有元素都会从前往后做透明度融合的渲染,所以重叠区域的所有UI元素都会被采样, 这将造成GPU的填充率(fill-rate)过高,也就是overdraw, 这会进一步导致帧率下降.”
- 所以为了避免这种情况,如果后渲染的不透明页面完全覆盖了先渲染的页面,那么可以把对GamePlay不可视的页面给Disable掉。
- 如果美术资源允许的情况下,可以对叠加的花纹材质进行处理,以减少叠加区域。然而这会使得素材复用性降低。
5.RayCast
引用kong66文章的话"射线碰撞检测所有的canvas, canvas上所有的Graphic, 最后找到有效点击的UI元素"流程如下图所示。
- 减少不必要的RaycastTraget,确定某些UI元素是不需要和用户交互的,那么就取消勾选RaycastTraget。
- 减少元素重叠,可点击的重叠元素会在步骤d, f, g增加排序,遍历的次数。
- 避免2D/3D遮挡检测. 尽量避免使用2D/3D遮挡检测,因为在步骤e中,这些检测函数本身消耗较大,且会产生GC.
6.Mask/RectMask2D
- 原理
- Mask:依据父物体图像组件的 alpha 通道来确定子元素的可见区域。当父物体图像的 alpha 值非零时,子元素在对应位置可见;若父物体图像完全透明或没有图像,Mask 可能无法按预期工作。
- RectMask2D:利用父物体的矩形边界来决定子元素的可见区域,不依赖父物体的图像组件及其 alpha 值。
- 应用场景
- Mask:适用于需要非矩形裁剪的场景,如圆形、不规则形状裁剪。例如,制作圆形头像框,可将头像设为 Mask 子物体,通过设置 Mask 形状为圆形实现裁剪。
- RectMask2D:用于矩形裁剪场景,如滚动列表、窗口等。实现滚动列表时,使用 RectMask2D 可确保列表项只在列表框矩形范围内显示,超出部分被裁剪。
- 性能消耗
- Mask:使用模板缓冲区,每次渲染涉及模板缓冲区的写入和比较操作,性能开销大,在大量 Mask 或复杂图形场景中,对性能影响更明显。
- RectMask2D:基于矩形裁剪,不使用模板缓冲区,渲染开销小,性能较好。