Merge branch 'wip/l157-speed' into wip/combined
This commit is contained in:
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user