Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions com.unity.multiplayer.mlapi/Editor/MLAPIProfilerModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using MLAPI.Profiling;
using Unity.Profiling;
using UnityEditor;
using UnityEngine;

namespace MLAPI
{
[InitializeOnLoad]
public static class MLAPIProfilerModule
{
#if UNITY_2020_2_OR_NEWER && ENABLE_PROFILER
const string RPCModuleName = "MLAPI RPCs";
const string OperationModuleName = "MLAPI Operations";
const string MessageModuleName = "MLAPI Messages";

/// <summary>
/// This needs to be in synced with the internal dynamic module structure to provide our own counters
/// </summary>
[Serializable]
public class MLAPIProfilerCounter
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, these can be private I think. In fact I think the whole class should probably be internal since it doesn't expose any public APIs

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

{
public string m_Name;
public string m_Category;
}

/// <summary>
/// This needs to be in synced with the internal dynamic module structure to provide our own counters
/// </summary>
[Serializable]
public class MLAPIProfilerModuleData
{
public List<MLAPIProfilerCounter> m_ChartCounters = new List<MLAPIProfilerCounter>();
public List<MLAPIProfilerCounter> m_DetailCounters = new List<MLAPIProfilerCounter>();
public string m_Name;
}

[Serializable]
public class MLAPIModules
{
public List<MLAPIProfilerModuleData> m_Modules;
}

private static List<MLAPIProfilerCounter> CreateRPCCounters() => new List<MLAPIProfilerCounter>()
{
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberOfRPCsSent, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberOfRPCsReceived, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberOfRPCBatchesSent, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberOfRPCBatchesReceived, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberOfRPCQueueProcessed, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberOfRPCsInQueueSize, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberOfRPCsOutQueueSize, m_Category = ProfilerCategory.Network.Name },
};

private static List<MLAPIProfilerCounter> CreateOperationsCounters() => new List<MLAPIProfilerCounter>()
{
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberOfConnections, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.ReceiveTickRate, m_Category = ProfilerCategory.Network.Name },
};

private static List<MLAPIProfilerCounter> CreateMessagesCounters() => new List<MLAPIProfilerCounter>()
{
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberOfNamedMessages, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberOfUnnamedMessages, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberBytesSent, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberBytesReceived, m_Category = ProfilerCategory.Network.Name },
new MLAPIProfilerCounter() { m_Name = ProfilerConstants.NumberNetworkVarsReceived, m_Category = ProfilerCategory.Network.Name },
};

private delegate List<MLAPIProfilerCounter> CounterListFactoryDelegate();

private static bool CreateMLAPIDynamicModule(ref MLAPIModules mlapiModules, string moduleName, CounterListFactoryDelegate counterListFactoryDelegate)
{
var module = mlapiModules.m_Modules.Find(x => x.m_Name == moduleName);
if (module == null)
{
var newModule = new MLAPIProfilerModuleData();
newModule.m_Name = moduleName;
newModule.m_ChartCounters = counterListFactoryDelegate();
newModule.m_DetailCounters = counterListFactoryDelegate();
mlapiModules.m_Modules.Add(newModule);
return true;
}

return false;
}
#endif

static MLAPIProfilerModule()
{
#if UNITY_2020_2_OR_NEWER && ENABLE_PROFILER
var dynamicModulesJson = EditorPrefs.GetString("ProfilerWindow.DynamicModules");

var dynamicModules = JsonUtility.FromJson<MLAPIModules>(dynamicModulesJson);

if (dynamicModules != null)
{
bool wasCreated = CreateMLAPIDynamicModule(ref dynamicModules, RPCModuleName, CreateRPCCounters);
wasCreated |= CreateMLAPIDynamicModule(ref dynamicModules, OperationModuleName, CreateOperationsCounters);
wasCreated |= CreateMLAPIDynamicModule(ref dynamicModules, MessageModuleName, CreateMessagesCounters);
if (wasCreated)
{
EditorPrefs.SetString("ProfilerWindow.DynamicModules", JsonUtility.ToJson(dynamicModules));
}
}
#endif
}


}
}
11 changes: 11 additions & 0 deletions com.unity.multiplayer.mlapi/Editor/MLAPIProfilerModule.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
#if UNITY_2020_2_OR_NEWER
using Unity.Profiling.LowLevel;
#endif

namespace MLAPI.Profiling
{
struct ProfilerCounterUtility
{
#if UNITY_2020_2_OR_NEWER && ENABLE_PROFILER
public static byte GetProfilerMarkerDataType<T>()
{
switch (Type.GetTypeCode(typeof(T))) {
case TypeCode.Int32:
return (byte)ProfilerMarkerDataType.Int32;
case TypeCode.UInt32:
return (byte)ProfilerMarkerDataType.UInt32;
case TypeCode.Int64:
return (byte)ProfilerMarkerDataType.Int64;
case TypeCode.UInt64:
return (byte)ProfilerMarkerDataType.UInt64;
case TypeCode.Single:
return (byte)ProfilerMarkerDataType.Float;
case TypeCode.Double:
return (byte)ProfilerMarkerDataType.Double;
case TypeCode.String:
return (byte)ProfilerMarkerDataType.String16;
default:
throw new ArgumentException($"Type {typeof(T)} is unsupported by ProfilerCounter.");
}
}
#endif
}
}


Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;
#if UNITY_2020_2_OR_NEWER
using Unity.Profiling;
using Unity.Profiling.LowLevel;
using Unity.Profiling.LowLevel.Unsafe;
#endif
using UnityEngine;

