几个有帮助的性能分析工具。关键工具:
- Unity Profiler
- Unity Frame Debugger
- Xcode's Instruments or Inter VTune
- Xcode's Frame Debugger or Intel GPA
拓展工具提供了方法级CPU毫秒级性能分析的解决方案,包括draw-call的细节和shader的性能分析。注意XCode帧调试和仪器只能在IL2CPP构建在苹果平台上的时候可用。
Unity Profiler
Unity Profiler的基础使用是去显示可对比的性能数据:在Unity Profiler运行的时候启用或者禁用UI元素,可以快速在UI hierarchy中缩小性能问题出现的位置。
去分析这个,可以去看Canvas.BuildBatch
和Canvas.SendWillRenderCanvases
的输出情况。
Canvas.BuildBatch
是原生代码计算平台的Canvas Batch创建过程,如前所述Canvas.SendWillRenderCanvases
将调用注册了willRenderCanvases
事件的C#脚本。Unity UI的CanvasUpdateRegistry
类接收这个事件并用它来实现重构过程。这时dirty的UI组件将更新Canvas Renderers。
注意:为了更容易的观察UI的表现,建议禁止除了"Rendering","Scripts"和"UI"以外的全部跟踪类别。在Unity 2017.1中UI类别是全新的,但是,UI部分还是存在一些问题,一些UI元素没有正确的分类,可能在Canvas.SendWillRenderCanvases
被认为是UI
,但是在Canvas.BuildBatch
被分类为Others
和Rendering
。
在2017.1或更高级版本中。这个profiler是Profiler窗口的最后一个,包括两个timeline和一个batch viewer。
第一个timeline显示CPU消耗时间在两个类别之间,分别计算layout和rendering过程。这个也存在一些问题一些UI function没有被包括进来。
第二个timeline显示了全部的batches,顶点和显示了event markers。 可以看见点击事件,这些marker帮助来确认造成CPU spike的原因
最后UI Profiler最有帮助的方法是在底部的batch viewer。这里可以看见全部的canvases和它们下面的生成的batched的列表。这里列出了可能感兴趣的关于每个canvas和batch的细节,这里你可以看到如何更好的优化UI和理解他们是如何batch的。
一个最频繁的原因,是UI元素使用了不同的texture或者material。在许多情况中,这可以通过sprite atlases来解决。
Unity Frame Debugger
Unity Frame Debugger是一个用于减少Unity UI 产生的 draw calls数量的很有用的工具。在menu的中的Window选项中可以打开。此工具将显示Unity产生的全部draw calls。
在编译器模式下frame Debugger将显示游戏场景中的draw calls。
Unity UI draw calls的位置取决于Canvas的渲染模式:
- Screen Space-Overlay 将在
Canvas.RenderOverlays
下产生 - Screen Space-Camera 将在所选相机
Camera.Render
组下面,作为Render.TransparentGeometry的子组。 - World Space 将显示在每一个可以看见Canvas的世界空间相机的子组Render.TransparentGeometry下面。
所有的UI被分类到"Shader:UI/Default"一栏。
通过改变UI可以观察到,Unity最大化结合UI元素到batch的能力相对简单。造成broken batches的情况很多情况下是因为无意义的重叠。
全部的Unity UI组件生成四边形作为他们的图元,然而许多UI图片和UI文字仅仅表示他们的多边形的一部分,剩下的部分是空白。这个的结果,一种常见的情况,开发人员无意中重叠了多个四边形的部分,它们使用的来自不同材质的纹理,这样它们无法进行批处理。
Unity UI的处理完全在transparent queue中,任何unbatchable quads覆盖在它们至上必须在unbatchable quads之前进行绘制,这样就不能与其他的quads进行batch。
这里有一种情况,在hierarchy中从顶到底是A,B和C,A、C使用的同种材质,但是B使用的不同材质,Quad B不能与A、C进行batched。因为B必须在A之上C之下。
Instruments 与 VTune
Xcode的Instruments和Intel的VTune提供了额外的Unity UI重建的性能分析和Canvas batch计算在苹果或者Intel CPU上,方法的名字几乎相同。
- Canvas::SendWillRenderCanvases 是C++父方法调用Canvas.SendWillRenderCanvases C#中的方法。这个方法包括重建的过程。
- Canvas::UpdateBatches 是和Canvas.BuildBatch相同,但是包括Unity Profiler未包含的其他样板代码。
当与通过IL2CPP构建的Unity app结合使用的时候,使用这些工具可以更深入的了解C#的Canvas::SendWillRenderCanvases的代码。以下是一些主要的方法:
-
IndexedSet_Sort
和CanvasUpdateRegistry_SortLayoutList
是用于在layout重新计算之前给dirty layout components进行排序的。如上所述,这涉及计算每个布局组件上方的父变换的数量。 -
ClipperRegistry_Cull
调用全部注册IClipRegion
接口的。内置实现包括RectMask2D。待用你ClipperRegistry.Cull
的时候RectMask2D
组件循环全部clippable元素,请求它们更新culling信息 -
Graphic_Rebuild
包括实际计算mesh需要的图片,文本或者其他图形驱动组件。
分析性能分析结果
收集性能数据之后,几个总结可以被描绘出来。如果Canvas.BuildBatch
或Canvas::UpdateBatch
占用了过多的CPU事件,那么问题可能是因为Canvas中过多的Render组件在一个Canvas中。可以参考Canvas中的Splitting Canvas部分。
如果在GPU上消耗了过多的时间,并且frame debugger指示性能瓶颈是在fragment shader管线,UI可能超出了GPU能提供的像素填充率。这种情况的造成原因很可能是过多的UI重绘。
如果重绘过程使用了过多的CPU,如果很大的一部分时间消耗在Canvas.SendWillRenderCanvases
或 Canvas::SendWillRenderCanvas
,那么就需要更进一步的深度分析。
如果WillRenderCanvas
的很多消耗在IndexedSet_Sort
或CanvasUpdateRegistry_SortLayoutList
,那么时间很可能被消耗在了dirty layout组件的排序。减少Canvas中的layout组件来解决这个问题。
如果过多的时间消耗在了Text_OnPopulateMesh
,那么问题出现的罪魁祸首是text meshes。适配或禁用部分Canvas是可用的调控措施,如果大部分重构的文本没有字符串的改变,可以考虑分割Canvases。
如果过多的时间消耗在Shadow_ModifyMesh
或Outline_ModifyMMesh
,那么问题是计算网格的修饰效果。考虑移除这些组件和通过静态图片实现这些效果。
如果Canvas.SendWillRenderCanvases
没有特定的热点,或者它在每一帧显示都有不同,那么问题可能是动态元素和静态元素收集在一起然后强制Canvas频繁重建。