江枫

Unity UGUI合批处理

参考文章

1.UGUI Batching

   UGUI以Canvas为单位进行批次生成和渲染,Canvas可以嵌套包含Canvas。Batching的生成和合并在Canvas::Update里:

   从源码我们可以看出来:

1.首先计算Canvas的alpha(可能通过CanvasGroup组件),当alpha为0的时候,则认为这个canvas的绘制范围为0,也就是不进行绘制。
2.UI层次结构(Hierarchy面板下的顺序)发生变化时,更新Batch顺序,对Canvas下所有UI元素(CanvasRenderer)按UI层次结构深度优先排序,生成UI Instructions。
3.更新所有需要同步数据的renderer UI 数据,包括vertex, color, material, transform, rect, depth(按UI层次结构深度优先排序的深度)等。非活动(IsActive() == false)且不强制更新的UI元素,将不同步数据。
4.Canvas数据更新时(m_CanvasData.isDirty,如情况都可以引发:层次结构改变,同步关键数据,Canvas.Awake等),计算UI Instructions的depth并排序、生成Batch。

   Mask:Mask实现的具体原理是一个Drawcall来创建Stencil mask(来做像素剔除),然后绘制所有子UI,再在最后一个Drawcall移掉Stencil mask。如果Mask的子节点属于同一个Atlas,那么Mask之间的元素可以进行合并;否则不能合并。Mask外的元素和Mask内的元素,无法合批。

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

2.Depth计算

1.遍历所有UI元素(已深度优先排序),对当前每一个UI元素CurrentUI,如果不渲染,CurrentUI.depth = -1,如果渲染该UI且底下没有其他UI元素与其相交(rect Intersects),其CurrentUI.depth = 0;
2.如果CurrentUI下面只有一个的需要渲染的UI元素LowerUI与其相交,且可以Batch(material instance id 和 texture instance id 相同,即与CurrentUI具有相同的Material和Texture),CurrentUI.depth = LowerUI.depth;否则,CurrentUI.depth= LowerUI.depth + 1;
3.如果CurrentUI下面叠了多个元素,这些元素的最大层是MaxLowerDepth,如果有多个元素的层都是MaxLowerDepth,那么CurrentUI和下面的元素是无法合批的;如果只有一个元素的层是MaxLowerDepth,并且这个元素和CurrentUI的材质、纹理相同,那么它们就能合批。

3.DrawCall合批(Batch)


1.Depth计算完后,依次根据Depth、material ID、texture ID、RendererOrder(即UI层级队列顺序,HierarchyOrder)排序(条件的优先级依次递减),剔除depth == -1的UI元素,得到Batch前的UI 元素队列VisiableList。
2.对VisiableList中相邻且可以Batch(相同material和texture等)的UI元素合并批次,然后再生成相应mesh数据进行绘制。

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

P.S.没有源码前提下的一些小发现

   上面的这些其实都是我从开头那个参考文章抄来的~但是我也遇到了几个让人困惑的问题,但是没有源码,只能靠一些实验进行猜测:

  1. UGUI中是包含剔除(cull)逻辑的,当新建一个Canvas,在Canvas下创建一个image,当把image拖出屏幕范围时,对应DC会-1。
  2. 当一个Canvas下包含两个image(不同材质,2DC)时,当把其中一个拖出屏幕范围时,剔除(cull)不会生效,DC数仍为2,只有将两个物体同时拖出屏幕范围,也就是Canvas下没有任何需要绘制的物体时,cull才会生效。

   所以UGUI的cull其实是以Canvas为最小粒度的,只有当整个Canvas都可以cull的时候才生效

文章大纲