From 6ad5aa3f2cdfd656904bff0c688ec45c250b61ac Mon Sep 17 00:00:00 2001 From: max Date: Wed, 5 Aug 2026 14:04:21 +0200 Subject: [PATCH] Add profiler warmup overhead tracking and tooltip display --- Nerfed.Editor/Systems/EditorProfilerWindow.cs | 11 ++++++++++- Nerfed.Runtime/Profiler.cs | 13 ++++++++++++- Nerfed.Runtime/ProfilerVisualizer.cs | 5 +++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Nerfed.Editor/Systems/EditorProfilerWindow.cs b/Nerfed.Editor/Systems/EditorProfilerWindow.cs index 0d8a6c2..a93dba4 100644 --- a/Nerfed.Editor/Systems/EditorProfilerWindow.cs +++ b/Nerfed.Editor/Systems/EditorProfilerWindow.cs @@ -194,7 +194,16 @@ namespace Nerfed.Editor.Systems ImGui.TableNextColumn(); ImGui.Text($"{node.SelfMilliseconds():0.000}"); ImGui.TableNextColumn(); - ImGui.Text($"{node.AllocatedBytes}"); + if (node.ProfilerSetupBytes > 0) + { + ImGui.Text($"{node.AllocatedBytes} !"); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip($"{node.ProfilerSetupBytes} B is profiler warmup overhead"); + } + else + { + ImGui.Text($"{node.AllocatedBytes}"); + } ImGui.TableNextColumn(); ImGui.Text($"{node.SelfAllocatedBytes()}"); diff --git a/Nerfed.Runtime/Profiler.cs b/Nerfed.Runtime/Profiler.cs index 2511a95..850bd75 100644 --- a/Nerfed.Runtime/Profiler.cs +++ b/Nerfed.Runtime/Profiler.cs @@ -434,6 +434,7 @@ public static class Profiler public int ManagedThreadId { get; private set; } public List Children { get; } = new List(); public long AllocatedBytes { get; private set; } + public long ProfilerSetupBytes { get; private set; } internal ScopeNode Parent { get; private set; } internal long ChildrenDurationTicks { get; private set; } internal long ChildrenAllocatedBytes { get; private set; } @@ -451,6 +452,7 @@ public static class Profiler ChildrenDurationTicks = 0; ChildrenAllocatedBytes = 0; AllocatedBytes = 0; + ProfilerSetupBytes = 0; Children.Clear(); allocatedBytesAtStart = GC.GetAllocatedBytesForCurrentThread(); } @@ -463,9 +465,11 @@ public static class Profiler } EndTime = Stopwatch.GetTimestamp(); - AllocatedBytes = Math.Max(0, GC.GetAllocatedBytesForCurrentThread() - allocatedBytesAtStart); if (Parent != null) { + // Root nodes are ended from FinalizeCurrentFrame on the main thread, so their + // GC counter would be from the wrong thread. Only track alloc on non-root nodes. + AllocatedBytes = Math.Max(0, GC.GetAllocatedBytesForCurrentThread() - allocatedBytesAtStart); Parent.ChildrenDurationTicks += Math.Max(0, EndTime - StartTime); Parent.ChildrenAllocatedBytes += AllocatedBytes; } @@ -488,6 +492,12 @@ public static class Profiler return Math.Max(0, AllocatedBytes - ChildrenAllocatedBytes); } + // Called after all profiler setup (Children.Add + scopes.Push) to measure overhead within this scope's window. + internal void NoteSetupOverhead() + { + ProfilerSetupBytes = Math.Max(0, GC.GetAllocatedBytesForCurrentThread() - allocatedBytesAtStart); + } + internal ScopeNode AddChild(string label, string category, ulong tagMask) { ScopeNode child = RentNode(label, category, tagMask, ManagedThreadId, this); @@ -648,6 +658,7 @@ public static class Profiler ScopeNode newScope = scopes.Peek().AddChild(label, category, tagMask); scopes.Push(newScope); + newScope.NoteSetupOverhead(); } [Conditional("PROFILING")] diff --git a/Nerfed.Runtime/ProfilerVisualizer.cs b/Nerfed.Runtime/ProfilerVisualizer.cs index 926e1bb..08322f1 100644 --- a/Nerfed.Runtime/ProfilerVisualizer.cs +++ b/Nerfed.Runtime/ProfilerVisualizer.cs @@ -372,6 +372,11 @@ public static class ProfilerVisualizer ImGui.Text($"Timeline Start: {hover.StartInTimelineMs:0.000} ms"); ImGui.Text($"Timeline End: {hover.EndInTimelineMs:0.000} ms"); ImGui.Text($"Children: {hover.Node.Children.Count}"); + if (hover.Node.ProfilerSetupBytes > 0) + { + ImGui.Separator(); + ImGui.TextColored(new Vector4(1f, 0.75f, 0f, 1f), $"\u26a0 {hover.Node.ProfilerSetupBytes} B of alloc is profiler warmup overhead"); + } ImGui.EndTooltip(); }