1.什么是UGUI优化,UGUI优化的理论基础

1.1理论基础

  •  Canvas, 是Unity渲染系统给层状几何体( layered geometry  )提供的可以被画入、被放在上面或者放在世界空间的底层Unity组件。Canvas负责将它包含的几何体组合成batch,生成合适的渲染命令发送给Unity图形系统。这个过程在底层的C++代码中完成,这个过程被称为一次rebatch或者一次batch build。当一个Canvas被标记为包含需要rebatch的几何体时,这个Canvas被认为是dirty的。
  •  layered geometry , 层状几何体,由Canvas Renderer组件提供给Canvas。[ Canvas 负责进行渲染, Canvas Renderer负责采集/接收. ]
  •  动静隔离 , 一个子Canvas仅仅是一个嵌套在父Canvas中的组件,子Canvas将它的子物体和它的父Canvas隔离,一个子Canvas下dirty的子物体不会触发父Canvas的rebuild,反之亦然。(这些在某些特殊情况下是不确定的,比如说改变父Canvas的大小导致子Canvas的大小改变。).
  • Graphic , 是UGUI的C#库提供的一个基类。它是UGUI所有类的基类,给所有的UGUI类提供可以画在Canvas系统上的几何图形。大多数Unity内置的继承Graphic的类都是通过继承一个叫MaskableGraphic的子类来实现,这使得他们可以通过IMaskable接口来被隐藏。Drawable类的子类主要是image和text,已经提供了同名的组件。
  • Layout , 组件控制着RectTransform的大小和位置,经常被用于要生成具有相似的大小和位置关系内容的复杂布局。它只依靠RectTransform,只影响与其相关的RectTransform的属性。这些layout组件不依赖于Graphic类,可以独立于UGUI的Graphic组件之外使用。
  • CanvasUpdateRegistry ,  Graphic和Layout组件都依赖于CanvasUpdateRegistry类,它不会在Unity编辑器的界面中显示。这个类追踪那些Graphic和Layout组件必须被更新的时候,还有与其对应的Canvas触发了willRenderCanvases事件的时候。更新Graphic类和Layout类叫做rebuild。rebuild的过程将在本文后续讨论。
  • OverDraw Overdraw指在一帧之内多次在屏幕上绘制某一个像素,当我们有若干UI元素重叠在一起相互遮挡时,每个UI元素被遮挡的部分即使看不到也同样会被绘制。UI层叠的层级越深,某些像素重复绘制的次数就越多,Overdraw开销就越大 Overdraw会影响填充率,简单说Overdraw和填充率成正比,过高的Overdraw可能会造成GPU的性能瓶颈,影响游戏帧率 Overdraw也会增大渲染消耗的内存带宽,每次绘制像素时,都需要从该缓冲区传输出和传入关键信息,尤其在移动设备上影响较大 可以在Scene窗口的Overdraw模式下查看Overdraw信息 ,颜色越深的表示Overdraw越严重(使用URP时,Overdarw模式可能会被隐藏,可借助插件,例如OverdrawForURP查看)。
  • DrawCall
    - CPU准备好数据和渲染状态后,向GPU发送的一次渲染请求
    - 每次调用DrawCall之前,CPU需要完成很多工作,例如检测渲染状态,提交渲染所需要的数据和状态等,当DrawCall过多时,CPU就会有很多额外开销用于准备工作,造成CPU性能瓶颈,因此DrawCall优化的方向就是减少DrawCall的数量

1.2 渲染细节

  • 在使用UGUI制作UI时,请牢记Canvas中所有几何体的绘制都在一个透明队列中,这就意味着由UGUI制作的几何体将从始至终伴随着alpha混合,所以从多边形栅格化的每个像素都将被采样,即使它被完全不透明的物体所覆盖。在手机设备上,这种高等级的过度绘制将迅速超过GPU填充频率的承受能力。

1.3 Batch构建过程(Canvas)

