streaming: ahead-first admission, same-center rebuild suppression, Core key cache; off by default (L160)

30 m/s UE, 5 pairs: forward-cone readiness 14.2 -> 18.2% (+3.75 pp), all-around 39.5 -> 36.6%; p99 <= 11 ms in all
candidate runs, but worst tick, far holes and arrival regressed in some pairs. Rebuilds per crossing 7.2 -> 4.4; per
rebuild cost unchanged (not yet incremental). Source/: VoxelWorld.cpp, VoxelWorld.h (defaults unchanged).

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 09:29:39 +01:00
1 parent 323fbe3736
commit aa458d3607
4 files changed
+931 -82

No files matched your search

+362 -82
View File
@@ -366,6 +366,24 @@ namespace
TEXT("voxel.StreamingFastTravelApplyBudgetMilliseconds"),
GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds,
TEXT("Optional visible-result apply-time cap during speed-aware fast travel; 0 keeps the configured budget."));
int32 GVoxelForgeStreamingFastTravelForwardFirst = 0;
FAutoConsoleVariableRef CVarVoxelForgeStreamingFastTravelForwardFirst(
TEXT("voxel.StreamingFastTravelForwardFirst"),
GVoxelForgeStreamingFastTravelForwardFirst,
TEXT("During fast travel, admit camera-forward tiles first, then nearest travel-ahead tiles; 0 uses the legacy lead-band order."));
int32 GVoxelForgeStreamingRebuildOnCoreRegionCompletion = 1;
FAutoConsoleVariableRef CVarVoxelForgeStreamingRebuildOnCoreRegionCompletion(
TEXT("voxel.StreamingRebuildOnCoreRegionCompletion"),
GVoxelForgeStreamingRebuildOnCoreRegionCompletion,
TEXT("Force a full desired-set rebuild after Core region compilation; 0 reuses the unchanged desired keys."));
int32 GVoxelForgeStreamingCoreDesiredCacheByCenter = 0;
FAutoConsoleVariableRef CVarVoxelForgeStreamingCoreDesiredCacheByCenter(
TEXT("voxel.StreamingCoreDesiredCacheByCenter"),
GVoxelForgeStreamingCoreDesiredCacheByCenter,
TEXT("Reuse Core clipmap desired keys when a same-center overlay rebuild occurs; 0 queries Core each time."));
constexpr int32 StreamingStartupMaxMeshApplies = 24;
constexpr float StreamingStartupApplyBudgetMilliseconds = 14.0f;
constexpr double StreamingStartupApplyMaxSeconds = 30.0;
@@ -2280,6 +2298,24 @@ void AVoxelWorld::BeginPlay()
GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds = FMath::Max(
0.0f, GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds);
}
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingFastTravelForwardFirst="),
GVoxelForgeStreamingFastTravelForwardFirst))
{
GVoxelForgeStreamingFastTravelForwardFirst =
GVoxelForgeStreamingFastTravelForwardFirst != 0 ? 1 : 0;
}
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingRebuildOnCoreRegionCompletion="),
GVoxelForgeStreamingRebuildOnCoreRegionCompletion))
{
GVoxelForgeStreamingRebuildOnCoreRegionCompletion =
GVoxelForgeStreamingRebuildOnCoreRegionCompletion != 0 ? 1 : 0;
}
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingCoreDesiredCacheByCenter="),
GVoxelForgeStreamingCoreDesiredCacheByCenter))
{
GVoxelForgeStreamingCoreDesiredCacheByCenter =
GVoxelForgeStreamingCoreDesiredCacheByCenter != 0 ? 1 : 0;
}
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingResultBacklogCap="),
CommandLineStreamingResultBacklogCap))
{
@@ -8081,6 +8117,9 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld(FString& OutFailureReason)
Request.parallelSignEdgeRepair = true;
VoxelForgeCoreState = State;
CachedCoreDesiredTiles.Reset();
bHasCachedCoreDesiredTiles = false;
CachedCoreDesiredCenter = FIntVector(MAX_int32, MAX_int32, MAX_int32);
State->bSnapshotReady.store(true, std::memory_order_release);
ActiveTaskCount.fetch_add(1, std::memory_order_relaxed);
const double CoreCompileLaunchSeconds = bTraceCoreStartup
@@ -11055,13 +11094,55 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
const TSharedPtr<FVoxelForgeCoreRuntimeState, ESPMode::ThreadSafe> State = VoxelForgeCoreState;
if (!State.IsValid() || !State->bReady.load(std::memory_order_acquire)) return;
const FVector CameraVoxels = WorldToLocalCm(PlayerPosition) / GetActiveVoxelSizeCm();
std::vector<VoxelForgeCore::TileKey> CoreDesired;
std::vector<VoxelForgeCore::TileKey> UncachedCoreDesiredTiles;
const TArray<FVoxelTileKey>* CoreDesiredTiles = nullptr;
double CoreDesiredQueryMilliseconds = 0.0;
bool bCoreDesiredCacheHit = false;
if (GVoxelForgeStreamingCoreDesiredCacheByCenter != 0
&& bHasCachedCoreDesiredTiles && CachedCoreDesiredCenter == Center)
{
FReadScopeLock Lock(State->WorldLock);
if (!State->World) return;
CoreDesired = State->World->desiredTiles(
{CameraVoxels.X, CameraVoxels.Y, CameraVoxels.Z});
CoreDesiredTiles = &CachedCoreDesiredTiles;
bCoreDesiredCacheHit = true;
}
else
{
const uint64 QueryStartCycles = VoxelForgeStartupTrace::IsActive()
? FPlatformTime::Cycles64() : 0;
const FVector CameraVoxels = WorldToLocalCm(PlayerPosition) / GetActiveVoxelSizeCm();
{
FReadScopeLock Lock(State->WorldLock);
if (!State->World) return;
UncachedCoreDesiredTiles = State->World->desiredTiles(
{CameraVoxels.X, CameraVoxels.Y, CameraVoxels.Z});
}
if (GVoxelForgeStreamingCoreDesiredCacheByCenter != 0)
{
CachedCoreDesiredTiles.Reset();
CachedCoreDesiredTiles.Reserve(static_cast<int32>(UncachedCoreDesiredTiles.size()));
for (const VoxelForgeCore::TileKey& Key : UncachedCoreDesiredTiles)
{
CachedCoreDesiredTiles.Emplace(
FIntVector(Key.coordinate.x, Key.coordinate.y, Key.coordinate.z), Key.lod);
}
CachedCoreDesiredCenter = Center;
bHasCachedCoreDesiredTiles = true;
CoreDesiredTiles = &CachedCoreDesiredTiles;
}
if (QueryStartCycles != 0)
{
CoreDesiredQueryMilliseconds = FPlatformTime::ToSeconds64(
FPlatformTime::Cycles64() - QueryStartCycles) * 1000.0;
}
}
if (VoxelForgeStartupTrace::IsActive())
{
VoxelForgeStartupTrace::RecordEvent(TEXT("core_desired_cache"), FString::Printf(
TEXT("\"frame\":%llu,\"center\":[%d,%d,%d],\"hit\":%d,\"keys\":%d,\"query_ms\":%s"),
static_cast<unsigned long long>(GFrameCounter), Center.X, Center.Y, Center.Z,
bCoreDesiredCacheHit ? 1 : 0,
CoreDesiredTiles ? CoreDesiredTiles->Num()
: static_cast<int32>(UncachedCoreDesiredTiles.size()),
*FString::SanitizeFloat(static_cast<float>(CoreDesiredQueryMilliseconds))));
}
const FIntVector LookaheadHeadingStep = GetStreamingHeadingStep(PlayerHeading, PlayerPawn);
@@ -11085,12 +11166,25 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
DesiredStamped.FindOrAdd(Key) = DesiredStamp;
};
for (const VoxelForgeCore::TileKey& Key : CoreDesired)
const auto AddCoreDesiredTile = [&](const FVoxelTileKey& RuntimeKey)
{
const FVoxelTileKey RuntimeKey(
FIntVector(Key.coordinate.x, Key.coordinate.y, Key.coordinate.z), Key.lod);
AddCandidate(RuntimeKey);
BaseVisualTiles.Add(RuntimeKey);
};
if (CoreDesiredTiles)
{
for (const FVoxelTileKey& RuntimeKey : *CoreDesiredTiles)
{
AddCoreDesiredTile(RuntimeKey);
}
}
else
{
for (const VoxelForgeCore::TileKey& Key : UncachedCoreDesiredTiles)
{
AddCoreDesiredTile(FVoxelTileKey(
FIntVector(Key.coordinate.x, Key.coordinate.y, Key.coordinate.z), Key.lod));
}
}
const int32 PawnCollisionRadius = FMath::Clamp(
@@ -11512,7 +11606,6 @@ void AVoxelWorld::StartPendingCoreRegionCompile()
}
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"),
@@ -12416,7 +12509,15 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
CoreRegionCompileTilesInFlight.Reset();
if (bRegionCompileSucceeded)
{
bForceDesiredRebuild = true;
if (GVoxelForgeStreamingRebuildOnCoreRegionCompletion != 0)
{
bForceDesiredRebuild = true;
}
// Region compilation adds evaluator pages; it does not change the desired
// tile keys. The live DesiredSorted list is admitted again below every
// update. Center, anchor, and collision-gate input changes already request
// their own desired rebuild, so compiling pages must not force another full
// clipmap rebuild on the same center.
bool bRetainedSetMatchesDesired = false;
TArray<FVoxelTileKey> MissingRetainedFineTiles;
{
@@ -12509,6 +12610,40 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
const int32 AuthoredNearTaskReserve = GetNearTaskReserve(MaxTasks);
const int32 FastTravelTotalCap = TotalAdmissionCap;
const FIntVector CenterChunk = GetTileCoordAtLocalCm(WorldToLocalCm(CenterPosition));
FVector TravelHeadingLocal = GetActorTransform().InverseTransformVectorNoScale(PlayerHeading)
.GetSafeNormal();
if (TravelHeadingLocal.IsNearlyZero() && IsValid(PlayerPawn))
{
TravelHeadingLocal = GetActorTransform().InverseTransformVectorNoScale(
PlayerPawn->GetActorForwardVector()).GetSafeNormal();
}
FVector CameraForwardLocal = FVector::ZeroVector;
bool bCameraForwardValid = false;
if (IsValid(PlayerPawn))
{
if (APlayerController* PlayerController = Cast<APlayerController>(PlayerPawn->GetController());
PlayerController && PlayerController->IsLocalController())
{
FVector ViewLocation;
FRotator ViewRotation;
PlayerController->GetPlayerViewPoint(ViewLocation, ViewRotation);
CameraForwardLocal = GetActorTransform().InverseTransformVectorNoScale(
ViewRotation.Vector()).GetSafeNormal();
bCameraForwardValid = !CameraForwardLocal.IsNearlyZero();
}
}
constexpr double StreamingForwardConeCosine = 0.573576436351046; // cos(55 degrees)
const FVector PlayerLocalCm = WorldToLocalCm(CenterPosition);
const auto GetNearTileOffset = [&](const FVoxelTileKey& Tile)
{
const double TileExtentCm = static_cast<double>(
VF_ClipLevelExtentVoxels(Tile.Level)) * GetActiveVoxelSizeCm();
const FVector TileCenter(
(static_cast<double>(Tile.Coord.X) + 0.5) * TileExtentCm,
(static_cast<double>(Tile.Coord.Y) + 0.5) * TileExtentCm,
(static_cast<double>(Tile.Coord.Z) + 0.5) * TileExtentCm);
return TileCenter - PlayerLocalCm;
};
FVoxelTileKey WalkableSupportTile;
const FIntVector WalkableCenter = GetPlayerSupportTile(
PlayerPawn, CenterPosition, WalkableSupportTile)
@@ -12968,6 +13103,70 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
// Coarse work is capped below the full task budget so later near requests keep admission.
// Le travail grossier reste sous le budget total pour reserver des admissions aux tuiles proches.
//=========================================================================
const auto GetNearDirectionClassForOffset = [&](const FVector& Offset)
{
const double Distance = Offset.Size();
if (Distance > 0.0)
{
const FVector& ConeHeading = bCameraForwardValid
? CameraForwardLocal : TravelHeadingLocal;
if (!ConeHeading.IsNearlyZero()
&& FVector::DotProduct(Offset, ConeHeading) / Distance
>= StreamingForwardConeCosine)
{
return 0; // camera cone, or travel cone when no local camera is available
}
}
const double TravelProjection = FVector::DotProduct(Offset, TravelHeadingLocal);
if (!TravelHeadingLocal.IsNearlyZero() && TravelProjection > 0.0) return 1;
if (!TravelHeadingLocal.IsNearlyZero() && TravelProjection < 0.0) return 3;
return 2; // side / direction unavailable
};
const auto GetNearDirectionClass = [&](const FVoxelTileKey& Tile)
{
return GetNearDirectionClassForOffset(GetNearTileOffset(Tile));
};
const auto SortNearTilesForwardFirst = [&](TArray<FVoxelTileKey>& Tiles)
{
struct FForwardTileEntry
{
FVoxelTileKey Key;
int32 DirectionClass = 2;
double DistanceSquared = 0.0;
};
TArray<FForwardTileEntry> Entries;
Entries.Reserve(Tiles.Num());
for (const FVoxelTileKey& Tile : Tiles)
{
const FVector Offset = GetNearTileOffset(Tile);
Entries.Add({Tile, GetNearDirectionClassForOffset(Offset), Offset.SizeSquared()});
}
std::stable_sort(Entries.GetData(), Entries.GetData() + Entries.Num(),
[](const FForwardTileEntry& A, const FForwardTileEntry& B)
{
if (A.DirectionClass != B.DirectionClass)
{
return A.DirectionClass < B.DirectionClass;
}
if (A.DirectionClass <= 1)
{
if (A.DistanceSquared != B.DistanceSquared)
{
return A.DistanceSquared < B.DistanceSquared;
}
if (A.Key.Coord.X != B.Key.Coord.X) return A.Key.Coord.X < B.Key.Coord.X;
if (A.Key.Coord.Y != B.Key.Coord.Y) return A.Key.Coord.Y < B.Key.Coord.Y;
return A.Key.Coord.Z < B.Key.Coord.Z;
}
return false; // preserve the existing deterministic order for side/back tiles
});
for (int32 Index = 0; Index < Entries.Num(); ++Index)
{
Tiles[Index] = Entries[Index].Key;
}
};
int32 SchedulerSubmittedByDirection[4] = {};
TArray<FString> SchedulerNearAdmissionOrder;
const bool bSubmissionLoopRan = !bAllChunksLoaded;
const uint64 TraceSubmitTilesStartCycles = bTraceStreamingScheduler && bSubmissionLoopRan
? FPlatformTime::Cycles64() : 0;
@@ -12997,6 +13196,20 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
&& Tile.Level >= 0 && Tile.Level < 9)
{
++SchedulerSubmittedByLevel[Tile.Level];
if (bTraceStreamingScheduler && Tile.Level <= 2)
{
const int32 DirectionClass = GetNearDirectionClass(Tile);
++SchedulerSubmittedByDirection[DirectionClass];
if (SchedulerNearAdmissionOrder.Num() < 12)
{
const TCHAR* Lane = Priority == EVoxelTileWorkPriority::CollisionCritical
? TEXT("critical") : TEXT("near");
SchedulerNearAdmissionOrder.Add(FString::Printf(
TEXT("\"%d,%d,%d,l%d,c%d,%s\""),
Tile.Coord.X, Tile.Coord.Y, Tile.Coord.Z, Tile.Level,
DirectionClass, Lane));
}
}
}
};
@@ -13004,7 +13217,16 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
// PRIORITE AU SOL — la tuile de support et l'anneau XY 3x3 requis pour la collision passent avant le reste.
// They use the foreground task lane when enabled, above ordinary background streaming.
// Ils utilisent la voie foreground lorsqu'elle est active, avant le streaming distant.
for (const FVoxelTileKey& T : CriticalDesiredTiles)
TArray<FVoxelTileKey> ForwardCriticalTiles;
const TArray<FVoxelTileKey>* CriticalAdmissionTiles = &CriticalDesiredTiles;
if (GVoxelForgeStreamingFastTravelForwardFirst != 0
&& bPolicy3FastTravel)
{
ForwardCriticalTiles = CriticalDesiredTiles;
SortNearTilesForwardFirst(ForwardCriticalTiles);
CriticalAdmissionTiles = &ForwardCriticalTiles;
}
for (const FVoxelTileKey& T : *CriticalAdmissionTiles)
{
if (GetGenerationAdmissionCount() >= TotalAdmissionCap)
{
@@ -13079,78 +13301,84 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
// order; this optional sort only adds the current travel heading and distance.
if (bPreferTravelDirection && 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();
}
const uint64 TraceLevelSortStartCycles = bTraceStreamingScheduler
? FPlatformTime::Cycles64() : 0;
const bool bUseFastNearLeadOrder = bPolicy3FastTravel && Level <= 2;
std::stable_sort(LevelTiles.GetData(),LevelTiles.GetData()+LevelTiles.Num(),
[&](const FVoxelTileKey& A,const FVoxelTileKey& B)
const bool bUseForwardFirstOrder =
GVoxelForgeStreamingFastTravelForwardFirst != 0
&& bPolicy3FastTravel && Level <= 2;
if (bUseForwardFirstOrder)
{
const FVector CenterA(
(static_cast<double>(A.Coord.X) + 0.5) * TileSizeCm,
(static_cast<double>(A.Coord.Y) + 0.5) * TileSizeCm,
(static_cast<double>(A.Coord.Z) + 0.5) * TileSizeCm);
const FVector CenterB(
(static_cast<double>(B.Coord.X) + 0.5) * TileSizeCm,
(static_cast<double>(B.Coord.Y) + 0.5) * TileSizeCm,
(static_cast<double>(B.Coord.Z) + 0.5) * TileSizeCm);
const FVector OffsetA = CenterA - PlayerLocalCm;
const FVector OffsetB = CenterB - PlayerLocalCm;
if (bUseFastNearLeadOrder && !Heading.IsNearlyZero())
SortNearTilesForwardFirst(LevelTiles);
}
else
{
FVector Heading = GetActorTransform().InverseTransformVectorNoScale(PlayerHeading);
if (Heading.IsNearlyZero() && IsValid(PlayerPawn))
{
const double ProjectionA = FVector::DotProduct(OffsetA, Heading);
const double ProjectionB = FVector::DotProduct(OffsetB, Heading);
const auto LeadRank = [TileSizeCm](double Projection)
{
if (Projection >= TileSizeCm && Projection <= TileSizeCm * 2.0) return 0;
if (Projection > 0.0 && Projection < TileSizeCm) return 1;
if (Projection > TileSizeCm * 2.0) return 2;
return 3;
};
const int32 LeadRankA = LeadRank(ProjectionA);
const int32 LeadRankB = LeadRank(ProjectionB);
if (LeadRankA != LeadRankB) return LeadRankA < LeadRankB;
if (LeadRankA == 0)
{
const double TargetLead = TileSizeCm * 1.5;
const double LeadErrorA = FMath::Abs(ProjectionA - TargetLead);
const double LeadErrorB = FMath::Abs(ProjectionB - TargetLead);
if (LeadErrorA != LeadErrorB) return LeadErrorA < LeadErrorB;
}
else if (LeadRankA == 1 && ProjectionA != ProjectionB)
{
return ProjectionA > ProjectionB;
}
else if (LeadRankA == 2 && ProjectionA != ProjectionB)
{
return ProjectionA < ProjectionB;
}
Heading = GetActorTransform().InverseTransformVectorNoScale(
PlayerPawn->GetActorForwardVector());
}
const bool bAheadA = !Heading.IsNearlyZero() && FVector::DotProduct(OffsetA, Heading) > 0.0;
const bool bAheadB = !Heading.IsNearlyZero() && FVector::DotProduct(OffsetB, Heading) > 0.0;
if (bAheadA != bAheadB) return bAheadA;
const double DistanceA = OffsetA.SizeSquared();
const double DistanceB = OffsetB.SizeSquared();
if (DistanceA != DistanceB) return DistanceA < DistanceB;
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;
});
Heading = Heading.GetSafeNormal();
const FVector LegacyPlayerLocalCm = WorldToLocalCm(CenterPosition);
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(),
[&](const FVoxelTileKey& A, const FVoxelTileKey& B)
{
const FVector CenterA(
(static_cast<double>(A.Coord.X) + 0.5) * TileSizeCm,
(static_cast<double>(A.Coord.Y) + 0.5) * TileSizeCm,
(static_cast<double>(A.Coord.Z) + 0.5) * TileSizeCm);
const FVector CenterB(
(static_cast<double>(B.Coord.X) + 0.5) * TileSizeCm,
(static_cast<double>(B.Coord.Y) + 0.5) * TileSizeCm,
(static_cast<double>(B.Coord.Z) + 0.5) * TileSizeCm);
const FVector OffsetA = CenterA - LegacyPlayerLocalCm;
const FVector OffsetB = CenterB - LegacyPlayerLocalCm;
if (bUseFastNearLeadOrder && !Heading.IsNearlyZero())
{
const double ProjectionA = FVector::DotProduct(OffsetA, Heading);
const double ProjectionB = FVector::DotProduct(OffsetB, Heading);
const auto LeadRank = [TileSizeCm](double Projection)
{
if (Projection >= TileSizeCm && Projection <= TileSizeCm * 2.0) return 0;
if (Projection > 0.0 && Projection < TileSizeCm) return 1;
if (Projection > TileSizeCm * 2.0) return 2;
return 3;
};
const int32 LeadRankA = LeadRank(ProjectionA);
const int32 LeadRankB = LeadRank(ProjectionB);
if (LeadRankA != LeadRankB) return LeadRankA < LeadRankB;
if (LeadRankA == 0)
{
const double TargetLead = TileSizeCm * 1.5;
const double LeadErrorA = FMath::Abs(ProjectionA - TargetLead);
const double LeadErrorB = FMath::Abs(ProjectionB - TargetLead);
if (LeadErrorA != LeadErrorB) return LeadErrorA < LeadErrorB;
}
else if (LeadRankA == 1 && ProjectionA != ProjectionB)
{
return ProjectionA > ProjectionB;
}
else if (LeadRankA == 2 && ProjectionA != ProjectionB)
{
return ProjectionA < ProjectionB;
}
}
const bool bAheadA = !Heading.IsNearlyZero()
&& FVector::DotProduct(OffsetA, Heading) > 0.0;
const bool bAheadB = !Heading.IsNearlyZero()
&& FVector::DotProduct(OffsetB, Heading) > 0.0;
if (bAheadA != bAheadB) return bAheadA;
const double DistanceA = OffsetA.SizeSquared();
const double DistanceB = OffsetB.SizeSquared();
if (DistanceA != DistanceB) return DistanceA < DistanceB;
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;
});
}
if (bTraceStreamingScheduler)
{
LevelSortMilliseconds += FPlatformTime::ToSeconds64(
@@ -13494,6 +13722,8 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
int32 NearBehindResident = 0;
int32 NearForwardConeDesired = 0;
int32 NearForwardConeResident = 0;
int32 NearViewForwardConeDesired = 0;
int32 NearViewForwardConeResident = 0;
double NearForwardWeightedDesired = 0.0;
double NearForwardWeightedResident = 0.0;
int32 TraceMaxDesiredLevel = 0;
@@ -13505,7 +13735,6 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
TraceHeading = GetActorTransform().InverseTransformVectorNoScale(
PlayerPawn->GetActorForwardVector()).GetSafeNormal();
}
constexpr double ForwardConeCosine = 0.573576436351046; // cos(55 degrees)
for (const FVoxelTileKey& Tile : DesiredSorted)
{
TraceMaxDesiredLevel = FMath::Max(TraceMaxDesiredLevel, Tile.Level);
@@ -13520,7 +13749,8 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
++SettledByLevel[Tile.Level];
if (PendingTiles.Contains(Tile)) ++PendingByLevel[Tile.Level];
if (Tile.Level <= 2 && !TraceHeading.IsNearlyZero())
if (Tile.Level <= 2
&& (!TraceHeading.IsNearlyZero() || bCameraForwardValid))
{
const double TileExtentCm = static_cast<double>(
VF_ClipLevelExtentVoxels(Tile.Level)) * GetActiveVoxelSizeCm();
@@ -13542,11 +13772,21 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
++NearBehindDesired;
if (bResident) ++NearBehindResident;
}
if (DirectionCosine >= ForwardConeCosine)
if (DirectionCosine >= StreamingForwardConeCosine)
{
++NearForwardConeDesired;
if (bResident) ++NearForwardConeResident;
}
if (bCameraForwardValid && Distance > 0.0)
{
const double ViewDirectionCosine =
FVector::DotProduct(Offset, CameraForwardLocal) / Distance;
if (ViewDirectionCosine >= StreamingForwardConeCosine)
{
++NearViewForwardConeDesired;
if (bResident) ++NearViewForwardConeResident;
}
}
const double Weight = FMath::Max(0.0, DirectionCosine);
NearForwardWeightedDesired += Weight;
if (bResident) NearForwardWeightedResident += Weight;
@@ -13599,6 +13839,9 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
"\"near_behind_desired\":%d,\"near_behind_resident\":%d,"
"\"near_forward_cone_half_angle_deg\":55,"
"\"near_forward_cone_desired\":%d,\"near_forward_cone_resident\":%d,"
"\"near_view_forward_cone_half_angle_deg\":55,"
"\"near_view_forward_cone_desired\":%d,\"near_view_forward_cone_resident\":%d,"
"\"near_view_forward_cone_valid\":%d,"
"\"near_forward_weighted_desired\":%s,"
"\"near_forward_weighted_resident\":%s,\"near_direction_valid\":%d,"
"\"pending_by_lod\":%s,"
@@ -13617,6 +13860,8 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
*DesiredCounts, *ResidentCounts, *SettledCounts,
NearAheadDesired, NearAheadResident, NearBehindDesired, NearBehindResident,
NearForwardConeDesired, NearForwardConeResident,
NearViewForwardConeDesired, NearViewForwardConeResident,
bCameraForwardValid ? 1 : 0,
*FString::SanitizeFloat(static_cast<float>(NearForwardWeightedDesired)),
*FString::SanitizeFloat(static_cast<float>(NearForwardWeightedResident)),
TraceHeading.IsNearlyZero() ? 0 : 1,
@@ -13638,6 +13883,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
int32 PendingByLevel[9] = {};
int32 AdmissionCapByLevel[9] = {};
int32 DeclinedRuleByLevel[9] = {};
int32 DesiredMissingByDirection[4] = {};
for (const FVoxelTileKey& Tile : DesiredSorted)
{
if (Tile.Level < 0 || Tile.Level > 8) continue;
@@ -13652,6 +13898,10 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
else
{
++DesiredMissingByLevel[Tile.Level];
if (Tile.Level <= 2)
{
++DesiredMissingByDirection[GetNearDirectionClass(Tile)];
}
}
}
@@ -13749,6 +13999,24 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
Json += TEXT("]");
return Json;
};
const auto IntArray4Json = [](const int32 Values[4])
{
FString Json = TEXT("[");
for (int32 Index = 0; Index < 4; ++Index)
{
if (Index > 0) Json += TEXT(",");
Json += FString::FromInt(Values[Index]);
}
Json += TEXT("]");
return Json;
};
FString NearAdmissionOrderJson = TEXT("[");
for (int32 Index = 0; Index < SchedulerNearAdmissionOrder.Num(); ++Index)
{
if (Index > 0) NearAdmissionOrderJson += TEXT(",");
NearAdmissionOrderJson += SchedulerNearAdmissionOrder[Index];
}
NearAdmissionOrderJson += TEXT("]");
const double SchedulerTraceNow = FPlatformTime::Seconds();
const uint64 TotalWorkerCycles = TotalGenerationWorkerTaskCycles.load(std::memory_order_relaxed);
const uint64 WorkerCyclesDelta = TotalWorkerCycles >= LastStreamingSchedulerWorkerCycles
@@ -13769,6 +14037,11 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
"\"max_tasks\":%d,\"ready_queue_depth\":%d,"
"\"total_admission_cap\":%d,\"near_admission_cap\":%d,"
"\"near_reserve\":%d,\"scheduler_policy\":%d,"
"\"forward_first\":%d,\"rebuild_on_core_region_completion\":%d,"
"\"core_desired_cache_by_center\":%d,"
"\"camera_forward_valid\":%d,"
"\"near_missing_by_direction\":%s,\"near_submitted_by_direction\":%s,"
"\"near_admission_order_first\":%s,"
"\"coarse_cap\":%d,\"first_far_level\":%d,"
"\"submission_stop_level\":%d,\"submission_stop_cap\":%d,"
"\"pending_entry\":%d,\"pending_after\":%d,\"admission_occupancy\":%d,"
@@ -13798,6 +14071,13 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
GVoxelForgeStreamingLegacyPendingResultCap,
GVoxelForgeCoreRegionConcurrentAdmission, MaxTasks, ReadyQueueDepth,
TotalAdmissionCap, NearAdmissionCap, NearTaskReserve, SchedulerPolicy,
GVoxelForgeStreamingFastTravelForwardFirst != 0 ? 1 : 0,
GVoxelForgeStreamingRebuildOnCoreRegionCompletion != 0 ? 1 : 0,
GVoxelForgeStreamingCoreDesiredCacheByCenter != 0 ? 1 : 0,
bCameraForwardValid ? 1 : 0,
*IntArray4Json(DesiredMissingByDirection),
*IntArray4Json(SchedulerSubmittedByDirection),
*NearAdmissionOrderJson,
CoarseTaskCap, FirstFarLevel,
SchedulerOrderStopLevel, SchedulerOrderStopCap,
PendingTilesAtUpdateEntry, PendingTiles.Num(), AdmissionOccupancy, ActiveTaskGuard,
+3
View File
@@ -1254,6 +1254,9 @@ public:
float StartupTraceSpeedCmPerSecond = 0.0f;
bool bSpeedAwareTravelMode = false;
bool bSpeedAwareFastMode = false;
TArray<FVoxelTileKey> CachedCoreDesiredTiles;
FIntVector CachedCoreDesiredCenter = FIntVector(MAX_int32, MAX_int32, MAX_int32);
bool bHasCachedCoreDesiredTiles = false;
TArray<FVoxelTileKey> DesiredSorted; // critical floor prefix, then true-pawn distance-first
TArray<FVoxelTileKey> CriticalDesiredTiles; // occupied/support tile, then heading tile
// Ancestor -> desired descendants, rebuilt with DesiredSorted. Reconciliation uses this