research: region completion gate (L123, no production change)
The 18 ms gate is one game-thread tick, not compile cost (0.25 ms). MPSC handoff keeps ownership but still polls per tick; velocity look-ahead didn't help (near -0.8 pt, p99 11.77 > 11.00). Both kept as a patch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
bae1144ee4
commit
5e7e0eb1c1
5 files changed
+1288
No files matched your search
@@ -0,0 +1,825 @@
|
||||
diff --git a/Source/VoxelForge/Private/VoxelWorld.cpp b/Source/VoxelForge/Private/VoxelWorld.cpp
|
||||
index 42ac6f5..2c7046b 100644
|
||||
--- a/Source/VoxelForge/Private/VoxelWorld.cpp
|
||||
+++ b/Source/VoxelForge/Private/VoxelWorld.cpp
|
||||
@@ -195,10 +195,13 @@ public:
|
||||
std::atomic<bool> bShuttingDown{false};
|
||||
std::atomic<uint64> NextEditSequence{1};
|
||||
std::atomic<uint64> RegionCompileStartCycles{0};
|
||||
+ std::atomic<uint64> RegionCompileWorkerStartCycles{0};
|
||||
std::atomic<uint64> RegionCompileEndCycles{0};
|
||||
std::atomic<int32> RegionCompileTileCount{0};
|
||||
std::atomic<int32> RegionCompileSerial{0};
|
||||
+ std::atomic<uint32> RegionCompileWorkerThreadId{0};
|
||||
std::atomic<double> RegionCompileWorkerSeconds{0.0};
|
||||
+ std::atomic<double> RegionCompileWallSeconds{0.0};
|
||||
int32 FullResolutionLodCount = 0;
|
||||
FString InitialFailureReason;
|
||||
};
|
||||
@@ -262,6 +265,12 @@ namespace
|
||||
GVoxelForgeCoreRegionConcurrentAdmission,
|
||||
TEXT("Allow tiles independent of a pending Core region compile to proceed; 0 restores the global streaming stop."));
|
||||
|
||||
+ int32 GVoxelForgeCoreRegionLookahead = 0;
|
||||
+ FAutoConsoleVariableRef CVarVoxelForgeCoreRegionLookahead(
|
||||
+ TEXT("voxel.CoreRegionLookahead"),
|
||||
+ GVoxelForgeCoreRegionLookahead,
|
||||
+ TEXT("Retain and compile the next near region set along velocity; 0 disables region lookahead."));
|
||||
+
|
||||
int32 GVoxelForgeStreamingResultBacklogCap = 256;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeStreamingResultBacklogCap(
|
||||
TEXT("voxel.StreamingResultBacklogCap"),
|
||||
@@ -1745,6 +1754,10 @@ void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (Discard.bObsolete) { ++ObsoleteTileAbortCount; }
|
||||
}
|
||||
+ FCoreRegionCompileResult DiscardedRegionCompile;
|
||||
+ while (CoreRegionCompileResults.Dequeue(DiscardedRegionCompile))
|
||||
+ {
|
||||
+ }
|
||||
UE_LOG(LogTemp, Display, TEXT("[VoxelWorld] EndPlay: workers drained in %.6fs; obsolete_tile_aborts=%d"),
|
||||
FPlatformTime::Seconds() - EndPlayStartSeconds, ObsoleteTileAbortCount);
|
||||
VoxelForgeCoreState.Reset();
|
||||
@@ -2093,6 +2106,11 @@ void AVoxelWorld::BeginPlay()
|
||||
PendingCoreRegionTiles.Reset();
|
||||
PendingCoreRegionTileSet.Reset();
|
||||
PendingCoreRetainedTiles.Reset();
|
||||
+ CoreRegionRetainedDesiredTiles.Reset();
|
||||
+ CoreRegionLookaheadTiles.Reset();
|
||||
+ LastCoreRegionLookaheadCenter = FIntVector(MAX_int32, MAX_int32, MAX_int32);
|
||||
+ LastCoreRegionLookaheadStep = FIntVector::ZeroValue;
|
||||
+ LastCoreRegionLookaheadGenerationEpoch = GenerationEpoch;
|
||||
CoreRegionCompileTilesInFlight.Reset();
|
||||
LastCoreRegionRequestCenter = FIntVector(MAX_int32, MAX_int32, MAX_int32);
|
||||
PendingCoreRegionRequestCenter = FIntVector::ZeroValue;
|
||||
@@ -2149,6 +2167,7 @@ void AVoxelWorld::BeginPlay()
|
||||
int32 CommandLineSampleStacks = GVoxelForgeSampleStacks;
|
||||
int32 CommandLineCollisionGate = GVoxelForgeCollisionGate;
|
||||
int32 CommandLineCoreRegionConcurrentAdmission = GVoxelForgeCoreRegionConcurrentAdmission;
|
||||
+ int32 CommandLineCoreRegionLookahead = GVoxelForgeCoreRegionLookahead;
|
||||
int32 CommandLineStreamingCollisionLookahead = GVoxelForgeStreamingCollisionLookahead;
|
||||
int32 CommandLineStreamingResultBacklogCap = GVoxelForgeStreamingResultBacklogCap;
|
||||
int32 CommandLineStreamingReadyQueueDepth = GVoxelForgeStreamingReadyQueueDepth;
|
||||
@@ -2257,6 +2276,11 @@ void AVoxelWorld::BeginPlay()
|
||||
GVoxelForgeCoreRegionConcurrentAdmission =
|
||||
CommandLineCoreRegionConcurrentAdmission != 0 ? 1 : 0;
|
||||
}
|
||||
+ if (FParse::Value(FCommandLine::Get(), TEXT("voxel.CoreRegionLookahead="),
|
||||
+ CommandLineCoreRegionLookahead))
|
||||
+ {
|
||||
+ GVoxelForgeCoreRegionLookahead = CommandLineCoreRegionLookahead != 0 ? 1 : 0;
|
||||
+ }
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingResultBacklogCap="),
|
||||
CommandLineStreamingResultBacklogCap))
|
||||
{
|
||||
@@ -2747,6 +2771,7 @@ void AVoxelWorld::BeginPlay()
|
||||
"\"streaming_coarse_first\":%d,\"streaming_dynamic_near_reserve\":%d,"
|
||||
"\"streaming_legacy_pending_result_cap\":%d,"
|
||||
"\"streaming_collision_lookahead\":%d,\"core_region_concurrent_admission\":%d,"
|
||||
+ "\"core_region_lookahead\":%d,"
|
||||
"\"collision_gate\":%d,"
|
||||
"\"streaming_result_backlog_cap\":%d,\"streaming_startup_apply_boost\":%d,"
|
||||
"\"streaming_far_apply_supplement\":%d,"
|
||||
@@ -2769,6 +2794,7 @@ void AVoxelWorld::BeginPlay()
|
||||
GVoxelForgeStreamingCoarseFirst, GVoxelForgeStreamingDynamicNearReserve,
|
||||
GVoxelForgeStreamingLegacyPendingResultCap,
|
||||
GVoxelForgeStreamingCollisionLookahead, GVoxelForgeCoreRegionConcurrentAdmission,
|
||||
+ GVoxelForgeCoreRegionLookahead,
|
||||
GVoxelForgeCollisionGate,
|
||||
GVoxelForgeStreamingResultBacklogCap, GVoxelForgeStreamingStartupApplyBoost,
|
||||
GVoxelForgeStreamingFarApplySupplement,
|
||||
@@ -3246,11 +3272,13 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
|
||||
float Speed = 800.0f;
|
||||
float StartSeconds = 20.0f;
|
||||
float MoveDurationSeconds = 0.0f;
|
||||
+ float MoveTurnAfterSeconds = 0.0f;
|
||||
float ExitSeconds = 0.0f;
|
||||
int32 ExitOnSteadyValue = 0;
|
||||
FParse::Value(CommandLine, TEXT("voxel.TestMoveSpeedCmPerSecond="), Speed);
|
||||
FParse::Value(CommandLine, TEXT("voxel.TestMoveStartSeconds="), StartSeconds);
|
||||
FParse::Value(CommandLine, TEXT("voxel.TestMoveDurationSeconds="), MoveDurationSeconds);
|
||||
+ FParse::Value(CommandLine, TEXT("voxel.TestMoveTurnAfterSeconds="), MoveTurnAfterSeconds);
|
||||
FParse::Value(CommandLine, TEXT("voxel.TestExitSeconds="), ExitSeconds);
|
||||
FParse::Value(CommandLine, TEXT("voxel.TestTeleportAfterSeconds="),
|
||||
HeadlessStreamingTestTeleportAtSeconds);
|
||||
@@ -3418,9 +3446,44 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
|
||||
{
|
||||
HeadlessStreamingTestDirection = FVector::XAxisVector;
|
||||
}
|
||||
+ FString TurnDirectionText;
|
||||
+ const FString TurnDirectionKey(TEXT("voxel.TestMoveTurnDirection="));
|
||||
+ const int32 TurnDirectionKeyIndex = CommandLineText.Find(
|
||||
+ TurnDirectionKey, ESearchCase::IgnoreCase, ESearchDir::FromStart);
|
||||
+ if (TurnDirectionKeyIndex != INDEX_NONE)
|
||||
+ {
|
||||
+ TurnDirectionText = CommandLineText.Mid(
|
||||
+ TurnDirectionKeyIndex + TurnDirectionKey.Len());
|
||||
+ const int32 DirectionEnd = TurnDirectionText.Find(
|
||||
+ TEXT(" "), ESearchCase::CaseSensitive, ESearchDir::FromStart);
|
||||
+ if (DirectionEnd != INDEX_NONE)
|
||||
+ {
|
||||
+ TurnDirectionText.LeftInline(DirectionEnd, EAllowShrinking::No);
|
||||
+ }
|
||||
+ TurnDirectionText.TrimStartAndEndInline();
|
||||
+ TurnDirectionText.RemoveFromStart(TEXT("\""));
|
||||
+ TurnDirectionText.RemoveFromEnd(TEXT("\""));
|
||||
+ TArray<FString> Components;
|
||||
+ TurnDirectionText.ParseIntoArray(Components, TEXT(","), true);
|
||||
+ if (Components.Num() == 3)
|
||||
+ {
|
||||
+ HeadlessStreamingTestTurnDirection = FVector(
|
||||
+ FCString::Atof(*Components[0]),
|
||||
+ FCString::Atof(*Components[1]),
|
||||
+ FCString::Atof(*Components[2]));
|
||||
+ }
|
||||
+ }
|
||||
+ HeadlessStreamingTestTurnDirection =
|
||||
+ HeadlessStreamingTestTurnDirection.GetSafeNormal();
|
||||
+ if (HeadlessStreamingTestTurnDirection.IsNearlyZero())
|
||||
+ {
|
||||
+ HeadlessStreamingTestTurnDirection = FVector::YAxisVector;
|
||||
+ }
|
||||
HeadlessStreamingTestSpeedCmPerSecond = FMath::Max(Speed, 0.0f);
|
||||
HeadlessStreamingTestStartSeconds = FMath::Max(StartSeconds, 0.0f);
|
||||
HeadlessStreamingTestMoveDurationSeconds = FMath::Max(MoveDurationSeconds, 0.0f);
|
||||
+ HeadlessStreamingTestTurnAfterSeconds = FMath::Max(MoveTurnAfterSeconds, 0.0f);
|
||||
+ bHeadlessStreamingTestTurnDone = false;
|
||||
bHeadlessStreamingTestMovement = HeadlessStreamingTestSpeedCmPerSecond > 0.0f;
|
||||
}
|
||||
|
||||
@@ -3431,15 +3494,20 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
|
||||
HeadlessStreamingTestBeginSeconds = FPlatformTime::Seconds();
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeStreamingTest] clean=1 movement=%d speed_cm_s=%.3f start_s=%.3f "
|
||||
- "move_duration_s=%.3f exit_s=%.3f direction=(%.3f,%.3f,%.3f)"),
|
||||
+ "move_duration_s=%.3f turn_after_s=%.3f exit_s=%.3f "
|
||||
+ "direction=(%.3f,%.3f,%.3f) turn_direction=(%.3f,%.3f,%.3f)"),
|
||||
bHeadlessStreamingTestMovement ? 1 : 0,
|
||||
HeadlessStreamingTestSpeedCmPerSecond,
|
||||
HeadlessStreamingTestStartSeconds,
|
||||
HeadlessStreamingTestMoveDurationSeconds,
|
||||
+ HeadlessStreamingTestTurnAfterSeconds,
|
||||
HeadlessStreamingTestExitSeconds,
|
||||
HeadlessStreamingTestDirection.X,
|
||||
HeadlessStreamingTestDirection.Y,
|
||||
- HeadlessStreamingTestDirection.Z);
|
||||
+ HeadlessStreamingTestDirection.Z,
|
||||
+ HeadlessStreamingTestTurnDirection.X,
|
||||
+ HeadlessStreamingTestTurnDirection.Y,
|
||||
+ HeadlessStreamingTestTurnDirection.Z);
|
||||
}
|
||||
|
||||
int32 CollisionGateStressValue = 0;
|
||||
@@ -4503,6 +4571,32 @@ void AVoxelWorld::AdvanceHeadlessStreamingTest(
|
||||
return;
|
||||
}
|
||||
|
||||
+ if (!bHeadlessStreamingTestTurnDone && HeadlessStreamingTestTurnAfterSeconds > 0.0f
|
||||
+ && ElapsedSeconds - static_cast<double>(HeadlessStreamingTestStartSeconds)
|
||||
+ >= static_cast<double>(HeadlessStreamingTestTurnAfterSeconds))
|
||||
+ {
|
||||
+ bHeadlessStreamingTestTurnDone = true;
|
||||
+ const FVector PreviousDirection = HeadlessStreamingTestDirection;
|
||||
+ HeadlessStreamingTestDirection = HeadlessStreamingTestTurnDirection;
|
||||
+ if (VoxelForgeStartupTrace::IsActive())
|
||||
+ {
|
||||
+ VoxelForgeStartupTrace::RecordEvent(TEXT("streaming_test_move_turned"),
|
||||
+ FString::Printf(
|
||||
+ TEXT("\"elapsed_s\":%s,\"from\":[%.0f,%.0f,%.0f],"
|
||||
+ "\"to\":[%.0f,%.0f,%.0f]"),
|
||||
+ *FString::SanitizeFloat(ElapsedSeconds),
|
||||
+ PreviousDirection.X, PreviousDirection.Y, PreviousDirection.Z,
|
||||
+ HeadlessStreamingTestDirection.X, HeadlessStreamingTestDirection.Y,
|
||||
+ HeadlessStreamingTestDirection.Z));
|
||||
+ }
|
||||
+ UE_LOG(LogTemp, Display,
|
||||
+ TEXT("[VoxelForgeStreamingTest] turn=1 elapsed_s=%.3f from=(%.0f,%.0f,%.0f) "
|
||||
+ "to=(%.0f,%.0f,%.0f)"),
|
||||
+ ElapsedSeconds, PreviousDirection.X, PreviousDirection.Y, PreviousDirection.Z,
|
||||
+ HeadlessStreamingTestDirection.X, HeadlessStreamingTestDirection.Y,
|
||||
+ HeadlessStreamingTestDirection.Z);
|
||||
+ }
|
||||
+
|
||||
if (HeadlessStreamingTestMoveDurationSeconds > 0.0f
|
||||
&& ElapsedSeconds - static_cast<double>(HeadlessStreamingTestStartSeconds)
|
||||
>= static_cast<double>(HeadlessStreamingTestMoveDurationSeconds))
|
||||
@@ -11258,16 +11352,113 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
|
||||
}
|
||||
BuildDesiredTransitionIndex();
|
||||
|
||||
- bool bRetainedSetChanged = false;
|
||||
- int32 RetainedTileCount = 0;
|
||||
+ FIntVector RegionLookaheadStep = FIntVector::ZeroValue;
|
||||
+ if (GVoxelForgeCoreRegionLookahead != 0 && PlayerHeading.Size() >= 50.0f)
|
||||
+ {
|
||||
+ RegionLookaheadStep = GetStreamingHeadingStep(PlayerHeading, PlayerPawn);
|
||||
+ }
|
||||
+ const bool bRegionLookaheadTurn = RegionLookaheadStep != FIntVector::ZeroValue
|
||||
+ && LastCoreRegionLookaheadStep != FIntVector::ZeroValue
|
||||
+ && LastCoreRegionLookaheadStep != RegionLookaheadStep
|
||||
+ && LastCoreRegionLookaheadGenerationEpoch == GenerationEpoch;
|
||||
+ const FIntVector PreviousRegionLookaheadStep = LastCoreRegionLookaheadStep;
|
||||
+ const TArray<FVoxelTileKey> PreviousRegionLookaheadTiles = bRegionLookaheadTurn
|
||||
+ ? CoreRegionLookaheadTiles : TArray<FVoxelTileKey>();
|
||||
+ if (RegionLookaheadStep != FIntVector::ZeroValue
|
||||
+ && (LastCoreRegionLookaheadCenter != Center
|
||||
+ || LastCoreRegionLookaheadStep != RegionLookaheadStep
|
||||
+ || LastCoreRegionLookaheadGenerationEpoch != GenerationEpoch))
|
||||
+ {
|
||||
+ const FVector AheadCameraVoxels = CameraVoxels + FVector(
|
||||
+ static_cast<float>(RegionLookaheadStep.X),
|
||||
+ static_cast<float>(RegionLookaheadStep.Y),
|
||||
+ static_cast<float>(RegionLookaheadStep.Z)) * static_cast<float>(CHUNK_SIZE);
|
||||
+ std::vector<VoxelForgeCore::TileKey> AheadDesired;
|
||||
+ {
|
||||
+ FReadScopeLock Lock(State->WorldLock);
|
||||
+ if (State->World)
|
||||
+ {
|
||||
+ AheadDesired = State->World->desiredTiles(
|
||||
+ {AheadCameraVoxels.X, AheadCameraVoxels.Y, AheadCameraVoxels.Z});
|
||||
+ }
|
||||
+ }
|
||||
+ CoreRegionLookaheadTiles.Reset();
|
||||
+ TSet<FVoxelTileKey> AheadSet;
|
||||
+ for (const VoxelForgeCore::TileKey& Key : AheadDesired)
|
||||
+ {
|
||||
+ if (Key.lod >= State->FullResolutionLodCount) continue;
|
||||
+ const FVoxelTileKey RuntimeKey(
|
||||
+ FIntVector(Key.coordinate.x, Key.coordinate.y, Key.coordinate.z), Key.lod);
|
||||
+ if (!AheadSet.Contains(RuntimeKey))
|
||||
+ {
|
||||
+ AheadSet.Add(RuntimeKey);
|
||||
+ CoreRegionLookaheadTiles.Add(RuntimeKey);
|
||||
+ }
|
||||
+ }
|
||||
+ if (bRegionLookaheadTurn)
|
||||
+ {
|
||||
+ TSet<FVoxelTileKey> ActualSet;
|
||||
+ for (const FVoxelTileKey& Key : DesiredSorted) ActualSet.Add(Key);
|
||||
+ int32 StaleRetainedLookaheadTiles = 0;
|
||||
+ {
|
||||
+ FScopeLock RetainedLock(&State->RetainedTilesLock);
|
||||
+ for (const FVoxelTileKey& Key : PreviousRegionLookaheadTiles)
|
||||
+ {
|
||||
+ if (ActualSet.Contains(Key) || AheadSet.Contains(Key)) continue;
|
||||
+ const VoxelForgeCore::TileKey CoreKey{
|
||||
+ {Key.Coord.X, Key.Coord.Y, Key.Coord.Z}, Key.Level};
|
||||
+ if (State->RetainedTiles.contains(CoreKey))
|
||||
+ {
|
||||
+ ++StaleRetainedLookaheadTiles;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (VoxelForgeStartupTrace::IsActive())
|
||||
+ {
|
||||
+ VoxelForgeStartupTrace::RecordEvent(TEXT("core_region_lookahead_turn"),
|
||||
+ FString::Printf(
|
||||
+ TEXT("\"previous_step\":[%d,%d,%d],\"new_step\":[%d,%d,%d],"
|
||||
+ "\"previous_prefetch_tiles\":%d,"
|
||||
+ "\"stale_retained_lookahead_tiles\":%d"),
|
||||
+ PreviousRegionLookaheadStep.X, PreviousRegionLookaheadStep.Y,
|
||||
+ PreviousRegionLookaheadStep.Z, RegionLookaheadStep.X,
|
||||
+ RegionLookaheadStep.Y, RegionLookaheadStep.Z,
|
||||
+ PreviousRegionLookaheadTiles.Num(), StaleRetainedLookaheadTiles));
|
||||
+ }
|
||||
+ }
|
||||
+ LastCoreRegionLookaheadCenter = Center;
|
||||
+ LastCoreRegionLookaheadStep = RegionLookaheadStep;
|
||||
+ LastCoreRegionLookaheadGenerationEpoch = GenerationEpoch;
|
||||
+ }
|
||||
+ else if (GVoxelForgeCoreRegionLookahead == 0)
|
||||
+ {
|
||||
+ CoreRegionLookaheadTiles.Reset();
|
||||
+ LastCoreRegionLookaheadCenter = FIntVector(MAX_int32, MAX_int32, MAX_int32);
|
||||
+ LastCoreRegionLookaheadStep = FIntVector::ZeroValue;
|
||||
+ LastCoreRegionLookaheadGenerationEpoch = GenerationEpoch;
|
||||
+ }
|
||||
+
|
||||
+ CoreRegionRetainedDesiredTiles = DesiredSorted;
|
||||
TSet<FVoxelTileKey> DesiredSet;
|
||||
+ DesiredSet.Reserve(DesiredSorted.Num() + CoreRegionLookaheadTiles.Num());
|
||||
for (const FVoxelTileKey& Key : DesiredSorted) DesiredSet.Add(Key);
|
||||
+ for (const FVoxelTileKey& Key : CoreRegionLookaheadTiles)
|
||||
+ {
|
||||
+ if (!DesiredSet.Contains(Key))
|
||||
+ {
|
||||
+ DesiredSet.Add(Key);
|
||||
+ CoreRegionRetainedDesiredTiles.Add(Key);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ bool bRetainedSetChanged = false;
|
||||
+ int32 RetainedTileCount = 0;
|
||||
TArray<FVoxelTileKey> MissingRetainedFineTiles;
|
||||
{
|
||||
FScopeLock Lock(&State->RetainedTilesLock);
|
||||
RetainedTileCount = static_cast<int32>(State->RetainedTiles.size());
|
||||
bRetainedSetChanged = DesiredSet.Num() != RetainedTileCount;
|
||||
- for (const FVoxelTileKey& Key : DesiredSorted)
|
||||
+ for (const FVoxelTileKey& Key : CoreRegionRetainedDesiredTiles)
|
||||
{
|
||||
const VoxelForgeCore::TileKey CoreKey{{Key.Coord.X, Key.Coord.Y, Key.Coord.Z}, Key.Level};
|
||||
const bool bAlreadyRetained = State->RetainedTiles.contains(CoreKey);
|
||||
@@ -11313,7 +11504,7 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
|
||||
{
|
||||
PendingCoreRegionTileSet.Add(Key);
|
||||
}
|
||||
- PendingCoreRetainedTiles = DesiredSorted;
|
||||
+ PendingCoreRetainedTiles = CoreRegionRetainedDesiredTiles;
|
||||
bCoreRegionCompilePending = true;
|
||||
++CoreRegionCompileRequestSerial;
|
||||
if (VoxelForgeStartupTrace::IsActive())
|
||||
@@ -11322,6 +11513,7 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
|
||||
TEXT("\"request_serial\":%d,\"reason\":\"%s\",\"center\":[%d,%d,%d],"
|
||||
"\"previous_center\":[%d,%d,%d],\"center_delta\":[%d,%d,%d],"
|
||||
"\"desired_tiles\":%d,\"retained_tiles\":%d,\"missing_region_tiles\":%d,"
|
||||
+ "\"lookahead_region_tiles\":%d,\"lookahead_step\":[%d,%d,%d],"
|
||||
"\"desired_epoch\":%u,\"active_tile_tasks\":%d"),
|
||||
CoreRegionCompileRequestSerial,
|
||||
!bHadPreviousCenter ? TEXT("initial_desired_region_set")
|
||||
@@ -11332,7 +11524,9 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
|
||||
bCenterMoved ? Center.X - PreviousCenter.X : 0,
|
||||
bCenterMoved ? Center.Y - PreviousCenter.Y : 0,
|
||||
bCenterMoved ? Center.Z - PreviousCenter.Z : 0,
|
||||
- DesiredSorted.Num(), RetainedTileCount, PendingCoreRegionTiles.Num(), DesiredEpoch,
|
||||
+ DesiredSorted.Num(), RetainedTileCount, PendingCoreRegionTiles.Num(),
|
||||
+ CoreRegionLookaheadTiles.Num(), RegionLookaheadStep.X, RegionLookaheadStep.Y,
|
||||
+ RegionLookaheadStep.Z, DesiredEpoch,
|
||||
ActiveTileTaskCount.load(std::memory_order_relaxed)));
|
||||
}
|
||||
StartPendingCoreRegionCompile();
|
||||
@@ -11363,13 +11557,27 @@ void AVoxelWorld::StartPendingCoreRegionCompile()
|
||||
State->bRegionCompileSucceeded.store(false, std::memory_order_release);
|
||||
const TArray<FVoxelTileKey> MissingTiles = PendingCoreRegionTiles;
|
||||
const TArray<FVoxelTileKey> RetainedTiles = PendingCoreRetainedTiles;
|
||||
+ TSet<FVoxelTileKey> ActualDesiredSet;
|
||||
+ TSet<FVoxelTileKey> PrefetchSet;
|
||||
+ for (const FVoxelTileKey& Key : DesiredSorted) ActualDesiredSet.Add(Key);
|
||||
+ for (const FVoxelTileKey& Key : CoreRegionLookaheadTiles) PrefetchSet.Add(Key);
|
||||
+ TArray<FVoxelTileKey> PrefetchTiles;
|
||||
+ for (const FVoxelTileKey& Key : MissingTiles)
|
||||
+ {
|
||||
+ if (PrefetchSet.Contains(Key) && !ActualDesiredSet.Contains(Key))
|
||||
+ {
|
||||
+ PrefetchTiles.Add(Key);
|
||||
+ }
|
||||
+ }
|
||||
const FIntVector CompileCenter = PendingCoreRegionRequestCenter;
|
||||
const bool bAllowConcurrentGeneration = GVoxelForgeCoreRegionConcurrentAdmission != 0;
|
||||
const uint32 TaskEpoch = GenerationEpoch;
|
||||
const uint32 TaskDesiredEpoch = DesiredEpoch;
|
||||
+ const int32 RequestSerial = CoreRegionCompileRequestSerial;
|
||||
const uint32 WorkerLimit = static_cast<uint32>(FMath::Clamp(GetMaxConcurrentTasks(), 1, 16));
|
||||
ActiveCoreRegionCompileCenter = CompileCenter;
|
||||
ActiveCoreRegionCompileRequestSerial = CoreRegionCompileRequestSerial;
|
||||
+ ActiveCoreRegionWastedPrefetchTiles = 0;
|
||||
ActiveCoreRegionCompileStartSeconds = FPlatformTime::Seconds();
|
||||
ActiveCoreRegionCompileStartCycles = FPlatformTime::Cycles64();
|
||||
CoreRegionCompileTilesInFlight.Reset();
|
||||
@@ -11381,25 +11589,32 @@ void AVoxelWorld::StartPendingCoreRegionCompile()
|
||||
1, std::memory_order_relaxed) + 1;
|
||||
State->RegionCompileStartCycles.store(ActiveCoreRegionCompileStartCycles,
|
||||
std::memory_order_release);
|
||||
+ State->RegionCompileWorkerStartCycles.store(0, std::memory_order_release);
|
||||
State->RegionCompileEndCycles.store(0, std::memory_order_release);
|
||||
State->RegionCompileTileCount.store(0, std::memory_order_release);
|
||||
+ State->RegionCompileWorkerThreadId.store(0, std::memory_order_release);
|
||||
State->RegionCompileWorkerSeconds.store(0.0, std::memory_order_release);
|
||||
+ State->RegionCompileWallSeconds.store(0.0, std::memory_order_release);
|
||||
if (VoxelForgeStartupTrace::IsActive())
|
||||
{
|
||||
VoxelForgeStartupTrace::RecordEvent(TEXT("core_region_compile_start"), FString::Printf(
|
||||
TEXT("\"compile_serial\":%d,\"request_serial\":%d,\"center\":[%d,%d,%d],"
|
||||
"\"missing_region_tiles\":%d,\"retained_tiles\":%d,\"concurrent_admission\":%d,"
|
||||
- "\"active_tile_tasks\":%d,\"active_task_guard\":%d"),
|
||||
+ "\"prefetch_compile_tiles\":%d,"
|
||||
+ "\"active_tile_tasks\":%d,\"active_generation_workers\":%d,"
|
||||
+ "\"active_task_guard\":%d,\"game_thread_id\":%u"),
|
||||
CompileSerial, CoreRegionCompileRequestSerial,
|
||||
CompileCenter.X, CompileCenter.Y, CompileCenter.Z,
|
||||
MissingTiles.Num(), RetainedTiles.Num(), bAllowConcurrentGeneration ? 1 : 0,
|
||||
+ PrefetchTiles.Num(),
|
||||
ActiveTileTaskCount.load(std::memory_order_relaxed),
|
||||
- ActiveTaskCount.load(std::memory_order_relaxed)));
|
||||
+ ActiveGenerationWorkerCount.load(std::memory_order_relaxed),
|
||||
+ ActiveTaskCount.load(std::memory_order_relaxed), FPlatformTLS::GetCurrentThreadId()));
|
||||
}
|
||||
ActiveTaskCount.fetch_add(1, std::memory_order_relaxed);
|
||||
UE::Tasks::Launch(TEXT("VoxelForgeCoreTileRegion"),
|
||||
- [this, State, MissingTiles, RetainedTiles, TaskEpoch, TaskDesiredEpoch, WorkerLimit,
|
||||
- bAllowConcurrentGeneration, CompileSerial]()
|
||||
+ [this, State, MissingTiles, RetainedTiles, PrefetchTiles, CompileCenter, TaskEpoch,
|
||||
+ TaskDesiredEpoch, RequestSerial, WorkerLimit, bAllowConcurrentGeneration, CompileSerial]()
|
||||
{
|
||||
struct FTaskGuard
|
||||
{
|
||||
@@ -11407,12 +11622,23 @@ void AVoxelWorld::StartPendingCoreRegionCompile()
|
||||
~FTaskGuard() { Counter.fetch_sub(1, std::memory_order_release); }
|
||||
} Guard{ActiveTaskCount};
|
||||
|
||||
+ FCoreRegionCompileResult Result;
|
||||
+ Result.RuntimeState = State;
|
||||
+ Result.GenerationEpoch = TaskEpoch;
|
||||
+ Result.DesiredEpoch = TaskDesiredEpoch;
|
||||
+ Result.Center = CompileCenter;
|
||||
+ Result.RequestSerial = RequestSerial;
|
||||
+ Result.CompileSerial = CompileSerial;
|
||||
+ Result.PrefetchTiles = PrefetchTiles;
|
||||
+ Result.WorkerStartCycles = FPlatformTime::Cycles64();
|
||||
+ Result.WorkerThreadId = FPlatformTLS::GetCurrentThreadId();
|
||||
+
|
||||
if (bShuttingDown.load(std::memory_order_acquire)
|
||||
|| State->bShuttingDown.load(std::memory_order_acquire))
|
||||
{
|
||||
- State->RegionCompileEndCycles.store(FPlatformTime::Cycles64(), std::memory_order_release);
|
||||
- State->bRegionCompileInFlight.store(false, std::memory_order_release);
|
||||
- State->bRegionCompileFinished.store(true, std::memory_order_release);
|
||||
+ Result.bAborted = true;
|
||||
+ Result.WorkerEndCycles = FPlatformTime::Cycles64();
|
||||
+ CoreRegionCompileResults.Enqueue(MoveTemp(Result));
|
||||
return;
|
||||
}
|
||||
std::vector<VoxelForgeCore::TileKey> Missing;
|
||||
@@ -11456,38 +11682,14 @@ void AVoxelWorld::StartPendingCoreRegionCompile()
|
||||
FWriteScopeLock Lock(State->WorldLock);
|
||||
CompileAndRetain();
|
||||
}
|
||||
- State->RegionCompileWorkerSeconds.store(CompileStats.workerSeconds,
|
||||
- std::memory_order_release);
|
||||
- State->bRegionCompileSucceeded.store(bCompiled, std::memory_order_release);
|
||||
- if (!bCompiled)
|
||||
- {
|
||||
- State->bRegionCompileFailed.store(true, std::memory_order_release);
|
||||
- UE_LOG(LogTemp, Error,
|
||||
- TEXT("[VoxelForgeCoreRuntime] tile-region compile failed epoch=%u desired_epoch=%u: %s"),
|
||||
- TaskEpoch, TaskDesiredEpoch, UTF8_TO_TCHAR(Error.c_str()));
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- {
|
||||
- FScopeLock RetainedLock(&State->RetainedTilesLock);
|
||||
- State->RetainedTiles.clear();
|
||||
- for (const VoxelForgeCore::TileKey& Key : Retained)
|
||||
- {
|
||||
- State->RetainedTiles.insert(Key);
|
||||
- }
|
||||
- }
|
||||
- State->RegionCompileTileCount.store(
|
||||
- static_cast<int32>(CompiledTileCount), std::memory_order_release);
|
||||
- State->bRebuildDesired.store(true, std::memory_order_release);
|
||||
- UE_LOG(LogTemp, Display,
|
||||
- TEXT("[VoxelForgeCoreRuntime] tile_regions_compiled=%d retained=%d epoch=%u desired_epoch=%u "
|
||||
- "wall_ms=%.3f worker_s=%.3f serial=%d"),
|
||||
- static_cast<int32>(CompiledTileCount), RetainedTiles.Num(), TaskEpoch, TaskDesiredEpoch,
|
||||
- CompileStats.wallSeconds * 1000.0, CompileStats.workerSeconds, CompileSerial);
|
||||
- }
|
||||
- State->RegionCompileEndCycles.store(FPlatformTime::Cycles64(), std::memory_order_release);
|
||||
- State->bRegionCompileInFlight.store(false, std::memory_order_release);
|
||||
- State->bRegionCompileFinished.store(true, std::memory_order_release);
|
||||
+ Result.bSucceeded = bCompiled;
|
||||
+ Result.RetainedTiles = RetainedTiles;
|
||||
+ Result.Error = UTF8_TO_TCHAR(Error.c_str());
|
||||
+ Result.CompiledTileCount = static_cast<int32>(CompiledTileCount);
|
||||
+ Result.WorkerSeconds = CompileStats.workerSeconds;
|
||||
+ Result.CoreWallSeconds = CompileStats.wallSeconds;
|
||||
+ Result.WorkerEndCycles = FPlatformTime::Cycles64();
|
||||
+ CoreRegionCompileResults.Enqueue(MoveTemp(Result));
|
||||
}, UE::Tasks::ETaskPriority::BackgroundNormal);
|
||||
}
|
||||
|
||||
@@ -12331,6 +12533,88 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
RecordSchedulerSkip(TEXT("core_not_ready"));
|
||||
return; // Core mode never falls through to the legacy generator.
|
||||
}
|
||||
+ FCoreRegionCompileResult RegionResult;
|
||||
+ while (CoreRegionCompileResults.Dequeue(RegionResult))
|
||||
+ {
|
||||
+ if (RegionResult.RuntimeState.IsValid())
|
||||
+ {
|
||||
+ RegionResult.RuntimeState->bRegionCompileInFlight.store(
|
||||
+ false, std::memory_order_release);
|
||||
+ }
|
||||
+ if (RegionResult.bAborted
|
||||
+ || RegionResult.RuntimeState != State
|
||||
+ || RegionResult.GenerationEpoch != GenerationEpoch)
|
||||
+ {
|
||||
+ if (VoxelForgeStartupTrace::IsActive())
|
||||
+ {
|
||||
+ VoxelForgeStartupTrace::RecordEvent(TEXT("core_region_compile_discard"),
|
||||
+ FString::Printf(
|
||||
+ TEXT("\"request_serial\":%d,\"compile_serial\":%d,"
|
||||
+ "\"result_epoch\":%u,\"current_epoch\":%u,\"aborted\":%d"),
|
||||
+ RegionResult.RequestSerial, RegionResult.CompileSerial,
|
||||
+ RegionResult.GenerationEpoch, GenerationEpoch,
|
||||
+ RegionResult.bAborted ? 1 : 0));
|
||||
+ }
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ State->RegionCompileWorkerStartCycles.store(
|
||||
+ RegionResult.WorkerStartCycles, std::memory_order_release);
|
||||
+ State->RegionCompileEndCycles.store(
|
||||
+ RegionResult.WorkerEndCycles, std::memory_order_release);
|
||||
+ State->RegionCompileTileCount.store(
|
||||
+ RegionResult.CompiledTileCount, std::memory_order_release);
|
||||
+ State->RegionCompileWorkerThreadId.store(
|
||||
+ RegionResult.WorkerThreadId, std::memory_order_release);
|
||||
+ State->RegionCompileWorkerSeconds.store(
|
||||
+ RegionResult.WorkerSeconds, std::memory_order_release);
|
||||
+ State->RegionCompileWallSeconds.store(
|
||||
+ RegionResult.CoreWallSeconds, std::memory_order_release);
|
||||
+ State->bRegionCompileSucceeded.store(
|
||||
+ RegionResult.bSucceeded, std::memory_order_release);
|
||||
+ ActiveCoreRegionCompileCenter = RegionResult.Center;
|
||||
+ ActiveCoreRegionCompileRequestSerial = RegionResult.RequestSerial;
|
||||
+ TSet<FVoxelTileKey> CurrentDesiredSet;
|
||||
+ for (const FVoxelTileKey& Key : DesiredSorted) CurrentDesiredSet.Add(Key);
|
||||
+ ActiveCoreRegionWastedPrefetchTiles = 0;
|
||||
+ for (const FVoxelTileKey& Key : RegionResult.PrefetchTiles)
|
||||
+ {
|
||||
+ if (!CurrentDesiredSet.Contains(Key) && !CoreRegionLookaheadTiles.Contains(Key))
|
||||
+ {
|
||||
+ ++ActiveCoreRegionWastedPrefetchTiles;
|
||||
+ }
|
||||
+ }
|
||||
+ if (RegionResult.bSucceeded)
|
||||
+ {
|
||||
+ {
|
||||
+ FScopeLock RetainedLock(&State->RetainedTilesLock);
|
||||
+ State->RetainedTiles.clear();
|
||||
+ for (const FVoxelTileKey& Key : RegionResult.RetainedTiles)
|
||||
+ {
|
||||
+ State->RetainedTiles.insert({
|
||||
+ {Key.Coord.X, Key.Coord.Y, Key.Coord.Z}, Key.Level});
|
||||
+ }
|
||||
+ }
|
||||
+ State->bRebuildDesired.store(true, std::memory_order_release);
|
||||
+ UE_LOG(LogTemp, Display,
|
||||
+ TEXT("[VoxelForgeCoreRuntime] tile_regions_compiled=%d retained=%d "
|
||||
+ "epoch=%u desired_epoch=%u wall_ms=%.3f worker_s=%.3f serial=%d"),
|
||||
+ RegionResult.CompiledTileCount, RegionResult.RetainedTiles.Num(),
|
||||
+ RegionResult.GenerationEpoch, RegionResult.DesiredEpoch,
|
||||
+ RegionResult.CoreWallSeconds * 1000.0, RegionResult.WorkerSeconds,
|
||||
+ RegionResult.CompileSerial);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ State->bRegionCompileFailed.store(true, std::memory_order_release);
|
||||
+ UE_LOG(LogTemp, Error,
|
||||
+ TEXT("[VoxelForgeCoreRuntime] tile-region compile failed epoch=%u "
|
||||
+ "desired_epoch=%u: %s"),
|
||||
+ RegionResult.GenerationEpoch, RegionResult.DesiredEpoch,
|
||||
+ *RegionResult.Error);
|
||||
+ }
|
||||
+ State->bRegionCompileFinished.store(true, std::memory_order_release);
|
||||
+ }
|
||||
if (State->bRebuildDesired.exchange(false, std::memory_order_acq_rel))
|
||||
{
|
||||
bForceDesiredRebuild = true;
|
||||
@@ -12345,9 +12629,17 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
State->RegionCompileStartCycles.load(std::memory_order_acquire);
|
||||
const uint64 CompileEndCycles =
|
||||
State->RegionCompileEndCycles.load(std::memory_order_acquire);
|
||||
+ const uint64 CompileWorkerStartCycles =
|
||||
+ State->RegionCompileWorkerStartCycles.load(std::memory_order_acquire);
|
||||
const double CompileElapsedMilliseconds = CompileEndCycles >= CompileStartCycles
|
||||
? FPlatformTime::ToSeconds64(CompileEndCycles - CompileStartCycles) * 1000.0
|
||||
: 0.0;
|
||||
+ const double CompileQueueMilliseconds = CompileWorkerStartCycles >= CompileStartCycles
|
||||
+ ? FPlatformTime::ToSeconds64(CompileWorkerStartCycles - CompileStartCycles) * 1000.0
|
||||
+ : 0.0;
|
||||
+ const double CompileTaskMilliseconds = CompileEndCycles >= CompileWorkerStartCycles
|
||||
+ ? FPlatformTime::ToSeconds64(CompileEndCycles - CompileWorkerStartCycles) * 1000.0
|
||||
+ : 0.0;
|
||||
const double GateElapsedMilliseconds = CoreRegionPendingSinceSeconds > 0.0
|
||||
? FMath::Max(0.0, FPlatformTime::Seconds() - CoreRegionPendingSinceSeconds) * 1000.0
|
||||
: 0.0;
|
||||
@@ -12355,6 +12647,10 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
State->RegionCompileTileCount.load(std::memory_order_acquire);
|
||||
const double CompileWorkerSeconds =
|
||||
State->RegionCompileWorkerSeconds.load(std::memory_order_acquire);
|
||||
+ const double CompileCoreWallSeconds =
|
||||
+ State->RegionCompileWallSeconds.load(std::memory_order_acquire);
|
||||
+ const uint32 CompileWorkerThreadId =
|
||||
+ State->RegionCompileWorkerThreadId.load(std::memory_order_acquire);
|
||||
const int32 CompileSerial =
|
||||
State->RegionCompileSerial.load(std::memory_order_acquire);
|
||||
const uint64 IndependentSubmissions = CoreRegionIndependentSubmissions;
|
||||
@@ -12364,16 +12660,23 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
VoxelForgeStartupTrace::RecordEvent(TEXT("core_region_compile_complete"), FString::Printf(
|
||||
TEXT("\"compile_serial\":%d,\"request_serial\":%d,\"center\":[%d,%d,%d],"
|
||||
"\"success\":%d,\"compiled_tiles\":%d,\"compile_elapsed_ms\":%s,"
|
||||
- "\"compile_worker_s\":%s,\"pending_gate_elapsed_ms\":%s,"
|
||||
- "\"independent_submissions\":%llu,"
|
||||
+ "\"compile_queue_ms\":%s,\"compile_task_ms\":%s,"
|
||||
+ "\"compile_core_wall_ms\":%s,\"compile_worker_s\":%s,"
|
||||
+ "\"compile_worker_thread_id\":%u,\"pending_gate_elapsed_ms\":%s,"
|
||||
+ "\"independent_submissions\":%llu,\"wasted_prefetch_tiles\":%d,"
|
||||
"\"dependent_declines\":%llu,\"concurrent_admission\":%d"),
|
||||
CompileSerial, ActiveCoreRegionCompileRequestSerial,
|
||||
ActiveCoreRegionCompileCenter.X, ActiveCoreRegionCompileCenter.Y,
|
||||
ActiveCoreRegionCompileCenter.Z, bRegionCompileSucceeded ? 1 : 0,
|
||||
CompiledTileCount, *FString::SanitizeFloat(CompileElapsedMilliseconds),
|
||||
+ *FString::SanitizeFloat(CompileQueueMilliseconds),
|
||||
+ *FString::SanitizeFloat(CompileTaskMilliseconds),
|
||||
+ *FString::SanitizeFloat(CompileCoreWallSeconds * 1000.0),
|
||||
*FString::SanitizeFloat(CompileWorkerSeconds),
|
||||
+ CompileWorkerThreadId,
|
||||
*FString::SanitizeFloat(GateElapsedMilliseconds),
|
||||
static_cast<unsigned long long>(IndependentSubmissions),
|
||||
+ ActiveCoreRegionWastedPrefetchTiles,
|
||||
static_cast<unsigned long long>(DependentDeclines),
|
||||
GVoxelForgeCoreRegionConcurrentAdmission != 0 ? 1 : 0));
|
||||
}
|
||||
@@ -12388,8 +12691,9 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
{
|
||||
FScopeLock RetainedLock(&State->RetainedTilesLock);
|
||||
bRetainedSetMatchesDesired =
|
||||
- DesiredSorted.Num() == static_cast<int32>(State->RetainedTiles.size());
|
||||
- for (const FVoxelTileKey& Key : DesiredSorted)
|
||||
+ CoreRegionRetainedDesiredTiles.Num()
|
||||
+ == static_cast<int32>(State->RetainedTiles.size());
|
||||
+ for (const FVoxelTileKey& Key : CoreRegionRetainedDesiredTiles)
|
||||
{
|
||||
const VoxelForgeCore::TileKey CoreKey{
|
||||
{Key.Coord.X, Key.Coord.Y, Key.Coord.Z}, Key.Level};
|
||||
@@ -12431,7 +12735,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
{
|
||||
PendingCoreRegionTileSet.Add(Key);
|
||||
}
|
||||
- PendingCoreRetainedTiles = DesiredSorted;
|
||||
+ PendingCoreRetainedTiles = CoreRegionRetainedDesiredTiles;
|
||||
PendingCoreRegionRequestCenter = DesiredEpochCenterChunk;
|
||||
bCoreRegionCompilePending = true;
|
||||
StartPendingCoreRegionCompile();
|
||||
@@ -12584,6 +12888,8 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
&& bWalkableRingReadyForReserve;
|
||||
int32 SchedulerSubmittedByLevel[9] = {};
|
||||
int32 SchedulerLoadAttemptsByLevel[9] = {};
|
||||
+ int32 SchedulerRegionCompileDeclinesByLevel[9] = {};
|
||||
+ int32 SchedulerFarParentDeclinesByLevel[9] = {};
|
||||
int32 SchedulerLevelCapReturns[9] = {};
|
||||
int32 SchedulerLevelCapBreaks[9] = {};
|
||||
int32 SchedulerCriticalFullCapBreaks = 0;
|
||||
@@ -12951,7 +13257,21 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
if(CoreState.IsValid()
|
||||
&&CoreState->bFarWorldRequired.load(std::memory_order_acquire)
|
||||
&&!CoreState->bFarWorldReady.load(std::memory_order_acquire))
|
||||
+ {
|
||||
+ if (bTraceStreamingScheduler && Tile.Level >= 0 && Tile.Level < 9)
|
||||
+ {
|
||||
+ ++SchedulerFarParentDeclinesByLevel[Tile.Level];
|
||||
+ }
|
||||
return;
|
||||
+ }
|
||||
+ }
|
||||
+ if (bTraceStreamingScheduler && bUseVoxelForgeCore && bCoreRegionCompilePending
|
||||
+ && GVoxelForgeCoreRegionConcurrentAdmission != 0
|
||||
+ && (PendingCoreRegionTileSet.Contains(Tile)
|
||||
+ || CoreRegionCompileTilesInFlight.Contains(Tile))
|
||||
+ && Tile.Level >= 0 && Tile.Level < 9)
|
||||
+ {
|
||||
+ ++SchedulerRegionCompileDeclinesByLevel[Tile.Level];
|
||||
}
|
||||
if (Tile.Level >= 0 && Tile.Level < 9)
|
||||
{
|
||||
@@ -13661,10 +13981,37 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
const double BusySeconds = FPlatformTime::ToSeconds64(WorkerCyclesDelta);
|
||||
const double SchedulerUpdateMilliseconds = (SchedulerTraceNow
|
||||
- StreamingUpdateStartSeconds) * 1000.0;
|
||||
+ int32 CriticalMissingTiles = 0;
|
||||
+ for (const FVoxelTileKey& Tile : CriticalDesiredTiles)
|
||||
+ {
|
||||
+ if (!LoadedTiles.Contains(Tile) && !CollisionNotRequiredTiles.Contains(Tile))
|
||||
+ {
|
||||
+ ++CriticalMissingTiles;
|
||||
+ }
|
||||
+ }
|
||||
+ int32 CollisionGateMissingTiles = 0;
|
||||
+ for (const FVoxelTileKey& Tile : CollisionGateDesiredTiles)
|
||||
+ {
|
||||
+ if (!LoadedTiles.Contains(Tile) && !CollisionNotRequiredTiles.Contains(Tile))
|
||||
+ {
|
||||
+ ++CollisionGateMissingTiles;
|
||||
+ }
|
||||
+ }
|
||||
+ const FIntVector DesiredCenterLag = DesiredEpochCenterChunk - CenterChunk;
|
||||
+ const TSharedPtr<FVoxelForgeCoreRuntimeState, ESPMode::ThreadSafe> TraceCoreState =
|
||||
+ VoxelForgeCoreState;
|
||||
+ const bool bFarWorldRequiredForTrace = TraceCoreState.IsValid()
|
||||
+ && TraceCoreState->bFarWorldRequired.load(std::memory_order_relaxed);
|
||||
+ const bool bFarWorldReadyForTrace = TraceCoreState.IsValid()
|
||||
+ && TraceCoreState->bFarWorldReady.load(std::memory_order_relaxed);
|
||||
VoxelForgeStartupTrace::RecordEvent(TEXT("scheduler_admission"), FString::Printf(
|
||||
TEXT("\"frame\":%llu,\"center\":[%d,%d,%d],\"desired_rebuilt\":%d,"
|
||||
"\"submission_loop_ran\":%d,\"all_chunks_loaded\":%d,"
|
||||
"\"walkable_ready\":%d,\"moving\":%d,"
|
||||
+ "\"pawn_collision_gate_engaged\":%d,\"critical_tiles_ready\":%d,"
|
||||
+ "\"critical_missing\":%d,\"collision_gate_missing\":%d,"
|
||||
+ "\"desired_center_lag\":[%d,%d,%d],\"desired_center_current\":%d,"
|
||||
+ "\"far_world_required\":%d,\"far_world_ready\":%d,"
|
||||
"\"core_region_compile_pending\":%d,\"core_ready\":%d,\"coarse_first\":%d,"
|
||||
"\"dynamic_near_reserve\":%d,\"legacy_pending_result_cap\":%d,"
|
||||
"\"core_region_concurrent_admission\":%d,"
|
||||
@@ -13685,6 +14032,8 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
"\"level_candidate_scan_ms\":%s,\"passage_score_ms\":%s,"
|
||||
"\"level_sort_ms\":%s,\"level_admit_ms\":%s,"
|
||||
"\"desired_missing_by_lod\":%s,\"loaded_by_lod\":%s,\"pending_by_lod\":%s,"
|
||||
+ "\"region_compile_declines_by_lod\":%s,"
|
||||
+ "\"far_parent_declines_by_lod\":%s,"
|
||||
"\"submitted_by_lod\":%s,\"load_attempts_by_lod\":%s,"
|
||||
"\"admission_cap_by_lod\":%s,\"declined_rule_by_lod\":%s,"
|
||||
"\"level_cap_returns_by_lod\":%s,\"level_cap_breaks_by_lod\":%s,"
|
||||
@@ -13695,6 +14044,11 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
static_cast<unsigned long long>(GFrameCounter), CenterChunk.X, CenterChunk.Y, CenterChunk.Z,
|
||||
bDesiredRebuiltThisUpdate ? 1 : 0, bSubmissionLoopRan ? 1 : 0, bAllChunksLoaded ? 1 : 0,
|
||||
bWalkableRingReadyForReserve ? 1 : 0, bPawnMoving ? 1 : 0,
|
||||
+ bPawnCollisionGateEngaged ? 1 : 0, CriticalMissingTiles == 0 ? 1 : 0,
|
||||
+ CriticalMissingTiles, CollisionGateMissingTiles,
|
||||
+ DesiredCenterLag.X, DesiredCenterLag.Y, DesiredCenterLag.Z,
|
||||
+ DesiredCenterLag == FIntVector::ZeroValue ? 1 : 0,
|
||||
+ bFarWorldRequiredForTrace ? 1 : 0, bFarWorldReadyForTrace ? 1 : 0,
|
||||
bCoreRegionCompilePending ? 1 : 0, IsVoxelForgeCoreReady() ? 1 : 0,
|
||||
GVoxelForgeStreamingCoarseFirst, GVoxelForgeStreamingDynamicNearReserve,
|
||||
GVoxelForgeStreamingLegacyPendingResultCap,
|
||||
@@ -13721,7 +14075,8 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
*FString::SanitizeFloat(LevelSortMilliseconds),
|
||||
*FString::SanitizeFloat(LevelAdmissionMilliseconds),
|
||||
*IntArrayJson(DesiredMissingByLevel), *IntArrayJson(LoadedByLevel),
|
||||
- *IntArrayJson(PendingByLevel), *IntArrayJson(SchedulerSubmittedByLevel),
|
||||
+ *IntArrayJson(PendingByLevel), *IntArrayJson(SchedulerRegionCompileDeclinesByLevel),
|
||||
+ *IntArrayJson(SchedulerFarParentDeclinesByLevel), *IntArrayJson(SchedulerSubmittedByLevel),
|
||||
*IntArrayJson(SchedulerLoadAttemptsByLevel), *IntArrayJson(AdmissionCapByLevel),
|
||||
*IntArrayJson(DeclinedRuleByLevel), *IntArrayJson(SchedulerLevelCapReturns),
|
||||
*IntArrayJson(SchedulerLevelCapBreaks), FullCapDeclinedTiles, ReserveDeclinedTiles,
|
||||
diff --git a/Source/VoxelForge/Public/VoxelWorld.h b/Source/VoxelForge/Public/VoxelWorld.h
|
||||
index b9fa34c..5979f29 100644
|
||||
--- a/Source/VoxelForge/Public/VoxelWorld.h
|
||||
+++ b/Source/VoxelForge/Public/VoxelWorld.h
|
||||
@@ -207,6 +207,28 @@ struct FChunkResult
|
||||
TArray<FVector> SurfaceWitnesses;
|
||||
};
|
||||
|
||||
+/** Worker-to-game-thread completion for immutable Core tile-region publication. */
|
||||
+struct FCoreRegionCompileResult
|
||||
+{
|
||||
+ TSharedPtr<FVoxelForgeCoreRuntimeState, ESPMode::ThreadSafe> RuntimeState;
|
||||
+ TArray<FVoxelTileKey> RetainedTiles;
|
||||
+ TArray<FVoxelTileKey> PrefetchTiles;
|
||||
+ FString Error;
|
||||
+ FIntVector Center = FIntVector::ZeroValue;
|
||||
+ uint32 GenerationEpoch = 0;
|
||||
+ uint32 DesiredEpoch = 0;
|
||||
+ uint64 WorkerStartCycles = 0;
|
||||
+ uint64 WorkerEndCycles = 0;
|
||||
+ uint32 WorkerThreadId = 0;
|
||||
+ int32 CompileSerial = 0;
|
||||
+ int32 RequestSerial = 0;
|
||||
+ int32 CompiledTileCount = 0;
|
||||
+ double WorkerSeconds = 0.0;
|
||||
+ double CoreWallSeconds = 0.0;
|
||||
+ bool bSucceeded = false;
|
||||
+ bool bAborted = false;
|
||||
+};
|
||||
+
|
||||
/** One worker-produced mesh identity emitted only by the explicit parity harness. */
|
||||
struct FVoxelTileHashDumpRecord
|
||||
{
|
||||
@@ -798,6 +820,11 @@ private:
|
||||
TArray<FVoxelTileKey> PendingCoreRegionTiles;
|
||||
TSet<FVoxelTileKey> PendingCoreRegionTileSet;
|
||||
TArray<FVoxelTileKey> PendingCoreRetainedTiles;
|
||||
+ TArray<FVoxelTileKey> CoreRegionRetainedDesiredTiles;
|
||||
+ TArray<FVoxelTileKey> CoreRegionLookaheadTiles;
|
||||
+ FIntVector LastCoreRegionLookaheadCenter = FIntVector(MAX_int32, MAX_int32, MAX_int32);
|
||||
+ FIntVector LastCoreRegionLookaheadStep = FIntVector::ZeroValue;
|
||||
+ uint32 LastCoreRegionLookaheadGenerationEpoch = 0;
|
||||
TSet<FVoxelTileKey> CoreRegionCompileTilesInFlight;
|
||||
FIntVector LastCoreRegionRequestCenter = FIntVector(MAX_int32, MAX_int32, MAX_int32);
|
||||
FIntVector PendingCoreRegionRequestCenter = FIntVector::ZeroValue;
|
||||
@@ -810,6 +837,7 @@ private:
|
||||
uint64 CoreRegionDependentDeclines = 0;
|
||||
int32 CoreRegionCompileRequestSerial = 0;
|
||||
int32 ActiveCoreRegionCompileRequestSerial = 0;
|
||||
+ int32 ActiveCoreRegionWastedPrefetchTiles = 0;
|
||||
|
||||
public:
|
||||
|
||||
@@ -1139,6 +1167,7 @@ public:
|
||||
TQueue<FChunkResult, EQueueMode::Mpsc> EditedProcessQueue;
|
||||
TQueue<FChunkResult, EQueueMode::Mpsc> NearProcessQueue;
|
||||
TQueue<FChunkResult, EQueueMode::Mpsc> ProcessQueue;
|
||||
+ TQueue<FCoreRegionCompileResult, EQueueMode::Mpsc> CoreRegionCompileResults;
|
||||
// The cap counts completed MPSC results plus admitted tile tasks, so a slow game-thread apply
|
||||
// cannot accumulate an unbounded payload queue while completed work never consumes worker slots.
|
||||
// Le plafond compte les resultats MPSC et les taches admises; un apply lent ne peut donc pas
|
||||
@@ -1362,6 +1391,9 @@ public:
|
||||
float HeadlessStreamingTestSpeedCmPerSecond = 0.0f;
|
||||
float HeadlessStreamingTestStartSeconds = 0.0f;
|
||||
float HeadlessStreamingTestMoveDurationSeconds = 0.0f;
|
||||
+ float HeadlessStreamingTestTurnAfterSeconds = 0.0f;
|
||||
+ bool bHeadlessStreamingTestTurnDone = false;
|
||||
+ FVector HeadlessStreamingTestTurnDirection = FVector::YAxisVector;
|
||||
float HeadlessStreamingTestExitSeconds = 0.0f;
|
||||
float HeadlessStreamingTestTeleportAtSeconds = 0.0f;
|
||||
float HeadlessStreamingTestTeleportDistanceCm = 0.0f;
|
||||
Reference in new issue
Block a user