streaming: restore same-level refill; early admission on by default; fixed UE harness (L172)
Scheduler stable-sorts the whole level and checks capacity per admission (refill when slots free mid-pass); regression test with the old partial-sort path as negative control. Harness repoints Source/Standalone/uplugin together, records root HEAD/status, builds baselines from fresh worktrees, gates on DWARF struct sizes (480/480/480). Frame-resolution arrival: A/A p90 36.8 ms; fixed-on vs fixed-off -12.2 ms median (4/10 slower). 30 m/s: near 43.3 -> 55.3%, far holes 1.8 -> 0%, p99 <= 7.46 ms, worst <= 12.3 ms. Defaults: StreamingFastTravelWaitForAllCriticalTiles=0, StreamingFastTravelApplyBudgetMilliseconds=1.0, StreamingIncrementalDesiredSet=1, CoreRegionConcurrentAdmission=1, StreamingFastTravelForwardFirst=0, StreamingRebuildOnCoreRegionCompletion=1, StreamingCoreDesiredCacheByCenter=0, audits 0. Source/: VoxelWorld.cpp, VoxelStreamingAdmissionOrder.h (new). 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
9d088ef96c
commit
52cb3d7a23
7 files changed
+1149
-24
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,
|
||||
@@ -14265,17 +14266,14 @@ 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 (LevelSubmissionCount > 0 && bUseForwardFirstOrder)
|
||||
if (bUseForwardFirstOrder)
|
||||
{
|
||||
SortNearTilesForwardFirst(LevelTiles);
|
||||
}
|
||||
else if (LevelSubmissionCount > 0)
|
||||
else
|
||||
{
|
||||
FVector Heading = GetActorTransform().InverseTransformVectorNoScale(PlayerHeading);
|
||||
if (Heading.IsNearlyZero() && IsValid(PlayerPawn))
|
||||
@@ -14288,12 +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;
|
||||
// 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(),
|
||||
// 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(
|
||||
@@ -14348,7 +14344,6 @@ 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)
|
||||
{
|
||||
@@ -14358,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(
|
||||
|
||||
Reference in new issue
Block a user