Optimize managed memory allocations in Editor profiler

This commit is contained in:
Wojtek Figat
2025-06-09 22:06:49 +02:00
parent 89c7f4b0a3
commit cfd2f42b0c
5 changed files with 49 additions and 13 deletions

View File

@@ -34,6 +34,7 @@ namespace FlaxEditor.Windows.Profiler
private List<ClickableRow> _tableRowsCache; private List<ClickableRow> _tableRowsCache;
private Dictionary<Guid, Resource> _resourceCache; private Dictionary<Guid, Resource> _resourceCache;
private StringBuilder _stringBuilder; private StringBuilder _stringBuilder;
private Asset[] _assetsCache;
public Assets() public Assets()
: base("Assets") : base("Assets")
@@ -138,12 +139,12 @@ namespace FlaxEditor.Windows.Profiler
_stringBuilder = new StringBuilder(); _stringBuilder = new StringBuilder();
// Capture current assets usage info // Capture current assets usage info
var assets = FlaxEngine.Content.Assets; FlaxEngine.Content.GetAssets(ref _assetsCache, out var count);
var resources = new Resource[assets.Length]; var resources = new Resource[count];
ulong totalMemoryUsage = 0; ulong totalMemoryUsage = 0;
for (int i = 0; i < resources.Length; i++) for (int i = 0; i < count; i++)
{ {
var asset = assets[i]; var asset = _assetsCache[i];
ref var resource = ref resources[i]; ref var resource = ref resources[i];
if (!asset) if (!asset)
continue; continue;
@@ -179,6 +180,7 @@ namespace FlaxEditor.Windows.Profiler
if (_resources == null) if (_resources == null)
_resources = new SamplesBuffer<Resource[]>(); _resources = new SamplesBuffer<Resource[]>();
_resources.Add(resources); _resources.Add(resources);
Array.Clear(_assetsCache);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -200,6 +202,7 @@ namespace FlaxEditor.Windows.Profiler
_resourceCache?.Clear(); _resourceCache?.Clear();
_tableRowsCache?.Clear(); _tableRowsCache?.Clear();
_stringBuilder?.Clear(); _stringBuilder?.Clear();
_assetsCache = null;
base.OnDestroy(); base.OnDestroy();
} }

View File

@@ -35,6 +35,7 @@ namespace FlaxEditor.Windows.Profiler
private Dictionary<string, Guid> _assetPathToId; private Dictionary<string, Guid> _assetPathToId;
private Dictionary<Guid, Resource> _resourceCache; private Dictionary<Guid, Resource> _resourceCache;
private StringBuilder _stringBuilder; private StringBuilder _stringBuilder;
private GPUResource[] _gpuResourcesCached;
public MemoryGPU() public MemoryGPU()
: base("GPU Memory") : base("GPU Memory")
@@ -138,12 +139,12 @@ namespace FlaxEditor.Windows.Profiler
// Capture current GPU resources usage info // Capture current GPU resources usage info
var contentDatabase = Editor.Instance.ContentDatabase; var contentDatabase = Editor.Instance.ContentDatabase;
var gpuResources = GPUDevice.Instance.Resources; GPUDevice.Instance.GetResources(ref _gpuResourcesCached, out var count);
var resources = new Resource[gpuResources.Length]; var resources = new Resource[count];
var sb = _stringBuilder; var sb = _stringBuilder;
for (int i = 0; i < resources.Length; i++) for (int i = 0; i < count; i++)
{ {
var gpuResource = gpuResources[i]; var gpuResource = _gpuResourcesCached[i];
ref var resource = ref resources[i]; ref var resource = ref resources[i];
// Try to reuse cached resource info // Try to reuse cached resource info
@@ -219,6 +220,7 @@ namespace FlaxEditor.Windows.Profiler
if (_resources == null) if (_resources == null)
_resources = new SamplesBuffer<Resource[]>(); _resources = new SamplesBuffer<Resource[]>();
_resources.Add(resources); _resources.Add(resources);
Array.Clear(_gpuResourcesCached);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -255,6 +257,7 @@ namespace FlaxEditor.Windows.Profiler
_assetPathToId?.Clear(); _assetPathToId?.Clear();
_tableRowsCache?.Clear(); _tableRowsCache?.Clear();
_stringBuilder?.Clear(); _stringBuilder?.Clear();
_gpuResourcesCached = null;
base.OnDestroy(); base.OnDestroy();
} }

