Files
FlaxEngine/Source/Editor/Windows/Profiler/Assets.cs
Luke Schneider ad28a3fdbf Better light theme (Style) support, and a Default light theme (as a secondary option)
1) Added ForegroundViewport as a new color.  It is used in the main game viewport (ViewportWidgetButton), and the viewport for rendering of particles and materials.  It is needed because the default foreground in a Light theme is black, but black does not work well in a viewport.  A new color seemed appropriate.

2) Fixed the profiler window to use the Foreground color in multiple text elements, instead of Color.White (or no default TitleColor).  This includes  the Row class, Asset class, SingleChart class, Timeline Class, and more.

3) Added a second theme/Style (DefaultLight) to include with the engine.  It uses RGB float values because those were easier to transfer from the saved values that I had created (and they're easier for me to edit if necessary).  I tried to emulate how the Default theme is created/loaded/etc as closely as possible.
2023-09-27 21:54:34 -05:00

293 lines
10 KiB
C#

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.Text;
using FlaxEditor.GUI;
using FlaxEngine;
using FlaxEngine.Json;
using FlaxEngine.GUI;
using FlaxEditor.GUI.ContextMenu;
using System.Linq;
namespace FlaxEditor.Windows.Profiler
{
/// <summary>
/// The Assets profiling mode.
/// </summary>
/// <seealso cref="FlaxEditor.Windows.Profiler.ProfilerMode" />
internal sealed class Assets : ProfilerMode
{
private class Resource
{
public string Name;
public string TypeName;
public int ReferencesCount;
public ulong MemoryUsage;
public Guid AssetId;
}
private readonly SingleChart _memoryUsageChart;
private readonly Table _table;
private SamplesBuffer<Resource[]> _resources;
private List<ClickableRow> _tableRowsCache;
private Dictionary<Guid, Resource> _resourceCache;
private StringBuilder _stringBuilder;
public Assets()
: base("Assets")
{
// 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
_memoryUsageChart = new SingleChart
{
Title = "Assets Memory Usage (CPU)",
FormatSample = v => Utilities.Utils.FormatBytesCount((int)v),
Parent = layout,
};
_memoryUsageChart.SelectedSampleChanged += OnSelectedSampleChanged;
// Table
var headerColor = Style.Current.LightBackground;
var textColor = Style.Current.Foreground;
_table = new Table
{
Columns = new[]
{
new ColumnDefinition
{
UseExpandCollapseMode = true,
CellAlignment = TextAlignment.Near,
Title = "Resource",
TitleBackgroundColor = headerColor,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "Type",
CellAlignment = TextAlignment.Center,
TitleBackgroundColor = headerColor,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "References",
TitleBackgroundColor = headerColor,
TitleColor = textColor,
},
new ColumnDefinition
{
Title = "Memory Usage",
TitleBackgroundColor = headerColor,
TitleColor = textColor,
FormatValue = v => Utilities.Utils.FormatBytesCount((ulong)v),
},
},
Parent = layout,
};
_table.Splits = new[]
{
0.6f,
0.2f,
0.08f,
0.12f,
};
}
/// <inheritdoc />
public override void Clear()
{
_memoryUsageChart.Clear();
_resources?.Clear();
_resourceCache?.Clear();
}
/// <inheritdoc />
public override void Update(ref SharedUpdateData sharedData)
{
if (_resourceCache == null)
_resourceCache = new Dictionary<Guid, Resource>();
if (_stringBuilder == null)
_stringBuilder = new StringBuilder();
// Capture current assets usage info
var assets = FlaxEngine.Content.Assets;
var resources = new Resource[assets.Length];
ulong totalMemoryUsage = 0;
for (int i = 0; i < resources.Length; i++)
{
var asset = assets[i];
ref var resource = ref resources[i];
if (!asset)
continue;
// Try to reuse cached resource info
var assetId = asset.ID;
if (!_resourceCache.TryGetValue(assetId, out resource))
{
resource = new Resource
{
Name = asset.Path,
TypeName = asset.TypeName,
AssetId = assetId,
};
var typeNameEnding = asset.TypeName.LastIndexOf('.');
if (typeNameEnding != -1)
resource.TypeName = resource.TypeName.Substring(typeNameEnding + 1);
var assetItem = Editor.Instance.ContentDatabase.FindAsset(assetId);
if (assetItem != null)
{
resource.Name = assetItem.NamePath;
}
if (string.IsNullOrEmpty(resource.Name) && asset.IsVirtual)
resource.Name = "<virtual>";
_resourceCache.Add(assetId, resource);
}
resource.MemoryUsage = asset.MemoryUsage;
resource.ReferencesCount = asset.ReferencesCount;
totalMemoryUsage += resource.MemoryUsage;
}
_memoryUsageChart.AddSample((float)totalMemoryUsage);
if (_resources == null)
_resources = new SamplesBuffer<Resource[]>();
_resources.Add(resources);
}
/// <inheritdoc />
public override void UpdateView(int selectedFrame, bool showOnlyLastUpdateEvents)
{
_memoryUsageChart.SelectedSampleIndex = selectedFrame;
if (_resources == null)
return;
if (_tableRowsCache == null)
_tableRowsCache = new List<ClickableRow>();
UpdateTable();
}
/// <inheritdoc />
public override void OnDestroy()
{
_resources?.Clear();
_resourceCache?.Clear();
_tableRowsCache?.Clear();
_stringBuilder?.Clear();
base.OnDestroy();
}
private void UpdateTable()
{
_table.IsLayoutLocked = true;
int idx = 0;
while (_table.Children.Count > idx)
{
var child = _table.Children[idx];
if (child is ClickableRow row)
{
_tableRowsCache.Add(row);
child.Parent = null;
}
else
{
idx++;
}
}
_table.LockChildrenRecursive();
UpdateTableInner();
_table.UnlockChildrenRecursive();
_table.PerformLayout();
}
private void UpdateTableInner()
{
if (_resources.Count == 0)
return;
var resources = _resources.Get(_memoryUsageChart.SelectedSampleIndex);
if (resources == null || resources.Length == 0)
return;
var resourcesOrdered = resources.OrderByDescending(x => x.MemoryUsage);
// Add rows
var rowColor2 = Style.Current.Background * 1.4f;
int rowIndex = 0;
foreach (var e in resourcesOrdered)
{
ClickableRow row;
if (_tableRowsCache.Count != 0)
{
// Reuse row
var last = _tableRowsCache.Count - 1;
row = _tableRowsCache[last];
_tableRowsCache.RemoveAt(last);
}
else
{
// Allocate new row
row = new ClickableRow { Values = new object[4] };
row.RowDoubleClick = OnRowDoubleClick;
row.RowRightClick = OnRowRightClick;
}
// Setup row data
row.Tag = e.AssetId;
row.Values[0] = e.Name;
row.Values[1] = e.TypeName;
row.Values[2] = e.ReferencesCount;
row.Values[3] = e.MemoryUsage;
// Add row to the table
row.Width = _table.Width;
row.BackgroundColor = rowIndex % 2 == 0 ? rowColor2 : Color.Transparent;
row.Parent = _table;
rowIndex++;
}
}
private void OnRowDoubleClick(ClickableRow row)
{
var assetId = (Guid)row.Tag;
var assetItem = Editor.Instance.ContentDatabase.FindAsset(assetId);
if (assetItem != null)
Editor.Instance.ContentEditing.Open(assetItem);
}
private void OnRowRightClick(ClickableRow row)
{
var assetId = (Guid)row.Tag;
var assetItem = Editor.Instance.ContentDatabase.FindAsset(assetId);
if (assetItem != null)
{
var cm = new ContextMenu();
ContextMenuButton b;
b = cm.AddButton("Open", () => Editor.Instance.ContentEditing.Open(assetItem));
cm.AddButton("Show in content window", () => Editor.Instance.Windows.ContentWin.Select(assetItem));
cm.AddButton("Show in explorer", () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(assetItem.Path)));
cm.AddButton("Select actors using this asset", () => Editor.Instance.SceneEditing.SelectActorsUsingAsset(assetItem.ID));
cm.AddButton("Show asset references graph", () => Editor.Instance.Windows.Open(new AssetReferencesGraphWindow(Editor.Instance, assetItem)));
cm.AddButton("Copy name", () => Clipboard.Text = assetItem.NamePath);
cm.AddButton("Copy path", () => Clipboard.Text = assetItem.Path);
cm.AddButton("Copy asset ID", () => Clipboard.Text = JsonSerializer.GetStringID(assetItem.ID));
cm.Show(row, row.PointFromScreen(Input.MouseScreenPosition));
}
}
}
}