UGUI是以 Canvas 为合批的根节点,首先 Canvas 与 Canvas 之间是没法合批的,Canvas 下所有节点的顶点数据会生成一张大的 BatchedMesh,再根据材质纹理、渲染顺序等把这个 Canvas 下能合批的 Mesh 生成 SubMesh,一个SubMesh 就是一个绘制批次。每当这个大的 BatchedMesh 中任意一个顶点数据发生变化的时候,UGUI 目前的实现方式很粗暴,就会直接重新生成BatchedMesh , Canvas.BuildBatch() 干 的 就 是 这 个 事 情 。 由 此 可 见Canvas.BuildBatch()消耗飙高的因素有两点:Canvas 下的顶点数量多和Canvas 下的顶点发生变化。

  •  Batch , 构建过程是指Canvas通过结合网格绘制它所承载的UI元素,生成适当的渲染命令发送给Unity图形流水线。Batch的结果被缓存复用,直到这个Canvas被标为dirty,当Canvas中某一个构成的网格改变的时候就会标记为dirty。
  • Canvas的网格从那些Canvas下的CnavasRenderer组件中获取,但不包含任何子Canvas。
  • 计算Batch要求按照深度排序网格,测试它们是否有重叠,共享材质等等。这个过程是多线程的,在不同的CPU架构下性能表现非常不同,特别是在手机芯片(通常CPU核心很少)和现代桌面CPU(通常拥有四核心或者更多)之间非常不同。

1.4 Rebuild过程(Graphics)

  •  Rebuild , 过程是指Layout和UGUI的C#的Graphic组件的网格被重新计算,这是在CanvasUpdateRegistry类中执行的。这是一个C#类,它的源码可以在Unity的Bitbucket上找到。
    CanvasUpdateRegistry , 类中,PerformUpdate方法,当一个Canvas组件触发它的WillRenderCanvases事件时,这个方法就会被执行。这个事件每帧调用一次。
    PerformUpdate ,  函数运行的三个步骤:
    1- 通过ICanvasElement.Rebuild函数,请求rebuild被Dirty的Layout组件
    2- 所有被注册的裁剪组件(例如Mask),对需要被裁剪的组件进行剔除。这在ClippingRegistry.Cull中执行。
    3- dirty的Graphic组件被要求rebuild其图形元素
  • Layout 和 Graphic 的 rebuild ,
    Layout:Layout rebuild , 三个部分 PreLayout , Layout  ,  PostLayout
    Graphic:Graphic rebuild ,两个部分 PreRender , LatePreRender
  • Layout Rebuild , 要重新计算一个或者多个Layout组件所包含的UI组件的适当位置(以及可能的大小),有必要对Layout应用层次的排序。在GameObject的hierarchy中靠近root的Layout可能会影响改变嵌套在它里面的其他Layout的位置和大小,所以必须首先计算。 为此,UGUI根据层次结构中的深度对dirty的Layout组件列表进行排序。层次结构中较高的Layout(即拥有较少的父transform)将被移到列表的前面。然后,排序好的Layout组件的列表将被rebuild,在这个步骤Layout组件控制的UI元素的位置和大小将被实际改变。关于独立的UI元素如何受Layout组件影响的详细细节,请参阅Unity Manual的UI Auto Layout章节。
  • GraphicRebuild , 当Graphic组件被rebuild的时候,UGUI将控制传递给ICanvasElement接口的Rebuild方法。Graphic执行了这一步,并在rebuild过程中的PreRender阶段运行了两个不同的rebuild步骤:1.如果顶点数据已经被标为Dirty(例如组件的RectTransform已经改变大小),则重建网格。2.如果材质数据已经被标为Dirty(例如组件的material或者texture已经被改变),则关联的Canvas Renderer的材质将被更新。Graphic的Rebuild不会按照Graphic组件的特殊顺序进行,也不会进行任何的排序操作。
  • 便于理解,制作了相关关系的导图

unity gameobject物体尺寸 unity游戏尺寸_unity

2.为什么要有UGUI优化

UI部分的消耗是移动端游戏主要消耗之一,尤其是在低端机或者场景渲染压力大的情况,降低UI部分的消耗对提升游戏体验有重要意义

3.如何使用UGUI优化,使用UGUI优化的不同方案比较

3.1 UI资源规范,内存优化

3.1.1 选择合适的压缩格式

