miduo_client/Assets/Scripts/LuaProfiler/Common/Editor/TreeView/LuaProfilerTreeView.cs

679 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* ==============================================================================
* Filename: LuaExport
* Created: 2018/7/13 14:29:22
* Author: エル・プサイ・コングリィ
* Purpose:
* ==============================================================================
*/
namespace MikuLuaProfiler
{
#if UNITY_5_6_OR_NEWER
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
#region item
//The TreeElement data class is extended to hold extra data, which you can show and edit in the front-end TreeView.
public class LuaProfilerTreeViewItem : TreeViewItem
{
private static ObjectPool<LuaProfilerTreeViewItem> objectPool = new ObjectPool<LuaProfilerTreeViewItem>(30);
public static LuaProfilerTreeViewItem Create(Sample sample, int depth, LuaProfilerTreeViewItem father)
{
var dict = LuaProfilerTreeView.m_nodeDict;
LuaProfilerTreeViewItem mt;
mt = objectPool.GetObject();
mt.ResetBySample(sample, depth, father);
dict.Add(mt.fullName, mt);
return mt;
}
public void Restore()
{
objectPool.Store(this);
}
public int frameCalls { private set; get; }
//没什么意义因为Lua 执行代码的同时 异步GC所以导致GC的数字一直闪烁用上这个去闪烁
private long _showGC = 0;
public long showGC
{
private set
{
_showGC = value;
}
get
{
return _showGC;
//if (!UnityEditor.EditorApplication.isPlaying)
//{
// return _showGC;
//}
//if (Time.frameCount == _frameCount)
//{
// return _showGC;
//}
//else { return 0; }
}
}
public long totalMemory { private set; get; }
public long totalTime { private set; get; }
public long averageTime { private set; get; }
public float currentTime { private set; get; }
public int totalCallTime { private set; get; }
public string fullName
{
get;
private set;
}
public readonly List<LuaProfilerTreeViewItem> childs = new List<LuaProfilerTreeViewItem>();
public LuaProfilerTreeViewItem rootFather { private set; get; }
public LuaProfilerTreeViewItem father { private set; get; }
private int _frameCount;
public LuaProfilerTreeViewItem()
{
}
public void ResetBySample(Sample sample, int depth, LuaProfilerTreeViewItem father)
{
if (sample != null)
{
totalMemory = sample.costGC;
_showGC = sample.costGC;
totalTime = (long)(sample.costTime * 1000000);
displayName = sample.name;
fullName = sample.fullName;
}
else
{
totalMemory = 0;
totalTime = 0;
displayName = "root";
fullName = "root";
}
totalCallTime = 1;
averageTime = totalTime / totalCallTime;
this.id = LuaProfilerTreeView.GetUniqueId();
this.depth = depth;
childs.Clear();
if (sample != null)
{
for (int i = 0, imax = sample.childs.Count; i < imax; i++)
{
var dict = LuaProfilerTreeView.m_nodeDict;
LuaProfilerTreeViewItem mt;
var childSample = sample.childs[i];
if (dict.TryGetValue(childSample.fullName, out mt))
{
mt.AddSample(childSample);
}
else
{
var item = Create(sample.childs[i], depth + 1, this);
childs.Add(item);
}
}
}
this.father = father;
rootFather = this;
while (true)
{
if (rootFather.father == null)
{
break;
}
rootFather = rootFather.father;
}
_frameCount = Time.frameCount;
}
public void AddSample(Sample sample)
{
if (_frameCount == sample.frameCount)
{
_showGC += sample.costGC;
frameCalls += sample.calls;
currentTime += sample.costTime;
}
else
{
_showGC = sample.costGC;
frameCalls = sample.calls;
currentTime = sample.costTime;
}
totalMemory += sample.costGC;
totalTime += (long)(sample.costTime * 1000000);
totalCallTime += sample.calls;
averageTime = totalTime / totalCallTime;
for (int i = 0, imax = sample.childs.Count; i < imax; i++)
{
LuaProfilerTreeViewItem childItem = null;
var sampleChild = sample.childs[i];
if (LuaProfilerTreeView.m_nodeDict.TryGetValue(sampleChild.fullName, out childItem))
{
childItem.AddSample(sampleChild);
}
else
{
var treeItem = Create(sampleChild, depth + 1, this);
childs.Add(treeItem);
}
}
_frameCount = Time.frameCount;
}
}
#endregion
public class LuaProfilerTreeView : TreeView
{
#region pool
private static int _uniqueId = 0;
public static int GetUniqueId()
{
return _uniqueId++;
}
private readonly List<LuaProfilerTreeViewItem> roots = new List<LuaProfilerTreeViewItem>();
#endregion
#region field
private int m_showRootItemId = 0;
public bool m_seaching = false;
private List<int> m_expandIds = new List<int>();
private readonly LuaProfilerTreeViewItem root;
private readonly List<TreeViewItem> treeViewItems = new List<TreeViewItem>();
public readonly List<Sample> history = new List<Sample>(216000);
private GUIStyle gs;
public bool needRebuild = true;
public string startUrl = null;
public string endUrl = null;
#endregion
public LuaProfilerTreeView(TreeViewState treeViewState, float width)
: base(treeViewState, CreateDefaultMultiColumnHeaderState(width))
{
LuaProfiler.SetSampleEnd(LoadRootSample);
root = LuaProfilerTreeViewItem.Create(null, -1, null);
needRebuild = true;
multiColumnHeader.sortingChanged += (multiColumnHeader) => { needRebuild = true; };
history.Clear();
Reload();
}
private static MultiColumnHeader CreateDefaultMultiColumnHeaderState(float treeViewWidth)
{
var columns = new[]
{
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("Overview"),
contextMenuText = "Overview",
headerTextAlignment = TextAlignment.Left,
sortedAscending = false,
sortingArrowAlignment = TextAlignment.Right,
width = 500,
minWidth = 200,
maxWidth = 2000,
autoResize = true,
canSort = false,
allowToggleVisibility = true
},
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("totalMemory"),
contextMenuText = "totalMemory",
headerTextAlignment = TextAlignment.Center,
sortedAscending = false,
sortingArrowAlignment = TextAlignment.Right,
width = 80,
minWidth = 80,
maxWidth = 80,
autoResize = true,
canSort = true,
allowToggleVisibility = false
},
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("currentTime"),
contextMenuText = "currentTime",
headerTextAlignment = TextAlignment.Center,
sortedAscending = false,
sortingArrowAlignment = TextAlignment.Right,
width = 120,
minWidth = 120,
maxWidth = 120,
autoResize = true,
canSort = true,
allowToggleVisibility = false
},
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("averageTime"),
contextMenuText = "averageTime",
headerTextAlignment = TextAlignment.Center,
sortedAscending = false,
sortingArrowAlignment = TextAlignment.Right,
width = 120,
minWidth = 120,
maxWidth = 120,
autoResize = true,
canSort = true,
allowToggleVisibility = false
},
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("totalTime"),
contextMenuText = "totalTime",
headerTextAlignment = TextAlignment.Center,
sortedAscending = false,
sortingArrowAlignment = TextAlignment.Right,
width = 120,
minWidth = 120,
maxWidth = 120,
autoResize = true,
canSort = true,
allowToggleVisibility = false
},
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("GC"),
contextMenuText = "GC",
headerTextAlignment = TextAlignment.Center,
sortedAscending = false,
sortingArrowAlignment = TextAlignment.Right,
width = 60,
minWidth = 60,
maxWidth = 60,
autoResize = true,
canSort = true,
allowToggleVisibility = false
},
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("totalCalls"),
contextMenuText = "totalCalls",
headerTextAlignment = TextAlignment.Center,
sortedAscending = false,
sortingArrowAlignment = TextAlignment.Right,
width = 120,
minWidth = 120,
maxWidth = 120,
autoResize = true,
canSort = true,
allowToggleVisibility = false
},
new MultiColumnHeaderState.Column
{
headerContent = new GUIContent("Calls"),
contextMenuText = "Calls",
headerTextAlignment = TextAlignment.Center,
sortedAscending = false,
sortingArrowAlignment = TextAlignment.Right,
width = 80,
minWidth = 80,
maxWidth = 80,
autoResize = true,
canSort = true,
allowToggleVisibility = false
},
};
var state = new MultiColumnHeaderState(columns);
return new MultiColumnHeader(state);
}
public void Clear(bool includeHistory)
{
roots.Clear();
m_nodeDict.Clear();
treeViewItems.Clear();
m_showRootItemId = -2;
m_expandIds.Clear();
if (includeHistory)
{
history.Clear();
startUrl = null;
endUrl = null;
}
needRebuild = true;
}
protected override void DoubleClickedItem(int id)
{
/*
base.DoubleClickedItem(id);
var selectItem = FindItem(id, BuildRoot());
string fileName = "/" + selectItem.displayName.Split(new char[] { ',' }, 2)[0].Replace(".", "/").Replace("/lua", ".lua").Trim();
try
{
int line = 0;
int.TryParse(Regex.Match(selectItem.displayName, @"(?<=(line:))\d*(?=( ))").Value, out line);
//LocalToLuaIDE.OnOpenAsset(fileName, line);
}
catch
{
}*/
}
private bool CheckIsRootId(int id)
{
bool result = false;
foreach (var item in roots)
{
if (item.id == id)
{
result = true;
break;
}
}
return result;
}
protected override void ExpandedStateChanged()
{
var ids = GetExpanded();
if (setting)
{
m_expandIds = new List<int>(ids);
return;
}
base.ExpandedStateChanged();
foreach (var id in m_expandIds)
{
if (!ids.Contains(id))
{
if (m_showRootItemId == id)
{
SetExpanded(id, false);
m_showRootItemId = -2;
}
}
}
foreach (var id in ids)
{
if (!m_expandIds.Contains(id) && CheckIsRootId(id))
{
m_showRootItemId = id;
break;
}
}
m_expandIds = new List<int>();
m_expandIds.Add(m_showRootItemId);
needRebuild = true;
}
public static Dictionary<string, LuaProfilerTreeViewItem> m_nodeDict = new Dictionary<string, LuaProfilerTreeViewItem>();
public void ReLoadSamples(int start, int end)
{
if (history.Count == 0) return;
Clear(false);
end = Mathf.Max(Mathf.Min(end, history.Count - 1), 0);
start = Mathf.Max(Mathf.Min(start, history.Count - 1), 0);
startUrl = history[start].captureUrl;
endUrl = history[end].captureUrl;
end = Mathf.Min(history.Count - 1, end);
for (int i = start; i <= end; i++)
{
LoadRootSample(history[i], false);
}
}
public void SaveHisotry()
{
string path = EditorUtility.SaveFilePanel("Save Sample", "", DateTime.Now.ToString("yyyy-MM-dd-HH-MM-ss"), "sample");
File.WriteAllBytes(path, Sample.SerializeList(history));
}
public void LoadHistory()
{
string path = EditorUtility.OpenFilePanel("Load Sample", "", "sample");
byte[] datas = File.ReadAllBytes(path);
history.Clear();
history.AddRange(Sample.DeserializeList(datas));
ReLoadSamples(0, history.Count);
}
private Queue<Sample> samplesQueue = new Queue<Sample>(256);
private void LoadRootSample(Sample sample)
{
samplesQueue.Enqueue(sample);
}
public int GetNextProgramFrame(int start)
{
int ret = start + 1;
for (int i = start + 1, imax = history.Count; i < imax; i++)
{
ret = i;
var s = history[i];
if (s.costGC > LuaDeepProfilerSetting.Instance.captureGC)
{
break;
}
else if (s.costTime >= 1 / 30.0f)
{
break;
}
}
return Mathf.Max(Mathf.Min(ret, history.Count - 1), 0);
}
public int GetPreProgramFrame(int start)
{
int ret = start - 1;
for (int i = start - 1; i >= 0; i--)
{
ret = i;
var s = history[i];
if (s.costGC > LuaDeepProfilerSetting.Instance.captureGC)
{
break;
}
else if (s.costTime >= 1 / 30.0f)
{
break;
}
}
return Mathf.Max(Mathf.Min(ret, history.Count - 1), 0);
}
private void LoadRootSample(Sample sample, bool needRecord)
{
if (needRecord)
{
//迟钝了
if (sample.costTime >= 1 / 30.0f)
{
sample.captureUrl = Sample.Capture();
}
else if (sample.costGC > LuaDeepProfilerSetting.Instance.captureGC)
{
sample.captureUrl = Sample.Capture();
}
else
{
sample.captureUrl = null;
}
}
LuaProfilerTreeViewItem item;
string f = sample.fullName;
if (m_nodeDict.TryGetValue(f, out item))
{
item.AddSample(sample);
}
else
{
item = LuaProfilerTreeViewItem.Create(sample, 0, null);
roots.Add(item);
needRebuild = true;
}
if (needRecord)
{
history.Add(sample.Clone());
}
}
private void ReLoadTreeItems()
{
treeViewItems.Clear();
List<LuaProfilerTreeViewItem> rootList = new List<LuaProfilerTreeViewItem>(roots);
int sortIndex = multiColumnHeader.sortedColumnIndex;
int sign = 0;
if (sortIndex > 0)
{
sign = multiColumnHeader.IsSortedAscending(sortIndex) ? -1 : 1;
}
switch (sortIndex)
{
case 1: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalMemory - b.totalMemory); }); break;
case 2: rootList.Sort((a, b) => { return sign * Math.Sign(a.currentTime - b.currentTime); }); break;
case 3: rootList.Sort((a, b) => { return sign * Math.Sign(a.averageTime - b.averageTime); }); break;
case 4: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalTime - b.totalTime); }); break;
case 5: rootList.Sort((a, b) => { return sign * Math.Sign(a.showGC - b.showGC); }); break;
case 6: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalCallTime - b.totalCallTime); }); break;
case 7: rootList.Sort((a, b) => { return sign * Math.Sign(a.frameCalls - b.frameCalls); }); break;
}
foreach (var item in rootList)
{
AddOneNode(item, false);
SortChildren(sortIndex, sign, item);
}
}
private void SortChildren(int sortIndex, int sign, LuaProfilerTreeViewItem item)
{
if (item.childs != null && item.childs.Count > 0)
{
List<LuaProfilerTreeViewItem> rootList = item.childs;
switch (sortIndex)
{
case 1: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalMemory - b.totalMemory); }); break;
case 2: rootList.Sort((a, b) => { return sign * Math.Sign(a.currentTime - b.currentTime); }); break;
case 3: rootList.Sort((a, b) => { return sign * Math.Sign(a.averageTime - b.averageTime); }); break;
case 4: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalTime - b.totalTime); }); break;
case 5: rootList.Sort((a, b) => { return sign * Math.Sign(a.showGC - b.showGC); }); break;
case 6: rootList.Sort((a, b) => { return sign * Math.Sign(a.totalCallTime - b.totalCallTime); }); break;
case 7: rootList.Sort((a, b) => { return sign * Math.Sign(a.frameCalls - b.frameCalls); }); break;
}
foreach (var t in rootList)
{
SortChildren(sortIndex, sign, t);
}
}
}
private void AddOneNode(LuaProfilerTreeViewItem root, bool r)
{
treeViewItems.Add(root);
m_nodeDict[root.fullName] = root;
if (root.children != null)
{
root.children.Clear();
}
if (r) return;
foreach (var item in root.childs)
{
AddOneNode(item, true);
if (m_showRootItemId != root.rootFather.id && !m_seaching)
{
break;
}
}
}
bool setting = false;
protected override TreeViewItem BuildRoot()
{
while (samplesQueue.Count > 0)
{
Sample s = samplesQueue.Dequeue();
LoadRootSample(s, LuaDeepProfilerSetting.Instance.isRecord);
s.Restore();
}
if (!needRebuild)
{
return root;
}
ReLoadTreeItems();
// Utility method that initializes the TreeViewItem.children and -parent for all items.
SetupParentsAndChildrenFromDepths(root, treeViewItems);
setting = true;
foreach (var t in roots)
{
SetExpanded(t.id, t.id == m_showRootItemId);
}
setting = false;
needRebuild = false;
// Return root of the tree
return root;
}
protected override void RowGUI(RowGUIArgs args)
{
var item = (LuaProfilerTreeViewItem)args.item;
if (gs == null)
{
gs = new GUIStyle(GUI.skin.label);
gs.alignment = TextAnchor.MiddleCenter;
}
Rect r = args.GetCellRect(0);
args.rowRect = r;
base.RowGUI(args);
r = args.GetCellRect(1);
GUI.Label(r, LuaProfiler.GetMemoryString(item.totalMemory), gs);
r = args.GetCellRect(2);
GUI.Label(r, item.currentTime.ToString("f6") + "s", gs);
r = args.GetCellRect(3);
GUI.Label(r, ((float)item.averageTime / 1000000).ToString("f6") + "s", gs);
r = args.GetCellRect(4);
GUI.Label(r, ((float)item.totalTime / 1000000).ToString("f6") + "s", gs);
r = args.GetCellRect(5);
GUI.Label(r, LuaProfiler.GetMemoryString(item.showGC), gs);
r = args.GetCellRect(6);
GUI.Label(r, LuaProfiler.GetMemoryString(item.totalCallTime, ""), gs);
r = args.GetCellRect(7);
GUI.Label(r, item.frameCalls.ToString(), gs);
}
}
#endif
}