Editor编程 GUILayout为什么控件一多一复杂性能就差的不行?

UnityEditor所用的UI框架是很老的那套IMGUI。
首先我们要知道IMGUI的处理流程。IMGUI将每一次的绘制拆分成好几帧,不同帧所处理的工作任务都不一样,任务类型由EventType.current表示。
EventType.Layout EventType.Repaint 这两个类型是IMGUI性能里最重要的。

GUILayout内部维护有一个stack,stack里保存的是每个控件的rect。
每次OnGUI执行时调用GUILayout.Button,第一帧EventType.current是EventType.Layout,这一帧并不做绘制工作,而是先计算GUILayout.Button的Rect push到GUILayout内部维护的stack里。接着在EventType.current是EventType.Repaint的那帧正式绘制。

所以IMGUI的性能问题主要是出在 计算控件大小、布局、渲染,还有就是字符串转GUIStyle的转换效率上。

//测试CalcSize方法性能GUILayout.Button
//GUI.skin.button.CalcSize(new GUIContent("测试" + i));
//计时开始
for(int i=0;i<1000;i++){
 //按钮创建代码
 GUI.skin.button.CalcSize(new GUIContent("测试" + i));
}
//计时结束
//耗时 0.0146211560081611
//测试GetRect方法性能
//GUILayoutUtility.GetRect(new GUIContent("测试" + i),GUI.skin.button);
//计时开始
for(int i=0;i<1000;i++){
 //按钮创建代码
 GUILayoutUtility.GetRect(new GUIContent("测试" + i),GUI.skin.button);
}
//计时结束
//耗时 0.000653807926937588
//测试string->GUIStyle性能
//GUILayoutUtility.GetRect(new GUIContent("测试" + i), "button");
//计时开始
for(int i=0;i<1000;i++){
 //按钮创建代码
 GUILayoutUtility.GetRect(new GUIContent("测试" + i), "button");
}
//计时结束

10000个UI对象从layout到repain一个周期耗时

方法

数量

Layout

Repaint

                GUILayoutUtility.GetRect(new GUIContent("测试" + i), "button");

10000

0.62s

0.009s

                GUILayoutUtility.GetRect(new GUIContent("测试" + i), GUI.skin.button);

10000

0.62s

0.013s

                GUI.skin.button.CalcSize(new GUIContent("测试" + i));

10000

0.59s

0.48s

                GUILayoutUtility.GetRect(new GUIContent("测试" + i), EditorStyles.miniButton);

10000

0.61s

0.011

 

可以看出 GetRect和CalcSize 耗时几乎没差多少。CalcSize才是IMGUI性能大户。
字符串转GUIStyle性能与用内置GUIStyle相差无几。


所以如果IMGUI里用GUILayout布局的控件不能超过一定量。我测试的结果是1000个按钮以上,CalcSize的计算负担就会过大,Editor界面开始卡。卡不是因为渲染的多,而是因为EventType.current==EventType.layout的那一帧,为了计算布局和控件size消耗太大。

优化的方案有两种
1、尽量减少控件复杂度,不要用GUIlayout.Horizontal和GUILayout.Vertical来组合包装一个更复杂的控件。
2、列表项太多就分页处理。
3、学外国一些插件的做法,自己缓存控件rect用GUI来画绝对坐标控件而不是每次重绘都用GUILayout重排布局。

你可能感兴趣的:(Unity3d)