309 lines
9.5 KiB
C#
309 lines
9.5 KiB
C#
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
|
|
|
using FlaxEditor.GUI;
|
|
using FlaxEngine;
|
|
using FlaxEngine.GUI;
|
|
|
|
namespace FlaxEditor.Windows.Profiler
|
|
{
|
|
/// <summary>
|
|
/// The GPU performance profiling mode.
|
|
/// </summary>
|
|
/// <seealso cref="FlaxEditor.Windows.Profiler.ProfilerMode" />
|
|
internal sealed unsafe class GPU : ProfilerMode
|
|
{
|
|
private readonly SingleChart _drawTimeCPU;
|
|
private readonly SingleChart _drawTimeGPU;
|
|
private readonly Timeline _timeline;
|
|
private readonly Table _table;
|
|
private readonly SamplesBuffer<ProfilerGPU.Event[]> _events = new SamplesBuffer<ProfilerGPU.Event[]>();
|
|
|
|
public GPU()
|
|
: base("GPU")
|
|
{
|
|
// Layout
|
|
var panel = new Panel(ScrollBars.Vertical)
|
|
{
|
|
AnchorPreset = AnchorPresets.StretchAll,
|
|
Offsets = Margin.Zero,
|
|
Parent = this,
|
|
};
|
|
var layout = new VerticalPanel
|
|
{
|
|
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
|
Offsets = Margin.Zero,
|
|
IsScrollable = true,
|
|
Parent = panel,
|
|
};
|
|
|
|
// Chart
|
|
_drawTimeCPU = new SingleChart
|
|
{
|
|
Title = "Draw (CPU)",
|
|
FormatSample = v => (Mathf.RoundToInt(v * 10.0f) / 10.0f) + " ms",
|
|
Parent = layout,
|
|
};
|
|
_drawTimeCPU.SelectedSampleChanged += OnSelectedSampleChanged;
|
|
_drawTimeGPU = new SingleChart
|
|
{
|
|
Title = "Draw (GPU)",
|
|
FormatSample = v => (Mathf.RoundToInt(v * 10.0f) / 10.0f) + " ms",
|
|
Parent = layout,
|
|
};
|
|
_drawTimeGPU.SelectedSampleChanged += OnSelectedSampleChanged;
|
|
|
|
// Timeline
|
|
_timeline = new Timeline
|
|
{
|
|
Height = 340,
|
|
Parent = layout,
|
|
};
|
|
|
|
// Table
|
|
var headerColor = Style.Current.LightBackground;
|
|
_table = new Table
|
|
{
|
|
Columns = new[]
|
|
{
|
|
new ColumnDefinition
|
|
{
|
|
UseExpandCollapseMode = true,
|
|
CellAlignment = TextAlignment.Near,
|
|
Title = "Event",
|
|
TitleBackgroundColor = headerColor,
|
|
},
|
|
new ColumnDefinition
|
|
{
|
|
Title = "Total",
|
|
TitleBackgroundColor = headerColor,
|
|
FormatValue = (x) => ((float)x).ToString("0.0") + '%',
|
|
},
|
|
new ColumnDefinition
|
|
{
|
|
Title = "GPU ms",
|
|
TitleBackgroundColor = headerColor,
|
|
FormatValue = (x) => ((float)x).ToString("0.000"),
|
|
},
|
|
new ColumnDefinition
|
|
{
|
|
Title = "Draw Calls",
|
|
TitleBackgroundColor = headerColor,
|
|
},
|
|
new ColumnDefinition
|
|
{
|
|
Title = "Triangles",
|
|
TitleBackgroundColor = headerColor,
|
|
},
|
|
new ColumnDefinition
|
|
{
|
|
Title = "Vertices",
|
|
TitleBackgroundColor = headerColor,
|
|
},
|
|
},
|
|
Parent = layout,
|
|
};
|
|
_table.Splits = new[]
|
|
{
|
|
0.5f,
|
|
0.1f,
|
|
0.1f,
|
|
0.1f,
|
|
0.1f,
|
|
0.1f,
|
|
};
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Clear()
|
|
{
|
|
_drawTimeCPU.Clear();
|
|
_drawTimeGPU.Clear();
|
|
_events.Clear();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Update(ref SharedUpdateData sharedData)
|
|
{
|
|
// Gather GPU events
|
|
var data = sharedData.GetEventsGPU();
|
|
_events.Add(data);
|
|
|
|
// Peek draw time
|
|
_drawTimeCPU.AddSample(sharedData.Stats.DrawCPUTimeMs);
|
|
_drawTimeGPU.AddSample(sharedData.Stats.DrawGPUTimeMs);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void UpdateView(int selectedFrame, bool showOnlyLastUpdateEvents)
|
|
{
|
|
_drawTimeCPU.SelectedSampleIndex = selectedFrame;
|
|
_drawTimeGPU.SelectedSampleIndex = selectedFrame;
|
|
UpdateTimeline();
|
|
UpdateTable();
|
|
}
|
|
|
|
private float AddEvent(float x, int maxDepth, int index, ProfilerGPU.Event[] events, ContainerControl parent)
|
|
{
|
|
ref ProfilerGPU.Event e = ref events[index];
|
|
|
|
double scale = 100.0;
|
|
float width = (float)(e.Time * scale);
|
|
string name = new string(e.Name);
|
|
|
|
new Timeline.Event(x, e.Depth, width)
|
|
{
|
|
Name = name,
|
|
TooltipText = string.Format("{0}, {1} ms", name, ((int)(e.Time * 10000.0) / 10000.0f)),
|
|
Parent = parent,
|
|
};
|
|
|
|
// Spawn sub events
|
|
int childrenDepth = e.Depth + 1;
|
|
if (childrenDepth <= maxDepth)
|
|
{
|
|
// Count sub events total duration
|
|
double subEventsDuration = 0;
|
|
int tmpIndex = index;
|
|
while (++tmpIndex < events.Length)
|
|
{
|
|
int subDepth = events[tmpIndex].Depth;
|
|
|
|
if (subDepth <= e.Depth)
|
|
break;
|
|
if (subDepth == childrenDepth)
|
|
subEventsDuration += events[tmpIndex].Time;
|
|
}
|
|
|
|
// Skip if has no sub events
|
|
if (subEventsDuration > 0)
|
|
{
|
|
// Apply some offset to sub-events (center them within this event)
|
|
x += (float)((e.Time - subEventsDuration) * scale) * 0.5f;
|
|
|
|
while (++index < events.Length)
|
|
{
|
|
int subDepth = events[index].Depth;
|
|
|
|
if (subDepth <= e.Depth)
|
|
break;
|
|
if (subDepth == childrenDepth)
|
|
{
|
|
x += AddEvent(x, maxDepth, index, events, parent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return width;
|
|
}
|
|
|
|
private void UpdateTimeline()
|
|
{
|
|
var container = _timeline.EventsContainer;
|
|
|
|
// Clear previous events
|
|
container.DisposeChildren();
|
|
|
|
container.LockChildrenRecursive();
|
|
|
|
_timeline.Height = UpdateTimelineInner();
|
|
|
|
container.UnlockChildrenRecursive();
|
|
container.PerformLayout();
|
|
}
|
|
|
|
private float UpdateTimelineInner()
|
|
{
|
|
if (_events.Count == 0)
|
|
return 0;
|
|
var data = _events.Get(_drawTimeCPU.SelectedSampleIndex);
|
|
if (data == null || data.Length == 0)
|
|
return 0;
|
|
|
|
var container = _timeline.EventsContainer;
|
|
var events = data;
|
|
|
|
// Check maximum depth
|
|
int maxDepth = 0;
|
|
for (int j = 0; j < events.Length; j++)
|
|
{
|
|
maxDepth = Mathf.Max(maxDepth, events[j].Depth);
|
|
}
|
|
|
|
// Add events
|
|
float x = 0;
|
|
for (int j = 0; j < events.Length; j++)
|
|
{
|
|
if (events[j].Depth == 0)
|
|
{
|
|
x += AddEvent(x, maxDepth, j, events, container);
|
|
}
|
|
}
|
|
|
|
return Timeline.Event.DefaultHeight * (maxDepth + 2);
|
|
}
|
|
|
|
private void UpdateTable()
|
|
{
|
|
_table.DisposeChildren();
|
|
|
|
_table.LockChildrenRecursive();
|
|
|
|
UpdateTableInner();
|
|
|
|
_table.UnlockChildrenRecursive();
|
|
_table.PerformLayout();
|
|
}
|
|
|
|
private void UpdateTableInner()
|
|
{
|
|
if (_events.Count == 0)
|
|
return;
|
|
var data = _events.Get(_drawTimeCPU.SelectedSampleIndex);
|
|
if (data == null || data.Length == 0)
|
|
return;
|
|
|
|
float totalTimeMs = _drawTimeCPU.SelectedSample;
|
|
|
|
// Add rows
|
|
var rowColor2 = Style.Current.Background * 1.4f;
|
|
for (int i = 0; i < data.Length; i++)
|
|
{
|
|
var e = data[i];
|
|
string name = new string(e.Name);
|
|
|
|
var row = new Row
|
|
{
|
|
Values = new object[]
|
|
{
|
|
// Event
|
|
name,
|
|
|
|
// Total (%)
|
|
(int)(e.Time / totalTimeMs * 1000.0f) / 10.0f,
|
|
|
|
// GPU ms
|
|
(e.Time * 10000.0f) / 10000.0f,
|
|
|
|
// Draw Calls
|
|
e.Stats.DrawCalls,
|
|
|
|
// Triangles
|
|
e.Stats.Triangles,
|
|
|
|
// Vertices
|
|
e.Stats.Vertices,
|
|
},
|
|
Depth = e.Depth,
|
|
Width = _table.Width,
|
|
Parent = _table,
|
|
};
|
|
|
|
if (i % 2 == 0)
|
|
row.BackgroundColor = rowColor2;
|
|
row.Visible = e.Depth < 3;
|
|
}
|
|
}
|
|
}
|
|
}
|