整个Profiler入门都会重头说说,重点UI
Profiler是官方提供的一个性能监控工具,甚至可以远程连接手机测试;不过这么多年过去了,连接功能还是和几年前一样,一样废,就不要指望了
重点说UI部分,因为最容易入门
如上图,好的
已经说完了,谢谢阅读
===================================================
其实网上也说很多了,+上我这个文章,是不是觉得说了等于没说呢
其实Unity这个工具也是如此,网上的,官方的资料一大堆,说的很详细
但实际用起来,用了等于没用,关键点,具体问题的产生原因官方文档压根就没说
网上关于Unity Profiler 很多说法,做法,实践到底能用不??其实和成功学一样,都是靠个人摸出来,对别人可能有滴点用处,对你肯定没用
这里只是按个人理解的Profiler,随便说说
DC
用一个脚本做个小测试,以下脚本代码不全,用了一个Easy Buttons组件,请自行斟酌使用
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using EasyButtons;
/// <summary>
/// 测试DC用
/// </summary>
public class GenMoreUIs : MonoBehaviour
{
public Transform genRoot;
public List<GameObject> uis;
public int GenCount=10;
// Start is called before the first frame update
void Start()
{
}
[EasyButtons.Button("GenMoreUIs")]
public void GenMore()
{
foreach (var ui in uis)
{
for (int i = 0; i < GenCount; i++)
{
Instantiate(ui,genRoot,false);
}
}
}
}
增加drawcall除了在Status观察到,也可以在Profiler - UI 显示
其中
Batch Count 列也就是DC,很多年前Unity已经改成这个名字,可能大家还是习惯叫DrawCall吧
但是,总结几点
1.即使增加1W个DC(实际上你也不会这么干)只有增加那一刻会有峰值
2.即使DC很多,很快就已经恢复render的效率,这应该得益于Unity框架自身的剔除渲染,原理之后再分析吧
3.实际中也没听过过那个团队靠减低DC,达成优化的目的,更多时候说给老板听而已
4.所在的团队是碰过某些很特定(必然触发)的情况有render超出,但已经是极少的情况,有点类似内存溢出,是难定位,收益少,出现几率很低的BUG了】
5.没看出(观察到)Ui的DC增加对GPU,CPU的影响
实际上,UNITY官方已经兑现承诺,将UnityEngine.UI开源了,我们可以从中了解更多
腾讯XLUA都开源了,其实没什么不能开源的
已失效的开源地址:https://bitbucket.org/Unity_Technologies/ui/
OverDraw
(待补充)
Layout(Rebuild)
从大师2017的视频的结论(原理请自行看源码,CSDN好像有一个小哥分析了整个UnityUI源码):
即使你不使用layout系统,但一个你用到UIElement,也就是UI.Text或者UI.Image,也仅仅是enable这样简单的操作,也会使得整个layout重建,这是相当耗时的,原因一是:
AllComponents.Get(),原因二是:LayoutGroup会在每次layout call调用,每多一个LayoutGroup更会多一次,HorizantalLayout或GirdLayout,这些滚动控件也是国内使用大户,非常耗时;
再做一个小小测试
*还是缺easybuttons控件,想直接用下面代码,请科学上网搜索并下载 Standard Assets 的easybuttons 资源
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
using Random = System.Random;
/// <summary>
/// 测试UnityEngine.UI RebuildLayout Queue
/// </summary>
[ExecuteInEditMode]
public class GenLayoutRebuild : MonoBehaviour
{
public Transform rootTransform;
public Text changeText;
public Image changeImage;
[Range(0.001f, 0.5f)] [FormerlySerializedAs("TickInterval")] [Header("定时器")]
public float tickInterval = 1f;
public Coroutine tickCoroutine;
public bool isTryRunning;
public enum ChangeStuff
{
ChangeText,
Disable,
}
public ChangeStuff changeStuffType;
public GameObject SlotScrollViewContent;
public GameObject SlotPrefab;
private void OnEnable()
{
if (rootTransform == null)
rootTransform = transform;
}
[EasyButtons.Button]
public void AttachLayoutElements()
{
var children = rootTransform.GetComponentsInChildren<Transform>();
foreach (var chi in children)
{
if (chi.GetComponent<LayoutElement>() == null)
chi.gameObject.AddComponent<LayoutElement>();
}
}
[EasyButtons.Button]
public void TryChangeText()
{
changeStuffType = ChangeStuff.ChangeText;
if (tickCoroutine == null)
{
tickCoroutine = StartCoroutine(EverySecTick());
}
}
[EasyButtons.Button]
public void TryDisablText()
{
changeStuffType = ChangeStuff.Disable;
if (tickCoroutine == null)
{
tickCoroutine = StartCoroutine(EverySecTick());
}
}
[EasyButtons.Button]
public void StopTry()
{
if (tickCoroutine != null)
StopCoroutine(tickCoroutine);
isTryRunning = false;
tickCoroutine = null;
}
[EasyButtons.Button("测试所有子节点disable,样例太少")]
public void TryAllDisable()
{
if (rootTransform.childCount == 0) return;
var act = rootTransform.GetChild(0).gameObject.activeSelf;
var trans = rootTransform.GetComponentsInChildren<Transform>(); //只能找到不隐藏的对象
foreach (var tran in trans)
{
tran.gameObject.SetActive(!act);
}
}
[EasyButtons.Button("填充ScrollView,测layout")]
public void ShowScrollViewContent()
{
Cleanup();
List<string> recipes = new List<string>();
for (int i = 0; i < 20; i++)
{
recipes.Add("" + i);
}
foreach (string s in recipes)
{
GameObject go = Instantiate(SlotPrefab);
go.transform.SetParent(SlotScrollViewContent.transform);
}
}
IEnumerator EverySecTick()
{
while (true)
{
isTryRunning = true;
if (changeText != null)
{
if (changeStuffType == ChangeStuff.ChangeText)
{
string[] strs = {"fff", "bbb", "ccc"};
var rand = new Random();
int i = rand.Next(0, strs.Length);
changeText.text = strs[i];
}
else if (changeStuffType == ChangeStuff.Disable)
{
changeText.gameObject.SetActive(!changeText.gameObject.activeSelf);
}
}
yield return new WaitForSeconds(tickInterval);
}
}
private void OnDestroy()
{
if (tickCoroutine != null)
StopCoroutine(tickCoroutine);
}
void Cleanup()
{
if(SlotScrollViewContent!=null)
for (int i = SlotScrollViewContent.transform.childCount - 1; i >= 0; i--)
{
DestroyImmediate(SlotScrollViewContent.transform.GetChild(i).gameObject);
}
}
}
尽管2017的大神,说的很重要
但是,
但是,
实测一轮下来
Render Queue 所耗时 ms 比 RebuildLayout Queue 多太多了
(可能也不排除是没有合批的原因,但无论如何layout rebuild所花时间并没有理论上的多)
所以,
我要解决的是ui render
粒子render
animation render
物理系统 Render
render,render,还是render
附上:2个canvas 的 cpu 对比
Render
这里就结合我之前分享的billboard ui; 动态ui做一些测试
(待补充)
但其实发现这个损耗比rebuild要多
合批
如下图,可能是Different Material Instance,可能是Different Texture导致不能
总之,找到这些问,并解决。。。
UI动画
(待补充)
RenderTexture
(待补充)
对象池
(待补充,可减少GC)
参考:
没什么实用价值的入门Profiler资料
Unity Profiler性能分析全解析_Tokyo_2024的博客-CSDN博客
官方手册
UI 和 UI Details Profiler - Unity 手册 (unity3d.com)
UIDRAwCall 优化参考,多1~n个角度