perf(generation): exact reach gating for the tunnel core (per tile) and passage carving (per 8^3 block)

Applies ce4d878's decide-once-then-prove pattern to the two largest exact targets from the re-rank
(tunnel core 20.8%, passage carving 9.1%). Codex; reviewed by me.

Measured first, where the time is:
  core world:      far 223,860 vs near 35,515,979 cycles   -> tile gate only; the core is
                                                             near-dominated (~99%)
  core tail:       far 363,440 vs near 20,110,219
  passage, tile:   far 7,315,426 vs near 30,168,796        -> refined to an 8^3 block gate
  passage, 8^3:    far 11,233,384 vs near 26,250,838
The core already uses the tunnel spatial index. An exact skip cannot remove its cost: that cost is
in tiles near tunnels. The remaining lever there is the field-changing SDF/core merge (next).

- Core reach per tile, from lattice plus halo: swept capsule reach, floor band, floor-profile
  deviation, relief bound, room-mouth ownership. The GetDensityAt tail is identity unless
  bSupportFloor, bRoomFloor or CoreSDF < -inset.
- Passage carving per 8^3 block: capsule-chain and landing-room domains, the carve threshold and
  the SmoothMin K/6 margin. Malformed descriptors stay candidates (conservative).
- voxel.TileReachCostDiagnostics (default 0) for the far/near cost split.

Soundness: the final-field debug comparison ran 130,285,889 comparisons with 0 differences; shrunk
reach (0.5) produced 140,035 differences (60,098 in the final field) and failed as required.
Field exact: canonical 07C14005 / b3e5f4c3..9b377, 64/64 per-tile ledger identical, capability
20,830 / 10,909, strate-crossing and sealed-proof tests pass.

Final DEFAULT build vs the committed-tip DLL (same command line, two interleaved runs each):
  static  worker 28.84 -> 28.10 s (-2.6%)  LOD0 p50 53.3 -> 51.5 ms  p95 94.4 -> 93.1 ms
  moving  worker 61.39 -> 60.61 s (-1.3%)  LOD0 p50 50.9 -> 49.7 ms  p95 92.21 -> 92.27 ms (flat,
                                                                        +0.06%, within noise)
Maintenance note: these reach proofs must track tunnel/passage geometry. The next round adds an
automation test for the final-field comparison so a geometry change cannot silently break them.
Ledger: 15 builds (one C2065 fixed, one access-denied path retry); rejected TileOnly/BlockLookup
variants were slower; one hung diagnostic (a non-owner PID) was stopped; wrapper quoting failures
launched no editor. No crash folders.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TaFdXhTdYMkW1xrV6ApT6Y
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-15 11:30:37 +02:00
1 parent 0208e93606
commit a668833ef5
8 files changed
+1062 -30

No files matched your search

