perf(streaming): prove sealed rock solid instead of sampling it -- crossing fix's cost recovered

4c8d06f stopped content-cutting LOD0 so the player can cross strates, at a cost of +29% static
worker CPU: LOD0 tiles in the sealed rock between strates, which the old cut skipped by accident,
were sampled in full and found solid. This proves those tiles solid instead (Codex; reviewed by me).

- UVoxelGenerator::TryProveSealedSolidTile: AllSolid only for a tile whose whole lattice lies in a
  gap/seal region. It rejects player mods (HasAnyModInChunkRange), passages plus carve pads, the
  origin spine/landing, disturbances, recipes and overrides. It is narrower than the old
  ClassifyTile, which stays off in streaming. CollisionSolidTiles / CollisionNotRequiredTiles
  bookkeeping is preserved, so the collision gate's support search still resolves.
- TryProveOutOfLayoutAirTile: a separate fail-closed constant-air proof for the zero-triangle tiles
  outside the strate layout. It never supplies collision support.
- voxel.SealedSolidProof (default 1). Mining a proven tile invalidates and re-meshes it.
- A soundness test that can fail (VoxelForgeSealedSolidProofTest): 22 candidates, 22 skips, 0
  non-solid lattice samples; a deliberately weakened diff guard produces 437 non-solid samples
  and fails.

Verified: mining tile (14,-18,-8) went from proof=1 / 0 triangles to proof=0 / 8 triangles and
re-meshed; strate-crossing test PASS (max gate 0.098 s); off/on trace 899/899 identical; export
b3e5f4c3..9b377; capability 20,830 / 10,909.

Two interleaved rounds, serialized (owner-editor count 0 before each launch):
                        baseline   crossing-fixed   + proof
  static  worker        35.5 s     41.9 s           36.0 s   (+1.4% vs baseline)
  moving  worker        78.1 s     93.2 s           78.2 s   (+0.1%)
  mesher calls static   18.078M    22.280M          18.078M
Applied triangles identical: 669,834 static / 1,331,482 moving.
Runs: one early automation probe exited 3 with no log (the corrected harness passed); one
exploratory moving run was interrupted and excluded.