Android :推荐使用ETC,如果是带透明通道的,可选择ETC2。

Ios:推荐使用PVRTC 4Bit格式。PVRTC格式要求方形的图集贴图。

3.1.2 图片尺寸保持2的N次幂(POT )

图形处理都是按照2的n次方处理的,非2的n次幂会强制转换成2的n次幂处理,而转换过程会比较耗时,所以我们尽量保持图片大小为2的幂。

POT: power of two

NPOT:not power of two

3.1.3 合并小图打图集

同一个界面出现的 UI 资源尽量放到一个图集,重复利用的公用资源放common(DrawCall 优化);

图集的整体大小不要超过2048*2048,过大的尺寸也会影响加载速度。要保证图集的填充率。

3.1.4 禁止mipmap

Mipmap对ui没有影响,故禁掉。

3.1.5 禁止ReadWrite

需要消耗双倍内存,通常情况下并不需要。

3.1.6 九宫格或者平铺

大的背景图片,如果可以用九宫格方式拉伸,尽量让美术提供小尺寸图片进行九宫格拉伸。

如果是可以平铺,则用平铺。

3.1.7 轴对称反转

如果图片左右或者上下是一致的,那么只需要切一边的的图片,另外一边做翻转。如果仅仅左右(或上下)对称,则可以省一半。

3.2 对UGUI的GPU 优化

3.2.1.减少片段着色器Shader的复杂性。

Fragment 剔除掉 Alpha 为 0 的像素点,减少 OverDraw;尽量简化 Shader 代码.

UGUI使用的内置着色器包含了对隐藏、裁剪和许多其他复杂操作的支持。由于这种复杂性的增加,在iPhone4这种较低端设备上,UI着色器的表现较简单的Unity2D着色器相比表现较差。