View File

@@ -21,6 +21,20 @@ namespace FlaxEngine
} }
} }
/// <summary>
/// Gets the assets (loaded or during load).
/// </summary>
/// <param name="buffer">Output buffer to fill with asset pointers. Can be provided by a user to avoid memory allocation. Buffer might be larger than actual list size. Use <paramref name="count"/> for actual item count.></param>
/// <param name="count">Amount of valid items inside <paramref name="buffer"/>.</param>
public static void GetAssets(ref Asset[] buffer, out int count)
{
count = 0;
IntPtr ptr = Internal_GetAssetsInternal();
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
buffer = NativeInterop.GCHandleArrayToManagedArray<Asset>(array, buffer);
count = buffer.Length;
}
/// <summary> /// <summary>
/// Loads asset to the Content Pool and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async. /// Loads asset to the Content Pool and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async.
/// </summary> /// </summary>

View File

@@ -195,17 +195,19 @@ namespace FlaxEngine.Interop
/// </summary> /// </summary>
/// <typeparam name="T">Array element type.</typeparam> /// <typeparam name="T">Array element type.</typeparam>
/// <param name="ptrArray">Input array.</param> /// <param name="ptrArray">Input array.</param>
/// <param name="buffer">Cached memory allocation buffer to use for the result (if size fits).</param>
/// <returns>Output array.</returns> /// <returns>Output array.</returns>
public static T[] GCHandleArrayToManagedArray<T>(ManagedArray ptrArray) where T : class public static T[] GCHandleArrayToManagedArray<T>(ManagedArray ptrArray, T[] buffer = null) where T : class
{ {
Span<IntPtr> span = ptrArray.ToSpan<IntPtr>(); Span<IntPtr> span = ptrArray.ToSpan<IntPtr>();
T[] managedArray = new T[ptrArray.Length]; if (buffer == null || buffer.Length < ptrArray.Length)
for (int i = 0; i < managedArray.Length; i++) buffer = new T[ptrArray.Length];
for (int i = 0; i < ptrArray.Length; i++)
{ {
IntPtr ptr = span[i]; IntPtr ptr = span[i];
managedArray[i] = ptr != IntPtr.Zero ? (T)ManagedHandle.FromIntPtr(ptr).Target : default; buffer[i] = ptr != IntPtr.Zero ? (T)ManagedHandle.FromIntPtr(ptr).Target : default;
} }
return managedArray; return buffer;
} }
/// <summary> /// <summary>

View File

@@ -20,6 +20,20 @@ namespace FlaxEngine
return NativeInterop.GCHandleArrayToManagedArray<GPUResource>(array); return NativeInterop.GCHandleArrayToManagedArray<GPUResource>(array);
} }
} }
/// <summary>
/// Gets the list with all active GPU resources.
/// </summary>
/// <param name="buffer">Output buffer to fill with resource pointers. Can be provided by a user to avoid memory allocation. Buffer might be larger than actual list size. Use <paramref name="count"/> for actual item count.></param>
/// <param name="count">Amount of valid items inside <paramref name="buffer"/>.</param>
public void GetResources(ref GPUResource[] buffer, out int count)
{
count = 0;
IntPtr ptr = Internal_GetResourcesInternal(__unmanagedPtr);
ManagedArray array = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(ptr).Target);
buffer = NativeInterop.GCHandleArrayToManagedArray<GPUResource>(array, buffer);
count = buffer.Length;
}
} }
partial struct GPUBufferDescription : IEquatable<GPUBufferDescription> partial struct GPUBufferDescription : IEquatable<GPUBufferDescription>