From 8e40bdd92ce26d340c019334a03d3bb91b061bf9 Mon Sep 17 00:00:00 2001 From: Fr0zka Date: Sun, 13 Sep 2026 03:32:29 +0200 Subject: [PATCH] measure(perf): in-process stack sampler; the game spends ~3/4 of worker CPU on room player-fit A diagnostic stack-sampling profiler, off by default. A sampler thread captures the stacks of registered generation workers with FPlatformStackWalk::CaptureThreadStackBackTrace every N us and tags each sample with the tile's LOD. Symbolisation (inline frames included) happens after the run, into a bounded CSV plus a summary. Registration is per tile task, never per sample; when the sampler is off it costs one relaxed atomic load. Game: -voxel.SampleStacks=. Commandlet: -samplestacks[=us]. The streaming latency line now also prints applied tile, visible tile and triangle counts. Instrument checks (Codex, PERF-SAMPLED.md): - a planted 20 ms busy loop was found at 10.5% inclusive, the expected share, then removed - closure: 193k static / 251k moving samples, 0 capture failures, <0.75% wait frames, leaf resolved for 82-84% - static game output identical with the sampler on or off: 841 tiles, 447 visible, 669,834 triangles - overhead at 1 ms: +4.7% static worker CPU; moving worker CPU within noise, moving timing +4-5%. So trust large shares, not single-digit deltas. The finding, on the game path (the owner's instruction: profile the game, not the export): all-LOD inclusive static moving BuildChunkCache 79.5% 80.5% VF_FindPlayerFitPointForRoom 73.4% 74.0% About three quarters of in-game worker CPU goes to player-fit searches for rooms. They are recomputed on every per-worker room-cache rebuild, and coarse tiles span many cache windows. At LOD0 the costs are the cave warp (3 Perlin per sample), the swept tunnel evaluated both in the SDF and in EvaluateTunnelCoreWorld (~12% inclusive each, duplication confirmed), and the passage structural tail (~11-14%). Candidate AABB rejection is cheap (1.5-2.2%). The 74-field params copy does not show up as a separate cost. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VZS94UH8bSsBeg5sZaxe2Y --- CODEMAP.md | 1 + PERF-SAMPLED.md | 768 ++++++++++++ .../VoxelForge/Private/VoxelForgeModule.cpp | 3 +- .../VoxelForge/Private/VoxelStackSampler.cpp | 1051 +++++++++++++++++ Source/VoxelForge/Private/VoxelWorld.cpp | 52 +- Source/VoxelForge/Public/VoxelStackSampler.h | 117 ++ Source/VoxelForge/Public/VoxelWorld.h | 7 + .../Private/VoxelForgeExploreCommandlet.cpp | 84 ++ 8 files changed, 2079 insertions(+), 4 deletions(-) create mode 100644 PERF-SAMPLED.md create mode 100644 Source/VoxelForge/Private/VoxelStackSampler.cpp create mode 100644 Source/VoxelForge/Public/VoxelStackSampler.h diff --git a/CODEMAP.md b/CODEMAP.md index 2966d93..bafdc4e 100644 --- a/CODEMAP.md +++ b/CODEMAP.md @@ -301,6 +301,7 @@ casse cette arithmetique *structurellement* — `BeginPlay` loggue une **Error** | `GetPlayerPosition` | 231 | Pawn position or zero. (`GetLODForChunk`/`LODToStep`/`IsChunkInRange` removed — dead since the clipmap.) | | `ProcessPendingChunks` | 301 | Drains ProcessQueue under per-frame budget; reads the per-request cancellation token before removing its map entry, drops obsolete/aborted results, then applies each live result via `ApplyTileResult`. | | `ApplyTileResult` | — | **Shared game-thread apply** for one `FChunkResult` (async drain + sync carve): discards stale generation epochs and keys no longer desired, marks loaded, ingests capture, EMPTY releases the tile's existing component (a re-gen can flip content→empty on band change — old geometry must not linger), else `ApplyMeshToTile`. Returns true iff a visible mesh uploaded (counts the budget). Doesn't touch `PendingTiles` (caller's). | +| `FVoxelStackSampler` / `FScopedVoxelStackRegistration` (`VoxelStackSampler.h/.cpp`) | — | **Diagnostic stack-sampling profiler (2026-09-13), off by default.** A sampler thread captures registered generation workers' stacks (`CaptureThreadStackBackTrace`) every N µs and tags each with the tile's LOD. It symbolises after the run (inline frames too) into CSV plus a summary under `Saved/`. Registration is per tile TASK in `LoadTile` (and the export's tasks), never per sample; disabled costs one relaxed atomic load. Game `-voxel.SampleStacks=`, commandlet `-samplestacks[=us]`. ~4-5% overhead at 1 ms, so trust large shares, not single-digit deltas. Report: `PERF-SAMPLED.md`. | | `GenerateTileResult` | — | **Shared worker-side gen** for one tile (async `LoadTile` task + sync `SyncRemeshTile`): [ClassifyTile (T1.d) + exact validation, only when `voxel.OuterClassifierMode=1`; **default 0 since 2026-09-13**] → `GenerateMesh`/`GenerateSheetMesh` → `BuildTileStreamSet`. Reads Generator/Mesher only → safe on a worker or the game thread; fills `FChunkResult`, no enqueue. | | `SyncRemeshTile` | — | **INSTANT DIG**: level-0 same-frame re-mesh on the game thread (`GenerateTileResult` + `ApplyTileResult` inline, Cells=CHUNK_SIZE/Step=1 + strate band, no capture). Used for the tile under the brush centre so a carve is visible THIS frame; one full-res gen on the game thread. | | `UpdateChunksAroundPosition` | 362 | Builds desired set, cancels pending keys that left it, sorts from the **true pawn position**, submits the occupied/support LOD0 tile and the next tile along the pawn heading as an absolute `BackgroundHigh` prefix, then loads/unloads and handles LOD changes. **Delta cull**: `BuildDesiredTiles` returns the LEAVERS (stamped `DesiredStamped` map, one sweep) — only those + `TransitionHold` are considered per crossing, not every loaded tile. `BuildDesiredTiles` also applies `RenderDistanceChunks`: the outermost shell widens to cover the distance — as level-MaxClipLevel MC tiles, or (F18 `bFarSheetRing`) as a SHEET ring at level MaxClipLevel+span (covered-check vs the MaxClipLevel box; `VF_OuterShell` shared with `IsTileInClipRange` so the cull sees the same horizon), dz pre-clamped to the vertical band. **§9.3 anchors:** also prunes dead `StreamingAnchors` + detects their chunk crossings → rebuilds the desired set when an anchor moves/(un)registers (`bAnchorsMoved`/`bForceDesiredRebuild`), same cadence as player movement. §8.10. | diff --git a/PERF-SAMPLED.md b/PERF-SAMPLED.md new file mode 100644 index 0000000..2a8fd30 --- /dev/null +++ b/PERF-SAMPLED.md @@ -0,0 +1,768 @@ +# PERF-SAMPLED + +Measurement-only report for the in-process stack sampler, 2026-09-13. No generation, classifier, or field logic was optimized in this round. + +## Bottom line + +The sampler is useful: it samples registered generation workers without adding scopes to the density hot path, tags every capture with LOD, finds the planted cost, and exposes the game path's dominant work. It is not yet an acceptance-clean low-overhead instrument at the requested default interval. Static timing and worker CPU move beyond the two-run off baseline; moving worker CPU is within noise, but moving request/generation timing is not. + +The headline game result is that LOD0 is not dominated by candidate AABB rejection. It is dominated by the evaluator and its downstream tunnel/floor/core and structural work. Coarse LOD1+ samples are dominated by room-cache/player-fit/room-landing work. The profile supports the suspected swept-tunnel/core duplication, but does not isolate the parameter-copy cost. + +## Instrument and build + +- Registration is at generation-task entry/exit in VoxelWorld.cpp, not around individual samples. Disabled registration is one relaxed atomic load of the global sampler flag. Idle workers are not registered. +- The sampler thread calls FPlatformStackWalk::CaptureThreadStackBackTrace for each active registered worker at the configured interval (default 1000 us). Raw PCs go to preallocated bounded storage; workers share no sampling lock and do no symbolization. +- After the run, PCs are symbolized with the platform resolver and DbgHelp inline callbacks when available. Inline frames were resolved in both game runs. The final bounded store is a 131072-sample reservoir, so the long moving run is not represented by only its first capture window. +- LOD is written beside each raw sample and is set when the generation task registers. The game switch is -voxel.SampleStacks=intervalus; the commandlet switch is -samplestacks[=intervalus]. Both write under the plugin's Saved directory. +- The game logs show the sampler starting and stopping from the measured staged runtime. The measured runtime DLL was E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Saved\BuildHost\HostProject\Plugins\VoxelForge\Binaries\Win64\UnrealEditor-VoxelForge.dll, SHA-256 FBC56BDD4826057A5EF6A9CB6D06694CF61C92DE077762AC05DB926779A68BA8. The staged editor DLL was E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Saved\BuildHost\HostProject\Plugins\VoxelForge\Binaries\Win64\UnrealEditor-VoxelForgeEditor.dll, SHA-256 E55930A8D8D7C2F8EF1D8FFEE0867323044D5054752DF531D3A6DBC98FB2E9B8. +- Build command: dotnet "E:\Program Files\Epic Games\UE_5.7\Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.dll" UnrealEditor Win64 Development "-Project=E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Saved\BuildHost\HostProject\HostProject.uproject" -WaitMutex -FromMsBuild -architecture=x64 -NoUBA "-Log=E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Saved\PerfSampledBuild_Fallback.log" + +## Closure + +| Run | Capture attempts | Registered-thread samples | Retained stacks | LOD0 | LOD1+ | Resolved frames | Unknown frames | Wait-like frames | Resolved leaf | +|---|---:|---:|---:|---:|---:|---:|---:|---:|---:| +| Static | 193166 | 193165 | 131072 | 16057 | 115015 | 59.1492% | 40.8508% | 0.0136% | 82.5142% | +| Moving | 251129 | 251126 | 131072 | 41467 | 89605 | 57.5231% | 42.4769% | 0.0370% | 83.9149% | + +There were zero untagged samples and zero capture failures. The sampler captured generation work rather than waits: wait-like frames were 368 static and 957 moving, or 0.2808% and 0.7301% of retained samples. Unknown frames are a real symbol-coverage limitation, not a wait problem: 40.8508% and 42.4769% of all captured frame slots were unknown, while the leaf frame still resolved for 82.5142% and 83.9149% of retained samples. Unknown is kept as an explicit top-table row. + +The static run had 7380 inline symbols and the moving run had 7833. The complete raw CSV and readable summary for the headline runs are: + +- E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Saved\BuildHost\HostProject\Plugins\VoxelForge\Saved\VoxelStackSamples_game_18392.csv +- E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Saved\BuildHost\HostProject\Plugins\VoxelForge\Saved\VoxelStackSummary_game_18392.txt +- E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Saved\BuildHost\HostProject\Plugins\VoxelForge\Saved\VoxelStackSamples_game_13844.csv +- E:\Projet Unreal\VoxelM\Plugins\VoxelForge\Saved\BuildHost\HostProject\Plugins\VoxelForge\Saved\VoxelStackSummary_game_13844.txt + +## Game overhead + +The off runs are the noise estimate. Worker generation seconds are the sum of generation time recorded by workers, not wall-clock time. + +| Path | Run | Sampler | Request p50 | Request p95 | Generation p50 | Generation p95 | Worker generation seconds | Applied tiles | Visible tiles | Triangles | Obsolete | +|---|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:| +| Static | GameStaticOffA_20260913 | off | 0.108582 | 0.171829 | 0.076228 | 0.122883 | 184.994019 | 841 | 447 | 669834 | 0 | +| Static | GameStaticOffB_20260913 | off | 0.110888 | 0.179788 | 0.077364 | 0.128214 | 185.959906 | 841 | 447 | 669834 | 0 | +| Static | GameStaticOnFinal2_20260913 | 1 ms | 0.112669 | 0.188827 | 0.079557 | 0.130637 | 193.721335 | 841 | 447 | 669834 | 0 | +| Moving | GameMovingOffA_20260913 | off | 0.105508 | 0.191523 | 0.074358 | 0.126597 | 256.549700 | 2219 | 949 | 1862188 | 1 | +| Moving | GameMovingOffB_20260913 | off | 0.106318 | 0.194674 | 0.074501 | 0.127767 | 256.792375 | 2216 | 946 | 1860284 | 2 | +| Moving | GameMovingOnFinal2_20260913 | 1 ms | 0.108368 | 0.201022 | 0.078347 | 0.132525 | 256.742287 | 2199 | 930 | 1853788 | 4 | + +Static off-run noise was 4.64% for request p95, 1.49% for generation p50, 4.34% for generation p95, and 0.52% for worker generation seconds. Against the mean of the two off runs, sampler-on static was +2.67% request p50, +7.41% request p95, +3.60% generation p50, +4.06% generation p95, and +4.71% worker generation seconds. This fails the requested within-noise test for static. + +Moving off-run noise was 1.64% for request p95, 0.19% for generation p50, 0.92% for generation p95, and 0.095% for worker generation seconds. Against the off mean, sampler-on moving was +2.32% request p50, +4.10% request p95, +5.26% generation p50, +4.20% generation p95, and +0.03% worker generation seconds. Moving worker CPU passes the noise check; request and generation timing do not. Therefore small (<5%) game-path differences should not be treated as reliable until the sampling overhead is reduced or calibrated. + +## Field and tile/triangle comparison + +Static on/off is exact at the game output level measured here: 841 applied tiles, 447 visible tiles, and 669834 triangles in both off runs and the sampler-on run. The sampler has no field writes and classifier mode was the default 0 in every game run; no classifier was enabled. + +The moving comparison is a finite 25-second streaming window, so applied work is schedule-sensitive even with the sampler off: the two off runs are already 2219/2216 applied tiles and 1/2 obsolete tile aborts. The on run is 2199 applied, 930 visible, 1853788 triangles, and 4 obsolete aborts. This is not evidence of a sampler-induced field mutation; it is evidence that the moving window did not finish the same tile frontier. The implementation remains deterministic for the same build and seed; any classifier used in a future run must remain outside sampler-controlled state and must be field-preserving. + +## Static game profile (GameStaticOnFinal2_20260913) + +Percentages are relative to the retained non-empty-stack samples in that subgroup. Inclusive rows count a function once per sample; they are intentionally not additive. + +### All registered generation samples + +#### Exclusive functions (leaf) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 22919 | 17.4858 | unknown | | unknown | +| 2 | 19415 | 14.8125 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 3 | 10059 | 7.6744 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:59 | +| 4 | 7519 | 5.7365 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2392 | +| 5 | 6377 | 4.8653 | plugin | [Inline Frame] VoxelNoise::Detail::Lerp() | Plugin/Public/VoxelNoise.h:87 | +| 6 | 4228 | 3.2257 | plugin | `anonymous namespace'::VF_EvaluateRoomLandingDensity() | Plugin/Private/VoxelCaveMorphology.cpp:2515 | +| 7 | 4104 | 3.1311 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:67 | +| 8 | 4048 | 3.0884 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 9 | 3725 | 2.8419 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2086 | +| 10 | 3565 | 2.7199 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2648 | +| 11 | 3522 | 2.6871 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:169 | +| 12 | 2475 | 1.8883 | plugin | [Inline Frame] ?A0x7db52731::VF_IsPointInsidePlayerCapsule() | Plugin/Private/VoxelCaveMorphology.cpp:1993 | +| 13 | 1827 | 1.3939 | plugin | [Inline Frame] VoxelHash::Mix() | Plugin/Public/VoxelCaveMorphology.h:215 | +| 14 | 1771 | 1.3512 | plugin | `anonymous namespace'::VF_ValidatePlayerFitPose() | Plugin/Private/VoxelCaveMorphology.cpp:2216 | +| 15 | 1646 | 1.2558 | engine | [Inline Frame] TArray,TSizedDefaultAllocator<32> >::Emplace() | Engine/Runtime/Core/Public/Containers/Array.h:2602 | +| 16 | 1271 | 0.9697 | plugin | `VoxelCaveMorphology::BuildChunkCache'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:5045 | +| 17 | 1263 | 0.9636 | plugin | `anonymous namespace'::VF_ProjectNativePassageFloor() | Plugin/Private/VoxelStrateManager.cpp:471 | +| 18 | 1220 | 0.9308 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:1656 | +| 19 | 1024 | 0.7812 | engine | [Inline Frame] UE::Math::TVector2::{ctor}() | Engine/Runtime/Core/Public/Math/Vector2D.h:837 | +| 20 | 952 | 0.7263 | engine | [Inline Frame] UE::Math::TVector::operator/() | Engine/Runtime/Core/Public/Math/Vector.h:1585 | +| 21 | 897 | 0.6844 | plugin | [Inline Frame] VoxelNoise::Perlin3D() | Plugin/Public/VoxelNoise.h:111 | +| 22 | 817 | 0.6233 | engine | [Inline Frame] FGenericPlatformMath::Min() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:990 | +| 23 | 758 | 0.5783 | plugin | VoxelCaveMorphology::BuildChunkCache() | Plugin/Private/VoxelCaveMorphology.cpp:5165 | +| 24 | 742 | 0.5661 | plugin | [Inline Frame] ?A0x7db52731::VF_GetFloorReliefBound() | Plugin/Private/VoxelCaveMorphology.cpp:296 | +| 25 | 725 | 0.5531 | engine | [Inline Frame] UE4::SSE4::FloorToFloat() | Engine/Runtime/Core/Public/Math/UnrealPlatformMathSSE4.h:34 | +| 26 | 684 | 0.5219 | plugin | `VoxelCaveMorphology::EvaluateTunnelCoreWorld'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6298 | +| 27 | 640 | 0.4883 | engine | [Inline Frame] FGenericPlatformMath::Max() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:968 | +| 28 | 633 | 0.4829 | plugin | [Inline Frame] `anonymous-namespace'::VF_FindPlayerFitPointForRoom::__l2::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:2629 | +| 29 | 602 | 0.4593 | engine | [Inline Frame] TArray > >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 30 | 589 | 0.4494 | engine | [Inline Frame] UE::Math::TVector::DistSquared() | Engine/Runtime/Core/Public/Math/Vector.h:2480 | + +#### Inclusive functions (once per sample) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 131072 | 100.0000 | engine | [Inline Frame] Invoke() | Engine/Runtime/Core/Public/Templates/Invoke.h:47 | +| 2 | 131072 | 100.0000 | engine | [Inline Frame] LowLevelTasks::FTask::Init::__l13::::operator()() | Engine/Runtime/Core/Public/Async/Fundamental/Task.h:499 | +| 3 | 131072 | 100.0000 | engine | [Inline Frame] LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::Call() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:162 | +| 4 | 131072 | 100.0000 | engine | [Inline Frame] UE::Tasks::Private::FTaskBase::Init::__l2::::operator()() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:180 | +| 5 | 131072 | 100.0000 | engine | LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::CallAndMove() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:171 | +| 6 | 131072 | 100.0000 | engine | UE::Tasks::Private::FTaskBase::TryExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:518 | +| 7 | 131072 | 100.0000 | engine | UE::Tasks::Private::TExecutableTaskBase<`AVoxelWorld::LoadTile'::`2'::,void,void>::ExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:898 | +| 8 | 131072 | 100.0000 | plugin | `AVoxelWorld::LoadTile'::`2'::::operator()() | Plugin/Private/VoxelWorld.cpp:3766 | +| 9 | 131072 | 100.0000 | plugin | AVoxelWorld::GenerateTileResult() | Plugin/Private/VoxelWorld.cpp:4078 | +| 10 | 131029 | 99.9672 | plugin | UVoxelMarchingCubesMesher::GenerateMesh() | Plugin/Private/VoxelMarchingCubesMesher.cpp:64 | +| 11 | 130502 | 99.5651 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:1587 | +| 12 | 118433 | 90.3572 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2800 | +| 13 | 104250 | 79.5364 | plugin | VoxelCaveMorphology::BuildChunkCache() | Plugin/Private/VoxelCaveMorphology.cpp:5276 | +| 14 | 96145 | 73.3528 | plugin | [Inline Frame] VoxelCaveMorphology::BuildChunkCache::__l2::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:5153 | +| 15 | 96139 | 73.3482 | plugin | `anonymous namespace'::VF_FindPlayerFitPointForRoom() | Plugin/Private/VoxelCaveMorphology.cpp:2687 | +| 16 | 95273 | 72.6875 | plugin | `anonymous namespace'::VF_ValidatePlayerFitPose() | Plugin/Private/VoxelCaveMorphology.cpp:2217 | +| 17 | 68853 | 52.5307 | engine | [Inline Frame] UE::Core::Private::Function::TFunctionRefBase::operator()() | Engine/Runtime/Core/Public/Templates/Function.h:414 | +| 18 | 68717 | 52.4269 | engine | UE::Core::Private::Function::TFunctionRefCaller<``anonymous namespace'::VF_FindPlayerFitPointForRoom'::`2':: const ,float,float,float,float>::Call() | Engine/Runtime/Core/Public/Templates/Function.h:298 | +| 19 | 67989 | 51.8715 | plugin | `anonymous namespace'::VF_EvaluateRoomLandingDensity() | Plugin/Private/VoxelCaveMorphology.cpp:2512 | +| 20 | 50851 | 38.7962 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2387 | +| 21 | 41573 | 31.7177 | plugin | [Inline Frame] VoxelNoise::Perlin3D() | Plugin/Public/VoxelNoise.h:115 | +| 22 | 20551 | 15.6792 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2039 | +| 23 | 19415 | 14.8125 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 24 | 11934 | 9.1049 | engine | UE::Core::Private::ReallocGrow1_DoAlloc_Tiny<3,TSizedHeapAllocator<32,FMemory>::ForAnyElementType>() | Engine/Runtime/Core/Public/Containers/Array.h:463 | +| 25 | 10059 | 7.6744 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:59 | +| 26 | 6377 | 4.8653 | plugin | [Inline Frame] VoxelNoise::Detail::Lerp() | Plugin/Public/VoxelNoise.h:87 | +| 27 | 4104 | 3.1311 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:67 | +| 28 | 4048 | 3.0884 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 29 | 3753 | 2.8633 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:169 | +| 30 | 3710 | 2.8305 | plugin | UVoxelStrateManager::ApplyPassageStructuralPostsMC() | Plugin/Private/VoxelStrateManager.cpp:2585 | + +#### Exclusive source lines (leaf source) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 22950 | 17.5095 | unknown | FSurfaceColumn::FSurfaceColumn() | unknown | +| 2 | 6377 | 4.8653 | plugin | [Inline Frame] VoxelNoise::Detail::Lerp() | Plugin/Public/VoxelNoise.h:87 | +| 3 | 4398 | 3.3554 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 4 | 4248 | 3.2410 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:83 | +| 5 | 3973 | 3.0312 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 6 | 3597 | 2.7443 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:82 | +| 7 | 3464 | 2.6428 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:59 | +| 8 | 3314 | 2.5284 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:80 | +| 9 | 3092 | 2.3590 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:170 | +| 10 | 2643 | 2.0164 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2382 | +| 11 | 2594 | 1.9791 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:58 | +| 12 | 2474 | 1.8875 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:68 | +| 13 | 2344 | 1.7883 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:79 | +| 14 | 2300 | 1.7548 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:54 | +| 15 | 2036 | 1.5533 | engine | [Inline Frame] TArray >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 16 | 1852 | 1.4130 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2377 | +| 17 | 1753 | 1.3374 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2392 | +| 18 | 1702 | 1.2985 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2049 | +| 19 | 1701 | 1.2978 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:57 | +| 20 | 1536 | 1.1719 | engine | [Inline Frame] TArray,TSizedDefaultAllocator<32> >::Emplace() | Engine/Runtime/Core/Public/Containers/Array.h:2602 | +| 21 | 1489 | 1.1360 | plugin | [Inline Frame] ?A0x7db52731::VF_IsPointInsidePlayerCapsule() | Plugin/Private/VoxelCaveMorphology.cpp:1993 | +| 22 | 1486 | 1.1337 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:67 | +| 23 | 1485 | 1.1330 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:84 | +| 24 | 1087 | 0.8293 | plugin | `anonymous namespace'::VF_EvaluateRoomLandingDensity() | Plugin/Private/VoxelCaveMorphology.cpp:2509 | +| 25 | 1024 | 0.7812 | engine | [Inline Frame] UE::Math::TVector2::{ctor}() | Engine/Runtime/Core/Public/Math/Vector2D.h:837 | +| 26 | 982 | 0.7492 | plugin | [Inline Frame] ?A0x7db52731::VF_IsPointInsidePlayerCapsule() | Plugin/Private/VoxelCaveMorphology.cpp:1995 | +| 27 | 952 | 0.7263 | engine | [Inline Frame] UE::Math::TVector::operator/() | Engine/Runtime/Core/Public/Math/Vector.h:1585 | +| 28 | 817 | 0.6233 | engine | [Inline Frame] FGenericPlatformMath::Min() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:990 | +| 29 | 725 | 0.5531 | engine | [Inline Frame] UE4::SSE4::FloorToFloat() | Engine/Runtime/Core/Public/Math/UnrealPlatformMathSSE4.h:34 | +| 30 | 706 | 0.5386 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2644 | + +### LOD0 (floor) + +#### Exclusive functions (leaf) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 2079 | 12.9476 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2648 | +| 2 | 1870 | 11.6460 | unknown | | unknown | +| 3 | 791 | 4.9262 | plugin | `anonymous namespace'::VF_ProjectNativePassageFloor() | Plugin/Private/VoxelStrateManager.cpp:471 | +| 4 | 705 | 4.3906 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:552 | +| 5 | 696 | 4.3346 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:1656 | +| 6 | 561 | 3.4938 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:169 | +| 7 | 547 | 3.4066 | engine | [Inline Frame] UE::Math::TVector2::{ctor}() | Engine/Runtime/Core/Public/Math/Vector2D.h:837 | +| 8 | 417 | 2.5970 | plugin | `VoxelCaveMorphology::EvaluateTunnelCoreWorld'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6298 | +| 9 | 406 | 2.5285 | engine | [Inline Frame] FGenericPlatformMath::Min() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:990 | +| 10 | 291 | 1.8123 | engine | [Inline Frame] TArray >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 11 | 246 | 1.5320 | plugin | [Inline Frame] ?A0x7db52731::VF_DistanceSquaredToAabb() | Plugin/Private/VoxelCaveMorphology.cpp:68 | +| 12 | 244 | 1.5196 | plugin | VF_EvaluatePassageLandingSDF() | Plugin/Private/VoxelCaveMorphology.cpp:4394 | +| 13 | 239 | 1.4884 | engine | [Inline Frame] UE::Math::TVector2::SizeSquared() | Engine/Runtime/Core/Public/Math/Vector2D.h:1119 | +| 14 | 236 | 1.4698 | plugin | `VoxelCaveMorphology::EvaluateSDFCached'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6192 | +| 15 | 229 | 1.4262 | plugin | [Inline Frame] ?A0x7db52731::VF_ProjectTunnelSegmentXY() | Plugin/Private/VoxelCaveMorphology.cpp:950 | +| 16 | 209 | 1.3016 | plugin | `anonymous namespace'::VF_EvaluateSweptTunnelChain() | Plugin/Private/VoxelCaveMorphology.cpp:982 | +| 17 | 205 | 1.2767 | engine | [Inline Frame] TArray,TSizedDefaultAllocator<32> >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 18 | 194 | 1.2082 | plugin | [Inline Frame] VoxelDensityProfile::FScopedTimer::{ctor}() | Plugin/Public/VoxelDensityProfile.h:393 | +| 19 | 188 | 1.1708 | plugin | ``anonymous namespace'::VF_EvaluateSweptTunnelChain'::`7'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:1058 | +| 20 | 181 | 1.1272 | plugin | UVoxelStrateManager::EvaluateModifierSDF() | Plugin/Private/VoxelStrateManager.cpp:2277 | +| 21 | 159 | 0.9902 | plugin | UVoxelMarchingCubesMesher::GenerateMesh() | Plugin/Private/VoxelMarchingCubesMesher.cpp:574 | +| 22 | 154 | 0.9591 | plugin | UVoxelStrateManager::ApplyPassageStructuralPostsMC() | Plugin/Private/VoxelStrateManager.cpp:2574 | +| 23 | 151 | 0.9404 | plugin | [Inline Frame] VoxelDensityProfile::FScopedTimer::End() | Plugin/Private/VoxelDensityProfile.cpp:975 | +| 24 | 142 | 0.8843 | plugin | [Inline Frame] VoxelPassageGeometry::ProjectWalkableTunnelFloor() | Plugin/Public/VoxelPassageGeometry.h:327 | +| 25 | 138 | 0.8594 | plugin | VoxelDensityProfile::FScopedTimer::~FScopedTimer() | Plugin/Private/VoxelDensityProfile.cpp:969 | +| 26 | 135 | 0.8408 | engine | [Inline Frame] UE4::SSE::FloorToInt32() | Engine/Runtime/Core/Public/Math/UnrealPlatformMathSSE.h:90 | +| 27 | 132 | 0.8221 | engine | [Inline Frame] FGenericPlatformMath::Max() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:968 | +| 28 | 126 | 0.7847 | plugin | UVoxelStrateManager::UsesOperatorStackForChunk() | Plugin/Private/VoxelStrateManager.cpp:4932 | +| 29 | 123 | 0.7660 | plugin | UVoxelStrateManager::ApplyPassageLandingFloorMC() | Plugin/Private/VoxelStrateManager.cpp:2472 | +| 30 | 122 | 0.7598 | plugin | ApplyDisturbances() | Plugin/Private/VoxelGenerator.cpp:533 | + +#### Inclusive functions (once per sample) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 16057 | 100.0000 | engine | [Inline Frame] Invoke() | Engine/Runtime/Core/Public/Templates/Invoke.h:47 | +| 2 | 16057 | 100.0000 | engine | [Inline Frame] LowLevelTasks::FTask::Init::__l13::::operator()() | Engine/Runtime/Core/Public/Async/Fundamental/Task.h:499 | +| 3 | 16057 | 100.0000 | engine | [Inline Frame] LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::Call() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:162 | +| 4 | 16057 | 100.0000 | engine | [Inline Frame] UE::Tasks::Private::FTaskBase::Init::__l2::::operator()() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:180 | +| 5 | 16057 | 100.0000 | engine | LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::CallAndMove() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:171 | +| 6 | 16057 | 100.0000 | engine | UE::Tasks::Private::FTaskBase::TryExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:518 | +| 7 | 16057 | 100.0000 | engine | UE::Tasks::Private::TExecutableTaskBase<`AVoxelWorld::LoadTile'::`2'::,void,void>::ExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:898 | +| 8 | 16057 | 100.0000 | plugin | `AVoxelWorld::LoadTile'::`2'::::operator()() | Plugin/Private/VoxelWorld.cpp:3766 | +| 9 | 16057 | 100.0000 | plugin | AVoxelWorld::GenerateTileResult() | Plugin/Private/VoxelWorld.cpp:4078 | +| 10 | 16036 | 99.8692 | plugin | UVoxelMarchingCubesMesher::GenerateMesh() | Plugin/Private/VoxelMarchingCubesMesher.cpp:64 | +| 11 | 15732 | 97.9760 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:1587 | +| 12 | 8893 | 55.3839 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2648 | +| 13 | 2323 | 14.4672 | plugin | UVoxelStrateManager::ApplyPassageStructuralPostsMC() | Plugin/Private/VoxelStrateManager.cpp:2585 | +| 14 | 2259 | 14.0686 | plugin | UVoxelStrateManager::ApplyPassageCarvingOnly() | Plugin/Private/VoxelStrateManager.cpp:2373 | +| 15 | 2226 | 13.8631 | plugin | [Inline Frame] VF_ApplyPassageCarving() | Plugin/Public/VoxelDensityPrimitives.h:75 | +| 16 | 2225 | 13.8569 | plugin | UVoxelStrateManager::EvaluateModifierSDF() | Plugin/Private/VoxelStrateManager.cpp:2259 | +| 17 | 1990 | 12.3933 | plugin | VoxelCaveMorphology::EvaluateTunnelCoreWorld() | Plugin/Private/VoxelCaveMorphology.cpp:6397 | +| 18 | 1983 | 12.3498 | plugin | VoxelCaveMorphology::EvaluateSDFCached() | Plugin/Private/VoxelCaveMorphology.cpp:6222 | +| 19 | 1899 | 11.8266 | plugin | `VoxelCaveMorphology::EvaluateTunnelCoreWorld'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6345 | +| 20 | 1856 | 11.5588 | plugin | `anonymous namespace'::VF_EvaluateSweptTunnel() | Plugin/Private/VoxelCaveMorphology.cpp:1149 | +| 21 | 1832 | 11.4094 | plugin | `anonymous namespace'::VF_ProjectNativePassageFloor() | Plugin/Private/VoxelStrateManager.cpp:470 | +| 22 | 1765 | 10.9921 | plugin | `anonymous namespace'::VF_EvaluateSweptTunnelChain() | Plugin/Private/VoxelCaveMorphology.cpp:1067 | +| 23 | 1594 | 9.9271 | plugin | `VoxelCaveMorphology::EvaluateSDFCached'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6214 | +| 24 | 1210 | 7.5357 | plugin | [Inline Frame] VF_IsWalkableTunnelAir() | Plugin/Private/VoxelStrateManager.cpp:714 | +| 25 | 1197 | 7.4547 | plugin | `anonymous namespace'::VF_ProjectPassageFloor() | Plugin/Private/VoxelStrateManager.cpp:550 | +| 26 | 886 | 5.5178 | plugin | [Inline Frame] VoxelSDF::TaperedCapsule() | Plugin/Public/VoxelCaveMorphology.h:149 | +| 27 | 770 | 4.7954 | engine | [Inline Frame] FGenericPlatformMath::Min() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:990 | +| 28 | 757 | 4.7145 | plugin | UVoxelStrateManager::ApplyPassageNativeFloorMC() | Plugin/Private/VoxelStrateManager.cpp:2658 | +| 29 | 732 | 4.5588 | plugin | VF_EvaluatePassageLandingSDF() | Plugin/Private/VoxelCaveMorphology.cpp:4394 | +| 30 | 705 | 4.3906 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:552 | + +#### Exclusive source lines (leaf source) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 1871 | 11.6522 | unknown | FSurfaceColumn::FSurfaceColumn() | unknown | +| 2 | 727 | 4.5276 | engine | [Inline Frame] TArray >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 3 | 666 | 4.1477 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 4 | 547 | 3.4066 | engine | [Inline Frame] UE::Math::TVector2::{ctor}() | Engine/Runtime/Core/Public/Math/Vector2D.h:837 | +| 5 | 482 | 3.0018 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:170 | +| 6 | 417 | 2.5970 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2648 | +| 7 | 406 | 2.5285 | engine | [Inline Frame] FGenericPlatformMath::Min() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:990 | +| 8 | 399 | 2.4849 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2644 | +| 9 | 354 | 2.2046 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2652 | +| 10 | 239 | 1.4884 | engine | [Inline Frame] UE::Math::TVector2::SizeSquared() | Engine/Runtime/Core/Public/Math/Vector2D.h:1119 | +| 11 | 215 | 1.3390 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:3652 | +| 12 | 186 | 1.1584 | plugin | `VoxelCaveMorphology::EvaluateTunnelCoreWorld'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6313 | +| 13 | 177 | 1.1023 | plugin | [Inline Frame] VoxelDensityProfile::FScopedTimer::{ctor}() | Plugin/Public/VoxelDensityProfile.h:393 | +| 14 | 173 | 1.0774 | plugin | [Inline Frame] VoxelDensityProfile::FScopedTimer::End() | Plugin/Private/VoxelDensityProfile.cpp:975 | +| 15 | 163 | 1.0151 | plugin | `VoxelCaveMorphology::EvaluateSDFCached'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6192 | +| 16 | 158 | 0.9840 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:3615 | +| 17 | 157 | 0.9778 | plugin | `anonymous namespace'::VF_ProjectNativePassageFloor() | Plugin/Private/VoxelStrateManager.cpp:471 | +| 18 | 135 | 0.8408 | engine | [Inline Frame] UE4::SSE::FloorToInt32() | Engine/Runtime/Core/Public/Math/UnrealPlatformMathSSE.h:90 | +| 19 | 119 | 0.7411 | engine | [Inline Frame] UE::Math::TVector::operator*() | Engine/Runtime/Core/Public/Math/Vector.h:316 | +| 20 | 109 | 0.6788 | engine | [Inline Frame] FGenericPlatformMath::Max() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:968 | +| 21 | 104 | 0.6477 | plugin | VF_EvaluatePassageLandingSDF() | Plugin/Private/VoxelCaveMorphology.cpp:4394 | +| 22 | 104 | 0.6477 | plugin | [Inline Frame] ?A0x7db52731::VF_DistanceSquaredToAabb() | Plugin/Private/VoxelCaveMorphology.cpp:62 | +| 23 | 98 | 0.6103 | engine | [Inline Frame] UE::Math::TVector::DistSquared() | Engine/Runtime/Core/Public/Math/Vector.h:2480 | +| 24 | 90 | 0.5605 | engine | [Inline Frame] UE::Math::TVector::operator-() | Engine/Runtime/Core/Public/Math/Vector.h:1573 | +| 25 | 90 | 0.5605 | plugin | ``anonymous namespace'::VF_EvaluateSweptTunnelChain'::`7'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:1058 | +| 26 | 89 | 0.5543 | engine | [Inline Frame] FMath::Square() | Engine/Runtime/Core/Public/Math/UnrealMathUtility.h:580 | +| 27 | 87 | 0.5418 | plugin | VoxelDensityProfile::FScopedTimer::~FScopedTimer() | Plugin/Private/VoxelDensityProfile.cpp:969 | +| 28 | 87 | 0.5418 | plugin | `anonymous namespace'::VF_ProjectNativePassageFloor() | Plugin/Private/VoxelStrateManager.cpp:452 | +| 29 | 86 | 0.5356 | plugin | [Inline Frame] ?A0x7db52731::VF_ProjectTunnelSegmentXY() | Plugin/Private/VoxelCaveMorphology.cpp:955 | +| 30 | 81 | 0.5045 | plugin | VF_EvaluatePassageLandingSDF() | Plugin/Private/VoxelCaveMorphology.cpp:4411 | + +### LOD1+ (coarse/background) + +#### Exclusive functions (leaf) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 21049 | 18.3011 | unknown | | unknown | +| 2 | 19342 | 16.8169 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 3 | 10021 | 8.7128 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:59 | +| 4 | 7486 | 6.5087 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2392 | +| 5 | 6355 | 5.5254 | plugin | [Inline Frame] VoxelNoise::Detail::Lerp() | Plugin/Public/VoxelNoise.h:87 | +| 6 | 4217 | 3.6665 | plugin | `anonymous namespace'::VF_EvaluateRoomLandingDensity() | Plugin/Private/VoxelCaveMorphology.cpp:2587 | +| 7 | 4090 | 3.5561 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:67 | +| 8 | 3704 | 3.2204 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2086 | +| 9 | 3343 | 2.9066 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 10 | 2961 | 2.5744 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:170 | +| 11 | 2469 | 2.1467 | plugin | [Inline Frame] ?A0x7db52731::VF_IsPointInsidePlayerCapsule() | Plugin/Private/VoxelCaveMorphology.cpp:1993 | +| 12 | 1802 | 1.5668 | plugin | [Inline Frame] VoxelHash::Mix() | Plugin/Public/VoxelCaveMorphology.h:217 | +| 13 | 1755 | 1.5259 | plugin | `anonymous namespace'::VF_ValidatePlayerFitPose() | Plugin/Private/VoxelCaveMorphology.cpp:2221 | +| 14 | 1639 | 1.4250 | engine | [Inline Frame] TArray,TSizedDefaultAllocator<32> >::Emplace() | Engine/Runtime/Core/Public/Containers/Array.h:2602 | +| 15 | 1486 | 1.2920 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2652 | +| 16 | 1263 | 1.0981 | plugin | `VoxelCaveMorphology::BuildChunkCache'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:5045 | +| 17 | 948 | 0.8242 | engine | [Inline Frame] UE::Math::TVector::operator/() | Engine/Runtime/Core/Public/Math/Vector.h:1585 | +| 18 | 893 | 0.7764 | plugin | [Inline Frame] VoxelNoise::Perlin3D() | Plugin/Public/VoxelNoise.h:111 | +| 19 | 757 | 0.6582 | plugin | VoxelCaveMorphology::BuildChunkCache() | Plugin/Private/VoxelCaveMorphology.cpp:5165 | +| 20 | 716 | 0.6225 | engine | [Inline Frame] UE4::SSE4::FloorToFloat() | Engine/Runtime/Core/Public/Math/UnrealPlatformMathSSE4.h:34 | +| 21 | 678 | 0.5895 | plugin | [Inline Frame] ?A0x7db52731::VF_GetFloorReliefBound() | Plugin/Private/VoxelCaveMorphology.cpp:296 | +| 22 | 631 | 0.5486 | plugin | [Inline Frame] `anonymous-namespace'::VF_FindPlayerFitPointForRoom::__l2::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:2629 | +| 23 | 599 | 0.5208 | engine | [Inline Frame] TArray > >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 24 | 524 | 0.4556 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:2078 | +| 25 | 508 | 0.4417 | engine | [Inline Frame] FGenericPlatformMath::Max() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:968 | +| 26 | 499 | 0.4339 | plugin | [Inline Frame] VoxelHash::SeedOffset() | Plugin/Public/VoxelCaveMorphology.h:257 | +| 27 | 491 | 0.4269 | engine | [Inline Frame] UE::Math::TVector::DistSquared() | Engine/Runtime/Core/Public/Math/Vector.h:2480 | +| 28 | 477 | 0.4147 | engine | [Inline Frame] UE::Math::TVector2::{ctor}() | Engine/Runtime/Core/Public/Math/Vector2D.h:837 | +| 29 | 472 | 0.4104 | plugin | `anonymous namespace'::VF_ProjectNativePassageFloor() | Plugin/Private/VoxelStrateManager.cpp:461 | +| 30 | 463 | 0.4026 | engine | [Inline Frame] FMicrosoftPlatformMathBase::IsFinite() | Engine/Runtime/Core/Public/Microsoft/MicrosoftPlatformMath.h:17 | + +#### Inclusive functions (once per sample) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 115015 | 100.0000 | engine | [Inline Frame] Invoke() | Engine/Runtime/Core/Public/Templates/Invoke.h:47 | +| 2 | 115015 | 100.0000 | engine | [Inline Frame] LowLevelTasks::FTask::Init::__l13::::operator()() | Engine/Runtime/Core/Public/Async/Fundamental/Task.h:499 | +| 3 | 115015 | 100.0000 | engine | [Inline Frame] LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::Call() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:162 | +| 4 | 115015 | 100.0000 | engine | [Inline Frame] UE::Tasks::Private::FTaskBase::Init::__l2::::operator()() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:180 | +| 5 | 115015 | 100.0000 | engine | LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::CallAndMove() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:171 | +| 6 | 115015 | 100.0000 | engine | UE::Tasks::Private::FTaskBase::TryExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:518 | +| 7 | 115015 | 100.0000 | engine | UE::Tasks::Private::TExecutableTaskBase<`AVoxelWorld::LoadTile'::`2'::,void,void>::ExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:898 | +| 8 | 115015 | 100.0000 | plugin | `AVoxelWorld::LoadTile'::`2'::::operator()() | Plugin/Private/VoxelWorld.cpp:3766 | +| 9 | 115015 | 100.0000 | plugin | AVoxelWorld::GenerateTileResult() | Plugin/Private/VoxelWorld.cpp:4078 | +| 10 | 114993 | 99.9809 | plugin | UVoxelMarchingCubesMesher::GenerateMesh() | Plugin/Private/VoxelMarchingCubesMesher.cpp:574 | +| 11 | 114770 | 99.7870 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:2073 | +| 12 | 109540 | 95.2398 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2800 | +| 13 | 103850 | 90.2926 | plugin | VoxelCaveMorphology::BuildChunkCache() | Plugin/Private/VoxelCaveMorphology.cpp:5276 | +| 14 | 95772 | 83.2691 | plugin | [Inline Frame] VoxelCaveMorphology::BuildChunkCache::__l2::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:5153 | +| 15 | 95766 | 83.2639 | plugin | `anonymous namespace'::VF_FindPlayerFitPointForRoom() | Plugin/Private/VoxelCaveMorphology.cpp:2687 | +| 16 | 94908 | 82.5179 | plugin | `anonymous namespace'::VF_ValidatePlayerFitPose() | Plugin/Private/VoxelCaveMorphology.cpp:2217 | +| 17 | 68607 | 59.6505 | engine | [Inline Frame] UE::Core::Private::Function::TFunctionRefBase::operator()() | Engine/Runtime/Core/Public/Templates/Function.h:414 | +| 18 | 68472 | 59.5331 | engine | UE::Core::Private::Function::TFunctionRefCaller<``anonymous namespace'::VF_FindPlayerFitPointForRoom'::`2':: const ,float,float,float,float>::Call() | Engine/Runtime/Core/Public/Templates/Function.h:298 | +| 19 | 67746 | 58.9019 | plugin | `anonymous namespace'::VF_EvaluateRoomLandingDensity() | Plugin/Private/VoxelCaveMorphology.cpp:2512 | +| 20 | 50654 | 44.0412 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2387 | +| 21 | 41417 | 36.0101 | plugin | [Inline Frame] VoxelNoise::Perlin3D() | Plugin/Public/VoxelNoise.h:115 | +| 22 | 20462 | 17.7907 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2039 | +| 23 | 19342 | 16.8169 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 24 | 11885 | 10.3334 | engine | UE::Core::Private::ReallocGrow1_DoAlloc_Tiny<3,TSizedHeapAllocator<32,FMemory>::ForAnyElementType>() | Engine/Runtime/Core/Public/Containers/Array.h:463 | +| 25 | 10021 | 8.7128 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:59 | +| 26 | 6355 | 5.5254 | plugin | [Inline Frame] VoxelNoise::Detail::Lerp() | Plugin/Public/VoxelNoise.h:87 | +| 27 | 4090 | 3.5561 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:67 | +| 28 | 3343 | 2.9066 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 29 | 3207 | 2.7883 | plugin | [Inline Frame] VoxelSDF::SmoothMax() | Plugin/Public/VoxelCaveMorphology.h:179 | +| 30 | 3178 | 2.7631 | plugin | [Inline Frame] VoxelSDF::Ellipsoid() | Plugin/Public/VoxelCaveMorphology.h:96 | + +#### Exclusive source lines (leaf source) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 21079 | 18.3272 | unknown | | unknown | +| 2 | 6355 | 5.5254 | plugin | [Inline Frame] VoxelNoise::Detail::Lerp() | Plugin/Public/VoxelNoise.h:87 | +| 3 | 4381 | 3.8091 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 4 | 4230 | 3.6778 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:83 | +| 5 | 3584 | 3.1161 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:82 | +| 6 | 3448 | 2.9979 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:59 | +| 7 | 3307 | 2.8753 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 8 | 3305 | 2.8735 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:80 | +| 9 | 2629 | 2.2858 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2382 | +| 10 | 2610 | 2.2693 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:170 | +| 11 | 2588 | 2.2501 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:58 | +| 12 | 2465 | 2.1432 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:68 | +| 13 | 2336 | 2.0310 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:79 | +| 14 | 2296 | 1.9963 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:54 | +| 15 | 1846 | 1.6050 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2377 | +| 16 | 1747 | 1.5189 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2392 | +| 17 | 1696 | 1.4746 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2049 | +| 18 | 1689 | 1.4685 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:57 | +| 19 | 1528 | 1.3285 | engine | [Inline Frame] TArray,TSizedDefaultAllocator<32> >::Emplace() | Engine/Runtime/Core/Public/Containers/Array.h:2602 | +| 20 | 1486 | 1.2920 | plugin | [Inline Frame] ?A0x7db52731::VF_IsPointInsidePlayerCapsule() | Plugin/Private/VoxelCaveMorphology.cpp:1993 | +| 21 | 1481 | 1.2877 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:67 | +| 22 | 1477 | 1.2842 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:84 | +| 23 | 1309 | 1.1381 | engine | [Inline Frame] TArray >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 24 | 1084 | 0.9425 | plugin | `anonymous namespace'::VF_EvaluateRoomLandingDensity() | Plugin/Private/VoxelCaveMorphology.cpp:2509 | +| 25 | 979 | 0.8512 | plugin | [Inline Frame] ?A0x7db52731::VF_IsPointInsidePlayerCapsule() | Plugin/Private/VoxelCaveMorphology.cpp:1995 | +| 26 | 948 | 0.8242 | engine | [Inline Frame] UE::Math::TVector::operator/() | Engine/Runtime/Core/Public/Math/Vector.h:1585 | +| 27 | 716 | 0.6225 | engine | [Inline Frame] UE4::SSE4::FloorToFloat() | Engine/Runtime/Core/Public/Math/UnrealPlatformMathSSE4.h:34 | +| 28 | 631 | 0.5486 | plugin | [Inline Frame] `anonymous-namespace'::VF_FindPlayerFitPointForRoom::__l2::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:2629 | +| 29 | 617 | 0.5365 | plugin | [Inline Frame] VoxelHash::Mix() | Plugin/Public/VoxelCaveMorphology.h:215 | +| 30 | 610 | 0.5304 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2051 | + +## Moving game profile (GameMovingOnFinal2_20260913) + +Percentages are relative to the retained non-empty-stack samples in that subgroup. Inclusive rows count a function once per sample; they are intentionally not additive. + +### All registered generation samples + +#### Exclusive functions (leaf) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 21083 | 16.0851 | unknown | | unknown | +| 2 | 13476 | 10.2814 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 3 | 8432 | 6.4331 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2648 | +| 4 | 7150 | 5.4550 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:59 | +| 5 | 5327 | 4.0642 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2382 | +| 6 | 4500 | 3.4332 | plugin | [Inline Frame] VoxelNoise::Detail::Lerp() | Plugin/Public/VoxelNoise.h:87 | +| 7 | 4246 | 3.2394 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 8 | 3562 | 2.7176 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:170 | +| 9 | 2987 | 2.2789 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:67 | +| 10 | 2944 | 2.2461 | plugin | `anonymous namespace'::VF_EvaluateRoomLandingDensity() | Plugin/Private/VoxelCaveMorphology.cpp:2509 | +| 11 | 2684 | 2.0477 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:1673 | +| 12 | 2644 | 2.0172 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2049 | +| 13 | 2211 | 1.6869 | plugin | `anonymous namespace'::VF_ProjectNativePassageFloor() | Plugin/Private/VoxelStrateManager.cpp:452 | +| 14 | 1964 | 1.4984 | engine | [Inline Frame] UE::Math::TVector2::{ctor}() | Engine/Runtime/Core/Public/Math/Vector2D.h:837 | +| 15 | 1845 | 1.4076 | plugin | `VoxelCaveMorphology::EvaluateTunnelCoreWorld'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6313 | +| 16 | 1668 | 1.2726 | plugin | [Inline Frame] ?A0x7db52731::VF_IsPointInsidePlayerCapsule() | Plugin/Private/VoxelCaveMorphology.cpp:1993 | +| 17 | 1373 | 1.0475 | engine | [Inline Frame] FGenericPlatformMath::Min() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:990 | +| 18 | 1360 | 1.0376 | plugin | `VoxelCaveMorphology::EvaluateSDFCached'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6214 | +| 19 | 1271 | 0.9697 | plugin | [Inline Frame] ?A0x7db52731::VF_DistanceSquaredToAabb() | Plugin/Private/VoxelCaveMorphology.cpp:68 | +| 20 | 1251 | 0.9544 | plugin | [Inline Frame] VoxelHash::Mix() | Plugin/Public/VoxelCaveMorphology.h:215 | +| 21 | 1241 | 0.9468 | plugin | `anonymous namespace'::VF_ValidatePlayerFitPose() | Plugin/Private/VoxelCaveMorphology.cpp:2207 | +| 22 | 1150 | 0.8774 | engine | [Inline Frame] TArray,TSizedDefaultAllocator<32> >::Emplace() | Engine/Runtime/Core/Public/Containers/Array.h:2602 | +| 23 | 994 | 0.7584 | plugin | `VoxelCaveMorphology::BuildChunkCache'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:5047 | +| 24 | 921 | 0.7027 | engine | [Inline Frame] TArray >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 25 | 883 | 0.6737 | plugin | `anonymous namespace'::VF_EvaluateSweptTunnelChain() | Plugin/Private/VoxelCaveMorphology.cpp:971 | +| 26 | 813 | 0.6203 | plugin | [Inline Frame] ?A0x7db52731::VF_ProjectTunnelSegmentXY() | Plugin/Private/VoxelCaveMorphology.cpp:950 | +| 27 | 757 | 0.5775 | plugin | ``anonymous namespace'::VF_EvaluateSweptTunnelChain'::`7'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:1058 | +| 28 | 745 | 0.5684 | plugin | VoxelDensityProfile::FScopedTimer::~FScopedTimer() | Plugin/Private/VoxelDensityProfile.cpp:969 | +| 29 | 731 | 0.5577 | engine | [Inline Frame] UE::Math::TVector::operator/() | Engine/Runtime/Core/Public/Math/Vector.h:1585 | +| 30 | 703 | 0.5363 | engine | [Inline Frame] FGenericPlatformMath::Max() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:968 | + +#### Inclusive functions (once per sample) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 131072 | 100.0000 | engine | [Inline Frame] Invoke() | Engine/Runtime/Core/Public/Templates/Invoke.h:47 | +| 2 | 131072 | 100.0000 | engine | [Inline Frame] LowLevelTasks::FTask::Init::__l13::::operator()() | Engine/Runtime/Core/Public/Async/Fundamental/Task.h:499 | +| 3 | 131072 | 100.0000 | engine | [Inline Frame] LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::Call() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:162 | +| 4 | 131072 | 100.0000 | engine | [Inline Frame] UE::Tasks::Private::FTaskBase::Init::__l2::::operator()() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:180 | +| 5 | 131072 | 100.0000 | engine | LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::CallAndMove() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:171 | +| 6 | 131072 | 100.0000 | engine | UE::Tasks::Private::FTaskBase::TryExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:518 | +| 7 | 131072 | 100.0000 | engine | UE::Tasks::Private::TExecutableTaskBase<`AVoxelWorld::LoadTile'::`2'::,void,void>::ExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:898 | +| 8 | 131072 | 100.0000 | plugin | `AVoxelWorld::LoadTile'::`2'::::operator()() | Plugin/Private/VoxelWorld.cpp:3766 | +| 9 | 131071 | 99.9992 | plugin | AVoxelWorld::GenerateTileResult() | Plugin/Private/VoxelWorld.cpp:4078 | +| 10 | 130985 | 99.9336 | plugin | UVoxelMarchingCubesMesher::GenerateMesh() | Plugin/Private/VoxelMarchingCubesMesher.cpp:574 | +| 11 | 129786 | 99.0189 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:2449 | +| 12 | 105813 | 80.7289 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2800 | +| 13 | 73739 | 56.2584 | plugin | VoxelCaveMorphology::BuildChunkCache() | Plugin/Private/VoxelCaveMorphology.cpp:5276 | +| 14 | 67864 | 51.7761 | plugin | [Inline Frame] VoxelCaveMorphology::BuildChunkCache::__l2::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:5153 | +| 15 | 67861 | 51.7738 | plugin | `anonymous namespace'::VF_FindPlayerFitPointForRoom() | Plugin/Private/VoxelCaveMorphology.cpp:2687 | +| 16 | 67251 | 51.3084 | plugin | `anonymous namespace'::VF_ValidatePlayerFitPose() | Plugin/Private/VoxelCaveMorphology.cpp:2217 | +| 17 | 48451 | 36.9652 | engine | [Inline Frame] UE::Core::Private::Function::TFunctionRefBase::operator()() | Engine/Runtime/Core/Public/Templates/Function.h:414 | +| 18 | 48320 | 36.8652 | engine | UE::Core::Private::Function::TFunctionRefCaller<``anonymous namespace'::VF_FindPlayerFitPointForRoom'::`2':: const ,float,float,float,float>::Call() | Engine/Runtime/Core/Public/Templates/Function.h:298 | +| 19 | 47804 | 36.4716 | plugin | `anonymous namespace'::VF_EvaluateRoomLandingDensity() | Plugin/Private/VoxelCaveMorphology.cpp:2512 | +| 20 | 35692 | 27.2308 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2387 | +| 21 | 29215 | 22.2893 | plugin | [Inline Frame] VoxelNoise::Perlin3D() | Plugin/Public/VoxelNoise.h:100 | +| 22 | 14512 | 11.0718 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2039 | +| 23 | 13476 | 10.2814 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 24 | 8855 | 6.7558 | plugin | VoxelCaveMorphology::EvaluateSDFCached() | Plugin/Private/VoxelCaveMorphology.cpp:6222 | +| 25 | 8494 | 6.4804 | engine | UE::Core::Private::ReallocGrow1_DoAlloc_Tiny<3,TSizedHeapAllocator<32,FMemory>::ForAnyElementType>() | Engine/Runtime/Core/Public/Containers/Array.h:463 | +| 26 | 7483 | 5.7091 | plugin | VoxelCaveMorphology::EvaluateTunnelCoreWorld() | Plugin/Private/VoxelCaveMorphology.cpp:6397 | +| 27 | 7217 | 5.5061 | plugin | `VoxelCaveMorphology::EvaluateTunnelCoreWorld'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6390 | +| 28 | 7156 | 5.4596 | plugin | `VoxelCaveMorphology::EvaluateSDFCached'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6214 | +| 29 | 7152 | 5.4565 | plugin | `anonymous namespace'::VF_EvaluateSweptTunnel() | Plugin/Private/VoxelCaveMorphology.cpp:1149 | +| 30 | 7150 | 5.4550 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:59 | + +#### Exclusive source lines (leaf source) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 21101 | 16.0988 | unknown | | unknown | +| 2 | 4500 | 3.4332 | plugin | [Inline Frame] VoxelNoise::Detail::Lerp() | Plugin/Public/VoxelNoise.h:87 | +| 3 | 4103 | 3.1303 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 4 | 3252 | 2.4811 | engine | [Inline Frame] TArray > >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 5 | 3098 | 2.3636 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:170 | +| 6 | 3004 | 2.2919 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 7 | 2865 | 2.1858 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:83 | +| 8 | 2531 | 1.9310 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:82 | +| 9 | 2503 | 1.9096 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:59 | +| 10 | 2422 | 1.8478 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:80 | +| 11 | 1964 | 1.4984 | engine | [Inline Frame] UE::Math::TVector2::{ctor}() | Engine/Runtime/Core/Public/Math/Vector2D.h:837 | +| 12 | 1867 | 1.4244 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2382 | +| 13 | 1821 | 1.3893 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:68 | +| 14 | 1817 | 1.3863 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:58 | +| 15 | 1714 | 1.3077 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2644 | +| 16 | 1705 | 1.3008 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2648 | +| 17 | 1612 | 1.2299 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:54 | +| 18 | 1603 | 1.2230 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:79 | +| 19 | 1584 | 1.2085 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2652 | +| 20 | 1373 | 1.0475 | engine | [Inline Frame] FGenericPlatformMath::Min() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:990 | +| 21 | 1295 | 0.9880 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2377 | +| 22 | 1218 | 0.9293 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:57 | +| 23 | 1216 | 0.9277 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2049 | +| 24 | 1214 | 0.9262 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2392 | +| 25 | 1063 | 0.8110 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:67 | +| 26 | 1043 | 0.7957 | engine | [Inline Frame] TArray,TSizedDefaultAllocator<32> >::Emplace() | Engine/Runtime/Core/Public/Containers/Array.h:2602 | +| 27 | 1025 | 0.7820 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:84 | +| 28 | 1018 | 0.7767 | plugin | [Inline Frame] ?A0x7db52731::VF_IsPointInsidePlayerCapsule() | Plugin/Private/VoxelCaveMorphology.cpp:1993 | +| 29 | 951 | 0.7256 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:3652 | +| 30 | 943 | 0.7195 | plugin | `VoxelCaveMorphology::EvaluateSDFCached'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6192 | + +### LOD0 (floor) + +#### Exclusive functions (leaf) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 5755 | 13.8785 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2648 | +| 2 | 5003 | 12.0650 | unknown | | unknown | +| 3 | 1859 | 4.4831 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:1771 | +| 4 | 1589 | 3.8320 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 5 | 1554 | 3.7476 | plugin | `anonymous namespace'::VF_ProjectNativePassageFloor() | Plugin/Private/VoxelStrateManager.cpp:466 | +| 6 | 1372 | 3.3087 | engine | [Inline Frame] UE::Math::TVector2::{ctor}() | Engine/Runtime/Core/Public/Math/Vector2D.h:837 | +| 7 | 1319 | 3.1808 | plugin | `VoxelCaveMorphology::EvaluateTunnelCoreWorld'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6313 | +| 8 | 1281 | 3.0892 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:170 | +| 9 | 974 | 2.3489 | plugin | `VoxelCaveMorphology::EvaluateSDFCached'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6214 | +| 10 | 906 | 2.1849 | plugin | [Inline Frame] ?A0x7db52731::VF_DistanceSquaredToAabb() | Plugin/Private/VoxelCaveMorphology.cpp:71 | +| 11 | 891 | 2.1487 | engine | [Inline Frame] FGenericPlatformMath::Min() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:990 | +| 12 | 668 | 1.6109 | engine | [Inline Frame] TArray >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 13 | 654 | 1.5772 | plugin | `anonymous namespace'::VF_EvaluateSweptTunnelChain() | Plugin/Private/VoxelCaveMorphology.cpp:971 | +| 14 | 600 | 1.4469 | plugin | [Inline Frame] ?A0x7db52731::VF_ProjectTunnelSegmentXY() | Plugin/Private/VoxelCaveMorphology.cpp:950 | +| 15 | 541 | 1.3047 | plugin | ``anonymous namespace'::VF_EvaluateSweptTunnelChain'::`7'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:1056 | +| 16 | 516 | 1.2444 | plugin | VF_EvaluatePassageLandingSDF() | Plugin/Private/VoxelCaveMorphology.cpp:4415 | +| 17 | 486 | 1.1720 | engine | [Inline Frame] UE::Math::TVector2::SizeSquared() | Engine/Runtime/Core/Public/Math/Vector2D.h:1119 | +| 18 | 479 | 1.1551 | plugin | VoxelDensityProfile::FScopedTimer::~FScopedTimer() | Plugin/Private/VoxelDensityProfile.cpp:969 | +| 19 | 468 | 1.1286 | engine | [Inline Frame] TArray,TSizedDefaultAllocator<32> >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 20 | 460 | 1.1093 | plugin | [Inline Frame] VoxelDensityProfile::FScopedTimer::{ctor}() | Plugin/Public/VoxelDensityProfile.h:393 | +| 21 | 407 | 0.9815 | plugin | UVoxelStrateManager::EvaluateModifierSDF() | Plugin/Private/VoxelStrateManager.cpp:2277 | +| 22 | 406 | 0.9791 | plugin | UVoxelMarchingCubesMesher::GenerateMesh() | Plugin/Private/VoxelMarchingCubesMesher.cpp:574 | +| 23 | 397 | 0.9574 | plugin | ApplyDisturbances() | Plugin/Private/VoxelGenerator.cpp:533 | +| 24 | 385 | 0.9284 | plugin | [Inline Frame] VoxelDensityProfile::FScopedTimer::End() | Plugin/Private/VoxelDensityProfile.cpp:975 | +| 25 | 361 | 0.8706 | plugin | UVoxelStrateManager::ApplyPassageStructuralPostsMC() | Plugin/Private/VoxelStrateManager.cpp:2521 | +| 26 | 328 | 0.7910 | plugin | `anonymous namespace'::VF_GetNearbyPassages() | Plugin/Private/VoxelStrateManager.cpp:246 | +| 27 | 327 | 0.7886 | engine | [Inline Frame] UE4::SSE::FloorToInt32() | Engine/Runtime/Core/Public/Math/UnrealPlatformMathSSE.h:90 | +| 28 | 320 | 0.7717 | plugin | UVoxelStrateManager::UsesOperatorStackForChunk() | Plugin/Private/VoxelStrateManager.cpp:4932 | +| 29 | 313 | 0.7548 | engine | [Inline Frame] TArray >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 30 | 309 | 0.7452 | plugin | `VoxelCaveMorphology::EvaluateSDFCached'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6060 | + +#### Inclusive functions (once per sample) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 41467 | 100.0000 | engine | [Inline Frame] Invoke() | Engine/Runtime/Core/Public/Templates/Invoke.h:47 | +| 2 | 41467 | 100.0000 | engine | [Inline Frame] LowLevelTasks::FTask::Init::__l13::::operator()() | Engine/Runtime/Core/Public/Async/Fundamental/Task.h:499 | +| 3 | 41467 | 100.0000 | engine | [Inline Frame] LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::Call() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:162 | +| 4 | 41467 | 100.0000 | engine | [Inline Frame] UE::Tasks::Private::FTaskBase::Init::__l2::::operator()() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:180 | +| 5 | 41467 | 100.0000 | engine | LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::CallAndMove() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:171 | +| 6 | 41467 | 100.0000 | engine | UE::Tasks::Private::FTaskBase::TryExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:518 | +| 7 | 41467 | 100.0000 | engine | UE::Tasks::Private::TExecutableTaskBase<`AVoxelWorld::LoadTile'::`2'::,void,void>::ExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:898 | +| 8 | 41467 | 100.0000 | plugin | `AVoxelWorld::LoadTile'::`2'::::operator()() | Plugin/Private/VoxelWorld.cpp:3766 | +| 9 | 41466 | 99.9976 | plugin | AVoxelWorld::GenerateTileResult() | Plugin/Private/VoxelWorld.cpp:4078 | +| 10 | 41416 | 99.8770 | plugin | UVoxelMarchingCubesMesher::GenerateMesh() | Plugin/Private/VoxelMarchingCubesMesher.cpp:574 | +| 11 | 40598 | 97.9044 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:2449 | +| 12 | 24328 | 58.6683 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2800 | +| 13 | 6307 | 15.2097 | plugin | VoxelCaveMorphology::EvaluateSDFCached() | Plugin/Private/VoxelCaveMorphology.cpp:6222 | +| 14 | 5363 | 12.9332 | plugin | VoxelCaveMorphology::EvaluateTunnelCoreWorld() | Plugin/Private/VoxelCaveMorphology.cpp:6397 | +| 15 | 5178 | 12.4870 | plugin | `VoxelCaveMorphology::EvaluateTunnelCoreWorld'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6390 | +| 16 | 5148 | 12.4147 | plugin | `anonymous namespace'::VF_EvaluateSweptTunnel() | Plugin/Private/VoxelCaveMorphology.cpp:1149 | +| 17 | 5145 | 12.4075 | plugin | `VoxelCaveMorphology::EvaluateSDFCached'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6214 | +| 18 | 4937 | 11.9059 | plugin | `anonymous namespace'::VF_EvaluateSweptTunnelChain() | Plugin/Private/VoxelCaveMorphology.cpp:992 | +| 19 | 4710 | 11.3584 | plugin | UVoxelStrateManager::ApplyPassageStructuralPostsMC() | Plugin/Private/VoxelStrateManager.cpp:2574 | +| 20 | 4663 | 11.2451 | plugin | UVoxelStrateManager::ApplyPassageCarvingOnly() | Plugin/Private/VoxelStrateManager.cpp:2373 | +| 21 | 4561 | 10.9991 | plugin | [Inline Frame] VF_ApplyPassageCarving() | Plugin/Public/VoxelDensityPrimitives.h:75 | +| 22 | 4556 | 10.9870 | plugin | UVoxelStrateManager::EvaluateModifierSDF() | Plugin/Private/VoxelStrateManager.cpp:2234 | +| 23 | 3711 | 8.9493 | plugin | `anonymous namespace'::VF_ProjectNativePassageFloor() | Plugin/Private/VoxelStrateManager.cpp:466 | +| 24 | 2459 | 5.9300 | plugin | [Inline Frame] VF_IsWalkableTunnelAir() | Plugin/Private/VoxelStrateManager.cpp:714 | +| 25 | 2441 | 5.8866 | plugin | `anonymous namespace'::VF_ProjectPassageFloor() | Plugin/Private/VoxelStrateManager.cpp:550 | +| 26 | 1994 | 4.8086 | plugin | [Inline Frame] VoxelSDF::TaperedCapsule() | Plugin/Public/VoxelCaveMorphology.h:159 | +| 27 | 1679 | 4.0490 | plugin | ``anonymous namespace'::VF_EvaluateSweptTunnelChain'::`7'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:1058 | +| 28 | 1663 | 4.0104 | plugin | UVoxelStrateManager::ApplyPassageNativeFloorMC() | Plugin/Private/VoxelStrateManager.cpp:2658 | +| 29 | 1643 | 3.9622 | plugin | VoxelCaveMorphology::BuildChunkCache() | Plugin/Private/VoxelCaveMorphology.cpp:5276 | +| 30 | 1615 | 3.8947 | engine | [Inline Frame] FGenericPlatformMath::Min() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:990 | + +#### Exclusive source lines (leaf source) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 5004 | 12.0674 | unknown | | unknown | +| 2 | 1888 | 4.5530 | engine | [Inline Frame] TArray >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 3 | 1489 | 3.5908 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 4 | 1372 | 3.3087 | engine | [Inline Frame] UE::Math::TVector2::{ctor}() | Engine/Runtime/Core/Public/Math/Vector2D.h:837 | +| 5 | 1150 | 2.7733 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2644 | +| 6 | 1146 | 2.7636 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2648 | +| 7 | 1107 | 2.6696 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:170 | +| 8 | 1081 | 2.6069 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2652 | +| 9 | 891 | 2.1487 | engine | [Inline Frame] FGenericPlatformMath::Min() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:990 | +| 10 | 671 | 1.6182 | plugin | `VoxelCaveMorphology::EvaluateSDFCached'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6192 | +| 11 | 646 | 1.5579 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:3652 | +| 12 | 616 | 1.4855 | plugin | `VoxelCaveMorphology::EvaluateTunnelCoreWorld'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6313 | +| 13 | 486 | 1.1720 | engine | [Inline Frame] UE::Math::TVector2::SizeSquared() | Engine/Runtime/Core/Public/Math/Vector2D.h:1119 | +| 14 | 484 | 1.1672 | plugin | VoxelDensityProfile::FScopedTimer::End() | Plugin/Private/VoxelDensityProfile.cpp:975 | +| 15 | 447 | 1.0780 | plugin | [Inline Frame] VoxelDensityProfile::FScopedTimer::{ctor}() | Plugin/Public/VoxelDensityProfile.h:393 | +| 16 | 344 | 0.8296 | plugin | [Inline Frame] ?A0x7db52731::VF_DistanceSquaredToAabb() | Plugin/Private/VoxelCaveMorphology.cpp:62 | +| 17 | 343 | 0.8272 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:3615 | +| 18 | 327 | 0.7886 | engine | [Inline Frame] UE4::SSE::FloorToInt32() | Engine/Runtime/Core/Public/Math/UnrealPlatformMathSSE.h:90 | +| 19 | 327 | 0.7886 | plugin | `anonymous namespace'::VF_ProjectNativePassageFloor() | Plugin/Private/VoxelStrateManager.cpp:471 | +| 20 | 298 | 0.7186 | engine | [Inline Frame] UE::Math::TVector::operator*() | Engine/Runtime/Core/Public/Math/Vector.h:316 | +| 21 | 274 | 0.6608 | plugin | VoxelDensityProfile::FScopedTimer::~FScopedTimer() | Plugin/Private/VoxelDensityProfile.cpp:969 | +| 22 | 251 | 0.6053 | plugin | [Inline Frame] ?A0x7db52731::VF_ProjectTunnelSegmentXY() | Plugin/Private/VoxelCaveMorphology.cpp:955 | +| 23 | 240 | 0.5788 | plugin | ``anonymous namespace'::VF_EvaluateSweptTunnelChain'::`7'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:1056 | +| 24 | 220 | 0.5305 | plugin | [Inline Frame] ?A0x7db52731::VF_DistanceSquaredToAabb() | Plugin/Private/VoxelCaveMorphology.cpp:68 | +| 25 | 218 | 0.5257 | plugin | ``anonymous namespace'::VF_EvaluateSweptTunnelChain'::`7'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:1058 | +| 26 | 217 | 0.5233 | engine | [Inline Frame] FGenericPlatformMath::Max() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:968 | +| 27 | 213 | 0.5137 | plugin | VF_EvaluatePassageLandingSDF() | Plugin/Private/VoxelCaveMorphology.cpp:4394 | +| 28 | 205 | 0.4944 | plugin | VoxelDensityProfile::FScopedTimer::~FScopedTimer() | Plugin/Private/VoxelDensityProfile.cpp:971 | +| 29 | 204 | 0.4920 | engine | [Inline Frame] FMath::Square() | Engine/Runtime/Core/Public/Math/UnrealMathUtility.h:580 | +| 30 | 204 | 0.4920 | engine | [Inline Frame] UE::Math::TVector::operator-() | Engine/Runtime/Core/Public/Math/Vector.h:1573 | + +### LOD1+ (coarse/background) + +#### Exclusive functions (leaf) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 16080 | 17.9454 | unknown | | unknown | +| 2 | 13179 | 14.7079 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 3 | 6983 | 7.7931 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:54 | +| 4 | 5204 | 5.8077 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2382 | +| 5 | 4419 | 4.9316 | plugin | [Inline Frame] VoxelNoise::Detail::Lerp() | Plugin/Public/VoxelNoise.h:87 | +| 6 | 2936 | 3.2766 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:68 | +| 7 | 2863 | 3.1951 | plugin | `anonymous namespace'::VF_EvaluateRoomLandingDensity() | Plugin/Private/VoxelCaveMorphology.cpp:2576 | +| 8 | 2677 | 2.9876 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:3650 | +| 9 | 2657 | 2.9652 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 10 | 2578 | 2.8771 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2049 | +| 11 | 2281 | 2.5456 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:170 | +| 12 | 1629 | 1.8180 | plugin | [Inline Frame] ?A0x7db52731::VF_IsPointInsidePlayerCapsule() | Plugin/Private/VoxelCaveMorphology.cpp:1993 | +| 13 | 1203 | 1.3426 | plugin | `anonymous namespace'::VF_ValidatePlayerFitPose() | Plugin/Private/VoxelCaveMorphology.cpp:2207 | +| 14 | 1196 | 1.3347 | plugin | [Inline Frame] VoxelHash::Mix() | Plugin/Public/VoxelCaveMorphology.h:217 | +| 15 | 1123 | 1.2533 | engine | [Inline Frame] TArray,TSizedDefaultAllocator<32> >::Emplace() | Engine/Runtime/Core/Public/Containers/Array.h:2602 | +| 16 | 980 | 1.0937 | plugin | `VoxelCaveMorphology::BuildChunkCache'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:5047 | +| 17 | 825 | 0.9207 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:1673 | +| 18 | 711 | 0.7935 | engine | [Inline Frame] UE::Math::TVector::operator/() | Engine/Runtime/Core/Public/Math/Vector.h:1585 | +| 19 | 657 | 0.7332 | plugin | `anonymous namespace'::VF_ProjectNativePassageFloor() | Plugin/Private/VoxelStrateManager.cpp:452 | +| 20 | 592 | 0.6607 | engine | [Inline Frame] UE::Math::TVector2::{ctor}() | Engine/Runtime/Core/Public/Math/Vector2D.h:837 | +| 21 | 578 | 0.6451 | plugin | [Inline Frame] VoxelNoise::Perlin3D() | Plugin/Public/VoxelNoise.h:98 | +| 22 | 526 | 0.5870 | plugin | `VoxelCaveMorphology::EvaluateTunnelCoreWorld'::`2'::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:6313 | +| 23 | 513 | 0.5725 | plugin | VoxelCaveMorphology::BuildChunkCache() | Plugin/Private/VoxelCaveMorphology.cpp:5192 | +| 24 | 504 | 0.5625 | engine | [Inline Frame] UE4::SSE4::FloorToFloat() | Engine/Runtime/Core/Public/Math/UnrealPlatformMathSSE4.h:34 | +| 25 | 496 | 0.5535 | plugin | [Inline Frame] ?A0x7db52731::VF_GetFloorReliefBound() | Plugin/Private/VoxelCaveMorphology.cpp:296 | +| 26 | 482 | 0.5379 | engine | [Inline Frame] FGenericPlatformMath::Min() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:990 | +| 27 | 438 | 0.4888 | plugin | [Inline Frame] `anonymous-namespace'::VF_FindPlayerFitPointForRoom::__l2::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:2629 | +| 28 | 430 | 0.4799 | engine | [Inline Frame] TArray > >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 29 | 425 | 0.4743 | engine | [Inline Frame] FGenericPlatformMath::Max() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:968 | +| 30 | 400 | 0.4464 | engine | [Inline Frame] FMicrosoftPlatformMathBase::IsFinite() | Engine/Runtime/Core/Public/Microsoft/MicrosoftPlatformMath.h:18 | + +#### Inclusive functions (once per sample) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 89605 | 100.0000 | engine | [Inline Frame] Invoke() | Engine/Runtime/Core/Public/Templates/Invoke.h:47 | +| 2 | 89605 | 100.0000 | engine | [Inline Frame] LowLevelTasks::FTask::Init::__l13::::operator()() | Engine/Runtime/Core/Public/Async/Fundamental/Task.h:499 | +| 3 | 89605 | 100.0000 | engine | [Inline Frame] LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::Call() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:162 | +| 4 | 89605 | 100.0000 | engine | [Inline Frame] UE::Tasks::Private::FTaskBase::Init::__l2::::operator()() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:180 | +| 5 | 89605 | 100.0000 | engine | LowLevelTasks::TTaskDelegate::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2':: >'::`13'::,0>::CallAndMove() | Engine/Runtime/Core/Public/Async/Fundamental/TaskDelegate.h:171 | +| 6 | 89605 | 100.0000 | engine | UE::Tasks::Private::FTaskBase::TryExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:518 | +| 7 | 89605 | 100.0000 | engine | UE::Tasks::Private::TExecutableTaskBase<`AVoxelWorld::LoadTile'::`2'::,void,void>::ExecuteTask() | Engine/Runtime/Core/Public/Tasks/TaskPrivate.h:898 | +| 8 | 89605 | 100.0000 | plugin | `AVoxelWorld::LoadTile'::`2'::::operator()() | Plugin/Private/VoxelWorld.cpp:3766 | +| 9 | 89605 | 100.0000 | plugin | AVoxelWorld::GenerateTileResult() | Plugin/Private/VoxelWorld.cpp:4078 | +| 10 | 89569 | 99.9598 | plugin | UVoxelMarchingCubesMesher::GenerateMesh() | Plugin/Private/VoxelMarchingCubesMesher.cpp:574 | +| 11 | 89188 | 99.5346 | plugin | UVoxelGenerator::GetDensityAt() | Plugin/Private/VoxelGenerator.cpp:2073 | +| 12 | 81485 | 90.9380 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2800 | +| 13 | 72096 | 80.4598 | plugin | VoxelCaveMorphology::BuildChunkCache() | Plugin/Private/VoxelCaveMorphology.cpp:4974 | +| 14 | 66315 | 74.0081 | plugin | [Inline Frame] VoxelCaveMorphology::BuildChunkCache::__l2::::operator()() | Plugin/Private/VoxelCaveMorphology.cpp:5153 | +| 15 | 66312 | 74.0048 | plugin | `anonymous namespace'::VF_FindPlayerFitPointForRoom() | Plugin/Private/VoxelCaveMorphology.cpp:2687 | +| 16 | 65715 | 73.3385 | plugin | `anonymous namespace'::VF_ValidatePlayerFitPose() | Plugin/Private/VoxelCaveMorphology.cpp:2221 | +| 17 | 47401 | 52.8999 | engine | [Inline Frame] UE::Core::Private::Function::TFunctionRefBase::operator()() | Engine/Runtime/Core/Public/Templates/Function.h:414 | +| 18 | 47272 | 52.7560 | engine | UE::Core::Private::Function::TFunctionRefCaller<``anonymous namespace'::VF_FindPlayerFitPointForRoom'::`2':: const ,float,float,float,float>::Call() | Engine/Runtime/Core/Public/Templates/Function.h:298 | +| 19 | 46764 | 52.1891 | plugin | `anonymous namespace'::VF_EvaluateRoomLandingDensity() | Plugin/Private/VoxelCaveMorphology.cpp:2577 | +| 20 | 34914 | 38.9643 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2377 | +| 21 | 28597 | 31.9145 | plugin | [Inline Frame] VoxelNoise::Perlin3D() | Plugin/Public/VoxelNoise.h:118 | +| 22 | 14149 | 15.7904 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2049 | +| 23 | 13179 | 14.7079 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 24 | 8269 | 9.2283 | engine | UE::Core::Private::ReallocGrow1_DoAlloc_Tiny<3,TSizedHeapAllocator<32,FMemory>::ForAnyElementType>() | Engine/Runtime/Core/Public/Containers/Array.h:463 | +| 25 | 6983 | 7.7931 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:54 | +| 26 | 4419 | 4.9316 | plugin | [Inline Frame] VoxelNoise::Detail::Lerp() | Plugin/Public/VoxelNoise.h:87 | +| 27 | 2936 | 3.2766 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:68 | +| 28 | 2657 | 2.9652 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 29 | 2548 | 2.8436 | plugin | VoxelCaveMorphology::EvaluateSDFCached() | Plugin/Private/VoxelCaveMorphology.cpp:6222 | +| 30 | 2426 | 2.7074 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:170 | + +#### Exclusive source lines (leaf source) + +| # | Samples | % | Label | Function | Source | +|---:|---:|---:|---|---|---| +| 1 | 16097 | 17.9644 | unknown | | unknown | +| 2 | 4419 | 4.9316 | plugin | [Inline Frame] VoxelNoise::Detail::Lerp() | Plugin/Public/VoxelNoise.h:87 | +| 3 | 2936 | 3.2766 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:77 | +| 4 | 2803 | 3.1282 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:83 | +| 5 | 2614 | 2.9172 | engine | [Inline Frame] FGenericPlatformMath::Sqrt() | Engine/Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h:553 | +| 6 | 2475 | 2.7621 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:82 | +| 7 | 2453 | 2.7376 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:59 | +| 8 | 2374 | 2.6494 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:80 | +| 9 | 1991 | 2.2220 | plugin | [Inline Frame] VoxelSDF::SmoothMin() | Plugin/Public/VoxelCaveMorphology.h:170 | +| 10 | 1822 | 2.0334 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2382 | +| 11 | 1789 | 1.9965 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:68 | +| 12 | 1773 | 1.9787 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:58 | +| 13 | 1570 | 1.7521 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:54 | +| 14 | 1567 | 1.7488 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:79 | +| 15 | 1364 | 1.5222 | engine | [Inline Frame] TArray > >::RangeCheck() | Engine/Runtime/Core/Public/Containers/Array.h:1095 | +| 16 | 1269 | 1.4162 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2377 | +| 17 | 1187 | 1.3247 | plugin | [Inline Frame] VoxelNoise::Detail::HashCorner() | Plugin/Public/VoxelNoise.h:57 | +| 18 | 1184 | 1.3214 | plugin | `anonymous namespace'::VF_ApplyCaveWarp() | Plugin/Private/VoxelCaveMorphology.cpp:2392 | +| 19 | 1180 | 1.3169 | plugin | `anonymous namespace'::VF_BuildPlayerFitStencil() | Plugin/Private/VoxelCaveMorphology.cpp:2049 | +| 20 | 1045 | 1.1662 | plugin | [Inline Frame] VoxelNoise::Detail::Fade() | Plugin/Public/VoxelNoise.h:67 | +| 21 | 1013 | 1.1305 | engine | [Inline Frame] TArray,TSizedDefaultAllocator<32> >::Emplace() | Engine/Runtime/Core/Public/Containers/Array.h:2602 | +| 22 | 998 | 1.1138 | plugin | [Inline Frame] VoxelNoise::Detail::GradDot() | Plugin/Public/VoxelNoise.h:84 | +| 23 | 997 | 1.1127 | plugin | [Inline Frame] ?A0x7db52731::VF_IsPointInsidePlayerCapsule() | Plugin/Private/VoxelCaveMorphology.cpp:1993 | +| 24 | 754 | 0.8415 | plugin | `anonymous namespace'::VF_EvaluateRoomLandingDensity() | Plugin/Private/VoxelCaveMorphology.cpp:2509 | +| 25 | 711 | 0.7935 | engine | [Inline Frame] UE::Math::TVector::operator/() | Engine/Runtime/Core/Public/Math/Vector.h:1585 | +| 26 | 628 | 0.7009 | plugin | [Inline Frame] ?A0x7db52731::VF_IsPointInsidePlayerCapsule() | Plugin/Private/VoxelCaveMorphology.cpp:1995 | +| 27 | 592 | 0.6607 | engine | [Inline Frame] UE::Math::TVector2::{ctor}() | Engine/Runtime/Core/Public/Math/Vector2D.h:837 | +| 28 | 564 | 0.6294 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2644 | +| 29 | 559 | 0.6238 | plugin | UVoxelGenerator::GetDensityWithParams() | Plugin/Private/VoxelGenerator.cpp:2648 | +| 30 | 504 | 0.5625 | engine | [Inline Frame] UE4::SSE4::FloorToFloat() | Engine/Runtime/Core/Public/Math/UnrealPlatformMathSSE4.h:34 | + + +## Component mapping + +The percentages below are inclusive unless marked exclusive/source-line. They are LOD0 shares where the LOD0 row is available; all-LOD figures are called out for work dominated by coarse tiles. Inclusive values overlap and must not be added. + +| Component | Static | Moving | Reading | +|---|---:|---:|---| +| Fused evaluator core, GetDensityWithParams | 55.38% LOD0 inclusive; 90.36% all-LOD | 58.67% LOD0 inclusive; 80.73% all-LOD | The evaluator is the parent of most density work. | +| Detail noise | GradDot 14.81%, HashCorner 7.67%, Lerp 4.87%, Fade 3.13% all-LOD exclusive leaves | GradDot 14.71%, HashCorner 7.79%, Lerp 4.93%, Fade 3.28% all-LOD exclusive leaves | Roughly 30% across these four resolved leaves before other noise helpers; this is a real arithmetic cost, not a wait frame. | +| Swept tunnel shape | VF_EvaluateSweptTunnel 11.56%; chain 10.99% LOD0 inclusive | 12.41%; chain 11.91% LOD0 inclusive | The chain is nested, so do not sum the two rows. | +| Tunnel floor projection | VF_ProjectNativePassageFloor 11.41%; VF_ProjectPassageFloor 7.45% LOD0 inclusive | 8.95%; 5.89% LOD0 inclusive | The current symbol names are the native/passage-floor implementations of the requested swept-floor component. | +| Tunnel core | EvaluateTunnelCoreWorld 12.39% and its lambda 11.83% LOD0 inclusive | 12.93% and 12.49% LOD0 inclusive | The core path is independently hot; the relevant implementation is around VoxelCaveMorphology.cpp:6275 and the resolved body at the later lambda lines. | +| Room graph / player fit | BuildChunkCache 79.54%, FindPlayerFitPointForRoom 73.35%, room landing 51.87% all-LOD inclusive | 80.46%, 74.00%, 52.19% all-LOD inclusive | This is mostly LOD1+ background work; LOD0's hot list shifts toward density/tunnel/floor work. | +| Candidate traversal | DistanceSquaredToAabb 1.53% LOD0 exclusive; floor-relief bound is 0.59% in LOD1+ | 2.18% LOD0 exclusive; floor-relief bound is 0.55% in LOD1+ | Cheap compared with tunnel/core evaluation. | +| Disturbances | ApplyDisturbances 0.7598% LOD0 exclusive | 0.9574% LOD0 exclusive | Not a top cost. | +| Post-stack structural tail | ApplyPassageStructuralPostsMC 14.47%, carving 14.07% LOD0 inclusive | 11.36%, carving 11.25% LOD0 inclusive | This is a material LOD0 tail after the density stack. | +| Surface column | FSurfaceColumn constructor 11.6522% LOD0 exclusive/source line; source file unresolved | 12.0674% LOD0 exclusive/source line; source file unresolved | The symbol is known but the source location is not; keep it as an unknown-source cost until PDB/source mapping is improved. | +| Per-sample FStrateGenerationParams copy | No isolated top-30 function or source-line share | No isolated top-30 function or source-line share | The ~74-field copy noted at VoxelDensityOpStack.cpp:3946 is folded into its caller or below the sampled leaf threshold; this profile cannot claim it is free or expensive. | +| Mesher | GenerateMesh 99.8692% LOD0 inclusive | 99.8770% LOD0 inclusive | This is the registered task parent, not an additive exclusive cost. Density is inside it. | + +The old component names cover the main LOD0 path, but the profile adds two important named costs: room-cache/player-fit/landing work for coarse tiles and FSurfaceColumn construction in LOD0. Those are the clearest candidates for the cost that the old 34f06df scope view did not name. + +## Hypotheses + +- Swept tunnel evaluated twice: supported. The LOD0 stack repeatedly contains both VF_EvaluateSweptTunnel and EvaluateTunnelCoreWorld at roughly 11.6–12.9% inclusive each, and the existing counters report the tunnel and tunnel-core candidate populations both at 28,951,420. This is measured duplication evidence; proving that every candidate takes both full paths still requires a targeted call counter. +- Candidate rejection cheap, evaluation expensive: held. AABB distance is about 1.5–2.2% exclusive in LOD0, while swept-tunnel and tunnel-core bodies are each about 11.6–12.9% inclusive. The scan is not the 4.5x-sized cost. +- Per-sample parameter copy is a standalone cost: not proven. It never appears as an isolated top-30 function/source line in either headline run. +- A cost absent from the old named list exists: supported, but not uniquely differential. Room graph/player fit/landing dominates LOD1+ and FSurfaceColumn is a material LOD0 leaf. The sampler cannot by itself prove which of those was absent from 34f06df without profiling that old build. +- Static and moving game profiles differ materially. Static retained samples were 12.2% LOD0 and 87.8% LOD1+; moving was 31.6% LOD0 and 68.4% LOD1+. Moving therefore exposes more floor/tunnel/core/structural work, while static is more heavily diluted by coarse room-graph work. + +## Planted-cost proof + +Before trusting the sampler, a diagnostic-only 20000 us busy loop was planted in one function, VF_SampleStacksPlantedBusyLoop, for a sampler-on commandlet run. The summary retained 712 samples with 712 registered samples and no reservoir drops. The planted function was: + +- exclusive: rank 21, 8 samples, 1.1236%; +- inclusive: rank 20, 75 samples, 10.5337%. + +That is the expected approximately 10–12% share for the planted duration in the active worker interval. The plant was then removed; a source search for SampleStacksPlant and PlantedBusyLoop is clean. + +## Secondary export cross-check + +The owner redirected this round to the game path, so no further export tables were run. The already retained primary export cross-check remains: + +- sampler-off mesh times: 0.971 s, 0.992 s, 0.954 s; mean 0.972333 s; +- sampler-on mesh times: 0.951 s, 0.964 s, 0.967 s; mean 0.960667 s; +- all off/on OBJ SHA-256 values: b3e5f4c398dd0547c6c55f415872058246d4a292c2f0d636ee1cd0829de9b377. + +## Ranked fixes for the game path + +These are measurement-derived estimates, not changes made this round. The ranges are expected LOD0 request-to-ready p95 and worker-CPU gains, assuming the relevant work can be removed or reused without changing scheduling. + +1. Share one exact swept-tunnel result between the SDF path and EvaluateTunnelCoreWorld/floor metadata: expected LOD0 p95 gain 8–15%, worker CPU gain 8–15%. Classification: field-preserving only if the exact existing values and evaluation order are reused; otherwise field-changing. Capability risk is medium because tunnel floor/core metadata affects player-fit and walk reachability, so recheck player-fit 20,830 and walk-reachable 10,909. +2. Reuse or move deterministic room-cache/player-fit work out of the LOD0 request critical path: expected LOD0 p95 gain 3–8%, worker CPU gain 5–12%. Classification: field-preserving if cache keys include seed, generator parameters, room graph, and LOD and the returned values are copied unchanged; field-changing if candidate pruning or fit thresholds change. Capability risk is medium, concentrated in player-fit and walk-reachable coverage. +3. Reuse exact detail-noise values within one sample/evaluator invocation: expected LOD0 p95 gain 4–10%, worker CPU gain 8–18%. Classification: field-preserving for exact value reuse; any approximation, altered hash, or reordered floating-point expression is field-changing. Capability risk is high for an approximation because it can alter narrow tunnel openings and walk reachability; exact reuse carries low field risk. + +No rank is assigned to the 74-field parameter copy: the sampler did not measure a standalone share, so optimizing it now would repeat the earlier fiction problem. + +## Handoff + +This round changed only diagnostic instrumentation and report artifacts under E:\Projet Unreal\VoxelM\Plugins\VoxelForge. Engine source was not modified. No commit, push, or stash was performed. The key caveat is explicit: the instrument has good closure and a successful planted-cost proof, but its 1 ms game overhead is above the requested timing/worker-CPU noise on static and above timing noise on moving. Treat the large, repeated component differences as useful; do not treat small single-digit improvements as established until that overhead is brought inside the game baseline. diff --git a/Source/VoxelForge/Private/VoxelForgeModule.cpp b/Source/VoxelForge/Private/VoxelForgeModule.cpp index 7739a44..68c774b 100644 --- a/Source/VoxelForge/Private/VoxelForgeModule.cpp +++ b/Source/VoxelForge/Private/VoxelForgeModule.cpp @@ -2,6 +2,7 @@ // Module implementation - boilerplate #include "VoxelForgeModule.h" +#include "VoxelStackSampler.h" // This macro registers our module with Unreal // "VoxelForge" must match the module name in VoxelForge.Build.cs and .uplugin @@ -17,6 +18,6 @@ void FVoxelForgeModule::StartupModule() void FVoxelForgeModule::ShutdownModule() { // Called when plugin unloads - // Cleanup would go here if we had any global resources + FVoxelStackSampler::Get().Shutdown(); UE_LOG(LogTemp, Log, TEXT("VoxelForge module shutdown.")); } diff --git a/Source/VoxelForge/Private/VoxelStackSampler.cpp b/Source/VoxelForge/Private/VoxelStackSampler.cpp new file mode 100644 index 0000000..e25caee --- /dev/null +++ b/Source/VoxelForge/Private/VoxelStackSampler.cpp @@ -0,0 +1,1051 @@ +// VoxelStackSampler.cpp +// Fixed-storage sampling and post-run symbolization for VoxelForge generation workers. + +#include "VoxelStackSampler.h" + +#include "Containers/Array.h" +#include "Containers/Map.h" +#include "Containers/Set.h" +#include "HAL/FileManager.h" +#include "HAL/PlatformProcess.h" +#include "HAL/PlatformStackWalk.h" +#include "HAL/PlatformTime.h" +#include "HAL/PlatformTLS.h" +#include "HAL/Runnable.h" +#include "HAL/RunnableThread.h" +#include "HAL/UnrealMemory.h" +#include "Math/UnrealMathUtility.h" +#include "Containers/StringConv.h" +#include "Misc/FileHelper.h" +#include "Misc/Paths.h" +#include "Serialization/Archive.h" +#include "Templates/UniquePtr.h" + +#if PLATFORM_WINDOWS +#include "Windows/WindowsPlatformStackWalk.h" +#endif + +#include +#include + +namespace +{ +constexpr uint64 RegistrationReservedBit = 1ull << 63; +constexpr uint32 MaxRegisteredThreads = 128; + +std::atomic GVoxelStackSamplerEnabled(false); + +// The registry is deliberately fixed-size and lock-free from the worker's point of view. A +// reserved state prevents two registering workers from racing on ThreadId before a slot becomes +// visible to the sampler. +struct FRegisteredThreadSlot +{ + std::atomic State{0}; + std::atomic ThreadId{0}; + std::atomic LODLevel{INDEX_NONE}; +}; + +thread_local int32 GTlsRegisteredSlot = INDEX_NONE; +thread_local uint64 GTlsRegistrationGeneration = 0; +thread_local int32 GTlsRegistrationDepth = 0; +thread_local int32 GTlsRegisteredLODLevel = INDEX_NONE; + +FString SanitizeLabel(const FString& InLabel) +{ + FString Result; + for (const TCHAR Character : InLabel) + { + const bool bAlphaNumeric = + (Character >= TEXT('a') && Character <= TEXT('z')) + || (Character >= TEXT('A') && Character <= TEXT('Z')) + || (Character >= TEXT('0') && Character <= TEXT('9')); + Result.AppendChar(bAlphaNumeric || Character == TEXT('_') || Character == TEXT('-') + ? Character : TEXT('_')); + } + return Result.IsEmpty() ? TEXT("run") : Result; +} + +void WriteUtf8(FArchive& Archive, const FString& Text) +{ + FTCHARToUTF8 Utf8(*Text); + if (Utf8.Length() > 0) + { + Archive.Serialize(const_cast(Utf8.Get()), Utf8.Length()); + } +} + +FString NormalizedForClassification(const FString& InValue) +{ + FString Result = InValue; + Result.ToLowerInline(); + Result.ReplaceInline(TEXT("\\"), TEXT("/")); + return Result; +} + +struct FResolvedSymbol +{ + FString Module; + FString Function; + FString File; + int32 Line = 0; + bool bInline = false; +}; + +struct FResolvedProgramCounter +{ + bool bAttempted = false; + TArray Symbols; +}; + +struct FProfileRow +{ + FString Key; + FString Function; + FString Module; + FString File; + FString Label; + int32 Line = 0; + uint64 Samples = 0; +}; + +struct FProfileAggregateSet +{ + TMap ExclusiveFunctions; + TMap InclusiveFunctions; + TMap ExclusiveLines; + uint64 SamplesWithStack = 0; +}; + +const FResolvedSymbol* FirstFunctionSymbol(const FResolvedProgramCounter& Entry) +{ + for (const FResolvedSymbol& Symbol : Entry.Symbols) + { + if (!Symbol.Function.IsEmpty()) + { + return &Symbol; + } + } + return nullptr; +} + +const FResolvedSymbol* FirstSourceSymbol(const FResolvedProgramCounter& Entry) +{ + for (const FResolvedSymbol& Symbol : Entry.Symbols) + { + if (!Symbol.File.IsEmpty() && Symbol.Line > 0) + { + return &Symbol; + } + } + return nullptr; +} + +FString SymbolLabel(const FResolvedSymbol& Symbol) +{ + if (Symbol.Function.IsEmpty()) + { + return TEXT("unknown"); + } + + const FString FunctionLower = NormalizedForClassification(Symbol.Function); + if (FunctionLower.Contains(TEXT("std::"))) + { + return TEXT("std"); + } + + const FString ModuleLower = NormalizedForClassification(Symbol.Module); + const FString FileLower = NormalizedForClassification(Symbol.File); + const bool bFileIsEngine = FileLower.Contains(TEXT("/engine/")); + if (bFileIsEngine) + { + return TEXT("engine"); + } + + const bool bVoxelForge = FunctionLower.Contains(TEXT("voxelforge")) + || FileLower.Contains(TEXT("/voxelforge/")) + || ModuleLower.Contains(TEXT("voxelforge")); + if (bVoxelForge) + { + return TEXT("plugin"); + } + + const bool bModuleIsEngine = ModuleLower.StartsWith(TEXT("unrealeditor-")) + || ModuleLower.StartsWith(TEXT("ue5-")); + return bModuleIsEngine ? TEXT("engine") : TEXT("other"); +} + +bool IsWaitLike(const FResolvedSymbol& Symbol) +{ + const FString FunctionLower = NormalizedForClassification(Symbol.Function); + return FunctionLower.Contains(TEXT("wait")) + || FunctionLower.Contains(TEXT("sleep")) + || FunctionLower.Contains(TEXT("semaphore")) + || FunctionLower.Contains(TEXT("fevent")); +} + +FString FunctionKey(const FResolvedSymbol& Symbol) +{ + return SymbolLabel(Symbol) + TEXT("\x1f") + Symbol.Function; +} + +FString SourceKey(const FResolvedSymbol* Symbol) +{ + if (Symbol == nullptr || Symbol->File.IsEmpty() || Symbol->Line <= 0) + { + return TEXT("unknown\x1f"); + } + return SymbolLabel(*Symbol) + TEXT("\x1f") + + FString::Printf(TEXT("%s:%d"), *Symbol->File, Symbol->Line); +} + +void AddFunctionRow( + TMap& Rows, + const FResolvedSymbol* Symbol, + uint64 SampleCount) +{ + if (Symbol == nullptr || Symbol->Function.IsEmpty()) + { + return; + } + const FString Key = FunctionKey(*Symbol); + FProfileRow& Row = Rows.FindOrAdd(Key); + if (Row.Key.IsEmpty()) + { + Row.Key = Key; + Row.Function = Symbol->Function; + Row.Module = Symbol->Module; + Row.File = Symbol->File; + Row.Line = Symbol->Line; + Row.Label = SymbolLabel(*Symbol); + } + Row.Samples += SampleCount; +} + +void AddSourceRow( + TMap& Rows, + const FResolvedSymbol* FunctionSymbol, + const FResolvedSymbol* SourceSymbol) +{ + const FString Key = SourceKey(SourceSymbol); + FProfileRow& Row = Rows.FindOrAdd(Key); + if (Row.Key.IsEmpty()) + { + Row.Key = Key; + Row.Label = SourceSymbol != nullptr ? SymbolLabel(*SourceSymbol) : TEXT("unknown"); + Row.File = SourceSymbol != nullptr ? SourceSymbol->File : TEXT(""); + Row.Line = SourceSymbol != nullptr ? SourceSymbol->Line : 0; + Row.Function = FunctionSymbol != nullptr ? FunctionSymbol->Function : TEXT(""); + Row.Module = FunctionSymbol != nullptr ? FunctionSymbol->Module : TEXT(""); + } + ++Row.Samples; +} + +TArray SortedRows(const TMap& InRows) +{ + TArray Rows; + Rows.Reserve(InRows.Num()); + for (const TPair& Pair : InRows) + { + Rows.Add(Pair.Value); + } + Rows.Sort([](const FProfileRow& A, const FProfileRow& B) + { + return A.Samples != B.Samples ? A.Samples > B.Samples : A.Key < B.Key; + }); + return Rows; +} + +void AppendRows( + FString& Out, + const TCHAR* Title, + const TMap& InRows, + uint64 Denominator, + int32 MaxRows) +{ + Out += TEXT("\n"); + Out += Title; + Out += TEXT("\nrank\tsamples\tpercent\tlabel\tfunction\tmodule\tfile\tline\n"); + const TArray Rows = SortedRows(InRows); + const int32 Count = FMath::Min(MaxRows, Rows.Num()); + for (int32 Index = 0; Index < Count; ++Index) + { + const FProfileRow& Row = Rows[Index]; + const double Percent = Denominator > 0 + ? static_cast(Row.Samples) * 100.0 / static_cast(Denominator) + : 0.0; + Out.Appendf( + TEXT("%d\t%llu\t%.4f\t%s\t%s\t%s\t%s\t%d\n"), + Index + 1, + static_cast(Row.Samples), + Percent, + *Row.Label, + *Row.Function, + *Row.Module, + *Row.File, + Row.Line); + } + if (Rows.Num() == 0) + { + Out += TEXT("(no rows)\n"); + } +} +} + +struct FVoxelStackSampler::FImpl final : public FRunnable +{ +struct FStoredSample + { + uint64 ElapsedUs = 0; + uint64 ThreadId = 0; + int32 LODLevel = INDEX_NONE; + uint32 Depth = 0; + uint64 ProgramCounters[MaxStackDepth]{}; + }; + + FVoxelStackSampler& Owner; + uint32 IntervalUs = DefaultIntervalUs; + FString RunLabel; + FString OutputDirectory; + FString RawSamplesPath; + FString SummaryPath; + double StartSeconds = 0.0; + std::atomic bStop{false}; + std::atomic NextRegistrationGeneration{1}; + FRegisteredThreadSlot Slots[MaxRegisteredThreads]; + TUniquePtr Samples; + uint32 StoredSampleCount = 0; + + uint64 SamplerTicks = 0; + uint64 CaptureAttempts = 0; + uint64 RegisteredThreadSamples = 0; + uint64 RegistrationRaceDrops = 0; + uint64 CaptureFailures = 0; + uint64 DroppedSamples = 0; + uint64 TotalFrames = 0; + uint64 ReservoirState = 0x9E3779B97F4A7C15ull; + uint64 MaxActiveRegisteredThreads = 0; + bool bStackWalkingInitialized = false; + FRunnableThread* Thread = nullptr; + + FImpl( + FVoxelStackSampler& InOwner, + uint32 InIntervalUs, + const FString& InOutputDirectory, + const FString& InRunLabel) + : Owner(InOwner) + , IntervalUs(FMath::Clamp(InIntervalUs, MinIntervalUs, MaxIntervalUs)) + , RunLabel(InRunLabel) + , OutputDirectory(InOutputDirectory) + { + const FString SafeLabel = SanitizeLabel(RunLabel); + const uint32 ProcessId = FPlatformProcess::GetCurrentProcessId(); + RawSamplesPath = FPaths::Combine( + OutputDirectory, + FString::Printf(TEXT("VoxelStackSamples_%s_%u.csv"), *SafeLabel, ProcessId)); + SummaryPath = FPaths::Combine( + OutputDirectory, + FString::Printf(TEXT("VoxelStackSummary_%s_%u.txt"), *SafeLabel, ProcessId)); + Samples = TUniquePtr(new FStoredSample[MaxStoredSamples]); + } + + virtual uint32 Run() override + { +#if PLATFORM_WINDOWS + StartSeconds = FPlatformTime::Seconds(); + double NextSampleSeconds = StartSeconds; + while (!bStop.load(std::memory_order_relaxed)) + { + const double Now = FPlatformTime::Seconds(); + if (Now < NextSampleSeconds) + { + const double SleepSeconds = NextSampleSeconds - Now; + FPlatformProcess::SleepNoStats(static_cast(FMath::Min(SleepSeconds, 0.001))); + continue; + } + + CaptureRegisteredThreads(Now); + ++SamplerTicks; + NextSampleSeconds += static_cast(IntervalUs) * 1.0e-6; + if (NextSampleSeconds < Now) + { + NextSampleSeconds = Now + static_cast(IntervalUs) * 1.0e-6; + } + } +#else + // The requested implementation is Windows-specific. Keep the runtime module buildable + // on other targets, but never pretend that a no-op is a profile. + while (!bStop.load(std::memory_order_relaxed)) + { + FPlatformProcess::SleepNoStats(0.01f); + } +#endif + return 0; + } + + virtual void Stop() override + { + bStop.store(true, std::memory_order_release); + } + + bool StartThread() + { +#if PLATFORM_WINDOWS + bStackWalkingInitialized = FPlatformStackWalk::InitStackWalking(); + StartSeconds = FPlatformTime::Seconds(); +#endif + Thread = FRunnableThread::Create(this, TEXT("VoxelStackSampler"), 0, TPri_Lowest); + return Thread != nullptr; + } + + void StopThread() + { + if (Thread == nullptr) + { + return; + } + bStop.store(true, std::memory_order_release); + Thread->WaitForCompletion(); + delete Thread; + Thread = nullptr; + } + + bool RegisterThread(int32 LODLevel) + { + const uint32 CurrentThreadId = FPlatformTLS::GetCurrentThreadId(); + if (GTlsRegistrationDepth > 0) + { + ++GTlsRegistrationDepth; + return true; + } + + const uint64 Generation = NextRegistrationGeneration.fetch_add( + 1, std::memory_order_relaxed); + const uint64 ReservedState = Generation | RegistrationReservedBit; + for (uint32 Index = 0; Index < MaxRegisteredThreads; ++Index) + { + uint64 Expected = 0; + if (!Slots[Index].State.compare_exchange_strong( + Expected, ReservedState, + std::memory_order_acq_rel, + std::memory_order_relaxed)) + { + continue; + } + + Slots[Index].ThreadId.store(CurrentThreadId, std::memory_order_relaxed); + Slots[Index].LODLevel.store(LODLevel, std::memory_order_relaxed); + Slots[Index].State.store(Generation, std::memory_order_release); + GTlsRegisteredSlot = static_cast(Index); + GTlsRegistrationGeneration = Generation; + GTlsRegistrationDepth = 1; + GTlsRegisteredLODLevel = LODLevel; + return true; + } + return false; + } + + void DeregisterThread() + { + if (GTlsRegistrationDepth <= 0) + { + return; + } + --GTlsRegistrationDepth; + if (GTlsRegistrationDepth > 0) + { + return; + } + + if (GTlsRegisteredSlot >= 0 && GTlsRegisteredSlot < static_cast(MaxRegisteredThreads)) + { + FRegisteredThreadSlot& Slot = Slots[GTlsRegisteredSlot]; + const uint64 State = Slot.State.load(std::memory_order_acquire); + if (State == GTlsRegistrationGeneration) + { + Slot.State.store(0, std::memory_order_release); + Slot.ThreadId.store(0, std::memory_order_relaxed); + Slot.LODLevel.store(INDEX_NONE, std::memory_order_relaxed); + } + } + GTlsRegisteredSlot = INDEX_NONE; + GTlsRegistrationGeneration = 0; + GTlsRegisteredLODLevel = INDEX_NONE; + } + + void CaptureRegisteredThreads(double NowSeconds) + { +#if PLATFORM_WINDOWS + uint64 BackTrace[MaxStackDepth]{}; + uint64 ActiveCount = 0; + for (uint32 Index = 0; Index < MaxRegisteredThreads; ++Index) + { + FRegisteredThreadSlot& Slot = Slots[Index]; + const uint64 Generation = Slot.State.load(std::memory_order_acquire); + if (Generation == 0 || (Generation & RegistrationReservedBit) != 0) + { + continue; + } + const uint64 ThreadId = Slot.ThreadId.load(std::memory_order_acquire); + if (ThreadId == 0) + { + continue; + } + const int32 LODLevel = Slot.LODLevel.load(std::memory_order_acquire); + ++ActiveCount; + ++CaptureAttempts; + const uint32 CapturedDepth = FPlatformStackWalk::CaptureThreadStackBackTrace( + ThreadId, BackTrace, MaxStackDepth); + const uint64 EndGeneration = Slot.State.load(std::memory_order_acquire); + const uint64 EndThreadId = Slot.ThreadId.load(std::memory_order_acquire); + const int32 EndLODLevel = Slot.LODLevel.load(std::memory_order_acquire); + if (EndGeneration != Generation || EndThreadId != ThreadId || EndLODLevel != LODLevel) + { + ++RegistrationRaceDrops; + continue; + } + + ++RegisteredThreadSamples; + const uint32 Depth = FMath::Min(CapturedDepth, MaxStackDepth); + if (Depth == 0) + { + ++CaptureFailures; + } + StoreSample( + NowSeconds, + ThreadId, + LODLevel, + Depth, + BackTrace); + } + MaxActiveRegisteredThreads = FMath::Max(MaxActiveRegisteredThreads, ActiveCount); +#else + (void)NowSeconds; +#endif + } + + uint64 NextReservoirValue() + { + ReservoirState ^= ReservoirState << 7; + ReservoirState ^= ReservoirState >> 9; + ReservoirState ^= ReservoirState << 8; + return ReservoirState; + } + + void StoreSample( + double NowSeconds, + uint64 ThreadId, + int32 LODLevel, + uint32 Depth, + const uint64* BackTrace) + { + FStoredSample* Sample = nullptr; + if (StoredSampleCount < MaxStoredSamples) + { + Sample = &Samples[StoredSampleCount++]; + } + else + { + ++DroppedSamples; + // RegisteredThreadSamples is the one-based ordinal of this stable capture. Select a + // uniform-ish prior slot; the raw store therefore represents the full run after the + // bound is reached without allocating or synchronizing with workers. + const uint64 Candidate = NextReservoirValue() % RegisteredThreadSamples; + if (Candidate >= MaxStoredSamples) + { + return; + } + Sample = &Samples[Candidate]; + TotalFrames -= Sample->Depth; + } + + Sample->ElapsedUs = NowSeconds >= StartSeconds + ? static_cast((NowSeconds - StartSeconds) * 1.0e6) + : 0; + Sample->ThreadId = ThreadId; + Sample->LODLevel = LODLevel; + Sample->Depth = Depth; + TotalFrames += Depth; + if (Depth > 0) + { + FMemory::Memcpy( + Sample->ProgramCounters, + BackTrace, + static_cast(Depth) * sizeof(uint64)); + } + } + + FSummary Finalize() + { + FSummary Result; + Result.bSupported = PLATFORM_WINDOWS != 0; + Result.bStarted = true; + Result.IntervalUs = IntervalUs; + Result.MaxDepth = MaxStackDepth; + Result.MaxStoredSampleCount = MaxStoredSamples; + Result.bStackWalkingInitialized = bStackWalkingInitialized; + Result.SamplerTicks = SamplerTicks; + Result.CaptureAttempts = CaptureAttempts; + Result.RegisteredThreadSamples = RegisteredThreadSamples; + Result.RegistrationRaceDrops = RegistrationRaceDrops; + Result.CaptureFailures = CaptureFailures; + Result.RetainedSamples = StoredSampleCount; + Result.DroppedSamples = DroppedSamples; + Result.TotalFrames = TotalFrames; + Result.MaxActiveRegisteredThreads = MaxActiveRegisteredThreads; + Result.RunLabel = RunLabel; + Result.RawSamplesPath = RawSamplesPath; + Result.SummaryPath = SummaryPath; + Result.RunSeconds = FPlatformTime::Seconds() - StartSeconds; + + WriteRawSamples(Result); + SymbolizeAndWriteSummary(Result); + return Result; + } + + void WriteRawSamples(FSummary& Result) + { + FArchive* Archive = IFileManager::Get().CreateFileWriter(*RawSamplesPath); + if (Archive == nullptr) + { + return; + } + + FString Header = TEXT("sample_index,elapsed_us,thread_id,lod,depth"); + for (uint32 Index = 0; Index < MaxStackDepth; ++Index) + { + Header += FString::Printf(TEXT(",pc%u"), Index); + } + Header += TEXT("\n"); + WriteUtf8(*Archive, Header); + + for (uint32 SampleIndex = 0; SampleIndex < StoredSampleCount; ++SampleIndex) + { + const FStoredSample& Sample = Samples[SampleIndex]; + FString Line = FString::Printf( + TEXT("%u,%llu,%llu,%d,%u"), + SampleIndex, + static_cast(Sample.ElapsedUs), + static_cast(Sample.ThreadId), + Sample.LODLevel, + Sample.Depth); + for (uint32 FrameIndex = 0; FrameIndex < Sample.Depth; ++FrameIndex) + { + Line += FString::Printf( + TEXT(",0x%016llX"), + static_cast(Sample.ProgramCounters[FrameIndex])); + } + Line += TEXT("\n"); + WriteUtf8(*Archive, Line); + } + delete Archive; + Result.bOutputWritten = true; + } + + void ResolveProgramCounter( + uint64 ProgramCounter, + TMap& SymbolCache, + FSummary& Result) + { + FResolvedProgramCounter& Entry = SymbolCache.FindOrAdd(ProgramCounter); + if (Entry.bAttempted) + { + return; + } + Entry.bAttempted = true; + +#if PLATFORM_WINDOWS + FPlatformStackWalk::EnumerateSymbolInfosForProgramCounter( + ProgramCounter, + true, + [&Entry, &Result](FProgramCounterSymbolInfo& Info) + { + FResolvedSymbol Symbol; + Symbol.Module = FString(ANSI_TO_TCHAR(Info.ModuleName)); + Symbol.Function = FString(ANSI_TO_TCHAR(Info.FunctionName)); + Symbol.File = FString(ANSI_TO_TCHAR(Info.Filename)); + Symbol.Line = Info.LineNumber; + Symbol.bInline = Symbol.Function.StartsWith(TEXT("[Inline Frame]")); + if (Symbol.bInline) + { + ++Result.InlineSymbols; + } + Entry.Symbols.Add(MoveTemp(Symbol)); + }); + // DbgHelp can return source-only inline records for a PC. Ask the ordinary resolver as + // well when none of the enumerated records has a function name, so an unresolved frame + // does not hide an available outer function symbol. + if (Entry.Symbols.Num() == 0 || FirstFunctionSymbol(Entry) == nullptr) + { + FProgramCounterSymbolInfo Info; + FPlatformStackWalk::ProgramCounterToSymbolInfo(ProgramCounter, Info); + FResolvedSymbol Symbol; + Symbol.Module = FString(ANSI_TO_TCHAR(Info.ModuleName)); + Symbol.Function = FString(ANSI_TO_TCHAR(Info.FunctionName)); + Symbol.File = FString(ANSI_TO_TCHAR(Info.Filename)); + Symbol.Line = Info.LineNumber; + Entry.Symbols.Add(MoveTemp(Symbol)); + } +#else + (void)ProgramCounter; + (void)Result; +#endif + } + + void SymbolizeAndWriteSummary(FSummary& Result) + { + TMap SymbolCache; + FProfileAggregateSet AllAggregates; + FProfileAggregateSet Lod0Aggregates; + FProfileAggregateSet Lod1PlusAggregates; + TSet SeenFunctions; + uint64 ResolvedFrames = 0; + uint64 UnknownFrames = 0; + uint64 WaitLikeFrames = 0; + uint64 SamplesWithResolvedLeaf = 0; + uint64 SamplesWithWaitLikeFrame = 0; + uint64 SamplesWithStack = 0; + uint64 UntaggedSamples = 0; + + auto AddUnknownExclusiveRow = [](FProfileAggregateSet& Aggregate) + { + const FString UnknownKey = TEXT("unknown\x1f"); + FProfileRow& UnknownRow = Aggregate.ExclusiveFunctions.FindOrAdd(UnknownKey); + if (UnknownRow.Key.IsEmpty()) + { + UnknownRow.Key = UnknownKey; + UnknownRow.Function = TEXT(""); + UnknownRow.Module = TEXT(""); + UnknownRow.File = TEXT(""); + UnknownRow.Label = TEXT("unknown"); + } + ++UnknownRow.Samples; + }; + + for (uint32 SampleIndex = 0; SampleIndex < StoredSampleCount; ++SampleIndex) + { + const FStoredSample& Sample = Samples[SampleIndex]; + if (Sample.Depth == 0) + { + continue; + } + ++SamplesWithStack; + SeenFunctions.Reset(); + TArray SampleFunctions; + + const FResolvedProgramCounter* LeafEntry = nullptr; + const FResolvedSymbol* LeafFunction = nullptr; + const FResolvedSymbol* LeafSource = nullptr; + bool bSampleHasResolvedLeaf = false; + bool bSampleHasWaitLike = false; + + for (uint32 FrameIndex = 0; FrameIndex < Sample.Depth; ++FrameIndex) + { + const uint64 ProgramCounter = Sample.ProgramCounters[FrameIndex]; + ResolveProgramCounter(ProgramCounter, SymbolCache, Result); + const FResolvedProgramCounter* Entry = SymbolCache.Find(ProgramCounter); + const FResolvedSymbol* FrameFunction = Entry != nullptr + ? FirstFunctionSymbol(*Entry) : nullptr; + if (FrameFunction != nullptr) + { + ++ResolvedFrames; + if (IsWaitLike(*FrameFunction)) + { + ++WaitLikeFrames; + bSampleHasWaitLike = true; + } + if (FrameIndex == 0) + { + LeafEntry = Entry; + LeafFunction = FrameFunction; + bSampleHasResolvedLeaf = true; + } + } + else + { + ++UnknownFrames; + } + + if (Entry != nullptr) + { + if (FrameIndex == 0) + { + LeafEntry = Entry; + if (LeafFunction == nullptr) + { + LeafFunction = FirstFunctionSymbol(*Entry); + } + LeafSource = FirstSourceSymbol(*Entry); + } + for (const FResolvedSymbol& Symbol : Entry->Symbols) + { + if (Symbol.Function.IsEmpty()) + { + continue; + } + const FString Key = FunctionKey(Symbol); + if (!SeenFunctions.Contains(Key)) + { + SeenFunctions.Add(Key); + SampleFunctions.Add(Symbol); + } + } + } + } + + if (LeafEntry != nullptr && LeafSource == nullptr) + { + LeafSource = FirstSourceSymbol(*LeafEntry); + } + + auto AddToAggregate = [&](FProfileAggregateSet& Aggregate) + { + ++Aggregate.SamplesWithStack; + if (bSampleHasResolvedLeaf) + { + AddFunctionRow(Aggregate.ExclusiveFunctions, LeafFunction, 1); + } + else + { + AddUnknownExclusiveRow(Aggregate); + } + for (const FResolvedSymbol& Symbol : SampleFunctions) + { + AddFunctionRow(Aggregate.InclusiveFunctions, &Symbol, 1); + } + AddSourceRow(Aggregate.ExclusiveLines, LeafFunction, LeafSource); + }; + + AddToAggregate(AllAggregates); + if (Sample.LODLevel == 0) + { + AddToAggregate(Lod0Aggregates); + } + else if (Sample.LODLevel >= 1) + { + AddToAggregate(Lod1PlusAggregates); + } + else + { + ++UntaggedSamples; + } + + if (bSampleHasResolvedLeaf) + { + ++SamplesWithResolvedLeaf; + } + if (bSampleHasWaitLike) + { + ++SamplesWithWaitLikeFrame; + } + } + + Result.UniqueProgramCounters = SymbolCache.Num(); + Result.ResolvedFrames = ResolvedFrames; + Result.UnknownFrames = UnknownFrames; + Result.WaitLikeFrames = WaitLikeFrames; + Result.SamplesWithResolvedLeaf = SamplesWithResolvedLeaf; + Result.SamplesWithWaitLikeFrame = SamplesWithWaitLikeFrame; + + FString Summary; + Summary += TEXT("VoxelForge in-process stack sampler\n"); + Summary += TEXT("==================================\n"); + Summary.Appendf(TEXT("run_label: %s\n"), *RunLabel); + Summary.Appendf(TEXT("supported: %s\n"), Result.bSupported ? TEXT("yes") : TEXT("no")); + Summary.Appendf(TEXT("stack_walking_initialized: %s\n"), + Result.bStackWalkingInitialized ? TEXT("yes") : TEXT("no")); + Summary.Appendf(TEXT("interval_us: %u\n"), IntervalUs); + Summary.Appendf(TEXT("max_stack_depth: %u\n"), MaxStackDepth); + Summary.Appendf(TEXT("max_stored_samples: %u\n"), MaxStoredSamples); + Summary.Appendf(TEXT("sampler_ticks: %llu\n"), + static_cast(SamplerTicks)); + Summary.Appendf(TEXT("capture_attempts: %llu\n"), + static_cast(CaptureAttempts)); + Summary.Appendf(TEXT("registered_thread_samples: %llu\n"), + static_cast(RegisteredThreadSamples)); + Summary.Appendf(TEXT("registration_race_drops: %llu\n"), + static_cast(RegistrationRaceDrops)); + Summary.Appendf(TEXT("capture_failures: %llu\n"), + static_cast(CaptureFailures)); + Summary.Appendf(TEXT("retained_samples: %llu\n"), + static_cast(StoredSampleCount)); + Summary.Appendf(TEXT("dropped_samples_after_bound: %llu\n"), + static_cast(DroppedSamples)); + Summary.Appendf(TEXT("samples_with_stack: %llu\n"), + static_cast(SamplesWithStack)); + Summary.Appendf(TEXT("lod0_samples_with_stack: %llu\n"), + static_cast(Lod0Aggregates.SamplesWithStack)); + Summary.Appendf(TEXT("lod1plus_samples_with_stack: %llu\n"), + static_cast(Lod1PlusAggregates.SamplesWithStack)); + Summary.Appendf(TEXT("untagged_samples: %llu\n"), + static_cast(UntaggedSamples)); + Summary.Appendf(TEXT("total_frames: %llu\n"), + static_cast(TotalFrames)); + Summary.Appendf(TEXT("resolved_frames: %llu\n"), + static_cast(ResolvedFrames)); + Summary.Appendf(TEXT("unknown_frames: %llu\n"), + static_cast(UnknownFrames)); + Summary.Appendf(TEXT("wait_like_frames: %llu\n"), + static_cast(WaitLikeFrames)); + Summary.Appendf(TEXT("samples_with_resolved_leaf: %llu\n"), + static_cast(SamplesWithResolvedLeaf)); + Summary.Appendf(TEXT("samples_with_wait_like_frame: %llu\n"), + static_cast(SamplesWithWaitLikeFrame)); + Summary.Appendf(TEXT("unique_program_counters: %llu\n"), + static_cast(Result.UniqueProgramCounters)); + Summary.Appendf(TEXT("inline_symbols: %llu\n"), + static_cast(Result.InlineSymbols)); + Summary.Appendf(TEXT("max_active_registered_threads: %llu\n"), + static_cast(MaxActiveRegisteredThreads)); + Summary.Appendf(TEXT("run_seconds: %.6f\n"), Result.RunSeconds); + Summary += TEXT("\nClosure uses stable registration snapshots. Top-table percentages use retained samples with a non-empty stack; the raw CSV includes zero-depth captures.\n"); + Summary.Appendf(TEXT("resolved_frame_fraction: %.6f\n"), TotalFrames > 0 + ? static_cast(ResolvedFrames) / static_cast(TotalFrames) : 0.0); + Summary.Appendf(TEXT("unknown_frame_fraction: %.6f\n"), TotalFrames > 0 + ? static_cast(UnknownFrames) / static_cast(TotalFrames) : 0.0); + Summary.Appendf(TEXT("wait_like_frame_fraction: %.6f\n"), TotalFrames > 0 + ? static_cast(WaitLikeFrames) / static_cast(TotalFrames) : 0.0); + Summary.Appendf(TEXT("resolved_leaf_fraction: %.6f\n"), SamplesWithStack > 0 + ? static_cast(SamplesWithResolvedLeaf) / static_cast(SamplesWithStack) : 0.0); + Summary.Appendf(TEXT("wait_like_sample_fraction: %.6f\n"), SamplesWithStack > 0 + ? static_cast(SamplesWithWaitLikeFrame) / static_cast(SamplesWithStack) : 0.0); + Summary += TEXT("\nLabels: plugin = VoxelForge, engine = Unreal/Engine, std = std:: frames, other = other resolved modules, unknown = no function symbol. Inline symbol callbacks were requested through DbgHelp when available.\n"); + AppendRows(Summary, TEXT("Top 30 exclusive functions (leaf)"), AllAggregates.ExclusiveFunctions, SamplesWithStack, 30); + AppendRows(Summary, TEXT("Top 30 inclusive functions (once per sample)"), AllAggregates.InclusiveFunctions, SamplesWithStack, 30); + AppendRows(Summary, TEXT("Top 30 exclusive source lines (leaf source)"), AllAggregates.ExclusiveLines, SamplesWithStack, 30); + Summary += TEXT("\nLOD0 tables (floor)\n"); + AppendRows(Summary, TEXT("LOD0 top 30 exclusive functions (leaf)"), Lod0Aggregates.ExclusiveFunctions, Lod0Aggregates.SamplesWithStack, 30); + AppendRows(Summary, TEXT("LOD0 top 30 inclusive functions (once per sample)"), Lod0Aggregates.InclusiveFunctions, Lod0Aggregates.SamplesWithStack, 30); + AppendRows(Summary, TEXT("LOD0 top 30 exclusive source lines (leaf source)"), Lod0Aggregates.ExclusiveLines, Lod0Aggregates.SamplesWithStack, 30); + Summary += TEXT("\nLOD1+ tables (coarse/background)\n"); + AppendRows(Summary, TEXT("LOD1+ top 30 exclusive functions (leaf)"), Lod1PlusAggregates.ExclusiveFunctions, Lod1PlusAggregates.SamplesWithStack, 30); + AppendRows(Summary, TEXT("LOD1+ top 30 inclusive functions (once per sample)"), Lod1PlusAggregates.InclusiveFunctions, Lod1PlusAggregates.SamplesWithStack, 30); + AppendRows(Summary, TEXT("LOD1+ top 30 exclusive source lines (leaf source)"), Lod1PlusAggregates.ExclusiveLines, Lod1PlusAggregates.SamplesWithStack, 30); + + if (FFileHelper::SaveStringToFile( + Summary, + *SummaryPath, + FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM)) + { + Result.bOutputWritten = true; + } + } +}; + +FVoxelStackSampler& FVoxelStackSampler::Get() +{ + static FVoxelStackSampler Instance; + return Instance; +} + +bool FVoxelStackSampler::Start( + uint32 IntervalUs, + const FString& OutputDirectory, + const FString& RunLabel) +{ + if (Impl != nullptr) + { + return false; + } + +#if !PLATFORM_WINDOWS + UE_LOG(LogTemp, Error, + TEXT("[VoxelStackSampler] requested on a non-Windows target; no profile was started.")); + return false; +#else + if (!IFileManager::Get().MakeDirectory(*OutputDirectory, true)) + { + UE_LOG(LogTemp, Error, TEXT("[VoxelStackSampler] could not create '%s'."), *OutputDirectory); + return false; + } + + FImpl* NewImpl = new FImpl(*this, IntervalUs, OutputDirectory, RunLabel); + if (!NewImpl->StartThread()) + { + delete NewImpl; + return false; + } + Impl = NewImpl; + GVoxelStackSamplerEnabled.store(true, std::memory_order_release); + UE_LOG(LogTemp, Display, + TEXT("[VoxelStackSampler] started label=%s interval_us=%u raw=%s summary=%s"), + *RunLabel, NewImpl->IntervalUs, *NewImpl->RawSamplesPath, *NewImpl->SummaryPath); + return true; +#endif +} + +FVoxelStackSampler::FSummary FVoxelStackSampler::StopAndWrite() +{ + GVoxelStackSamplerEnabled.store(false, std::memory_order_release); + FSummary Result; + if (Impl == nullptr) + { + return Result; + } + + FImpl* StoppedImpl = Impl; + StoppedImpl->StopThread(); + Result = StoppedImpl->Finalize(); + UE_LOG(LogTemp, Display, + TEXT("[VoxelStackSampler] stopped label=%s ticks=%llu registered_samples=%llu " + "retained=%llu dropped=%llu resolved_fraction=%.4f raw=%s summary=%s"), + *Result.RunLabel, + static_cast(Result.SamplerTicks), + static_cast(Result.RegisteredThreadSamples), + static_cast(Result.RetainedSamples), + static_cast(Result.DroppedSamples), + Result.TotalFrames > 0 + ? static_cast(Result.ResolvedFrames) / static_cast(Result.TotalFrames) + : 0.0, + *Result.RawSamplesPath, + *Result.SummaryPath); + delete StoppedImpl; + Impl = nullptr; + return Result; +} + +void FVoxelStackSampler::Shutdown() +{ + if (Impl != nullptr) + { + StopAndWrite(); + } +} + +bool FVoxelStackSampler::IsRunning() const +{ + return Impl != nullptr; +} + +bool FVoxelStackSampler::RegisterCurrentThread(int32 LODLevel) +{ + // Keep this as the first and only operation in the disabled fast path. In particular, do not + // touch the singleton or TLS until this relaxed load says a session is active. + if (!GVoxelStackSamplerEnabled.load(std::memory_order_relaxed)) + { + return false; + } + return Get().RegisterThreadInternal(LODLevel); +} + +void FVoxelStackSampler::DeregisterCurrentThread() +{ + Get().DeregisterThreadInternal(); +} + +bool FVoxelStackSampler::IsEnabled() +{ + return GVoxelStackSamplerEnabled.load(std::memory_order_relaxed); +} + +bool FVoxelStackSampler::RegisterThreadInternal(int32 LODLevel) +{ + return Impl != nullptr && Impl->RegisterThread(LODLevel); +} + +void FVoxelStackSampler::DeregisterThreadInternal() +{ + if (Impl != nullptr) + { + Impl->DeregisterThread(); + } +} diff --git a/Source/VoxelForge/Private/VoxelWorld.cpp b/Source/VoxelForge/Private/VoxelWorld.cpp index 880e4d3..9556e94 100644 --- a/Source/VoxelForge/Private/VoxelWorld.cpp +++ b/Source/VoxelForge/Private/VoxelWorld.cpp @@ -15,6 +15,7 @@ #include "VoxelDensityOpStack.h" #include "VoxelDensityProfile.h" #include "VoxelStartupTrace.h" +#include "VoxelStackSampler.h" #include "VoxelStats.h" // IWYU (FPSemantics = Precise ⇒ plus de PCH partagé) : GetPlayerPosition déréférence le pawn, donc // APawn doit être COMPLET — `Casts.h` n'en donne qu'une déclaration avant. APlayerController était @@ -73,6 +74,11 @@ namespace TEXT("voxel.PerfAttribution"), GVoxelForgePerfAttribution, TEXT("Collect low-overhead component attribution for the game path.")); + int32 GVoxelForgeSampleStacks = 0; + FAutoConsoleVariableRef CVarVoxelForgeSampleStacks( + TEXT("voxel.SampleStacks"), + GVoxelForgeSampleStacks, + TEXT("Sample active generation worker stacks at the requested interval in microseconds; 0 is off.")); // The nested 8^3 proof is useful for focused/offline measurements, but it is opt-in for // streaming because every qualifying mixed tile would pay the recursive proof again. @@ -988,6 +994,11 @@ void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason) } UE_LOG(LogTemp, Display, TEXT("[VoxelWorld] EndPlay: workers drained in %.6fs; obsolete_tile_aborts=%d"), FPlatformTime::Seconds() - EndPlayStartSeconds, ObsoleteTileAbortCount); + if (bSampleStacksStarted) + { + FVoxelStackSampler::Get().StopAndWrite(); + bSampleStacksStarted = false; + } LogStreamingLatencySummary(); PendingTiles.Empty(); PendingTileCancellation.Empty(); @@ -1075,6 +1086,7 @@ void AVoxelWorld::BeginPlay() int32 CommandLinePerfAttribution = GVoxelForgePerfAttribution; int32 CommandLineUseBlockEarlyOut = GVoxelForgeUseBlockEarlyOut; int32 CommandLineOuterClassifierMode = GVoxelForgeOuterClassifierMode; + int32 CommandLineSampleStacks = GVoxelForgeSampleStacks; if (FParse::Value(FCommandLine::Get(), TEXT("voxel.ProfileDensity="), CommandLineProfile)) { GVoxelForgeProfileDensity = CommandLineProfile; @@ -1095,8 +1107,16 @@ void AVoxelWorld::BeginPlay() { GVoxelForgeOuterClassifierMode = CommandLineOuterClassifierMode; } + if (FParse::Value(FCommandLine::Get(), TEXT("voxel.SampleStacks="), CommandLineSampleStacks)) + { + GVoxelForgeSampleStacks = CommandLineSampleStacks; + } GVoxelForgeUseBlockEarlyOut = GVoxelForgeUseBlockEarlyOut != 0 ? 1 : 0; GVoxelForgeOuterClassifierMode = FMath::Clamp(GVoxelForgeOuterClassifierMode, 0, 1); + GVoxelForgeSampleStacks = FMath::Clamp( + GVoxelForgeSampleStacks, + 0, + static_cast(FVoxelStackSampler::MaxIntervalUs)); if (GVoxelForgePerfAttribution != 0) { @@ -1276,6 +1296,21 @@ void AVoxelWorld::BeginPlay() { UpdatePawnCollisionGate(InitialPawn, InitialPlayerPosition, 0.0f); } + + if (GVoxelForgeSampleStacks > 0) + { + const FString SamplerOutputDirectory = FPaths::Combine( + FPaths::ProjectDir(), TEXT("Plugins/VoxelForge/Saved")); + bSampleStacksStarted = FVoxelStackSampler::Get().Start( + static_cast(GVoxelForgeSampleStacks), + SamplerOutputDirectory, + TEXT("game")); + if (!bSampleStacksStarted) + { + UE_LOG(LogTemp, Error, + TEXT("[VoxelWorld] stack sampler requested but could not be started.")); + } + } } void AVoxelWorld::ConfigureHeadlessStreamingTest() @@ -2177,6 +2212,7 @@ void AVoxelWorld::LogStreamingLatencySummary() const "collision_cook_s[p50=%.6f p95=%.6f max=%.6f] " "peak_speed_cm_s=%.3f p95_travel_m=%.6f obsolete_tile_aborts=%d " "outer_classifier_mode=%d nested_block_early_out=%d " + "applied_tiles=%llu applied_visible_tiles=%llu applied_triangles=%llu " "worker_generation_tasks=%llu worker_generation_sum_s=%.6f " "validation_density_calls=%llu mesher_density_calls=%llu " "obsolete_worker_tasks=%llu obsolete_worker_sum_s=%.6f " @@ -2188,9 +2224,12 @@ void AVoxelWorld::LogStreamingLatencySummary() const Percentile(QueueWait, 0.50), Percentile(QueueWait, 0.95), Maximum(QueueWait), Percentile(Generation, 0.50), Percentile(Generation, 0.95), Maximum(Generation), Percentile(CollisionCook, 0.50), Percentile(CollisionCook, 0.95), Maximum(CollisionCook), - PeakObservedPawnSpeedCmPerSecond, TravelP95Metres, ObsoleteTileAbortCount, - GVoxelForgeOuterClassifierMode, GVoxelForgeUseBlockEarlyOut, - static_cast(TotalWorkerGenerationTasks.load(std::memory_order_relaxed)), + PeakObservedPawnSpeedCmPerSecond, TravelP95Metres, ObsoleteTileAbortCount, + GVoxelForgeOuterClassifierMode, GVoxelForgeUseBlockEarlyOut, + static_cast(AppliedTileCount), + static_cast(AppliedVisibleTileCount), + static_cast(AppliedTriangleCount), + static_cast(TotalWorkerGenerationTasks.load(std::memory_order_relaxed)), FPlatformTime::ToSeconds64(WorkerGenerationCycles), static_cast(TotalValidationDensityCalls.load(std::memory_order_relaxed)), static_cast(TotalMesherDensityCalls.load(std::memory_order_relaxed)), @@ -2444,6 +2483,12 @@ bool AVoxelWorld::ApplyTileResult(FChunkResult& Result) Result.ApplyStartCycles = ApplyStartCycles; const auto LogTileApplied = [&]() { + ++AppliedTileCount; + if (!bResultEmpty) + { + ++AppliedVisibleTileCount; + AppliedTriangleCount += static_cast(FMath::Max(0, Result.NumTriangles)); + } if ((GVoxelForgeProfileTileGeneration != 0 || bTraceTile) && Result.RequestStartCycles != 0) { @@ -3709,6 +3754,7 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, bool bHighPriority) return; } + FScopedVoxelStackRegistration StackRegistration(Tile.Level); FChunkResult Result; Result.DesiredEpoch = TaskDesiredEpoch; const uint64 GenerationStartCycles = FPlatformTime::Cycles64(); diff --git a/Source/VoxelForge/Public/VoxelStackSampler.h b/Source/VoxelForge/Public/VoxelStackSampler.h new file mode 100644 index 0000000..54415c5 --- /dev/null +++ b/Source/VoxelForge/Public/VoxelStackSampler.h @@ -0,0 +1,117 @@ +// VoxelStackSampler.h +// Opt-in, out-of-process-symbolized stack sampling for active VoxelForge generation workers. + +#pragma once + +#include "CoreTypes.h" +#include "Containers/UnrealString.h" + +/** + * A diagnostic-only in-process stack sampler. + * + * The generation path only constructs FScopedVoxelStackRegistration at task entry. When the + * sampler is disabled that constructor performs one relaxed load of the global enable flag and + * returns; it does not touch the registry, allocate, lock, or read a console variable. + * + * The sampler thread owns a bounded preallocated array of raw program counters. Symbolization + * and aggregation happen after the sampler thread has stopped, so DbgHelp and FString activity are + * kept out of both generation workers and the sampling loop. + */ +class VOXELFORGE_API FVoxelStackSampler +{ +public: + static constexpr uint32 DefaultIntervalUs = 1000; + static constexpr uint32 MinIntervalUs = 1; + static constexpr uint32 MaxIntervalUs = 1000000; + static constexpr uint32 MaxStackDepth = 64; + // Bounded allocation. The implementation uses reservoir replacement after this fills so a + // long moving session is represented across its whole run rather than only its first window. + static constexpr uint32 MaxStoredSamples = 131072; + + struct FSummary + { + bool bSupported = false; + bool bStarted = false; + bool bOutputWritten = false; + bool bStackWalkingInitialized = false; + uint32 IntervalUs = 0; + uint32 MaxDepth = 0; + uint32 MaxStoredSampleCount = 0; + + uint64 SamplerTicks = 0; + uint64 CaptureAttempts = 0; + uint64 RegisteredThreadSamples = 0; + uint64 RegistrationRaceDrops = 0; + uint64 CaptureFailures = 0; + uint64 RetainedSamples = 0; + uint64 DroppedSamples = 0; + uint64 TotalFrames = 0; + uint64 ResolvedFrames = 0; + uint64 UnknownFrames = 0; + uint64 WaitLikeFrames = 0; + uint64 SamplesWithResolvedLeaf = 0; + uint64 SamplesWithWaitLikeFrame = 0; + uint64 UniqueProgramCounters = 0; + uint64 InlineSymbols = 0; + uint64 MaxActiveRegisteredThreads = 0; + double RunSeconds = 0.0; + + FString RunLabel; + FString RawSamplesPath; + FString SummaryPath; + }; + + static FVoxelStackSampler& Get(); + + /** Start one sampling session. Returns false when another session is active or setup fails. */ + bool Start(uint32 IntervalUs, const FString& OutputDirectory, const FString& RunLabel); + + /** Stop, symbolize, aggregate, and write the bounded raw CSV plus readable summary. */ + FSummary StopAndWrite(); + + /** Used by the module shutdown path as a final lifecycle backstop. */ + void Shutdown(); + + bool IsRunning() const; + + /** Called only by FScopedVoxelStackRegistration; cheap when disabled. */ + static bool RegisterCurrentThread(int32 LODLevel = INDEX_NONE); + static void DeregisterCurrentThread(); + static bool IsEnabled(); + +private: + FVoxelStackSampler() = default; + ~FVoxelStackSampler() = default; + FVoxelStackSampler(const FVoxelStackSampler&) = delete; + FVoxelStackSampler& operator=(const FVoxelStackSampler&) = delete; + + struct FImpl; + FImpl* Impl = nullptr; + + bool RegisterThreadInternal(int32 LODLevel); + void DeregisterThreadInternal(); +}; + +/** Scope a generation task, not an individual density sample. */ +class FScopedVoxelStackRegistration final +{ +public: + explicit FScopedVoxelStackRegistration(int32 InLODLevel = INDEX_NONE) + : bRegistered(FVoxelStackSampler::RegisterCurrentThread(InLODLevel)) + { + } + + ~FScopedVoxelStackRegistration() + { + if (bRegistered) + { + FVoxelStackSampler::DeregisterCurrentThread(); + } + } + + FScopedVoxelStackRegistration(const FScopedVoxelStackRegistration&) = delete; + FScopedVoxelStackRegistration& operator=(const FScopedVoxelStackRegistration&) = delete; + +private: + bool bRegistered = false; +}; diff --git a/Source/VoxelForge/Public/VoxelWorld.h b/Source/VoxelForge/Public/VoxelWorld.h index 3f69bfd..d8935ec 100644 --- a/Source/VoxelForge/Public/VoxelWorld.h +++ b/Source/VoxelForge/Public/VoxelWorld.h @@ -913,6 +913,7 @@ public: FIntVector LastUpdateCenter = FIntVector(INT32_MAX, INT32_MAX, INT32_MAX); bool bAllChunksLoaded = false; bool bStartupTraceDesiredRecorded = false; + bool bSampleStacksStarted = false; TArray DesiredSorted; // critical floor prefix, then true-pawn distance-first TArray CriticalDesiredTiles; // occupied/support tile, then heading tile uint32 DesiredEpoch = 0; // increments whenever DesiredStamped is rebuilt @@ -931,6 +932,12 @@ public: std::atomic OuterClassifierCallsByLOD[TrackedClassifierLODCount]{}; std::atomic OuterClassifierVerdictsByLOD[TrackedClassifierLODCount][3]{}; + // Low-overhead game-thread geometry totals used to prove that sampler toggling does not alter + // the applied tile/triangle stream. These are measurement counters, not generation inputs. + uint64 AppliedTileCount = 0; + uint64 AppliedVisibleTileCount = 0; + uint64 AppliedTriangleCount = 0; + struct FStreamingLatencySample { double RequestToReadySeconds = 0.0; diff --git a/Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp b/Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp index 56f725d..af28123 100644 --- a/Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp +++ b/Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp @@ -33,6 +33,7 @@ #include "VoxelMarchingCubesMesher.h" #include "VoxelNoise.h" #include "VoxelSettings.h" +#include "VoxelStackSampler.h" #include "VoxelStrateDefinition.h" #include "VoxelStrateManager.h" #include "VoxelStrateMeasure.h" @@ -267,6 +268,8 @@ struct FExploreArguments bool bProfileDensity = false; bool bProfileDensityFull = false; bool bPerfAttribution = false; + bool bSampleStacks = false; + uint32 SampleStacksIntervalUs = FVoxelStackSampler::DefaultIntervalUs; bool bProfileLod = false; bool bOpBounds = false; bool bFailureFocusRender = false; @@ -365,6 +368,14 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr OutArguments.bProfileDensityFull = FParse::Param(*Params, TEXT("profiledensityfull")); OutArguments.bProfileDensity |= OutArguments.bProfileDensityFull; OutArguments.bPerfAttribution = FParse::Param(*Params, TEXT("perfattribution")); + OutArguments.bSampleStacks = FParse::Param(*Params, TEXT("samplestacks")); + int32 SampleStacksIntervalUs = static_cast( + FVoxelStackSampler::DefaultIntervalUs); + if (FParse::Value(*Params, TEXT("samplestacks="), SampleStacksIntervalUs)) + { + OutArguments.bSampleStacks = true; + OutArguments.SampleStacksIntervalUs = static_cast(FMath::Max(0, SampleStacksIntervalUs)); + } OutArguments.bProfileLod = FParse::Param(*Params, TEXT("profilelod")); OutArguments.bOpBounds = FParse::Param(*Params, TEXT("opbounds")); FParse::Value(*Params, TEXT("out="), OutText); @@ -605,6 +616,16 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr TEXT("budget must be finite and in (0,%g] minutes."), MaxBudgetMinutes); return false; } + if (OutArguments.bSampleStacks + && (OutArguments.SampleStacksIntervalUs < FVoxelStackSampler::MinIntervalUs + || OutArguments.SampleStacksIntervalUs > FVoxelStackSampler::MaxIntervalUs)) + { + OutError = FString::Printf( + TEXT("samplestacks interval must be in [%u,%u] microseconds."), + FVoxelStackSampler::MinIntervalUs, + FVoxelStackSampler::MaxIntervalUs); + return false; + } if (OutArguments.bSurfaceRoughnessOverride && !bSurfaceRoughnessParsed) { @@ -1294,6 +1315,36 @@ struct FExploreRunOutput FExploreProfilerComparison ProfilerComparison; }; +struct FExploreStackSamplerSession +{ + bool bStarted = false; + + ~FExploreStackSamplerSession() + { + Stop(); + } + + bool Start(uint32 IntervalUs, const FString& OutputDirectory) + { + if (bStarted) + { + return true; + } + bStarted = FVoxelStackSampler::Get().Start( + IntervalUs, OutputDirectory, TEXT("commandlet")); + return bStarted; + } + + void Stop() + { + if (bStarted) + { + FVoxelStackSampler::Get().StopAndWrite(); + bStarted = false; + } + } +}; + bool ValidateCanonicalTile(const FVoxelMeshData& MeshData, FString& OutError); bool MeasureExploreMesh( const FVoxelMeshData& MeshData, @@ -1748,6 +1799,7 @@ bool EnsureExploreMesh( bGridWorkCancelled.store(true, std::memory_order_relaxed); return; } + FScopedVoxelStackRegistration StackRegistration; const int32 GridOctaveBias = (World.Mesher->LODOctaveDrop > 0 && Arguments.ExportStep > 1) ? World.Mesher->LODOctaveDrop @@ -1848,6 +1900,7 @@ bool EnsureExploreMesh( return; } + FScopedVoxelStackRegistration StackRegistration; const double TileStartSeconds = FPlatformTime::Seconds(); TileStartOffsets[LinearTileIndex] = TileStartSeconds - ParallelStartSeconds; TileThreadIds[LinearTileIndex] = FPlatformTLS::GetCurrentThreadId(); @@ -4406,6 +4459,9 @@ FString BuildExploreJson( Writer->WriteValue(TEXT("profile_density"), Arguments.bProfileDensity); Writer->WriteValue(TEXT("profile_density_full"), Arguments.bProfileDensityFull); Writer->WriteValue(TEXT("perf_attribution"), Arguments.bPerfAttribution); + Writer->WriteValue(TEXT("sample_stacks"), Arguments.bSampleStacks); + Writer->WriteValue(TEXT("sample_stacks_interval_us"), + static_cast(Arguments.SampleStacksIntervalUs)); Writer->WriteValue(TEXT("profile_lod"), Arguments.bProfileLod); Writer->WriteValue(TEXT("op_bounds"), Arguments.bOpBounds); Writer->WriteValue(TEXT("budget_minutes"), static_cast(Arguments.BudgetMinutes)); @@ -5590,6 +5646,16 @@ int32 RunExploreCase(const FString& Params, FString* OutJson) } const double SetupSeconds = FPlatformTime::Seconds() - SetupStartSeconds; + FExploreStackSamplerSession StackSamplerSession; + if (Arguments.bSampleStacks + && !StackSamplerSession.Start( + Arguments.SampleStacksIntervalUs, Arguments.OutDirectory)) + { + UE_LOG(LogTemp, Error, + TEXT("[VoxelForgeExplore] stack sampler was requested but could not be started.")); + return 1; + } + bool bRequestedModeFailed = false; if (Arguments.bOpBounds) { @@ -5852,6 +5918,11 @@ int32 RunExploreCase(const FString& Params, FString* OutJson) FPlatformTime::Seconds() - Start, *Output.Export.Status); } + // Stop before the report is assembled, while World still owns the generation state. This + // guarantees that the sampler's summary describes only this case and that all worker stacks + // have been released before the commandlet tears the world down. + StackSamplerSession.Stop(); + // A phase can finish just inside its own check and cross the deadline while its artifact is // being closed. Mark that state before serialising the report so the JSON never claims a full // run after the budget has actually expired. @@ -6299,6 +6370,14 @@ bool BuildBatchCaseParams( bProfileDensity |= bProfileDensityFull; bool bPerfAttribution = false; JsonBool(Case, TEXT("perf_attribution"), bPerfAttribution); + bool bSampleStacks = false; + JsonBool(Case, TEXT("sample_stacks"), bSampleStacks); + int32 SampleStacksIntervalUs = static_cast(FVoxelStackSampler::DefaultIntervalUs); + if (JsonNumber(Case, TEXT("sample_stacks_interval_us"), Number)) + { + SampleStacksIntervalUs = FMath::RoundToInt(Number); + bSampleStacks = true; + } bool bProfileLod = false; JsonBool(Case, TEXT("profile_lod"), bProfileLod); bool bOpBounds = false; @@ -6343,6 +6422,11 @@ bool BuildBatchCaseParams( { OutParams += TEXT(" -perfattribution"); } + if (bSampleStacks) + { + OutParams += FString::Printf( + TEXT(" -samplestacks=%d"), SampleStacksIntervalUs); + } if (bProfileLod) { OutParams += TEXT(" -profilelod");