Unity UI 优化最佳实践2 -Unity UI 基础

 

理解组成Unity UI系统的不同部分很重要。 有几个基本的类和组件共同组成系统。 本章首先定义了本系列文章中使用的许多术语,然后讨论了Unity UI中几个关键系统的低级(low-level)行为。low-level

术语

Canvas是一个本地代码(native-code)的Unity组件,Unity的渲染系统使用该组件来提供分层的几何图形,这些图形将被绘制在游戏世界空间(world-space)中或世界空间(world-space)前面。

Canvases负责将其组成几何图形组合成批,生成相应的渲染命令并将其发送到Unity的图形系统。所有这些都是在本机C ++代码中完成的,称为rebatch 或batch build。当Canvas被标记为包含需要rebatching的几何体时,Canvas被认为是脏的。

几何图形由Canvas Renderer组件提供给Canvases。

Sub-canvas只是一个嵌套在另一个Canvas组件内的Canvas组件。Sub-canvas将他们的子canvas与父canvas分开;一个脏(dirty)的子canvas不会强迫父canvas重建其几何图形,反之亦然。但有些情况并非如此,例如对父Canvas进行更改导致调整子Canvas的大小时。

Graphic是由Unity UI C#库提供的基类。它是所有Unity UI C#类的基类,可为Canvas系统提供可绘制的几何图形。大多数内置的Unity UI图形都是通过MaskableGraphic子类实现的,这使得它们可以通过IMaskable接口进行屏蔽。 Drawable的主要子类是Image和Text,它们提供了同名的组件。

Layout 组件控制RectTransforms的大小和位置,通常用于创建需要相对大小或相对定位其内容的复杂布局。布局组件仅依赖于RectTransforms,并且仅影响其关联的RectTransforms的属性。它们不依赖于Graphic类,可以独立于Unity UI的Graphic组件使用。

Graphic和Layout组件都依赖于CanvasUpdateRegistry类,该类未暴露在Unity编辑器界面。该类跟踪必须更新的一组Layout组件和Graphic组件,并在其关联的Canvas调用willRenderCanvases事件时根据需要触发更新。

Layout和Graphic组件的更新称为重建(rebuild)。本文档稍后会详细讨论重建过程。

渲染细节

在Unity UI中编写用户界面时,请记住,Canvas绘制的所有几何图形都将绘制在透明队列(Transparent queue)中。 也就是说,Unity UI生成的几何图形将始终通过alpha混合重新绘制。 从性能角度要记住的重要一点是,多边形中被光栅化的每个像素都会被采样,即使它完全被其他不透明的多边形覆盖。 在移动设备上,这种高水平的过度绘制(overdraw)[1]可能会迅速超过GPU的填充率。

批量构建(Batch building)过程 -(Canvases)

批量构建过程是Canvas合并表示其UI元素的网格(mesh)并生成相应的渲染命令以发送到Unity的图形管线的过程。这个过程的结果被缓存并重复使用,直到Canvas被标记为脏的,每当其中任意一个组成网格发生变化时就会发生这种情况。

Canvas使用的网格取自附加到Canvas但不包含在任何Sub-canvas中的Canvas Renderer组件集合。

计算批次需要按照深度对网格进行排序并检查它们是否有重叠,共享材料等。这个操作是多线程的,所以在不同的CPU架构中,尤其是在移动SoC(通常具有很少CPU核心)和现代台式机CPU(通常具有4个或更多核心)之间,其性能通常会有很大差异。

重建(rebuild)过程 -(Graphics)

重建rebuild过程是Unity UI C#图形组件的布局和网格被重新计算的地方。这是在CanvasUpdateRegistry类中执行的。请记住,这是一个C#类,其源可以在Unity的Bitbucket上找到。

在CanvasUpdateRegistry中,相关方法是PerformUpdate。只要Canvas组件调用WillRenderCanvases事件,就会调用此方法。该事件每帧调用一次。

PerformUpdate运行三个步骤:

  • 请求脏的Layout组件通过ICanvasElement.Rebuild方法重建其布局。
  • 任何注册了Clipping 组件(例如Masks)都被要求剔除任何被裁剪组件。这是通过ClippingRegistry.Cull完成的。
  • 脏的Graphic组件被要求重建其图形元素。
    对于Layout和Graphic重建,该过程分为多个部分。Layout重建以三部分(PreLayout,Layout和PostLayout)运行,而Graphic重建以两部分运行(PreRender和LatePreRender)。

布局(Layout)重建

为了重新计算一个或多个Layout组件中包含的组件的适当位置(以及可能的大小),有必要以适当的分层次序来应用布局。靠近GameObject层次结构根部的Layout可能会改变可能嵌套在其中的任何Layout的位置和大小,因此必须首先进行计算。

为此,Unity UI根据层次结构中深度对脏的Layout组件的列表进行排序。层次结构中较高的项目(即使用较少的父Transforms)将移动到列表的前面。

然后排好序的Layout组件列表请求重建其布局;这是由布局组件控制的UI元素的位置和大小实际上被改变的地方。有关各个元素位置如何受布局影响的更多详细信息,请参阅Unity手册的UI Auto Layout部分。

Graphic重建

当重新构建Graphic组件时,Unity UI将控制传递给ICanvasElement接口的Rebuild方法。 Graphic实现了这一点,并在重建过程的PreRender阶段执行两个不同的重建步骤。

  • 如果顶点数据已被标记为脏(例如,当组件的RectTransform已改变尺寸时),则重建网格。
  • 如果材质数据已被标记为脏(例如,当组件的材质或纹理已更改),则attach的Canvas Renderer的材质将被更新。
    Graphic重建不按照任何特定顺序处理Graphic组件列表,也不需要任何排序操作。

译注:
[1]overdraw 过度绘制,相关概念:
总填充数峰值:项目运行过程中,单帧总填充像素数量的最大值;
填充倍数峰值:项目运行过程中,单帧的最大填充倍数。
单帧填充倍数 = 该帧总填充数 / 该帧渲染相机的分辨率。

注:翻译自unity官网 https://unity3d.com/cn/learn/tutorials/topics/best-practices/fundamentals-unity-ui?playlist=30089