streaming: split dense passage-junction grid fills; heavy-first order + far-parent deferral in Unreal (L85)

Tiles with passage-overlap score >= 9 split grid fill over up to 4 helper lanes (disjoint rows,
same per-sample order, join before MC) when workers are idle. FI junction tile fill 173 -> 99 ms;
estimated serial floor ~0.55-0.60 s. Readiness essentially unchanged (FI/TN LOD0-2 ~2.15 s @W12):
the limit is now aggregate work, not single tiles.
Unreal: LOD0-2 ordered by passage overlap before travel/distance (L80 modes kept); far parents
built after LOD0-1, LOD2 gated on them; helper budget via ParallelFor. Unreal null-RHI FI
arrival: LOD0-2 resident at 3.65 s.
All tile hashes identical to L84 (four whole views). Fast tier PASS; Linux UE host builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-28 02:02:32 +01:00
1 parent 0a98da6fd0
commit 06f558dc91
17 files changed
+1561 -54

No files matched your search

+143 -11
View File
@@ -163,6 +163,10 @@ public:
std::atomic<bool> bSnapshotReady{false};
std::atomic<bool> bReady{false};
std::atomic<bool> bInitialFailed{false};
std::atomic<bool> bFarWorldRequired{false};
std::atomic<bool> bFarWorldReady{false};
std::atomic<bool> bFarWorldBuildInFlight{false};
std::atomic<bool> bFarWorldBuildFailed{false};
std::atomic<bool> bRegionCompileInFlight{false};
std::atomic<bool> bRegionCompileFinished{false};
std::atomic<bool> bRegionCompileSucceeded{false};
@@ -7694,6 +7698,10 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld(FString& OutFailureReason)
MakeShared<FVoxelForgeCoreRuntimeState, ESPMode::ThreadSafe>();
State->World = std::make_unique<VoxelForgeCore::World>(std::move(*Snapshot));
State->FullResolutionLodCount = State->World->configuration().fullResolutionLodCount;
const bool bFarWorldRequired=State->World->configuration().maxLod
>=State->FullResolutionLodCount;
State->bFarWorldRequired.store(bFarWorldRequired,std::memory_order_release);
State->bFarWorldReady.store(!bFarWorldRequired,std::memory_order_release);
FVector CameraVoxels = FVector::ZeroVector;
FVector InitialPlayerPosition = FVector::ZeroVector;
@@ -7766,7 +7774,7 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld(FString& OutFailureReason)
Request.clipMaxVoxel = ClipMax;
Request.workerLimit = static_cast<std::uint32_t>(FMath::Clamp(GetMaxConcurrentTasks(), 1, 16));
Request.detail = VoxelForgeCore::DetailVariant::coherent;
Request.includeFarWorld = true;
Request.includeFarWorld = false;
VoxelForgeCoreState = State;
State->bSnapshotReady.store(true, std::memory_order_release);
@@ -7802,6 +7810,9 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld(FString& OutFailureReason)
if (State->World)
{
State->World->retainTileRegions(InitialDesired);
if(State->bFarWorldRequired.load(std::memory_order_acquire))
State->bFarWorldReady.store(State->World->hasFarWorld(),
std::memory_order_release);
FScopeLock RetainedLock(&State->RetainedTilesLock);
for (const VoxelForgeCore::TileKey& Key : InitialDesired)
{
@@ -12365,6 +12376,15 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
const auto SubmitTileWithTraceCount = [&](const FVoxelTileKey& Tile,
EVoxelTileWorkPriority Priority)
{
if(bUseVoxelForgeCore&&Tile.Level>=2)
{
const TSharedPtr<FVoxelForgeCoreRuntimeState,ESPMode::ThreadSafe> CoreState=
VoxelForgeCoreState;
if(CoreState.IsValid()
&&CoreState->bFarWorldRequired.load(std::memory_order_acquire)
&&!CoreState->bFarWorldReady.load(std::memory_order_acquire))
return;
}
if (Tile.Level >= 0 && Tile.Level < 9)
{
++SchedulerLoadAttemptsByLevel[Tile.Level];
@@ -12418,6 +12438,8 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
{
MaxDesiredLevel = FMath::Max(MaxDesiredLevel, T.Level);
}
const TSharedPtr<FVoxelForgeCoreRuntimeState,ESPMode::ThreadSafe> CoreState=
VoxelForgeCoreState;
auto SubmitLevel = [&](int32 Level, EVoxelTileWorkPriority Priority, int32 AdmissionCap,
bool bPreferTravelDirection)
@@ -12437,20 +12459,50 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
}
// Sort only outstanding keys; re-sorting loaded or pending tiles on every tick inflated game-thread work.
// Trier uniquement les cles restantes ; re-trier chaque tick les tuiles chargees ou en attente alourdissait le thread de jeu.
if (bPreferTravelDirection && LevelTiles.Num() > 1)
TMap<FVoxelTileKey,uint32> PassageScores;
if(Level<=2&&CoreState.IsValid()&&CoreState->bReady.load(std::memory_order_acquire))
{
FVector Heading = GetActorTransform().InverseTransformVectorNoScale(PlayerHeading);
if (Heading.IsNearlyZero() && IsValid(PlayerPawn))
FReadScopeLock WorldReadLock(CoreState->WorldLock);
if(CoreState->World)
{
Heading = GetActorTransform().InverseTransformVectorNoScale(
PlayerPawn->GetActorForwardVector());
PassageScores.Reserve(LevelTiles.Num());
for(const FVoxelTileKey& Key:LevelTiles)
{
const VoxelForgeCore::TileKey CoreKey{
{Key.Coord.X,Key.Coord.Y,Key.Coord.Z},Key.Level};
PassageScores.Add(Key,CoreState->World->passageOverlapScore(CoreKey));
}
}
Heading = Heading.GetSafeNormal();
const FVector PlayerLocalCm = WorldToLocalCm(CenterPosition);
const double TileSizeCm = static_cast<double>(VF_ClipLevelExtentVoxels(Level))
* GetActiveVoxelSizeCm();
LevelTiles.Sort([&](const FVoxelTileKey& A, const FVoxelTileKey& B)
}
const bool bSortForPassagePriority=Level<=2&&!PassageScores.IsEmpty();
if ((bPreferTravelDirection||bSortForPassagePriority)&&LevelTiles.Num()>1)
{
FVector Heading=FVector::ZeroVector;
FVector PlayerLocalCm=FVector::ZeroVector;
double TileSizeCm=0.0;
if(bPreferTravelDirection)
{
Heading=GetActorTransform().InverseTransformVectorNoScale(PlayerHeading);
if(Heading.IsNearlyZero()&&IsValid(PlayerPawn))
{
Heading=GetActorTransform().InverseTransformVectorNoScale(
PlayerPawn->GetActorForwardVector());
}
Heading=Heading.GetSafeNormal();
PlayerLocalCm=WorldToLocalCm(CenterPosition);
TileSizeCm=static_cast<double>(VF_ClipLevelExtentVoxels(Level))
*GetActiveVoxelSizeCm();
}
std::stable_sort(LevelTiles.GetData(),LevelTiles.GetData()+LevelTiles.Num(),
[&](const FVoxelTileKey& A,const FVoxelTileKey& B)
{
if(bSortForPassagePriority)
{
const uint32 ScoreA=PassageScores.FindRef(A);
const uint32 ScoreB=PassageScores.FindRef(B);
if(ScoreA!=ScoreB)return ScoreA>ScoreB;
}
if(!bPreferTravelDirection)return false;
const FVector CenterA(
(static_cast<double>(A.Coord.X) + 0.5) * TileSizeCm,
(static_cast<double>(A.Coord.Y) + 0.5) * TileSizeCm,
@@ -12509,6 +12561,64 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
}
return true;
};
const auto AreEarlyLODTilesReady = [this]()
{
for(const FVoxelTileKey& Tile:DesiredSorted)
{
if(Tile.Level<=1&&!LoadedTiles.Contains(Tile)
&&!CollisionNotRequiredTiles.Contains(Tile))
return false;
}
return true;
};
if(bUseVoxelForgeCore&&CoreState.IsValid()
&&CoreState->bFarWorldRequired.load(std::memory_order_acquire)
&&CoreState->bReady.load(std::memory_order_acquire)
&&!CoreState->bFarWorldReady.load(std::memory_order_acquire)
&&!CoreState->bFarWorldBuildFailed.load(std::memory_order_acquire)
&&!CoreState->bFarWorldBuildInFlight.load(std::memory_order_acquire)
&&AreEarlyLODTilesReady())
{
bool bExpected=false;
if(CoreState->bFarWorldBuildInFlight.compare_exchange_strong(
bExpected,true,std::memory_order_acq_rel))
{
ActiveTaskCount.fetch_add(1,std::memory_order_relaxed);
UE::Tasks::Launch(TEXT("VoxelForgeCoreFarWorldBuild"),[this,CoreState]()
{
struct FTaskGuard
{
std::atomic<int32>& Counter;
~FTaskGuard(){Counter.fetch_sub(1,std::memory_order_release);}
} Guard{ActiveTaskCount};
if(bShuttingDown.load(std::memory_order_acquire)
||CoreState->bShuttingDown.load(std::memory_order_acquire))
{
CoreState->bFarWorldBuildInFlight.store(false,std::memory_order_release);
return;
}
bool bBuilt=false;
{
FWriteScopeLock WorldWriteLock(CoreState->WorldLock);
if(CoreState->World)bBuilt=CoreState->World->buildFarWorld();
}
if(bBuilt)
{
CoreState->bFarWorldReady.store(true,std::memory_order_release);
CoreState->bRebuildDesired.store(true,std::memory_order_release);
UE_LOG(LogTemp,Display,
TEXT("[VoxelForgeCoreRuntime] deferred_far_world=ready"));
}
else
{
CoreState->bFarWorldBuildFailed.store(true,std::memory_order_release);
UE_LOG(LogTemp,Error,
TEXT("[VoxelForgeCoreRuntime] deferred far-world build failed"));
}
CoreState->bFarWorldBuildInFlight.store(false,std::memory_order_release);
},UE::Tasks::ETaskPriority::BackgroundNormal);
}
}
// Policy 0 reconstructs L77. Policy 3 enters that order only after sustained fast travel.
const bool bPolicy3FastTravel = SchedulerPolicy == 3 && bSpeedAwareFastMode;
@@ -13306,6 +13416,8 @@ void AVoxelWorld::LoadCoreTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority
if (!State.IsValid()||State->bInitialFailed.load(std::memory_order_acquire)
||State->bRegionCompileFailed.load(std::memory_order_acquire)
||State->bShuttingDown.load(std::memory_order_acquire))return;
if(Tile.Level>=2&&State->bFarWorldRequired.load(std::memory_order_acquire)
&&!State->bFarWorldReady.load(std::memory_order_acquire))return;
const bool bShapeReady=State->bReady.load(std::memory_order_acquire);
if(!bShapeReady)
{
@@ -13502,6 +13614,26 @@ void AVoxelWorld::LoadCoreTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority
Options.hashDensity = bCaptureHashes;
Options.legacyDirectMappedColumnCache=bLegacyDirectMappedColumnCache;
Options.memoizeMcGradients=bMemoizeCoreMCGradients;
if(Tile.Level==0)
{
const int32 ActiveStreamingTasks=GetActiveGenerationTaskCount();
const int32 WorkerBudget=FMath::Clamp(
GetMaxConcurrentStreamingTasks(),1,12);
const int32 AvailableGridLanes=FMath::Max(0,
WorkerBudget-FMath::Max(0,ActiveStreamingTasks-1));
const int32 GridFillWorkers=FMath::Min(4,AvailableGridLanes);
if(GridFillWorkers>1)
{
FReadScopeLock WorldReadLock(State->WorldLock);
// Only split dense junction overlaps; lower scores are too common to repay
// the helper-lane and per-thread context setup costs.
if(State->World&&State->World->passageOverlapScore(CoreKey)>=9)
{
Options.gridFillScheduler=&State->Scheduler;
Options.gridFillWorkerLimit=static_cast<std::uint32_t>(GridFillWorkers);
}
}
}
std::string CoreError;
std::optional<VoxelForgeCore::TileMesh> CoreMesh;
{