Unity UI 优化指南 (1. Unity UI 的基础)

理解组成Unity UI系统的不同部分是非常重要的,有一些基础类和组件共同组成了Unity UI系统。这个章节首先会介绍经常会用到的一些术语,然后会讨论Unity UI关键系统的底层细节。

术语

Canvas:画布
Unity渲染系统用来提供层次化几何布局的类,是用原生的C++代码编写,会在游戏世界坐标系之中或者之上进行显示。
画布负责将其包含的几何图元组合成批,生成合适的渲染命令,然后把这些发送给Unity的图形系统。以上部分都在原生的C++代码中完成,这个过程称为重建或者批处理。当某个画布中的几何图元被认为需要重新批处理,这个画布就被认为已经是脏画布。

Geometry:几何结构
通过Canvas Renderer组件提供给画布

Sub-canvas:子画布或者嵌入画布
子画布指的是嵌入到其他的画布控件之中的画布。子画布会将其包含的控件和其父画布包含的控件进行分离;如果子画布已脏,则只会对自己进行重新批处理的过程,不会影响父画布,反之亦然。(某些情况下,这个说法也不正确,例如当父画布导致子画布重绘大小的时候)

Graphic:图形类
图形类是Unity UI的C#代码库中提供的基类,它是所有向Canvas系统提供绘制的几何结构的Unity UI类的基类。大部分内置的Unity UI图形是派生自MaskableGraphic这个子类,这个子类实现了IMaskable的接口,从而可以实现被遮罩的效果。Drawable的子类主要有ImageText,这两个类组成了对应的ImageText组件。

Layout components:布局组件
布局组件控制RectTransform的尺寸和位置,通常用来构建复杂的布局,比如说根据内容来适配大小和位置。布局组件只依赖与RectTransform而且只会改变关联的RectTranform的属性。布局组件不依赖于Graphic类,可以独立于Unity UI的Graphic组件使用。

Graphic和布局组件都依赖于CanvasUpdateRegistry类,这个类并没有在Unity编辑器中暴露出来。这个类会跟踪需要更新的布局组件和Graphic组件,并且当关联的Canvas触发了willRenderCanvases事件的时候触发更新操作。

布局组件和Graphic组件的更新被称为重建。重建的过程细节将在之后详细讨论。

渲染的细节

当使用Unity UI构建用户界面的时候,需要牢牢记住Canvas绘制几何图元的顺序是按照透明队列进行的。也就是说,Unity UI产生的几何图元都是按照从后向前使用alpha混合进行绘制。从性能角度解释就是,多边形栅格化之后的每个像素都会被采样,即使他会被其他的不透明像素完全挡住。在手机设备上,很高程度的重绘会超出GPU的填充率能力。

画布的批处理过程

画布的批处理过程是将UI元素的网格合并,产生合适的渲染命令发送给Unity的图形管道。处理的结果会被缓存和重用,直到Canvas被标记为dirty,这种情况会在画布包含的某个Mesh改变的时候出现。

画布使用的mesh来自于画布的CanvasRenderer组件,但是不包含任何子画布。

计算批处理需要将Mesh按照深度进行排序并且检查它们的覆盖情况和是否共享了材质等等。这个操作是多线程的,所以性能差异在不同的CPU架构上面比较大,尤其是移动设备和桌面计算机环境。

图形的重建过程

图形的重建过程指的是Unity UI的C#库中的图形组件的布局和mesh被重新计算的过程。这些操作是在CanvasUpdateRegistry类中完成的。相关代码可以在Unity UI的源代码中找到。

CanvasUpdateRegistry中,值得关注的方法是PerformUpdate方法。只要画布组件调用WillRenderCanvases事件的时候,这个方法就会被调用。这个事件每帧只会被调用一次。

PerformUpdate分三步进行:

  • 脏布局组件需要重建布局,通过ICanvasElement.Rebuild方法
  • 任何注册的需要剪切组件(如Mask组件)需要剔除被剪切掉的部分。这个操作是通过ClippingRegistry.Cull完成的。
  • 脏Graphic组件需要重建图形元素。

对于布局重建和图形重建而言,都需要分成多个部分完成。布局重建分成三步完成(PreLayout,Layout和PostLayout),图形的重建则是分成两部分完成(PreRender和LatePreRender)

布局重建

为了重新计算在一个或者多个布局组件下的组件的位置(和大小),需要将不同的布局组件按照合适的层次进行排序。在GameObject的层级中,越靠近根节点的布局组件可以改变其包含的Layout组件的大小和位置,所以需要首先倍计算。

为了完成这个任务,Unity UI 会将标记为dirty的布局组件按照在层级中的深度进行排序。层次越高(父Transform越少的元素)会排到列表的前面。

排好序的Layout组件需要重建布局,这时候Layout组件绑定的UI元素的位置和大小才会实际改变。对于Layout布局如何改变单个元素的位置和大小,参见Unity手册的UI Auto Layouts章节。

图形重建

当Graphic组件重建的时候,Unity会通过ICanvasElement接口的Rebuild方法控制。Graphic实现这个方法并且在PreRender阶段执行两种不同的重建步骤。

  • 如果顶点数据被标记成dirty(例如组件的RectTransform改变大小),mesh会被重建;
  • 如果材质数据被标记为dirty(例如组件的材质或者纹理改变),那么绑定的CanvasRenderer的材质信息会被更新;
    Graphic重建的过程不需要对Graphic组件进行排序。

你可能感兴趣的:(Unity UI 优化指南 (1. Unity UI 的基础))