streaming: early-admission + apply-cap experiment controls, off by default; forward-cone metric (L157)
30 m/s UE, 5 pairs: near coverage 36.1 -> 48.6% but 2/5 runs had p99 15.8/18.2 ms (worst 19.9 ms), so controls stay off. Measured: only ~23% of desired LOD0-2 tiles lie in the 55 deg forward cone; forward readiness is 12.1% (base) / 16.9%, vs 50.9/68.5% behind. Slow frames are dominated by desired-set rebuild/UpdateChunks, not apply. Source/: VoxelWorld.cpp (default behaviour unchanged). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
eb47d72258
commit
323fbe3736
3 files changed
+541
-5
No files matched your search
@@ -262,6 +262,12 @@ namespace
|
||||
GVoxelForgeCoreRegionConcurrentAdmission,
|
||||
TEXT("Allow tiles independent of a pending Core region compile to proceed; 0 restores the global streaming stop."));
|
||||
|
||||
int32 GVoxelForgeStreamingFastTravelWaitForAllCriticalTiles = 1;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeStreamingFastTravelWaitForAllCriticalTiles(
|
||||
TEXT("voxel.StreamingFastTravelWaitForAllCriticalTiles"),
|
||||
GVoxelForgeStreamingFastTravelWaitForAllCriticalTiles,
|
||||
TEXT("Wait for the expanded critical tile set before sharing fast-travel admissions with near/far work; 0 keeps the walkable ring first and admits the normal shares."));
|
||||
|
||||
int32 GVoxelForgeStreamingResultBacklogCap = 256;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeStreamingResultBacklogCap(
|
||||
TEXT("voxel.StreamingResultBacklogCap"),
|
||||
@@ -355,6 +361,11 @@ 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;
|
||||
FAutoConsoleVariableRef CVarVoxelForgeStreamingFastTravelApplyBudgetMilliseconds(
|
||||
TEXT("voxel.StreamingFastTravelApplyBudgetMilliseconds"),
|
||||
GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds,
|
||||
TEXT("Optional visible-result apply-time cap during speed-aware fast travel; 0 keeps the configured budget."));
|
||||
constexpr int32 StreamingStartupMaxMeshApplies = 24;
|
||||
constexpr float StreamingStartupApplyBudgetMilliseconds = 14.0f;
|
||||
constexpr double StreamingStartupApplyMaxSeconds = 30.0;
|
||||
@@ -2257,6 +2268,18 @@ void AVoxelWorld::BeginPlay()
|
||||
GVoxelForgeCoreRegionConcurrentAdmission =
|
||||
CommandLineCoreRegionConcurrentAdmission != 0 ? 1 : 0;
|
||||
}
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingFastTravelWaitForAllCriticalTiles="),
|
||||
GVoxelForgeStreamingFastTravelWaitForAllCriticalTiles))
|
||||
{
|
||||
GVoxelForgeStreamingFastTravelWaitForAllCriticalTiles =
|
||||
GVoxelForgeStreamingFastTravelWaitForAllCriticalTiles != 0 ? 1 : 0;
|
||||
}
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingFastTravelApplyBudgetMilliseconds="),
|
||||
GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds))
|
||||
{
|
||||
GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds = FMath::Max(
|
||||
0.0f, GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds);
|
||||
}
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingResultBacklogCap="),
|
||||
CommandLineStreamingResultBacklogCap))
|
||||
{
|
||||
@@ -2750,6 +2773,8 @@ void AVoxelWorld::BeginPlay()
|
||||
"\"collision_gate\":%d,"
|
||||
"\"streaming_result_backlog_cap\":%d,\"streaming_startup_apply_boost\":%d,"
|
||||
"\"streaming_far_apply_supplement\":%d,"
|
||||
"\"streaming_fast_travel_wait_for_all_critical_tiles\":%d,"
|
||||
"\"streaming_fast_travel_apply_budget_ms\":%.3f,"
|
||||
"\"streaming_background_worker_reserve\":%d,\"core_parallel_for_background_priority\":%d,"
|
||||
"\"streaming_tile_background_low\":%d,\"streaming_tile_background_normal\":%d,"
|
||||
"\"core_column_cache_direct_mapped\":%d,"
|
||||
@@ -2772,6 +2797,8 @@ void AVoxelWorld::BeginPlay()
|
||||
GVoxelForgeCollisionGate,
|
||||
GVoxelForgeStreamingResultBacklogCap, GVoxelForgeStreamingStartupApplyBoost,
|
||||
GVoxelForgeStreamingFarApplySupplement,
|
||||
GVoxelForgeStreamingFastTravelWaitForAllCriticalTiles,
|
||||
GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds,
|
||||
GVoxelForgeStreamingBackgroundWorkerReserve,
|
||||
GVoxelForgeCoreParallelForBackgroundPriority,
|
||||
GVoxelForgeStreamingTileBackgroundLow,
|
||||
@@ -9889,9 +9916,16 @@ void AVoxelWorld::ProcessPendingChunks()
|
||||
const int32 MaxApplies = GVoxelForgeStep1MaxMeshApplies > 0
|
||||
? GVoxelForgeStep1MaxMeshApplies
|
||||
: (bUseStartupApplyBoost ? StreamingStartupMaxMeshApplies : ConfiguredMaxApplies);
|
||||
const float ApplyBudgetMilliseconds = bUseStartupApplyBoost
|
||||
float ApplyBudgetMilliseconds = bUseStartupApplyBoost
|
||||
? StreamingStartupApplyBudgetMilliseconds
|
||||
: (Settings ? FMath::Max(Settings->MaxMeshApplyMilliseconds, 0.0f) : 2.0f);
|
||||
const bool bFastTravelApplyBudgetActive = bStreamingPlayerMoving && bSpeedAwareFastMode
|
||||
&& GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds > 0.0f;
|
||||
if (bFastTravelApplyBudgetActive)
|
||||
{
|
||||
ApplyBudgetMilliseconds = FMath::Min(
|
||||
ApplyBudgetMilliseconds, GVoxelForgeStreamingFastTravelApplyBudgetMilliseconds);
|
||||
}
|
||||
const double ApplyBudgetSeconds =
|
||||
static_cast<double>(ApplyBudgetMilliseconds) * 0.001;
|
||||
int32 MeshesApplied = 0;
|
||||
@@ -13231,7 +13265,10 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
// Policy 0 reconstructs L77. Policy 3 enters that order only after sustained fast travel.
|
||||
if (bPolicy3FastTravel)
|
||||
{
|
||||
if (bWalkableRingReadyForReserve && AreCriticalTilesReady())
|
||||
const bool bWaitForExpandedCriticalSet =
|
||||
GVoxelForgeStreamingFastTravelWaitForAllCriticalTiles != 0;
|
||||
if (bWalkableRingReadyForReserve
|
||||
&& (!bWaitForExpandedCriticalSet || AreCriticalTilesReady()))
|
||||
{
|
||||
const int32 ShareAdmissionCap = FastTravelTotalCap;
|
||||
const int32 StartingAdmissions = GetGenerationAdmissionCount();
|
||||
@@ -13451,7 +13488,24 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
int32 SettledByLevel[9] = {};
|
||||
int32 CoveredByLevel[9] = {};
|
||||
int32 PendingByLevel[9] = {};
|
||||
int32 NearAheadDesired = 0;
|
||||
int32 NearAheadResident = 0;
|
||||
int32 NearBehindDesired = 0;
|
||||
int32 NearBehindResident = 0;
|
||||
int32 NearForwardConeDesired = 0;
|
||||
int32 NearForwardConeResident = 0;
|
||||
double NearForwardWeightedDesired = 0.0;
|
||||
double NearForwardWeightedResident = 0.0;
|
||||
int32 TraceMaxDesiredLevel = 0;
|
||||
const FVector LocalPawn = WorldToLocalCm(CenterPosition);
|
||||
FVector TraceHeading = GetActorTransform().InverseTransformVectorNoScale(PlayerHeading)
|
||||
.GetSafeNormal();
|
||||
if (TraceHeading.IsNearlyZero() && IsValid(PlayerPawn))
|
||||
{
|
||||
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);
|
||||
@@ -13460,10 +13514,43 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
{
|
||||
if (Tile.Level < 0 || Tile.Level > 8) continue;
|
||||
++DesiredByLevel[Tile.Level];
|
||||
if (LoadedTiles.Contains(Tile)) ++ResidentByLevel[Tile.Level];
|
||||
const bool bResident = LoadedTiles.Contains(Tile);
|
||||
if (bResident) ++ResidentByLevel[Tile.Level];
|
||||
if (LoadedTiles.Contains(Tile) || CollisionNotRequiredTiles.Contains(Tile))
|
||||
++SettledByLevel[Tile.Level];
|
||||
if (PendingTiles.Contains(Tile)) ++PendingByLevel[Tile.Level];
|
||||
|
||||
if (Tile.Level <= 2 && !TraceHeading.IsNearlyZero())
|
||||
{
|
||||
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);
|
||||
const FVector Offset = TileCenter - LocalPawn;
|
||||
const double Distance = Offset.Size();
|
||||
const double Projection = FVector::DotProduct(Offset, TraceHeading);
|
||||
const double DirectionCosine = Distance > 0.0 ? Projection / Distance : 0.0;
|
||||
if (Projection > 0.0)
|
||||
{
|
||||
++NearAheadDesired;
|
||||
if (bResident) ++NearAheadResident;
|
||||
}
|
||||
else if (Projection < 0.0)
|
||||
{
|
||||
++NearBehindDesired;
|
||||
if (bResident) ++NearBehindResident;
|
||||
}
|
||||
if (DirectionCosine >= ForwardConeCosine)
|
||||
{
|
||||
++NearForwardConeDesired;
|
||||
if (bResident) ++NearForwardConeResident;
|
||||
}
|
||||
const double Weight = FMath::Max(0.0, DirectionCosine);
|
||||
NearForwardWeightedDesired += Weight;
|
||||
if (bResident) NearForwardWeightedResident += Weight;
|
||||
}
|
||||
bool bCovered = LoadedTiles.Contains(Tile);
|
||||
for (int32 AncestorLevel = Tile.Level + 1;
|
||||
!bCovered && AncestorLevel <= TraceMaxDesiredLevel && AncestorLevel <= 8;
|
||||
@@ -13503,12 +13590,17 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
SettledCounts += TEXT("]");
|
||||
CoveredCounts += TEXT("]");
|
||||
PendingCounts += TEXT("]");
|
||||
const FVector LocalPawn = WorldToLocalCm(CenterPosition);
|
||||
const double StreamingUpdateMilliseconds = (TraceNow - StreamingUpdateStartSeconds) * 1000.0;
|
||||
VoxelForgeStartupTrace::RecordEvent(TEXT("streaming_sample"), FString::Printf(
|
||||
TEXT("\"scope\":\"desired_residency_not_visual_enclosure\",\"center\":[%d,%d,%d],"
|
||||
"\"position_cm\":[%s,%s,%s],\"speed_cm_s\":%s,\"walkable_ready\":%d,"
|
||||
"\"desired_by_lod\":%s,\"resident_by_lod\":%s,\"settled_by_lod\":%s,"
|
||||
"\"near_ahead_desired\":%d,\"near_ahead_resident\":%d,"
|
||||
"\"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_forward_weighted_desired\":%s,"
|
||||
"\"near_forward_weighted_resident\":%s,\"near_direction_valid\":%d,"
|
||||
"\"pending_by_lod\":%s,"
|
||||
"\"covered_by_lod\":%s,"
|
||||
"\"walkable_ready_tiles\":%d,\"walkable_required_tiles\":9,"
|
||||
@@ -13522,7 +13614,13 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
|
||||
*FString::SanitizeFloat(LocalPawn.Z),
|
||||
*FString::SanitizeFloat(PlayerHeading.Size()),
|
||||
bWalkableRingReadyForReserve ? 1 : 0,
|
||||
*DesiredCounts, *ResidentCounts, *SettledCounts, *PendingCounts, *CoveredCounts,
|
||||
*DesiredCounts, *ResidentCounts, *SettledCounts,
|
||||
NearAheadDesired, NearAheadResident, NearBehindDesired, NearBehindResident,
|
||||
NearForwardConeDesired, NearForwardConeResident,
|
||||
*FString::SanitizeFloat(static_cast<float>(NearForwardWeightedDesired)),
|
||||
*FString::SanitizeFloat(static_cast<float>(NearForwardWeightedResident)),
|
||||
TraceHeading.IsNearlyZero() ? 0 : 1,
|
||||
*PendingCounts, *CoveredCounts,
|
||||
WalkableRingReadyCount,
|
||||
GetActiveGenerationTaskCount(), GetGenerationAdmissionCount(), MaxTasks, NearTaskReserve,
|
||||
GVoxelForgeStreamingCoarseFirst, GVoxelForgeStreamingDynamicNearReserve,
|
||||
|
||||
Reference in new issue
Block a user