UGUI优化:批次合并源码分析及工具

发表于2017-02-23
评论12 2.22w浏览

1.   概述

本文档对Unity GUI 批次合并(Batching)源码(基于Unity 4.6版本源码)进行研究,总结UGUI Batching的规则,并提供UI优化建议,以及UI层级显示辅助工具使用说明。

2.   UGUI Batching规则分析

UGUICanvas为单位进行批次生成和渲染,Canvas可以嵌套包含Canvas

Batching的生成和合并在canvas::Update里:

Batching主要流程如下:

1)计算Canvas alpha,包括父Canvas和嵌套Canvasalpha=0不生成及合并批次)。

2)UI层次结构发生变化时,更新Batch顺序,对Canvas下所有UI元素(Canvas renderer)按UI层次结构深度优先排序,生成UI Instructions

注意1 Mask:其中有mask UI会增加两个UI Instructionsmask / unmask),且不能合并(最终增加两个drawcall,分别使用Increment / Decrement mask Material Instance)。Mask实际上是前后使用或移除Stencil mask(来做像素剔除),在之间画所有子UI元素,mask UI下的子UI元素也无法与mask UI 之外的UI元素合并批次。

 

注意2更新UI层次结构发生变化(orderIsDirty),新增、删除UIUI子节点都会引起整个Canvas UI顺序更新。因此,应避免频繁删除/增加UI对象,使用GameObject.SetActive

3)更新所有需要同步数据的renderer UI 数据,包括vertex, color, material, transform, rect, depth(步骤2里的排序)等。

非活动(IsActive() == false)且不强制更新的UI元素,将不同步数据。

4)Canvas数据更新时(m_CanvasData.isDirty,如情况都可以引发:层次结构改变,同步关键数据,Canvas.Awake等),计算UI Instructionsdepth并排序、生成Batch

Depth计算算法

a) 遍历所有UI元素(已深度优先排序),对当前每一个UI元素CurrentUI,如果不渲染,CurrentUI.depth = -1,如果渲染该UI且底下没有其他UI元素与其相交(rect Intersects),其CurrentUI.depth = 0;

b) 如果底下有一个的需要渲染的UI元素LowerUICurrentUI相交的情况下,且

可以Batchmaterial instance id  texture instance id 相同),depth_i = LowerUI.depth

不可以Batchdepth _i= LowerUI.depth + 1;

如果底下有nUI元素与CurrentUI相交,根据b)计算n个的depth_i取最大的作为CurrentUIdepth:  CurrentUI.depth = Max( depth _1, depth _2, depth _3 ... , depth _n)

Depth计算完后,根据Depth排序UI Instructions,如果Depth相等,依次根据材质IDtexture ID、渲染顺序(即UI层级队列顺序)排序,剔除depth == -1UI元素,得到Batch前的UI 元素队列VisiableList

 

VisiableList中相邻且可以Batch(相同materialtexture等)的UI元素合并批次,然后再生成相应mesh数据进行绘制。

 

注意:在Depth计算算法中,由于要遍历所有UI元素和已计算的底层UI元素(平方复杂度),源码中使用分组计算包围盒矩形的方法加快计算,即16UI元素为一组计算Group Rect,检查是否与底层UI元素相交时,先计算是否与底层Group相交,如果相交再与Group中的元素做判定。

因此,UI元素数目过多和层次结构过于复杂,会影响排序和Batch更新速度,合理规划UI元素数量和层次结构可以提高UI性能。

 

规则小结

UGUI批次合并生成规则可以看出,提高UI性能尽量注意以下几点:

尽量避免使用Mask,使用Mask至少增加两个Drawcall,并可能导致本可以BatchUI元素无法Batch,从而增加更多Drawcall

避免在UI树形结构下(Canvas下)频繁删除/增加UI对象,UI层次结构发生变化会引起整个Canvas UI顺序更新,特别是复杂的UI树形结构。

避免频繁动态的更新UI元素的Vertex, Rect, Color, Material, Texture等,可能引起Canvas数据更新和Batch更新计算,有可能引起VBO Update(重新提交顶点数据)。

尽可能使用少的UI Material和贴图(使用图集),使得可以Batching

相邻的UI元素(比如父节点与第一个子节点、相同层次结构下的节点),使用相同材质贴图的UI元素尽可能排在一起,便于合并。

同一父节点下所有子节点,保持相同的层次结构(如List控件下的item),便于底层相同depthUI元素Batch

避免UI元素数目过多和层次结构过于复杂影响Batch更新速度。

固定的Text考虑与背景图层合在一张图上(可能不便本地化,但可以减少drawcall)。

3.   UI层级显示辅助工具

根据源码中的UI Batching规则,写了个UI层级辅助工具,用于显示UI的层级、批次等数据,便于UI性能优化。使用者可以结合以上规则,分析当前UI元素排列顺序、材质贴图设置,优化UI Batching,减少UI Drawcall

安装:将附件中UIAssistant.cs放在Editor目录下即可

使用:点击菜单栏Tools/UIAssistant,打开如下图工具。默认分析名为UIRoot下所有UI组件(active状态),或者自定义选择Root UIcanvas,一般用于单独分析)。

 

UpdateBatching:根据源码算法计算UI元素的层级及批次序号。手动点击触发,当UI树形结构发生变化、激活/不激活时,需要重新计算。

ShowDepth: 会在Hierarchy View active的有效UI元素(canvas renderer),以“批次序号/层级序号/材质序号”的方式显示(开/关)。原则上,尽可能的使相同/或相邻的层级,批次序号相同(意味着同一批次),用于辅助分析。

 

注意:由于嵌套和动态UI结构,上层接口无法取到,导致总体批次有可能偏差(存在这些结构时),但不影响UI层级显示辅助工具可分析局部UI情况;另外,具有mask情况下,目前只做简化处理,没有考虑unmaskUI元素合批情况。

 

例子1(部分截图):

Btn_CloseImage的相邻层层级(23),材质不同,批次不同,尝试是否可以修改材质和图集,减少1drawcall

修改前:

修改后:

 

例子2

前后的Btn_CloseImageIner_FrameRight材质相同(序号都为0),因Image材质不同,batching中断了,调换一下次序,减少2drawcall :调换UI次序注意交互响应问题,一般适用背景图次序调整,其他情况具体分析)。

 

4.   小结

Unity UI层级计算和合并批次规则(batching)显复杂,具体UI实现上又受需求等限制,需要UI同学根据情况调整(材质,贴图,层次等),尽可能满足合并批次(batching)规则。另外,以上源码分析基于Unity 4.6版本,使用正常,其他版本兼容。Unity 5以上UGUI有所改动(如mask等),请自行修改使用。

以上如有纰漏,欢迎指正和补充。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引