From 81fd0263131a19576621e48db13ce602663ab6bc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 11 Jun 2021 16:44:26 +0200 Subject: [PATCH] Add pooling for timeline event controls in profiler --- Source/Editor/Windows/Profiler/CPU.cs | 93 +++++++++++++++++----- Source/Editor/Windows/Profiler/GPU.cs | 59 +++++++++++--- Source/Editor/Windows/Profiler/Timeline.cs | 25 +++--- 3 files changed, 134 insertions(+), 43 deletions(-) diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs index 84ab4ec16..e0cfef46f 100644 --- a/Source/Editor/Windows/Profiler/CPU.cs +++ b/Source/Editor/Windows/Profiler/CPU.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using System.Collections.Generic; using FlaxEditor.GUI; using FlaxEngine; using FlaxEngine.GUI; @@ -50,7 +51,9 @@ namespace FlaxEditor.Windows.Profiler private readonly SingleChart _mainChart; private readonly Timeline _timeline; private readonly Table _table; - private readonly SamplesBuffer _events = new SamplesBuffer(); + private SamplesBuffer _events; + private List _timelineLabelsCache; + private List _timelineEventsCache; private bool _showOnlyLastUpdateEvents; public CPU() @@ -163,7 +166,7 @@ namespace FlaxEditor.Windows.Profiler public override void Clear() { _mainChart.Clear(); - _events.Clear(); + _events?.Clear(); } /// @@ -173,6 +176,8 @@ namespace FlaxEditor.Windows.Profiler // Gather CPU events var events = sharedData.GetEventsCPU(); + if (_events == null) + _events = new SamplesBuffer(); _events.Add(events); } @@ -182,11 +187,28 @@ namespace FlaxEditor.Windows.Profiler _showOnlyLastUpdateEvents = showOnlyLastUpdateEvents; _mainChart.SelectedSampleIndex = selectedFrame; + if (_events == null) + return; + if (_timelineLabelsCache == null) + _timelineLabelsCache = new List(); + if (_timelineEventsCache == null) + _timelineEventsCache = new List(); + var viewRange = GetEventsViewRange(); UpdateTimeline(ref viewRange); UpdateTable(ref viewRange); } + /// + public override void OnDestroy() + { + Clear(); + _timelineLabelsCache?.Clear(); + _timelineEventsCache?.Clear(); + + base.OnDestroy(); + } + private struct ViewRange { public double Start; @@ -215,7 +237,7 @@ namespace FlaxEditor.Windows.Profiler if (_showOnlyLastUpdateEvents) { // Find root event named 'Update' and use it as a view range - if (_events.Count != 0) + if (_events != null && _events.Count != 0) { var data = _events.Get(_mainChart.SelectedSampleIndex); if (data != null) @@ -251,14 +273,22 @@ namespace FlaxEditor.Windows.Profiler double scale = 100.0; float x = (float)((e.Start - startTime) * scale); float width = (float)(length * scale); - string name = e.Name.Replace("::", "."); - var control = new Timeline.Event(x + xOffset, e.Depth + depthOffset, width) + Timeline.Event control; + if (_timelineEventsCache.Count != 0) { - Name = name, - TooltipText = string.Format("{0}, {1} ms", name, ((int)(length * 1000.0) / 1000.0f)), - Parent = parent, - }; + var last = _timelineEventsCache.Count - 1; + control = _timelineEventsCache[last]; + _timelineEventsCache.RemoveAt(last); + } + else + { + control = new Timeline.Event(); + } + control.Bounds = new Rectangle(x + xOffset, (e.Depth + depthOffset) * Timeline.Event.DefaultHeight, width, Timeline.Event.DefaultHeight - 1); + control.Name = e.Name.Replace("::", "."); + control.TooltipText = string.Format("{0}, {1} ms", control.Name, ((int)(length * 1000.0) / 1000.0f)); + control.Parent = parent; // Spawn sub events int childrenDepth = e.Depth + 1; @@ -282,10 +312,26 @@ namespace FlaxEditor.Windows.Profiler { var container = _timeline.EventsContainer; - // Clear previous events - container.DisposeChildren(); - - container.LockChildrenRecursive(); + container.IsLayoutLocked = true; + int idx = 0; + while (container.Children.Count > idx) + { + var child = container.Children[idx]; + if (child is Timeline.Event e) + { + _timelineEventsCache.Add(e); + child.Parent = null; + } + else if (child is Timeline.TrackLabel l) + { + _timelineLabelsCache.Add(l); + child.Parent = null; + } + else + { + idx++; + } + } _timeline.Height = UpdateTimelineInner(ref viewRange); @@ -340,13 +386,21 @@ namespace FlaxEditor.Windows.Profiler // Add thread label float xOffset = 90; - var label = new Timeline.TrackLabel + Timeline.TrackLabel trackLabel; + if (_timelineLabelsCache.Count != 0) { - Bounds = new Rectangle(0, depthOffset * Timeline.Event.DefaultHeight, xOffset, (maxDepth + 2) * Timeline.Event.DefaultHeight), - Name = data[i].Name, - BackgroundColor = Style.Current.Background * 1.1f, - Parent = container, - }; + var last = _timelineLabelsCache.Count - 1; + trackLabel = _timelineLabelsCache[last]; + _timelineLabelsCache.RemoveAt(last); + } + else + { + trackLabel = new Timeline.TrackLabel(); + } + trackLabel.Bounds = new Rectangle(0, depthOffset * Timeline.Event.DefaultHeight, xOffset, (maxDepth + 2) * Timeline.Event.DefaultHeight); + trackLabel.Name = data[i].Name; + trackLabel.BackgroundColor = Style.Current.Background * 1.1f; + trackLabel.Parent = container; // Add events for (int j = 0; j < events.Length; j++) @@ -371,7 +425,6 @@ namespace FlaxEditor.Windows.Profiler private void UpdateTable(ref ViewRange viewRange) { _table.DisposeChildren(); - _table.LockChildrenRecursive(); UpdateTableInner(ref viewRange); diff --git a/Source/Editor/Windows/Profiler/GPU.cs b/Source/Editor/Windows/Profiler/GPU.cs index a1af36a70..da7d04249 100644 --- a/Source/Editor/Windows/Profiler/GPU.cs +++ b/Source/Editor/Windows/Profiler/GPU.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System.Collections.Generic; using FlaxEditor.GUI; using FlaxEngine; using FlaxEngine.GUI; @@ -16,7 +17,8 @@ namespace FlaxEditor.Windows.Profiler private readonly SingleChart _drawTimeGPU; private readonly Timeline _timeline; private readonly Table _table; - private readonly SamplesBuffer _events = new SamplesBuffer(); + private SamplesBuffer _events; + private List _timelineEventsCache; public GPU() : base("GPU") @@ -118,7 +120,7 @@ namespace FlaxEditor.Windows.Profiler { _drawTimeCPU.Clear(); _drawTimeGPU.Clear(); - _events.Clear(); + _events?.Clear(); } /// @@ -126,6 +128,8 @@ namespace FlaxEditor.Windows.Profiler { // Gather GPU events var data = sharedData.GetEventsGPU(); + if (_events == null) + _events = new SamplesBuffer(); _events.Add(data); // Peek draw time @@ -138,10 +142,25 @@ namespace FlaxEditor.Windows.Profiler { _drawTimeCPU.SelectedSampleIndex = selectedFrame; _drawTimeGPU.SelectedSampleIndex = selectedFrame; + + if (_events == null) + return; + if (_timelineEventsCache == null) + _timelineEventsCache = new List(); + UpdateTimeline(); UpdateTable(); } + /// + public override void OnDestroy() + { + Clear(); + _timelineEventsCache?.Clear(); + + base.OnDestroy(); + } + private float AddEvent(float x, int maxDepth, int index, ProfilerGPU.Event[] events, ContainerControl parent) { ref ProfilerGPU.Event e = ref events[index]; @@ -150,12 +169,21 @@ namespace FlaxEditor.Windows.Profiler float width = (float)(e.Time * scale); string name = new string(e.Name); - new Timeline.Event(x, e.Depth, width) + Timeline.Event control; + if (_timelineEventsCache.Count != 0) { - Name = name, - TooltipText = string.Format("{0}, {1} ms", name, ((int)(e.Time * 10000.0) / 10000.0f)), - Parent = parent, - }; + var last = _timelineEventsCache.Count - 1; + control = _timelineEventsCache[last]; + _timelineEventsCache.RemoveAt(last); + } + else + { + control = new Timeline.Event(); + } + control.Bounds = new Rectangle(x, e.Depth * Timeline.Event.DefaultHeight, width, Timeline.Event.DefaultHeight - 1); + control.Name = name; + control.TooltipText = string.Format("{0}, {1} ms", name, ((int)(e.Time * 10000.0) / 10000.0f)); + control.Parent = parent; // Spawn sub events int childrenDepth = e.Depth + 1; @@ -201,8 +229,21 @@ namespace FlaxEditor.Windows.Profiler { var container = _timeline.EventsContainer; - // Clear previous events - container.DisposeChildren(); + container.IsLayoutLocked = true; + int idx = 0; + while (container.Children.Count > idx) + { + var child = container.Children[idx]; + if (child is Timeline.Event e) + { + _timelineEventsCache.Add(e); + child.Parent = null; + } + else + { + idx++; + } + } container.LockChildrenRecursive(); diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 53f4c9987..8e7ab145d 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -40,7 +40,8 @@ namespace FlaxEditor.Windows.Profiler }; private Color _color; - private float _nameLength; + private string _name; + private float _nameLength = -1; /// /// The default height of the event. @@ -50,18 +51,14 @@ namespace FlaxEditor.Windows.Profiler /// /// Gets or sets the event name. /// - public string Name { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The x position. - /// The timeline row index (event depth). - /// The width. - public Event(float x, int depth, float width) - : base(x, depth * DefaultHeight, width, DefaultHeight - 1) + public string Name { - _nameLength = -1; + get => _name; + set + { + _name = value; + _nameLength = -1; + } } /// @@ -88,12 +85,12 @@ namespace FlaxEditor.Windows.Profiler Render2D.DrawRectangle(bounds, color * 0.5f); if (_nameLength < 0 && style.FontMedium) - _nameLength = style.FontMedium.MeasureText(Name).X; + _nameLength = style.FontMedium.MeasureText(_name).X; if (_nameLength < bounds.Width + 4) { Render2D.PushClip(bounds); - Render2D.DrawText(style.FontMedium, Name, bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _name, bounds, Color.White, TextAlignment.Center, TextAlignment.Center); Render2D.PopClip(); } }