Add more improvements to usability of memory profiler

This commit is contained in:
Wojtek Figat
2025-06-06 22:40:43 +02:00
parent e8b60060ab
commit 091f76bbf2
8 changed files with 63 additions and 20 deletions

View File

@@ -232,7 +232,7 @@ namespace FlaxEditor.Windows.Profiler
Array.Sort(_groupOrder, (x, y) =>
{
var tmp = _frames.Get(selectedFrame);
return (int)(tmp.Usage.Values0[y] - tmp.Usage.Values0[x]);
return tmp.Usage.Values0[y].CompareTo(tmp.Usage.Values0[x]);
});
// Add rows

View File

@@ -400,6 +400,7 @@ void Foliage::DrawClusterGlobalSA(GlobalSurfaceAtlasPass* globalSA, const Vector
void Foliage::DrawFoliageJob(int32 i)
{
PROFILE_CPU();
PROFILE_MEM(Graphics);
const FoliageType& type = FoliageTypes[i];
if (type.IsReady() && type.Model->CanBeRendered())
{
@@ -551,6 +552,7 @@ FoliageType* Foliage::GetFoliageType(int32 index)
void Foliage::AddFoliageType(Model* model)
{
PROFILE_CPU();
PROFILE_MEM(LevelFoliage);
// Ensure to have unique model
CHECK(model);
@@ -629,6 +631,7 @@ int32 Foliage::GetFoliageTypeInstancesCount(int32 index) const
void Foliage::AddInstance(const FoliageInstance& instance)
{
PROFILE_MEM(LevelFoliage);
ASSERT(instance.Type >= 0 && instance.Type < FoliageTypes.Count());
auto type = &FoliageTypes[instance.Type];
@@ -705,6 +708,7 @@ void Foliage::OnFoliageTypeModelLoaded(int32 index)
if (_disableFoliageTypeEvents)
return;
PROFILE_CPU();
PROFILE_MEM(LevelFoliage);
auto& type = FoliageTypes[index];
ASSERT(type.IsReady());
@@ -803,6 +807,7 @@ void Foliage::OnFoliageTypeModelLoaded(int32 index)
void Foliage::RebuildClusters()
{
PROFILE_CPU();
PROFILE_MEM(LevelFoliage);
// Faster path if foliage is empty or no types is ready
bool anyTypeReady = false;
@@ -1328,6 +1333,7 @@ void Foliage::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
Actor::Deserialize(stream, modifier);
PROFILE_CPU();
PROFILE_MEM(LevelFoliage);
// Clear
#if FOLIAGE_USE_SINGLE_QUAD_TREE

View File

@@ -4,6 +4,7 @@
#include "Engine/Core/Collections/ArrayExtensions.h"
#include "Engine/Core/Random.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Foliage.h"
FoliageType::FoliageType()
@@ -62,6 +63,7 @@ Array<MaterialBase*> FoliageType::GetMaterials() const
void FoliageType::SetMaterials(const Array<MaterialBase*>& value)
{
PROFILE_MEM(LevelFoliage);
CHECK(value.Count() == Entries.Count());
for (int32 i = 0; i < value.Count(); i++)
Entries[i].Material = value[i];
@@ -114,6 +116,8 @@ void FoliageType::OnModelChanged()
void FoliageType::OnModelLoaded()
{
PROFILE_MEM(LevelFoliage);
// Now it's ready
_isReady = 1;
@@ -169,6 +173,7 @@ void FoliageType::Serialize(SerializeStream& stream, const void* otherObj)
void FoliageType::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
PROFILE_MEM(LevelFoliage);
DESERIALIZE(Model);
const auto member = stream.FindMember("Materials");

View File

@@ -21,6 +21,7 @@
#define USE_TRACY_MEMORY_PLOTS (defined(TRACY_ENABLE))
static_assert(GROUPS_COUNT <= MAX_uint8, "Fix memory profiler groups to fit a single byte.");
static_assert(sizeof(ProfilerMemory::Groups) == sizeof(uint8), "Fix memory profiler groups to fit a single byte.");
// Compact name storage.
struct GroupNameBuffer
@@ -32,17 +33,17 @@ struct GroupNameBuffer
void Set(const T* str, bool autoFormat = false)
{
int32 max = StringUtils::Length(str), dst = 0;
char prev = 0;
T prev = 0;
for (int32 i = 0; i < max && dst < ARRAY_COUNT(Buffer) - 2; i++)
{
char cur = (char)str[i];
T cur = (T)str[i];
if (autoFormat && StringUtils::IsUpper(cur) && StringUtils::IsLower(prev))
{
Ansi[dst] = '/';
Buffer[dst++] = '/';
}
Ansi[dst] = cur;
Buffer[dst++] = cur;
Ansi[dst] = (char)cur;
Buffer[dst++] = (Char)cur;
prev = cur;
}
Buffer[dst] = 0;
@@ -257,6 +258,8 @@ void InitProfilerMemory(const Char* cmdLine, int32 stage)
INIT_PARENT(Animations, AnimationsData);
INIT_PARENT(Content, ContentAssets);
INIT_PARENT(Content, ContentFiles);
INIT_PARENT(Level, LevelFoliage);
INIT_PARENT(Level, LevelTerrain);
INIT_PARENT(Scripting, ScriptingVisual);
INIT_PARENT(Scripting, ScriptingCSharp);
INIT_PARENT(ScriptingCSharp, ScriptingCSharpGCCommitted);
@@ -403,10 +406,10 @@ ProfilerMemory::GroupsArray ProfilerMemory::GetGroups(int32 mode)
void ProfilerMemory::Dump(const StringView& options)
{
#if LOG_ENABLE
bool file = options.Contains(TEXT("file"));
bool file = options.Contains(TEXT("file"), StringSearchCase::IgnoreCase);
StringBuilder output;
int32 maxCount = 20;
if (file || options.Contains(TEXT("all")))
if (file || options.Contains(TEXT("all"), StringSearchCase::IgnoreCase))
maxCount = MAX_int32;
::Dump(output, maxCount);
if (file)
@@ -476,10 +479,10 @@ void ProfilerMemory::OnMemoryFree(void* ptr)
stack.SkipRecursion = false;
}
void ProfilerMemory::OnGroupUpdate(Groups group, int64 sizeDelta, int64 countDetla)
void ProfilerMemory::OnGroupUpdate(Groups group, int64 sizeDelta, int64 countDelta)
{
Platform::InterlockedAdd(&GroupMemory[(int32)group], sizeDelta);
Platform::InterlockedAdd(&GroupMemoryCount[(int32)group], countDetla);
Platform::InterlockedAdd(&GroupMemoryCount[(int32)group], countDelta);
UPDATE_PEEK(group);
}

View File

@@ -61,7 +61,7 @@ public:
GraphicsVertexBuffers,
// Total index buffers memory usage.
GraphicsIndexBuffers,
// Total meshes memory usage (vertex and idnex buffers allocated by models).
// Total meshes memory usage (vertex and index buffers allocated by models).
GraphicsMeshes,
// Totoal shaders memory usage (shaders bytecode, PSOs data).
GraphicsShaders,
@@ -78,7 +78,7 @@ public:
// Total animation data memory usage (curves, events, keyframes, graphs, etc.).
AnimationsData,
// Total autio system memory.
// Total audio system memory.
Audio,
// Total content system memory usage.
@@ -90,11 +90,15 @@ public:
// Total memory used by content streaming system (internals).
ContentStreaming,
// Total memory allocated by input system.
Input,
// Total memory allocated by scene objects.
Level,
// Total memory allocated by the foliage system (quad-tree, foliage instances data). Excluding foliage models data.
LevelFoliage,
// Total memory allocated by the terrain system (patches).
LevelTerrain,
// Total memory allocated by input system.
Input,
// Total localization system memory.
Localization,
@@ -148,7 +152,7 @@ public:
CustomGame8,
// Custom game-specific memory tracking.
CustomGame9,
// Custom plugin-specific memory tracking.
CustomPlugin0,
// Custom plugin-specific memory tracking.
@@ -186,7 +190,7 @@ public:
};
/// <summary>
/// The memory groups array wraper to avoid dynamic memory allocation.
/// The memory groups array wrapper to avoid dynamic memory allocation.
/// </summary>
API_STRUCT(NoDefault) struct GroupsArray
{
@@ -254,7 +258,7 @@ public:
static void OnMemoryAlloc(void* ptr, uint64 size);
static void OnMemoryFree(void* ptr);
static void OnGroupUpdate(Groups group, int64 sizeDelta, int64 countDetla);
static void OnGroupUpdate(Groups group, int64 sizeDelta, int64 countDelta);
public:
/// <summary>

View File

@@ -16,6 +16,7 @@
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
#include "Engine/Renderer/GI/GlobalSurfaceAtlasPass.h"
@@ -290,6 +291,7 @@ void Terrain::SetCollisionLOD(int32 value)
void Terrain::SetPhysicalMaterials(const Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>>& value)
{
PROFILE_MEM(LevelTerrain);
_physicalMaterials = value;
_physicalMaterials.Resize(8);
JsonAsset* materials[8];
@@ -431,6 +433,7 @@ void Terrain::Setup(int32 lodCount, int32 chunkSize)
void Terrain::AddPatches(const Int2& numberOfPatches)
{
PROFILE_MEM(LevelTerrain);
if (_chunkSize == 0)
Setup();
_patches.ClearDelete();
@@ -470,6 +473,7 @@ void Terrain::AddPatch(const Int2& patchCoord)
LOG(Warning, "Cannot add patch at {0}x{1}. The patch at the given location already exists.", patchCoord.X, patchCoord.Y);
return;
}
PROFILE_MEM(LevelTerrain);
if (_chunkSize == 0)
Setup();
@@ -726,6 +730,8 @@ void Terrain::Serialize(SerializeStream& stream, const void* otherObj)
void Terrain::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
PROFILE_MEM(LevelTerrain);
// Base
Actor::Deserialize(stream, modifier);

View File

@@ -5,16 +5,17 @@
#include "Engine/Threading/Threading.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/GPUBuffer.h"
#include "Engine/Graphics/Shaders/GPUVertexLayout.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Collections/ChunkedArray.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Content/Content.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/MaterialBase.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Core/Log.h"
#include "Engine/Graphics/Shaders/GPUVertexLayout.h"
#include "Engine/Renderer/DrawCall.h"
#include "Engine/Profiler/ProfilerMemory.h"
// Must match structure defined in Terrain.shader
struct TerrainVertex
@@ -94,6 +95,7 @@ bool TerrainManager::GetChunkGeometry(DrawCall& drawCall, int32 chunkSize, int32
data->GetChunkGeometry(drawCall);
return false;
}
PROFILE_MEM(LevelTerrain);
// Prepare
const int32 vertexCount = (chunkSize + 1) >> lodIndex;

View File

@@ -6,6 +6,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Profiler/ProfilerMemory.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Physics/PhysicsBackend.h"
@@ -66,6 +67,7 @@ TerrainPatch::TerrainPatch(const SpawnParams& params)
void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z)
{
PROFILE_MEM(LevelTerrain);
ScopeLock lock(_collisionLocker);
_terrain = terrain;
@@ -823,6 +825,7 @@ bool ModifyCollision(TerrainDataUpdateInfo& info, TextureBase::InitData* initDat
bool TerrainPatch::SetupHeightMap(int32 heightMapLength, const float* heightMap, const byte* holesMask, bool forceUseVirtualStorage)
{
PROFILE_CPU_NAMED("Terrain.Setup");
PROFILE_MEM(LevelTerrain);
if (heightMap == nullptr)
{
LOG(Warning, "Cannot create terrain without a heightmap specified.");
@@ -1034,6 +1037,7 @@ bool TerrainPatch::SetupHeightMap(int32 heightMapLength, const float* heightMap,
bool TerrainPatch::SetupSplatMap(int32 index, int32 splatMapLength, const Color32* splatMap, bool forceUseVirtualStorage)
{
PROFILE_CPU_NAMED("Terrain.SetupSplatMap");
PROFILE_MEM(LevelTerrain);
CHECK_RETURN(index >= 0 && index < TERRAIN_MAX_SPLATMAPS_COUNT, true);
if (splatMap == nullptr)
{
@@ -1182,6 +1186,7 @@ bool TerrainPatch::SetupSplatMap(int32 index, int32 splatMapLength, const Color3
bool TerrainPatch::InitializeHeightMap()
{
PROFILE_CPU_NAMED("Terrain.InitializeHeightMap");
PROFILE_MEM(LevelTerrain);
const auto heightmapSize = _terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
Array<float> heightmap;
heightmap.Resize(heightmapSize * heightmapSize);
@@ -1248,6 +1253,7 @@ void TerrainPatch::ClearCache()
void TerrainPatch::CacheHeightData()
{
PROFILE_CPU_NAMED("Terrain.CacheHeightData");
PROFILE_MEM(LevelTerrain);
const TerrainDataUpdateInfo info(this);
// Ensure that heightmap data is all loaded
@@ -1313,6 +1319,7 @@ void TerrainPatch::CacheHeightData()
void TerrainPatch::CacheSplatData()
{
PROFILE_CPU_NAMED("Terrain.CacheSplatData");
PROFILE_MEM(LevelTerrain);
const TerrainDataUpdateInfo info(this);
// Cache all the splatmaps
@@ -1396,6 +1403,7 @@ bool TerrainPatch::ModifyHeightMap(const float* samples, const Int2& modifiedOff
return true;
}
PROFILE_CPU_NAMED("Terrain.ModifyHeightMap");
PROFILE_MEM(LevelTerrain);
// Check if has no heightmap
if (Heightmap == nullptr)
@@ -1490,6 +1498,7 @@ bool TerrainPatch::ModifyHolesMask(const byte* samples, const Int2& modifiedOffs
return true;
}
PROFILE_CPU_NAMED("Terrain.ModifyHolesMask");
PROFILE_MEM(LevelTerrain);
// Check if has no heightmap
if (Heightmap == nullptr)
@@ -1567,6 +1576,7 @@ bool TerrainPatch::ModifySplatMap(int32 index, const Color32* samples, const Int
return true;
}
PROFILE_CPU_NAMED("Terrain.ModifySplatMap");
PROFILE_MEM(LevelTerrain);
// Get the current data to modify it
Color32* splatMap = GetSplatMapData(index);
@@ -1738,6 +1748,7 @@ bool TerrainPatch::ModifySplatMap(int32 index, const Color32* samples, const Int
bool TerrainPatch::UpdateHeightData(TerrainDataUpdateInfo& info, const Int2& modifiedOffset, const Int2& modifiedSize, bool wasHeightRangeChanged, bool wasHeightChanged)
{
PROFILE_CPU();
PROFILE_MEM(LevelTerrain);
float* heightMap = GetHeightmapData();
byte* holesMask = GetHolesMaskData();
ASSERT(heightMap && holesMask);
@@ -2126,6 +2137,7 @@ void TerrainPatch::UpdatePostManualDeserialization()
void TerrainPatch::CreateCollision()
{
PROFILE_CPU();
PROFILE_MEM(LevelTerrain);
ASSERT(!HasCollision());
if (CreateHeightField())
return;
@@ -2241,6 +2253,7 @@ void TerrainPatch::DestroyCollision()
void TerrainPatch::CacheDebugLines()
{
PROFILE_CPU();
PROFILE_MEM(LevelTerrain);
ASSERT(_physicsHeightField);
_debugLinesDirty = false;
if (!_debugLines)
@@ -2322,6 +2335,7 @@ void TerrainPatch::DrawPhysicsDebug(RenderView& view)
const BoundingBox bounds(_bounds.Minimum - view.Origin, _bounds.Maximum - view.Origin);
if (!_physicsShape || !view.CullingFrustum.Intersects(bounds))
return;
PROFILE_MEM(LevelTerrain);
if (view.Mode == ViewMode::PhysicsColliders)
{
const auto& triangles = GetCollisionTriangles();
@@ -2378,6 +2392,7 @@ const Array<Vector3>& TerrainPatch::GetCollisionTriangles()
if (!_physicsShape || _collisionTriangles.HasItems())
return _collisionTriangles;
PROFILE_CPU();
PROFILE_MEM(LevelTerrain);
int32 rows, cols;
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
@@ -2428,6 +2443,7 @@ const Array<Vector3>& TerrainPatch::GetCollisionTriangles()
void TerrainPatch::GetCollisionTriangles(const BoundingSphere& bounds, Array<Vector3>& result)
{
PROFILE_CPU();
PROFILE_MEM(LevelTerrain);
result.Clear();
// Skip if no intersection with patch
@@ -2525,6 +2541,7 @@ void TerrainPatch::GetCollisionTriangles(const BoundingSphere& bounds, Array<Vec
void TerrainPatch::ExtractCollisionGeometry(Array<Float3>& vertexBuffer, Array<int32>& indexBuffer)
{
PROFILE_CPU();
PROFILE_MEM(LevelTerrain);
vertexBuffer.Clear();
indexBuffer.Clear();