@@ -7914,10 +7914,20 @@ FTunnelCoreWorldEvaluation VoxelCaveMorphology::EvaluateTunnelCoreWorld(
const FTunnelSupportFloorColumn* SupportColumn, const FTunnelSupportFloorColumn* SupportColumn,
bool bUseSpatialIndex) bool bUseSpatialIndex)
{ {
VoxelGenLOD::FScopedReachCost ReachCost(
VoxelGenLOD::ETileReachCostKind::TunnelCoreWorld);
if (VoxelDensityAblation::IsTunnelCoreOff()) if (VoxelDensityAblation::IsTunnelCoreOff())
{ {
return FTunnelCoreWorldEvaluation(); return FTunnelCoreWorldEvaluation();
} }
// GenerateMesh installs the exact tile/block reach mask after building the complete tile
// cache. Point queries retain the canonical all-reachable TLS default; debug proof calls set
// the bypass bit so the same sample can be evaluated as the reference field.
if (!VoxelGenLOD::IsTilePostReachBypassActive()
&& !VoxelGenLOD::IsTunnelCoreReachable())
{
return FTunnelCoreWorldEvaluation();
}
VoxelDensityProfile::FScopedTimer ProfileTimer( VoxelDensityProfile::FScopedTimer ProfileTimer(
VoxelDensityProfile::EBucket::TunnelCoreWorld); VoxelDensityProfile::EBucket::TunnelCoreWorld);
const FVector Pos(WorldX, WorldY, WorldZ); const FVector Pos(WorldX, WorldY, WorldZ);
@@ -8042,6 +8052,318 @@ FTunnelCoreWorldEvaluation VoxelCaveMorphology::EvaluateTunnelCoreWorld(
return Result; return Result;
} }
bool VoxelCaveMorphology::AnyTunnelCoreWorldNearLattice(
const FChunkSDFCache& Cache,
const FBox& VoxelBox,
float ReachScale)
{
if (VoxelDensityAblation::IsTunnelCoreOff())
{
return false;
}
if (!VoxelBox.IsValid
|| !VoxelMath::IsFinite(ReachScale) || ReachScale <= 0.0f)
{
return true;
}
const auto TouchesSphere = [&VoxelBox](const FVector& Center, float Radius) -> bool
{
if (!VoxelMath::IsFinite(Center.X)
|| !VoxelMath::IsFinite(Center.Y)
|| !VoxelMath::IsFinite(Center.Z)
|| !VoxelMath::IsFinite(Radius) || Radius < 0.0f)
{
return true;
}
const FVector Closest(
FMath::Clamp(Center.X, (float)VoxelBox.Min.X, (float)VoxelBox.Max.X),
FMath::Clamp(Center.Y, (float)VoxelBox.Min.Y, (float)VoxelBox.Max.Y),
FMath::Clamp(Center.Z, (float)VoxelBox.Min.Z, (float)VoxelBox.Max.Z));
return FVector::DistSquared(Closest, Center) <= Radius * Radius;
};
const auto TouchesExpandedAabb = [&VoxelBox](
const FVector& Min, const FVector& Max, float Pad) -> bool
{
if (!VoxelMath::IsFinite(Min.X) || !VoxelMath::IsFinite(Min.Y)
|| !VoxelMath::IsFinite(Min.Z) || !VoxelMath::IsFinite(Max.X)
|| !VoxelMath::IsFinite(Max.Y) || !VoxelMath::IsFinite(Max.Z)
|| !VoxelMath::IsFinite(Pad) || Pad < 0.0f)
{
return true;
}
const float DX = FMath::Max3(
static_cast<float>(VoxelBox.Min.X) - static_cast<float>(Max.X),
static_cast<float>(Min.X) - static_cast<float>(VoxelBox.Max.X), 0.0f);
const float DY = FMath::Max3(
static_cast<float>(VoxelBox.Min.Y) - static_cast<float>(Max.Y),
static_cast<float>(Min.Y) - static_cast<float>(VoxelBox.Max.Y), 0.0f);
const float DZ = FMath::Max3(
static_cast<float>(VoxelBox.Min.Z) - static_cast<float>(Max.Z),
static_cast<float>(Min.Z) - static_cast<float>(VoxelBox.Max.Z), 0.0f);
return DX <= Pad && DY <= Pad && DZ <= Pad;
};
const float Fade = FMath::Max(Cache.SDFBlendRadius, 0.0f) * 0.35f;
if (!VoxelMath::IsFinite(Fade))
{
return true;
}
for (const FCachedTunnel& Tunnel : Cache.Tunnels)
{
const bool bHasWorldChain = Tunnel.WorldControlPoints.Num() >= 2
&& Tunnel.WorldControlRadii.Num() == Tunnel.WorldControlPoints.Num();
const FVector& BoundCenter = bHasWorldChain
? Tunnel.WorldBoundCenter : Tunnel.BoundCenter;
const float BoundRadiusSq = bHasWorldChain
? Tunnel.WorldBoundRadiusSq : Tunnel.BoundRadiusSq;
const float InfluenceRadius = bHasWorldChain
? Tunnel.WorldInfluenceRadius : Tunnel.SDFInfluenceRadius;
const FVector& CenterlineMin = bHasWorldChain
? Tunnel.WorldCenterlineMin : Tunnel.SDFCenterlineMin;
const FVector& CenterlineMax = bHasWorldChain
? Tunnel.WorldCenterlineMax : Tunnel.SDFCenterlineMax;
if (!VoxelMath::IsFinite(BoundCenter.X)
|| !VoxelMath::IsFinite(BoundCenter.Y)
|| !VoxelMath::IsFinite(BoundCenter.Z)
|| !VoxelMath::IsFinite(BoundRadiusSq) || BoundRadiusSq < 0.0f
|| !VoxelMath::IsFinite(InfluenceRadius) || InfluenceRadius < 0.0f
|| !VoxelMath::IsFinite(CenterlineMin.X)
|| !VoxelMath::IsFinite(CenterlineMin.Y)
|| !VoxelMath::IsFinite(CenterlineMin.Z)
|| !VoxelMath::IsFinite(CenterlineMax.X)
|| !VoxelMath::IsFinite(CenterlineMax.Y)
|| !VoxelMath::IsFinite(CenterlineMax.Z))
{
return true;
}
if (bHasWorldChain)
{
for (int32 Index = 0; Index < Tunnel.WorldControlPoints.Num(); ++Index)
{
const FVector& Point = Tunnel.WorldControlPoints[Index];
if (!VoxelMath::IsFinite(Point.X) || !VoxelMath::IsFinite(Point.Y)
|| !VoxelMath::IsFinite(Point.Z)
|| !VoxelMath::IsFinite(Tunnel.WorldControlRadii[Index]))
{
return true;
}
}
}
// VF_TunnelFloorRelief uses the same bounded noise as the floor profile. Use the
// profile-independent maximum scale (1.0) here: the normal authored scale is <= .20,
// but this remains sound for an old or malformed profile that bypassed that clamp.
float ReliefBound = 0.0f;
if (Tunnel.FloorReliefStrength > 0.0f)
{
if (!VoxelMath::IsFinite(Tunnel.FloorReliefStrength)
|| !VoxelMath::IsFinite(Tunnel.FloorReliefFrequency))
{
return true;
}
ReliefBound = FMath::Abs(Tunnel.FloorReliefStrength)
* VOXEL_NOISE_SCALE * 1.01f;
if (!VoxelMath::IsFinite(ReliefBound))
{
return true;
}
}
float MouthReach = 0.0f;
float MouthFloorDelta = 0.0f;
if (bHasWorldChain && Tunnel.bHasFloorRoomOwnership)
{
MouthReach = FMath::Max(
FMath::Max(Tunnel.WorldMouthBlendRadiusA, Tunnel.WorldMouthBlendRadiusB),
0.0f) + Fade + 1.0f;
if (!VoxelMath::IsFinite(MouthReach))
{
return true;
}
const float NaturalA = static_cast<float>(Tunnel.WorldControlPoints[0].Z)
- FMath::Abs(Tunnel.WorldControlRadii[0]);
const int32 Last = Tunnel.WorldControlPoints.Num() - 1;
const float NaturalB = static_cast<float>(Tunnel.WorldControlPoints[Last].Z)
- FMath::Abs(Tunnel.WorldControlRadii[Last]);
if (!VoxelMath::IsFinite(NaturalA) || !VoxelMath::IsFinite(NaturalB))
{
return true;
}
if (Tunnel.WorldMouthFloorZA > -FLT_MAX
&& !VoxelMath::IsFinite(Tunnel.WorldMouthFloorZA))
{
return true;
}
if (Tunnel.WorldMouthFloorZB > -FLT_MAX
&& !VoxelMath::IsFinite(Tunnel.WorldMouthFloorZB))
{
return true;
}
if (Tunnel.WorldMouthFloorZA > -FLT_MAX)
{
MouthFloorDelta = FMath::Max(
MouthFloorDelta, FMath::Abs(Tunnel.WorldMouthFloorZA - NaturalA));
}
if (Tunnel.WorldMouthFloorZB > -FLT_MAX)
{
MouthFloorDelta = FMath::Max(
MouthFloorDelta, FMath::Abs(Tunnel.WorldMouthFloorZB - NaturalB));
}
}
// Tighten the construction sphere to the actual swept chain before falling back to the
// legacy bound. The evaluator tests a capsule segment, not the sphere around the whole
// wandering chain; using segment AABBs removes the long-chain false near result while
// retaining a conservative pad for radius, floor band, relief, and SmoothMax blend.
const TArray<FVector>& ChainPoints = bHasWorldChain
? Tunnel.WorldControlPoints : Tunnel.ControlPoints;
const TArray<float>& ChainRadii = bHasWorldChain
? Tunnel.WorldControlRadii : Tunnel.ControlRadii;
bool bHasTightChain = ChainPoints.Num() >= 2
&& ChainRadii.Num() == ChainPoints.Num();
bool bTightTouch = false;
if (bHasTightChain)
{
float MaxRadius = 0.0f;
for (int32 Index = 0; Index < ChainPoints.Num(); ++Index)
{
const FVector& Point = ChainPoints[Index];
if (!VoxelMath::IsFinite(Point.X) || !VoxelMath::IsFinite(Point.Y)
|| !VoxelMath::IsFinite(Point.Z)
|| !VoxelMath::IsFinite(ChainRadii[Index]))
{
bHasTightChain = false;
break;
}
MaxRadius = FMath::Max(MaxRadius, FMath::Abs(ChainRadii[Index]));
}
if (bHasTightChain)
{
float ProfileFloorDelta = 0.0f;
const TArray<FTunnelFloorSegmentProfile>& Profiles = bHasWorldChain
? Tunnel.WorldFloorProfiles : Tunnel.FloorProfiles;
if (Tunnel.bTunnelFloorEnabled
&& Profiles.Num() == ChainPoints.Num() - 1)
{
for (int32 SegmentIndex = 0;
SegmentIndex < Profiles.Num(); ++SegmentIndex)
{
const FTunnelFloorSegmentProfile& Profile = Profiles[SegmentIndex];
if (!VoxelMath::IsFinite(Profile.StartFloorZ)
|| !VoxelMath::IsFinite(Profile.EndFloorZ))
{
bHasTightChain = false;
break;
}
const float NaturalA = static_cast<float>(
ChainPoints[SegmentIndex].Z)
- FMath::Abs(ChainRadii[SegmentIndex]);
const float NaturalB = static_cast<float>(
ChainPoints[SegmentIndex + 1].Z)
- FMath::Abs(ChainRadii[SegmentIndex + 1]);
ProfileFloorDelta = FMath::Max(
ProfileFloorDelta,
FMath::Max(
FMath::Abs(Profile.StartFloorZ - NaturalA),
FMath::Abs(Profile.EndFloorZ - NaturalB)));
}
}
if (bHasTightChain)
{
const float FloorPad = Tunnel.bTunnelFloorEnabled
? FMath::Max(
VoxelPassageGeometry::LandingFloorThicknessVoxels,
FMath::Max(Cache.SDFBlendRadius, 0.0f) * 0.35f)
: 0.0f;
const float SegmentPad = MaxRadius + FloorPad
+ ProfileFloorDelta + ReliefBound;
if (!VoxelMath::IsFinite(SegmentPad))
{
return true;
}
for (int32 SegmentIndex = 0;
SegmentIndex + 1 < ChainPoints.Num(); ++SegmentIndex)
{
const FVector Min(
FMath::Min(ChainPoints[SegmentIndex].X,
ChainPoints[SegmentIndex + 1].X),
FMath::Min(ChainPoints[SegmentIndex].Y,
ChainPoints[SegmentIndex + 1].Y),
FMath::Min(ChainPoints[SegmentIndex].Z,
ChainPoints[SegmentIndex + 1].Z));
const FVector Max(
FMath::Max(ChainPoints[SegmentIndex].X,
ChainPoints[SegmentIndex + 1].X),
FMath::Max(ChainPoints[SegmentIndex].Y,
ChainPoints[SegmentIndex + 1].Y),
FMath::Max(ChainPoints[SegmentIndex].Z,
ChainPoints[SegmentIndex + 1].Z));
if (TouchesExpandedAabb(Min, Max, SegmentPad * ReachScale))
{
bTightTouch = true;
break;
}
}
if (!bTightTouch && Tunnel.bTunnelFloorEnabled
&& bHasWorldChain && Tunnel.bHasFloorRoomOwnership)
{
const float MouthHorizontal = MouthReach;
const float MouthVertical = FMath::Max(
MaxRadius + VoxelPassageGeometry::LandingFloorThicknessVoxels
+ ReliefBound,
MouthFloorDelta + VoxelPassageGeometry::LandingFloorThicknessVoxels
+ ReliefBound);
const float ScaledHorizontal = MouthHorizontal * ReachScale;
const float ScaledVertical = MouthVertical * ReachScale;
for (int32 Endpoint : {0, ChainPoints.Num() - 1})
{
const FVector& Point = ChainPoints[Endpoint];
if (TouchesExpandedAabb(
FVector(
static_cast<float>(Point.X) - ScaledHorizontal,
static_cast<float>(Point.Y) - ScaledHorizontal,
static_cast<float>(Point.Z) - ScaledVertical),
FVector(
static_cast<float>(Point.X) + ScaledHorizontal,
static_cast<float>(Point.Y) + ScaledHorizontal,
static_cast<float>(Point.Z) + ScaledVertical),
0.0f))
{
bTightTouch = true;
break;
}
}
}
}
}
}
if (bHasTightChain && !bTightTouch)
{
continue;
}
// The broad sphere already encloses the swept radius and finite floor band. Add the
// vertical relief and room-mouth ownership envelopes before asking whether the lattice
// box can touch it. A miss is therefore an identity proof for the core evaluator and
// its support/room-floor tail; its only field writes are bSupportFloor, bRoomFloor, or
// CoreSDF < -inset, all of which are inside this envelope.
const float Reach = (
FMath::Sqrt(FMath::Max(BoundRadiusSq, 0.0f))
+ ReliefBound + MouthReach + MouthFloorDelta) * ReachScale;
if (!VoxelMath::IsFinite(Reach)
|| TouchesSphere(BoundCenter, Reach))
{
return true;
}
}
return false;
}
float VoxelCaveMorphology::EvaluateTunnelCoreWorldSDF( float VoxelCaveMorphology::EvaluateTunnelCoreWorldSDF(
float WorldX, float WorldY, float WorldZ, float WorldX, float WorldY, float WorldZ,
const FChunkSDFCache& Cache, const FChunkSDFCache& Cache,
+120 -25
View File
@@ -2370,6 +2370,49 @@ namespace
return static_cast<int32>(Hash & (TunnelDensityCacheSlotCount - 1)); return static_cast<int32>(Hash & (TunnelDensityCacheSlotCount - 1));
} }
static void UpdateTunnelCoreTileReach(
const FChunkSDFCache& Cache,
bool bUseTileCacheWindow)
{
if (!bUseTileCacheWindow
|| !VoxelGenLOD::bTileReachDiagnosticTile
|| VoxelGenLOD::TileCellsPerAxis <= 0)
{
return;
}
const int32 Step = FMath::Max(VoxelGenLOD::GetThreadSampleStep(), 1);
const int64 Extent = static_cast<int64>(VoxelGenLOD::TileCellsPerAxis + 1)
* static_cast<int64>(Step);
if (Extent <= 0 || Extent > MAX_int32)
{
VoxelGenLOD::RecordTunnelCoreReachDecision(true);
return;
}
const FIntVector& Origin = VoxelGenLOD::TileOriginVoxels;
const FBox LatticeWithHalo(
FVector(
static_cast<float>(static_cast<int64>(Origin.X) - Step),
static_cast<float>(static_cast<int64>(Origin.Y) - Step),
static_cast<float>(static_cast<int64>(Origin.Z) - Step)),
FVector(
static_cast<float>(static_cast<int64>(Origin.X) + Extent),
static_cast<float>(static_cast<int64>(Origin.Y) + Extent),
static_cast<float>(static_cast<int64>(Origin.Z) + Extent)));
const bool bNear = VoxelCaveMorphology::AnyTunnelCoreWorldNearLattice(
Cache, LatticeWithHalo, VoxelGenLOD::TileReachScale);
if (bNear)
{
VoxelGenLOD::TilePostReachFlags |= VoxelGenLOD::TunnelCoreReachable;
}
else
{
VoxelGenLOD::TilePostReachFlags &= ~VoxelGenLOD::TunnelCoreReachable;
}
VoxelGenLOD::bTileCoreReachProofEnabled = true;
VoxelGenLOD::RecordTunnelCoreReachDecision(bNear);
}
void PrepareTunnelCoreCache( void PrepareTunnelCoreCache(
const UVoxelStrateManager& Manager, const UVoxelStrateManager& Manager,
const FIntVector& ChunkCoord, const FIntVector& ChunkCoord,
@@ -2398,6 +2441,14 @@ namespace
* static_cast<float>(CHUNK_SIZE) * VOXEL_SIZE); * static_cast<float>(CHUNK_SIZE) * VOXEL_SIZE);
const uint32 WorldSeed = static_cast<uint32>(Manager.GetWorldSeed()); const uint32 WorldSeed = static_cast<uint32>(Manager.GetWorldSeed());
// A tile-window cache contains the complete graph reach for this tile. Chunk-local
// caches deliberately leave the core bit conservative; updating it there would make the
// first chunk's false result hide a tunnel that enters through a later cache window.
if (bUseTileCacheWindow && OutState.bValid)
{
UpdateTunnelCoreTileReach(OutState.Cache, bUseTileCacheWindow);
}
const bool bSameGraphWindow = OutState.bValid const bool bSameGraphWindow = OutState.bValid
&& OutState.OwnerId == OwnerId && OutState.OwnerId == OwnerId
&& OutState.ManagerLifetimeId == ManagerLifetimeId && OutState.ManagerLifetimeId == ManagerLifetimeId
@@ -2457,6 +2508,7 @@ namespace
Params, WorldSeed, Params, WorldSeed,
StrateIndex, nullptr, StrateIndex, nullptr,
ERoomGraphBuildSite::GeneratorTunnelCore); ERoomGraphBuildSite::GeneratorTunnelCore);
UpdateTunnelCoreTileReach(OutState.Cache, bUseTileCacheWindow);
BuildTunnelSupportColumnsForChunk( BuildTunnelSupportColumnsForChunk(
OutState.Cache, ChunkCoord, bUseSpatialIndex); OutState.Cache, ChunkCoord, bUseSpatialIndex);
OutState.OwnerId = OwnerId; OutState.OwnerId = OwnerId;
@@ -2723,6 +2775,24 @@ void UVoxelGenerator::EndDensityBlock() const
float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) const float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) const
{ {
// Soundness mode: for a sample in a block whose core or passage-carving reach bit is clear,
// evaluate the complete canonical field once with the bypass bit set. The normal call then
// takes the fast path below and compares the final MC field after the boundary seal and diff
// layer—not an intermediate SDF. This is deliberately debug-only; production pays no
// recursive call and only the evaluator/tail flag reads.
const bool bCompareFinalField = VoxelGenLOD::bTilePostReachDebug
&& !VoxelGenLOD::IsTilePostReachBypassActive()
&& VoxelGenLOD::bTileReachDiagnosticTile
&& (!VoxelGenLOD::IsTunnelCoreReachable()
|| !VoxelGenLOD::IsPassageCarvingReachable());
float CanonicalFinalField = 0.0f;
if (bCompareFinalField)
{
const bool PreviousBypass = VoxelGenLOD::bTilePostReachBypass;
VoxelGenLOD::bTilePostReachBypass = true;
CanonicalFinalField = GetDensityAt(WorldX, WorldY, WorldZ);
VoxelGenLOD::bTilePostReachBypass = PreviousBypass;
}
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_GetDensityAt); TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_GetDensityAt);
VF_ParseFastIsFiniteSwitch(); VF_ParseFastIsFiniteSwitch();
VF_ParseFusedEvaluatorSwitch(); VF_ParseFusedEvaluatorSwitch();
@@ -3542,16 +3612,52 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
// bridge over a graph tunnel. Reassert the native cached tunnel core here, after every // bridge over a graph tunnel. Reassert the native cached tunnel core here, after every
// solid floor writer but before the global XY seal. This cache is built once per chunk, // solid floor writer but before the global XY seal. This cache is built once per chunk,
// never once per voxel. // never once per voxel.
VoxelGenLOD::FScopedReachCost TunnelCoreTailReachCost(
VoxelGenLOD::ETileReachCostKind::TunnelCoreTail);
const bool bTunnelCoreReachable = VoxelGenLOD::IsTunnelCoreReachable();
const bool bTunnelCoreBypass = VoxelGenLOD::IsTilePostReachBypassActive();
auto ApplyGraphLandingFloors = [&]()
{
// These are passage/origin landing posts, not tunnel-core writes. Keep them alive when
// the core bit is clear because their own per-tile reach bits may still be set.
{
VoxelDensityProfile::FScopedTimer ProfileTimer(
VoxelDensityProfile::EBucket::PassageLandingFloor);
VF_ApplyOriginLandingFloorMC(
Result, WorldX, WorldY, WorldZ,
CP_Dist.StrateTopWorldZ, CP_Dist.StrateBottomWorldZ,
CP_Dist.BoundarySealThickness, LandingBaseDensity, OriginSpineRadius);
}
{
VoxelDensityProfile::FScopedTimer ProfileTimer(
VoxelDensityProfile::EBucket::PassageLandingFloor);
StrateManager->ApplyPassageLandingFloorMC(
Result, WorldX, WorldY, WorldZ, LandingBaseDensity);
}
{
VoxelDensityProfile::FScopedTimer ProfileTimer(
VoxelDensityProfile::EBucket::PassageLandingRoomFloor);
StrateManager->ApplyPassageLandingRoomFloorMC(
Result, WorldX, WorldY, WorldZ, LandingBaseDensity);
}
};
if (!VoxelDensityAblation::IsTunnelCoreOff() if (!VoxelDensityAblation::IsTunnelCoreOff()
&& (ActiveTunnelCoreCache->bValid || CP_UseOpStack)) && (ActiveTunnelCoreCache->bValid || CP_UseOpStack))
{ {
FTunnelCoreWorldEvaluation TunnelCore; FTunnelCoreWorldEvaluation TunnelCore;
bool bHaveTunnelCore = bHavePreDisturbanceTunnelCore; bool bHaveTunnelCore = bHavePreDisturbanceTunnelCore;
if (!bTunnelCoreReachable && !bTunnelCoreBypass)
{
// The fused/source hand-off is also empty on the skipped path. Do not let the
// default object turn this into a second tail evaluation.
bHaveTunnelCore = false;
}
if (bHaveTunnelCore) if (bHaveTunnelCore)
{ {
TunnelCore = PreDisturbanceTunnelCore; TunnelCore = PreDisturbanceTunnelCore;
} }
if (!bHaveTunnelCore && ActiveTunnelCoreCache->bValid) if (!bHaveTunnelCore && ActiveTunnelCoreCache->bValid
&& (bTunnelCoreReachable || bTunnelCoreBypass))
{ {
const FTunnelSupportFloorColumn* SupportColumn = nullptr; const FTunnelSupportFloorColumn* SupportColumn = nullptr;
FTunnelSupportFloorColumn EmptySupportColumn; FTunnelSupportFloorColumn EmptySupportColumn;
@@ -3631,30 +3737,10 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
FMath::Max(LandingBaseDensity * 2.0f, 1.0f)); FMath::Max(LandingBaseDensity * 2.0f, 1.0f));
} }
// The graph tunnel can overlap an inter-strate landing at a room mouth. Its air }
// backstop is allowed to reopen the tunnel, but the landing's proved support floor if (bHaveTunnelCore || (!bTunnelCoreReachable && !bTunnelCoreBypass))
// must own the final floor band; otherwise the graph post can erase the only support {
// surface at the mouth and leave the player-fit graph with a disconnected pocket. ApplyGraphLandingFloors();
{
VoxelDensityProfile::FScopedTimer ProfileTimer(
VoxelDensityProfile::EBucket::PassageLandingFloor);
VF_ApplyOriginLandingFloorMC(
Result, WorldX, WorldY, WorldZ,
CP_Dist.StrateTopWorldZ, CP_Dist.StrateBottomWorldZ,
CP_Dist.BoundarySealThickness, LandingBaseDensity, OriginSpineRadius);
}
{
VoxelDensityProfile::FScopedTimer ProfileTimer(
VoxelDensityProfile::EBucket::PassageLandingFloor);
StrateManager->ApplyPassageLandingFloorMC(
Result, WorldX, WorldY, WorldZ, LandingBaseDensity);
}
{
VoxelDensityProfile::FScopedTimer ProfileTimer(
VoxelDensityProfile::EBucket::PassageLandingRoomFloor);
StrateManager->ApplyPassageLandingRoomFloorMC(
Result, WorldX, WorldY, WorldZ, LandingBaseDensity);
}
} }
} }
// Inter-strate passages own the same final D-floor contract as graph tunnels. This is // Inter-strate passages own the same final D-floor contract as graph tunnels. This is
@@ -3750,6 +3836,14 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
VoxelDensityProfile::FScopedTimer DensityTailTimer( VoxelDensityProfile::FScopedTimer DensityTailTimer(
VoxelDensityProfile::EBucket::DensityTail); VoxelDensityProfile::EBucket::DensityTail);
VoxelPassageGeometry::ResetVerticalShaftConnectorAirMarker(); VoxelPassageGeometry::ResetVerticalShaftConnectorAirMarker();
if (bCompareFinalField)
{
const bool bFinalFieldDifferent =
!VoxelGenLOD::SameFloatBits(Result, CanonicalFinalField);
VoxelGenLOD::RecordSkippedPostComparison(
VoxelGenLOD::ETilePostComparisonKind::FinalField,
bFinalFieldDifferent);
}
return Result; return Result;
} }
@@ -4089,6 +4183,7 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
TerrainOps, TerrainOps,
ERoomGraphBuildSite::GeneratorTile ERoomGraphBuildSite::GeneratorTile
); );
UpdateTunnelCoreTileReach(SDFCache, bUseTileCacheWindow);
CachedSMinX = SMinX; CachedSMaxX = SMaxX; CachedSMinX = SMinX; CachedSMaxX = SMaxX;
CachedSMinY = SMinY; CachedSMaxY = SMaxY; CachedSMinY = SMinY; CachedSMaxY = SMaxY;
@@ -33,6 +33,12 @@ namespace
TEXT("Scale the per-tile post reach for deliberate soundness tests. Default 1.0 is exact; " TEXT("Scale the per-tile post reach for deliberate soundness tests. Default 1.0 is exact; "
"values below 1 intentionally shrink the guard.")); "values below 1 intentionally shrink the guard."));
int32 GVoxelForgeTileReachCostDiagnostics = 0;
FAutoConsoleVariableRef CVarVoxelForgeTileReachCostDiagnostics(
TEXT("voxel.TileReachCostDiagnostics"),
GVoxelForgeTileReachCostDiagnostics,
TEXT("Measure core/passage density cost split by tile reach; no field gate."));
bool GVoxelForgeTilePostReachSwitchesParsed = false; bool GVoxelForgeTilePostReachSwitchesParsed = false;
void VF_ParseTilePostReachSwitches() void VF_ParseTilePostReachSwitches()
@@ -44,18 +50,24 @@ namespace
GVoxelForgeTilePostReachSwitchesParsed = true; GVoxelForgeTilePostReachSwitchesParsed = true;
int32 DebugValue = GVoxelForgeTilePostReachDebug; int32 DebugValue = GVoxelForgeTilePostReachDebug;
float ScaleValue = GVoxelForgeTilePostReachScale; float ScaleValue = GVoxelForgeTilePostReachScale;
int32 CostDiagnosticsValue = GVoxelForgeTileReachCostDiagnostics;
FParse::Value( FParse::Value(
FCommandLine::Get(), TEXT("voxel.TilePostReachDebug="), DebugValue); FCommandLine::Get(), TEXT("voxel.TilePostReachDebug="), DebugValue);
FParse::Value( FParse::Value(
FCommandLine::Get(), TEXT("voxel.TilePostReachScale="), ScaleValue); FCommandLine::Get(), TEXT("voxel.TilePostReachScale="), ScaleValue);
FParse::Value(
FCommandLine::Get(), TEXT("voxel.TileReachCostDiagnostics="), CostDiagnosticsValue);
GVoxelForgeTilePostReachDebug = DebugValue != 0 ? 1 : 0; GVoxelForgeTilePostReachDebug = DebugValue != 0 ? 1 : 0;
if (!VoxelMath::IsFinite(ScaleValue) || ScaleValue <= 0.0f) if (!VoxelMath::IsFinite(ScaleValue) || ScaleValue <= 0.0f)
{ {
ScaleValue = 1.0f; ScaleValue = 1.0f;
} }
GVoxelForgeTilePostReachScale = FMath::Min(ScaleValue, 1.0f); GVoxelForgeTilePostReachScale = FMath::Min(ScaleValue, 1.0f);
GVoxelForgeTileReachCostDiagnostics = CostDiagnosticsValue != 0 ? 1 : 0;
VoxelGenLOD::GTilePostReachDebugEnabled.store( VoxelGenLOD::GTilePostReachDebugEnabled.store(
GVoxelForgeTilePostReachDebug != 0, std::memory_order_relaxed); GVoxelForgeTilePostReachDebug != 0, std::memory_order_relaxed);
VoxelGenLOD::GTileReachCostDiagnosticsEnabled.store(
GVoxelForgeTileReachCostDiagnostics != 0, std::memory_order_relaxed);
} }
void VF_ParseOperatorBlockSwitch() void VF_ParseOperatorBlockSwitch()
@@ -284,6 +296,23 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
GVoxelForgeTilePostReachDebug != 0); GVoxelForgeTilePostReachDebug != 0);
TGuardValue<bool> TilePostReachBypassGuard( TGuardValue<bool> TilePostReachBypassGuard(
VoxelGenLOD::bTilePostReachBypass, false); VoxelGenLOD::bTilePostReachBypass, false);
TGuardValue<bool> TileReachDiagnosticGuard(
VoxelGenLOD::bTileReachDiagnosticTile, true);
TGuardValue<bool> TileCoreReachProofGuard(
VoxelGenLOD::bTileCoreReachProofEnabled, false);
TGuardValue<bool> TileCoreReachDecisionGuard(
VoxelGenLOD::bTileCoreReachDecisionRecorded, false);
TGuardValue<float> TileReachScaleGuard(
VoxelGenLOD::TileReachScale, GVoxelForgeTilePostReachScale);
const float ReachScale = GVoxelForgeTilePostReachScale;
const int32 ReachBlocksPerAxis = FMath::DivideAndRoundUp(
CellsPerAxis, VoxelGenLOD::TileReachBlockCells);
TGuardValue<int32> TileReachBlocksGuard(
VoxelGenLOD::TileReachBlocksPerAxis, ReachBlocksPerAxis);
TGuardValue<int32> TileReachBlockIndexGuard(
VoxelGenLOD::TileReachBlockIndex, INDEX_NONE);
TGuardValue<bool> TileBlockReachValidGuard(
VoxelGenLOD::bTileBlockReachValid, false);
uint8 TilePostReachFlags = VoxelGenLOD::AllTilePostReach; uint8 TilePostReachFlags = VoxelGenLOD::AllTilePostReach;
if (Generator->StrateManager != nullptr) if (Generator->StrateManager != nullptr)
{ {
@@ -297,8 +326,9 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
static_cast<float>(static_cast<int64>(OriginVoxels.X) + Extent), static_cast<float>(static_cast<int64>(OriginVoxels.X) + Extent),
static_cast<float>(static_cast<int64>(OriginVoxels.Y) + Extent), static_cast<float>(static_cast<int64>(OriginVoxels.Y) + Extent),
static_cast<float>(static_cast<int64>(OriginVoxels.Z) + Extent))); static_cast<float>(static_cast<int64>(OriginVoxels.Z) + Extent)));
const float ReachScale = GVoxelForgeTilePostReachScale; // Core stays conservative until the tile-window tunnel cache is built. Passage carving
TilePostReachFlags = 0; // is manager-owned and can be decided immediately from the same lattice+halo box.
TilePostReachFlags = VoxelGenLOD::TunnelCoreReachable;
if (Generator->StrateManager->AnyOriginLandingNearLattice( if (Generator->StrateManager->AnyOriginLandingNearLattice(
LatticeWithHalo, OriginVoxels, Step, ReachScale)) LatticeWithHalo, OriginVoxels, Step, ReachScale))
{ {
@@ -309,6 +339,11 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
{ {
TilePostReachFlags |= VoxelGenLOD::PassageLandingReachable; TilePostReachFlags |= VoxelGenLOD::PassageLandingReachable;
} }
if (Generator->StrateManager->AnyPassageCarvingNearLattice(
LatticeWithHalo, OriginVoxels, Step, ReachScale))
{
TilePostReachFlags |= VoxelGenLOD::PassageCarvingReachable;
}
if (Generator->StrateManager->AnyPassageStructuralPostNearLattice( if (Generator->StrateManager->AnyPassageStructuralPostNearLattice(
LatticeWithHalo, OriginVoxels, Step, ReachScale)) LatticeWithHalo, OriginVoxels, Step, ReachScale))
{ {
@@ -317,6 +352,30 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
} }
TGuardValue<uint8> TilePostReachFlagsGuard( TGuardValue<uint8> TilePostReachFlagsGuard(
VoxelGenLOD::TilePostReachFlags, TilePostReachFlags); VoxelGenLOD::TilePostReachFlags, TilePostReachFlags);
for (int32 BlockZ = 0; BlockZ < ReachBlocksPerAxis; ++BlockZ)
{
for (int32 BlockY = 0; BlockY < ReachBlocksPerAxis; ++BlockY)
{
for (int32 BlockX = 0; BlockX < ReachBlocksPerAxis; ++BlockX)
{
const int32 BlockIndex =
((BlockZ * ReachBlocksPerAxis) + BlockY) * ReachBlocksPerAxis + BlockX;
uint8 BlockFlags = TilePostReachFlags;
if (Generator->StrateManager != nullptr
&& (TilePostReachFlags & VoxelGenLOD::PassageCarvingReachable) != 0
&& !Generator->StrateManager->AnyPassageCarvingNearLattice(
VoxelGenLOD::MakeTileReachBlockBox(
OriginVoxels, Step, CellsPerAxis,
BlockX, BlockY, BlockZ),
OriginVoxels, Step, ReachScale))
{
BlockFlags &= ~VoxelGenLOD::PassageCarvingReachable;
}
VoxelGenLOD::TileBlockReachFlags[BlockIndex] = BlockFlags;
}
}
}
VoxelGenLOD::bTileBlockReachValid = true;
VoxelGenLOD::RecordTileReachMask(TilePostReachFlags); VoxelGenLOD::RecordTileReachMask(TilePostReachFlags);
// COUPE DE CONTENU PAR STRATE — restreint le maillage (et l'échantillonnage) aux cellules // COUPE DE CONTENU PAR STRATE — restreint le maillage (et l'échantillonnage) aux cellules
// dont l'intervalle Z chevauche la bande [BandZMinVox, BandZMaxVox] (voxels inclusifs). // dont l'intervalle Z chevauche la bande [BandZMinVox, BandZMaxVox] (voxels inclusifs).
@@ -660,6 +719,15 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
else else
{ {
// World voxel = tile origin + grid offset scaled by the cell size (Step). // World voxel = tile origin + grid offset scaled by the cell size (Step).
const int32 ReachBlockX = FMath::Clamp(gx, 0, CellsPerAxis - 1)
/ VoxelGenLOD::TileReachBlockCells;
const int32 ReachBlockY = FMath::Clamp(gy, 0, CellsPerAxis - 1)
/ VoxelGenLOD::TileReachBlockCells;
const int32 ReachBlockZ = FMath::Clamp(gz, 0, CellsPerAxis - 1)
/ VoxelGenLOD::TileReachBlockCells;
VoxelGenLOD::TileReachBlockIndex =
((ReachBlockZ * ReachBlocksPerAxis) + ReachBlockY)
* ReachBlocksPerAxis + ReachBlockX;
DensityGrid[LocalIndex] = Generator->GetDensityAt( DensityGrid[LocalIndex] = Generator->GetDensityAt(
OriginVoxels.X + gx * Step, OriginVoxels.X + gx * Step,
OriginVoxels.Y + gy * Step, OriginVoxels.Y + gy * Step,
@@ -2506,10 +2506,21 @@ void UVoxelStrateManager::GeneratePassages()
float UVoxelStrateManager::EvaluateModifierSDF(float WorldX, float WorldY, float WorldZ) const float UVoxelStrateManager::EvaluateModifierSDF(float WorldX, float WorldY, float WorldZ) const
{ {
VoxelGenLOD::FScopedReachCost ReachCost(
VoxelGenLOD::ETileReachCostKind::PassageCarving);
if (VoxelDensityAblation::IsPassageCarvingOff()) if (VoxelDensityAblation::IsPassageCarvingOff())
{ {
return FLT_MAX; return FLT_MAX;
} }
// GenerateMesh installs this once per tile/block. Outside the mesher the TLS defaults to
// the canonical all-reachable state, and debug canonical calls set the bypass bit explicitly.
// A miss is an identity for VF_ApplyPassageCarving, so do not even enter the spatial shortlist
// or capsule chain on a proven-far sample.
if (!VoxelGenLOD::IsTilePostReachBypassActive()
&& !VoxelGenLOD::IsPassageCarvingReachable())
{
return FLT_MAX;
}
// The landing terms are part of the passage modifier, so their reach proof is deliberately // The landing terms are part of the passage modifier, so their reach proof is deliberately
// wider than the direct post's four-voxel blend: SmoothMin can lower a finite term by K/6 at // wider than the direct post's four-voxel blend: SmoothMin can lower a finite term by K/6 at
@@ -5096,6 +5107,224 @@ bool UVoxelStrateManager::AnyPassageLandingNearLattice(
return false; return false;
} }
bool UVoxelStrateManager::AnyPassageCarvingNearLattice(
const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step,
float ReachScale) const
{
if (VoxelDensityAblation::IsPassageCarvingOff())
{
return false;
}
if (Step <= 0 || !VoxelBox.IsValid
|| !VoxelMath::IsFinite(ReachScale) || ReachScale <= 0.0f)
{
return true;
}
// BoundRadius is built around every capsule control point and both landing rooms. The
// construction already adds the ModSDF<4 carve band. SmoothMin can lower a chain of
// otherwise-far finite terms by at most K/6 per combine, so include that finite morphology
// loss as well. A miss of this sphere proves every capsule/landing term folds to the hard
// no-op band before VF_ApplyPassageCarving; malformed descriptors remain candidates.
int32 SmoothMinCombines = 0;
for (const FVoxelPassage& Passage : Passages)
{
if (!VoxelMath::IsFinite(Passage.BoundCenter.X)
|| !VoxelMath::IsFinite(Passage.BoundCenter.Y)
|| !VoxelMath::IsFinite(Passage.BoundCenter.Z)
|| !VoxelMath::IsFinite(Passage.BoundRadius)
|| !VoxelMath::IsFinite(Passage.BoundRadiusSq)
|| Passage.BoundRadius < 0.0f || Passage.BoundRadiusSq < 0.0f)
{
return true;
}
if (Passage.ControlPoints.Num() >= 2)
{
if (Passage.ControlPoints.Num() > 4096)
{
return true;
}
const bool bTaper = Passage.ControlRadii.Num() == Passage.ControlPoints.Num();
if (!bTaper && !VoxelMath::IsFinite(Passage.Radius))
{
return true;
}
for (int32 Index = 0; Index < Passage.ControlPoints.Num(); ++Index)
{
const FVector& Point = Passage.ControlPoints[Index];
if (!VoxelMath::IsFinite(Point.X) || !VoxelMath::IsFinite(Point.Y)
|| !VoxelMath::IsFinite(Point.Z)
|| (bTaper && !VoxelMath::IsFinite(Passage.ControlRadii[Index])))
{
return true;
}
}
SmoothMinCombines += FMath::Max(Passage.ControlPoints.Num() - 2, 0);
}
else
{
if (!VoxelMath::IsFinite(Passage.UpperPoint.X)
|| !VoxelMath::IsFinite(Passage.UpperPoint.Y)
|| !VoxelMath::IsFinite(Passage.UpperPoint.Z)
|| !VoxelMath::IsFinite(Passage.LowerPoint.X)
|| !VoxelMath::IsFinite(Passage.LowerPoint.Y)
|| !VoxelMath::IsFinite(Passage.LowerPoint.Z)
|| !VoxelMath::IsFinite(Passage.Radius))
{
return true;
}
}
if (Passage.bNativeFloorEnabled)
{
++SmoothMinCombines; // SmoothMax is the same K/6 polynomial bound.
}
SmoothMinCombines += 3; // passage union plus its two landing terms
const FVoxelPassageLanding* Landings[] = {
&Passage.UpperLanding, &Passage.LowerLanding };
for (const FVoxelPassageLanding* Landing : Landings)
{
if (!VoxelMath::IsFinite(Landing->StandingPoint.X)
|| !VoxelMath::IsFinite(Landing->StandingPoint.Y)
|| !VoxelMath::IsFinite(Landing->FloorZ)
|| !VoxelMath::IsFinite(Landing->CeilingZ)
|| !VoxelMath::IsFinite(Landing->HalfWidth)
|| !VoxelMath::IsFinite(Landing->FloorThickness))
{
return true;
}
}
}
constexpr float CarveThreshold = 4.0f;
constexpr float BlendK = 3.0f;
const float MorphologyPad = CarveThreshold
+ BlendK * static_cast<float>(FMath::Max(SmoothMinCombines, 0)) / 6.0f;
if (!VoxelMath::IsFinite(MorphologyPad) || MorphologyPad < 0.0f)
{
return true;
}
for (const FVoxelPassage& Passage : Passages)
{
bool bHasTightGeometry = false;
bool bTightTouch = false;
const float PassagePad = MorphologyPad * ReachScale;
if (!VoxelMath::IsFinite(PassagePad) || PassagePad < 0.0f)
{
return true;
}
if (Passage.ControlPoints.Num() >= 2)
{
bHasTightGeometry = Passage.ControlPoints.Num() <= 4096;
const bool bTaper = Passage.ControlRadii.Num() == Passage.ControlPoints.Num();
const float MaxRadius = bTaper
? [&Passage]()
{
float Result = 0.0f;
for (const float Radius : Passage.ControlRadii)
{
Result = FMath::Max(Result, FMath::Abs(Radius));
}
return Result;
}()
: FMath::Abs(Passage.Radius);
if (!VoxelMath::IsFinite(MaxRadius))
{
return true;
}
const float SegmentPad = (MaxRadius
+ (Passage.bNativeFloorEnabled
? VoxelPassageGeometry::LandingFloorThicknessVoxels : 0.0f)
+ PassagePad);
if (!VoxelMath::IsFinite(SegmentPad))
{
return true;
}
for (int32 SegmentIndex = 0;
bHasTightGeometry && SegmentIndex + 1 < Passage.ControlPoints.Num();
++SegmentIndex)
{
const FVector& A = Passage.ControlPoints[SegmentIndex];
const FVector& B = Passage.ControlPoints[SegmentIndex + 1];
if (VF_LatticeBoxTouchesAABB(
VoxelBox, LatticeOrigin, Step,
FMath::Min(A.X, B.X) - SegmentPad,
FMath::Max(A.X, B.X) + SegmentPad,
FMath::Min(A.Y, B.Y) - SegmentPad,
FMath::Max(A.Y, B.Y) + SegmentPad,
FMath::Min(A.Z, B.Z) - SegmentPad,
FMath::Max(A.Z, B.Z) + SegmentPad))
{
bTightTouch = true;
}
}
}
else
{
bHasTightGeometry = true;
const float SegmentPad = FMath::Abs(Passage.Radius)
+ (Passage.bNativeFloorEnabled
? VoxelPassageGeometry::LandingFloorThicknessVoxels : 0.0f)
+ PassagePad;
if (!VoxelMath::IsFinite(SegmentPad))
{
return true;
}
if (VF_LatticeBoxTouchesAABB(
VoxelBox, LatticeOrigin, Step,
FMath::Min(Passage.UpperPoint.X, Passage.LowerPoint.X) - SegmentPad,
FMath::Max(Passage.UpperPoint.X, Passage.LowerPoint.X) + SegmentPad,
FMath::Min(Passage.UpperPoint.Y, Passage.LowerPoint.Y) - SegmentPad,
FMath::Max(Passage.UpperPoint.Y, Passage.LowerPoint.Y) + SegmentPad,
FMath::Min(Passage.UpperPoint.Z, Passage.LowerPoint.Z) - SegmentPad,
FMath::Max(Passage.UpperPoint.Z, Passage.LowerPoint.Z) + SegmentPad))
{
bTightTouch = true;
}
}
const float RoomPad = PassagePad;
for (const FVoxelPassageLanding* Landing : {
&Passage.UpperLanding, &Passage.LowerLanding })
{
if (Landing->HalfWidth <= 0.0f || Landing->CeilingZ <= Landing->FloorZ)
{
continue;
}
constexpr float RoomRounding = 1.5f;
const float Height = Landing->CeilingZ - Landing->FloorZ;
const float HalfX = FMath::Max(Landing->HalfWidth - RoomRounding, 0.25f);
const float HalfZ = FMath::Max(Height * 0.5f - RoomRounding, 0.25f);
const float CenterZ = (Landing->FloorZ + Landing->CeilingZ) * 0.5f;
if (VF_LatticeBoxTouchesAABB(
VoxelBox, LatticeOrigin, Step,
Landing->StandingPoint.X - HalfX - RoomRounding - RoomPad,
Landing->StandingPoint.X + HalfX + RoomRounding + RoomPad,
Landing->StandingPoint.Y - HalfX - RoomRounding - RoomPad,
Landing->StandingPoint.Y + HalfX + RoomRounding + RoomPad,
FMath::Min(Landing->FloorZ,
CenterZ - HalfZ - RoomRounding) - RoomPad,
FMath::Max(Landing->CeilingZ,
CenterZ + HalfZ + RoomRounding) + RoomPad))
{
bTightTouch = true;
}
}
if (bHasTightGeometry && !bTightTouch)
{
continue;
}
const float Reach = (Passage.BoundRadius + MorphologyPad) * ReachScale;
if (!VoxelMath::IsFinite(Reach)
|| VF_LatticeBoxTouchesSphere(VoxelBox, Passage.BoundCenter, Reach))
{
return true;
}
}
return false;
}
bool UVoxelStrateManager::AnyPassageStructuralPostNearLattice( bool UVoxelStrateManager::AnyPassageStructuralPostNearLattice(
const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step, const FBox& VoxelBox, const FIntVector& LatticeOrigin, int32 Step,
float ReachScale) const float ReachScale) const
+104 -1
View File
@@ -1162,7 +1162,7 @@ void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason)
TEXT("[VoxelForgeTilePostReachKinds] comparisons=" TEXT("[VoxelForgeTilePostReachKinds] comparisons="
"origin_air=%llu/origin_floor=%llu/passage_sdf=%llu/landing_air=%llu/" "origin_air=%llu/origin_floor=%llu/passage_sdf=%llu/landing_air=%llu/"
"tunnel_air=%llu/landing_floor=%llu/structural=%llu/native_floor=%llu/room_floor=%llu " "tunnel_air=%llu/landing_floor=%llu/structural=%llu/native_floor=%llu/room_floor=%llu "
"differences=%llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu"), "final_field=%llu differences=%llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu"),
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[ static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::OriginAir)].load( static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::OriginAir)].load(
std::memory_order_relaxed)), std::memory_order_relaxed)),
@@ -1190,6 +1190,9 @@ void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason)
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[ static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingRoomFloor)].load( static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingRoomFloor)].load(
std::memory_order_relaxed)), std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostComparisonsByKind[
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::FinalField)].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[ static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::OriginAir)].load( static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::OriginAir)].load(
std::memory_order_relaxed)), std::memory_order_relaxed)),
@@ -1216,6 +1219,106 @@ void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason)
std::memory_order_relaxed)), std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[ static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingRoomFloor)].load( static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::PassageLandingRoomFloor)].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GSkippedPostDifferencesByKind[
static_cast<uint8>(VoxelGenLOD::ETilePostComparisonKind::FinalField)].load(
std::memory_order_relaxed)));
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTileReachDecisions] core_reachable=%llu core_skipped=%llu "
"passage_carving_reachable=%llu passage_carving_skipped=%llu"),
static_cast<unsigned long long>(VoxelGenLOD::GTunnelCoreReachableTiles.load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTunnelCoreSkippedTiles.load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GPassageCarvingReachableTiles.load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GPassageCarvingSkippedTiles.load(
std::memory_order_relaxed)));
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTileReachCost] core_world_far_calls=%llu far_cycles=%llu "
"near_calls=%llu near_cycles=%llu"),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreWorld)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreWorld)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreWorld)][1].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreWorld)][1].load(
std::memory_order_relaxed)));
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTileReachCost] core_tail_far_calls=%llu far_cycles=%llu "
"near_calls=%llu near_cycles=%llu"),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreTail)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreTail)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreTail)][1].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreTail)][1].load(
std::memory_order_relaxed)));
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTileReachCost] passage_carving_far_calls=%llu far_cycles=%llu "
"near_calls=%llu near_cycles=%llu"),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::PassageCarving)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::PassageCarving)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::PassageCarving)][1].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::PassageCarving)][1].load(
std::memory_order_relaxed)));
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTileBlockReachCost] core_world_far_calls=%llu far_cycles=%llu "
"near_calls=%llu near_cycles=%llu; core_tail_far_calls=%llu far_cycles=%llu "
"near_calls=%llu near_cycles=%llu; passage_far_calls=%llu far_cycles=%llu "
"near_calls=%llu near_cycles=%llu"),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreWorld)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreWorld)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreWorld)][1].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreWorld)][1].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreTail)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreTail)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreTail)][1].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::TunnelCoreTail)][1].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::PassageCarving)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::PassageCarving)][0].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCalls[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::PassageCarving)][1].load(
std::memory_order_relaxed)),
static_cast<unsigned long long>(VoxelGenLOD::GTileReachBlockCostCycles[
static_cast<uint8>(VoxelGenLOD::ETileReachCostKind::PassageCarving)][1].load(
std::memory_order_relaxed))); std::memory_order_relaxed)));
if (VoxelGenLOD::GTilePostReachDebugEnabled.load(std::memory_order_relaxed) if (VoxelGenLOD::GTilePostReachDebugEnabled.load(std::memory_order_relaxed)
&& TilePostReachDifferences != 0) && TilePostReachDifferences != 0)
@@ -1081,6 +1081,14 @@ namespace VoxelCaveMorphology
bool bUseSpatialIndex = true bool bUseSpatialIndex = true
); );
// Per-tile broad reach for the world-space core/floor post. The bound includes the swept
// chain, floor band, relief envelope, and the finite room-mouth ownership apron.
VOXELFORGE_API bool AnyTunnelCoreWorldNearLattice(
const FChunkSDFCache& Cache,
const FBox& VoxelBox,
float ReachScale = 1.0f
);
// True for the finite support slab beneath a world-space graph tunnel. The native density // True for the finite support slab beneath a world-space graph tunnel. The native density
// path and the operator stack use this same predicate before reopening the air core, keeping // path and the operator stack use this same predicate before reopening the air core, keeping
// the rounded capsule's bottom from becoming an unsupported point. // the rounded capsule's bottom from becoming an unsupported point.
@@ -476,6 +476,10 @@ public:
bool AnyPassageLandingNearLattice(const FBox& VoxelBox, bool AnyPassageLandingNearLattice(const FBox& VoxelBox,
const FIntVector& LatticeOrigin, int32 Step, const FIntVector& LatticeOrigin, int32 Step,
float ReachScale = 1.0f) const; float ReachScale = 1.0f) const;
/** Per-tile reach for the modifier itself: capsule chain, landing terms, and carve blend. */
bool AnyPassageCarvingNearLattice(const FBox& VoxelBox,
const FIntVector& LatticeOrigin, int32 Step,
float ReachScale = 1.0f) const;
/** Conservative sphere guard for all passage-owned structural posts and native D-floors. */ /** Conservative sphere guard for all passage-owned structural posts and native D-floors. */
bool AnyPassageStructuralPostNearLattice(const FBox& VoxelBox, bool AnyPassageStructuralPostNearLattice(const FBox& VoxelBox,
const FIntVector& LatticeOrigin, int32 Step, const FIntVector& LatticeOrigin, int32 Step,
+205 -2
View File
@@ -7,6 +7,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "HAL/PlatformTime.h"
#include <atomic> #include <atomic>
@@ -17,9 +18,14 @@ namespace VoxelGenLOD
OriginLandingReachable = 1u << 0, OriginLandingReachable = 1u << 0,
PassageLandingReachable = 1u << 1, PassageLandingReachable = 1u << 1,
PassageStructuralReachable= 1u << 2, PassageStructuralReachable= 1u << 2,
TunnelCoreReachable = 1u << 3,
PassageCarvingReachable = 1u << 4,
AllTilePostReach = OriginLandingReachable AllTilePostReach = OriginLandingReachable
| PassageLandingReachable | PassageLandingReachable
| PassageStructuralReachable, | PassageStructuralReachable
| TunnelCoreReachable
| PassageCarvingReachable,
AllTileReach = AllTilePostReach,
}; };
enum class ETilePostComparisonKind : uint8 enum class ETilePostComparisonKind : uint8
@@ -34,14 +40,65 @@ namespace VoxelGenLOD
PassageStructuralPosts, PassageStructuralPosts,
PassageNativeFloor, PassageNativeFloor,
PassageLandingRoomFloor, PassageLandingRoomFloor,
TunnelCore,
PassageCarving,
FinalField,
Count, Count,
}; };
enum class ETileReachCostKind : uint8
{
TunnelCoreWorld = 0,
TunnelCoreTail,
PassageCarving,
Count,
};
constexpr int32 TileReachBlockCells = 8;
constexpr int32 TileReachMaxBlocksPerAxis = 4;
constexpr int32 TileReachMaxBlockCount =
TileReachMaxBlocksPerAxis * TileReachMaxBlocksPerAxis * TileReachMaxBlocksPerAxis;
FORCEINLINE FBox MakeTileReachBlockBox(
const FIntVector& TileOrigin, int32 Step, int32 CellsPerAxis,
int32 BlockX, int32 BlockY, int32 BlockZ)
{
const int32 SafeStep = FMath::Max(Step, 1);
const int32 MinX = BlockX * TileReachBlockCells;
const int32 MinY = BlockY * TileReachBlockCells;
const int32 MinZ = BlockZ * TileReachBlockCells;
const int32 MaxX = FMath::Min(
CellsPerAxis, MinX + TileReachBlockCells);
const int32 MaxY = FMath::Min(
CellsPerAxis, MinY + TileReachBlockCells);
const int32 MaxZ = FMath::Min(
CellsPerAxis, MinZ + TileReachBlockCells);
return FBox(
FVector(
static_cast<float>(static_cast<int64>(TileOrigin.X) + MinX * SafeStep - SafeStep),
static_cast<float>(static_cast<int64>(TileOrigin.Y) + MinY * SafeStep - SafeStep),
static_cast<float>(static_cast<int64>(TileOrigin.Z) + MinZ * SafeStep - SafeStep)),
FVector(
static_cast<float>(static_cast<int64>(TileOrigin.X) + MaxX * SafeStep),
static_cast<float>(static_cast<int64>(TileOrigin.Y) + MaxY * SafeStep),
static_cast<float>(static_cast<int64>(TileOrigin.Z) + MaxZ * SafeStep)));
}
// A tile that is not being meshed, or a point query outside the mesher, must retain the // A tile that is not being meshed, or a point query outside the mesher, must retain the
// canonical behaviour. GenerateMesh scopes this to its computed mask. // canonical behaviour. GenerateMesh scopes this to its computed mask.
inline thread_local uint8 TilePostReachFlags = AllTilePostReach; inline thread_local uint8 TilePostReachFlags = AllTileReach;
inline thread_local bool bTilePostReachDebug = false; inline thread_local bool bTilePostReachDebug = false;
inline thread_local bool bTilePostReachBypass = false; inline thread_local bool bTilePostReachBypass = false;
inline thread_local bool bTileReachDiagnosticTile = false;
inline thread_local bool bTileCoreReachDecisionRecorded = false;
inline thread_local float TileReachScale = 1.0f;
inline thread_local int32 TileReachBlocksPerAxis = 0;
inline thread_local int32 TileReachBlockIndex = INDEX_NONE;
inline thread_local bool bTileBlockReachValid = false;
inline thread_local uint8 TileBlockReachFlags[TileReachMaxBlockCount]{};
// A false result is installed only after a cache was built with the whole mesher tile as its
// graph window. Chunk-local caches leave this true (the conservative no-skip fallback).
inline thread_local bool bTileCoreReachProofEnabled = false;
// These are diagnostics only. They never participate in a field decision. // These are diagnostics only. They never participate in a field decision.
inline std::atomic<bool> GTilePostReachDebugEnabled { false }; inline std::atomic<bool> GTilePostReachDebugEnabled { false };
@@ -52,12 +109,25 @@ namespace VoxelGenLOD
inline std::atomic<uint64> GOriginLandingSkippedTiles { 0 }; inline std::atomic<uint64> GOriginLandingSkippedTiles { 0 };
inline std::atomic<uint64> GPassageLandingSkippedTiles { 0 }; inline std::atomic<uint64> GPassageLandingSkippedTiles { 0 };
inline std::atomic<uint64> GPassageStructuralSkippedTiles { 0 }; inline std::atomic<uint64> GPassageStructuralSkippedTiles { 0 };
inline std::atomic<uint64> GTunnelCoreReachableTiles { 0 };
inline std::atomic<uint64> GTunnelCoreSkippedTiles { 0 };
inline std::atomic<uint64> GPassageCarvingReachableTiles { 0 };
inline std::atomic<uint64> GPassageCarvingSkippedTiles { 0 };
inline std::atomic<uint64> GSkippedPostComparisons { 0 }; inline std::atomic<uint64> GSkippedPostComparisons { 0 };
inline std::atomic<uint64> GSkippedPostDifferences { 0 }; inline std::atomic<uint64> GSkippedPostDifferences { 0 };
inline std::atomic<uint64> GSkippedPostComparisonsByKind[ inline std::atomic<uint64> GSkippedPostComparisonsByKind[
static_cast<uint8>(ETilePostComparisonKind::Count)]{}; static_cast<uint8>(ETilePostComparisonKind::Count)]{};
inline std::atomic<uint64> GSkippedPostDifferencesByKind[ inline std::atomic<uint64> GSkippedPostDifferencesByKind[
static_cast<uint8>(ETilePostComparisonKind::Count)]{}; static_cast<uint8>(ETilePostComparisonKind::Count)]{};
inline std::atomic<bool> GTileReachCostDiagnosticsEnabled { false };
inline std::atomic<uint64> GTileReachCostCalls[
static_cast<uint8>(ETileReachCostKind::Count)][2]{};
inline std::atomic<uint64> GTileReachCostCycles[
static_cast<uint8>(ETileReachCostKind::Count)][2]{};
inline std::atomic<uint64> GTileReachBlockCostCalls[
static_cast<uint8>(ETileReachCostKind::Count)][2]{};
inline std::atomic<uint64> GTileReachBlockCostCycles[
static_cast<uint8>(ETileReachCostKind::Count)][2]{};
FORCEINLINE bool IsTilePostReachable(uint8 Bit) FORCEINLINE bool IsTilePostReachable(uint8 Bit)
{ {
@@ -79,6 +149,25 @@ namespace VoxelGenLOD
return IsTilePostReachable(PassageStructuralReachable); return IsTilePostReachable(PassageStructuralReachable);
} }
FORCEINLINE bool IsTunnelCoreReachable()
{
// Core's measured far share is negligible; keep its exact tile decision and avoid a
// second, weaker block geometry approximation. Passage carving is the only block-gated
// work because its tight chain produced a material far share in the pre-gate measure.
return IsTilePostReachable(TunnelCoreReachable);
}
FORCEINLINE bool IsPassageCarvingReachable()
{
if (bTileBlockReachValid
&& TileReachBlockIndex >= 0
&& TileReachBlockIndex < TileReachMaxBlockCount)
{
return (TileBlockReachFlags[TileReachBlockIndex] & PassageCarvingReachable) != 0;
}
return IsTilePostReachable(PassageCarvingReachable);
}
FORCEINLINE bool IsTilePostReachBypassActive() FORCEINLINE bool IsTilePostReachBypassActive()
{ {
return bTilePostReachBypass; return bTilePostReachBypass;
@@ -111,8 +200,122 @@ namespace VoxelGenLOD
{ {
GPassageStructuralSkippedTiles.fetch_add(1, std::memory_order_relaxed); GPassageStructuralSkippedTiles.fetch_add(1, std::memory_order_relaxed);
} }
if ((Flags & PassageCarvingReachable) != 0)
{
GPassageCarvingReachableTiles.fetch_add(1, std::memory_order_relaxed);
}
else
{
GPassageCarvingSkippedTiles.fetch_add(1, std::memory_order_relaxed);
}
} }
FORCEINLINE void RecordTunnelCoreReachDecision(bool bReachable)
{
if (bTileCoreReachDecisionRecorded)
{
return;
}
bTileCoreReachDecisionRecorded = true;
if (bReachable)
{
GTunnelCoreReachableTiles.fetch_add(1, std::memory_order_relaxed);
}
else
{
GTunnelCoreSkippedTiles.fetch_add(1, std::memory_order_relaxed);
}
}
FORCEINLINE uint8 TileReachCostIndex(ETileReachCostKind Kind)
{
switch (Kind)
{
case ETileReachCostKind::TunnelCoreWorld:
case ETileReachCostKind::TunnelCoreTail:
return IsTilePostReachable(TunnelCoreReachable) ? 1u : 0u;
case ETileReachCostKind::PassageCarving:
return IsTilePostReachable(PassageCarvingReachable) ? 1u : 0u;
default:
return 1u;
}
}
FORCEINLINE uint8 BlockReachCostIndex(ETileReachCostKind Kind)
{
switch (Kind)
{
case ETileReachCostKind::TunnelCoreWorld:
case ETileReachCostKind::TunnelCoreTail:
return IsTunnelCoreReachable() ? 1u : 0u;
case ETileReachCostKind::PassageCarving:
return IsPassageCarvingReachable() ? 1u : 0u;
default:
return 1u;
}
}
FORCEINLINE void RecordTileReachBlockCost(
ETileReachCostKind Kind, uint8 ReachIndex, uint64 Cycles)
{
const uint8 KindIndex = static_cast<uint8>(Kind);
if (KindIndex < static_cast<uint8>(ETileReachCostKind::Count))
{
GTileReachBlockCostCalls[KindIndex][ReachIndex].fetch_add(
1, std::memory_order_relaxed);
GTileReachBlockCostCycles[KindIndex][ReachIndex].fetch_add(
Cycles, std::memory_order_relaxed);
}
}
/** Inclusive timing split used for the pre-gate near/far measurement only. */
struct FScopedReachCost
{
ETileReachCostKind Kind;
uint8 TileReachIndex = 1;
uint8 BlockReachIndex = 1;
uint64 StartCycles = 0;
bool bActive = false;
explicit FScopedReachCost(ETileReachCostKind InKind)
: Kind(InKind)
{
if (!bTileReachDiagnosticTile
|| !GTileReachCostDiagnosticsEnabled.load(std::memory_order_relaxed))
{
return;
}
TileReachIndex = TileReachCostIndex(Kind);
BlockReachIndex = BlockReachCostIndex(Kind);
StartCycles = FPlatformTime::Cycles64();
bActive = true;
}
~FScopedReachCost()
{
if (!bActive)
{
return;
}
const uint8 KindIndex = static_cast<uint8>(Kind);
if (KindIndex < static_cast<uint8>(ETileReachCostKind::Count))
{
const uint64 Cycles = FPlatformTime::Cycles64() - StartCycles;
GTileReachCostCalls[KindIndex][TileReachIndex].fetch_add(
1, std::memory_order_relaxed);
GTileReachCostCycles[KindIndex][TileReachIndex].fetch_add(
Cycles,
std::memory_order_relaxed);
if (bTileBlockReachValid
&& TileReachBlockIndex >= 0
&& TileReachBlockIndex < TileReachMaxBlockCount)
{
RecordTileReachBlockCost(Kind, BlockReachIndex, Cycles);
}
}
}
};
FORCEINLINE bool SameFloatBits(float A, float B) FORCEINLINE bool SameFloatBits(float A, float B)
{ {
uint32 ABits = 0; uint32 ABits = 0;