From 86ac756741875631e0ee01dccab7c52ea5895caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Thu, 24 Oct 2024 20:35:07 +0200 Subject: [PATCH 1/6] Added profiler core API dependency to asmdef --- .../com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef | 3 ++- Packages/manifest.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef b/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef index 6bc8cc3e6a..b2c9545c97 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef +++ b/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef @@ -2,7 +2,8 @@ "name": "Unity.InputSystem", "rootNamespace": "", "references": [ - "Unity.ugui" + "Unity.ugui", + "Unity.Profiling.Core" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Packages/manifest.json b/Packages/manifest.json index 4acf434955..ae02c47ed0 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -5,6 +5,7 @@ "com.unity.coding": "0.1.0-preview.24", "com.unity.ide.rider": "1.2.1", "com.unity.ide.visualstudio": "2.0.15", + "com.unity.profiling.core": "1.0.2", "com.unity.settings-manager": "1.0.3", "com.unity.test-framework": "1.4.5", "com.unity.test-framework.build": "0.0.1-preview.12", From 94afade71663372460a5dc78e0ae9192e0d90074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Thu, 24 Oct 2024 20:40:14 +0200 Subject: [PATCH 2/6] Removed temp mesh pro from manifest causing warnings --- Packages/manifest.json | 1 - 1 file changed, 1 deletion(-) diff --git a/Packages/manifest.json b/Packages/manifest.json index ae02c47ed0..1e558fda9a 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -11,7 +11,6 @@ "com.unity.test-framework.build": "0.0.1-preview.12", "com.unity.test-framework.performance": "3.0.3", "com.unity.test-framework.utp-reporter": "1.1.0-preview", - "com.unity.textmeshpro": "2.1.4", "com.unity.ugui": "1.0.0", "nuget.mono-cecil": "0.1.6-preview", "com.unity.modules.ai": "1.0.0", From ba2588d4627fca9c48dc484d1d308c4d887e529a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Thu, 24 Oct 2024 20:55:46 +0200 Subject: [PATCH 3/6] Added ProfilerModule and detailed view as well as custom profile counters. --- .../Editor/Internal/ProfilerModule.cs | 120 ++++++++++++++++++ .../Editor/Internal/ProfilerModule.cs.meta | 3 + .../InputSystem/Utilities/InputStatistics.cs | 54 ++++++++ .../Utilities/InputStatistics.cs.meta | 3 + 4 files changed, 180 insertions(+) create mode 100644 Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs create mode 100644 Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs.meta create mode 100644 Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs create mode 100644 Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs.meta diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs new file mode 100644 index 0000000000..a6c020a8b1 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs @@ -0,0 +1,120 @@ +#if UNITY_EDITOR // Input System currently do not have proper asmdef for editor code. + +using Unity.Profiling.Editor; +using UnityEditor; +using UnityEditorInternal; +using UnityEngine.UIElements; + +namespace UnityEngine.InputSystem.Editor +{ + /// + /// A profiler module that integrates Input System with the Profiler editor window. + /// + [ProfilerModuleMetadata("Input System")] + internal sealed class InputSystemProfilerModule : ProfilerModule + { + /// + /// A profiler module detail view that extends the Profiler window and shows details for the selected frame. + /// + private sealed class InputSystemDetailsViewController : ProfilerModuleViewController + { + public InputSystemDetailsViewController(ProfilerWindow profilerWindow) + : base(profilerWindow) + { } + + private Label m_EventCountLabel; + private Label m_EventSizeLabel; + private Label m_AverageLatencyLabel; + private Label m_MaxLatencyLabel; + private Label m_EventProcessingTimeLabel; + + private Label CreateLabel() + { + return new Label() { style = { paddingTop = 8, paddingLeft = 8 } }; + } + + protected override VisualElement CreateView() + { + var view = new VisualElement(); + + m_EventCountLabel = CreateLabel(); + m_EventSizeLabel = CreateLabel(); + m_AverageLatencyLabel = CreateLabel(); + m_MaxLatencyLabel = CreateLabel(); + m_EventProcessingTimeLabel = CreateLabel(); + + view.Add(m_EventCountLabel); + view.Add(m_EventSizeLabel); + view.Add(m_AverageLatencyLabel); + view.Add(m_MaxLatencyLabel); + view.Add(m_EventProcessingTimeLabel); + + // Populate the label with the current data for the selected frame. + ReloadData(); + + // Be notified when the selected frame index in the Profiler Window changes, so we can update the label. + ProfilerWindow.SelectedFrameIndexChanged += OnSelectedFrameIndexChanged; + + return view; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + // Unsubscribe from the Profiler window event that we previously subscribed to. + ProfilerWindow.SelectedFrameIndexChanged -= OnSelectedFrameIndexChanged; + } + + base.Dispose(disposing); + } + + void ReloadData() + { + var selectedFrameIndex = System.Convert.ToInt32(ProfilerWindow.selectedFrameIndex); + + var eventCount = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, + InputStatistics.Category.Name, InputStatistics.kEventCountName); + var eventSizeBytes = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, + InputStatistics.Category.Name, InputStatistics.kEventSizeName); + var averageLatency = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, + InputStatistics.Category.Name, InputStatistics.kAverageLatencyName); + var maxLatency = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, + InputStatistics.Category.Name, InputStatistics.kMaxLatencyName); + var eventProcessingTime = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, + InputStatistics.Category.Name, InputStatistics.kEventProcessingTimeName); + + m_EventCountLabel.text = $"{InputStatistics.kEventCountName}: {eventCount}"; + m_EventSizeLabel.text = $"{InputStatistics.kEventSizeName}: {eventSizeBytes}"; + m_AverageLatencyLabel.text = $"{InputStatistics.kAverageLatencyName}: {averageLatency}"; + m_MaxLatencyLabel.text = $"{InputStatistics.kMaxLatencyName}: {maxLatency}"; + m_EventProcessingTimeLabel.text = $"{InputStatistics.kEventProcessingTimeName}: {eventProcessingTime}"; + } + + void OnSelectedFrameIndexChanged(long selectedFrameIndex) + { + ReloadData(); + } + } + + private static readonly ProfilerCounterDescriptor[] Counters = new ProfilerCounterDescriptor[] + { + new (InputStatistics.kEventCountName, InputStatistics.Category), + new (InputStatistics.kEventSizeName, InputStatistics.Category), + new (InputStatistics.kAverageLatencyName, InputStatistics.Category), + new (InputStatistics.kMaxLatencyName, InputStatistics.Category), + new (InputStatistics.kEventProcessingTimeName, InputStatistics.Category), + }; + + public InputSystemProfilerModule() + : base(Counters) + { } + + public override ProfilerModuleViewController CreateDetailsViewController() + { + return new InputSystemDetailsViewController(ProfilerWindow); + } + } +} + +#endif // UNITY_EDITOR \ No newline at end of file diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs.meta new file mode 100644 index 0000000000..87f697b3f2 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 820e154f9c3b47a2b7a50c6680dd4aed +timeCreated: 1729795357 \ No newline at end of file diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs b/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs new file mode 100644 index 0000000000..424fea859e --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs @@ -0,0 +1,54 @@ +using Unity.Profiling; + +namespace UnityEngine.InputSystem +{ + /// + /// Input Statistics for Unity Profiler integration. + /// + internal class InputStatistics + { + public static readonly ProfilerCategory Category = ProfilerCategory.Input; + + public const string kEventCountName = "Input Event Count"; + public const string kEventSizeName = "Input Event Size"; + public const string kAverageLatencyName = "Average Latency"; + public const string kMaxLatencyName = "Max Latency"; + public const string kEventProcessingTimeName = "Event Processing Time"; + + /// + /// Counter reflecting the number of input events. + /// + public static readonly ProfilerCounterValue EventCount = new ProfilerCounterValue( + Category, kEventCountName, ProfilerMarkerDataUnit.Count, + ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); + + /// + /// Counter reflecting the accumulated input event size in bytes. + /// + public static readonly ProfilerCounterValue EventSize = new ProfilerCounterValue( + Category, kEventSizeName, ProfilerMarkerDataUnit.Bytes, + ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); + + /// + /// Counter value reflecting the average input latency. + /// + public static readonly ProfilerCounterValue AverageLatency = new ProfilerCounterValue( + Category, kAverageLatencyName, ProfilerMarkerDataUnit.TimeNanoseconds, + ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); + + /// + /// Counter value reflecting the maximum input latency. + /// + public static readonly ProfilerCounterValue MaxLatency = new ProfilerCounterValue( + Category, kMaxLatencyName, ProfilerMarkerDataUnit.TimeNanoseconds, + ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); + + /// + /// Counter value reflecting the accumulated event processing time (Update) during a rendering frame. + /// + public static readonly ProfilerCounterValue EventProcessingTime = new ProfilerCounterValue( + Category, kEventProcessingTimeName, ProfilerMarkerDataUnit.TimeNanoseconds, + ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush + ); + } +} \ No newline at end of file diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs.meta new file mode 100644 index 0000000000..d48433f422 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2656d8b72fb540fd836a3bc0c02f8e20 +timeCreated: 1729795584 \ No newline at end of file From 5429203a6391c11f5e34567274514fe66f8714d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Thu, 24 Oct 2024 23:39:09 +0200 Subject: [PATCH 4/6] Added basic profiling counters --- ...Module.cs => InputSystemProfilerModule.cs} | 30 ++++++++--------- ...meta => InputSystemProfilerModule.cs.meta} | 0 .../InputSystem/InputManager.cs | 33 +++++++++++++++---- .../InputSystem/Utilities/InputStatistics.cs | 29 ++++++++-------- 4 files changed, 55 insertions(+), 37 deletions(-) rename Packages/com.unity.inputsystem/InputSystem/Editor/Internal/{ProfilerModule.cs => InputSystemProfilerModule.cs} (83%) rename Packages/com.unity.inputsystem/InputSystem/Editor/Internal/{ProfilerModule.cs.meta => InputSystemProfilerModule.cs.meta} (100%) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs similarity index 83% rename from Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs rename to Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs index a6c020a8b1..b72da1db7f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs @@ -74,21 +74,21 @@ void ReloadData() var selectedFrameIndex = System.Convert.ToInt32(ProfilerWindow.selectedFrameIndex); var eventCount = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, - InputStatistics.Category.Name, InputStatistics.kEventCountName); + InputStatistics.Category.Name, InputStatistics.EventCountName); var eventSizeBytes = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, - InputStatistics.Category.Name, InputStatistics.kEventSizeName); + InputStatistics.Category.Name, InputStatistics.EventSizeName); var averageLatency = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, - InputStatistics.Category.Name, InputStatistics.kAverageLatencyName); + InputStatistics.Category.Name, InputStatistics.AverageLatencyName); var maxLatency = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, - InputStatistics.Category.Name, InputStatistics.kMaxLatencyName); + InputStatistics.Category.Name, InputStatistics.MaxLatencyName); var eventProcessingTime = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, - InputStatistics.Category.Name, InputStatistics.kEventProcessingTimeName); + InputStatistics.Category.Name, InputStatistics.EventProcessingTimeName); - m_EventCountLabel.text = $"{InputStatistics.kEventCountName}: {eventCount}"; - m_EventSizeLabel.text = $"{InputStatistics.kEventSizeName}: {eventSizeBytes}"; - m_AverageLatencyLabel.text = $"{InputStatistics.kAverageLatencyName}: {averageLatency}"; - m_MaxLatencyLabel.text = $"{InputStatistics.kMaxLatencyName}: {maxLatency}"; - m_EventProcessingTimeLabel.text = $"{InputStatistics.kEventProcessingTimeName}: {eventProcessingTime}"; + m_EventCountLabel.text = $"{InputStatistics.EventCountName}: {eventCount}"; + m_EventSizeLabel.text = $"{InputStatistics.EventSizeName}: {eventSizeBytes}"; + m_AverageLatencyLabel.text = $"{InputStatistics.AverageLatencyName}: {averageLatency}"; + m_MaxLatencyLabel.text = $"{InputStatistics.MaxLatencyName}: {maxLatency}"; + m_EventProcessingTimeLabel.text = $"{InputStatistics.EventProcessingTimeName}: {eventProcessingTime}"; } void OnSelectedFrameIndexChanged(long selectedFrameIndex) @@ -99,11 +99,11 @@ void OnSelectedFrameIndexChanged(long selectedFrameIndex) private static readonly ProfilerCounterDescriptor[] Counters = new ProfilerCounterDescriptor[] { - new (InputStatistics.kEventCountName, InputStatistics.Category), - new (InputStatistics.kEventSizeName, InputStatistics.Category), - new (InputStatistics.kAverageLatencyName, InputStatistics.Category), - new (InputStatistics.kMaxLatencyName, InputStatistics.Category), - new (InputStatistics.kEventProcessingTimeName, InputStatistics.Category), + new (InputStatistics.EventCountName, InputStatistics.Category), + new (InputStatistics.EventSizeName, InputStatistics.Category), + new (InputStatistics.AverageLatencyName, InputStatistics.Category), + new (InputStatistics.MaxLatencyName, InputStatistics.Category), + new (InputStatistics.EventProcessingTimeName, InputStatistics.Category), }; public InputSystemProfilerModule() diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs.meta similarity index 100% rename from Packages/com.unity.inputsystem/InputSystem/Editor/Internal/ProfilerModule.cs.meta rename to Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs.meta diff --git a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs index 10e3c17fb8..b95346d3f1 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs @@ -3204,7 +3204,10 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev } var processingStartTime = Stopwatch.GetTimestamp(); + var totalEventCount = 0; + var totalEventSizeBytes = 0; var totalEventLag = 0.0; + var maxEventLag = 0.0; #if UNITY_EDITOR var isPlaying = gameIsPlaying; @@ -3465,9 +3468,14 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev // Update metrics. if (currentEventTimeInternal <= currentTime) - totalEventLag += currentTime - currentEventTimeInternal; - ++m_Metrics.totalEventCount; - m_Metrics.totalEventBytes += (int)currentEventReadPtr->sizeInBytes; + { + var lag = currentTime - currentEventTimeInternal; + totalEventLag += lag; + if (lag > maxEventLag) + maxEventLag = lag; + } + ++totalEventCount; + totalEventSizeBytes += (int)currentEventReadPtr->sizeInBytes; // Process. switch (currentEventType) @@ -3621,12 +3629,23 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev break; } - m_Metrics.totalEventProcessingTime += + ResetCurrentProcessedEventBytesForDevices(); + + // Update metrics (exposed via analytics and debugger) + var eventProcessingTime = ((double)(Stopwatch.GetTimestamp() - processingStartTime)) / Stopwatch.Frequency; + m_Metrics.totalEventCount += totalEventCount; + m_Metrics.totalEventBytes += totalEventSizeBytes; + m_Metrics.totalEventProcessingTime += eventProcessingTime; m_Metrics.totalEventLagTime += totalEventLag; - - ResetCurrentProcessedEventBytesForDevices(); - + + // Profiler counters + InputStatistics.EventCount.Value += totalEventCount; + InputStatistics.EventSize.Value += totalEventSizeBytes; + InputStatistics.AverageLatency.Value += ((totalEventLag / totalEventCount) * 1e9); + InputStatistics.MaxLatency.Value += (maxEventLag * 1e9); + InputStatistics.EventProcessingTime.Value += eventProcessingTime * 1e9; // TODO Possible to replace Stopwatch with marker somehow? + m_InputEventStream.Close(ref eventBuffer); } catch (Exception) diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs b/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs index 424fea859e..b338f6f639 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs @@ -5,50 +5,49 @@ namespace UnityEngine.InputSystem /// /// Input Statistics for Unity Profiler integration. /// - internal class InputStatistics + internal static class InputStatistics { public static readonly ProfilerCategory Category = ProfilerCategory.Input; - public const string kEventCountName = "Input Event Count"; - public const string kEventSizeName = "Input Event Size"; - public const string kAverageLatencyName = "Average Latency"; - public const string kMaxLatencyName = "Max Latency"; - public const string kEventProcessingTimeName = "Event Processing Time"; + public const string EventCountName = "Input Event Count"; + public const string EventSizeName = "Input Event Size"; + public const string AverageLatencyName = "Average Latency"; + public const string MaxLatencyName = "Max Latency"; + public const string EventProcessingTimeName = "Event Processing Time"; /// /// Counter reflecting the number of input events. /// public static readonly ProfilerCounterValue EventCount = new ProfilerCounterValue( - Category, kEventCountName, ProfilerMarkerDataUnit.Count, + Category, EventCountName, ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); /// /// Counter reflecting the accumulated input event size in bytes. /// public static readonly ProfilerCounterValue EventSize = new ProfilerCounterValue( - Category, kEventSizeName, ProfilerMarkerDataUnit.Bytes, + Category, EventSizeName, ProfilerMarkerDataUnit.Bytes, ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); /// /// Counter value reflecting the average input latency. /// - public static readonly ProfilerCounterValue AverageLatency = new ProfilerCounterValue( - Category, kAverageLatencyName, ProfilerMarkerDataUnit.TimeNanoseconds, + public static readonly ProfilerCounterValue AverageLatency = new ProfilerCounterValue( + Category, AverageLatencyName, ProfilerMarkerDataUnit.TimeNanoseconds, ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); /// /// Counter value reflecting the maximum input latency. /// - public static readonly ProfilerCounterValue MaxLatency = new ProfilerCounterValue( - Category, kMaxLatencyName, ProfilerMarkerDataUnit.TimeNanoseconds, + public static readonly ProfilerCounterValue MaxLatency = new ProfilerCounterValue( + Category, MaxLatencyName, ProfilerMarkerDataUnit.TimeNanoseconds, ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); /// /// Counter value reflecting the accumulated event processing time (Update) during a rendering frame. /// public static readonly ProfilerCounterValue EventProcessingTime = new ProfilerCounterValue( - Category, kEventProcessingTimeName, ProfilerMarkerDataUnit.TimeNanoseconds, - ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush - ); + Category, EventProcessingTimeName, ProfilerMarkerDataUnit.TimeNanoseconds, + ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); } } \ No newline at end of file From 6f4c89425e3e825b08558ffd70831ebe62b899f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Fri, 25 Oct 2024 13:35:44 +0200 Subject: [PATCH 5/6] Added profiling prototype basics --- .../Internal/InputSystemProfilerModule.cs | 74 +++++++++++------ .../InputSystem/InputManager.cs | 42 +++++++--- .../InputSystem/Utilities/InputStatistics.cs | 80 ++++++++++++++++--- 3 files changed, 155 insertions(+), 41 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs index b72da1db7f..ca771a159e 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs @@ -10,46 +10,58 @@ namespace UnityEngine.InputSystem.Editor /// /// A profiler module that integrates Input System with the Profiler editor window. /// - [ProfilerModuleMetadata("Input System")] + [ProfilerModuleMetadata("Input System")] internal sealed class InputSystemProfilerModule : ProfilerModule { /// /// A profiler module detail view that extends the Profiler window and shows details for the selected frame. /// private sealed class InputSystemDetailsViewController : ProfilerModuleViewController - { - public InputSystemDetailsViewController(ProfilerWindow profilerWindow) - : base(profilerWindow) - { } + { + public InputSystemDetailsViewController(ProfilerWindow profilerWindow) + : base(profilerWindow) + {} + private Label m_UpdateCountLabel; private Label m_EventCountLabel; private Label m_EventSizeLabel; private Label m_AverageLatencyLabel; private Label m_MaxLatencyLabel; private Label m_EventProcessingTimeLabel; + private Label m_DeviceCountLabel; + private Label m_ControlCountLabel; + private Label m_StateBufferSizeLabel; private Label CreateLabel() { return new Label() { style = { paddingTop = 8, paddingLeft = 8 } }; } - + protected override VisualElement CreateView() { var view = new VisualElement(); + m_UpdateCountLabel = CreateLabel(); m_EventCountLabel = CreateLabel(); m_EventSizeLabel = CreateLabel(); m_AverageLatencyLabel = CreateLabel(); m_MaxLatencyLabel = CreateLabel(); m_EventProcessingTimeLabel = CreateLabel(); - + m_DeviceCountLabel = CreateLabel(); + m_ControlCountLabel = CreateLabel(); + m_StateBufferSizeLabel = CreateLabel(); + + view.Add(m_UpdateCountLabel); view.Add(m_EventCountLabel); view.Add(m_EventSizeLabel); view.Add(m_AverageLatencyLabel); view.Add(m_MaxLatencyLabel); view.Add(m_EventProcessingTimeLabel); + view.Add(m_DeviceCountLabel); + view.Add(m_ControlCountLabel); + view.Add(m_StateBufferSizeLabel); - // Populate the label with the current data for the selected frame. + // Populate the label with the current data for the selected frame. ReloadData(); // Be notified when the selected frame index in the Profiler Window changes, so we can update the label. @@ -57,7 +69,7 @@ protected override VisualElement CreateView() return view; } - + protected override void Dispose(bool disposing) { if (disposing) @@ -65,15 +77,17 @@ protected override void Dispose(bool disposing) // Unsubscribe from the Profiler window event that we previously subscribed to. ProfilerWindow.SelectedFrameIndexChanged -= OnSelectedFrameIndexChanged; } - + base.Dispose(disposing); } void ReloadData() { var selectedFrameIndex = System.Convert.ToInt32(ProfilerWindow.selectedFrameIndex); - - var eventCount = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, + + var updateCount = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, + InputStatistics.Category.Name, InputStatistics.UpdateCountName); + var eventCount = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, InputStatistics.Category.Name, InputStatistics.EventCountName); var eventSizeBytes = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, InputStatistics.Category.Name, InputStatistics.EventSizeName); @@ -83,33 +97,47 @@ void ReloadData() InputStatistics.Category.Name, InputStatistics.MaxLatencyName); var eventProcessingTime = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, InputStatistics.Category.Name, InputStatistics.EventProcessingTimeName); + var stateBufferSizeBytes = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, + InputStatistics.Category.Name, InputStatistics.StateBufferSizeBytesName); + var deviceCount = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, + InputStatistics.Category.Name, InputStatistics.DeviceCountName); + var controlCount = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex, + InputStatistics.Category.Name, InputStatistics.ControlCountName); + m_UpdateCountLabel.text = $"{InputStatistics.UpdateCountName}: {updateCount}"; m_EventCountLabel.text = $"{InputStatistics.EventCountName}: {eventCount}"; m_EventSizeLabel.text = $"{InputStatistics.EventSizeName}: {eventSizeBytes}"; m_AverageLatencyLabel.text = $"{InputStatistics.AverageLatencyName}: {averageLatency}"; m_MaxLatencyLabel.text = $"{InputStatistics.MaxLatencyName}: {maxLatency}"; m_EventProcessingTimeLabel.text = $"{InputStatistics.EventProcessingTimeName}: {eventProcessingTime}"; + m_StateBufferSizeLabel.text = $"{InputStatistics.StateBufferSizeBytesName}: {stateBufferSizeBytes}"; + m_DeviceCountLabel.text = $"{InputStatistics.DeviceCountName}: {deviceCount}"; + m_ControlCountLabel.text = $"{InputStatistics.ControlCountName}: {controlCount}"; } - + void OnSelectedFrameIndexChanged(long selectedFrameIndex) { ReloadData(); } } - + private static readonly ProfilerCounterDescriptor[] Counters = new ProfilerCounterDescriptor[] { - new (InputStatistics.EventCountName, InputStatistics.Category), - new (InputStatistics.EventSizeName, InputStatistics.Category), - new (InputStatistics.AverageLatencyName, InputStatistics.Category), - new (InputStatistics.MaxLatencyName, InputStatistics.Category), - new (InputStatistics.EventProcessingTimeName, InputStatistics.Category), + new(InputStatistics.UpdateCountName, InputStatistics.Category), + new(InputStatistics.EventCountName, InputStatistics.Category), + new(InputStatistics.EventSizeName, InputStatistics.Category), + new(InputStatistics.StateBufferSizeBytesName, InputStatistics.Category), + new(InputStatistics.AverageLatencyName, InputStatistics.Category), + new(InputStatistics.MaxLatencyName, InputStatistics.Category), + new(InputStatistics.EventProcessingTimeName, InputStatistics.Category), + new(InputStatistics.DeviceCountName, InputStatistics.Category), + new(InputStatistics.ControlCountName, InputStatistics.Category), }; - + public InputSystemProfilerModule() : base(Counters) - { } - + {} + public override ProfilerModuleViewController CreateDetailsViewController() { return new InputSystemDetailsViewController(ProfilerWindow); @@ -117,4 +145,4 @@ public override ProfilerModuleViewController CreateDetailsViewController() } } -#endif // UNITY_EDITOR \ No newline at end of file +#endif // UNITY_EDITOR diff --git a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs index b95346d3f1..5b3504db2f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs @@ -80,6 +80,13 @@ internal partial class InputManager static readonly ProfilerMarker k_InputOnDeviceChangeMarker = new ProfilerMarker("InpustSystem.onDeviceChange"); static readonly ProfilerMarker k_InputOnActionsChangeMarker = new ProfilerMarker("InpustSystem.onActionsChange"); + private int CountControls() + { + var count = m_DevicesCount; + for (var i = 0; i < m_DevicesCount; ++i) + count += m_Devices[i].allControls.Count; + return count; + } public InputMetrics metrics { @@ -89,11 +96,7 @@ public InputMetrics metrics result.currentNumDevices = m_DevicesCount; result.currentStateSizeInBytes = (int)m_StateBuffers.totalSize; - - // Count controls. - result.currentControlCount = m_DevicesCount; - for (var i = 0; i < m_DevicesCount; ++i) - result.currentControlCount += m_Devices[i].allControls.Count; + result.currentControlCount = CountControls(); // Count layouts. result.currentLayoutCount = m_Layouts.layoutTypes.Count; @@ -3055,6 +3058,10 @@ internal bool ShouldRunUpdate(InputUpdateType updateType) return (updateType & mask) != 0; } + struct UpdateMetrics + { + } + /// /// Process input events. /// @@ -3072,8 +3079,25 @@ internal bool ShouldRunUpdate(InputUpdateType updateType) /// which buffers we activate in the update and write the event data into. /// /// Thrown if OnUpdate is called recursively. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1809:AvoidExcessiveLocals", Justification = "TODO: Refactor later.")] private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer eventBuffer) + { + try + { + DoUpdate(updateType, ref eventBuffer); + } + finally + { + // According to documentation, profile counter calls should be stripped out automatically in + // non-development builds. + InputStatistics.DeviceCount.Sample(m_DevicesCount); + InputStatistics.StateBufferSizeBytes.Sample((int)m_StateBuffers.totalSize); + InputStatistics.ControlCount.Sample(CountControls()); + ++InputStatistics.UpdateCount.Value; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1809:AvoidExcessiveLocals", Justification = "TODO: Refactor later.")] + private unsafe void DoUpdate(InputUpdateType updateType, ref InputEventBuffer eventBuffer) { // NOTE: This is *not* using try/finally as we've seen unreliability in the EndSample() // execution (and we're not sure where it's coming from). @@ -3630,7 +3654,7 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev } ResetCurrentProcessedEventBytesForDevices(); - + // Update metrics (exposed via analytics and debugger) var eventProcessingTime = ((double)(Stopwatch.GetTimestamp() - processingStartTime)) / Stopwatch.Frequency; @@ -3638,14 +3662,14 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev m_Metrics.totalEventBytes += totalEventSizeBytes; m_Metrics.totalEventProcessingTime += eventProcessingTime; m_Metrics.totalEventLagTime += totalEventLag; - + // Profiler counters InputStatistics.EventCount.Value += totalEventCount; InputStatistics.EventSize.Value += totalEventSizeBytes; InputStatistics.AverageLatency.Value += ((totalEventLag / totalEventCount) * 1e9); InputStatistics.MaxLatency.Value += (maxEventLag * 1e9); InputStatistics.EventProcessingTime.Value += eventProcessingTime * 1e9; // TODO Possible to replace Stopwatch with marker somehow? - + m_InputEventStream.Close(ref eventBuffer); } catch (Exception) diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs b/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs index b338f6f639..4d7220718c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/InputStatistics.cs @@ -7,24 +7,42 @@ namespace UnityEngine.InputSystem /// internal static class InputStatistics { - public static readonly ProfilerCategory Category = ProfilerCategory.Input; + /// + /// The Profiler Category to be used. + /// + internal static readonly ProfilerCategory Category = ProfilerCategory.Input; - public const string EventCountName = "Input Event Count"; - public const string EventSizeName = "Input Event Size"; - public const string AverageLatencyName = "Average Latency"; - public const string MaxLatencyName = "Max Latency"; - public const string EventProcessingTimeName = "Event Processing Time"; + internal const string EventCountName = "Total Input Event Count"; + internal const string EventSizeName = "Total Input Event Size"; + internal const string AverageLatencyName = "Average Input Latency"; + internal const string MaxLatencyName = "Max Input Latency"; + internal const string EventProcessingTimeName = "Total Input Event Processing Time"; + internal const string DeviceCountName = "Input Device Count"; + internal const string ControlCountName = "Active Control Count"; + internal const string CurrentStateMemoryBytesName = "Current State Memory Bytes"; + internal const string StateBufferSizeBytesName = "Total State Buffer Size"; + internal const string UpdateCountName = "Update Count"; /// - /// Counter reflecting the number of input events. + /// Counter reflecting the number of input events. /// + /// + /// We use ProfilerCounterValue instead of ProfilerCounter since there may be multiple Input System updates + /// per frame and we want it to accumulate for the profilers perspective on what a frame is but auto-reset + /// when outside the profilers perspective of a frame. + /// public static readonly ProfilerCounterValue EventCount = new ProfilerCounterValue( Category, EventCountName, ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); - + /// /// Counter reflecting the accumulated input event size in bytes. /// + /// + /// We use ProfilerCounterValue instead of ProfilerCounter since there may be multiple Input System updates + /// per frame and we want it to accumulate for the profilers perspective on what a frame is but auto-reset + /// when outside the profilers perspective of a frame. + /// public static readonly ProfilerCounterValue EventSize = new ProfilerCounterValue( Category, EventSizeName, ProfilerMarkerDataUnit.Bytes, ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); @@ -32,6 +50,11 @@ internal static class InputStatistics /// /// Counter value reflecting the average input latency. /// + /// + /// We use ProfilerCounterValue instead of ProfilerCounter since there may be multiple Input System updates + /// per frame and we want it to accumulate for the profilers perspective on what a frame is but auto-reset + /// when outside the profilers perspective of a frame. + /// public static readonly ProfilerCounterValue AverageLatency = new ProfilerCounterValue( Category, AverageLatencyName, ProfilerMarkerDataUnit.TimeNanoseconds, ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); @@ -39,6 +62,11 @@ internal static class InputStatistics /// /// Counter value reflecting the maximum input latency. /// + /// + /// We use ProfilerCounterValue instead of ProfilerCounter since there may be multiple Input System updates + /// per frame and we want it to accumulate for the profilers perspective on what a frame is but auto-reset + /// when outside the profilers perspective of a frame. + /// public static readonly ProfilerCounterValue MaxLatency = new ProfilerCounterValue( Category, MaxLatencyName, ProfilerMarkerDataUnit.TimeNanoseconds, ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); @@ -46,8 +74,42 @@ internal static class InputStatistics /// /// Counter value reflecting the accumulated event processing time (Update) during a rendering frame. /// + /// + /// We use ProfilerCounterValue instead of ProfilerCounter since there may be multiple Input System updates + /// per frame and we want it to accumulate for the profilers perspective on what a frame is but auto-reset + /// when outside the profilers perspective of a frame. + /// public static readonly ProfilerCounterValue EventProcessingTime = new ProfilerCounterValue( Category, EventProcessingTimeName, ProfilerMarkerDataUnit.TimeNanoseconds, ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); + + /// + /// The number of devices currently added to the Input System. + /// + public static readonly ProfilerCounter DeviceCount = new ProfilerCounter( + Category, DeviceCountName, ProfilerMarkerDataUnit.Count); + + /// + /// The total number of device controls currently in the Input System. + /// + public static readonly ProfilerCounter ControlCount = new ProfilerCounter( + Category, ControlCountName, ProfilerMarkerDataUnit.Count); + + /// + /// The total state buffer size in bytes. + /// + public static readonly ProfilerCounter StateBufferSizeBytes = new ProfilerCounter( + Category, StateBufferSizeBytesName, ProfilerMarkerDataUnit.Bytes); + + /// + /// The total update count. + /// + /// + /// Update may get called multiple times, e.g. either via manual updates, dynamic update, fixed update + /// or editor update while running in the editor. + /// + public static readonly ProfilerCounterValue UpdateCount = new ProfilerCounterValue( + Category, UpdateCountName, ProfilerMarkerDataUnit.Count, + ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); } -} \ No newline at end of file +} From a3f8272d7b7561b0bf9e0c6330c27b257038a00c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Fri, 25 Oct 2024 14:34:19 +0200 Subject: [PATCH 6/6] Added older style C# new --- .../Internal/InputSystemProfilerModule.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs index ca771a159e..ee1a39fee0 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputSystemProfilerModule.cs @@ -123,15 +123,15 @@ void OnSelectedFrameIndexChanged(long selectedFrameIndex) private static readonly ProfilerCounterDescriptor[] Counters = new ProfilerCounterDescriptor[] { - new(InputStatistics.UpdateCountName, InputStatistics.Category), - new(InputStatistics.EventCountName, InputStatistics.Category), - new(InputStatistics.EventSizeName, InputStatistics.Category), - new(InputStatistics.StateBufferSizeBytesName, InputStatistics.Category), - new(InputStatistics.AverageLatencyName, InputStatistics.Category), - new(InputStatistics.MaxLatencyName, InputStatistics.Category), - new(InputStatistics.EventProcessingTimeName, InputStatistics.Category), - new(InputStatistics.DeviceCountName, InputStatistics.Category), - new(InputStatistics.ControlCountName, InputStatistics.Category), + new ProfilerCounterDescriptor(InputStatistics.UpdateCountName, InputStatistics.Category), + new ProfilerCounterDescriptor(InputStatistics.EventCountName, InputStatistics.Category), + new ProfilerCounterDescriptor(InputStatistics.EventSizeName, InputStatistics.Category), + new ProfilerCounterDescriptor(InputStatistics.StateBufferSizeBytesName, InputStatistics.Category), + new ProfilerCounterDescriptor(InputStatistics.AverageLatencyName, InputStatistics.Category), + new ProfilerCounterDescriptor(InputStatistics.MaxLatencyName, InputStatistics.Category), + new ProfilerCounterDescriptor(InputStatistics.EventProcessingTimeName, InputStatistics.Category), + new ProfilerCounterDescriptor(InputStatistics.DeviceCountName, InputStatistics.Category), + new ProfilerCounterDescriptor(InputStatistics.ControlCountName, InputStatistics.Category), }; public InputSystemProfilerModule()