如果一个针对低端设备的应用程序不需要隐藏、裁剪和其他奇特的功能,那么就可以创建一个自定义的着色器来省略没有使用的操作,比如下面这个最简单的UI着色器:(着色器代码见原网页) [https://unity3d.com/learn/tutorials/topics/best-practices/fill-rate-canvases-and-input]

 3.2.2 Overdraw优化

  • 尽量少使用半透明图片和文字,尽量减少UI重叠层级过多半透明带来的Overdraw消耗更高,因为不透明元素的渲染是替换像素,而半透明的像素需要和原有像素Blend
  • 对于中间透明的图片,可以设置ImageType为Sliced,并去掉勾选FillCenter
  • 不要使用空的Image

空的Image依然需要渲染,它不仅会打断合批,且会增加Overdraw

  • 慎用后处理

后处理可能会使得Overdraw翻倍

  • 被完全遮挡的元素可以添加CanvasGroup组件,然后将alpha值设为0,就不会传给GPU渲染了
  • 看不见的元素且没有逻辑功能要 disable 或者挪出裁剪区域,而不要通过设置Image的Alpha=0 来隐藏; 

3.3 对UGUI的CPU 优化

3.3.1 DrawCall 优化

DrawCall优化的方向是减少DrawCall的数量,而UGUI是通过mesh动态合批(Batch)来实现这一优化的。在Batch前,UGUI会根据一定的规则对UI元素进行重排,排序之后符合合批条件的UI元素就会合并在同一个SubMesh中,从而降低DrawCall的数量。

UGUI的合批以Canvas为单位,不同的Canvas之间的UI组件是不能够合批的。

合批的条件排序后相邻的UI元素、UI组件相同、材质相同、texture相同。

UGUI首先会根据一定的排序规则将所有的UI组件以一定的顺序存储,这个顺序是深度的降序顺序存储,然后按照合批规则,依照存储顺序依次判断他们存储顺序中相邻的UI组件是否能进行合批,所以能合批的UI一定是在存储顺序中连续相邻的两个或几个UI。

Batch的流程

  1. 筛选所有的Canvas(包含嵌套的Canvas),alpha=0的Canvas剔除
  2. 对Canvas下的UI元素按照Hierarchy中的层级从上向下(深度优先),分析每个元素的depth,并得到一个列表(过程中会跳过不渲染的元素,如activeSelf为false、空节点、或者Image等脚本被禁用的节点等)

depth计算规则:不显示的UI深度值为-1,不用管,接着判断是否该元素底部是否有物体,如果没有则赋值Depth为0,如果盖住物体(这块是通过Mesh进行判断,判断Mesh是否相交)则等于底部盖住的UI元素中Depth最大的值+1,如果两个相邻元素通过了合批测试,则这两个相邻元素的深度值相等。

    3. 再次对列表排序得到Batch前的UI元素队列VisiableList

  • 按照depth升序排列
  • 如果depth相同按materialId升序排列
  • 如果depth和materialId都相同,按textureId升序排列
  • 如果depth、materialId、textureId都相同,按renderOrder升序排序

   4.对VisiableList中相邻且满足合批条件(material相同、texture相同)的元素进行网格合并

深度排序之后,就会根据matID进行排序,如果材质相同则对ImgID进行排序,如果也相同,那会根据inspection面板上的RendererOrder,最后真正进行UI的合批。 

优化策略:

1.合理分配图集,同一个界面上的图尽量打到一个图集,多个界面复用的图,放到 common;

2.制作界面的时候,相邻节点尽量使用同一个图集的图片;尽量减少不同图集之间相互遮挡。

3.尽量减少Image和Text之间相互遮挡

4.尽量保证使用相同图集的Image元素depth一致

5.Text 本身也是用的 Font Texture,不同字体的 Text 也是来自不同的图集,所以在布局界面的时候也要尽量避免穿插打断绘制流程;

 6.少用 Mask 组件,Mask 实现的原理是 Stencil Buffer,往模版缓存里绘制,模版缓存里的东西才是可见的。模板缓存会打断所有的合批,Mask 的子节点和外面的节点无法合批,模板缓存自己占一个 DrawCall。Unity5.2 之后的版本建议使用 2D Rect Mask 替代。

  • RectMask2D实现剪裁是在c#和c++代码中通过将子物体的在RectMask2D的矩形区域外的部分的网格裁剪掉从而达到遮挡剔除的效果的,区域外是没有任何的顶点或者三角面产生的,这种做法完全不会产生任何的drawcall(或者说batch)。(结论:同深度、同材质、同tex的RectMask2D之间可以合批,任意两个ReckMask2D下的子物体UI不能合批,但同一RectMask2D下的子物体可以合批。)

3.3.2 Rebuild 优化

  • UI动静分离,在经常发生重建的UI加上canvas
  • 对于更改会设置脏标记的UI的属性,尽量考虑采用不会引起脏标记的方法进行设置,例如更改image颜色的时候,不采取直接更改Image的color属性,而是设置一个材质,更改shader的颜色。image.material.SetColor("_Color", color)
  • 避免频繁SetActive,如果需要频繁的显隐操作,可以CanvasRenderer.alpha=0或者通过enable和disable UI元素的Canvas组件。
  • 谨慎使用Canvas的Pixel Perfect选项(强制画布中的元素按像素对齐),它会使得UI元素位置移动时,引起LayoutRebuild,例如在ScrollView滚动时

引起Rebuild的原因:

unity gameobject物体尺寸 unity游戏尺寸_Graphic_02

 

3.3.3 Rebatch优化 (重新打batch合批)

Canvas下UI元素发生改变,引起的整个Canvas网格的重建(重新合批,合批网格)。Canvas下面一个元素的变化,都会导致整个网格的Rebatch,如果该Canvas下元素众多,结构复杂,Rebatch的将会有明显的消耗。

Rebatch开销是包含在主线程的Canvas.BuildBatch和worker线程中的Canvas.SortJob、Canvas.GeometryJob等函数中。

unity gameobject物体尺寸 unity游戏尺寸_UI_03

unity gameobject物体尺寸 unity游戏尺寸_unity_04

 

  • 会引起UI元素Rebuild的操作,都会导致整个网格Rebatch。
  • BatchedMesh该 Mesh 包含了顶点色,所以修改顶点色、缩放、位移等导致 Mesh 数据变化的操作,都会引起 Canvas.BuildBatch()。
  1. 尽量减少会引起频繁Rebatch的操作,例如在Update中对UI做修改
  2. 对于精度要求不高的UI更新操作,可以使用隔帧更新,例如角色头顶跟随UI的位置更新
  3. 动静分离 也就是将界面中频繁变化的UI,通过添加Canvas组件作隔离,使这一部分UI单独Rebatch,防止一小部分的UI变化导致整个复杂界面重建。(例如:1.一个大界面中的Text倒计时,可以给Text组件挂上Canvas。 2. 一个大界面中的,选中某个item的边框呼吸动画,可以给边框挂上Canvas。3 .动态UI层 整个层添加Canvas,例如用作飞图标动画容器的层,会跟随玩家移动的气泡UI层等)

4.源码实现

看另一篇,UGUI源码分析。

5.关键点,重要节点,疑难杂症场景

5.1 POT: power of two 是什么?

就是2的指数倍数的宽高的图。

5.2 如何简化Compiled-UI-Default.shader

现在完全不懂Unity的shader,等过了一遍之后再进行。

5.3 Image设置Sliced有什么用?设置FillCenter又有什么用?

Sliced就是设置九宫格。FillCenter就是把九宫格配置好四周之后,中间的空,填上。

unity gameobject物体尺寸 unity游戏尺寸_Layout_05

 

 

6.其他

6.1 UGUI性能分析工具

6.1. 1 Unity Profiler 工具

Unity Profiler的主要用途是执行性能比较分析:当Unity Profiler运行的时候进行enabling和disabling的操作,它可以迅速的缩小定位到性能问题最大的UI层级。查看profiler输出结果的“Canvas.BuildBatch”和“Canvas.SendWillRenderCanvases”。               Canvas.BuildBatch是执行Canvas的Batch build过程的底层代码计算量。Canvas.SendWillRenderCanvases包含了C#脚本对Canvas组件的willRenderCanvases事件的订阅的调用。UGUI的CanvasUpdateRegistry类接收这个事件并且通过它来执行前文所描述的rebuild过程。预计所有被标dirty的UI组件都会在这个时候更新他们的Canvas Renderer。

注意:为了更容易地看到UI性能的差异,通常建议禁用除了Rendering和Scripts以外所有trace category。这可以通过点击CPU Usage profiler左侧的名叫trace category旁边的彩色方块来完成。

还要注意,category可以在CPU profiler中重新排列,可以点击或者拖拽category向上或者向下来对他们进行重新排列。

unity gameobject物体尺寸 unity游戏尺寸_UI_06

 

6.1.2 Unity Frame Debugger 工具

Unity Frame Debugger是一个减少UGUI的draw call的实用工具。这个内置的工具可以通过Unity Editor中的Window菜单来访问。当它运行的时候,它将显示包括UGUI在内的所有Unity产生的draw call。特别要注意的是,Unity Frame Debugger             在Unity Editor界面就可以更新游戏视口产生的draw call信息,因此可以用来尝试不同的UI配置而无需进入游戏模式。

UGUI的drawcall产生的位置取决于Canvas组件上被设置的渲染模式:

1.Screen Space – Overlay将出现在Canvas.RenderOverlays组中。

2.Screen Space – Camera将出现在Render.TransparentGeometry子项,所选渲染相机的Camera.Render组中。

3.World Space将出现在Render.TransparentGeometry子项,每个可以看见Canvas的World Space的摄像机中。

如果UI的shader没有被自定义的shader替换的话,那么所有UI都可以被 “Shader: UI/Default”识别,列出在哪个组和drawcall的细节。在下图中请看高亮红框标注的地方。

unity gameobject物体尺寸 unity游戏尺寸_Layout_07

在调整UI的时候观察Unity Frame Debugger所显示的信息,这就相对比较简单的使Canvas中的UI元素最优的合成batch。最常见的与设计相关的打断批次的原因是UI元素间不小心造成的重叠。

所有的UGUI组件将它们的几何体生成成一系列的 quad。然而,很多sprite和text只占用用于显示它们的 quad的一小部分,留下了大量的剩余空间。这样的结果就是,UI开发者无意中使多个不同的 quad互相覆盖,它们的texture来自不同的        material,不能合成batch。

由于UGUI的操作完全在透明队列中,任何有不能合batch的quad在它上边的quad必须在不能合batch的quad之前绘制,因此它不能与放在不能合batch的quad上的quad合batch。(翻译这段我尽力了,但是估计还是不清楚。我讲一下大意:就是两个能合batch的quad中间夹了一个不能合batch的quad,造成这两个quad也不能合batch了)

考虑一个情景,有三个quadA、B、C。假设这三个quad彼此覆盖,并且A和C使用了相同的Material,B使用了单独的Material。B不能和A、C合成batch。

如果在层级结构中从上到下的是A、B、C,那么A、C也不能合batch,因为B必须绘制在A的上面,C的下面。然而,如果B被放在可被合batch的quad前面或者后面,那么可以被合batch的quad就能构成batch。B只需要在batch的quad之前或者之后绘制,而不会介入其中。

关于这个问题更深入的探讨,请看Canvas章节的Child order部分。

 

6.1.3 Instruments & VTune 工具

XCode的Instruments和Intel的VTune各自可以非常深入的分析UGUI的rebuild和Canvas的batch计算在Apple设备和Intel CPU上的性能。方法名称几乎和我们之前介绍过的Unity Profiler的标签完全相同。它们是:

Canvas::SendWillRenderCanvases是一个C++父类调用C#中的Canvas.SendWillRenderCanvases方法,并控制 Unity Profiler中该行显示。它包含了用于进行rebuild过程的代码,这已经在上一章节详细介绍了。

Canvas::UpdateBatches几乎和Canvas.BuildBatch完全相同,但是增加了Unity Profiler页面并不包括的代码引用。它运行上文描述的Canvas的batch建立的实际过程。

当通过IL2CPP构建一个Unity APP时,这些工具可以被用于更深入的查看C#中Canvas::SendWillRenderCanvases的编译。(注意:编译的方法的名字是近似的。)

IndexedSet_Sort和CanvasUpdateRegistry_SortLayoutList是用于排序显示在标为dirty的Layout组件被重新计算之前的一个列表。如上文所述,这包括了计算每个Layout组件的父transform数量。

ClipperRegistry.Cull调用所有IClipRegion接口注册的实现者。内置的实现者包括使用IClipRegion接口的RectMask2D组件。当ClipperRegistry.Cull被调用时,RectMask2D组件将遍历在它层级下的所有要被裁剪的UI元素,更新他们的剔除信息。

所有可嵌套元素,并要求它们更新其剔除信息。

Graphic_Rebuild包含所有要显示的Image,Text或其他Graphic派生的组件所需要的网格的实际计算性能开销。在这之下有其他一些方法,如Graphic_UpdateGeometry,最值得注意的是Text_OnPopulateMesh。

-当Best Fit勾选时,Text_OnPopulateMesh通常是一个热点。这将在本指南后面详细讨论。

-网格修饰符,比如Shadow_ModifyMesh和Outline_ModifyMesh也在这里运行。通过这些方法可以看到shadow,       outline和其他特殊效果组件的计算性能开销。

6.1.4 Xcode Frame Debugger和Intel GPA 工具

底层的Frame Debugger对监测UI不同独立部分的batch性能开销和UI过度绘制开销非常重要

Xcode Frame Debugger的使用

为了测试一个给定的UI是否过度榨取GPU资源,可以使用Xcode内置的GPU诊断工具。首先将项目配置为使用Metal或OpenGLES3,然后进行构建并打开生成的Xcode项目工程。如果Unity在OpenGLES 2下运行,则Xcode不能对Unity进行分析,因此这些技术不能用于较旧的设备。注意:在某些版本的Xcode中,为了使图形分析器工作,有必要在Build Scheme中选择适当的Graphics API。为此,请转到Xcode中的Product菜单,展开Scheme菜单项,然后选择Edit Scheme ….选择Run target并转到Options页面。更改GPU Frame Capture选项来使API适配您的工程。假设Unity工程设置了自动选择图形API,则大多数新一代的iPad将默认选择Metal。如果有疑问,请启动项目并查看Xcode中的调试日志,前面的几行应该会指出哪个渲染路径(Metal,GLES3或GLES2)正在被初始化。注意:上述调整在Xcode 7.4中应该不是必需的,但在Xcode 7.3.1和更旧的版本中仍然偶尔会被发现是必须的。在iOS设备上构建并运行项目。GPU profiler显示在Xcode的Navigator边栏中,点击FPS条目。(图请参见原网页)GPU分析器中第一个重要的是屏幕中的三个条目:“Tiler”、“Renderer”、“Device”。这些表示:

“Tiler”是对GPU生成几何体(包括在顶点着色器中的花费时间)过程中压力的衡量。

——一般来讲,“Tiler”值高表明顶点着色器计算过慢或者是绘制的顶点过多。

“Renderer”是对GPU的像素流水线压力的衡量。

——一般来讲,“Renderer”值高表明应用程序超过了GPU的最大填充率,或是片段着色器效率低下。

“Device” 是GPU使用的综合衡量标准,包括“Tiler”和“Renderer”的性能分析。它通常可以被忽略,因为它大体上跟踪监测“Tiler”和“Renderer”的较高者。

有关Xcode GPU  Profiler的更多信息,请参阅此文档(链接见原网页)。

Xcode’s Frame Debugger可以通过点击隐藏在GPU Profiler底部的小“相机”图标来打开。在下面的屏幕截图中,通过箭头和红色框突出显示。(截图见原网页)

暂停一下之后,Frame Debugger的摘要视图就会出现,如下所示(截图见原网页):

在使用默认UI着色器时,假设默认UI着色器没有被自定义着色器替换,那么由UGUI系统生成的渲染几何图形的开销将显示在“UI / Default”着色器通道下。在上面的截图中可以看到这个渲染管线的默认的UI着色器是“UI / Default”。

UGUI只产生quad,所以顶点着色器不太可能给GPU Tiler流水线产生压力。出现在这个着色器中的任何问题都应归结于填充率问题。

6.2 分析各种工具得出的结果

1- 如果Canvas.BuildBatch或Canvas :: UpdateBatches占用了过多的CPU时间,则可能的问题是单个Canvas上的Canvas Renderer组件数量过多。请参阅“Canvas”一章的“Splitting Canvases”章节。

2- 如果GPU过度的时间花费在绘制UI上,并且frame debugger表明片段着色器流水线是瓶颈,那么应该是UI的像素填充率超过了GPU的能力,最可能的原因是UI的过渡绘制。请参考Fill-rate, Canvases and input章节的Remediating fill-rate issues部分。

3- 如果Graphic的rebuild占用了过多的CPU,如在Canvas.SendWillRenderCanvases或者Canvas::SendWillRenderCanvases中看到了大量的CPU时间占用,那么就需要进行深层分析,应该与Graphic的rebuild过程中的一些部分有关。

4- 如果大量的WillRenderCanvas花费在IndexedSet_Sort或是CanvasUpdateRegistry_SortLayoutList上,时间花费在对dirty的layout组件列表进行排序,那么就要考虑减少Canvas中的Layout组件数量。请在Replacing layouts with RectTransforms和Splitting Canvases部分中也许会找到补救措施。

5- 如果过多的时间花在Text_OnPopulateMesh上,那么Text网格的生成就是罪魁祸首。请参阅Best Fit和 Disabling Canvas Renderers部分,也许会找到补救措施。并考虑Splitting Canvases中的建议,如果正在重建的大部分文本实际上并未更改其基础字符串数据,text大量rebuild实际上并没有改变其基础的字符串数据。

6- 如果时间花在内置的Shadow_ModifyMesh或Outline_ModifyMesh(或任何其他使用的ModifyMesh),则问题在于花费在计算修饰性网格过多的时间。考虑删除这些组件,并通过静态图像实现其视觉效果。

7- 如果Canvas.SendWillRenderCanvas中没有特定的热点,或者它看起来每帧都在运行,那么问题可能是动态元素与静态元素混合在一起,致使整个Canvas过于频繁地重建。参见Splitting Canvases部分。