Merge branch 'wip/l157-speed' into wip/combined

This commit is contained in:
Fr0zka committed 2026-11-02 20:27:26 +01:00
commit 82eb5b1c66
13 files changed
+2367 -30

No files matched your search

@@ -0,0 +1,28 @@
#pragma once
#include <algorithm>
namespace VoxelStreamingAdmissionOrder
{
template <typename Iterator, typename Less>
void StableSortCandidates(Iterator First, Iterator Last, Less Compare)
{
std::stable_sort(First, Last, Compare);
}
template <typename Range, typename IsAtCapacity, typename SubmitCandidate,
typename OnCapacityReached>
void AdmitInOrder(const Range& Candidates, IsAtCapacity AtCapacity,
SubmitCandidate Submit, OnCapacityReached CapacityReached)
{
for (const auto& Candidate : Candidates)
{
if (AtCapacity())
{
CapacityReached();
break;
}
Submit(Candidate);
}
}
}
+185 -29
View File
@@ -2,6 +2,7 @@
// Implementation of the voxel world manager
#include "VoxelWorld.h"
#include "VoxelStreamingAdmissionOrder.h"
#include "VoxelDiffLayer.h"
#include "RealtimeMeshComponent.h"
#include "RealtimeMeshSimple.h"
@@ -262,7 +263,7 @@ namespace
GVoxelForgeCoreRegionConcurrentAdmission,
TEXT("Allow tiles independent of a pending Core region compile to proceed; 0 restores the global streaming stop."));
int32 GVoxelForgeStreamingFastTravelWaitForAllCriticalTiles = 1;
int32 GVoxelForgeStreamingFastTravelWaitForAllCriticalTiles = 0;
FAutoConsoleVariableRef CVarVoxelForgeStreamingFastTravelWaitForAllCriticalTiles(
TEXT("voxel.StreamingFastTravelWaitForAllCriticalTiles"),
GVoxelForgeStreamingFastTravelWaitForAllCriticalTiles,
@@ -361,7 +362,7 @@ namespace
TEXT("voxel.StreamingStartupApplyBoost"),
GVoxelForgeStreamingStartupApplyBoost,
TEXT("Bounded cold-view upload budget (default on): 1 allows a 14 ms / 24-mesh cap until the initial desired set and walkable ring are ready; 0 uses the authored per-frame budget."));
float GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds = 0.0f;
float GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds = 1.0f;
FAutoConsoleVariableRef CVarVoxelForgeStreamingFastTravelApplyBudgetMilliseconds(
TEXT("voxel.StreamingFastTravelApplyBudgetMilliseconds"),
GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds,
@@ -7191,7 +7192,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 +7419,7 @@ void AVoxelWorld::Tick(float DeltaTime)
}
const uint64 PendingDrainStartCycles = bTraceFrameTiming
? FPlatformTime::Cycles64() : 0;
ProcessPendingChunks();
ProcessPendingChunks(TickStartCycles);
if (bTraceFrameTiming)
{
PendingDrainCycles += FPlatformTime::Cycles64() - PendingDrainStartCycles;
@@ -9899,7 +9900,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.
@@ -9984,6 +9985,7 @@ void AVoxelWorld::ProcessPendingChunks()
}
}
}
bInitialArrivalSetSettled = bInitialArrivalSetSettled || bInitialDesiredSetReady;
const bool bStartupApplyOverridden = GVoxelForgeStreamingStartupApplyBoost <= 0
|| GVoxelForgeStep1MaxMeshApplies > 0;
if (bStartupApplyBoostStarted && !bStartupApplyBoostEnded
@@ -10035,7 +10037,8 @@ void AVoxelWorld::ProcessPendingChunks()
float ApplyBudgetMilliseconds = bUseStartupApplyBoost
? StreamingStartupApplyBudgetMilliseconds
: (Settings ? FMath::Max(Settings->MaxMeshApplyMilliseconds, 0.0f) : 2.0f);
const bool bFastTravelApplyBudgetActive = bStreamingPlayerMoving && bSpeedAwareFastMode
const bool bFastTravelApplyBudgetActive = bInitialArrivalSetSettled
&& bStreamingPlayerMoving && bSpeedAwareFastMode
&& GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds > 0.0f;
if (bFastTravelApplyBudgetActive)
{
@@ -10105,6 +10108,7 @@ void AVoxelWorld::ProcessPendingChunks()
GetStreamingResultBacklogCount(), GVoxelForgeStreamingResultBacklogCap));
};
FChunkResult DequeuedChunk;
TArray<FChunkResult> DeferredApplyResults;
const auto DequeueNextResult = [&]() -> bool
{
bDequeuedSupplementalFar = false;
@@ -10234,8 +10238,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 +10246,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 +10268,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 +10376,11 @@ void AVoxelWorld::ProcessPendingChunks()
}
}
for (FChunkResult& DeferredResult : DeferredApplyResults)
{
EnqueueStreamingResult(MoveTemp(DeferredResult));
}
if (bProfileApply && ResultsDrained > 0)
{
UE_LOG(LogTemp, Display,
@@ -10827,7 +10885,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 +11126,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 +11150,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 +11160,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 +11265,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 +12036,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 +12049,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 +12224,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 +12234,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 +12265,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 +12292,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 +12318,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 +12384,11 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
}
StartPendingCoreRegionCompile();
}
if (DesiredRegionReconcileStartSeconds != 0.0)
{
DesiredRegionReconcileMilliseconds =
(FPlatformTime::Seconds() - DesiredRegionReconcileStartSeconds) * 1000.0;
}
if (bTraceDesiredBreakdown)
{
@@ -12258,7 +12398,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 +12414,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(),
@@ -13418,7 +13565,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
const int32 ConfiguredReadyQueueDepth = FMath::Clamp(
GVoxelForgeStreamingReadyQueueDepth, 0, FMath::Min(64, FMath::Max(1, MaxTasks) * 4));
const bool bFastTravelAdmission = GVoxelForgeStreamingSchedulerPolicy == 3
&& bSpeedAwareFastMode;
&& bInitialArrivalSetSettled && bSpeedAwareFastMode;
const int32 ReadyQueueDepth = bFastTravelAdmission
? FMath::Max(ConfiguredReadyQueueDepth, 24) : ConfiguredReadyQueueDepth;
const int32 TotalAdmissionCap = MaxTasks + ReadyQueueDepth;
@@ -13551,7 +13698,8 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
? AuthoredNearTaskReserve
: ((bPawnMoving || !bWalkableRingReadyForReserve) ? QueueAwareNearTaskReserve : 0);
const int32 NearTaskReserve = EffectiveNearTaskReserve;
const bool bPolicy3FastTravel = SchedulerPolicy == 3 && bSpeedAwareFastMode;
const bool bPolicy3FastTravel = SchedulerPolicy == 3
&& bInitialArrivalSetSettled && bSpeedAwareFastMode;
// Fast travel keeps three quarters of the bounded ready queue for near detail and
// leaves the final quarter open for horizon coverage. The walkable ring is submitted first.
const int32 FastTravelNearCap = MaxTasks + (ReadyQueueDepth * 3) / 4;
@@ -14138,7 +14286,10 @@ 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(),
// Keep the complete stable order. Capacity can open while this pass is
// submitting; truncating to the pass-start count starves this level's refill.
VoxelStreamingAdmissionOrder::StableSortCandidates(
LevelTiles.GetData(), LevelTiles.GetData() + LevelTiles.Num(),
[&](const FVoxelTileKey& A, const FVoxelTileKey& B)
{
const FVector CenterA(
@@ -14202,18 +14353,19 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
}
const uint64 TraceLevelAdmissionStartCycles = bTraceStreamingScheduler
? FPlatformTime::Cycles64() : 0;
for (const FVoxelTileKey& T : LevelTiles)
{
if (GetGenerationAdmissionCount() >= AdmissionCap)
VoxelStreamingAdmissionOrder::AdmitInOrder(LevelTiles,
[&]() { return GetGenerationAdmissionCount() >= AdmissionCap; },
[&](const FVoxelTileKey& T)
{
if (PendingTiles.Contains(T)) return;
if (LoadedTiles.Contains(T)) return;
SubmitTileWithTraceCount(T, Priority);
++Submitted;
},
[&]()
{
if (Level >= 0 && Level < 9) { ++SchedulerLevelCapBreaks[Level]; }
break;
}
if (PendingTiles.Contains(T)) continue;
if (LoadedTiles.Contains(T)) continue;
SubmitTileWithTraceCount(T, Priority);
++Submitted;
}
});
if (bTraceStreamingScheduler)
{
LevelAdmissionMilliseconds += FPlatformTime::ToSeconds64(
@@ -14846,6 +14998,8 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
TEXT("\"frame\":%llu,\"center\":[%d,%d,%d],\"desired_rebuilt\":%d,"
"\"submission_loop_ran\":%d,\"all_chunks_loaded\":%d,"
"\"walkable_ready\":%d,\"moving\":%d,"
"\"initial_arrival_set_settled\":%d,\"fast_travel_mode\":%d,"
"\"wait_for_all_critical_tiles\":%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,"
@@ -14885,6 +15039,8 @@ 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,
bInitialArrivalSetSettled ? 1 : 0, bPolicy3FastTravel ? 1 : 0,
GVoxelForgeStreamingFastTravelWaitForAllCriticalTiles,
bCoreRegionCompilePending ? 1 : 0, IsVoxelForgeCoreReady() ? 1 : 0,
GVoxelForgeStreamingCoarseFirst, GVoxelForgeStreamingDynamicNearReserve,
GVoxelForgeStreamingLegacyPendingResultCap,
@@ -14965,7 +15121,7 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
const int32 ConfiguredReadyQueueDepth = FMath::Clamp(
GVoxelForgeStreamingReadyQueueDepth, 0, FMath::Min(64, FMath::Max(1, MaxTasks) * 4));
const bool bFastTravelAdmission = GVoxelForgeStreamingSchedulerPolicy == 3
&& bSpeedAwareFastMode;
&& bInitialArrivalSetSettled && bSpeedAwareFastMode;
const int32 ReadyQueueDepth = bFastTravelAdmission
? FMath::Max(ConfiguredReadyQueueDepth, 24) : ConfiguredReadyQueueDepth;
const int32 TotalAdmissionCap = MaxTasks + ReadyQueueDepth;
@@ -15367,7 +15523,7 @@ void AVoxelWorld::LoadCoreTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority
const int32 ConfiguredReadyQueueDepth = FMath::Clamp(
GVoxelForgeStreamingReadyQueueDepth, 0, FMath::Min(64, FMath::Max(1, MaxTasks) * 4));
const bool bFastTravelAdmission = GVoxelForgeStreamingSchedulerPolicy == 3
&& bSpeedAwareFastMode;
&& bInitialArrivalSetSettled && bSpeedAwareFastMode;
const int32 ReadyQueueDepth = bFastTravelAdmission
? FMath::Max(ConfiguredReadyQueueDepth, 24) : ConfiguredReadyQueueDepth;
const int32 TotalAdmissionCap = MaxTasks + ReadyQueueDepth;
+3 -1
View File
@@ -1247,6 +1247,8 @@ public:
bool bSampleStacksStarted = false;
bool bStartupApplyBoostStarted = false;
bool bStartupApplyBoostEnded = false;
// Fast-travel admission stays disabled until the initial near LOD and collision set is ready.
bool bInitialArrivalSetSettled = false;
uint64 StartupApplyBoostStartCycles = 0;
// Trace the resolved startup budget once per world. / Tracer une fois le budget de demarrage resolu par monde.
bool bStartupApplyConfigTraced = false;
@@ -1617,7 +1619,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);