diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp index ea58f3240..8f33d04d7 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.cpp +++ b/Source/Editor/Scripting/ScriptsBuilder.cpp @@ -664,6 +664,9 @@ bool ScriptsBuilderService::Init() void ScriptsBuilderService::Update() { + PROFILE_CPU(); + PROFILE_MEM(Editor); + // Send compilation events { ScopeLock scopeLock(_compileEventsLocker); diff --git a/Source/Engine/Audio/AudioClip.cpp b/Source/Engine/Audio/AudioClip.cpp index 2ac9d218c..2ca7f3512 100644 --- a/Source/Engine/Audio/AudioClip.cpp +++ b/Source/Engine/Audio/AudioClip.cpp @@ -19,6 +19,7 @@ REGISTER_BINARY_ASSET_WITH_UPGRADER(AudioClip, "FlaxEngine.AudioClip", AudioClip bool AudioClip::StreamingTask::Run() { + PROFILE_MEM(Audio); AssetReference ref = _asset.Get(); if (ref == nullptr || AudioBackend::Instance == nullptr) return true; diff --git a/Source/Engine/Content/JsonAsset.cpp b/Source/Engine/Content/JsonAsset.cpp index 04487eb65..1aa434c41 100644 --- a/Source/Engine/Content/JsonAsset.cpp +++ b/Source/Engine/Content/JsonAsset.cpp @@ -310,6 +310,7 @@ Asset::LoadResult JsonAssetBase::loadAsset() void JsonAssetBase::unload(bool isReloading) { + PROFILE_MEM(ContentAssets); ISerializable::SerializeDocument tmp; Document.Swap(tmp); Data = nullptr; diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 7692f375e..7c1e2e64a 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -1277,6 +1277,14 @@ namespace FlaxEngine.Interop return GC.MaxGeneration; } + [UnmanagedCallersOnly] + internal static void GCMemoryInfo(long* totalCommitted, long* heapSize) + { + GCMemoryInfo gcMemoryInfo = GC.GetGCMemoryInfo(); + *totalCommitted = gcMemoryInfo.TotalCommittedBytes; + *heapSize = gcMemoryInfo.HeapSizeBytes; + } + [UnmanagedCallersOnly] internal static void GCWaitForPendingFinalizers() { diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 5bcd98994..1233282be 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -813,6 +813,7 @@ bool LevelImpl::unloadScene(Scene* scene) bool LevelImpl::unloadScenes() { + PROFILE_MEM(Level); auto scenes = Level::Scenes; for (int32 i = scenes.Count() - 1; i >= 0; i--) { diff --git a/Source/Engine/Navigation/NavCrowd.cpp b/Source/Engine/Navigation/NavCrowd.cpp index cb2a4ebee..ed7f3ba7f 100644 --- a/Source/Engine/Navigation/NavCrowd.cpp +++ b/Source/Engine/Navigation/NavCrowd.cpp @@ -7,12 +7,14 @@ #include "Engine/Level/Level.h" #include "Engine/Level/Scene/Scene.h" #include "Engine/Profiler/ProfilerCPU.h" +#include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Threading/Threading.h" #include NavCrowd::NavCrowd(const SpawnParams& params) : ScriptingObject(params) { + PROFILE_MEM(Navigation); _crowd = dtAllocCrowd(); } @@ -51,6 +53,7 @@ bool NavCrowd::Init(float maxAgentRadius, int32 maxAgents, NavMeshRuntime* navMe if (!_crowd || !navMesh) return true; PROFILE_CPU(); + PROFILE_MEM(Navigation); // This can happen on game start when no navmesh is loaded yet (eg. navmesh tiles data is during streaming) so wait for navmesh if (navMesh->GetNavMesh() == nullptr) @@ -175,6 +178,7 @@ void NavCrowd::RemoveAgent(int32 id) void NavCrowd::Update(float dt) { PROFILE_CPU(); + PROFILE_MEM(Navigation); _crowd->update(Math::Max(dt, ZeroTolerance), nullptr); } diff --git a/Source/Engine/Profiler/ProfilerMemory.cpp b/Source/Engine/Profiler/ProfilerMemory.cpp index a18004fc1..61433c0e7 100644 --- a/Source/Engine/Profiler/ProfilerMemory.cpp +++ b/Source/Engine/Profiler/ProfilerMemory.cpp @@ -12,6 +12,7 @@ #include "Engine/Platform/MemoryStats.h" #include "Engine/Platform/File.h" #include "Engine/Scripting/Enums.h" +#include "Engine/Scripting/ManagedCLR/MCore.h" #include "Engine/Threading/ThreadLocal.h" #include "Engine/Utilities/StringConverter.h" #include @@ -24,8 +25,8 @@ static_assert(GROUPS_COUNT <= MAX_uint8, "Fix memory profiler groups to fit a si // Compact name storage. struct GroupNameBuffer { - Char Buffer[30]; - char Ansi[30]; + Char Buffer[40]; + char Ansi[40]; template void Set(const T* str, bool autoFormat = false) @@ -248,6 +249,10 @@ void InitProfilerMemory(const Char* cmdLine, int32 stage) INIT_PARENT(Animations, AnimationsData); INIT_PARENT(Content, ContentAssets); INIT_PARENT(Content, ContentFiles); + INIT_PARENT(Scripting, ScriptingVisual); + INIT_PARENT(Scripting, ScriptingCSharp); + INIT_PARENT(ScriptingCSharp, ScriptingCSharpGCCommitted); + INIT_PARENT(ScriptingCSharp, ScriptingCSharpGCHeap); #undef INIT_PARENT // Init group names @@ -262,6 +267,8 @@ void InitProfilerMemory(const Char* cmdLine, int32 stage) RENAME_GROUP(GraphicsVolumeTextures, "Graphics/VolumeTextures"); RENAME_GROUP(GraphicsVertexBuffers, "Graphics/VertexBuffers"); RENAME_GROUP(GraphicsIndexBuffers, "Graphics/IndexBuffers"); + RENAME_GROUP(ScriptingCSharpGCCommitted, "Scripting/CSharp/GC/Committed"); + RENAME_GROUP(ScriptingCSharpGCHeap, "Scripting/CSharp/GC/Heap"); #undef RENAME_GROUP // Init Tracy @@ -291,10 +298,23 @@ void InitProfilerMemory(const Char* cmdLine, int32 stage) void TickProfilerMemory() { + // Update .NET GC memory stats + int64 totalCommitted, heapSize; + MCore::GC::MemoryInfo(totalCommitted, heapSize); + int64 gcComittedDelta = totalCommitted - GroupMemory[(int32)ProfilerMemory::Groups::ScriptingCSharpGCCommitted]; + GroupMemory[(int32)ProfilerMemory::Groups::ScriptingCSharpGCCommitted] = totalCommitted; + GroupMemory[(int32)ProfilerMemory::Groups::ScriptingCSharpGCHeap] = heapSize; + UPDATE_PEEK(ProfilerMemory::Groups::ScriptingCSharpGCCommitted); + UPDATE_PEEK(ProfilerMemory::Groups::ScriptingCSharpGCHeap); + Platform::InterlockedAdd(&GroupMemory[(int32)ProfilerMemory::Groups::TotalTracked], gcComittedDelta); + // Update profiler memory PointersLocker.Lock(); GroupMemory[(int32)ProfilerMemory::Groups::Profiler] = sizeof(GroupMemory) + sizeof(GroupNames) + sizeof(GroupStack) + +#ifdef USE_TRACY_MEMORY_PLOTS + sizeof(GroupTracyPlotEnable) + +#endif Pointers.Capacity() * sizeof(Dictionary::Bucket); PointersLocker.Unlock(); diff --git a/Source/Engine/Profiler/ProfilerMemory.h b/Source/Engine/Profiler/ProfilerMemory.h index a9dedadad..1b1ad5ac3 100644 --- a/Source/Engine/Profiler/ProfilerMemory.h +++ b/Source/Engine/Profiler/ProfilerMemory.h @@ -110,6 +110,12 @@ public: Scripting, // Total Visual scripting memory allocated by game (visual script graphs, data and runtime allocations). ScriptingVisual, + // Total C# scripting memory allocated by game (runtime assemblies, managed interop and runtime allocations). + ScriptingCSharp, + // Total amount of committed virtual memory in use by the .NET GC, as observed during the latest garbage collection. + ScriptingCSharpGCCommitted, + // Total managed GC heap size (including fragmentation), as observed during the latest garbage collection. + ScriptingCSharpGCHeap, // Total User Interface components memory. UI, diff --git a/Source/Engine/Renderer/AtmospherePreCompute.cpp b/Source/Engine/Renderer/AtmospherePreCompute.cpp index 81cee0cb6..595ebcca5 100644 --- a/Source/Engine/Renderer/AtmospherePreCompute.cpp +++ b/Source/Engine/Renderer/AtmospherePreCompute.cpp @@ -342,6 +342,9 @@ void AtmospherePreComputeService::Update() } else if (_isUpdatePending && (_task == nullptr || !_task->Enabled)) { + PROFILE_CPU(); + PROFILE_MEM(Graphics); + // TODO: init but without a stalls, just wait for resources loaded and then start rendering // Init service diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index da01a9f07..ef76de61c 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -763,7 +763,7 @@ ManagedBinaryModule* ManagedBinaryModule::GetModule(const MAssembly* assembly) ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSpawnParams& params) { - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); // Create native object ScriptingTypeHandle managedTypeHandle = params.Type; @@ -935,7 +935,7 @@ void ManagedBinaryModule::OnLoaded(MAssembly* assembly) { #if !COMPILE_WITHOUT_CSHARP PROFILE_CPU(); - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ASSERT(ClassToTypeIndex.IsEmpty()); ScopeLock lock(Locker); @@ -1032,7 +1032,7 @@ void ManagedBinaryModule::InitType(MClass* mclass) const StringAnsiView typeName = mclass->GetFullName(); if (TypeNameToTypeIndex.ContainsKey(typeName)) return; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); // Find first native base C++ class of this C# class MClass* baseClass = mclass->GetBaseClass(); @@ -1192,7 +1192,7 @@ void ManagedBinaryModule::OnUnloading(MAssembly* assembly) void ManagedBinaryModule::OnUnloaded(MAssembly* assembly) { PROFILE_CPU(); - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); // Clear managed-only types Types.Resize(_firstManagedTypeIndex); diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.cpp b/Source/Engine/Scripting/ManagedCLR/MCore.cpp index 675e09ddf..0b38730e8 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MCore.cpp @@ -92,7 +92,7 @@ bool MAssembly::Load(const String& assemblyPath, const StringView& nativePath) if (IsLoaded()) return false; PROFILE_CPU(); - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ZoneText(*assemblyPath, assemblyPath.Length()); Stopwatch stopwatch; diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.h b/Source/Engine/Scripting/ManagedCLR/MCore.h index 549dfadf6..cedebe7b8 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.h +++ b/Source/Engine/Scripting/ManagedCLR/MCore.h @@ -122,6 +122,7 @@ public: static void Collect(int32 generation); static void Collect(int32 generation, MGCCollectionMode collectionMode, bool blocking, bool compacting); static int32 MaxGeneration(); + static void MemoryInfo(int64& totalCommitted, int64& heapSize); static void WaitForPendingFinalizers(); static void WriteRef(void* ptr, MObject* ref); static void WriteValue(void* dst, void* src, int32 count, const MClass* klass); diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 24e07859d..5b67670e5 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -282,7 +282,7 @@ void MCore::UnloadDomain(const StringAnsi& domainName) bool MCore::LoadEngine() { PROFILE_CPU(); - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); // Initialize hostfxr if (InitHostfxr()) @@ -550,6 +550,12 @@ int32 MCore::GC::MaxGeneration() return maxGeneration; } +void MCore::GC::MemoryInfo(int64& totalCommitted, int64& heapSize) +{ + static void* GCMemoryInfoPtr = GetStaticMethodPointer(TEXT("GCMemoryInfo")); + CallStaticMethod(GCMemoryInfoPtr, &totalCommitted, &heapSize); +} + void MCore::GC::WaitForPendingFinalizers() { PROFILE_CPU(); @@ -737,7 +743,7 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const if (_hasCachedClasses || !IsLoaded()) return _classes; PROFILE_CPU(); - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); Stopwatch stopwatch; #if TRACY_ENABLE @@ -800,7 +806,7 @@ void GetAssemblyName(void* assemblyHandle, StringAnsi& name, StringAnsi& fullnam DEFINE_INTERNAL_CALL(void) NativeInterop_CreateClass(NativeClassDefinitions* managedClass, void* assemblyHandle) { - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ScopeLock lock(BinaryModule::Locker); MAssembly* assembly = GetAssembly(assemblyHandle); if (assembly == nullptr) @@ -836,7 +842,7 @@ bool MAssembly::LoadCorlib() if (IsLoaded()) return false; PROFILE_CPU(); - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); #if TRACY_ENABLE const StringAnsiView name("Corlib"); ZoneText(*name, name.Length()); @@ -1060,7 +1066,7 @@ const Array& MClass::GetMethods() const { if (_hasCachedMethods) return _methods; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ScopeLock lock(BinaryModule::Locker); if (_hasCachedMethods) return _methods; @@ -1099,7 +1105,7 @@ const Array& MClass::GetFields() const { if (_hasCachedFields) return _fields; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ScopeLock lock(BinaryModule::Locker); if (_hasCachedFields) return _fields; @@ -1126,7 +1132,7 @@ const Array& MClass::GetEvents() const { if (_hasCachedEvents) return _events; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); // TODO: implement MEvent in .NET @@ -1149,7 +1155,7 @@ const Array& MClass::GetProperties() const { if (_hasCachedProperties) return _properties; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ScopeLock lock(BinaryModule::Locker); if (_hasCachedProperties) return _properties; @@ -1176,7 +1182,7 @@ const Array& MClass::GetInterfaces() const { if (_hasCachedInterfaces) return _interfaces; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ScopeLock lock(BinaryModule::Locker); if (_hasCachedInterfaces) return _interfaces; @@ -1216,7 +1222,7 @@ const Array& MClass::GetAttributes() const { if (_hasCachedAttributes) return _attributes; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ScopeLock lock(BinaryModule::Locker); if (_hasCachedAttributes) return _attributes; @@ -1401,7 +1407,7 @@ const Array& MField::GetAttributes() const { if (_hasCachedAttributes) return _attributes; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ScopeLock lock(BinaryModule::Locker); if (_hasCachedAttributes) return _attributes; @@ -1467,7 +1473,7 @@ void MMethod::CacheSignature() const ScopeLock lock(BinaryModule::Locker); if (_hasCachedSignature) return; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); static void* GetMethodReturnTypePtr = GetStaticMethodPointer(TEXT("GetMethodReturnType")); static void* GetMethodParameterTypesPtr = GetStaticMethodPointer(TEXT("GetMethodParameterTypes")); @@ -1568,7 +1574,7 @@ const Array& MMethod::GetAttributes() const { if (_hasCachedAttributes) return _attributes; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ScopeLock lock(BinaryModule::Locker); if (_hasCachedAttributes) return _attributes; @@ -1658,7 +1664,7 @@ const Array& MProperty::GetAttributes() const { if (_hasCachedAttributes) return _attributes; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ScopeLock lock(BinaryModule::Locker); if (_hasCachedAttributes) return _attributes; @@ -1689,7 +1695,7 @@ MClass* GetOrCreateClass(MType* typeHandle) { if (!typeHandle) return nullptr; - PROFILE_MEM(Scripting); + PROFILE_MEM(ScriptingCSharp); ScopeLock lock(BinaryModule::Locker); MClass* klass; if (!CachedClassHandles.TryGet(typeHandle, klass)) @@ -1911,6 +1917,7 @@ void* GetStaticMethodPointer(StringView methodName) if (CachedFunctions.TryGet(methodName, fun)) return fun; PROFILE_CPU(); + PROFILE_MEM(ScriptingCSharp); const int rc = get_function_pointer(NativeInteropTypeName, FLAX_CORECLR_STRING(methodName).Get(), UNMANAGEDCALLERSONLY_METHOD, nullptr, nullptr, &fun); if (rc != 0) LOG(Fatal, "Failed to get unmanaged function pointer for method '{0}': 0x{1:x}", methodName, (unsigned int)rc); @@ -2278,6 +2285,7 @@ void* GetStaticMethodPointer(StringView methodName) if (CachedFunctions.TryGet(methodName, fun)) return fun; PROFILE_CPU(); + PROFILE_MEM(ScriptingCSharp); static MonoClass* nativeInteropClass = nullptr; if (!nativeInteropClass) diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp index 86b800e97..06392f932 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -864,6 +864,11 @@ int32 MCore::GC::MaxGeneration() return mono_gc_max_generation(); } +void MCore::GC::MemoryInfo(int64& totalCommitted, int64& heapSize) +{ + totalCommitted = heapSize = 0; +} + void MCore::GC::WaitForPendingFinalizers() { PROFILE_CPU(); diff --git a/Source/Engine/Scripting/Runtime/None.cpp b/Source/Engine/Scripting/Runtime/None.cpp index 580029eef..1ddaeae8e 100644 --- a/Source/Engine/Scripting/Runtime/None.cpp +++ b/Source/Engine/Scripting/Runtime/None.cpp @@ -190,6 +190,11 @@ int32 MCore::GC::MaxGeneration() return 0; } +void MCore::GC::MemoryInfo(int64& totalCommitted, int64& heapSize) +{ + totalCommitted = heapSize = 0; +} + void MCore::GC::WaitForPendingFinalizers() { } diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 8c27dc09f..3a69a7601 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -617,6 +617,7 @@ bool Scripting::Load() void Scripting::Release() { PROFILE_CPU(); + PROFILE_MEM(Scripting); // Note: this action can be called from main thread (due to Mono problems with assemblies actions from other threads) ASSERT(IsInMainThread());