wip(streaming): LOD rings tile exactly once, cache race, reconcile 104x -- gate stress FAILS 3/4

- Clipmap: finer rings snap to child-pair boundaries, so no two desired tiles of different
  levels overlap. Owner config 635 overlapping pairs -> 0; LOD0 ring 343 -> 512 tiles.
  New test sweeps 6,174 cases; fails on 7486c29 as intended, passes here.
- Shared room-graph cache: Entry->Cache read/written only under the mutex (race fix).
- ReconcileReadyTransitionVisibility indexed: moving p95 32.0 ms -> 0.31 ms.
- Block-vs-scalar density equivalence test: 5.1M samples bit-identical; catches an injected
  optimistic EffectOverBox (5,612 mismatches).

UNVALIDATED for merge: collision-gate stress passed 1 of 4 runs on this code (fails at 4.56 m,
support collision unready at start); it passed on 7486c29. No control run yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-09-25 02:47:54 +02:00
1 parent 7486c299d4
commit 85a803b873
6 files changed
+1300 -51

No files matched your search

+213 -26
View File
@@ -592,6 +592,8 @@ void AVoxelWorld::RegenerateAllChunks()
bAllChunksLoaded = false;
DesiredSorted.Reset();
CriticalDesiredTiles.Reset();
DesiredTransitionDescendants.Reset();
DesiredTransitionMaxLevel = 0;
DesiredStamped.Reset();
++DesiredEpoch;
TransitionHold.Reset();
@@ -1421,6 +1423,7 @@ void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason)
VoxelForgeStartupTrace::Finish(TEXT("end_play_before_steady_state"));
}
LogReadyTransitionVisibilityMeasure();
Super::EndPlay(EndPlayReason);
}
@@ -1429,6 +1432,17 @@ void AVoxelWorld::BeginPlay()
VoxelForgeStartupTrace::BeginFromCommandLine();
VoxelForgeStartupTrace::FStageScope StartupTraceStage(TEXT("BeginPlay"));
Super::BeginPlay();
int32 MeasureReadyTransitionVisibility = 0;
FParse::Value(FCommandLine::Get(),
TEXT("voxel.MeasureReadyTransitionVisibility="), MeasureReadyTransitionVisibility);
bMeasureReadyTransitionVisibility = MeasureReadyTransitionVisibility != 0;
ReadyTransitionVisibilitySamples.Reset();
FarFieldSubmitSamples.Reset();
if (bMeasureReadyTransitionVisibility)
{
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeReadyVisibilityMeasure] enabled=1 instrumentation=command_line"));
}
VoxelCaveMorphology::ConfigurePlayerFitMemoFromCommandLine();
VoxelCaveMorphology::ResetPlayerFitMemoStats();
bShuttingDown.store(false, std::memory_order_relaxed);
@@ -3724,6 +3738,90 @@ void AVoxelWorld::LogVisualStalenessAudit()
VisualAuditLODOverlapMaxPairs);
}
void AVoxelWorld::LogReadyTransitionVisibilityMeasure()
{
if (!bMeasureReadyTransitionVisibility)
{
return;
}
if (!ReadyTransitionVisibilitySamples.IsEmpty())
{
TArray<FReadyTransitionVisibilitySample> Sorted = ReadyTransitionVisibilitySamples;
Sorted.Sort([](const FReadyTransitionVisibilitySample& A,
const FReadyTransitionVisibilitySample& B)
{
return A.Cycles < B.Cycles;
});
const int32 P50Index = Sorted.Num() / 2;
const int32 P95Index = FMath::Clamp(
FMath::CeilToInt(static_cast<float>(Sorted.Num()) * 0.95f) - 1,
0, Sorted.Num() - 1);
const FReadyTransitionVisibilitySample& P95 = Sorted[P95Index];
const FReadyTransitionVisibilitySample& Max = Sorted.Last();
int32 MinComponents = MAX_int32, MaxComponents = 0;
int32 MinDesired = MAX_int32, MaxDesired = 0;
int64 TotalComponents = 0, TotalDesired = 0;
for (const FReadyTransitionVisibilitySample& Sample : Sorted)
{
MinComponents = FMath::Min(MinComponents, Sample.TileComponentCount);
MaxComponents = FMath::Max(MaxComponents, Sample.TileComponentCount);
MinDesired = FMath::Min(MinDesired, Sample.DesiredTileCount);
MaxDesired = FMath::Max(MaxDesired, Sample.DesiredTileCount);
TotalComponents += Sample.TileComponentCount;
TotalDesired += Sample.DesiredTileCount;
}
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeReadyVisibilityMeasure] samples=%d p50_ms=%.6f p95_ms=%.6f max_ms=%.6f "
"p95_components=%d p95_desired=%d components_range=%d..%d desired_range=%d..%d "
"components_mean=%.1f desired_mean=%.1f"),
Sorted.Num(),
FPlatformTime::ToSeconds64(Sorted[P50Index].Cycles) * 1000.0,
FPlatformTime::ToSeconds64(P95.Cycles) * 1000.0,
FPlatformTime::ToSeconds64(Max.Cycles) * 1000.0,
P95.TileComponentCount, P95.DesiredTileCount,
MinComponents, MaxComponents, MinDesired, MaxDesired,
static_cast<double>(TotalComponents) / Sorted.Num(),
static_cast<double>(TotalDesired) / Sorted.Num());
}
if (!FarFieldSubmitSamples.IsEmpty())
{
TArray<FFarFieldSubmitSample> Sorted = FarFieldSubmitSamples;
Sorted.Sort([](const FFarFieldSubmitSample& A, const FFarFieldSubmitSample& B)
{
return A.Cycles < B.Cycles;
});
const int32 P50Index = Sorted.Num() / 2;
const int32 P95Index = FMath::Clamp(
FMath::CeilToInt(static_cast<float>(Sorted.Num()) * 0.95f) - 1,
0, Sorted.Num() - 1);
const FFarFieldSubmitSample& P95 = Sorted[P95Index];
const FFarFieldSubmitSample& Max = Sorted.Last();
int32 MinDesired = MAX_int32, MaxDesired = 0;
int32 MinLevel = MAX_int32, MaxLevel = 0;
for (const FFarFieldSubmitSample& Sample : Sorted)
{
MinDesired = FMath::Min(MinDesired, Sample.DesiredTileCount);
MaxDesired = FMath::Max(MaxDesired, Sample.DesiredTileCount);
MinLevel = FMath::Min(MinLevel, Sample.MaxDesiredLevel);
MaxLevel = FMath::Max(MaxLevel, Sample.MaxDesiredLevel);
}
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeFarFieldSubmitMeasure] samples=%d p50_ms=%.6f p95_ms=%.6f max_ms=%.6f "
"p95_desired=%d p95_max_level=%d desired_range=%d..%d levels=%d..%d"),
Sorted.Num(),
FPlatformTime::ToSeconds64(Sorted[P50Index].Cycles) * 1000.0,
FPlatformTime::ToSeconds64(P95.Cycles) * 1000.0,
FPlatformTime::ToSeconds64(Max.Cycles) * 1000.0,
P95.DesiredTileCount, P95.MaxDesiredLevel,
MinDesired, MaxDesired, MinLevel, MaxLevel);
}
}
void AVoxelWorld::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
@@ -3932,9 +4030,9 @@ void AVoxelWorld::Tick(float DeltaTime)
}
// Complete the one-shot diagnostic request only after the player's level-0 centre tile is
// loaded and idle. Waiting for any coarse tile would take the async-neighbour branch and would
// not measure the normal synchronous centre remesh that makes a local edit feel expensive.
// No pawn is a distinct state from a pawn at (0,0,0); keep the diagnostic fallback for
// loaded and idle. The edit fixture selects an already-visible level-0 geometry tile, so it
// measures the normal synchronous centre remesh without depending on a coarse overlay being
// visible. No pawn is distinct from a pawn at (0,0,0); retain the actor-location fallback for
// headless/editor use without making the world-origin pawn skip streaming.
FVector ModificationPosition = bHasPlayer ? PlayerLastPos : GetActorLocation();
FVoxelTileKey ModificationCenterTile(
@@ -3962,17 +4060,8 @@ void AVoxelWorld::Tick(float DeltaTime)
}
if (GVoxelForgeTestModificationLoadedGeometryTarget)
{
bool bHasVisibleCoarseOverlay = false;
for (const TPair<FVoxelTileKey, URealtimeMeshComponent*>& Pair : TileComponents)
{
if (Pair.Key.Level > 0 && Pair.Value && Pair.Value->IsVisible()
&& VF_TileFootprintsOverlap(CandidateTile, Pair.Key))
{
bHasVisibleCoarseOverlay = true;
break;
}
}
if (!bHasVisibleCoarseOverlay)
URealtimeMeshComponent* CandidateComponent = TileComponents.FindRef(CandidateTile);
if (!CandidateComponent || !CandidateComponent->IsVisible())
{
continue;
}
@@ -5575,12 +5664,32 @@ void AVoxelWorld::HideOverlappingDesiredVisuals(const FVoxelTileKey& RetiredTile
void AVoxelWorld::ReconcileReadyTransitionVisibility()
{
const bool bMeasure = bMeasureReadyTransitionVisibility;
const uint64 MeasureStartCycles = bMeasure ? FPlatformTime::Cycles64() : 0;
const int32 ComponentCountAtStart = bMeasure ? TileComponents.Num() : 0;
const int32 DesiredCountAtStart = bMeasure ? DesiredSorted.Num() : 0;
const auto RecordMeasure = [this, bMeasure, MeasureStartCycles,
ComponentCountAtStart, DesiredCountAtStart]()
{
if (!bMeasure)
{
return;
}
FReadyTransitionVisibilitySample& Sample =
ReadyTransitionVisibilitySamples.AddDefaulted_GetRef();
Sample.Cycles = FPlatformTime::Cycles64() - MeasureStartCycles;
Sample.TileComponentCount = ComponentCountAtStart;
Sample.DesiredTileCount = DesiredCountAtStart;
};
if (bAllChunksLoaded && TransitionHold.Num() == 0 && PendingUnload.Num() == 0)
{
RecordMeasure();
return;
}
if (DesiredSorted.Num() == 0 || TileComponents.Num() == 0)
{
RecordMeasure();
return;
}
@@ -5590,6 +5699,11 @@ void AVoxelWorld::ReconcileReadyTransitionVisibility()
// queue below.
TArray<FVoxelTileKey> ReadyToHide;
ReadyToHide.Reserve(TileComponents.Num());
auto GetAncestorAtLevel = [](const FVoxelTileKey& Tile, int32 AncestorLevel)
{
const int32 Divisor = 1 << (AncestorLevel - Tile.Level);
return FVoxelTileKey(VoxelClipmapDesiredTiles::FloorDiv(Tile.Coord, Divisor), AncestorLevel);
};
for (const TPair<FVoxelTileKey, URealtimeMeshComponent*>& Pair : TileComponents)
{
const FVoxelTileKey& OldTile = Pair.Key;
@@ -5606,9 +5720,12 @@ void AVoxelWorld::ReconcileReadyTransitionVisibility()
bool bHasReplacement = false;
bool bAllReplacementsLoaded = true;
for (const FVoxelTileKey& NewTile : DesiredSorted)
// A coarser desired tile can only overlap this tile by containing it. Test those exact
// ancestors directly, then test desired finer tiles from the prebuilt ancestor index.
for (int32 Level = OldTile.Level + 1; Level <= DesiredTransitionMaxLevel; ++Level)
{
if (!VF_TileFootprintsOverlap(OldTile, NewTile))
const FVoxelTileKey NewTile = GetAncestorAtLevel(OldTile, Level);
if (!IsDesired(NewTile))
{
continue;
}
@@ -5619,6 +5736,21 @@ void AVoxelWorld::ReconcileReadyTransitionVisibility()
break;
}
}
if (bAllReplacementsLoaded)
{
if (const TArray<FVoxelTileKey>* NewDescendants = DesiredTransitionDescendants.Find(OldTile))
{
for (const FVoxelTileKey& NewTile : *NewDescendants)
{
bHasReplacement = true;
if (!LoadedTiles.Contains(NewTile))
{
bAllReplacementsLoaded = false;
break;
}
}
}
}
if (bHasReplacement && bAllReplacementsLoaded)
{
ReadyToHide.Add(OldTile);
@@ -5634,6 +5766,26 @@ void AVoxelWorld::ReconcileReadyTransitionVisibility()
// only part of the transition. Reveal them only after every overlapping retired component has
// entered PendingUnload (and was therefore hidden above). Collision visibility is independent:
// the component remains queryable until the budgeted UnloadTile call.
TSet<FVoxelTileKey> VisibleRetiredTiles;
TSet<FVoxelTileKey> VisibleRetiredAncestors;
int32 MaxVisibleRetiredLevel = DesiredTransitionMaxLevel;
for (const TPair<FVoxelTileKey, URealtimeMeshComponent*>& OldPair : TileComponents)
{
const FVoxelTileKey& OldTile = OldPair.Key;
URealtimeMeshComponent* OldComp = OldPair.Value;
if (IsDesired(OldTile) || PendingUnload.Contains(OldTile)
|| !OldComp || !OldComp->IsVisible())
{
continue;
}
VisibleRetiredTiles.Add(OldTile);
MaxVisibleRetiredLevel = FMath::Max(MaxVisibleRetiredLevel, OldTile.Level);
for (int32 Level = OldTile.Level + 1; Level <= DesiredTransitionMaxLevel; ++Level)
{
VisibleRetiredAncestors.Add(GetAncestorAtLevel(OldTile, Level));
}
}
for (const TPair<FVoxelTileKey, URealtimeMeshComponent*>& NewPair : TileComponents)
{
const FVoxelTileKey& NewTile = NewPair.Key;
@@ -5643,19 +5795,13 @@ void AVoxelWorld::ReconcileReadyTransitionVisibility()
continue;
}
bool bBlockedByVisibleRetiredTile = false;
for (const TPair<FVoxelTileKey, URealtimeMeshComponent*>& OldPair : TileComponents)
bool bBlockedByVisibleRetiredTile = VisibleRetiredAncestors.Contains(NewTile);
for (int32 Level = NewTile.Level + 1;
!bBlockedByVisibleRetiredTile && Level <= MaxVisibleRetiredLevel; ++Level)
{
const FVoxelTileKey& OldTile = OldPair.Key;
if (IsDesired(OldTile) || PendingUnload.Contains(OldTile)
|| !OldPair.Value || !OldPair.Value->IsVisible())
{
continue;
}
if (VF_TileFootprintsOverlap(NewTile, OldTile))
if (VisibleRetiredTiles.Contains(GetAncestorAtLevel(NewTile, Level)))
{
bBlockedByVisibleRetiredTile = true;
break;
}
}
if (!bBlockedByVisibleRetiredTile)
@@ -5663,6 +5809,8 @@ void AVoxelWorld::ReconcileReadyTransitionVisibility()
NewComp->SetVisibility(true);
}
}
RecordMeasure();
}
// Integer floor-division (correct for negatives), scalar + vector.
@@ -5855,6 +6003,36 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
}
}
// Build a dyadic ancestor index once per desired-set epoch. Different-level tile overlap is
// containment because every tile footprint is power-of-two aligned. This lets the per-frame
// transition pass inspect only actual overlapping candidates.
DesiredTransitionDescendants.Reset();
DesiredTransitionMaxLevel = 0;
for (const FVoxelTileKey& Key : DesiredSorted)
{
DesiredTransitionMaxLevel = FMath::Max(DesiredTransitionMaxLevel, Key.Level);
}
for (const TPair<FVoxelTileKey, URealtimeMeshComponent*>& Pair : TileComponents)
{
DesiredTransitionMaxLevel = FMath::Max(DesiredTransitionMaxLevel, Pair.Key.Level);
}
// A worker from the previous desired epoch can finish late with a component at a level no
// longer present in TileComponents when this index is built. Index through the supported
// maximum sheet level (MaxClipLevel 8 + FarSheetSpanLevels 4) so that such a retired tile still
// finds all of its desired descendants when the result is reconciled.
constexpr int32 MaxSupportedTileLevel = 12;
const int32 DescendantIndexMaxLevel = FMath::Max(
DesiredTransitionMaxLevel, MaxSupportedTileLevel);
for (const FVoxelTileKey& Key : DesiredSorted)
{
for (int32 Level = Key.Level + 1; Level <= DescendantIndexMaxLevel; ++Level)
{
const int32 Divisor = 1 << (Level - Key.Level);
const FVoxelTileKey Ancestor(VF_FloorDiv(Key.Coord, Divisor), Level);
DesiredTransitionDescendants.FindOrAdd(Ancestor).Add(Key);
}
}
if (VoxelForgeStartupTrace::IsActive())
{
int32 LOD0Count = 0;
@@ -6433,6 +6611,8 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
// FAR FIELD — preserve the clipmap's level ordering explicitly. DesiredSorted is
// distance-sorted for the critical/near work, but a level pass prevents a level-5 horizon
// tile from being admitted ahead of an unfinished level-1/2 bridge tile.
const uint64 FarFieldMeasureStartCycles = bMeasureReadyTransitionVisibility
? FPlatformTime::Cycles64() : 0;
int32 MaxDesiredLevel = 0;
for (const FVoxelTileKey& T : DesiredSorted)
{
@@ -6451,6 +6631,13 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
}
if (PendingTiles.Num() >= CoarseTaskCap) break;
}
if (bMeasureReadyTransitionVisibility)
{
FFarFieldSubmitSample& Sample = FarFieldSubmitSamples.AddDefaulted_GetRef();
Sample.Cycles = FPlatformTime::Cycles64() - FarFieldMeasureStartCycles;
Sample.DesiredTileCount = DesiredSorted.Num();
Sample.MaxDesiredLevel = MaxDesiredLevel;
}
// Bande de strate changée : re-gen budgétée des tuiles grossières concernées (le vieux
// mesh reste visible jusqu'au résultat — même schéma que RemeshDirtyChunks).