您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
373 行
13 KiB
373 行
13 KiB
using System;
|
|
using UnityEditor.IMGUI.Controls;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.Experimental.LowLevel;
|
|
using UnityEngine.Profiling;
|
|
|
|
namespace Unity.Entities.Editor
|
|
{
|
|
|
|
public delegate void SystemSelectionCallback(ScriptBehaviourManager manager, World world);
|
|
|
|
public class SystemListView : TreeView
|
|
{
|
|
private class AverageRecorder
|
|
{
|
|
private readonly Recorder recorder;
|
|
private int frameCount;
|
|
private int totalNanoseconds;
|
|
private float lastReading;
|
|
|
|
public AverageRecorder(Recorder recorder)
|
|
{
|
|
this.recorder = recorder;
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
++frameCount;
|
|
totalNanoseconds += (int)recorder.elapsedNanoseconds;
|
|
}
|
|
|
|
public float ReadMilliseconds()
|
|
{
|
|
if (frameCount > 0)
|
|
{
|
|
lastReading = (totalNanoseconds/1e6f) / frameCount;
|
|
frameCount = totalNanoseconds = 0;
|
|
}
|
|
|
|
return lastReading;
|
|
}
|
|
}
|
|
internal readonly Dictionary<int, ScriptBehaviourManager> managersById = new Dictionary<int, ScriptBehaviourManager>();
|
|
private readonly Dictionary<int, World> worldsById = new Dictionary<int, World>();
|
|
private readonly Dictionary<ScriptBehaviourManager, AverageRecorder> recordersByManager = new Dictionary<ScriptBehaviourManager, AverageRecorder>();
|
|
|
|
private const float kToggleWidth = 22f;
|
|
private const float kTimingWidth = 70f;
|
|
|
|
private readonly SystemSelectionCallback systemSelectionCallback;
|
|
private readonly WorldSelectionGetter getWorldSelection;
|
|
|
|
private static GUIStyle RightAlignedLabel
|
|
{
|
|
get
|
|
{
|
|
if (rightAlignedText == null)
|
|
{
|
|
rightAlignedText = new GUIStyle(GUI.skin.label);
|
|
rightAlignedText.alignment = TextAnchor.MiddleRight;
|
|
}
|
|
|
|
return rightAlignedText;
|
|
}
|
|
}
|
|
|
|
private static GUIStyle rightAlignedText;
|
|
|
|
internal static MultiColumnHeaderState GetHeaderState()
|
|
{
|
|
var columns = new[]
|
|
{
|
|
new MultiColumnHeaderState.Column
|
|
{
|
|
headerContent = GUIContent.none,
|
|
contextMenuText = "Enabled",
|
|
headerTextAlignment = TextAlignment.Left,
|
|
canSort = false,
|
|
width = kToggleWidth,
|
|
minWidth = kToggleWidth,
|
|
maxWidth = kToggleWidth,
|
|
autoResize = false,
|
|
allowToggleVisibility = false
|
|
},
|
|
new MultiColumnHeaderState.Column
|
|
{
|
|
headerContent = new GUIContent("System Name"),
|
|
headerTextAlignment = TextAlignment.Left,
|
|
sortingArrowAlignment = TextAlignment.Right,
|
|
canSort = true,
|
|
sortedAscending = true,
|
|
width = 100,
|
|
minWidth = 100,
|
|
maxWidth = 2000,
|
|
autoResize = true,
|
|
allowToggleVisibility = false
|
|
},
|
|
new MultiColumnHeaderState.Column
|
|
{
|
|
headerContent = new GUIContent("main (ms)"),
|
|
headerTextAlignment = TextAlignment.Right,
|
|
canSort = false,
|
|
width = kTimingWidth,
|
|
minWidth = kTimingWidth,
|
|
maxWidth = kTimingWidth,
|
|
autoResize = false,
|
|
allowToggleVisibility = false
|
|
}
|
|
};
|
|
|
|
return new MultiColumnHeaderState(columns);
|
|
}
|
|
|
|
private static TreeViewState GetStateForWorld(World world, List<TreeViewState> states, List<string> stateNames)
|
|
{
|
|
if (world == null)
|
|
return new TreeViewState();
|
|
|
|
var currentWorldName = world.Name;
|
|
|
|
var stateForCurrentWorld = states.Where((t, i) => stateNames[i] == currentWorldName).FirstOrDefault();
|
|
if (stateForCurrentWorld != null)
|
|
return stateForCurrentWorld;
|
|
|
|
stateForCurrentWorld = new TreeViewState();
|
|
states.Add(stateForCurrentWorld);
|
|
stateNames.Add(currentWorldName);
|
|
return stateForCurrentWorld;
|
|
}
|
|
|
|
public static SystemListView CreateList(List<TreeViewState> states, List<string> stateNames, SystemSelectionCallback systemSelectionCallback, WorldSelectionGetter worldSelectionGetter)
|
|
{
|
|
var state = GetStateForWorld(worldSelectionGetter(), states, stateNames);
|
|
var header = new MultiColumnHeader(GetHeaderState());
|
|
return new SystemListView(state, header, systemSelectionCallback, worldSelectionGetter);
|
|
}
|
|
|
|
internal SystemListView(TreeViewState state, MultiColumnHeader header, SystemSelectionCallback systemSelectionCallback, WorldSelectionGetter worldSelectionGetter) : base(state, header)
|
|
{
|
|
this.getWorldSelection = worldSelectionGetter;
|
|
this.systemSelectionCallback = systemSelectionCallback;
|
|
columnIndexForTreeFoldouts = 1;
|
|
Reload();
|
|
}
|
|
|
|
private TreeViewItem CreateManagerItem(int id, ScriptBehaviourManager manager, World world)
|
|
{
|
|
managersById.Add(id, manager);
|
|
worldsById.Add(id, world);
|
|
var recorder = Recorder.Get($"{world.Name} {manager.GetType().FullName}");
|
|
recordersByManager.Add(manager, new AverageRecorder(recorder));
|
|
recorder.enabled = true;
|
|
var name = getWorldSelection() == null ? $"{manager.GetType().Name} ({world.Name})" : manager.GetType().Name;
|
|
return new TreeViewItem { id = id, displayName = name };
|
|
}
|
|
|
|
private PlayerLoopSystem lastPlayerLoop;
|
|
|
|
protected override TreeViewItem BuildRoot()
|
|
{
|
|
managersById.Clear();
|
|
worldsById.Clear();
|
|
recordersByManager.Clear();
|
|
|
|
var currentID = 0;
|
|
var root = new TreeViewItem { id = currentID++, depth = -1, displayName = "Root" };
|
|
|
|
lastPlayerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
|
|
|
|
var expandedIds = new List<int>();
|
|
|
|
if (ScriptBehaviourUpdateOrder.CurrentPlayerLoop.subSystemList == null)
|
|
{
|
|
root.children = new List<TreeViewItem>(0);
|
|
}
|
|
else
|
|
{
|
|
foreach (var group in ScriptBehaviourUpdateOrder.CurrentPlayerLoop.subSystemList)
|
|
{
|
|
var groupItem = new TreeViewItem { id = currentID++, depth = 0, displayName = group.type.Name};
|
|
var hasManagerChildren = false;
|
|
foreach (var child in group.subSystemList)
|
|
{
|
|
var executionDelegate = child.updateDelegate;
|
|
if (executionDelegate != null && executionDelegate.Target is ScriptBehaviourUpdateOrder.DummyDelagateWrapper dummy)
|
|
{
|
|
var system = dummy.Manager;
|
|
if (getWorldSelection() == null)
|
|
{
|
|
foreach (var world in World.AllWorlds)
|
|
{
|
|
if (world.BehaviourManagers.Contains(system))
|
|
{
|
|
groupItem.AddChild(CreateManagerItem(currentID++, system, world));
|
|
hasManagerChildren = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (getWorldSelection().BehaviourManagers.Contains(system))
|
|
{
|
|
groupItem.AddChild(CreateManagerItem(currentID++, system, getWorldSelection()));
|
|
hasManagerChildren = true;
|
|
}
|
|
}
|
|
}
|
|
else if (getWorldSelection() == null)
|
|
{
|
|
groupItem.AddChild(new TreeViewItem(currentID++, 1, child.type.Name));
|
|
}
|
|
}
|
|
|
|
if (groupItem.hasChildren)
|
|
{
|
|
root.AddChild(groupItem);
|
|
if (hasManagerChildren)
|
|
expandedIds.Add(groupItem.id);
|
|
}
|
|
else
|
|
{
|
|
--currentID;
|
|
}
|
|
}
|
|
|
|
if (!root.hasChildren)
|
|
{
|
|
root.children = new List<TreeViewItem>(0);
|
|
}
|
|
}
|
|
|
|
state.expandedIDs = expandedIds;
|
|
|
|
SetupDepthsFromParentsAndChildren(root);
|
|
return root;
|
|
}
|
|
|
|
protected override void RowGUI (RowGUIArgs args)
|
|
{
|
|
if (args.item.depth == -1)
|
|
return;
|
|
var item = args.item;
|
|
|
|
var enabled = GUI.enabled;
|
|
|
|
if (managersById.ContainsKey(item.id))
|
|
{
|
|
var manager = managersById[item.id];
|
|
var componentSystemBase = manager as ComponentSystemBase;
|
|
if (componentSystemBase != null)
|
|
{
|
|
var toggleRect = args.GetCellRect(0);
|
|
toggleRect.xMin = toggleRect.xMin + 4f;
|
|
componentSystemBase.Enabled = GUI.Toggle(toggleRect, componentSystemBase.Enabled, GUIContent.none);
|
|
}
|
|
|
|
if (componentSystemBase != null)
|
|
{
|
|
var timingRect = args.GetCellRect(2);
|
|
if (componentSystemBase.ShouldRunSystem())
|
|
{
|
|
var recorder = recordersByManager[manager];
|
|
GUI.Label(timingRect, recorder.ReadMilliseconds().ToString("f2"), RightAlignedLabel);
|
|
}
|
|
else
|
|
{
|
|
GUI.enabled = false;
|
|
GUI.Label(timingRect, "not run", RightAlignedLabel);
|
|
GUI.enabled = enabled;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GUI.enabled = false;
|
|
}
|
|
|
|
var indent = GetContentIndent(item);
|
|
var nameRect = args.GetCellRect(1);
|
|
nameRect.xMin = nameRect.xMin + indent;
|
|
GUI.Label(nameRect, item.displayName);
|
|
GUI.enabled = enabled;
|
|
}
|
|
|
|
protected override void AfterRowsGUI()
|
|
{
|
|
base.AfterRowsGUI();
|
|
if (Event.current.type == EventType.MouseDown)
|
|
{
|
|
SetSelection(new List<int>());
|
|
}
|
|
}
|
|
|
|
protected override void SelectionChanged(IList<int> selectedIds)
|
|
{
|
|
if (selectedIds.Count > 0 && managersById.ContainsKey(selectedIds[0]))
|
|
{
|
|
systemSelectionCallback(managersById[selectedIds[0]], worldsById[selectedIds[0]]);
|
|
}
|
|
else
|
|
{
|
|
systemSelectionCallback(null, null);
|
|
SetSelection(new List<int>());
|
|
}
|
|
}
|
|
|
|
protected override bool CanMultiSelect(TreeViewItem item)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public void TouchSelection()
|
|
{
|
|
SetSelection(GetSelection(), TreeViewSelectionOptions.FireSelectionChanged);
|
|
}
|
|
|
|
private bool PlayerLoopsMatch(PlayerLoopSystem a, PlayerLoopSystem b)
|
|
{
|
|
if (a.type != b.type)
|
|
return false;
|
|
if (a.subSystemList == b.subSystemList)
|
|
return true;
|
|
if (a.subSystemList == null || b.subSystemList == null)
|
|
return false;
|
|
if (a.subSystemList.Length != b.subSystemList.Length)
|
|
return false;
|
|
for (var i = 0; i < a.subSystemList.Length; ++i)
|
|
{
|
|
if (!PlayerLoopsMatch(a.subSystemList[i], b.subSystemList[i]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void UpdateIfNecessary()
|
|
{
|
|
if (!PlayerLoopsMatch(lastPlayerLoop, ScriptBehaviourUpdateOrder.CurrentPlayerLoop))
|
|
Reload();
|
|
}
|
|
|
|
private int lastTimedFrame;
|
|
|
|
public void UpdateTimings()
|
|
{
|
|
if (Time.frameCount == lastTimedFrame)
|
|
return;
|
|
|
|
foreach (var recorder in recordersByManager.Values)
|
|
{
|
|
recorder.Update();
|
|
}
|
|
|
|
lastTimedFrame = Time.frameCount;
|
|
}
|
|
|
|
public void SetSystemSelection(ScriptBehaviourManager manager, World world)
|
|
{
|
|
foreach (var pair in managersById)
|
|
{
|
|
if (pair.Value == manager)
|
|
{
|
|
SetSelection(new List<int> {pair.Key});
|
|
return;
|
|
}
|
|
}
|
|
SetSelection(new List<int>());
|
|
}
|
|
}
|
|
}
|