namespace MLAPI.Profiling
{
#if ENABLE_PROFILER
[StructLayout(LayoutKind.Sequential)]
#else
[StructLayout(LayoutKind.Sequential, Size = 0)]
#endif
public readonly struct ProfilerCounterValue<T> where T : unmanaged
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, should be internal? Especially since this is sort of a copy of the upcoming work in the core package, we don't want anyone to start leveraging this

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

{
#if UNITY_2020_2_OR_NEWER
#if ENABLE_PROFILER
[NativeDisableUnsafePtrRestriction]
[NonSerialized]
readonly unsafe T* m_Value;
#endif

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ProfilerCounterValue(ProfilerCategory category, string name, ProfilerMarkerDataUnit dataUnit, ProfilerCounterOptions counterOptions)
{
#if ENABLE_PROFILER
byte dataType = ProfilerCounterUtility.GetProfilerMarkerDataType<T>();
unsafe {
m_Value = (T*)ProfilerUnsafeUtility.CreateCounterValue(out var counterPtr, name, category, MarkerFlags.Default, dataType, (byte)dataUnit, UnsafeUtility.SizeOf<T>(), counterOptions);
}
#endif
}

public T Value
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get {
#if ENABLE_PROFILER
unsafe {
return *m_Value;
}
#else
return default;
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
set {
#if ENABLE_PROFILER
unsafe {
*m_Value = value;
}
#endif
}
}
#endif
}
}


Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System;
#if UNITY_2020_2_OR_NEWER
using Unity.Profiling;
#endif
using UnityEngine;

namespace MLAPI.Profiling
{

public static class ProfilerCountersInfo
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, should be internal

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

{
#if UNITY_2020_2_OR_NEWER && ENABLE_PROFILER
// Operations
public static ProfilerCounterValue<int> ConnectionsCounterValue =
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, all these counters can be private I think. With them being public we are exposing them to be modified, which isn't the intent

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Updated

new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberOfConnections,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

public static ProfilerCounterValue<int> TickRateCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.ReceiveTickRate,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

//Messages
public static ProfilerCounterValue<int> NamedMessagesCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberOfNamedMessages,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

public static ProfilerCounterValue<int> UnnamedMessagesCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberOfUnnamedMessages,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

public static ProfilerCounterValue<int> BytesSentCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberBytesSent,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

public static ProfilerCounterValue<int> BytesReceivedCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberBytesReceived,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

public static ProfilerCounterValue<int> NetworkVarsCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberNetworkVarsReceived,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

//RPCs
public static ProfilerCounterValue<int> RPCsSentCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberOfRPCsSent,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

public static ProfilerCounterValue<int> RPCsReceivedCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberOfRPCsReceived,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

public static ProfilerCounterValue<int> RPCBatchesSentCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberOfRPCBatchesSent,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

public static ProfilerCounterValue<int> RPCBatchesReceivedCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberOfRPCBatchesReceived,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

public static ProfilerCounterValue<int> RPCQueueProcessedCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberOfRPCQueueProcessed,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

public static ProfilerCounterValue<int> RPCsInQueueSizeCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberOfRPCsInQueueSize,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

public static ProfilerCounterValue<int> RPCsOutQueueSizeCounterValue =
new ProfilerCounterValue<int>(ProfilerCategory.Network, ProfilerConstants.NumberOfRPCsOutQueueSize,
ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame);

[RuntimeInitializeOnLoadMethod]
static void RegisterMLAPIPerformanceEvent()
{
NetworkingManager.OnPerformanceDataEvent += OnPerformanceTickData;
}

static void OnPerformanceTickData(PerformanceTickData tickData)
{
//Operations
ConnectionsCounterValue.Value += tickData.GetData(ProfilerConstants.NumberOfConnections);
TickRateCounterValue.Value += tickData.GetData(ProfilerConstants.ReceiveTickRate);

//Messages
NamedMessagesCounterValue.Value += tickData.GetData(ProfilerConstants.NumberOfNamedMessages);
UnnamedMessagesCounterValue.Value += tickData.GetData(ProfilerConstants.NumberOfUnnamedMessages);
BytesSentCounterValue.Value += tickData.GetData(ProfilerConstants.NumberBytesSent);
BytesReceivedCounterValue.Value += tickData.GetData(ProfilerConstants.NumberBytesReceived);
NetworkVarsCounterValue.Value += tickData.GetData(ProfilerConstants.NumberNetworkVarsReceived);

//RPCs
RPCsSentCounterValue.Value += tickData.GetData(ProfilerConstants.NumberOfRPCsSent);
RPCsReceivedCounterValue.Value += tickData.GetData(ProfilerConstants.NumberOfRPCsReceived);
RPCBatchesSentCounterValue.Value += tickData.GetData(ProfilerConstants.NumberOfRPCBatchesSent);
RPCBatchesReceivedCounterValue.Value += tickData.GetData(ProfilerConstants.NumberOfRPCBatchesReceived);
RPCBatchesReceivedCounterValue.Value += tickData.GetData(ProfilerConstants.NumberOfRPCBatchesReceived);
RPCQueueProcessedCounterValue.Value += tickData.GetData(ProfilerConstants.NumberOfRPCQueueProcessed);
RPCsInQueueSizeCounterValue.Value += tickData.GetData(ProfilerConstants.NumberOfRPCsInQueueSize);
RPCsOutQueueSizeCounterValue.Value += tickData.GetData(ProfilerConstants.NumberOfRPCsOutQueueSize);
}
#endif
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.