measure(step1): cook is 1.5-2.3 ms, RMC's route is 8-11 s; CMC cannot take a field floor; cap 16 uploads

Rebuild plan step 1 (RESEARCH-SPEED-2026-09.md), all behind default-off switches.
A. Direct cook on a dedicated thread, 27-tile support cohort: core cook p50 1.50 ms, p95 2.28 ms;
   a 3,949-tri tile 1.67 ms extract+cook. PASSES the 10 ms gate. Through RMC's route the same
   support bodies became ready 8.46 s p50 / 10.80 s p95 after their meshes existed: FAILS 150 ms.
   Exact-target capsule sweep confirmed (0.75 ms after body update).
B. 100 rapid underfoot digs on CMC with a field override: 883 false-support frames, 0 false-air,
   0 invented velocity. CMC's floor comes from collision-scene traces -> step 4 = field-aware Mover.
C. 341 prequeued tiles, render on: cap 4 = 238 applies/s, cap 8 = 476, cap 16 = 639 (drain p95
   2.22 ms vs 2 ms gate); first visible 1 frame after release; no RT/RHI backlog. Tune drain,
   no grouping needed yet.
D. Same 128^3 region, 64/64 tile hashes equal in export and game. The "263 ns/sample" was export
   WALL time with 15 workers sharing a lattice (~3.4 us CPU/sample); game tile time includes
   meshing and duplicated borders (+22%); contention cap 1 vs 14: 74 vs 112 ms per tile.
   Profiling is not the cause.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-09-30 00:07:59 +02:00
1 parent 97a1874498
commit 41b001b652
5 files changed
+481 -55

No files matched your search