Note: HasAnyModInChunkRange scans every modified chunk per candidate (cold audit #21). That is cheap
today (~20 candidates), but it grows with mining; it is queued in the correctness round.

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-14 22:08:24 +02:00
1 parent 481e7bbaee
commit 39c306b524
7 files changed
+1067 -15

No files matched your search

@@ -0,0 +1,179 @@
// Sealed-solid proof soundness and invalidation.
//
// The production shortcut is intentionally one-sided: it may return false too often, but a true
// result skips both meshing and level-0 collision generation. This test therefore brute-forces
// every tile that the fixture presents to the proof and treats any non-solid lattice sample as a
// hard failure. The second half deliberately disables the diff guard; a carve must then make the
// weakened proof look true while the same oracle finds an air sample.
#if WITH_DEV_AUTOMATION_TESTS
#include "Misc/AutomationTest.h"
#include "VoxelForgeTestFixture.h"
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FVoxelForgeSealedSolidProofTest,
"VoxelForge.Determinism.SealedSolidProofSoundness",
EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::EngineFilter)
namespace
{
struct FTileSpec
{
FIntVector Origin = FIntVector::ZeroValue;
int32 Step = 1;
int32 Cells = CHUNK_SIZE;
};
int32 CountNonSolidSamples(const UVoxelGenerator& Generator, const FTileSpec& Tile)
{
const int32 Cells = FMath::Clamp(Tile.Cells, 2, CHUNK_SIZE);
int32 NonSolid = 0;
for (int32 Z = 0; Z <= Cells; ++Z)
for (int32 Y = 0; Y <= Cells; ++Y)
for (int32 X = 0; X <= Cells; ++X)
{
const float Density = Generator.GetDensityAt(
static_cast<float>(Tile.Origin.X + X * Tile.Step),
static_cast<float>(Tile.Origin.Y + Y * Tile.Step),
static_cast<float>(Tile.Origin.Z + Z * Tile.Step));
if (!VoxelMath::IsFinite(Density) || !(Density < 0.0f))
{
++NonSolid;
}
}
return NonSolid;
}
void SetSealThickness(UVoxelStrateDefinition& Definition, float Thickness)
{
Definition.GenerationParams.BoundarySealThickness = Thickness;
Definition.SlabParams.BoundarySealThickness = Thickness;
Definition.MazeParams.BoundarySealThickness = Thickness;
Definition.SurfaceParams.BoundarySealThickness = Thickness;
Definition.VerticalShaftParams.BoundarySealThickness = Thickness;
Definition.FloatingIslandParams.BoundarySealThickness = Thickness;
}
}
bool FVoxelForgeSealedSolidProofTest::RunTest(const FString& Parameters)
{
using namespace VoxelForgeTest;
// Forty voxels makes a complete 32-cell level-0 tile fit inside a bottom boundary seal. The
// production asset remains untouched; this is a transient proof-only fixture configuration.
FTestWorld World;
World.Build(/*Seed*/1337, /*GapChunks*/2, /*bUseOperatorStack*/false,
/*InStrateHeightInChunks*/8);
if (!World.IsValid())
{
AddError(World.WhyInvalid());
return false;
}
for (const TStrongObjectPtr<UVoxelStrateDefinition>& Definition : World.Definitions)
{
if (Definition.IsValid())
{
SetSealThickness(*Definition.Get(), 40.0f);
}
}
World.Reinitialize();
const UVoxelGenerator* Generator = World.Generator.Get();
const FIntVector FarXY(4096, 4096, 0);
TArray<FTileSpec> CandidateTiles;
// Enumerate every gap chunk and every strate's bottom-seal tile in this fixture. No sample
// is selected after looking at the proof result: all candidates are offered to it first.
for (int32 ChunkZ = World.BottomChunkZ; ChunkZ <= World.TopChunkZ; ++ChunkZ)
{
if (World.StrateManager->IsGapChunk(FIntVector(0, 0, ChunkZ)))
{
CandidateTiles.Add({FIntVector(FarXY.X, FarXY.Y, ChunkZ * CHUNK_SIZE), 1, CHUNK_SIZE});
}
}
for (const FStrateSlot& Slot : World.StrateManager->GetLayout())
{
CandidateTiles.Add({
FIntVector(FarXY.X, FarXY.Y, Slot.BottomChunkZ * CHUNK_SIZE),
1, CHUNK_SIZE});
}
int32 NumProofSkips = 0;
int32 NumNonSolidSamples = 0;
int32 FirstProofTile = INDEX_NONE;
for (int32 Index = 0; Index < CandidateTiles.Num(); ++Index)
{
const FTileSpec& Tile = CandidateTiles[Index];
if (!Generator->TryProveSealedSolidTile(Tile.Origin, Tile.Step, Tile.Cells))
{
continue;
}
++NumProofSkips;
if (FirstProofTile == INDEX_NONE)
{
FirstProofTile = Index;
}
const int32 TileNonSolid = CountNonSolidSamples(*Generator, Tile);
NumNonSolidSamples += TileNonSolid;
if (TileNonSolid != 0)
{
AddError(FString::Printf(
TEXT("FALSE ALL-SOLID PROOF: candidate tile (%d,%d,%d) returned true but has "
"%d non-solid core-lattice samples."),
Tile.Origin.X, Tile.Origin.Y, Tile.Origin.Z, TileNonSolid));
}
}
AddInfo(FString::Printf(
TEXT("Sealed-solid proof brute force: %d candidate tiles, %d proof skips, %d non-solid "
"core-lattice samples."),
CandidateTiles.Num(), NumProofSkips, NumNonSolidSamples));
TestTrue(TEXT("the fixture exercised at least one sealed-solid proof skip"),
NumProofSkips > 0);
TestEqual(TEXT("every skipped tile has zero non-solid core-lattice samples"),
NumNonSolidSamples, 0);
if (FirstProofTile == INDEX_NONE)
{
return false;
}
// A carve in the proven region is the live-edit case that must invalidate the proof. The
// diff layer stores the modification in the tile's chunk, so this is also the exact guard used
// by the worker before it decides to skip meshing.
const FTileSpec& ProofTile = CandidateTiles[FirstProofTile];
const FVector CarveCenter(
static_cast<float>(ProofTile.Origin.X + 16),
static_cast<float>(ProofTile.Origin.Y + 16),
static_cast<float>(ProofTile.Origin.Z + 16));
FVoxelModification Carve;
Carve.Center = CarveCenter;
Carve.Radius = 6.0f;
Carve.Strength = -64.0f;
Carve.Shape = EVoxelBrushShape::Sphere;
TestTrue(TEXT("the adversarial carve was registered"),
World.DiffLayer->ApplyModification(Carve).Num() > 0);
TestFalse(TEXT("a player carve kills the sealed-solid proof"),
Generator->TryProveSealedSolidTile(
ProofTile.Origin, ProofTile.Step, ProofTile.Cells));
const uint8 DiffGuardDisabled = static_cast<uint8>(
EVoxelSealedSolidProofGuard::DiffLayer);
TestTrue(TEXT("the deliberately weakened proof still exposes the geometric candidate"),
Generator->TryProveSealedSolidTileForTest(
ProofTile.Origin, ProofTile.Step, ProofTile.Cells, DiffGuardDisabled));
const int32 WeakenedNonSolidSamples = CountNonSolidSamples(*Generator, ProofTile);
AddInfo(FString::Printf(
TEXT("Weakened proof (diff guard disabled): %d non-solid core-lattice samples after the "
"carve."),
WeakenedNonSolidSamples));
TestTrue(TEXT("the brute-force oracle fails the deliberately weakened guard"),
WeakenedNonSolidSamples > 0);
return true;
}
#endif // WITH_DEV_AUTOMATION_TESTS
+640 -1
View File
@@ -1600,8 +1600,624 @@ static FORCEINLINE bool VF_AnyDisturbanceCanTouchBox(
|| VF_AnyRidgeCanTouchBox(Box, D, Seed); || VF_AnyRidgeCanTouchBox(Box, D, Seed);
} }
static FORCEINLINE bool VF_SealedSolidProofGuardDisabled(
uint8 DisabledGuards, EVoxelSealedSolidProofGuard Guard)
{
return (DisabledGuards & static_cast<uint8>(Guard)) != 0;
}
static FORCEINLINE bool VF_IsSafeBoundarySealLatticeZ(
float WorldZ, float StrateTopZ, float StrateBottomZ,
float SealThickness, float BaseDensity)
{
// ApplyBoundarySeal is a positive internal-density lower bound. At an exact lattice point,
// the original inequalities are sufficient: a strictly positive seal factor times a finite,
// positive base density makes the final MC density negative. Do not widen the band here;
// false Mixed costs time, while the brute-force test below is the authority for this shortcut.
if (!VoxelMath::IsFinite(WorldZ)
|| !VoxelMath::IsFinite(StrateTopZ)
|| !VoxelMath::IsFinite(StrateBottomZ)
|| !VoxelMath::IsFinite(SealThickness)
|| !VoxelMath::IsFinite(BaseDensity)
|| !(StrateTopZ > StrateBottomZ)
|| !(SealThickness > 0.0f)
|| !(BaseDensity > 0.0f))
{
return false;
}
const float DistTop = StrateTopZ - WorldZ;
const float DistBottom = WorldZ - StrateBottomZ;
return (DistTop >= 0.0f && DistTop < SealThickness)
|| (DistBottom >= 0.0f && DistBottom < SealThickness);
}
static bool VF_SealedSolidProofPassageInputsAreValid(const UVoxelStrateManager& Manager)
{
// AnyPassageNearBox is intentionally a cheap bounding-sphere test. Its normal runtime inputs
// are generated data, but a malformed bound must fail closed: NaN distance comparisons would
// otherwise turn an unknown passage into a false "no passage" result.
for (const FVoxelPassage& Passage : Manager.GetPassages())
{
if (!VoxelMath::IsFinite(Passage.BoundCenter.X)
|| !VoxelMath::IsFinite(Passage.BoundCenter.Y)
|| !VoxelMath::IsFinite(Passage.BoundCenter.Z)
|| !VoxelMath::IsFinite(Passage.BoundRadius)
|| Passage.BoundRadius < 0.0f
|| !VoxelMath::IsFinite(Passage.Radius)
|| Passage.Radius < 0.0f)
{
return false;
}
for (const FVector& Point : Passage.ControlPoints)
{
if (!VoxelMath::IsFinite(Point.X)
|| !VoxelMath::IsFinite(Point.Y)
|| !VoxelMath::IsFinite(Point.Z))
{
return false;
}
}
for (const float Radius : Passage.ControlRadii)
{
if (!VoxelMath::IsFinite(Radius) || Radius < 0.0f)
{
return false;
}
}
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->StandingPoint.Z)
|| !VoxelMath::IsFinite(Landing->DoorPoint.X)
|| !VoxelMath::IsFinite(Landing->DoorPoint.Y)
|| !VoxelMath::IsFinite(Landing->DoorPoint.Z)
|| !VoxelMath::IsFinite(Landing->FloorZ)
|| !VoxelMath::IsFinite(Landing->CeilingZ)
|| !VoxelMath::IsFinite(Landing->HalfWidth)
|| !VoxelMath::IsFinite(Landing->FloorThickness)
|| Landing->HalfWidth < 0.0f
|| Landing->FloorThickness < 0.0f)
{
return false;
}
}
if (Passage.bWalkableTunnelContract
&& (Passage.ControlPoints.Num() < 2
|| Passage.ControlRadii.Num() != Passage.ControlPoints.Num()))
{
return false;
}
}
return true;
}
static bool VF_TryProveSealedSolidTile(
const UVoxelGenerator& Generator, const FIntVector& OriginVoxels,
int32 Step, int32 CellsPerAxis, uint8 DisabledGuards)
{
const UVoxelStrateManager* Manager = Generator.StrateManager;
if (Manager == nullptr)
{
return false;
}
Step = FMath::Max(1, Step);
const int32 CPA = FMath::Clamp(CellsPerAxis, 2, CHUNK_SIZE);
// Keep the integer arithmetic that defines the MC lattice identical to ClassifyTile, but
// reject a caller whose endpoint would overflow before any floor division or FBox creation.
const int64 MaxX64 = static_cast<int64>(OriginVoxels.X)
+ static_cast<int64>(CPA) * static_cast<int64>(Step);
const int64 MaxY64 = static_cast<int64>(OriginVoxels.Y)
+ static_cast<int64>(CPA) * static_cast<int64>(Step);
const int64 MaxZ64 = static_cast<int64>(OriginVoxels.Z)
+ static_cast<int64>(CPA) * static_cast<int64>(Step);
if (MaxX64 < MIN_int32 || MaxX64 > MAX_int32
|| MaxY64 < MIN_int32 || MaxY64 > MAX_int32
|| MaxZ64 < MIN_int32 || MaxZ64 > MAX_int32)
{
return false;
}
const int32 MinX = OriginVoxels.X;
const int32 MinY = OriginVoxels.Y;
const int32 MinZ = OriginVoxels.Z;
const int32 MaxX = static_cast<int32>(MaxX64);
const int32 MaxY = static_cast<int32>(MaxY64);
const int32 MaxZ = static_cast<int32>(MaxZ64);
const FBox TileVoxelBox(
FVector(static_cast<float>(MinX), static_cast<float>(MinY), static_cast<float>(MinZ)),
FVector(static_cast<float>(MaxX), static_cast<float>(MaxY), static_cast<float>(MaxZ)));
auto FloorDivC = [](int32 A, int32 B) -> int32
{
const int32 Q = A / B;
const int32 R = A % B;
return (R != 0 && ((R < 0) != (B < 0))) ? Q - 1 : Q;
};
// First pass is deliberately Z-only. Most streaming tiles are neither gap nor seal, so they
// leave in this cheap loop without taking a diff lock, scanning passages, or copying params.
enum : uint8 { GapRow = 1u, SealRow = 2u };
int32 SampleChunkZ[CHUNK_SIZE + 1];
uint8 SampleKind[CHUNK_SIZE + 1];
bool bSawSeal = false;
bool bSawSolidRegion = false;
for (int32 G = 0; G <= CPA; ++G)
{
const int64 Z64 = static_cast<int64>(OriginVoxels.Z)
+ static_cast<int64>(G) * static_cast<int64>(Step);
const int32 Z = static_cast<int32>(Z64);
const int32 ChunkZ = FloorDivC(Z, CHUNK_SIZE);
SampleChunkZ[G] = ChunkZ;
if (Manager->IsGapChunk(FIntVector(0, 0, ChunkZ)))
{
SampleKind[G] = GapRow;
bSawSolidRegion = true;
continue;
}
int32 UnusedTopChunkZ = 0;
int32 UnusedBottomChunkZ = 0;
if (!Manager->GetStrateChunkZBounds(
ChunkZ, UnusedTopChunkZ, UnusedBottomChunkZ))
{
// Above/below the stack is open air, not a solid gap. A tile that touches it is not
// a sealed-solid candidate even if another row happens to be bedrock.
return false;
}
SampleKind[G] = SealRow;
bSawSeal = true;
bSawSolidRegion = true;
}
if (!bSawSolidRegion)
{
return false;
}
if (bSawSeal && VoxelDensityAblation::IsBoundarySealOff())
{
return false;
}
// Player edits are the final writer and can carve a tile that was structurally solid. This
// is intentionally checked only after the Z pre-pass: ordinary mixed tiles avoid the lock.
const int32 MinChunkX = FloorDivC(MinX, CHUNK_SIZE);
const int32 MaxChunkX = FloorDivC(MaxX, CHUNK_SIZE);
const int32 MinChunkY = FloorDivC(MinY, CHUNK_SIZE);
const int32 MaxChunkY = FloorDivC(MaxY, CHUNK_SIZE);
const int32 MinChunkZ = FloorDivC(MinZ, CHUNK_SIZE);
const int32 MaxChunkZ = FloorDivC(MaxZ, CHUNK_SIZE);
if (!VF_SealedSolidProofGuardDisabled(
DisabledGuards, EVoxelSealedSolidProofGuard::DiffLayer)
&& Generator.DiffLayer != nullptr
&& Generator.DiffLayer->HasAnyMods())
{
if (Generator.DiffLayer->HasAnyModInChunkRange(
FIntVector(MinChunkX, MinChunkY, MinChunkZ),
FIntVector(MaxChunkX, MaxChunkY, MaxChunkZ)))
{
return false;
}
}
// PassageCarving's public box guard includes the 4-voxel carve blend pad on top of each
// generated passage bound. Landing and structural-post air are also handled conservatively:
// if carving is ablated but another passage air writer remains active, reject any passage data
// rather than trusting an API whose historical early-out is specific to carving.
if (!VF_SealedSolidProofGuardDisabled(
DisabledGuards, EVoxelSealedSolidProofGuard::Passages))
{
const bool bPassageAirWriterActive =
!VoxelDensityAblation::IsPassageCarvingOff()
|| !VoxelDensityAblation::IsLandingPostsOff()
|| !VoxelDensityAblation::IsPassageStructuralPostsOff();
if (bPassageAirWriterActive)
{
if (!VF_SealedSolidProofPassageInputsAreValid(*Manager))
{
return false;
}
if (!VoxelDensityAblation::IsPassageCarvingOff())
{
if (Manager->AnyPassageNearBox(
TileVoxelBox.Min, TileVoxelBox.Max))
{
return false;
}
}
else if (Manager->GetPassages().Num() > 0)
{
// AnyPassageNearBox deliberately returns false when the carve stage is off. The
// remaining landing/tunnel posts have no equivalent box API, so this is the
// capability-first answer under that development ablation.
return false;
}
}
}
// The origin landing is the (0,0) spine's current room-shaped air writer. Its box guard is
// broader than the exact lattice guard used by ClassifyTile; false Mixed is cheap here.
if (!VF_SealedSolidProofGuardDisabled(
DisabledGuards, EVoxelSealedSolidProofGuard::OriginLanding)
&& (!VoxelDensityAblation::IsOriginSpineOff()
|| !VoxelDensityAblation::IsLandingPostsOff())
&& Manager->AnyOriginLandingNearBox(TileVoxelBox.Min, TileVoxelBox.Max))
{
return false;
}
const int64 XYChunkCount =
(static_cast<int64>(MaxChunkX) - static_cast<int64>(MinChunkX) + 1)
* (static_cast<int64>(MaxChunkY) - static_cast<int64>(MinChunkY) + 1);
const int64 ZChunkCount = static_cast<int64>(MaxChunkZ) - static_cast<int64>(MinChunkZ) + 1;
// This is a per-tile proof, not a second coarse mesher. A normal LOD0/LOD1 tile touches at
// most a handful of chunks. Refuse pathological caller boxes before a nested loop can turn
// the cheap path into a large param walk.
constexpr int64 MaxProofChunkCoordinates = 65536;
if (XYChunkCount <= 0 || ZChunkCount <= 0
|| XYChunkCount > MaxProofChunkCoordinates
|| XYChunkCount * ZChunkCount > MaxProofChunkCoordinates)
{
return false;
}
auto ResolveSealFields = [&](const FIntVector& ChunkCoord,
float& OutTop, float& OutBottom,
float& OutThickness, float& OutBase) -> bool
{
switch (Manager->GetGeneratorTypeForChunk(ChunkCoord))
{
case ECaveGeneratorType::FlatPlain:
case ECaveGeneratorType::CrystalChamber:
{
const FSlabGenerationParams P = Manager->GetSlabParamsForChunk(ChunkCoord);
OutTop = P.StrateTopWorldZ;
OutBottom = P.StrateBottomWorldZ;
OutThickness = P.BoundarySealThickness;
OutBase = P.BaseDensity;
return true;
}
case ECaveGeneratorType::Maze:
{
const FMazeGenerationParams P = Manager->GetMazeParamsForChunk(ChunkCoord);
OutTop = P.StrateTopWorldZ;
OutBottom = P.StrateBottomWorldZ;
OutThickness = P.BoundarySealThickness;
OutBase = P.BaseDensity;
return true;
}
case ECaveGeneratorType::SurfaceWorld:
{
const FSurfaceGenerationParams P = Manager->GetSurfaceParamsForChunk(ChunkCoord);
OutTop = P.StrateTopWorldZ;
OutBottom = P.StrateBottomWorldZ;
OutThickness = P.BoundarySealThickness;
OutBase = P.BaseDensity;
return true;
}
case ECaveGeneratorType::VerticalShafts:
{
const FVerticalShaftParams P = Manager->GetVerticalShaftParamsForChunk(ChunkCoord);
OutTop = P.StrateTopWorldZ;
OutBottom = P.StrateBottomWorldZ;
OutThickness = P.BoundarySealThickness;
OutBase = P.BaseDensity;
return true;
}
case ECaveGeneratorType::FloatingIslands:
{
const FFloatingIslandParams P = Manager->GetFloatingIslandParamsForChunk(ChunkCoord);
OutTop = P.StrateTopWorldZ;
OutBottom = P.StrateBottomWorldZ;
OutThickness = P.BoundarySealThickness;
OutBase = P.BaseDensity;
return true;
}
case ECaveGeneratorType::TunnelNetwork:
case ECaveGeneratorType::Underwater:
{
const FStrateGenerationParams P = Manager->GetGenerationParams(ChunkCoord);
OutTop = P.StrateTopWorldZ;
OutBottom = P.StrateBottomWorldZ;
OutThickness = P.BoundarySealThickness;
OutBase = P.BaseDensity;
return true;
}
default:
return false;
}
};
for (int32 ChunkZ = MinChunkZ; ChunkZ <= MaxChunkZ; ++ChunkZ)
{
bool bThisChunkHasSealRow = false;
for (int32 G = 0; G <= CPA; ++G)
{
if (SampleKind[G] == SealRow && SampleChunkZ[G] == ChunkZ)
{
bThisChunkHasSealRow = true;
break;
}
}
if (!bThisChunkHasSealRow)
{
continue;
}
for (int32 ChunkY = MinChunkY; ChunkY <= MaxChunkY; ++ChunkY)
for (int32 ChunkX = MinChunkX; ChunkX <= MaxChunkX; ++ChunkX)
{
const FIntVector ChunkCoord(ChunkX, ChunkY, ChunkZ);
#if WITH_EDITOR
// A plain composer override changes the archetype/params, while a region override
// changes the lateral field. Both must remain on the ordinary mesher/classifier path.
int32 ComposerSeed = 0;
ECaveGeneratorType ComposerArchetype = ECaveGeneratorType::TunnelNetwork;
FVoxelStrateArchetypeParams ComposerParams;
bool bComposerRecipe = false;
FVoxelOpStackRecipe ComposerRecipe;
if (Manager->GetComposerOverrideForChunk(
ChunkCoord, ComposerSeed, ComposerArchetype, ComposerParams,
bComposerRecipe, ComposerRecipe))
{
return false;
}
FVoxelStrateRegionManifest ComposerRegions;
if (Manager->GetComposerRegionOverrideForChunk(ChunkCoord, ComposerRegions))
{
return false;
}
#endif
// Cooked seasons can materialise a custom recipe for a chunk. The cheap proof only
// knows the native structural seal contract, so selected recipes fail closed.
int32 RecipeSeed = 0;
ECaveGeneratorType RecipeArchetype = ECaveGeneratorType::TunnelNetwork;
FVoxelStrateArchetypeParams RecipeParams;
FVoxelOpStackRecipe Recipe;
if (Manager->GetRecipeForChunk(
ChunkCoord, RecipeSeed, RecipeArchetype, RecipeParams, Recipe))
{
return false;
}
float Top = 0.0f;
float Bottom = 0.0f;
float Thickness = 0.0f;
float BaseDensity = 0.0f;
if (!ResolveSealFields(ChunkCoord, Top, Bottom, Thickness, BaseDensity))
{
return false;
}
if (!VF_SealedSolidProofGuardDisabled(
DisabledGuards, EVoxelSealedSolidProofGuard::DisturbanceChasms)
&& !VoxelDensityAblation::IsDisturbancesOff())
{
const FStrateDisturbanceParams D =
Manager->GetDisturbanceParamsForChunk(ChunkCoord);
if (VF_AnyChasmCanTouchBox(TileVoxelBox, D, static_cast<uint32>(Generator.Seed)))
{
return false;
}
}
for (int32 G = 0; G <= CPA; ++G)
{
if (SampleKind[G] != SealRow || SampleChunkZ[G] != ChunkZ)
{
continue;
}
const float Z = static_cast<float>(
static_cast<int64>(OriginVoxels.Z)
+ static_cast<int64>(G) * static_cast<int64>(Step));
if (!VF_IsSafeBoundarySealLatticeZ(
Z, Top, Bottom, Thickness, BaseDensity))
{
return false;
}
}
}
}
return true;
}
static bool VF_TryProveOutOfLayoutAirTile(
const UVoxelGenerator& Generator, const FIntVector& OriginVoxels,
int32 Step, int32 CellsPerAxis)
{
const UVoxelStrateManager* Manager = Generator.StrateManager;
if (Manager == nullptr)
{
return false;
}
Step = FMath::Max(1, Step);
const int32 CPA = FMath::Clamp(CellsPerAxis, 2, CHUNK_SIZE);
const int64 MaxX64 = static_cast<int64>(OriginVoxels.X)
+ static_cast<int64>(CPA) * static_cast<int64>(Step);
const int64 MaxY64 = static_cast<int64>(OriginVoxels.Y)
+ static_cast<int64>(CPA) * static_cast<int64>(Step);
const int64 MaxZ64 = static_cast<int64>(OriginVoxels.Z)
+ static_cast<int64>(CPA) * static_cast<int64>(Step);
if (MaxX64 < MIN_int32 || MaxX64 > MAX_int32
|| MaxY64 < MIN_int32 || MaxY64 > MAX_int32
|| MaxZ64 < MIN_int32 || MaxZ64 > MAX_int32)
{
return false;
}
const int32 MinX = OriginVoxels.X;
const int32 MinY = OriginVoxels.Y;
const int32 MinZ = OriginVoxels.Z;
const int32 MaxX = static_cast<int32>(MaxX64);
const int32 MaxY = static_cast<int32>(MaxY64);
const int32 MaxZ = static_cast<int32>(MaxZ64);
const FBox TileVoxelBox(
FVector(static_cast<float>(MinX), static_cast<float>(MinY), static_cast<float>(MinZ)),
FVector(static_cast<float>(MaxX), static_cast<float>(MaxY), static_cast<float>(MaxZ)));
auto FloorDivC = [](int32 A, int32 B) -> int32
{
const int32 Q = A / B;
const int32 R = A % B;
return (R != 0 && ((R < 0) != (B < 0))) ? Q - 1 : Q;
};
// All core lattice rows must be outside the layout. A gap or a real strate row means that
// this is no longer the constant-air case, even when another row is open air.
for (int32 G = 0; G <= CPA; ++G)
{
const int64 Z64 = static_cast<int64>(OriginVoxels.Z)
+ static_cast<int64>(G) * static_cast<int64>(Step);
const int32 ChunkZ = FloorDivC(static_cast<int32>(Z64), CHUNK_SIZE);
if (Manager->IsGapChunk(FIntVector(0, 0, ChunkZ)))
{
return false;
}
int32 UnusedTopChunkZ = 0;
int32 UnusedBottomChunkZ = 0;
if (Manager->GetStrateChunkZBounds(
ChunkZ, UnusedTopChunkZ, UnusedBottomChunkZ))
{
return false;
}
}
const int32 MinChunkX = FloorDivC(MinX, CHUNK_SIZE);
const int32 MaxChunkX = FloorDivC(MaxX, CHUNK_SIZE);
const int32 MinChunkY = FloorDivC(MinY, CHUNK_SIZE);
const int32 MaxChunkY = FloorDivC(MaxY, CHUNK_SIZE);
const int32 MinChunkZ = FloorDivC(MinZ, CHUNK_SIZE);
const int32 MaxChunkZ = FloorDivC(MaxZ, CHUNK_SIZE);
if (Generator.DiffLayer != nullptr
&& Generator.DiffLayer->HasAnyMods()
&& Generator.DiffLayer->HasAnyModInChunkRange(
FIntVector(MinChunkX, MinChunkY, MinChunkZ),
FIntVector(MaxChunkX, MaxChunkY, MaxChunkZ)))
{
return false;
}
// The final XY pass can add rock to open air. Radius == 0 (and other non-positive inputs) is
// the documented no-op, so only validate and inspect the band when that writer is live.
if (!VoxelDensityAblation::IsXYEdgeSealOff()
&& Generator.WorldRadiusVoxels > 0.0f
&& Generator.EdgeSealThickness > 0.0f)
{
if (!VF_IsValidXYEdgeSealProofInput(
TileVoxelBox, Generator.WorldRadiusVoxels,
Generator.EdgeSealThickness, 1.0f))
{
return false;
}
// Use double arithmetic here: the production helper is float-based, while this proof
// must not turn a large finite coordinate into an overflowed squared-distance false
// negative. Equality is safe because the production inner test is <= as well.
const double FarthestX = FMath::Max(
FMath::Abs(static_cast<double>(TileVoxelBox.Min.X)),
FMath::Abs(static_cast<double>(TileVoxelBox.Max.X)));
const double FarthestY = FMath::Max(
FMath::Abs(static_cast<double>(TileVoxelBox.Min.Y)),
FMath::Abs(static_cast<double>(TileVoxelBox.Max.Y)));
const double InnerRadius = static_cast<double>(Generator.WorldRadiusVoxels)
- static_cast<double>(Generator.EdgeSealThickness);
if (!(InnerRadius > 0.0)
|| FarthestX * FarthestX + FarthestY * FarthestY
> InnerRadius * InnerRadius)
{
return false;
}
}
if (Manager->GetPassages().Num() > 0)
{
if (!VF_SealedSolidProofPassageInputsAreValid(*Manager))
{
return false;
}
// Passage carving is air-monotone and therefore cannot invalidate an AllAir proof. Only
// the solid floor/support writers need a guard here. Use the same exact lattice floor
// predicate as ClassifyTile: its padded AABBs stay cheap, but do not reject an open-air
// tile merely because a harmless carve bound passes near it.
const bool bPassageFloorWriterActive =
!VoxelDensityAblation::IsPassageStructuralPostsOff()
|| !VoxelDensityAblation::IsNativeFloorOff()
|| !VoxelDensityAblation::IsLandingPostsOff();
if (bPassageFloorWriterActive
&& Manager->AnyPassageLandingFloorNearLattice(
TileVoxelBox, OriginVoxels, Step))
{
return false;
}
}
// Origin rooms only carve open air, but their landing floors reassert solid. The manager's
// floor box includes every per-strate room; this is intentionally broader than the exact
// lattice query used by ClassifyTile because a false Mixed is cheaper than a dropped floor.
if (!VoxelDensityAblation::IsOriginSpineOff()
|| !VoxelDensityAblation::IsLandingPostsOff())
{
if (!VoxelMath::IsFinite(Generator.OriginSpineRadius)
|| Generator.OriginSpineRadius <= 0.0f)
{
return false;
}
if (Manager->AnyOriginLandingFloorNearLattice(
TileVoxelBox, OriginVoxels, Step))
{
return false;
}
}
if (!VoxelDensityAblation::IsDisturbancesOff())
{
const int64 XYChunkCount =
(static_cast<int64>(MaxChunkX) - static_cast<int64>(MinChunkX) + 1)
* (static_cast<int64>(MaxChunkY) - static_cast<int64>(MinChunkY) + 1);
const int64 ZChunkCount =
static_cast<int64>(MaxChunkZ) - static_cast<int64>(MinChunkZ) + 1;
constexpr int64 MaxProofChunkCoordinates = 65536;
if (XYChunkCount <= 0 || ZChunkCount <= 0
|| XYChunkCount > MaxProofChunkCoordinates
|| XYChunkCount * ZChunkCount > MaxProofChunkCoordinates)
{
return false;
}
// Out-of-layout chunks normally return disabled disturbance params. Still ask the exact
// bridge/ridge predicates for every touched chunk so a future out-of-layout decoration
// cannot silently turn this air proof into an invisible solid patch.
for (int32 ChunkZ = MinChunkZ; ChunkZ <= MaxChunkZ; ++ChunkZ)
for (int32 ChunkY = MinChunkY; ChunkY <= MaxChunkY; ++ChunkY)
for (int32 ChunkX = MinChunkX; ChunkX <= MaxChunkX; ++ChunkX)
{
const FStrateDisturbanceParams D =
Manager->GetDisturbanceParamsForChunk(FIntVector(ChunkX, ChunkY, ChunkZ));
if (VF_AnyBridgeCanTouchBox(TileVoxelBox, D, static_cast<uint32>(Generator.Seed))
|| VF_AnyRidgeCanTouchBox(TileVoxelBox, D, static_cast<uint32>(Generator.Seed)))
{
return false;
}
}
}
return true;
}
//============================================================================= //=============================================================================
// LE MAPPING « ARCHÉTYPE → PILE D'OPÉRATEURS » — UNE SEULE DÉFINITION // LE MAPPING « ARCHÉTYPE → PILE D’OPÉRATEURS » — UNE SEULE DÉFINITION
//============================================================================= //=============================================================================
// ⚠️ EXTRAIT DE `GetDensityAt` PARCE QUE `ClassifyTile` EN A BESOIN AUSSI, ET QU'UNE DEUXIÈME COPIE // ⚠️ EXTRAIT DE `GetDensityAt` PARCE QUE `ClassifyTile` EN A BESOIN AUSSI, ET QU'UNE DEUXIÈME COPIE
// SERAIT LA PIRE FORME DE BUG DISPONIBLE ICI. // SERAIT LA PIRE FORME DE BUG DISPONIBLE ICI.
@@ -5713,6 +6329,29 @@ EVoxelTileClass UVoxelGenerator::ClassifyTile(const FIntVector& OriginVoxels,
return ClassifyTile(OriginVoxels, Step, CellsPerAxis, nullptr); return ClassifyTile(OriginVoxels, Step, CellsPerAxis, nullptr);
} }
bool UVoxelGenerator::TryProveSealedSolidTile(
const FIntVector& OriginVoxels, int32 Step, int32 CellsPerAxis) const
{
return VF_TryProveSealedSolidTile(
*this, OriginVoxels, Step, CellsPerAxis, /*DisabledGuards=*/0);
}
bool UVoxelGenerator::TryProveOutOfLayoutAirTile(
const FIntVector& OriginVoxels, int32 Step, int32 CellsPerAxis) const
{
return VF_TryProveOutOfLayoutAirTile(*this, OriginVoxels, Step, CellsPerAxis);
}
#if WITH_DEV_AUTOMATION_TESTS
bool UVoxelGenerator::TryProveSealedSolidTileForTest(
const FIntVector& OriginVoxels, int32 Step, int32 CellsPerAxis,
uint8 DisabledGuards) const
{
return VF_TryProveSealedSolidTile(
*this, OriginVoxels, Step, CellsPerAxis, DisabledGuards);
}
#endif
EVoxelTileClass UVoxelGenerator::ClassifyTile(const FIntVector& OriginVoxels, int32 Step, EVoxelTileClass UVoxelGenerator::ClassifyTile(const FIntVector& OriginVoxels, int32 Step,
int32 CellsPerAxis, int32 CellsPerAxis,
FVoxelTileClassificationStats* OutStats) const FVoxelTileClassificationStats* OutStats) const
@@ -260,11 +260,12 @@ void RecordTile(const FTileSample& Sample)
GState->Samples.Add(Sample); GState->Samples.Add(Sample);
++GState->TileRecordsWritten; ++GState->TileRecordsWritten;
WriteEventLocked(*GState, TEXT("tile"), FString::Printf( WriteEventLocked(*GState, TEXT("tile"), FString::Printf(
TEXT("\"tile\":[%d,%d,%d],\"level\":%d,\"verdict\":%d,\"empty\":%d,\"triangles\":%d," TEXT("\"tile\":[%d,%d,%d],\"level\":%d,\"verdict\":%d,\"proof\":%d,\"empty\":%d,\"triangles\":%d,"
"\"request_to_apply_s\":%s,\"queue_wait_s\":%s,\"worker_queue_s\":%s,\"result_queue_s\":%s," "\"request_to_apply_s\":%s,\"queue_wait_s\":%s,\"worker_queue_s\":%s,\"result_queue_s\":%s,"
"\"generation_s\":%s,\"classify_s\":%s,\"mesh_s\":%s,\"stream_s\":%s,\"apply_s\":%s"), "\"generation_s\":%s,\"classify_s\":%s,\"mesh_s\":%s,\"stream_s\":%s,\"apply_s\":%s"),
Sample.TileX, Sample.TileY, Sample.TileZ, Sample.TileX, Sample.TileY, Sample.TileZ,
Sample.Level, Sample.Verdict, Sample.bEmpty ? 1 : 0, Sample.Triangles, Sample.Level, Sample.Verdict, Sample.bSealedSolidProof ? 1 : 0,
Sample.bEmpty ? 1 : 0, Sample.Triangles,
*Number(Sample.RequestToApplySeconds), *Number(Sample.QueueWaitSeconds), *Number(Sample.RequestToApplySeconds), *Number(Sample.QueueWaitSeconds),
*Number(Sample.WorkerQueueSeconds), *Number(Sample.ResultQueueSeconds), *Number(Sample.WorkerQueueSeconds), *Number(Sample.ResultQueueSeconds),
*Number(Sample.GenerationSeconds), *Number(Sample.ClassifySeconds), *Number(Sample.GenerationSeconds), *Number(Sample.ClassifySeconds),
+196 -12
View File
@@ -97,6 +97,15 @@ namespace
GVoxelForgeOuterClassifierMode, GVoxelForgeOuterClassifierMode,
TEXT("Outer tile classifier mode: 0 off, 1 discarded exact validation.")); TEXT("Outer tile classifier mode: 0 off, 1 discarded exact validation."));
// Cheap structural proofs for LOD tiles whose exact core lattice lies entirely in a gap/seal
// (AllSolid) or constant out-of-layout air (AllAir). They are independent of the discarded
// full classifier and default on; 0 is retained for per-tile identity/A-B traces.
int32 GVoxelForgeSealedSolidProof = 1;
FAutoConsoleVariableRef CVarVoxelForgeSealedSolidProof(
TEXT("voxel.SealedSolidProof"),
GVoxelForgeSealedSolidProof,
TEXT("Skip meshing only when an exact gap/seal or constant-air proof holds: 0 off, 1 on."));
// Startup command-line cvars can arrive before the game world has spawned AVoxelWorld (or // Startup command-line cvars can arrive before the game world has spawned AVoxelWorld (or
// before its first tiles have become visible). Keep the diagnostic edit pending until Tick has // before its first tiles have become visible). Keep the diagnostic edit pending until Tick has
// a loaded tile; otherwise a startup measurement silently becomes a no-op or edits the wrong // a loaded tile; otherwise a startup measurement silently becomes a no-op or edits the wrong
@@ -104,6 +113,7 @@ namespace
int32 GVoxelForgePendingTestModification = 0; int32 GVoxelForgePendingTestModification = 0;
float GVoxelForgePendingModificationRadius = 3.0f; float GVoxelForgePendingModificationRadius = 3.0f;
float GVoxelForgePendingModificationStrength = 10.0f; float GVoxelForgePendingModificationStrength = 10.0f;
bool GVoxelForgeTestModificationLoadedProofTarget = false;
bool GVoxelForgeTestModificationCommandLineConsumed = false; bool GVoxelForgeTestModificationCommandLineConsumed = false;
bool GVoxelForgeStreamingBudgetReported = false; bool GVoxelForgeStreamingBudgetReported = false;
bool GVoxelForgeGenerationCapHitReported = false; bool GVoxelForgeGenerationCapHitReported = false;
@@ -1105,6 +1115,9 @@ void AVoxelWorld::BeginPlay()
TotalObsoleteWorkerTasks.store(0, std::memory_order_relaxed); TotalObsoleteWorkerTasks.store(0, std::memory_order_relaxed);
TotalValidationDensityCalls.store(0, std::memory_order_relaxed); TotalValidationDensityCalls.store(0, std::memory_order_relaxed);
TotalMesherDensityCalls.store(0, std::memory_order_relaxed); TotalMesherDensityCalls.store(0, std::memory_order_relaxed);
TotalSealedSolidProofCandidates.store(0, std::memory_order_relaxed);
TotalSealedSolidProofSkips.store(0, std::memory_order_relaxed);
TotalOutOfLayoutAirProofSkips.store(0, std::memory_order_relaxed);
AppliedTileCount = 0; AppliedTileCount = 0;
AppliedVisibleTileCount = 0; AppliedVisibleTileCount = 0;
AppliedTriangleCount = 0; AppliedTriangleCount = 0;
@@ -1133,6 +1146,7 @@ void AVoxelWorld::BeginPlay()
int32 CommandLineProfileTileGeneration = GVoxelForgeProfileTileGeneration; int32 CommandLineProfileTileGeneration = GVoxelForgeProfileTileGeneration;
int32 CommandLineUseBlockEarlyOut = GVoxelForgeUseBlockEarlyOut; int32 CommandLineUseBlockEarlyOut = GVoxelForgeUseBlockEarlyOut;
int32 CommandLineOuterClassifierMode = GVoxelForgeOuterClassifierMode; int32 CommandLineOuterClassifierMode = GVoxelForgeOuterClassifierMode;
int32 CommandLineSealedSolidProof = GVoxelForgeSealedSolidProof;
int32 CommandLineSampleStacks = GVoxelForgeSampleStacks; int32 CommandLineSampleStacks = GVoxelForgeSampleStacks;
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.ProfileDensity="), CommandLineProfile)) if (FParse::Value(FCommandLine::Get(), TEXT("voxel.ProfileDensity="), CommandLineProfile))
{ {
@@ -1161,6 +1175,10 @@ void AVoxelWorld::BeginPlay()
{ {
GVoxelForgeOuterClassifierMode = CommandLineOuterClassifierMode; GVoxelForgeOuterClassifierMode = CommandLineOuterClassifierMode;
} }
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.SealedSolidProof="), CommandLineSealedSolidProof))
{
GVoxelForgeSealedSolidProof = CommandLineSealedSolidProof;
}
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.SampleStacks="), CommandLineSampleStacks)) if (FParse::Value(FCommandLine::Get(), TEXT("voxel.SampleStacks="), CommandLineSampleStacks))
{ {
GVoxelForgeSampleStacks = CommandLineSampleStacks; GVoxelForgeSampleStacks = CommandLineSampleStacks;
@@ -1168,6 +1186,7 @@ void AVoxelWorld::BeginPlay()
GVoxelForgeProfileTileGeneration = GVoxelForgeProfileTileGeneration != 0 ? 1 : 0; GVoxelForgeProfileTileGeneration = GVoxelForgeProfileTileGeneration != 0 ? 1 : 0;
GVoxelForgeUseBlockEarlyOut = GVoxelForgeUseBlockEarlyOut != 0 ? 1 : 0; GVoxelForgeUseBlockEarlyOut = GVoxelForgeUseBlockEarlyOut != 0 ? 1 : 0;
GVoxelForgeOuterClassifierMode = FMath::Clamp(GVoxelForgeOuterClassifierMode, 0, 1); GVoxelForgeOuterClassifierMode = FMath::Clamp(GVoxelForgeOuterClassifierMode, 0, 1);
GVoxelForgeSealedSolidProof = GVoxelForgeSealedSolidProof != 0 ? 1 : 0;
GVoxelForgeSampleStacks = FMath::Clamp( GVoxelForgeSampleStacks = FMath::Clamp(
GVoxelForgeSampleStacks, GVoxelForgeSampleStacks,
0, 0,
@@ -1281,8 +1300,9 @@ void AVoxelWorld::BeginPlay()
Mesher->bUseBlockEarlyOut = GVoxelForgeUseBlockEarlyOut != 0; Mesher->bUseBlockEarlyOut = GVoxelForgeUseBlockEarlyOut != 0;
UE_LOG(LogTemp, Display, UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeClassifierConfig] outer_mode=%d nested_block_early_out=%d"), TEXT("[VoxelForgeClassifierConfig] outer_mode=%d sealed_solid_proof=%d nested_block_early_out=%d"),
GVoxelForgeOuterClassifierMode, Mesher->bUseBlockEarlyOut ? 1 : 0); GVoxelForgeOuterClassifierMode, GVoxelForgeSealedSolidProof,
Mesher->bUseBlockEarlyOut ? 1 : 0);
VoxelForgeStartupTrace::RecordEvent(TEXT("generator_mesher_constructed"), FString::Printf( VoxelForgeStartupTrace::RecordEvent(TEXT("generator_mesher_constructed"), FString::Printf(
TEXT("\"generator\":1,\"mesher\":1,\"generate_skirts\":%d,\"full_res_clip_levels\":%d,\"coarse_tile_cells\":%d"), TEXT("\"generator\":1,\"mesher\":1,\"generate_skirts\":%d,\"full_res_clip_levels\":%d,\"coarse_tile_cells\":%d"),
@@ -1377,6 +1397,9 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
{ {
bHeadlessStreamingTestMovement = false; bHeadlessStreamingTestMovement = false;
bHeadlessStreamingTestExitRequested = false; bHeadlessStreamingTestExitRequested = false;
bStartupTraceThroughCrossing = false;
bHeadlessStreamingTestCenterOverride = false;
HeadlessStreamingTestCenterVoxel = FVector::ZeroVector;
HeadlessStreamingTestMoveAttempts = 0; HeadlessStreamingTestMoveAttempts = 0;
HeadlessStreamingTestDistanceCm = 0.0; HeadlessStreamingTestDistanceCm = 0.0;
HeadlessStreamingTestSpeedCmPerSecond = 0.0f; HeadlessStreamingTestSpeedCmPerSecond = 0.0f;
@@ -1472,6 +1495,33 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
HeadlessStrateCrossingTestRequestedPassageIndex); HeadlessStrateCrossingTestRequestedPassageIndex);
} }
int32 TraceThroughCrossingValue = 0;
if (FParse::Value(
CommandLine,
TEXT("voxel.StartupTraceThroughCrossing="),
TraceThroughCrossingValue))
{
bStartupTraceThroughCrossing = TraceThroughCrossingValue != 0;
}
float CenterX = 0.0f;
float CenterY = 0.0f;
float CenterZ = 0.0f;
const bool bCenterX = FParse::Value(CommandLine, TEXT("voxel.StreamingTestCenterX="), CenterX);
const bool bCenterY = FParse::Value(CommandLine, TEXT("voxel.StreamingTestCenterY="), CenterY);
const bool bCenterZ = FParse::Value(CommandLine, TEXT("voxel.StreamingTestCenterZ="), CenterZ);
if (bCenterX && bCenterY && bCenterZ
&& VoxelMath::IsFinite(CenterX)
&& VoxelMath::IsFinite(CenterY)
&& VoxelMath::IsFinite(CenterZ))
{
bHeadlessStreamingTestCenterOverride = true;
HeadlessStreamingTestCenterVoxel = FVector(CenterX, CenterY, CenterZ);
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeStreamingTest] center_override=1 local_vox=(%.2f,%.2f,%.2f)"),
CenterX, CenterY, CenterZ);
}
if (bMoveRequested && !bHeadlessStrateCrossingTest) if (bMoveRequested && !bHeadlessStrateCrossingTest)
{ {
FString DirectionText; FString DirectionText;
@@ -2000,6 +2050,11 @@ void AVoxelWorld::MaybeFinishHeadlessStrateCrossingTest()
HeadlessStrateCrossingTestLastActualPosition.X, HeadlessStrateCrossingTestLastActualPosition.X,
HeadlessStrateCrossingTestLastActualPosition.Y, HeadlessStrateCrossingTestLastActualPosition.Y,
HeadlessStrateCrossingTestLastActualPosition.Z); HeadlessStrateCrossingTestLastActualPosition.Z);
if (bStartupTraceThroughCrossing && VoxelForgeStartupTrace::IsActive())
{
VoxelForgeStartupTrace::Finish(
bPassed ? TEXT("strate_crossing_passed") : TEXT("strate_crossing_failed"));
}
FPlatformMisc::RequestExitWithStatus( FPlatformMisc::RequestExitWithStatus(
false, bPassed ? 0 : 1, TEXT("VoxelForge strate crossing test complete")); false, bPassed ? 0 : 1, TEXT("VoxelForge strate crossing test complete"));
} }
@@ -2110,7 +2165,10 @@ void AVoxelWorld::Tick(float DeltaTime)
PeakObservedPawnSpeedCmPerSecond, PlayerHeading.Size2D()); PeakObservedPawnSpeedCmPerSecond, PlayerHeading.Size2D());
UpdatePawnCollisionGate(PlayerPawn, PlayerLastPos, DeltaTime); UpdatePawnCollisionGate(PlayerPawn, PlayerLastPos, DeltaTime);
ObserveHeadlessStrateCrossingTest(PlayerLastPos, PlayerPawn); ObserveHeadlessStrateCrossingTest(PlayerLastPos, PlayerPawn);
UpdateChunksAroundPosition(PlayerLastPos, PlayerPawn, PlayerHeading); const FVector StreamingCenterPosition = bHeadlessStreamingTestCenterOverride
? LocalVoxelToWorld(HeadlessStreamingTestCenterVoxel)
: PlayerLastPos;
UpdateChunksAroundPosition(StreamingCenterPosition, PlayerPawn, PlayerHeading);
if (AtmosphereManager) if (AtmosphereManager)
{ {
AtmosphereManager->UpdateForPlayer(PlayerLastPos); AtmosphereManager->UpdateForPlayer(PlayerLastPos);
@@ -2165,10 +2223,20 @@ void AVoxelWorld::Tick(float DeltaTime)
{ {
GVoxelForgePendingModificationRadius = 3.0f; GVoxelForgePendingModificationRadius = 3.0f;
GVoxelForgePendingModificationStrength = 10.0f; GVoxelForgePendingModificationStrength = 10.0f;
FString ModificationTarget;
if (FParse::Value(
*CommandLine,
TEXT("voxel.TestModificationTarget="),
ModificationTarget)
&& ModificationTarget.Equals(TEXT("loaded-proof"), ESearchCase::IgnoreCase))
{
GVoxelForgeTestModificationLoadedProofTarget = true;
}
UE_LOG(LogTemp, Display, UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTestModification] queued radius=%.2f strength=%.2f"), TEXT("[VoxelForgeTestModification] queued radius=%.2f strength=%.2f target=%s"),
GVoxelForgePendingModificationRadius, GVoxelForgePendingModificationRadius,
GVoxelForgePendingModificationStrength); GVoxelForgePendingModificationStrength,
GVoxelForgeTestModificationLoadedProofTarget ? TEXT("loaded-proof") : TEXT("player"));
GVoxelForgePendingTestModification = 1; GVoxelForgePendingTestModification = 1;
} }
} }
@@ -2183,10 +2251,16 @@ void AVoxelWorld::Tick(float DeltaTime)
&& PendingTiles.Num() == 0 && PendingTiles.Num() == 0
&& bStartupTraceDesiredRecorded && bStartupTraceDesiredRecorded
&& PendingCollisionCooks.Num() == 0 && PendingCollisionCooks.Num() == 0
&& (!bStartupTraceThroughCrossing
|| !bHeadlessStrateCrossingTest
|| bHeadlessStrateCrossingTestPassed
|| bHeadlessStrateCrossingTestFailed)
// The desired set being applied is not the player-ready point: the feet tile must have // The desired set being applied is not the player-ready point: the feet tile must have
// received its completed LOD0 RMC body first. If no pawn exists, there is no gate to wait // received its completed LOD0 RMC body first. If no pawn exists, there is no gate to wait
// for and the headless trace keeps its old completion behavior. // for and the headless trace keeps its old completion behavior.
&& (!bHasPlayer || IsPlayerSupportCollisionReady(PlayerPawn, PlayerLastPos))) && (!bHasPlayer
|| bHeadlessStreamingTestCenterOverride
|| IsPlayerSupportCollisionReady(PlayerPawn, PlayerLastPos)))
{ {
TMap<int32, int32> SteadyLevelCounts; TMap<int32, int32> SteadyLevelCounts;
TMap<int32, FIntVector> SteadyLevelMins; TMap<int32, FIntVector> SteadyLevelMins;
@@ -2244,22 +2318,75 @@ void AVoxelWorld::Tick(float DeltaTime)
// No pawn is a distinct state from a pawn at (0,0,0); keep the diagnostic fallback for // No pawn is a distinct state from a pawn at (0,0,0); keep the diagnostic fallback for
// headless/editor use without making the world-origin pawn skip streaming. // headless/editor use without making the world-origin pawn skip streaming.
FVector ModificationPosition = bHasPlayer ? PlayerLastPos : GetActorLocation(); FVector ModificationPosition = bHasPlayer ? PlayerLastPos : GetActorLocation();
const FVoxelTileKey ModificationCenterTile( FVoxelTileKey ModificationCenterTile(
WorldToChunkCoord(WorldToLocalCm(ModificationPosition)), 0); WorldToChunkCoord(WorldToLocalCm(ModificationPosition)), 0);
bool bModificationTargetSelected = false;
if (GVoxelForgePendingTestModification != 0
&& GVoxelForgeTestModificationLoadedProofTarget
&& Generator)
{
// The diagnostic target is deliberately selected from the applied set. That makes the
// edit exercise the normal loaded-tile synchronous remesh, while the proof query here is
// the same public predicate used by GenerateTileResult before the diff exists.
for (const FVoxelTileKey& CandidateTile : LoadedTiles)
{
if (CandidateTile.Level != 0
|| PendingTiles.Contains(CandidateTile))
{
continue;
}
const FIntVector CandidateOrigin(
CandidateTile.Coord.X * CHUNK_SIZE,
CandidateTile.Coord.Y * CHUNK_SIZE,
CandidateTile.Coord.Z * CHUNK_SIZE);
if (!Generator->TryProveSealedSolidTile(CandidateOrigin, 1, CHUNK_SIZE))
{
continue;
}
const FIntVector CandidateCenter(
CandidateOrigin.X + CHUNK_SIZE / 2,
CandidateOrigin.Y + CHUNK_SIZE / 2,
CandidateOrigin.Z + CHUNK_SIZE / 2);
ModificationPosition = LocalVoxelToWorld(FVector(CandidateCenter));
ModificationCenterTile = CandidateTile;
bModificationTargetSelected = true;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTestModification] player_mine_target tile=(%d,%d,%d) "
"local_center=(%d,%d,%d) before_proof=1"),
CandidateTile.Coord.X, CandidateTile.Coord.Y, CandidateTile.Coord.Z,
CandidateCenter.X, CandidateCenter.Y, CandidateCenter.Z);
break;
}
}
if (GVoxelForgePendingTestModification != 0 if (GVoxelForgePendingTestModification != 0
&& DiffLayer && DiffLayer
&& (!GVoxelForgeTestModificationLoadedProofTarget || bModificationTargetSelected)
&& LoadedTiles.Contains(ModificationCenterTile) && LoadedTiles.Contains(ModificationCenterTile)
&& !PendingTiles.Contains(ModificationCenterTile)) && !PendingTiles.Contains(ModificationCenterTile))
{ {
GVoxelForgePendingTestModification = 0; GVoxelForgePendingTestModification = 0;
UE_LOG(LogTemp, Display, UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTestModification] applying position=(%.2f,%.2f,%.2f) radius=%.2f strength=%.2f"), TEXT("[VoxelForgeTestModification] applying player_mine=1 tile=(%d,%d,%d) "
"position=(%.2f,%.2f,%.2f) radius=%.2f strength=%.2f"),
ModificationCenterTile.Coord.X, ModificationCenterTile.Coord.Y, ModificationCenterTile.Coord.Z,
ModificationPosition.X, ModificationPosition.Y, ModificationPosition.Z, ModificationPosition.X, ModificationPosition.Y, ModificationPosition.Z,
GVoxelForgePendingModificationRadius, GVoxelForgePendingModificationStrength); GVoxelForgePendingModificationRadius, GVoxelForgePendingModificationStrength);
CarveAtPosition( CarveAtPosition(
ModificationPosition, ModificationPosition,
GVoxelForgePendingModificationRadius, GVoxelForgePendingModificationRadius,
GVoxelForgePendingModificationStrength); GVoxelForgePendingModificationStrength);
const FIntVector ModifiedOrigin(
ModificationCenterTile.Coord.X * CHUNK_SIZE,
ModificationCenterTile.Coord.Y * CHUNK_SIZE,
ModificationCenterTile.Coord.Z * CHUNK_SIZE);
const bool bProofSurvivedCarve = Generator
&& Generator->TryProveSealedSolidTile(ModifiedOrigin, 1, CHUNK_SIZE);
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTestModification] player_mine_complete tile=(%d,%d,%d) "
"after_proof=%d"),
ModificationCenterTile.Coord.X, ModificationCenterTile.Coord.Y,
ModificationCenterTile.Coord.Z, bProofSurvivedCarve ? 1 : 0);
} }
ProcessUnloadQueue(); ProcessUnloadQueue();
MaybeFinishHeadlessStreamingTest(); MaybeFinishHeadlessStreamingTest();
@@ -2849,6 +2976,12 @@ void AVoxelWorld::LogStreamingLatencySummary() const
TotalWorkerGenerationCycles.load(std::memory_order_relaxed); TotalWorkerGenerationCycles.load(std::memory_order_relaxed);
const uint64 ObsoleteWorkerCycles = const uint64 ObsoleteWorkerCycles =
TotalObsoleteWorkerCycles.load(std::memory_order_relaxed); TotalObsoleteWorkerCycles.load(std::memory_order_relaxed);
const uint64 SealedSolidProofCandidates =
TotalSealedSolidProofCandidates.load(std::memory_order_relaxed);
const uint64 SealedSolidProofSkips =
TotalSealedSolidProofSkips.load(std::memory_order_relaxed);
const uint64 OutOfLayoutAirProofSkips =
TotalOutOfLayoutAirProofSkips.load(std::memory_order_relaxed);
const bool bDiagnosticsEnabled = GVoxelForgeProfileTileGeneration != 0 const bool bDiagnosticsEnabled = GVoxelForgeProfileTileGeneration != 0
|| VoxelForgeStartupTrace::IsActive() || VoxelForgeStartupTrace::IsActive()
|| VoxelDensityProfile::GetMode() != VoxelDensityProfile::EMode::Disabled; || VoxelDensityProfile::GetMode() != VoxelDensityProfile::EMode::Disabled;
@@ -2863,6 +2996,8 @@ void AVoxelWorld::LogStreamingLatencySummary() const
"applied_tiles=%llu applied_visible_tiles=%llu applied_triangles=%llu " "applied_tiles=%llu applied_visible_tiles=%llu applied_triangles=%llu "
"worker_generation_tasks=%llu worker_generation_sum_s=%.6f " "worker_generation_tasks=%llu worker_generation_sum_s=%.6f "
"validation_density_calls=%llu mesher_density_calls=%llu " "validation_density_calls=%llu mesher_density_calls=%llu "
"sealed_solid_proof_candidates=%llu sealed_solid_proof_skips=%llu "
"out_of_layout_air_proof_skips=%llu "
"obsolete_worker_tasks=%llu obsolete_worker_sum_s=%.6f " "obsolete_worker_tasks=%llu obsolete_worker_sum_s=%.6f "
"outer_classifier_verdicts=%s applied_level_summary=%s band_level_summary=%s"), "outer_classifier_verdicts=%s applied_level_summary=%s band_level_summary=%s"),
bDiagnosticsEnabled ? TEXT("profiled") : TEXT("clean"), bDiagnosticsEnabled ? TEXT("profiled") : TEXT("clean"),
@@ -2881,6 +3016,9 @@ void AVoxelWorld::LogStreamingLatencySummary() const
FPlatformTime::ToSeconds64(WorkerGenerationCycles), FPlatformTime::ToSeconds64(WorkerGenerationCycles),
static_cast<unsigned long long>(TotalValidationDensityCalls.load(std::memory_order_relaxed)), static_cast<unsigned long long>(TotalValidationDensityCalls.load(std::memory_order_relaxed)),
static_cast<unsigned long long>(TotalMesherDensityCalls.load(std::memory_order_relaxed)), static_cast<unsigned long long>(TotalMesherDensityCalls.load(std::memory_order_relaxed)),
static_cast<unsigned long long>(SealedSolidProofCandidates),
static_cast<unsigned long long>(SealedSolidProofSkips),
static_cast<unsigned long long>(OutOfLayoutAirProofSkips),
static_cast<unsigned long long>(TotalObsoleteWorkerTasks.load(std::memory_order_relaxed)), static_cast<unsigned long long>(TotalObsoleteWorkerTasks.load(std::memory_order_relaxed)),
FPlatformTime::ToSeconds64(ObsoleteWorkerCycles), FPlatformTime::ToSeconds64(ObsoleteWorkerCycles),
*ClassifierVerdictSummary, *ClassifierVerdictSummary,
@@ -3191,6 +3329,7 @@ bool AVoxelWorld::ApplyTileResult(FChunkResult& Result)
Sample.TileZ = Result.Tile.Coord.Z; Sample.TileZ = Result.Tile.Coord.Z;
Sample.Level = Result.Tile.Level; Sample.Level = Result.Tile.Level;
Sample.Verdict = Result.ClassifyVerdict; Sample.Verdict = Result.ClassifyVerdict;
Sample.bSealedSolidProof = Result.bSealedSolidProof;
Sample.bEmpty = bResultEmpty; Sample.bEmpty = bResultEmpty;
Sample.Triangles = Result.NumTriangles; Sample.Triangles = Result.NumTriangles;
Sample.RequestToApplySeconds = RequestToApply; Sample.RequestToApplySeconds = RequestToApply;
@@ -4523,7 +4662,7 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
UE_LOG(LogTemp, Display, UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTileProfile] tile=(%d,%d,%d) level=%d step=%d cells=%d sheet=%d aborted=%d empty=%d triangles=%d " TEXT("[VoxelForgeTileProfile] tile=(%d,%d,%d) level=%d step=%d cells=%d sheet=%d aborted=%d empty=%d triangles=%d "
"band=(%d,%d) " "band=(%d,%d) "
"verdict=%d classify=%.6f mesh=%.6f streams=%.6f seconds=%.6f " "verdict=%d proof=%d air_proof=%d classify=%.6f mesh=%.6f streams=%.6f seconds=%.6f "
"cache_build=%.6f evaluation=%.6f " "cache_build=%.6f evaluation=%.6f "
"refine=%u stack=%u core_samples=%u final_samples=%u core_hits=%u final_hits=%u " "refine=%u stack=%u core_samples=%u final_samples=%u core_hits=%u final_hits=%u "
"core_leaves=%u final_leaves=%u tail_queries=%u tail_eval=%u " "core_leaves=%u final_leaves=%u tail_queries=%u tail_eval=%u "
@@ -4543,7 +4682,9 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
Tile.Coord.X, Tile.Coord.Y, Tile.Coord.Z, Tile.Level, Step, Cells, Tile.Coord.X, Tile.Coord.Y, Tile.Coord.Z, Tile.Level, Step, Cells,
bSheetTile ? 1 : 0, Result.bAborted ? 1 : 0, bSheetTile ? 1 : 0, Result.bAborted ? 1 : 0,
Result.bEmpty ? 1 : 0, Result.NumTriangles, BandChunkLo, BandChunkHi, Result.bEmpty ? 1 : 0, Result.NumTriangles, BandChunkLo, BandChunkHi,
ClassifyVerdict, ClassifySeconds, MeshSeconds, StreamSeconds, ClassifyVerdict, Result.bSealedSolidProof ? 1 : 0,
Result.bOutOfLayoutAirProof ? 1 : 0,
ClassifySeconds, MeshSeconds, StreamSeconds,
TileSeconds, CacheBuildSeconds, EvaluationSeconds, TileSeconds, CacheBuildSeconds, EvaluationSeconds,
ClassifierStats.RefineNodes, ClassifierStats.StackBoxCalls, ClassifierStats.RefineNodes, ClassifierStats.StackBoxCalls,
ClassifierStats.ExactCoreSamples, ClassifierStats.ExactFinalSamples, ClassifierStats.ExactCoreSamples, ClassifierStats.ExactFinalSamples,
@@ -4637,6 +4778,8 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
Result.bObsolete = false; Result.bObsolete = false;
Result.bEmpty = true; Result.bEmpty = true;
Result.ClassifyVerdict = -1; Result.ClassifyVerdict = -1;
Result.bSealedSolidProof = false;
Result.bOutOfLayoutAirProof = false;
Result.ClassifySeconds = 0.0; Result.ClassifySeconds = 0.0;
Result.MeshSeconds = 0.0; Result.MeshSeconds = 0.0;
Result.StreamSeconds = 0.0; Result.StreamSeconds = 0.0;
@@ -4674,8 +4817,49 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
// même pour les cellules uniformes, et ces tuiles sont rares (fenêtre d'ombre). // même pour les cellules uniformes, et ces tuiles sont rares (fenêtre d'ombre).
// (Gate IsoLevel == 0 : les verdicts du classifieur supposent l'iso MC à zéro exactement.) // (Gate IsoLevel == 0 : les verdicts du classifieur supposent l'iso MC à zéro exactement.)
bool bTrivialEmpty = false; bool bTrivialEmpty = false;
if (GVoxelForgeSealedSolidProof != 0
&& !bSheetTile && !bWantCapture && Generator && Mesher
&& Mesher->IsoLevel == 0.0f)
{
TotalSealedSolidProofCandidates.fetch_add(1, std::memory_order_relaxed);
const double ProofStartSeconds = bMeasureTile ? FPlatformTime::Seconds() : 0.0;
if (Generator->TryProveSealedSolidTile(OriginVoxels, Step, Cells))
{
ClassifySeconds = bMeasureTile
? FPlatformTime::Seconds() - ProofStartSeconds : 0.0;
ClassifyVerdict = static_cast<int32>(EVoxelTileClass::AllSolid);
Result.ClassifyVerdict = ClassifyVerdict;
Result.bSealedSolidProof = true;
TotalSealedSolidProofSkips.fetch_add(1, std::memory_order_relaxed);
INC_DWORD_STAT(STAT_VoxelForgeTilesSkippedAllSolid);
bTrivialEmpty = true;
}
else if (Generator->TryProveOutOfLayoutAirTile(OriginVoxels, Step, Cells))
{
ClassifySeconds = bMeasureTile
? FPlatformTime::Seconds() - ProofStartSeconds : 0.0;
ClassifyVerdict = static_cast<int32>(EVoxelTileClass::AllAir);
Result.ClassifyVerdict = ClassifyVerdict;
Result.bOutOfLayoutAirProof = true;
TotalOutOfLayoutAirProofSkips.fetch_add(1, std::memory_order_relaxed);
INC_DWORD_STAT(STAT_VoxelForgeTilesSkippedAllAir);
bTrivialEmpty = true;
}
else if (bMeasureTile)
{
// Keep the measured proof cost visible on a tile that continues through the normal
// path. The clean summary remains counter-only and pays no timer overhead.
ClassifySeconds = FPlatformTime::Seconds() - ProofStartSeconds;
}
}
if (ShouldAbortWork(ObsoleteFlag))
{
AbortResult();
EmitTileProfile();
return;
}
const int32 OuterClassifierMode = FMath::Clamp(GVoxelForgeOuterClassifierMode, 0, 1); const int32 OuterClassifierMode = FMath::Clamp(GVoxelForgeOuterClassifierMode, 0, 1);
if (OuterClassifierMode != 0 if (!bTrivialEmpty && OuterClassifierMode != 0
&& !bSheetTile && !bWantCapture && Generator && Mesher && Mesher->IsoLevel == 0.0f) && !bSheetTile && !bWantCapture && Generator && Mesher && Mesher->IsoLevel == 0.0f)
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_ClassifyTile); TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_ClassifyTile);
@@ -4754,7 +4938,7 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
EmitTileProfile(); EmitTileProfile();
return; return;
} }
ClassifySeconds = bMeasureTile ? FPlatformTime::Seconds() - ClassifyStartSeconds : 0.0; ClassifySeconds += bMeasureTile ? FPlatformTime::Seconds() - ClassifyStartSeconds : 0.0;
ClassifyVerdict = static_cast<int32>(ValidatedVerdict); ClassifyVerdict = static_cast<int32>(ValidatedVerdict);
Result.ClassifyVerdict = ClassifyVerdict; Result.ClassifyVerdict = ClassifyVerdict;
const int32 TrackedLOD = FMath::Clamp(Tile.Level, 0, TrackedClassifierLODCount - 1); const int32 TrackedLOD = FMath::Clamp(Tile.Level, 0, TrackedClassifierLODCount - 1);
+40
View File
@@ -22,6 +22,16 @@ class UVoxelDiffLayer;
class UVoxelBiomeDefinition; class UVoxelBiomeDefinition;
struct FTunnelCoreWorldEvaluation; struct FTunnelCoreWorldEvaluation;
/** Guards which may be disabled only by the sealed-solid proof's soundness tests. */
enum class EVoxelSealedSolidProofGuard : uint8
{
None = 0,
DiffLayer = 1u << 0,
Passages = 1u << 1,
OriginLanding = 1u << 2,
DisturbanceChasms = 1u << 3,
};
//============================================================================= //=============================================================================
// LOD-AWARE OCTAVE REDUCTION (T2.b) // LOD-AWARE OCTAVE REDUCTION (T2.b)
//============================================================================= //=============================================================================
@@ -166,6 +176,27 @@ public:
int32 SizeX, int32 SizeY, int32 SizeZ) const; int32 SizeX, int32 SizeY, int32 SizeZ) const;
void EndDensityBlock() const; void EndDensityBlock() const;
/**
* Prove that the mesher's core lattice is entirely solid from the cheap, structural rules
* that own inter-strate gaps and boundary seals. This never samples density. It is intended
* for an empty-tile fast path; a false answer is harmless, while a true answer must make every
* core lattice vertex negative in the final MC field.
*
* The proof is deliberately narrower than ClassifyTile: it accepts only a tile made entirely
* of gap/seal lattice rows and rejects recipes/composer overrides plus every known air-writing
* structural feature. The mesher's +/-1 normal halo is not part of the cell lattice, matching
* ClassifyTile and GenerateMesh.
*/
bool TryProveSealedSolidTile(const FIntVector& OriginVoxels, int32 Step,
int32 CellsPerAxis) const;
#if WITH_DEV_AUTOMATION_TESTS
/** Test hook: disable named proof guards and require the brute-force oracle to catch it. */
bool TryProveSealedSolidTileForTest(
const FIntVector& OriginVoxels, int32 Step, int32 CellsPerAxis,
uint8 DisabledGuards) const;
#endif
/** /**
* Densité pour une strate TunnelNetwork (rooms + tunnels + worm noise). * Densité pour une strate TunnelNetwork (rooms + tunnels + worm noise).
* Utilisée en interne par GetDensityAt quand la strate est de ce type. * Utilisée en interne par GetDensityAt quand la strate est de ce type.
@@ -385,6 +416,15 @@ public:
int32 CellsPerAxis, int32 CellsPerAxis,
struct FVoxelTileClassificationStats* OutStats) const; struct FVoxelTileClassificationStats* OutStats) const;
/**
* Prove the exact constant-air field above/below the strate layout for a mesher lattice.
* This is separate from TryProveSealedSolidTile: it never supplies collision support and its
* caller records AllAir, not AllSolid. It shares the layout/diff/structural guards with the
* production classifier and returns false for every mixed or uncertain box.
*/
bool TryProveOutOfLayoutAirTile(const FIntVector& OriginVoxels, int32 Step,
int32 CellsPerAxis) const;
private: private:
/** Identité process-unique du propriétaire des caches `CP_*` thread_local. /** Identité process-unique du propriétaire des caches `CP_*` thread_local.
* Process-unique owner identity for the `CP_*` thread-local cache key. */ * Process-unique owner identity for the `CP_*` thread-local cache key. */
@@ -14,6 +14,7 @@ namespace VoxelForgeStartupTrace
int32 TileZ = 0; int32 TileZ = 0;
int32 Level = -1; int32 Level = -1;
int32 Verdict = -1; // Mixed=0, AllSolid=1, AllAir=2, -1=not classified (sheet/early path) int32 Verdict = -1; // Mixed=0, AllSolid=1, AllAir=2, -1=not classified (sheet/early path)
bool bSealedSolidProof = false;
bool bEmpty = true; bool bEmpty = true;
int32 Triangles = 0; int32 Triangles = 0;
// This is worker-result -> game-thread mesh submission. Collision readiness is a later // This is worker-result -> game-thread mesh submission. Collision readiness is a later
+8
View File
@@ -107,6 +107,8 @@ struct FChunkResult
uint64 GenerationEndCycles = 0; uint64 GenerationEndCycles = 0;
uint64 ApplyStartCycles = 0; uint64 ApplyStartCycles = 0;
int32 ClassifyVerdict = -1; double ClassifySeconds = 0.0; double MeshSeconds = 0.0; double StreamSeconds = 0.0; int32 NumTriangles = 0; int32 ClassifyVerdict = -1; double ClassifySeconds = 0.0; double MeshSeconds = 0.0; double StreamSeconds = 0.0; int32 NumTriangles = 0;
bool bSealedSolidProof = false; // true when the cheap gap/seal proof skipped meshing
bool bOutOfLayoutAirProof = false; // true when the exact constant-air guard skipped meshing
// Clean, low-overhead work counters. These are carried with the result so the worker can // Clean, low-overhead work counters. These are carried with the result so the worker can
// count exact validation and mesher density calls locally, then publish one aggregate update. // count exact validation and mesher density calls locally, then publish one aggregate update.
int64 ValidationDensityCalls = 0; int64 ValidationDensityCalls = 0;
@@ -934,6 +936,9 @@ public:
std::atomic<uint64> TotalObsoleteWorkerTasks{0}; std::atomic<uint64> TotalObsoleteWorkerTasks{0};
std::atomic<uint64> TotalValidationDensityCalls{0}; std::atomic<uint64> TotalValidationDensityCalls{0};
std::atomic<uint64> TotalMesherDensityCalls{0}; std::atomic<uint64> TotalMesherDensityCalls{0};
std::atomic<uint64> TotalSealedSolidProofCandidates{0};
std::atomic<uint64> TotalSealedSolidProofSkips{0};
std::atomic<uint64> TotalOutOfLayoutAirProofSkips{0};
std::atomic<uint64> OuterClassifierCallsByLOD[TrackedClassifierLODCount]{}; std::atomic<uint64> OuterClassifierCallsByLOD[TrackedClassifierLODCount]{};
std::atomic<uint64> OuterClassifierVerdictsByLOD[TrackedClassifierLODCount][3]{}; std::atomic<uint64> OuterClassifierVerdictsByLOD[TrackedClassifierLODCount][3]{};
@@ -959,6 +964,9 @@ public:
// by explicit command-line switches; they do not alter ordinary player movement or streaming. // by explicit command-line switches; they do not alter ordinary player movement or streaming.
bool bHeadlessStreamingTestMovement = false; bool bHeadlessStreamingTestMovement = false;
bool bHeadlessStreamingTestExitRequested = false; bool bHeadlessStreamingTestExitRequested = false;
bool bStartupTraceThroughCrossing = false;
bool bHeadlessStreamingTestCenterOverride = false;
FVector HeadlessStreamingTestCenterVoxel = FVector::ZeroVector;
int32 HeadlessStreamingTestMoveAttempts = 0; int32 HeadlessStreamingTestMoveAttempts = 0;
double HeadlessStreamingTestDistanceCm = 0.0; double HeadlessStreamingTestDistanceCm = 0.0;
float HeadlessStreamingTestSpeedCmPerSecond = 0.0f; float HeadlessStreamingTestSpeedCmPerSecond = 0.0f;