UGUI性能分析工具
性能分析有很多可用的工具,下面是常用的几种:
- Unity Profiler
- Unity Frame Debugger
- Xcode Instruments
- Xcode Frame Debugger
Unity Profiler
Unity Profiler主要用来进行比较分析:在Unity Profiler运行的时候,通过启用、禁用一些 UI 元素,如果发现波形的振幅迅速变小,那么说明这部分的UI可能存在一定的性能问题。
为了分析这种情况,观察Unity Profiler 中的
Canvas.BuildBatch
和
Canvas.SendWillRenderCanvases
行。
Canvas.BuildBatch
是本机代码调用,主要是Canvas的批处理构建操作,可以参考
前文
的描述。
Canvas.SendWillRenderCanvases
包含对 Canvas.willRenderCanvas 事件订阅者的调用,UGUI 的 CanvasUpdateRegistry 类接收该事件,并且进行重构操作,所有被标记为 dirty 的组件,它们的 CanvasRenderer 将在此时被更新。
提示
:为了能够更贱方便的观察性能变化,除了 “Renderer”、“Scripts”、“UI” 之外,其他的分类可以禁用掉,通过点击分类前面的小方块可以完成禁用,通过上下拖动各个分类也可以进行排序。
UI分类是在2017.1之后才添加的,不幸的是,部分UI更新操作的分类并不正确,所以在观察 UI 曲线的时候需要非常小心,因为它可能包括相关的UI 调用,例如,Canvas.SendWillRenderCanvases 的分类是 “UI”,但是 Canvas.BuildBatch 的分类是 “Others”、“Renderer”。
在2017.1之后,同时还新增了
UI Profiler
,它包含两条时间线和一个 Batch 视窗。
第一个时间线按照“Layout”、“Render”两个分类来展示 CPU 的消耗,这个也存在上述问题,有些方法没有进行说明。
第二个时间线显示了Batch的个数、定点数量以及事件标记,在上面的截图中,可以看到在时间线上有几个按钮点击事件,这些标记可以帮助你判断是什么导致 CPU 出现尖峰。
最后,UI Profiler 提供的最有用的功能是 Batch 视窗,在视窗的左边,通过树形视图显示每个 Canvas以及他们声称的 Batch,数据列包含了一些关于Batch 和 Canvas 的有用信息,理解 Batch Breaking Reason(中断批处理的原因) 的含义对于更好的优化 UI 性能至关重要。
Canvas 在进行 Batch 的时候,可能会由于某些原因而停止,Batch Breaking Reason列指明了这个原因。其中最常见的原因是使用了不同的贴图、不同的材质,在多数情况下,该问题可以通过使用 Sprite Atlas 来解决,最后一列显示了与该 Batch 相关的一些物体,可以通过双击该物体来选择物体。
从2017.3开始,Batch 视窗只能在 Editor 模式下工作,通常Batch 在各个设备上是相同的,所以这依然是很有用的,如果你怀疑在不同的设备上 Batch 结果不一样,可以使用 Frame Debugger 来做进一步判断。
Unity Frame Debugger
对于减少由 UGUI 产生的drawcall,Unity Frame Debugger 是一个非常有用的工具,可以在Windwos->Frame Debugger下打开该工具,当启用时,它将显示所有由 Unity 产生的 drawcall,当然也包括UGUI产生的。
UGUI的 drawcall 根据渲染模式的不同,所在的位置也有所不同:
Screen Space – Overlay
模式时,将会出现在 Canvas.RenderOverlays 分组。
Screen Space – Camera
模式时,将会出现在所选相机的 Camera.Render 分组,作为一个
Render.TransparentGeometry
子组。
World Space
渲染模式时,将会作为一个
Render.TransparentGeometry
子组,出现在每个可以观察到该 Canvas 的相机下。
通过“Shader:UI/Default”可以找到各个UI对象(假如没有使用自定义Shader的情况下),如下图:
通过这一组信息,可以很容易将 Canvas 的批处理能力放到最大化,最常见的与设计相关的中断批处理现象就是,不小心将UI进行重叠。
由于 UGUI 的渲染完全在透明队列中,如果在四边形A上有不能合批的其他四边形B,那么A就必须在B之前被绘制,因此A不能和在B之上的四边形C进行合批。
举个列子,假设有三个相互重叠的四边形A,B,C,其中A、C使用相同的材质,B使用不同的材质,因此四边形B不能和A、C进行合批。在布局UI时,从上到下它们的顺序是A、B、C,那么A和C是不能进行合批的,因为B必须在A之前、C之后进行绘制。然而,如果B放在A、C之前或者之后,那么A和C就可以进行合批。
更加详细的内容,会在后续章节进行研究。
结果分析
当收集到分析数据之后,可能会得出一些结论,如果
Canvas.BuildBatch
或者
Canvas.UpdateBatches
占用了大量的CPU 时间,那么一个可能的问题是,一个Canvas 包含了过多的 CanvasRederer 组件,可以参考
分割画布
部分内容解决该问题。
如果 GPU 消耗了大量的时间去渲染 UI,并且 Frame Debugger 表明片段管线是性能的瓶颈,那么可能的原因是 UI 的像素填充率(pixel fill rate)超过了GPU 的处理能力,最有可能是出现了 UI 透支,可以参考
修复填充率问题
部分内容。
如果 Graphic 重构消耗了大量的 CPU 时间,大量时间用来执行
Canvas.SendWillRenderCanvases
和
Canvas::SendWillRenderCanvases
,此时需要进一步分析该问题,Graphic 重构过程中的某些步骤可能存在问题。
如果大量大时间花费在了
WillRenderCanvas
下的
IndexedSet_Sort
或者
CanvasUpdateRegistry_SortLayoutList
,那么时间是用来排序脏的Layout 组件列表。此时可以考虑减少Cavans下的Layout 组件。参考
使用RectTransform 替代 Layout
、
分隔画布
中的内容来解决该问题。
如果大量的时间花费在了
Text_OnPopulateMesh
,那么元凶就是文本网格的生成,参考
Best Fit
、
禁用画布
中的内容来解决问题,如果存在大量的 Text 被重构,但实际文本内容却没变化,此时可以考虑
分隔画布
中的一些建议。
如果大量的时间花费在了
Shadow_ModifyMesh
或者
Outline_ModifyMesh
(或者其他的一些 ModifyMesh 实现),那么问题在于花费了大量的时间来计算网格修改器,考虑删除这些组件,然后通过静态图片实现这些效果。
如果在
Canvas.SendWillRenderCanvases
没有发现特定的性能热点,或者他好像每一帧都在执行,那么问题可能是将动态元素和静态元素分到同一个组中,这会强制整个 Canvas 重构非常频繁。参考
画布分隔
部分内容进行解决。