streaming: bounded early admission - partial level sort, apply preflight, sorted transition index (L169, opt-in)

With StreamingFastTravelWaitForAllCriticalTiles=0 and ApplyBudget=1 ms (test values; defaults stay 1 and 0), 7 pairs
vs ca0bf76: near 43.3 -> 56.1%, forward 15.5 -> 17.9%, far holes 1.8 -> 0.0%, p99 7.06 -> 6.52 ms, worst 12.2 ->
11.8 ms, every run p99 <= 7.4 and worst <= 13.8 ms; arrival 1.671 -> 1.683 s (slower in 7/7) so not promoted.
Source/: VoxelWorld.cpp, VoxelWorld.h.

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-11-02 16:45:20 +01:00
1 parent ca0bf767ca
commit 786ecc4cdf
4 files changed
+664 -15

No files matched your search

+167 -14
View File
@@ -7191,7 +7191,7 @@ void AVoxelWorld::LogReadyTransitionVisibilityMeasure()
void AVoxelWorld::Tick(float DeltaTime)
{
const bool bTraceFrameTiming = VoxelForgeStartupTrace::IsActive();
const uint64 TickStartCycles = bTraceFrameTiming ? FPlatformTime::Cycles64() : 0;
const uint64 TickStartCycles = FPlatformTime::Cycles64();
uint64 CollisionGateCycles = 0;
uint64 UpdateChunksCycles = 0;
uint64 PendingDrainCycles = 0;
@@ -7418,7 +7418,7 @@ void AVoxelWorld::Tick(float DeltaTime)
}
const uint64 PendingDrainStartCycles = bTraceFrameTiming
? FPlatformTime::Cycles64() : 0;
ProcessPendingChunks();
ProcessPendingChunks(TickStartCycles);
if (bTraceFrameTiming)
{
PendingDrainCycles += FPlatformTime::Cycles64() - PendingDrainStartCycles;
@@ -9899,7 +9899,7 @@ void AVoxelWorld::HandleTileCollisionCookComplete(
}
}
void AVoxelWorld::ProcessPendingChunks()
void AVoxelWorld::ProcessPendingChunks(uint64 TickStartCycles)
{
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_ProcessPending);
// This runs on the game thread, called from Tick.
@@ -10105,6 +10105,7 @@ void AVoxelWorld::ProcessPendingChunks()
GetStreamingResultBacklogCount(), GVoxelForgeStreamingResultBacklogCap));
};
FChunkResult DequeuedChunk;
TArray<FChunkResult> DeferredApplyResults;
const auto DequeueNextResult = [&]() -> bool
{
bDequeuedSupplementalFar = false;
@@ -10234,8 +10235,6 @@ void AVoxelWorld::ProcessPendingChunks()
const bool bRequestObsolete = DequeuedChunk.bObsolete
|| (Cancellation.IsValid()
&& Cancellation->bObsolete.load(std::memory_order_acquire));
PendingTileCancellation.Remove(DequeuedChunk.Tile);
PendingTiles.Remove(DequeuedChunk.Tile);
// A worker can observe shutdown after entering the mesher. Do not turn that partial
// result into an all-air tile: it must remain eligible for a future generation epoch.
@@ -10244,6 +10243,8 @@ void AVoxelWorld::ProcessPendingChunks()
// if the tile leaves and re-enters the desired set before the queue is drained.
if (DequeuedChunk.bAborted || bRequestObsolete)
{
PendingTileCancellation.Remove(DequeuedChunk.Tile);
PendingTiles.Remove(DequeuedChunk.Tile);
if (VoxelForgeStartupTrace::IsActive())
{
VoxelForgeStartupTrace::RecordEvent(TEXT("tile_discard"), FString::Printf(
@@ -10264,6 +10265,55 @@ void AVoxelWorld::ProcessPendingChunks()
continue;
}
// Estimate visible mesh submission cost before entering RMC's atomic CreateSectionGroup
// call. If this result would consume the remaining 11 ms tick target plus a 0.5 ms tail
// reserve, hold it for a later frame. The prediction is calibrated from L169 tile sizes:
// 2.2 ms fixed headroom + per-triangle and per-vertex work. Keep PendingTiles and its
// cancellation token alive while queued so the scheduler cannot submit a duplicate.
const bool bPreflightVisibleMesh = bFastTravelApplyBudgetActive
&& DequeuedChunk.Epoch == GenerationEpoch
&& IsDesired(DequeuedChunk.Tile)
&& !DequeuedChunk.bEmpty
&& DequeuedChunk.Streams.IsValid()
&& DequeuedChunk.NumTriangles > 0;
if (bPreflightVisibleMesh)
{
const uint64 PreflightCycles = FPlatformTime::Cycles64();
const double TickElapsedMilliseconds = TickStartCycles != 0
&& PreflightCycles >= TickStartCycles
? FPlatformTime::ToSeconds64(PreflightCycles - TickStartCycles) * 1000.0
: 0.0;
const double EstimatedApplyMilliseconds = 2.2
+ static_cast<double>(FMath::Max(0, DequeuedChunk.NumTriangles)) * 0.00012
+ static_cast<double>(FMath::Max(0, DequeuedChunk.NumVertices)) * 0.00003;
constexpr double TickApplyTargetMilliseconds = 11.0;
constexpr double TickApplyTailReserveMilliseconds = 0.5;
const double RemainingApplyMilliseconds = FMath::Max(
0.0, TickApplyTargetMilliseconds - TickApplyTailReserveMilliseconds
- TickElapsedMilliseconds);
if (EstimatedApplyMilliseconds > RemainingApplyMilliseconds)
{
if (VoxelForgeStartupTrace::IsActive())
{
VoxelForgeStartupTrace::RecordEvent(TEXT("tile_apply_deferred"), FString::Printf(
TEXT("\"frame\":%llu,\"tile\":[%d,%d,%d],\"level\":%d,"
"\"triangles\":%d,\"vertices\":%d,\"estimated_apply_ms\":%.3f,"
"\"elapsed_tick_ms\":%.3f,\"remaining_apply_ms\":%.3f"),
static_cast<unsigned long long>(GFrameCounter),
DequeuedChunk.Tile.Coord.X, DequeuedChunk.Tile.Coord.Y,
DequeuedChunk.Tile.Coord.Z, DequeuedChunk.Tile.Level,
DequeuedChunk.NumTriangles, DequeuedChunk.NumVertices,
EstimatedApplyMilliseconds, TickElapsedMilliseconds,
RemainingApplyMilliseconds));
}
DeferredApplyResults.Add(MoveTemp(DequeuedChunk));
continue;
}
}
PendingTileCancellation.Remove(DequeuedChunk.Tile);
PendingTiles.Remove(DequeuedChunk.Tile);
// ApplyTileResult does epoch check, capture ingest, empty-release / mesh submission, and
// only then records the tile as loaded. Only a real (visible) upload counts against the
// per-frame budget — stale/empty drains are free.
@@ -10323,6 +10373,11 @@ void AVoxelWorld::ProcessPendingChunks()
}
}
for (FChunkResult& DeferredResult : DeferredApplyResults)
{
EnqueueStreamingResult(MoveTemp(DeferredResult));
}
if (bProfileApply && ResultsDrained > 0)
{
UE_LOG(LogTemp, Display,
@@ -10827,7 +10882,11 @@ void AVoxelWorld::HideOverlappingDesiredVisuals(const FVoxelTileKey& RetiredTile
TArray<FVoxelTileKey, TInlineAllocator<32>> OverlappingDesired;
if (const TArray<FVoxelTileKey>* Descendants = DesiredTransitionDescendants.Find(RetiredTile))
{
OverlappingDesired.Append(*Descendants);
OverlappingDesired.Reserve(OverlappingDesired.Num() + Descendants->Num());
for (const FVoxelTileKey& Descendant : *Descendants)
{
OverlappingDesired.Add(Descendant);
}
}
for (int32 Level = RetiredTile.Level + 1; Level <= DesiredTransitionMaxLevel; ++Level)
{
@@ -11064,9 +11123,18 @@ static FORCEINLINE void VF_OuterShell(const UVoxelSettings* Settings, int32 R, i
VoxelClipmapDesiredTiles::OuterShell(Parameters, R, MaxLevel, OutLevel, OutRadius);
}
static bool VF_TransitionIndexKeyLess(const FVoxelTileKey& A, const FVoxelTileKey& B)
{
if (A.Level != B.Level) return A.Level < B.Level;
if (A.Coord.X != B.Coord.X) return A.Coord.X < B.Coord.X;
if (A.Coord.Y != B.Coord.Y) return A.Coord.Y < B.Coord.Y;
return A.Coord.Z < B.Coord.Z;
}
void AVoxelWorld::BuildDesiredTransitionIndex()
{
DesiredTransitionDescendants.Reset();
DesiredTransitionDescendants.Reserve(DesiredSorted.Num());
DesiredTransitionMaxLevel = 0;
for (const FVoxelTileKey& Key : DesiredSorted)
{
@@ -11079,7 +11147,9 @@ void AVoxelWorld::BuildDesiredTransitionIndex()
constexpr int32 MaxSupportedTileLevel = 8;
const int32 DescendantIndexMaxLevel = FMath::Max(
DesiredTransitionMaxLevel, MaxSupportedTileLevel);
for (const FVoxelTileKey& Key : DesiredSorted)
TArray<FVoxelTileKey> TransitionIndexBuildKeys = DesiredSorted;
TransitionIndexBuildKeys.Sort(VF_TransitionIndexKeyLess);
for (const FVoxelTileKey& Key : TransitionIndexBuildKeys)
{
for (int32 Level = Key.Level + 1; Level <= DescendantIndexMaxLevel; ++Level)
{
@@ -11087,7 +11157,12 @@ void AVoxelWorld::BuildDesiredTransitionIndex()
VF_AppendTouchingAncestors(Key, Level, TouchingAncestors);
for (const FVoxelTileKey& Ancestor : TouchingAncestors)
{
DesiredTransitionDescendants.FindOrAdd(Ancestor).Add(Key);
TArray<FVoxelTileKey>& Descendants =
DesiredTransitionDescendants.FindOrAdd(Ancestor);
if (Descendants.IsEmpty() || Descendants.Last() != Key)
{
Descendants.Add(Key);
}
}
}
}
@@ -11187,6 +11262,10 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
double OverlayMaterializationMilliseconds = 0.0;
double PrioritySortMilliseconds = 0.0;
double CriticalAdmissionOrderMilliseconds = 0.0;
double DesiredCollisionAndStampMilliseconds = 0.0;
double DesiredTransitionIndexMilliseconds = 0.0;
double DesiredRetainedRegionMilliseconds = 0.0;
double DesiredRegionReconcileMilliseconds = 0.0;
int32 IncrementalChangedLod4PlusKeyCount = 0;
int32 IncrementalFallbackReason = 0;
bool bCoreDesiredCacheHit = false;
@@ -11954,6 +12033,8 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
(FPlatformTime::Seconds() - PrioritySortStartSeconds) * 1000.0;
}
const double DesiredCollisionAndStampStartSeconds = bTraceDesiredBreakdown
? FPlatformTime::Seconds() : 0.0;
RestoreKnownEmptyCollisionTiles();
ReconcileCollisionDesiredTiles();
for (auto It = DesiredStamped.CreateIterator(); It; ++It)
@@ -11965,6 +12046,11 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
}
}
CancelObsoleteTileWork();
if (DesiredCollisionAndStampStartSeconds != 0.0)
{
DesiredCollisionAndStampMilliseconds =
(FPlatformTime::Seconds() - DesiredCollisionAndStampStartSeconds) * 1000.0;
}
const double CriticalAdmissionOrderStartSeconds = bTraceDesiredBreakdown
? FPlatformTime::Seconds() : 0.0;
@@ -12135,6 +12221,8 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
checkf(false, TEXT("VoxelForge incremental desired priority-order equality failure"));
}
}
const double DesiredTransitionIndexStartSeconds = bTraceDesiredBreakdown
? FPlatformTime::Seconds() : 0.0;
if (bIncrementalDesiredSet && bHadPreviousDesiredSet)
{
constexpr int32 MaxSupportedTileLevel = 8;
@@ -12143,6 +12231,27 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
const auto ApplyTransitionDelta = [this, TransitionIndexMaxLevel](
const FVoxelTileKey& Key, bool bAdd)
{
const auto LowerBound = [](const TArray<FVoxelTileKey>& Descendants,
const FVoxelTileKey& SearchKey)
{
int32 First = 0;
int32 Count = Descendants.Num();
while (Count > 0)
{
const int32 Step = Count / 2;
const int32 Index = First + Step;
if (VF_TransitionIndexKeyLess(Descendants[Index], SearchKey))
{
First = Index + 1;
Count -= Step + 1;
}
else
{
Count = Step;
}
}
return First;
};
for (int32 Level = Key.Level + 1; Level <= TransitionIndexMaxLevel; ++Level)
{
TArray<FVoxelTileKey, TInlineAllocator<8>> TouchingAncestors;
@@ -12153,12 +12262,21 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
{
TArray<FVoxelTileKey>& Descendants =
DesiredTransitionDescendants.FindOrAdd(Ancestor);
if (!Descendants.Contains(Key)) Descendants.Add(Key);
const int32 InsertIndex = LowerBound(Descendants, Key);
if (InsertIndex == Descendants.Num() || Descendants[InsertIndex] != Key)
{
Descendants.Insert(Key, InsertIndex);
}
}
else if (TArray<FVoxelTileKey>* Descendants =
DesiredTransitionDescendants.Find(Ancestor))
{
Descendants->RemoveSingleSwap(Key, EAllowShrinking::No);
const int32 RemoveIndex = LowerBound(*Descendants, Key);
if (RemoveIndex < Descendants->Num()
&& (*Descendants)[RemoveIndex] == Key)
{
Descendants->RemoveAt(RemoveIndex, 1, EAllowShrinking::No);
}
if (Descendants->IsEmpty()) DesiredTransitionDescendants.Remove(Ancestor);
}
}
@@ -12171,7 +12289,14 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
{
BuildDesiredTransitionIndex();
}
if (DesiredTransitionIndexStartSeconds != 0.0)
{
DesiredTransitionIndexMilliseconds =
(FPlatformTime::Seconds() - DesiredTransitionIndexStartSeconds) * 1000.0;
}
const double DesiredRetainedRegionStartSeconds = bTraceDesiredBreakdown
? FPlatformTime::Seconds() : 0.0;
bool bRetainedSetChanged = false;
int32 RetainedTileCount = 0;
TArray<FVoxelTileKey> MissingRetainedFineTiles;
@@ -12190,6 +12315,13 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
}
}
}
if (DesiredRetainedRegionStartSeconds != 0.0)
{
DesiredRetainedRegionMilliseconds =
(FPlatformTime::Seconds() - DesiredRetainedRegionStartSeconds) * 1000.0;
}
const double DesiredRegionReconcileStartSeconds = bTraceDesiredBreakdown
? FPlatformTime::Seconds() : 0.0;
TArray<FVoxelTileKey> MissingRegionTiles;
{
FReadScopeLock Lock(State->WorldLock);
@@ -12249,6 +12381,11 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
}
StartPendingCoreRegionCompile();
}
if (DesiredRegionReconcileStartSeconds != 0.0)
{
DesiredRegionReconcileMilliseconds =
(FPlatformTime::Seconds() - DesiredRegionReconcileStartSeconds) * 1000.0;
}
if (bTraceDesiredBreakdown)
{
@@ -12258,7 +12395,10 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
TEXT("\"frame\":%llu,\"center\":[%d,%d,%d],\"total_ms\":%s,"
"\"core_prepare_ms\":%s,\"core_delta_ms\":%s,\"fallback_ms\":%s,"
"\"overlay_materialize_ms\":%s,\"priority_sort_ms\":%s,"
"\"critical_order_ms\":%s,\"core_keys\":%d,\"candidate_keys\":%d,"
"\"critical_order_ms\":%s,\"collision_stamp_ms\":%s,"
"\"transition_index_ms\":%s,\"retained_region_ms\":%s,"
"\"region_reconcile_ms\":%s,"
"\"core_keys\":%d,\"candidate_keys\":%d,"
"\"overlay_keys\":%d,\"critical_keys\":%d,\"fallback_reason\":%d,"
"\"changed_lod4plus\":%d,\"materialization_update\":%d,"
"\"priority_order_reused\":%d,\"priority_order_updated\":%d,"
@@ -12271,6 +12411,10 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
*FString::SanitizeFloat(static_cast<float>(OverlayMaterializationMilliseconds)),
*FString::SanitizeFloat(static_cast<float>(PrioritySortMilliseconds)),
*FString::SanitizeFloat(static_cast<float>(CriticalAdmissionOrderMilliseconds)),
*FString::SanitizeFloat(static_cast<float>(DesiredCollisionAndStampMilliseconds)),
*FString::SanitizeFloat(static_cast<float>(DesiredTransitionIndexMilliseconds)),
*FString::SanitizeFloat(static_cast<float>(DesiredRetainedRegionMilliseconds)),
*FString::SanitizeFloat(static_cast<float>(DesiredRegionReconcileMilliseconds)),
CoreDesiredTiles ? CoreDesiredTiles->Num()
: static_cast<int32>(UncachedCoreDesiredTiles.size()),
CandidateKeys.Num(), OverlayDesiredTiles.Num(), CriticalDesiredTiles.Num(),
@@ -14118,14 +14262,17 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
{
const uint64 TraceLevelSortStartCycles = bTraceStreamingScheduler
? FPlatformTime::Cycles64() : 0;
const int32 LevelSubmissionCount = FMath::Min(
LevelTiles.Num(), FMath::Max(
0, AdmissionCap - GetGenerationAdmissionCount()));
const bool bUseForwardFirstOrder =
GVoxelForgeStreamingFastTravelForwardFirst != 0
&& bPolicy3FastTravel && Level <= 2;
if (bUseForwardFirstOrder)
if (LevelSubmissionCount > 0 && bUseForwardFirstOrder)
{
SortNearTilesForwardFirst(LevelTiles);
}
else
else if (LevelSubmissionCount > 0)
{
FVector Heading = GetActorTransform().InverseTransformVectorNoScale(PlayerHeading);
if (Heading.IsNearlyZero() && IsValid(PlayerPawn))
@@ -14138,7 +14285,12 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
const double TileSizeCm = static_cast<double>(VF_ClipLevelExtentVoxels(Level))
* GetActiveVoxelSizeCm();
const bool bUseFastNearLeadOrder = bPolicy3FastTravel && Level <= 2;
std::stable_sort(LevelTiles.GetData(), LevelTiles.GetData() + LevelTiles.Num(),
// The full order is deterministic because the comparator breaks every tie
// with the tile coordinates. Select only the prefix this admission pass can
// submit; sorting every outstanding key made a small worker cap pay O(N log N).
std::partial_sort(LevelTiles.GetData(),
LevelTiles.GetData() + LevelSubmissionCount,
LevelTiles.GetData() + LevelTiles.Num(),
[&](const FVoxelTileKey& A, const FVoxelTileKey& B)
{
const FVector CenterA(
@@ -14193,6 +14345,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
if (A.Coord.Y != B.Coord.Y) return A.Coord.Y < B.Coord.Y;
return A.Coord.Z < B.Coord.Z;
});
LevelTiles.SetNum(LevelSubmissionCount, EAllowShrinking::No);
}
if (bTraceStreamingScheduler)
{
+1 -1
View File
@@ -1617,7 +1617,7 @@ public:
// ONE frame → a game-thread teardown spike. Drained by ProcessUnloadQueue (catch-up scaled).
TSet<FVoxelTileKey> PendingUnload;
void ProcessPendingChunks();
void ProcessPendingChunks(uint64 TickStartCycles);
void ProcessUnloadQueue(); // budgeted teardown drain (see PendingUnload)
void QueuePendingUnload(const FVoxelTileKey& Tile);
void HideOverlappingDesiredVisuals(const FVoxelTileKey& RetiredTile);