From cfd2f42b0cf3787f6f502fde7f885a369a909936 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 9 Jun 2025 22:06:49 +0200 Subject: [PATCH] Optimize managed memory allocations in Editor profiler --- Source/Editor/Windows/Profiler/Assets.cs | 11 +++++++---- Source/Editor/Windows/Profiler/MemoryGPU.cs | 11 +++++++---- Source/Engine/Content/Content.cs | 14 ++++++++++++++ Source/Engine/Engine/NativeInterop.cs | 12 +++++++----- Source/Engine/Graphics/GPUBufferDescription.cs | 14 ++++++++++++++ 5 files changed, 49 insertions(+), 13 deletions(-) diff --git a/Source/Editor/Windows/Profiler/Assets.cs b/Source/Editor/Windows/Profiler/Assets.cs index 0ad47735f..e4e41c668 100644 --- a/Source/Editor/Windows/Profiler/Assets.cs +++ b/Source/Editor/Windows/Profiler/Assets.cs @@ -34,6 +34,7 @@ namespace FlaxEditor.Windows.Profiler private List _tableRowsCache; private Dictionary _resourceCache; private StringBuilder _stringBuilder; + private Asset[] _assetsCache; public Assets() : base("Assets") @@ -138,12 +139,12 @@ namespace FlaxEditor.Windows.Profiler _stringBuilder = new StringBuilder(); // Capture current assets usage info - var assets = FlaxEngine.Content.Assets; - var resources = new Resource[assets.Length]; + FlaxEngine.Content.GetAssets(ref _assetsCache, out var count); + var resources = new Resource[count]; 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]; if (!asset) continue; @@ -179,6 +180,7 @@ namespace FlaxEditor.Windows.Profiler if (_resources == null) _resources = new SamplesBuffer(); _resources.Add(resources); + Array.Clear(_assetsCache); } /// @@ -200,6 +202,7 @@ namespace FlaxEditor.Windows.Profiler _resourceCache?.Clear(); _tableRowsCache?.Clear(); _stringBuilder?.Clear(); + _assetsCache = null; base.OnDestroy(); } diff --git a/Source/Editor/Windows/Profiler/MemoryGPU.cs b/Source/Editor/Windows/Profiler/MemoryGPU.cs index acaeca364..ce266777d 100644 --- a/Source/Editor/Windows/Profiler/MemoryGPU.cs +++ b/Source/Editor/Windows/Profiler/MemoryGPU.cs @@ -35,6 +35,7 @@ namespace FlaxEditor.Windows.Profiler private Dictionary _assetPathToId; private Dictionary _resourceCache; private StringBuilder _stringBuilder; + private GPUResource[] _gpuResourcesCached; public MemoryGPU() : base("GPU Memory") @@ -138,12 +139,12 @@ namespace FlaxEditor.Windows.Profiler // Capture current GPU resources usage info var contentDatabase = Editor.Instance.ContentDatabase; - var gpuResources = GPUDevice.Instance.Resources; - var resources = new Resource[gpuResources.Length]; + GPUDevice.Instance.GetResources(ref _gpuResourcesCached, out var count); + var resources = new Resource[count]; 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]; // Try to reuse cached resource info @@ -219,6 +220,7 @@ namespace FlaxEditor.Windows.Profiler if (_resources == null) _resources = new SamplesBuffer(); _resources.Add(resources); + Array.Clear(_gpuResourcesCached); } /// @@ -255,6 +257,7 @@ namespace FlaxEditor.Windows.Profiler _assetPathToId?.Clear(); _tableRowsCache?.Clear(); _stringBuilder?.Clear(); + _gpuResourcesCached = null; base.OnDestroy(); } diff --git a/Source/Engine/Content/Content.cs b/Source/Engine/Content/Content.cs index 4f07e1dc6..010abbc56 100644 --- a/Source/Engine/Content/Content.cs +++ b/Source/Engine/Content/Content.cs @@ -21,6 +21,20 @@ namespace FlaxEngine } } + /// + /// Gets the assets (loaded or during load). + /// + /// 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 for actual item count.> + /// Amount of valid items inside . + public static void GetAssets(ref Asset[] buffer, out int count) + { + count = 0; + IntPtr ptr = Internal_GetAssetsInternal(); + ManagedArray array = Unsafe.As(ManagedHandle.FromIntPtr(ptr).Target); + buffer = NativeInterop.GCHandleArrayToManagedArray(array, buffer); + count = buffer.Length; + } + /// /// 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. /// diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 8138d3604..7d16b4752 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -195,17 +195,19 @@ namespace FlaxEngine.Interop /// /// Array element type. /// Input array. + /// Cached memory allocation buffer to use for the result (if size fits). /// Output array. - public static T[] GCHandleArrayToManagedArray(ManagedArray ptrArray) where T : class + public static T[] GCHandleArrayToManagedArray(ManagedArray ptrArray, T[] buffer = null) where T : class { Span span = ptrArray.ToSpan(); - T[] managedArray = new T[ptrArray.Length]; - for (int i = 0; i < managedArray.Length; i++) + if (buffer == null || buffer.Length < ptrArray.Length) + buffer = new T[ptrArray.Length]; + for (int i = 0; i < ptrArray.Length; 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; } /// diff --git a/Source/Engine/Graphics/GPUBufferDescription.cs b/Source/Engine/Graphics/GPUBufferDescription.cs index 9e52876fc..107d17b3e 100644 --- a/Source/Engine/Graphics/GPUBufferDescription.cs +++ b/Source/Engine/Graphics/GPUBufferDescription.cs @@ -20,6 +20,20 @@ namespace FlaxEngine return NativeInterop.GCHandleArrayToManagedArray(array); } } + + /// + /// Gets the list with all active GPU resources. + /// + /// 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 for actual item count.> + /// Amount of valid items inside . + public void GetResources(ref GPUResource[] buffer, out int count) + { + count = 0; + IntPtr ptr = Internal_GetResourcesInternal(__unmanagedPtr); + ManagedArray array = Unsafe.As(ManagedHandle.FromIntPtr(ptr).Target); + buffer = NativeInterop.GCHandleArrayToManagedArray(array, buffer); + count = buffer.Length; + } } partial struct GPUBufferDescription : IEquatable