+400 -36
View File
@@ -65,6 +65,7 @@
#include "Policies/CondensedJsonPrintPolicy.h"
#include "Serialization/JsonWriter.h"
#include "ProfilingDebugging/CpuProfilerTrace.h" // Unreal Insights scopes (Perf 0)
#include "ProfilingDebugging/CsvProfiler.h" // Delayed CSV capture for Step 1 upload runs
static bool VF_TileOverlapsVoxelBounds(const FVoxelTileKey& Tile,
const FVector& BoundsMin, const FVector& BoundsMax);
@@ -101,6 +102,20 @@ namespace
GVoxelForgeStep1GenerationCap,
TEXT("Step 1 measurement override: -1 uses the normal cap, 0 pauses generation, positive values cap worker tasks."));
int32 GVoxelForgeStep1ExitAfterFrames = 0;
FAutoConsoleVariableRef CVarVoxelForgeStep1ExitAfterFrames(
TEXT("voxel.Step1ExitAfterFrames"),
GVoxelForgeStep1ExitAfterFrames,
TEXT("Step 1 measurement: request clean exit after this many engine frames; 0 disables."));
uint64 GVoxelForgeStep1ExitStartFrame = 0;
bool bVoxelForgeStep1ExitRequested = false;
int32 GVoxelForgeStep1CsvCaptureOnBeginPlay = 0;
FAutoConsoleVariableRef CVarVoxelForgeStep1CsvCaptureOnBeginPlay(
TEXT("voxel.Step1CsvCaptureOnBeginPlay"),
GVoxelForgeStep1CsvCaptureOnBeginPlay,
TEXT("Step 1 measurement: emit a delayed CSV start event from BeginPlay; 0 disables."));
float GVoxelForgeStep1PauseGenerationAfterSeconds = 0.0f;
FAutoConsoleVariableRef CVarVoxelForgeStep1PauseGenerationAfterSeconds(
TEXT("voxel.Step1PauseGenerationAfterSeconds"),
@@ -137,7 +152,7 @@ namespace
FAutoConsoleVariableRef CVarVoxelForgeStep1CoreCookBenchmark(
TEXT("voxel.Step1CoreCookBenchmark"),
GVoxelForgeStep1CoreCookBenchmark,
TEXT("Step 1 measurement: directly extract and CookComplexMesh for critical L0 support tiles on a dedicated one-thread pool; does not install this sidecar body."));
TEXT("Step 1 measurement: directly extract and CookComplexMesh for the pawn's exact L0 support cohort plus critical support tiles on a dedicated one-thread pool; does not install this sidecar body."));
struct FStep1CoreCookPoolOwner
{
@@ -182,8 +197,10 @@ namespace
public:
FVoxelStep1CoreCookWork(
TSharedPtr<RealtimeMesh::FRealtimeMeshStreamSet> InStreams,
FVoxelTileKey InTile, int32 InTriangleCount, uint64 InQueueCycles)
FVoxelTileKey InTile, int32 InTriangleCount, bool bInSupportCohort,
bool bInPausedReplay, uint64 InQueueCycles)
: Streams(MoveTemp(InStreams)), Tile(InTile), TriangleCount(InTriangleCount),
bSupportCohort(bInSupportCohort), bPausedReplay(bInPausedReplay),
QueueCycles(InQueueCycles)
{
}
@@ -204,11 +221,13 @@ namespace
}
const uint64 CookEndCycles = FPlatformTime::Cycles64();
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeStep1CoreCook] tile=(%d,%d,%d) level=%d extracted=%d "
TEXT("[VoxelForgeStep1CoreCook] tile=(%d,%d,%d) level=%d cohort=%d paused_replay=%d extracted=%d "
"triangles=%d collision_vertices=%d queue_to_worker_ms=%.3f "
"stream_extract_ms=%.3f cook_ms=%.3f total_ms=%.3f thread_id=%u"),
Tile.Coord.X, Tile.Coord.Y, Tile.Coord.Z, Tile.Level,
bExtracted ? 1 : 0, TriangleCount, CollisionMesh.GetVertices().Num(),
bSupportCohort ? 1 : 0, bPausedReplay ? 1 : 0,
bExtracted ? 1 : 0, TriangleCount,
CollisionMesh.GetVertices().Num(),
FPlatformTime::ToSeconds64(WorkerStartCycles - QueueCycles) * 1000.0,
FPlatformTime::ToSeconds64(ExtractionEndCycles - ExtractionStartCycles) * 1000.0,
FPlatformTime::ToSeconds64(CookEndCycles - CookStartCycles) * 1000.0,
@@ -226,6 +245,8 @@ namespace
TSharedPtr<RealtimeMesh::FRealtimeMeshStreamSet> Streams;
FVoxelTileKey Tile;
int32 TriangleCount = 0;
bool bSupportCohort = false;
bool bPausedReplay = false;
uint64 QueueCycles = 0;
};
@@ -361,10 +382,19 @@ namespace
bool bEnabled = false;
bool bStarted = false;
bool bUnderDigIssued = false;
bool bUnderfootFieldOverride = false;
bool bUnderfootProbePending = false;
bool bSawFalling = false;
bool bResultLogged = false;
int32 NearDigCount = 8;
int32 NearDigsIssued = 0;
int32 UnderfootDigCount = 1;
int32 UnderfootDigsIssued = 0;
int32 UnderfootFirstFrameChecks = 0;
int32 UnderfootSupportObservedFrames = 0;
int32 UnderfootFalseSupportFrames = 0;
int32 UnderfootFalseAirFrames = 0;
int32 UnderfootInventedVelocityEvents = 0;
int32 Frame = 0;
int32 NearFrames = 0;
int32 NearGateFrames = 0;
@@ -379,6 +409,12 @@ namespace
double UnderDigSeconds = 0.0;
double LastFrameSeconds = 0.0;
double DigIntervalSeconds = 0.35;
double UnderfootDigIntervalSeconds = 0.1;
double NextUnderfootDigSeconds = 0.0;
uint64 UnderfootEditFrame = 0;
uint64 UnderfootLastObservedFrame = 0;
float UnderfootVelocityBefore = 0.0f;
float UnderfootFieldAfter = 0.0f;
FVector StartPosition = FVector::ZeroVector;
FVector UnderDigStartPosition = FVector::ZeroVector;
};
@@ -1778,6 +1814,8 @@ void AVoxelWorld::BeginPlay()
int32 CommandLineSampleStacks = GVoxelForgeSampleStacks;
int32 CommandLineCollisionGate = GVoxelForgeCollisionGate;
int32 CommandLineStep1GenerationCap = GVoxelForgeStep1GenerationCap;
int32 CommandLineStep1ExitAfterFrames = GVoxelForgeStep1ExitAfterFrames;
int32 CommandLineStep1CsvCaptureOnBeginPlay = GVoxelForgeStep1CsvCaptureOnBeginPlay;
float CommandLineStep1PauseGenerationAfterSeconds =
GVoxelForgeStep1PauseGenerationAfterSeconds;
int32 CommandLineStep1MaxMeshApplies = GVoxelForgeStep1MaxMeshApplies;
@@ -1816,6 +1854,19 @@ void AVoxelWorld::BeginPlay()
{
GVoxelForgeStep1GenerationCap = CommandLineStep1GenerationCap;
}
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.Step1ExitAfterFrames="),
CommandLineStep1ExitAfterFrames))
{
GVoxelForgeStep1ExitAfterFrames = FMath::Max(0, CommandLineStep1ExitAfterFrames);
}
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.Step1CsvCaptureOnBeginPlay="),
CommandLineStep1CsvCaptureOnBeginPlay))
{
GVoxelForgeStep1CsvCaptureOnBeginPlay =
CommandLineStep1CsvCaptureOnBeginPlay != 0 ? 1 : 0;
}
GVoxelForgeStep1ExitStartFrame = GFrameCounter;
bVoxelForgeStep1ExitRequested = false;
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.Step1PauseGenerationAfterSeconds="),
CommandLineStep1PauseGenerationAfterSeconds))
{
@@ -1848,9 +1899,11 @@ void AVoxelWorld::BeginPlay()
GVoxelForgeStep1CoreCookBenchmark = CommandLineStep1CoreCookBenchmark;
}
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeStep1] gen_cap=%d pause_after_s=%.3f max_applies=%d prequeue_l0=%d "
TEXT("[VoxelForgeStep1] gen_cap=%d exit_after_frames=%d pause_after_s=%.3f "
"max_applies=%d prequeue_l0=%d "
"disable_collision=%d collision_probe=%d core_cook_benchmark=%d"),
GVoxelForgeStep1GenerationCap, GVoxelForgeStep1PauseGenerationAfterSeconds,
GVoxelForgeStep1GenerationCap, GVoxelForgeStep1ExitAfterFrames,
GVoxelForgeStep1PauseGenerationAfterSeconds,
GVoxelForgeStep1MaxMeshApplies,
GVoxelForgeStep1PrequeueVisibleLOD0, GVoxelForgeStep1UploadDisableCollision,
GVoxelForgeStep1CollisionProbe, GVoxelForgeStep1CoreCookBenchmark);
@@ -1923,6 +1976,30 @@ void AVoxelWorld::BeginPlay()
VoxelDensityProfile::DefaultSampleInterval);
}
if (GVoxelForgeProfileTileGeneration != 0
|| GVoxelForgeStep1CsvCaptureOnBeginPlay != 0)
{
const VoxelDensityProfile::EMode DensityMode = VoxelDensityProfile::GetMode();
const TCHAR* DensityModeName = DensityMode == VoxelDensityProfile::EMode::Disabled
? TEXT("disabled")
: DensityMode == VoxelDensityProfile::EMode::Sampled
? TEXT("sampled")
: DensityMode == VoxelDensityProfile::EMode::Attribution
? TEXT("attribution") : TEXT("full");
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeProfileState] density_mode=%s profile_density=%d "
"profile_density_full=%d perf_attribution=%d profile_tile_generation=%d"),
DensityModeName, GVoxelForgeProfileDensity, GVoxelForgeProfileDensityFull,
GVoxelForgePerfAttribution, GVoxelForgeProfileTileGeneration);
}
if (GVoxelForgeStep1CsvCaptureOnBeginPlay != 0)
{
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeStep1Csv] stage=beginplay_event frame=%llu"),
static_cast<unsigned long long>(GFrameCounter));
CSV_EVENT_GLOBAL(TEXT("VFStep1GameBeginPlay"));
}
if (!Settings)
{
UE_LOG(LogTemp, Error, TEXT("[VoxelWorld] No Settings assigned — world won't generate."));
@@ -1934,19 +2011,28 @@ void AVoxelWorld::BeginPlay()
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestRenderDistanceChunks="),
TestRenderDistanceChunks)
&& TestRenderDistanceChunks > 0;
int32 TestClipRadius = 0;
const bool bTestClipRadius =
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestClipRadius="), TestClipRadius)
&& TestClipRadius > 0;
int32 TestMaxClipLevel = -1;
const bool bTestMaxClipLevel =
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestMaxClipLevel="), TestMaxClipLevel);
if (bTestRenderDistance || bTestMaxClipLevel)
if (bTestRenderDistance || bTestClipRadius || bTestMaxClipLevel)
{
if (UVoxelSettings* TestSettings = DuplicateObject<UVoxelSettings>(Settings, this))
{
const int32 AuthoredRenderDistance = Settings->RenderDistanceChunks;
const int32 AuthoredClipRadius = Settings->ClipRadius;
const int32 AuthoredMaxClipLevel = Settings->MaxClipLevel;
if (bTestRenderDistance)
{
TestSettings->RenderDistanceChunks = TestRenderDistanceChunks;
}
if (bTestClipRadius)
{
TestSettings->ClipRadius = FMath::Clamp(TestClipRadius, 1, 16);
}
if (bTestMaxClipLevel)
{
TestSettings->MaxClipLevel = FMath::Clamp(TestMaxClipLevel, 0, 8);
@@ -1954,8 +2040,10 @@ void AVoxelWorld::BeginPlay()
Settings = TestSettings;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeHorizonMeasure] transient_settings=1 "
"render_distance_chunks=%d authored=%d max_clip_level=%d authored=%d"),
"render_distance_chunks=%d authored=%d clip_radius=%d authored=%d "
"max_clip_level=%d authored=%d"),
Settings->RenderDistanceChunks, AuthoredRenderDistance,
Settings->ClipRadius, AuthoredClipRadius,
Settings->MaxClipLevel, AuthoredMaxClipLevel);
}
}
@@ -2390,13 +2478,26 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
{
FDigMotionTestState& Test = GVoxelForgeDigMotionTests.Add(this);
Test.bEnabled = true;
int32 FieldOverrideValue = 0;
FParse::Value(CommandLine, TEXT("voxel.TestDigMotionNearDigCount="), Test.NearDigCount);
FParse::Value(CommandLine, TEXT("voxel.TestDigMotionIntervalSeconds="), Test.DigIntervalSeconds);
Test.NearDigCount = FMath::Clamp(Test.NearDigCount, 1, 32);
Test.DigIntervalSeconds = FMath::Clamp(Test.DigIntervalSeconds, 0.1, 3.0);
FParse::Value(CommandLine, TEXT("voxel.TestDigMotionUnderfootDigCount="),
Test.UnderfootDigCount);
FParse::Value(CommandLine, TEXT("voxel.TestDigMotionUnderfootIntervalSeconds="),
Test.UnderfootDigIntervalSeconds);
FParse::Value(CommandLine, TEXT("voxel.TestDigMotionFieldOverride="),
FieldOverrideValue);
Test.NearDigCount = FMath::Clamp(Test.NearDigCount, 0, 32);
Test.UnderfootDigCount = FMath::Clamp(Test.UnderfootDigCount, 1, 100);
Test.DigIntervalSeconds = FMath::Clamp(Test.DigIntervalSeconds, 0.0, 3.0);
Test.UnderfootDigIntervalSeconds = FMath::Clamp(
Test.UnderfootDigIntervalSeconds, 0.0, 1.0);
Test.bUnderfootFieldOverride = FieldOverrideValue != 0;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeDigMotion] configured=1 near_dig_count=%d interval_s=%.3f"),
Test.NearDigCount, Test.DigIntervalSeconds);
TEXT("[VoxelForgeDigMotion] configured=1 near_dig_count=%d interval_s=%.3f "
"underfoot_dig_count=%d underfoot_interval_s=%.3f field_override=%d"),
Test.NearDigCount, Test.DigIntervalSeconds, Test.UnderfootDigCount,
Test.UnderfootDigIntervalSeconds, Test.bUnderfootFieldOverride ? 1 : 0);
}
FString RequestedTileHashDumpPath;
if (FParse::Value(CommandLine, TEXT("voxel.TileHashDump="), RequestedTileHashDumpPath))
@@ -3331,6 +3432,54 @@ void AVoxelWorld::AdvanceHeadlessDigMotionTest(FVector& InOutPlayerPosition, APa
}
const double Now = FPlatformTime::Seconds();
if (Test->bUnderfootProbePending && GFrameCounter > Test->UnderfootEditFrame)
{
const float HalfHeightVoxels = Character->GetCapsuleComponent()
->GetScaledCapsuleHalfHeight() / VOXEL_SIZE;
FVector ProbePosition = WorldToLocalVoxel(InOutPlayerPosition);
ProbePosition.Z -= HalfHeightVoxels + 0.5f;
const float FieldDensity = Generator
? Generator->GetDensityAt(ProbePosition.X, ProbePosition.Y, ProbePosition.Z)
: 0.0f;
const bool bFieldSolidAtFeet = VoxelMath::IsFinite(FieldDensity)
&& FieldDensity < 0.0f;
const bool bCmcHasFloor = Movement->IsMovingOnGround()
&& Movement->CurrentFloor.IsWalkableFloor();
if (!bFieldSolidAtFeet && bCmcHasFloor)
{
++Test->UnderfootFalseSupportFrames;
}
if (bFieldSolidAtFeet && Movement->MovementMode == MOVE_Falling)
{
++Test->UnderfootFalseAirFrames;
}
const float FrameDelta = GetWorld() ? GetWorld()->GetDeltaSeconds() : 0.0f;
const float ExpectedGravityDelta = Movement->GetGravityZ() * FrameDelta;
const float ActualVelocityDelta = Movement->Velocity.Z - Test->UnderfootVelocityBefore;
const float UnexplainedDownwardDelta = FMath::Max(
0.0f, ExpectedGravityDelta - ActualVelocityDelta);
if (UnexplainedDownwardDelta > 25.0f)
{
++Test->UnderfootInventedVelocityEvents;
}
++Test->UnderfootFirstFrameChecks;
++Test->UnderfootSupportObservedFrames;
Test->UnderfootLastObservedFrame = GFrameCounter;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeDigMotionField] dig=%d first_sim_frame=%llu field_density=%.5f "
"field_solid=%d movement_mode=%d floor_walkable=%d support=%d "
"velocity_z_cm_s=%.3f expected_gravity_delta_cm_s=%.3f "
"actual_velocity_delta_cm_s=%.3f unexplained_downward_cm_s=%.3f"),
Test->UnderfootDigsIssued,
static_cast<unsigned long long>(GFrameCounter), FieldDensity,
bFieldSolidAtFeet ? 1 : 0, static_cast<int32>(Movement->MovementMode),
Movement->CurrentFloor.IsWalkableFloor() ? 1 : 0, bCmcHasFloor ? 1 : 0,
Movement->Velocity.Z, ExpectedGravityDelta, ActualVelocityDelta,
UnexplainedDownwardDelta);
Test->bUnderfootProbePending = false;
}
if (Now - Test->BeginSeconds > 240.0)
{
Test->bResultLogged = true;
@@ -3398,28 +3547,68 @@ void AVoxelWorld::AdvanceHeadlessDigMotionTest(FVector& InOutPlayerPosition, APa
return;
}
if (Test->NearDigsIssued >= Test->NearDigCount && !Test->bUnderDigIssued
&& Now >= Test->NextDigSeconds + 0.5)
const bool bUnderfootDelayElapsed = Test->bUnderDigIssued
? Now >= Test->NextUnderfootDigSeconds
: Now >= Test->NextDigSeconds + 0.5;
if (Test->NearDigsIssued >= Test->NearDigCount
&& Test->UnderfootDigsIssued < Test->UnderfootDigCount
&& bUnderfootDelayElapsed)
{
const float HalfHeightVoxels = Character->GetCapsuleComponent()
->GetScaledCapsuleHalfHeight() / VOXEL_SIZE;
FVector DigCenterVoxel = WorldToLocalVoxel(InOutPlayerPosition);
const FVector LocalPawnPosition = WorldToLocalVoxel(InOutPlayerPosition);
FVector DigCenterVoxel = LocalPawnPosition;
DigCenterVoxel.Z -= HalfHeightVoxels + 1.0f;
Test->UnderDigStartPosition = InOutPlayerPosition;
Test->UnderDigSeconds = Now;
Test->bUnderDigIssued = true;
Test->NextDigSeconds = Now;
if (!Test->bUnderDigIssued)
{
Test->UnderDigStartPosition = InOutPlayerPosition;
Test->UnderDigSeconds = Now;
Test->bUnderDigIssued = true;
}
const FVector DigCenterWorld = LocalVoxelToWorld(DigCenterVoxel);
FVector FeetProbe = LocalPawnPosition;
FeetProbe.Z -= HalfHeightVoxels + 0.5f;
const float FieldDensityBefore = Generator
? Generator->GetDensityAt(FeetProbe.X, FeetProbe.Y, FeetProbe.Z)
: 0.0f;
Test->UnderfootVelocityBefore = Movement->Velocity.Z;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeDigMotion] under_dig=1 center=(%.2f,%.2f,%.2f) pawn=(%.2f,%.2f,%.2f) "
"radius_vox=5"),
TEXT("[VoxelForgeDigMotion] under_dig=%d center=(%.2f,%.2f,%.2f) "
"pawn=(%.2f,%.2f,%.2f) radius_vox=5 field_before=%.5f frame=%llu"),
Test->UnderfootDigsIssued + 1,
DigCenterWorld.X, DigCenterWorld.Y, DigCenterWorld.Z,
InOutPlayerPosition.X, InOutPlayerPosition.Y, InOutPlayerPosition.Z);
InOutPlayerPosition.X, InOutPlayerPosition.Y, InOutPlayerPosition.Z,
FieldDensityBefore,
static_cast<unsigned long long>(GFrameCounter));
CarveAtPosition(DigCenterWorld, 5.0f, 100.0f);
Test->UnderfootFieldAfter = Generator
? Generator->GetDensityAt(FeetProbe.X, FeetProbe.Y, FeetProbe.Z)
: 0.0f;
++Test->UnderfootDigsIssued;
Test->UnderfootEditFrame = GFrameCounter;
Test->bUnderfootProbePending = true;
Test->NextUnderfootDigSeconds = Now + Test->UnderfootDigIntervalSeconds;
Test->LastFrameSeconds = Now;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeDigMotionField] dig=%d stage=post_edit frame=%llu "
"field_before=%.5f field_after=%.5f override=%d"),
Test->UnderfootDigsIssued,
static_cast<unsigned long long>(GFrameCounter), FieldDensityBefore,
Test->UnderfootFieldAfter, Test->bUnderfootFieldOverride ? 1 : 0);
if (Test->bUnderfootFieldOverride
&& VoxelMath::IsFinite(Test->UnderfootFieldAfter)
&& Test->UnderfootFieldAfter >= 0.0f)
{
// Measurement-only prototype: detach immediately from the removed field floor.
// Normal play is unchanged because TestDigMotion is an explicit headless switch.
Movement->CurrentFloor.Clear();
Movement->SetMovementMode(MOVE_Falling);
}
return;
}
if (Test->bUnderDigIssued && Now - Test->UnderDigSeconds >= 2.5)
if (Test->UnderfootDigsIssued >= Test->UnderfootDigCount
&& !Test->bUnderfootProbePending && Now - Test->UnderDigSeconds >= 2.5)
{
Test->bResultLogged = true;
const double FallCm = Test->UnderDigStartPosition.Z - InOutPlayerPosition.Z;
@@ -3429,16 +3618,29 @@ void AVoxelWorld::AdvanceHeadlessDigMotionTest(FVector& InOutPlayerPosition, APa
&& Test->NearVerticalMotionFrames == 0
&& Test->bSawFalling
&& FallCm >= 10.0
&& Test->UnderMaxDownwardVelocity < 6000.0f;
&& Test->UnderMaxDownwardVelocity < 6000.0f
&& Test->UnderfootDigsIssued == Test->UnderfootDigCount
&& Test->UnderfootFirstFrameChecks == Test->UnderfootDigCount
&& Test->UnderfootSupportObservedFrames >= Test->UnderfootFirstFrameChecks
&& Test->UnderfootFalseSupportFrames == 0
&& Test->UnderfootFalseAirFrames == 0
&& Test->UnderfootInventedVelocityEvents == 0;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeDigMotion] result=%s near_digs=%d near_frames=%d near_gate_frames=%d "
"near_falling_frames=%d near_vertical_motion_frames=%d "
"under_saw_falling=%d under_gate_frames=%d under_fall_cm=%.2f "
"under_max_downward_velocity_cm_s=%.2f"),
"under_max_downward_velocity_cm_s=%.2f under_digs=%d "
"first_frame_checks=%d support_observed_frames=%d "
"false_support_frames=%d false_air_frames=%d "
"invented_downward_velocity_events=%d field_override=%d"),
bPassed ? TEXT("PASS") : TEXT("FAIL"), Test->NearDigsIssued, Test->NearFrames,
Test->NearGateFrames, Test->NearFallingFrames, Test->NearVerticalMotionFrames,
Test->bSawFalling ? 1 : 0, Test->UnderGateFrames, FallCm,
Test->UnderMaxDownwardVelocity);
Test->UnderMaxDownwardVelocity, Test->UnderfootDigsIssued,
Test->UnderfootFirstFrameChecks, Test->UnderfootSupportObservedFrames,
Test->UnderfootFalseSupportFrames,
Test->UnderfootFalseAirFrames, Test->UnderfootInventedVelocityEvents,
Test->bUnderfootFieldOverride ? 1 : 0);
FPlatformMisc::RequestExitWithStatus(
false, bPassed ? 0 : 1, TEXT("VoxelForge dig-motion repro complete"));
}
@@ -3461,6 +3663,39 @@ void AVoxelWorld::RecordHeadlessDigMotionFrame(
const float VerticalVelocity = Movement ? Movement->Velocity.Z : Pawn->GetVelocity().Z;
const float GravityZ = Movement ? Movement->GetGravityZ() : 0.0f;
const int32 MovementMode = Movement ? static_cast<int32>(Movement->MovementMode) : -1;
if (Movement && Test->UnderfootDigsIssued > 0
&& GFrameCounter > Test->UnderfootEditFrame
&& Test->UnderfootLastObservedFrame != GFrameCounter)
{
const float HalfHeightVoxels = Character && Character->GetCapsuleComponent()
? Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight() / VOXEL_SIZE
: 0.0f;
FVector ProbePosition = WorldToLocalVoxel(PlayerPosition);
ProbePosition.Z -= HalfHeightVoxels + 0.5f;
const float FieldDensity = Generator
? Generator->GetDensityAt(ProbePosition.X, ProbePosition.Y, ProbePosition.Z)
: 0.0f;
const bool bFieldSolidAtFeet = VoxelMath::IsFinite(FieldDensity)
&& FieldDensity < 0.0f;
const bool bCmcHasFloor = Movement->IsMovingOnGround()
&& Movement->CurrentFloor.IsWalkableFloor();
if (!bFieldSolidAtFeet && bCmcHasFloor)
{
++Test->UnderfootFalseSupportFrames;
}
if (bFieldSolidAtFeet && Movement->MovementMode == MOVE_Falling)
{
++Test->UnderfootFalseAirFrames;
}
++Test->UnderfootSupportObservedFrames;
Test->UnderfootLastObservedFrame = GFrameCounter;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeDigMotionField] stage=frame frame=%llu field_density=%.5f "
"field_solid=%d movement_mode=%d floor_walkable=%d support=%d"),
static_cast<unsigned long long>(GFrameCounter), FieldDensity,
bFieldSolidAtFeet ? 1 : 0, MovementMode,
Movement->CurrentFloor.IsWalkableFloor() ? 1 : 0, bCmcHasFloor ? 1 : 0);
}
FVoxelTileKey SupportTile;
const bool bSupportReady = IsPlayerSupportCollisionReady(Pawn, PlayerPosition, &SupportTile);
const bool bNearPhase = !Test->bUnderDigIssued;
@@ -5158,6 +5393,37 @@ void AVoxelWorld::MaybeFinishHeadlessCollisionGateStressTest()
void AVoxelWorld::MaybeFinishHeadlessStreamingTest()
{
if (GVoxelForgeStep1ExitAfterFrames > 0 && !bVoxelForgeStep1ExitRequested
&& GFrameCounter - GVoxelForgeStep1ExitStartFrame
>= static_cast<uint64>(GVoxelForgeStep1ExitAfterFrames))
{
bVoxelForgeStep1ExitRequested = true;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeStep1Exit] target_frames=%d elapsed_frames=%llu; requesting clean exit"),
GVoxelForgeStep1ExitAfterFrames,
static_cast<unsigned long long>(GFrameCounter - GVoxelForgeStep1ExitStartFrame));
FPlatformMisc::RequestExit(false, TEXT("VoxelForge step 1 frame-count capture complete"));
return;
}
const double ExitDiagnosticNowSeconds = FPlatformTime::Seconds();
const double ExitDiagnosticElapsedSeconds = HeadlessStreamingTestBeginSeconds > 0.0
? ExitDiagnosticNowSeconds - HeadlessStreamingTestBeginSeconds : -1.0;
static double LastExitDiagnosticElapsedSeconds = -30.0;
if (ExitDiagnosticElapsedSeconds >= 0.0
&& ExitDiagnosticElapsedSeconds - LastExitDiagnosticElapsedSeconds >= 30.0)
{
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeStreamingExitDiag] elapsed_s=%.3f threshold_s=%.3f "
"exit_requested=%d surface_fall=%d crossing=%d gate_stress=%d"),
ExitDiagnosticElapsedSeconds, HeadlessStreamingTestExitSeconds,
bHeadlessStreamingTestExitRequested ? 1 : 0,
bHeadlessSurfaceFallTest ? 1 : 0,
bHeadlessStrateCrossingTest ? 1 : 0,
bHeadlessCollisionGateStressTest ? 1 : 0);
LastExitDiagnosticElapsedSeconds = ExitDiagnosticElapsedSeconds;
}
if (bHeadlessStrateCrossingTest)
{
MaybeFinishHeadlessStrateCrossingTest();
@@ -5933,6 +6199,26 @@ void AVoxelWorld::Tick(float DeltaTime)
TEXT("[VoxelForgeStep1] generation_paused=1 elapsed_s=%.3f pending=%d held_results=%d"),
NowSeconds - GVoxelForgeStep1StartSeconds, PendingTiles.Num(),
Step1HeldApplyResults.Num());
if (GVoxelForgeStep1CoreCookBenchmark != 0
&& Step1PauseReplayStreams.IsValid() && !bStep1PauseReplayQueued)
{
if (FQueuedThreadPool* CookPool = GVoxelForgeStep1CoreCookPool.GetOrCreate())
{
CookPool->AddQueuedWork(
new FVoxelStep1CoreCookWork(
Step1PauseReplayStreams, Step1PauseReplayTile,
Step1PauseReplayTriangles, true, true,
FPlatformTime::Cycles64()),
EQueuedWorkPriority::High);
bStep1PauseReplayQueued = true;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeStep1CoreCook] stage=paused_replay_queued "
"tile=(%d,%d,%d) triangles=%d"),
Step1PauseReplayTile.Coord.X, Step1PauseReplayTile.Coord.Y,
Step1PauseReplayTile.Coord.Z, Step1PauseReplayTriangles);
Step1PauseReplayStreams.Reset();
}
}
}
}
@@ -7195,9 +7481,10 @@ void AVoxelWorld::HandleRealtimeMeshCollisionBodyUpdated(URealtimeMesh* Mesh, UB
APawn* Pawn = nullptr;
FVector PlayerPosition = FVector::ZeroVector;
FVoxelTileKey CurrentSupportTile;
if (TryGetPlayerPosition(PlayerPosition, &Pawn)
&& GetPlayerSupportTile(Pawn, PlayerPosition, CurrentSupportTile)
&& CurrentSupportTile == Pair.Key)
const bool bHaveCurrentSupport = TryGetPlayerPosition(PlayerPosition, &Pawn)
&& GetPlayerSupportTile(Pawn, PlayerPosition, CurrentSupportTile);
if ((bHaveCurrentSupport && CurrentSupportTile == Pair.Key)
|| Pending.bStep1SupportCohort)
{
const URealtimeMeshComponent* Component = TileComponents.FindRef(Pair.Key);
Pending.bPhysicsStateReadyAtBodyUpdate = IsValid(Component)
@@ -7324,10 +7611,16 @@ void AVoxelWorld::PollStep1SupportSweepProbes()
}
if (bHit && Hit.GetComponent() == Component)
{
const UPrimitiveComponent* ExactHitComponent = Hit.GetComponent();
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeCollisionProbe] stage=first_support_capsule_sweep tile=(%d,%d,%d) "
"request_to_sweep_ms=%.3f body_to_sweep_ms=%.3f hit_z=%.3f"),
"pawn_channel_target=1 target_component=%s hit_component=%s "
"current_support_match=%d request_to_sweep_ms=%.3f "
"body_to_sweep_ms=%.3f hit_z=%.3f"),
It.Key().Coord.X, It.Key().Coord.Y, It.Key().Coord.Z,
*Component->GetName(),
ExactHitComponent ? *ExactHitComponent->GetName() : TEXT("none"),
bHaveCurrentSupportTile && CurrentSupportTile == It.Key() ? 1 : 0,
FPlatformTime::ToSeconds64(NowCycles - Probe.CollisionRequestCycles) * 1000.0,
FPlatformTime::ToSeconds64(NowCycles - Probe.BodyUpdatedCycles) * 1000.0,
Hit.ImpactPoint.Z);
@@ -7337,7 +7630,7 @@ void AVoxelWorld::PollStep1SupportSweepProbes()
}
if (Probe.BodyUpdatedCycles != 0 && NowCycles >= Probe.BodyUpdatedCycles
&& FPlatformTime::ToSeconds64(NowCycles - Probe.BodyUpdatedCycles) > 10.0)
&& FPlatformTime::ToSeconds64(NowCycles - Probe.BodyUpdatedCycles) > 30.0)
{
UE_LOG(LogTemp, Warning,
TEXT("[VoxelForgeCollisionProbe] stage=sweep_timeout tile=(%d,%d,%d) "
@@ -7656,6 +7949,7 @@ void AVoxelWorld::HandleTileCollisionCookComplete(
const uint64 MeasuredGenerationEndCycles = Pending->GenerationEndCycles;
const uint64 MeasuredApplyStartCycles = Pending->ApplyStartCycles;
const EVoxelTileWorkPriority WorkPriority = Pending->WorkPriority;
const bool bMeasuredSupportCohort = Pending->bStep1SupportCohort;
const int32 NumVertices = Pending->NumVertices;
const int32 NumTriangles = Pending->NumTriangles;
const int32 NumSkirtTriangles = Pending->NumSkirtTriangles;
@@ -7752,6 +8046,8 @@ void AVoxelWorld::HandleTileCollisionCookComplete(
? FPlatformTime::ToSeconds64(EndCycles - StartCycles)
: -1.0;
};
const double MeshGeneratedToReadySeconds = SecondsBetween(
ReadyCycles, MeasuredGenerationEndCycles);
const double RequestToWorkerStartSeconds = SecondsBetween(
MeasuredGenerationStartCycles, MeasuredRequestStartCycles);
const double WorkerGenerationSeconds = SecondsBetween(
@@ -7787,7 +8083,8 @@ void AVoxelWorld::HandleTileCollisionCookComplete(
{
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeCollisionReady] tile=(%d,%d,%d) level=%d result=%s body_setup=%d "
"ready=%d body_event=%d priority=%d critical=%d vertices=%d triangles=%d "
"ready=%d body_event=%d priority=%d critical=%d support_cohort=%d "
"vertices=%d triangles=%d "
"skirt_triangles=%d pending_cooks=%d async=%d complex_as_simple=%d fast_cook=%d "
"merge=%d request_to_worker_s=%.6f worker_gen_s=%.6f worker_to_apply_s=%.6f "
"apply_to_mesh_submit_s=%.6f mesh_to_collision_submit_s=%.6f "
@@ -7795,6 +8092,7 @@ void AVoxelWorld::HandleTileCollisionCookComplete(
"physics_ready_at_body=%d body_update_to_physics_ready_s=%.6f "
"body_update_to_future_callback_s=%.6f future_callback_to_gt_s=%.6f "
"suppressed_to_ready_s=%.6f mesh_submit_to_ready=%.6f "
"mesh_generated_to_ready_s=%.6f "
"collision_request_to_ready=%.6f body_event_to_future=%.6f"),
Tile.Coord.X, Tile.Coord.Y, Tile.Coord.Z, Tile.Level, ResultName,
CompletedMesh->GetBodySetup() != nullptr ? 1 : 0,
@@ -7802,6 +8100,7 @@ void AVoxelWorld::HandleTileCollisionCookComplete(
bBodyUpdatedEventSeen ? 1 : 0,
static_cast<int32>(WorkPriority),
WorkPriority == EVoxelTileWorkPriority::CollisionCritical ? 1 : 0,
bMeasuredSupportCohort ? 1 : 0,
NumVertices, NumTriangles, NumSkirtTriangles, PendingCooksAtSubmit,
bUseAsyncCook ? 1 : 0, bUseComplexAsSimpleCollision ? 1 : 0,
bShouldFastCookMeshes ? 1 : 0, bMergeAllMeshes ? 1 : 0,
@@ -7810,7 +8109,7 @@ void AVoxelWorld::HandleTileCollisionCookComplete(
RMCSubmitToBodyUpdateSeconds, FramesToBodyUpdate,
bPhysicsStateReadyAtBodyUpdate ? 1 : 0, BodyEventToPhysicsReadySeconds,
BodyUpdateToFutureCallbackSeconds, FutureCallbackToGameThreadSeconds,
SuppressedToReadySeconds, MeshToCookSeconds,
SuppressedToReadySeconds, MeshToCookSeconds, MeshGeneratedToReadySeconds,
CollisionRequestToCookSeconds, BodyEventToFutureSeconds);
}
if (VoxelForgeStartupTrace::IsActive())
@@ -7964,6 +8263,7 @@ void AVoxelWorld::ProcessPendingChunks()
GVoxelForgeStep1PrequeueVisibleLOD0, Step1HeldVisibleLOD0Count,
Step1HeldApplyResults.Num(), GetActiveGenerationTaskCount(), PendingTiles.Num(),
static_cast<unsigned long long>(GFrameCounter));
CSV_EVENT_GLOBAL(TEXT("VFStep1UploadCorpusReleased"));
DrainStartCycles = FPlatformTime::Cycles64();
}
else
@@ -8058,6 +8358,21 @@ bool AVoxelWorld::ApplyTileResult(FChunkResult& Result)
return false;
}
// Retain one approximately 4k-triangle support stream only for the opt-in paused cook
// replay. The replay is queued after the timed generation pause, so it measures the dedicated
// cook path without concurrent tile generation and still uses this exact generated mesh.
if (GVoxelForgeStep1CoreCookBenchmark != 0
&& Result.bStep1SupportCohort
&& !Step1PauseReplayStreams.IsValid()
&& Result.Streams.IsValid()
&& Result.NumTriangles >= 3500 && Result.NumTriangles <= 4300)
{
Step1PauseReplayStreams = MakeShared<RealtimeMesh::FRealtimeMeshStreamSet>(
*Result.Streams);
Step1PauseReplayTile = Result.Tile;
Step1PauseReplayTriangles = Result.NumTriangles;
}
// Store only lattices still within the local pawn's fixed 3×3×3 neighborhood. The cache is
// independent of DiffLayer revisions because it contains only the procedural pre-edit field.
if (Result.PreEditDensityGrid.Samples.Num() > 0
@@ -8689,6 +9004,17 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
VoxelCollisionDesiredTiles::AddBox(
CollisionDesiredTiles, Center, PawnCollisionRadius,
PawnCollisionRadius, PawnCollisionRadius);
if (GVoxelForgeStep1CoreCookBenchmark != 0 && Step1SupportCohortTiles.IsEmpty())
{
VoxelCollisionDesiredTiles::AddBox(
Step1SupportCohortTiles, Center, PawnCollisionRadius,
PawnCollisionRadius, PawnCollisionRadius);
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeStep1SupportCohort] stage=defined center=(%d,%d,%d) "
"radius=%d tiles=%d"),
Center.X, Center.Y, Center.Z, PawnCollisionRadius,
Step1SupportCohortTiles.Num());
}
// Streaming anchors (AI / remote players, §9.3): fold each one's small level-0 box into the SAME
// desired set BEFORE the leaver sweep, so the delta cull releases an anchor's tiles automatically
@@ -9617,6 +9943,28 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
const uint32 TaskEpoch = GenerationEpoch;
const uint32 TaskDesiredEpoch = DesiredEpoch;
const uint32 TaskModificationEpoch = ModificationEpoch;
if (GVoxelForgeStep1CoreCookBenchmark != 0
&& Step1SupportCohortTiles.IsEmpty())
{
FVector PlayerPosition = FVector::ZeroVector;
if (TryGetPlayerPosition(PlayerPosition))
{
const FIntVector CohortCenter = WorldToChunkCoord(
WorldToLocalCm(PlayerPosition));
const int32 PawnRadius = FMath::Clamp(
Settings ? Settings->PawnCollisionRadiusChunks : 1, 1, 4);
VoxelCollisionDesiredTiles::AddBox(
Step1SupportCohortTiles, CohortCenter, PawnRadius,
PawnRadius, PawnRadius);
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeStep1SupportCohort] stage=defined center=(%d,%d,%d) "
"radius=%d tiles=%d source=first_load"),
CohortCenter.X, CohortCenter.Y, CohortCenter.Z, PawnRadius,
Step1SupportCohortTiles.Num());
}
}
const bool bStep1SupportCohort = GVoxelForgeStep1CoreCookBenchmark != 0
&& Step1SupportCohortTiles.Contains(Tile);
// CAPTURE-DURING-MESHING: only level-0 full-res tiles map 1:1 onto a density-clipmap level
// (Step == 1<<Level, Cells == CHUNK_SIZE). When the density volume is active, ask the mesher to
@@ -9662,6 +10010,7 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
Empty.BandChunkLo = BandChunkLo;
Empty.BandChunkHi = BandChunkHi;
Empty.WorkPriority = WorkPriority;
Empty.bStep1SupportCohort = bStep1SupportCohort;
if (WorkPriority == EVoxelTileWorkPriority::CollisionCritical)
CriticalProcessQueue.Enqueue(MoveTemp(Empty));
else if (WorkPriority == EVoxelTileWorkPriority::Edited)
@@ -9694,7 +10043,7 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
bWantCapture, bWantPreEditDensityGrid, bWantSurfaceWitnesses,
BandVoxLo, BandVoxHi, BandChunkLo, BandChunkHi,
RequestStartCycles, TaskDesiredEpoch, TaskModificationEpoch, Cancellation,
WorkPriority]()
WorkPriority, bStep1SupportCohort]()
{
// RAII: decrement the counter on every exit path.
struct FTaskGuard
@@ -9717,6 +10066,7 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
Canceled.ModificationEpoch = TaskModificationEpoch;
Canceled.RequestStartCycles = RequestStartCycles;
Canceled.WorkPriority = WorkPriority;
Canceled.bStep1SupportCohort = bStep1SupportCohort;
Canceled.bAborted = true;
Canceled.bObsolete = true;
if (WorkPriority == EVoxelTileWorkPriority::CollisionCritical)
@@ -9741,6 +10091,7 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
Result.DesiredEpoch = TaskDesiredEpoch;
Result.ModificationEpoch = TaskModificationEpoch;
Result.WorkPriority = WorkPriority;
Result.bStep1SupportCohort = bStep1SupportCohort;
Result.bCaptureSurfaceWitnesses = bWantSurfaceWitnesses;
const uint64 GenerationStartCycles = FPlatformTime::Cycles64();
Result.GenerationStartCycles = GenerationStartCycles;
@@ -9753,6 +10104,17 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
const uint64 WorkerGenerationCycles = Result.GenerationEndCycles >= GenerationStartCycles
? Result.GenerationEndCycles - GenerationStartCycles : 0;
if (bStep1SupportCohort)
{
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeStep1SupportCohort] stage=mesh_ready tile=(%d,%d,%d) "
"empty=%d triangles=%d priority=%d request_to_worker_ms=%.3f "
"generation_ms=%.3f"),
Tile.Coord.X, Tile.Coord.Y, Tile.Coord.Z, Result.bEmpty ? 1 : 0,
Result.NumTriangles, static_cast<int32>(WorkPriority),
FPlatformTime::ToSeconds64(GenerationStartCycles - RequestStartCycles) * 1000.0,
FPlatformTime::ToSeconds64(WorkerGenerationCycles) * 1000.0);
}
TotalWorkerGenerationTasks.fetch_add(1, std::memory_order_relaxed);
TotalWorkerGenerationCycles.fetch_add(WorkerGenerationCycles, std::memory_order_relaxed);
const bool bObsoleteWork = Cancellation->bObsolete.load(std::memory_order_relaxed)
@@ -10235,14 +10597,15 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
if (GVoxelForgeStep1CoreCookBenchmark != 0
&& Result.Tile.Level == 0
&& Result.WorkPriority == EVoxelTileWorkPriority::CollisionCritical)
&& (Result.WorkPriority == EVoxelTileWorkPriority::CollisionCritical
|| Result.bStep1SupportCohort))
{
if (FQueuedThreadPool* CookPool = GVoxelForgeStep1CoreCookPool.GetOrCreate())
{
CookPool->AddQueuedWork(
new FVoxelStep1CoreCookWork(
MakeShared<RealtimeMesh::FRealtimeMeshStreamSet>(*Result.Streams),
Result.Tile, Result.NumTriangles,
Result.Tile, Result.NumTriangles, Result.bStep1SupportCohort, false,
FPlatformTime::Cycles64()),
EQueuedWorkPriority::High);
}
@@ -10656,6 +11019,7 @@ bool AVoxelWorld::ApplyMeshToTile(FChunkResult& Result)
Pending.GenerationEndCycles = Result.GenerationEndCycles;
Pending.ApplyStartCycles = Result.ApplyStartCycles;
Pending.WorkPriority = Result.WorkPriority;
Pending.bStep1SupportCohort = Result.bStep1SupportCohort;
Pending.NumVertices = Result.NumVertices;
Pending.NumTriangles = Result.NumTriangles;
Pending.NumSkirtTriangles = Result.NumSkirtTriangles;
+9
View File
@@ -117,6 +117,8 @@ struct FChunkResult
uint64 GenerationEndCycles = 0;
uint64 ApplyStartCycles = 0;
EVoxelTileWorkPriority WorkPriority = EVoxelTileWorkPriority::Horizon;
// Step-1-only tag captured on the game thread for the pawn's bounded support cohort.
bool bStep1SupportCohort = false;
int32 ClassifyVerdict = -1; double ClassifySeconds = 0.0; double MeshSeconds = 0.0; double StreamSeconds = 0.0; int32 NumTriangles = 0; int32 NumCeilingTriangles = 0; int32 NumSkirtTriangles = 0;
int32 NumVertices = 0;
uint64 GeometryBytes = 0;
@@ -304,6 +306,7 @@ public:
uint64 GenerationEndCycles = 0;
uint64 ApplyStartCycles = 0;
EVoxelTileWorkPriority WorkPriority = EVoxelTileWorkPriority::Horizon;
bool bStep1SupportCohort = false;
int32 NumVertices = 0;
int32 NumTriangles = 0;
int32 NumSkirtTriangles = 0;
@@ -1040,6 +1043,12 @@ public:
// Independent from DesiredSorted: rendered LOD0 tiles outside the pawn radius remain visible
// without a Chaos body. Pawn radius, anchor boxes, and collision-gate support are unioned here.
TSet<FVoxelTileKey> CollisionDesiredTiles;
// Exact pawn-radius box used only by the default-off Step-1 cook sidecar measurement.
TSet<FVoxelTileKey> Step1SupportCohortTiles;
TSharedPtr<RealtimeMesh::FRealtimeMeshStreamSet> Step1PauseReplayStreams;
FVoxelTileKey Step1PauseReplayTile;
int32 Step1PauseReplayTriangles = 0;
bool bStep1PauseReplayQueued = false;
// A short collision-only column below an unresolved player support tile. Keeping these keys
// stamped prevents a critical request outside the render clipmap from being canceled on rebuild.
TSet<FVoxelTileKey> CollisionGateDesiredTiles;
@@ -334,6 +334,10 @@ struct FExploreArguments
float RenderMaxDistanceVoxels = DefaultRenderMaxDistanceVoxels;
int32 ExportSize = DefaultExportSize;
int32 ExportStep = DefaultExportStep;
bool bExportTileOverride = false;
int32 ExportTileX = 0;
int32 ExportTileY = 0;
int32 ExportTileZ = 0;
// The canonical export owns one immutable lattice for the whole run. Batch cases can pass
// density_grid_reuse=false to produce a matched wall-clock control.
bool bReuseDensityGrid = true;
@@ -463,6 +467,24 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
FParse::Value(*Params, TEXT("rendermaxdistance="), OutArguments.RenderMaxDistanceVoxels);
FParse::Value(*Params, TEXT("exportsize="), OutArguments.ExportSize);
FParse::Value(*Params, TEXT("exportstep="), OutArguments.ExportStep);
const bool bExportTileXSpecified = Params.Contains(
TEXT("exporttilex="), ESearchCase::IgnoreCase);
const bool bExportTileYSpecified = Params.Contains(
TEXT("exporttiley="), ESearchCase::IgnoreCase);
const bool bExportTileZSpecified = Params.Contains(
TEXT("exporttilez="), ESearchCase::IgnoreCase);
if (bExportTileXSpecified || bExportTileYSpecified || bExportTileZSpecified)
{
if (!bExportTileXSpecified || !bExportTileYSpecified || !bExportTileZSpecified
|| !FParse::Value(*Params, TEXT("exporttilex="), OutArguments.ExportTileX)
|| !FParse::Value(*Params, TEXT("exporttiley="), OutArguments.ExportTileY)
|| !FParse::Value(*Params, TEXT("exporttilez="), OutArguments.ExportTileZ))
{
OutError = TEXT("exporttilex, exporttiley, and exporttilez must be supplied together as level-0 tile coordinates.");
return false;
}
OutArguments.bExportTileOverride = true;
}
int32 DensityGridReuse = OutArguments.bReuseDensityGrid ? 1 : 0;
FParse::Value(*Params, TEXT("densitygridreuse="), DensityGridReuse);
OutArguments.bReuseDensityGrid = DensityGridReuse != 0;
@@ -2015,6 +2037,14 @@ FIntVector ChooseExploreMeshOrigin(
int32 MeshSize,
int32 TargetMiddleWorldZ)
{
if (Arguments.bExportTileOverride)
{
return FIntVector(
Arguments.ExportTileX * CHUNK_SIZE,
Arguments.ExportTileY * CHUNK_SIZE,
Arguments.ExportTileZ * CHUNK_SIZE);
}
FVector Centre(0.0f, 0.0f, static_cast<float>(TargetMiddleWorldZ));
if (CameraSeedWalk != nullptr && CameraSeedWalk->CameraSeedPoses.Num() > 0)
{