Skip to content

Commit 67fe54b

Browse files
authored
Merge pull request #59 from TwoTenPvP/network-profiler
Network profiler
2 parents a44f939 + 0ff8a15 commit 67fe54b

File tree

7 files changed

+437
-5
lines changed

7 files changed

+437
-5
lines changed

MLAPI-Editor/MLAPIProfiler.cs

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
using MLAPI.Data.NetworkProfiler;
2+
using MLAPI.NetworkingManagerComponents.Binary;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using UnityEditor;
6+
using UnityEngine;
7+
8+
public class MLAPIProfiler : EditorWindow
9+
{
10+
[MenuItem("Window/MLAPI Profiler")]
11+
public static void ShowWindow()
12+
{
13+
GetWindow<MLAPIProfiler>();
14+
}
15+
16+
GUIStyle wrapStyle
17+
{
18+
get
19+
{
20+
Color color = EditorStyles.label.normal.textColor;
21+
GUIStyle style = EditorStyles.centeredGreyMiniLabel;
22+
style.wordWrap = true;
23+
style.normal.textColor = color;
24+
return style;
25+
}
26+
}
27+
float updateDelay = 1f;
28+
int captureCount = 100;
29+
float showMax = 0;
30+
float showMin = 0;
31+
bool record = false;
32+
AnimationCurve curve = AnimationCurve.Constant(0, 1, 0);
33+
readonly List<ProfilerTick> currentTicks = new List<ProfilerTick>();
34+
float lastDrawn = 0;
35+
struct ProfilerContainer
36+
{
37+
public ProfilerTick[] ticks;
38+
}
39+
private void OnGUI()
40+
{
41+
if (!NetworkProfiler.IsRunning && record)
42+
{
43+
if (NetworkProfiler.Ticks != null && NetworkProfiler.Ticks.Count >= 2)
44+
curve = AnimationCurve.Constant(NetworkProfiler.Ticks.ElementAt(0).Frame, NetworkProfiler.Ticks.ElementAt(NetworkProfiler.Ticks.Count - 1).Frame, 0);
45+
else
46+
curve = AnimationCurve.Constant(0, 1, 0);
47+
48+
lastDrawn = 0;
49+
NetworkProfiler.Start(captureCount);
50+
}
51+
52+
//Draw top bar
53+
EditorGUILayout.BeginVertical();
54+
EditorGUILayout.BeginHorizontal();
55+
bool prevRec = record;
56+
record = EditorGUILayout.Toggle("Record", record);
57+
58+
if (GUILayout.Button("Import datafile"))
59+
{
60+
ProfilerTick[] ticks = BinarySerializer.Deserialize<ProfilerContainer>(File.ReadAllBytes(EditorUtility.OpenFilePanel("Choose a NetworkProfiler file", "", ""))).ticks;
61+
if (ticks.Length >= 2)
62+
curve = AnimationCurve.Constant(ticks[0].EventId, ticks[(ticks.Length - 1)].EventId, 0);
63+
else
64+
curve = AnimationCurve.Constant(0, 1, 0);
65+
currentTicks.Clear();
66+
for (int i = 0; i < ticks.Length; i++)
67+
{
68+
currentTicks.Add(ticks[i]);
69+
70+
uint bytes = 0;
71+
if (ticks[i].Events.Count > 0)
72+
{
73+
for (int j = 0; j < ticks[i].Events.Count; j++)
74+
{
75+
TickEvent tickEvent = ticks[i].Events[j];
76+
bytes += tickEvent.Bytes;
77+
}
78+
}
79+
curve.AddKey(ticks[i].EventId, bytes);
80+
}
81+
}
82+
83+
EditorGUILayout.EndHorizontal();
84+
float prevHis = captureCount;
85+
captureCount = EditorGUILayout.DelayedIntField("History count", captureCount);
86+
if (captureCount <= 0)
87+
captureCount = 1;
88+
updateDelay = EditorGUILayout.Slider("Refresh delay", updateDelay, 0.1f, 10f);
89+
EditorGUILayout.EndVertical();
90+
91+
if (prevRec != record)
92+
{
93+
if (prevRec)
94+
{
95+
NetworkProfiler.Stop();
96+
}
97+
else
98+
{
99+
if (NetworkProfiler.Ticks != null && NetworkProfiler.Ticks.Count >= 2)
100+
curve = AnimationCurve.Constant(NetworkProfiler.Ticks.ElementAt(0).EventId, NetworkProfiler.Ticks.ElementAt(NetworkProfiler.Ticks.Count - 1).EventId, 0);
101+
else
102+
curve = AnimationCurve.Constant(0, 1, 0);
103+
lastDrawn = 0;
104+
NetworkProfiler.Start(captureCount);
105+
}
106+
}
107+
if (prevHis != captureCount)
108+
{
109+
NetworkProfiler.Stop();
110+
111+
if (NetworkProfiler.Ticks != null && NetworkProfiler.Ticks.Count >= 2)
112+
curve = AnimationCurve.Constant(NetworkProfiler.Ticks.ElementAt(0).EventId, NetworkProfiler.Ticks.ElementAt(NetworkProfiler.Ticks.Count - 1).EventId, 0);
113+
else
114+
curve = AnimationCurve.Constant(0, 1, 0);
115+
116+
lastDrawn = 0;
117+
NetworkProfiler.Start(captureCount);
118+
}
119+
120+
//Cache
121+
if (NetworkProfiler.IsRunning)
122+
{
123+
if (Time.unscaledTime - lastDrawn > updateDelay)
124+
{
125+
lastDrawn = Time.unscaledTime;
126+
currentTicks.Clear();
127+
if (NetworkProfiler.Ticks.Count >= 2)
128+
curve = AnimationCurve.Constant(NetworkProfiler.Ticks.ElementAt(0).EventId, NetworkProfiler.Ticks.ElementAt(NetworkProfiler.Ticks.Count - 1).EventId, 0);
129+
130+
for (int i = 0; i < NetworkProfiler.Ticks.Count; i++)
131+
{
132+
ProfilerTick tick = NetworkProfiler.Ticks.ElementAt(i);
133+
currentTicks.Add(tick);
134+
135+
uint bytes = 0;
136+
if (tick.Events.Count > 0)
137+
{
138+
for (int j = 0; j < tick.Events.Count; j++)
139+
{
140+
TickEvent tickEvent = tick.Events[j];
141+
bytes += tickEvent.Bytes;
142+
}
143+
}
144+
curve.AddKey(tick.EventId, bytes);
145+
}
146+
}
147+
}
148+
149+
150+
//Draw Animation curve and slider
151+
curve = EditorGUILayout.CurveField(curve);
152+
EditorGUILayout.MinMaxSlider(ref showMin, ref showMax, 0, currentTicks.Count);
153+
//Verify slider values
154+
if (showMin < 0)
155+
showMin = 0;
156+
if (showMax > currentTicks.Count)
157+
showMax = currentTicks.Count;
158+
if (showMin <= 0 && showMax <= 0)
159+
{
160+
showMin = 0;
161+
showMax = currentTicks.Count;
162+
}
163+
164+
//Draw main board
165+
int nonEmptyTicks = 0;
166+
int largestTickCount = 0;
167+
int totalTicks = ((int)showMax - (int)showMin);
168+
169+
for (int i = (int)showMin; i < (int)showMax; i++)
170+
{
171+
if (currentTicks[i].Events.Count > 0) nonEmptyTicks++; //Count non empty ticks
172+
if (currentTicks[i].Events.Count > largestTickCount) largestTickCount = currentTicks[i].Events.Count; //Get how many events the tick with most events has
173+
}
174+
int emptyTicks = totalTicks - nonEmptyTicks;
175+
176+
float equalWidth = position.width / totalTicks;
177+
float propWidth = equalWidth * 0.3f;
178+
float widthPerTick = ((position.width - emptyTicks * propWidth) / nonEmptyTicks);
179+
180+
float currentX = 0;
181+
int emptyStreak = 0;
182+
for (int i = (int)showMin; i < (int)showMax; i++)
183+
{
184+
ProfilerTick tick = currentTicks[i];
185+
if (tick.Events.Count == 0 && i != totalTicks - 1)
186+
{
187+
emptyStreak++;
188+
continue;
189+
}
190+
else if (emptyStreak > 0 || i == totalTicks - 1)
191+
{
192+
Rect dataRect = new Rect(currentX, 100, propWidth * emptyStreak, position.height - 100);
193+
currentX += propWidth * emptyStreak;
194+
EditorGUI.LabelField(new Rect(dataRect.x, dataRect.y, dataRect.width, dataRect.height), emptyStreak.ToString(), wrapStyle);
195+
emptyStreak = 0;
196+
}
197+
198+
if (tick.Events.Count > 0)
199+
{
200+
float heightPerEvent = ((position.height - 100f) - (5f * largestTickCount)) / largestTickCount;
201+
float heightPerTotalBackground = ((position.height - 100f) - (5f * largestTickCount)) / tick.Events.Count;
202+
203+
float currentY = 100;
204+
for (int j = 0; j < tick.Events.Count; j++)
205+
{
206+
TickEvent tickEvent = tick.Events[j];
207+
Rect dataRect = new Rect(currentX, currentY, widthPerTick, heightPerEvent);
208+
if (j == tick.Events.Count - 1)
209+
dataRect.height -= 45f;
210+
EditorGUI.DrawRect(dataRect, TickTypeToColor(tickEvent.EventType));
211+
float heightPerField = heightPerEvent / 12f;
212+
EditorGUI.LabelField(new Rect(dataRect.x, dataRect.y + heightPerField * -3f, dataRect.width, dataRect.height), "EventType: " + tickEvent.EventType.ToString(), wrapStyle);
213+
EditorGUI.LabelField(new Rect(dataRect.x, dataRect.y + heightPerField * -1f, dataRect.width, dataRect.height), "Size: " + tickEvent.Bytes + "B", wrapStyle);
214+
EditorGUI.LabelField(new Rect(dataRect.x, dataRect.y + heightPerField * 1f, dataRect.width, dataRect.height), "Channel: " + tickEvent.ChannelName, wrapStyle);
215+
EditorGUI.LabelField(new Rect(dataRect.x, dataRect.y + heightPerField * 3f, dataRect.width, dataRect.height), "MessageType: " + tickEvent.MessageType, wrapStyle);
216+
217+
currentY += heightPerEvent + 5f;
218+
}
219+
}
220+
EditorGUI.DrawRect(new Rect(currentX, position.height - 40, widthPerTick, 40), TickTypeToColor(tick.Type));
221+
EditorGUI.LabelField(new Rect(currentX, position.height - 40, widthPerTick, 20), "TickType: " + tick.Type.ToString(), wrapStyle);
222+
EditorGUI.LabelField(new Rect(currentX, position.height - 20, widthPerTick, 20), "Frame: " + tick.Frame.ToString(), wrapStyle);
223+
currentX += widthPerTick;
224+
}
225+
226+
Repaint();
227+
}
228+
229+
private static Color TickTypeToColor(TickType type)
230+
{
231+
switch (type)
232+
{
233+
case TickType.Event:
234+
return new Color(0.58f, 0f, 0.56f, 0.37f);
235+
case TickType.Receive:
236+
return new Color(0f, 0.85f, 0.85f, 0.28f);
237+
case TickType.Send:
238+
return new Color(0, 0.55f, 1f, 0.06f);
239+
}
240+
return EditorGUIUtility.isProSkin ? new Color32(70, 70, 70, 255) : new Color32(200, 200, 200, 255);
241+
}
242+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using MLAPI.NetworkingManagerComponents.Core;
2+
using UnityEngine;
3+
4+
namespace MLAPI.Data.NetworkProfiler
5+
{
6+
public static class NetworkProfiler
7+
{
8+
public static FixedQueue<ProfilerTick> Ticks = null;
9+
private static int tickHistory = 1024;
10+
private static int EventIdCounter = 0;
11+
private static bool isRunning = false;
12+
public static bool IsRunning
13+
{
14+
get
15+
{
16+
return isRunning;
17+
}
18+
}
19+
private static ProfilerTick CurrentTick;
20+
21+
public static void Start(int historyLength)
22+
{
23+
if (isRunning)
24+
return;
25+
EventIdCounter = 0;
26+
Ticks = new FixedQueue<ProfilerTick>(historyLength);
27+
tickHistory = historyLength;
28+
CurrentTick = null;
29+
isRunning = true;
30+
}
31+
32+
public static ProfilerTick[] Stop()
33+
{
34+
if (!isRunning)
35+
return new ProfilerTick[0];
36+
ProfilerTick[] ticks = new ProfilerTick[Ticks.Count];
37+
for (int i = 0; i < Ticks.Count; i++)
38+
ticks[i] = Ticks.ElementAt(i);
39+
40+
Ticks = null; //leave to GC
41+
CurrentTick = null; //leave to GC
42+
isRunning = false;
43+
return ticks;
44+
}
45+
46+
internal static void StartTick(TickType type)
47+
{
48+
if (!isRunning)
49+
return;
50+
if (Ticks.Count == tickHistory)
51+
Ticks.Dequeue();
52+
53+
ProfilerTick tick = new ProfilerTick()
54+
{
55+
Type = type,
56+
Frame = Time.frameCount,
57+
EventId = EventIdCounter
58+
};
59+
EventIdCounter++;
60+
Ticks.Enqueue(tick);
61+
CurrentTick = tick;
62+
}
63+
64+
internal static void EndTick()
65+
{
66+
if (!isRunning)
67+
return;
68+
if (CurrentTick == null)
69+
return;
70+
CurrentTick = null;
71+
}
72+
73+
internal static void StartEvent(TickType eventType, uint bytes, int channelId, ushort messageId)
74+
{
75+
if (!isRunning)
76+
return;
77+
if (CurrentTick == null)
78+
return;
79+
string channelName = MessageManager.reverseChannels.ContainsKey(channelId) ? MessageManager.reverseChannels[channelId] : "INVALID_CHANNEL";
80+
string messageName = MessageManager.reverseMessageTypes.ContainsKey(messageId) ? MessageManager.reverseMessageTypes[messageId] : "INVALID_MESSAGE_TYPE";
81+
82+
CurrentTick.StartEvent(eventType, bytes, channelName, messageName);
83+
}
84+
85+
internal static void StartEvent(TickType eventType, uint bytes, string channelName, string messageName)
86+
{
87+
if (!isRunning)
88+
return;
89+
if (CurrentTick == null)
90+
return;
91+
92+
CurrentTick.StartEvent(eventType, bytes, channelName, messageName);
93+
}
94+
95+
internal static void EndEvent()
96+
{
97+
if (!isRunning)
98+
return;
99+
if (CurrentTick == null)
100+
return;
101+
CurrentTick.EndEvent();
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)