fix(classifier): exact lattice check before a uniform verdict; ceiling cause is NOT false certificates

ParameterRoll's 5 false box verdicts are fixed: fine room-graph intervals are now checked against exact lattice samples before a uniform verdict is accepted, while the SDF bound keeps its conservative lower bound. Lattice sweep 600 tiles / 0 violations; ParameterRoll passes with 9,577 exact lattice leaves and 774 interval disagreements caught.

The owner's ceiling holes are NOT explained by this: in his recorded session outer_classifier_mode=0, solid-proof skips were 0, and air-proof skips were outside the strate. The trace points at the far-field sheet system instead: sheet tile L5 (0,1,0) is deliberately empty inside the sheet hole while its bridge tile L4 (0,1,-1) meshed 662 ceiling triangles. Continuous ceiling is NOT verified: every render launch failed (D3D12 driver error, stalls before map load, the 6 GB cap), so the round produced no matched after-render.

Cost note: this round used 3.46M tokens on Luna 6 at max, most of it burned on failed render launches. Verification by render is unreliable in this harness; verify ceiling continuity from tile/geometry data instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-23 06:30:05 +02:00
1 parent 30253f0e89
commit ea3f0d9a52
7 files changed
+410 -18

No files matched your search

@@ -13,6 +13,7 @@
#include "VoxelStrateComposer.h"
#include "VoxelStrateMeasure.h"
#include "VoxelTerrainOpDefinition.h"
#include "VoxelDensityOpStack.h"
namespace
{
@@ -238,8 +239,11 @@ namespace
int32 AllAir = 0;
int32 Proved = 0;
int64 CheckedVoxels = 0;
int64 ExactIntervalChecks = 0;
int64 ExactIntervalDisagreements = 0;
int32 Violations = 0;
FString FirstViolation;
TArray<FString> ViolationDetails;
};
/**
@@ -248,7 +252,8 @@ namespace
* to expose. The report is aggregate: there is no per-sample logging.
*/
FBoxVerdictReport VF_CheckRolledBoxVerdicts(
const VoxelForgeTest::FTestWorld& World, int32 CandidateIndex, int32 StrateIndex)
const VoxelForgeTest::FTestWorld& World, int32 CandidateIndex, int32 StrateIndex,
const FVoxelStrateRollInfo& Roll)
{
FBoxVerdictReport Report;
const UVoxelGenerator* Generator = World.Generator.Get();
@@ -286,8 +291,18 @@ namespace
(float)(Origin.Y + (Cells + 1) * Step),
(float)(Origin.Z + (Cells + 1) * Step)));
(void)Box; // The production API receives the equivalent origin/step/cell box.
const FBox ClassifierBox(
FVector(static_cast<float>(Origin.X), static_cast<float>(Origin.Y),
static_cast<float>(Origin.Z)),
FVector(static_cast<float>(Origin.X + Cells * Step),
static_cast<float>(Origin.Y + Cells * Step),
static_cast<float>(Origin.Z + Cells * Step)));
const EVoxelTileClass Verdict = Generator->ClassifyTile(Origin, Step, Cells);
FVoxelTileClassificationStats ClassifyStats;
const EVoxelTileClass Verdict = Generator->ClassifyTile(
Origin, Step, Cells, &ClassifyStats);
Report.ExactIntervalChecks += ClassifyStats.ExactCoreIntervalChecks;
Report.ExactIntervalDisagreements += ClassifyStats.ExactCoreIntervalDisagreements;
if (Verdict == EVoxelTileClass::Mixed)
{
++Report.Mixed;
@@ -305,6 +320,15 @@ namespace
const bool bClaimsSolid = Verdict == EVoxelTileClass::AllSolid;
bool bBoxBad = false;
bool bDiagnosticStackReady = false;
bool bDiagnosticStackBuilt = false;
FVoxelOpStack DiagnosticStack;
FVoxelOpContext DiagnosticContext;
EVoxelTileClass DiagnosticStackVerdict = EVoxelTileClass::Mixed;
int32 DiagnosticSolidKiller = INDEX_NONE;
int32 DiagnosticAirKiller = INDEX_NONE;
TArray<FVoxelOpStack::FOpBoxDiagnostic> OpDiagnostics;
FString WriterRouteDetails;
// ClassifyTile covers the cell vertices passed to it: g is [0, Cells]. The mesher
// samples a separate halo, but that halo is not part of this production verdict's
// proof domain and must not turn a sound tile into a false failure.
@@ -325,6 +349,173 @@ namespace
{
++Report.Violations;
bBoxBad = true;
if (!bDiagnosticStackReady)
{
bDiagnosticStackReady = true;
int32 SlotTop = 0, SlotBottom = 0;
FVoxelStrateArchetypeParams RuntimeParams = Roll.ArchetypeParams;
bDiagnosticStackBuilt = World.GetSlotVoxelZRange(
StrateIndex, SlotTop, SlotBottom);
if (bDiagnosticStackBuilt)
{
VF_SetStrateArchetypeRuntimeBounds(
RuntimeParams, static_cast<float>(SlotTop) + 1.0f,
static_cast<float>(SlotBottom));
bDiagnosticStackBuilt = VF_BuildNativeStrateStackForCandidate(
Roll.Archetype, RuntimeParams, World.Settings->Seed,
World.Generator->OriginSpineRadius,
World.Generator->WorldRadiusVoxels,
World.Generator->EdgeSealThickness,
World.StrateManager.Get(), DiagnosticStack, DiagnosticContext);
}
if (bDiagnosticStackBuilt)
{
DiagnosticContext.ChunkCoord = FIntVector(
0, 0, VF_FloorDiv(Origin.Z, CHUNK_SIZE));
DiagnosticContext.Step = Step;
DiagnosticContext.LayoutVersion =
World.StrateManager->GetLayoutVersion();
DiagnosticContext.WorldRadiusVoxels =
World.Generator->WorldRadiusVoxels;
DiagnosticContext.EdgeSealThickness =
World.Generator->EdgeSealThickness;
DiagnosticContext.bUseLatticeProof = true;
DiagnosticContext.bTightenWarpProof = Step <= 2;
DiagnosticContext.LatticeOriginVoxels = Origin;
DiagnosticStack.PrepareChunk(DiagnosticContext);
DiagnosticStackVerdict = DiagnosticStack.ClassifyBoxAttributed(
ClassifierBox, DiagnosticContext,
DiagnosticSolidKiller, DiagnosticAirKiller);
DiagnosticStack.DiagnoseBox(
ClassifierBox, DiagnosticContext, Step, OpDiagnostics,
&DiagnosticStackVerdict);
}
}
if (WriterRouteDetails.IsEmpty())
{
const FIntVector SampleChunk(
VF_FloorDiv(static_cast<int32>(X), CHUNK_SIZE),
VF_FloorDiv(static_cast<int32>(Y), CHUNK_SIZE),
VF_FloorDiv(static_cast<int32>(Z), CHUNK_SIZE));
int32 SampleSlotTop = 0;
int32 SampleSlotBottom = 0;
int32 RecipeSeedAtSample = 0;
ECaveGeneratorType RecipeArchetypeAtSample = ECaveGeneratorType::TunnelNetwork;
FVoxelStrateArchetypeParams RecipeParamsAtSample;
FVoxelOpStackRecipe RecipeAtSample;
const bool bSampleHasSlot = World.StrateManager->GetStrateChunkZBounds(
SampleChunk.Z, SampleSlotTop, SampleSlotBottom);
const bool bSampleHasRecipe = World.StrateManager->GetRecipeForChunk(
SampleChunk, RecipeSeedAtSample, RecipeArchetypeAtSample,
RecipeParamsAtSample, RecipeAtSample);
const float PassageSdf = World.StrateManager->EvaluateModifierSDF(X, Y, Z);
const float MaxPassageCarve =
World.StrateManager->MaxPassageCarveFactorNearLattice(
ClassifierBox, Origin, Step);
const bool bPassageNear = World.StrateManager->AnyPassageNearLattice(
ClassifierBox, Origin, Step);
const bool bPassageAirPostNear = World.StrateManager->AnyPassageAirPostNearLattice(
ClassifierBox, Origin, Step, 8.0f, World.Generator->EdgeSealThickness);
const bool bLandingFloorNear =
World.StrateManager->AnyPassageLandingFloorNearLattice(
ClassifierBox, Origin, Step);
const bool bStructuralPostNear =
World.StrateManager->AnyPassageStructuralPostNearLattice(
ClassifierBox, Origin, Step);
WriterRouteDetails = FString::Printf(
TEXT("sample_chunk=(%d,%d,%d) gap=%d slot_bounds=%d:[%d..%d] "
"generator=%s operator_stack=%d recipe=%d world_radius=%.6g "
"passage_sdf=%.9g passage_max_carve=%.9g "
"passage_near=%d passage_air_post=%d landing_floor=%d structural_post=%d"),
SampleChunk.X, SampleChunk.Y, SampleChunk.Z,
World.StrateManager->IsGapChunk(SampleChunk) ? 1 : 0,
bSampleHasSlot ? 1 : 0, SampleSlotTop, SampleSlotBottom,
VF_GetStrateArchetypeName(
World.StrateManager->GetGeneratorTypeForChunk(SampleChunk)),
World.StrateManager->UsesOperatorStackForChunk(SampleChunk) ? 1 : 0,
bSampleHasRecipe ? 1 : 0, World.Generator->WorldRadiusVoxels,
PassageSdf, MaxPassageCarve, bPassageNear ? 1 : 0,
bPassageAirPostNear ? 1 : 0, bLandingFloorNear ? 1 : 0,
bStructuralPostNear ? 1 : 0);
}
float StackDensity = FLT_MAX;
FString StackAttribution = TEXT("stack diagnostic unavailable");
if (bDiagnosticStackBuilt)
{
StackDensity = DiagnosticStack.EvalMC(X, Y, Z);
FString BoundViolations;
FString SdfIntervalViolations;
FString OperatorIntervals;
for (const FVoxelOpStack::FOpBoxDiagnostic& OpDiagnostic : OpDiagnostics)
{
if (OpDiagnostic.ForcedVerdict != EVoxelTileClass::Mixed
|| OpDiagnostic.bHasDensityDelta
|| OpDiagnostic.bHasSdfValue)
{
if (!OperatorIntervals.IsEmpty()) { OperatorIntervals += TEXT("; "); }
OperatorIntervals += FString::Printf(
TEXT("%s(forced=%d,effect=%d,carve=%.6g,fill=%.6g,delta=[%.6g,%.6g],sdf_bound=[%.6g,%.6g],sdf_actual=[%.6g,%.6g])"),
*OpDiagnostic.Name,
static_cast<int32>(OpDiagnostic.ForcedVerdict),
static_cast<int32>(OpDiagnostic.Effect),
OpDiagnostic.MaxCarve, OpDiagnostic.MaxFill,
OpDiagnostic.ActualDensityDeltaMin,
OpDiagnostic.ActualDensityDeltaMax,
OpDiagnostic.SdfBoundMin, OpDiagnostic.SdfBoundMax,
OpDiagnostic.ActualSdfMin, OpDiagnostic.ActualSdfMax);
}
if (OpDiagnostic.bCarveBoundViolated
|| OpDiagnostic.bFillBoundViolated)
{
if (!BoundViolations.IsEmpty()) { BoundViolations += TEXT(", "); }
BoundViolations += FString::Printf(
TEXT("%s(carve=%d,fill=%d,delta=[%.6g,%.6g])"),
*OpDiagnostic.Name,
OpDiagnostic.bCarveBoundViolated ? 1 : 0,
OpDiagnostic.bFillBoundViolated ? 1 : 0,
OpDiagnostic.ActualDensityDeltaMin,
OpDiagnostic.ActualDensityDeltaMax);
}
if (OpDiagnostic.bHasSdfBound && OpDiagnostic.bHasSdfValue
&& (OpDiagnostic.ActualSdfMin < OpDiagnostic.SdfBoundMin - 1.0e-4f
|| OpDiagnostic.ActualSdfMax > OpDiagnostic.SdfBoundMax + 1.0e-4f))
{
if (!SdfIntervalViolations.IsEmpty())
{
SdfIntervalViolations += TEXT(", ");
}
SdfIntervalViolations += FString::Printf(
TEXT("%s(bound=[%.6g,%.6g],actual=[%.6g,%.6g])"),
*OpDiagnostic.Name, OpDiagnostic.SdfBoundMin,
OpDiagnostic.SdfBoundMax, OpDiagnostic.ActualSdfMin,
OpDiagnostic.ActualSdfMax);
}
}
StackAttribution = FString::Printf(
TEXT("stack_verdict=%s solid_killer=%s air_killer=%s stack_density=%.9g "
"operator_bound_violations=[%s] sdf_interval_violations=[%s] "
"op_intervals=[%s] "
"refine_nodes=%u depth=%u exact_core_samples=%u exact_final_samples=%u "
"post_nodes=%u"),
DiagnosticStackVerdict == EVoxelTileClass::AllSolid ? TEXT("AllSolid")
: DiagnosticStackVerdict == EVoxelTileClass::AllAir ? TEXT("AllAir")
: TEXT("Mixed"),
DiagnosticStack.GetOpDebugName(DiagnosticSolidKiller),
DiagnosticStack.GetOpDebugName(DiagnosticAirKiller),
StackDensity,
BoundViolations.IsEmpty() ? TEXT("none") : *BoundViolations,
SdfIntervalViolations.IsEmpty() ? TEXT("none") : *SdfIntervalViolations,
OperatorIntervals.IsEmpty() ? TEXT("none") : *OperatorIntervals,
ClassifyStats.RefineNodes, ClassifyStats.MaxRefinementDepth,
ClassifyStats.ExactCoreSamples, ClassifyStats.ExactFinalSamples,
ClassifyStats.NeedsFinalFieldNodes);
}
Report.ViolationDetails.Add(FString::Printf(
TEXT("candidate %d box %d origin (%d,%d,%d) said %s; final density at "
"(%0.f,%0.f,%0.f) = %.9g; %s; %s"),
CandidateIndex, BoxIndex, Origin.X, Origin.Y, Origin.Z,
bClaimsSolid ? TEXT("AllSolid") : TEXT("AllAir"),
X, Y, Z, Density, *WriterRouteDetails, *StackAttribution));
if (Report.FirstViolation.IsEmpty())
{
Report.FirstViolation = FString::Printf(
@@ -801,6 +992,8 @@ bool FVoxelForgeComposerParameterRollTest::RunTest(const FString& Parameters)
int32 TotalBoxAllAir = 0;
int32 TotalBoxProved = 0;
int64 TotalBoxCheckedVoxels = 0;
int64 TotalExactIntervalChecks = 0;
int64 TotalExactIntervalDisagreements = 0;
int32 TotalBoxViolations = 0;
int32 NumTunnelLikeCandidates = 0;
int32 NumCandidatesWithLiveDetail = 0;
@@ -996,16 +1189,23 @@ bool FVoxelForgeComposerParameterRollTest::RunTest(const FString& Parameters)
}
const FBoxVerdictReport BoxReport = VF_CheckRolledBoxVerdicts(
World, CandidateIndex, CandidateStrateIndex);
World, CandidateIndex, CandidateStrateIndex, Roll);
TotalBoxMixed += BoxReport.Mixed;
TotalBoxAllSolid += BoxReport.AllSolid;
TotalBoxAllAir += BoxReport.AllAir;
TotalBoxProved += BoxReport.Proved;
TotalBoxCheckedVoxels += BoxReport.CheckedVoxels;
TotalExactIntervalChecks += BoxReport.ExactIntervalChecks;
TotalExactIntervalDisagreements += BoxReport.ExactIntervalDisagreements;
TotalBoxViolations += BoxReport.Violations;
if (BoxReport.Violations > 0)
{
BoxViolationCandidates.Add(CandidateIndex);
for (const FString& ViolationDetail : BoxReport.ViolationDetails)
{
AddInfo(FString::Printf(TEXT("ROLLED BOX-VERDICT COUNTEREXAMPLE: %s"),
*ViolationDetail));
}
if (FirstBoxViolation.IsEmpty())
{
FirstBoxViolation = BoxReport.FirstViolation;
@@ -1098,6 +1298,10 @@ bool FVoxelForgeComposerParameterRollTest::RunTest(const FString& Parameters)
TotalBoxMixed, TotalBoxAllSolid, TotalBoxAllAir, TotalBoxProved,
TotalBoxCheckedVoxels, TotalBoxViolations,
BoxViolationCandidates.Num() == 0 ? TEXT("none") : TEXT("see first violation below")));
AddInfo(FString::Printf(
TEXT("Fine room-graph interval checks: %lld exact lattice leaves; %lld interval verdicts "
"disagreed with the exact child samples."),
TotalExactIntervalChecks, TotalExactIntervalDisagreements));
if (!BoxViolationCandidates.IsEmpty())
{
AddError(FString::Printf(
@@ -6753,7 +6753,12 @@ namespace
FVoxelBoxSdfInterval Own;
if (bUseExactLatticeWarp)
{
Own.Set(ExactSdfLower, FLT_MAX);
// Exact per-primitive scans are a tightening only. Their candidate-domain
// filters can miss a warped lattice query near a primitive, so retain the
// broad lower bound over the AABB that contains every warped sample. The
// minimum stays useful when both agree and remains safe when an exact scan is
// incomplete.
Own.Set(FMath::Min(ExactSdfLower, CheapSdfLower), FLT_MAX);
}
else if (!bAnyPrimitive)
{
@@ -6328,6 +6328,34 @@ static EVoxelTileClass VF_ClassifyBoxRefined(const FVoxelOpStack& Stack,
static_cast<float>(Box.Max.Z - Box.Min.Z));
const bool bSmallFinalFieldBox = bNeedsFinalField
&& FinalFieldBoxExtent <= 8.0f;
// A room-graph SDF interval can be conservative at the parent while a fine child still
// resolves uniformly through a state-aware SDF writer (for example the worm mask). Before
// using that fine interval to skip geometry, certify the exact MC lattice in the child.
// This runs only after a mixed parent has been refined to a small room-graph child; clear
// uniform roots and all non-room stacks keep the interval-only fast path.
const bool bNeedsExactRoomIntervalCheck = bHasRoomGeometry
&& Context.bUseLatticeProof && Context.bTightenWarpProof
&& !bNeedsFinalField && Whole != EVoxelTileClass::Mixed
&& Depth >= 3 && FinalFieldBoxExtent <= 8.0f;
if (bNeedsExactRoomIntervalCheck)
{
int32 ExactIX0 = 0, ExactIY0 = 0, ExactIZ0 = 0;
int32 ExactIX1 = 0, ExactIY1 = 0, ExactIZ1 = 0;
int64 ExactCount = 0;
if (GetLatticeBounds(Box, ExactIX0, ExactIY0, ExactIZ0,
ExactIX1, ExactIY1, ExactIZ1, ExactCount)
&& ExactCount <= 8192)
{
if (Stats) { ++Stats->ExactCoreIntervalChecks; }
const EVoxelTileClass Exact = ClassifyExactLatticeLeaf(Box, false);
if (Exact != Whole && Stats)
{
++Stats->ExactCoreIntervalDisagreements;
}
return Exact;
}
}
if (bSmallFinalFieldBox)
{
int32 ExactIX0 = 0, ExactIY0 = 0, ExactIZ0 = 0;
+94 -2
View File
@@ -39,6 +39,8 @@
#include "Materials/MaterialParameterCollection.h"
#include "Kismet/KismetMaterialLibrary.h"
#include "Engine/VolumeTexture.h"
#include "Engine/Engine.h"
#include "Camera/CameraActor.h"
#include "VoxelAtmosphereManager.h"
#include "DrawDebugHelpers.h"
#include "IImageWrapper.h"
@@ -1533,6 +1535,55 @@ void AVoxelWorld::BeginPlay()
return;
}
int32 TestRenderDistanceChunks = 0;
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.TestRenderDistanceChunks="),
TestRenderDistanceChunks)
&& TestRenderDistanceChunks > 0)
{
if (UVoxelSettings* TestSettings = DuplicateObject<UVoxelSettings>(Settings, this))
{
const int32 AuthoredRenderDistance = Settings->RenderDistanceChunks;
TestSettings->RenderDistanceChunks = TestRenderDistanceChunks;
Settings = TestSettings;
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTestCeiling] transient_render_distance_chunks=%d authored=%d"),
TestRenderDistanceChunks, AuthoredRenderDistance);
}
}
int32 TestCeilingView = 0;
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingView="), TestCeilingView);
bTestCeilingViewEnabled = TestCeilingView != 0;
if (bTestCeilingViewEnabled)
{
TestCeilingCameraWorldPosition = FVector(10583.003176, 20477.525456, -38442.958976);
TestCeilingCameraRotation = FRotator(78.399901, 255.200004, 0.0);
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingCameraX="),
TestCeilingCameraWorldPosition.X);
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingCameraY="),
TestCeilingCameraWorldPosition.Y);
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingCameraZ="),
TestCeilingCameraWorldPosition.Z);
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingCameraPitch="),
TestCeilingCameraRotation.Pitch);
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingCameraYaw="),
TestCeilingCameraRotation.Yaw);
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingCaptureDelaySeconds="),
TestCeilingCaptureDelaySeconds);
TestCeilingCaptureDelaySeconds = FMath::Max(TestCeilingCaptureDelaySeconds, 1.0f);
TestCeilingViewBeginSeconds = FPlatformTime::Seconds();
bHeadlessStreamingTestCenterOverride = true;
HeadlessStreamingTestCenterVoxel = WorldToLocalVoxel(TestCeilingCameraWorldPosition);
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTestCeiling] camera_world_cm=(%.3f,%.3f,%.3f) "
"camera_rotation=(%.3f,%.3f,%.3f) center_vox=(%.3f,%.3f,%.3f) capture_delay_s=%.1f"),
TestCeilingCameraWorldPosition.X, TestCeilingCameraWorldPosition.Y,
TestCeilingCameraWorldPosition.Z, TestCeilingCameraRotation.Pitch,
TestCeilingCameraRotation.Yaw, TestCeilingCameraRotation.Roll,
HeadlessStreamingTestCenterVoxel.X, HeadlessStreamingTestCenterVoxel.Y,
HeadlessStreamingTestCenterVoxel.Z, TestCeilingCaptureDelaySeconds);
}
// The owner's current DA_Settings is intentionally a one-strate surface layout, so it cannot
// exercise the separate inter-strate traversal regression. The explicit headless crossing
// switch uses a transient copy of the authored four-definition corpus; no package is modified
@@ -3667,6 +3718,45 @@ void AVoxelWorld::Tick(float DeltaTime)
ProcessUnloadQueue();
MaybeFinishHeadlessStreamingTest();
if (bTestCeilingViewEnabled)
{
UWorld* World = GetWorld();
APlayerController* PC = World ? World->GetFirstPlayerController() : nullptr;
if (PC && !TestCeilingCamera.IsValid())
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
ACameraActor* Camera = World->SpawnActor<ACameraActor>(
ACameraActor::StaticClass(), TestCeilingCameraWorldPosition,
TestCeilingCameraRotation, SpawnParams);
if (IsValid(Camera))
{
TestCeilingCamera = Camera;
PC->SetViewTarget(Camera);
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTestCeiling] camera_installed=1 applied_visible_tiles=%llu"),
static_cast<unsigned long long>(AppliedVisibleTileCount));
}
}
if (TestCeilingCamera.IsValid() && !bTestCeilingCaptureRequested
&& AppliedVisibleTileCount >= 16
&& FPlatformTime::Seconds() - TestCeilingViewBeginSeconds
>= static_cast<double>(TestCeilingCaptureDelaySeconds))
{
bTestCeilingCaptureRequested = true;
const bool bExecSucceeded = GEngine
&& GEngine->Exec(World, TEXT("HighResShot 1"));
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeTestCeiling] capture_requested=%d elapsed_s=%.3f "
"visible_tiles=%llu triangles=%llu"),
bExecSucceeded ? 1 : 0,
FPlatformTime::Seconds() - TestCeilingViewBeginSeconds,
static_cast<unsigned long long>(AppliedVisibleTileCount),
static_cast<unsigned long long>(AppliedTriangleCount));
}
}
#if ENABLE_DRAW_DEBUG
// Density-volume overlay (step 1a): cyan boxes for solid level-0 cells near the player.
if (DensityVolume) { DensityVolume->DebugDraw(); }
@@ -6068,7 +6158,7 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
/ static_cast<double>(WormEligibleSamples)
: 0.0;
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 ceiling_triangles=%d "
"band=(%d,%d) "
"verdict=%d proof=%d air_proof=%d classify=%.6f mesh=%.6f streams=%.6f seconds=%.6f "
"cache_build=%.6f evaluation=%.6f "
@@ -6089,7 +6179,8 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
"worm_skip_rate=%.6f"),
Tile.Coord.X, Tile.Coord.Y, Tile.Coord.Z, Tile.Level, Step, Cells,
bSheetTile ? 1 : 0, Result.bAborted ? 1 : 0,
Result.bEmpty ? 1 : 0, Result.NumTriangles, BandChunkLo, BandChunkHi,
Result.bEmpty ? 1 : 0, Result.NumTriangles, Result.NumCeilingTriangles,
BandChunkLo, BandChunkHi,
ClassifyVerdict, Result.bSealedSolidProof ? 1 : 0,
Result.bOutOfLayoutAirProof ? 1 : 0,
ClassifySeconds, MeshSeconds, StreamSeconds,
@@ -6428,6 +6519,7 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
// AND cap in one tile — entirely with the winner's material.)
const int32 NumTris = MeshData.Triangles.Num() / 3;
Result.NumTriangles = NumTris;
Result.NumCeilingTriangles = MeshData.NumCeilingTriangles;
Result.bHasCeilingTris = MeshData.NumCeilingTriangles > 0;
Result.bHasGroundTris = NumTris > MeshData.NumCeilingTriangles;
}
@@ -504,6 +504,8 @@ struct FVoxelTileClassificationStats
uint32 MaxRefinementDepth = 0;
uint32 ExactCoreSamples = 0;
uint32 ExactFinalSamples = 0;
uint32 ExactCoreIntervalChecks = 0;
uint32 ExactCoreIntervalDisagreements = 0;
uint32 ExactCoreCacheHits = 0;
uint32 ExactFinalCacheHits = 0;
uint32 ExactCoreLeaves = 0;
+12 -1
View File
@@ -32,6 +32,7 @@ class UVolumeTexture;
class UMaterialInterface;
class UMaterialInstanceDynamic;
class FScopedGenerationPause;
class ACameraActor;
namespace RealtimeMesh { struct FRealtimeMeshStreamSet; } // T1.f — worker-built geometry buffers
/** Per-request cancellation state. The game thread flips the flag when a pending tile leaves the
@@ -107,7 +108,7 @@ struct FChunkResult
uint64 GenerationStartCycles = 0;
uint64 GenerationEndCycles = 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; int32 NumCeilingTriangles = 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
@@ -1011,6 +1012,16 @@ public:
double HeadlessStreamingTestBeginSeconds = 0.0;
double HeadlessStreamingTestLastElapsedSeconds = 0.0;
// Transient owner-view render hook. Enabled only by explicit voxel.TestCeilingView arguments;
// the camera and smaller test radius are held in memory and never saved to a package.
bool bTestCeilingViewEnabled = false;
bool bTestCeilingCaptureRequested = false;
TWeakObjectPtr<ACameraActor> TestCeilingCamera;
FVector TestCeilingCameraWorldPosition = FVector::ZeroVector;
FRotator TestCeilingCameraRotation = FRotator::ZeroRotator;
double TestCeilingViewBeginSeconds = 0.0;
float TestCeilingCaptureDelaySeconds = 18.0f;
// Explicit high-speed collision-gate repro. The streamer stays centred at the launch tile while
// the character uses ordinary swept movement across the still-pending desired set. The test
// distinguishes a gate hold from a full horizontal stop so a fall-only gate can be validated.
+61 -11
View File
@@ -28,6 +28,7 @@ $EngineRoot = 'E:\Program Files\Epic Games\UE_5.7'
$EditorExe = Join-Path $EngineRoot 'Engine\Binaries\Win64\UnrealEditor-Cmd.exe'
$UbtDll = Join-Path $EngineRoot 'Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.dll'
$LaunchTimeoutSeconds = 1800
$UnrealPrivateMemoryLimitBytes = 6GB
# The VoxelForge category includes two late, several-minute op-stack sweeps. They remain in
# the suite; when the serialized aggregate launch reaches this 30-minute guard, validate those
# named tests with -TestFilter so every test still runs under the standing launch limit.
@@ -264,17 +265,48 @@ function Get-NewCrashes($Before, [datetime]$StartedUtc) {
return @($new)
}
function Wait-OwnedProcess($Process, [int]$TimeoutSeconds) {
$finished = $Process.WaitForExit($TimeoutSeconds * 1000)
if ($finished) {
[void]$Process.WaitForExit()
return [ordered]@{ timed_out = $false; exit_code = [int]$Process.ExitCode }
function Wait-OwnedProcess($Process, [int]$TimeoutSeconds, [switch]$EnforceMemoryLimit) {
$watch = [Diagnostics.Stopwatch]::StartNew()
$peakPrivateBytes = 0L
while ($watch.Elapsed.TotalSeconds -lt $TimeoutSeconds) {
if ($Process.HasExited) {
[void]$Process.WaitForExit()
return [ordered]@{
timed_out = $false
memory_limit_hit = $false
peak_private_bytes = $peakPrivateBytes
exit_code = [int]$Process.ExitCode
}
}
if ($EnforceMemoryLimit) {
$owned = Get-Process -Id ([int]$Process.Id) -ErrorAction SilentlyContinue
if ($null -ne $owned) {
$peakPrivateBytes = [Math]::Max($peakPrivateBytes, [int64]$owned.PrivateMemorySize64)
if ([int64]$owned.PrivateMemorySize64 -gt $UnrealPrivateMemoryLimitBytes) {
# This PID came from our Start-Process call. Do not use a name-wide kill here.
try { Stop-Process -Id ([int]$Process.Id) -Force -ErrorAction Stop } catch { }
try { [void]$Process.WaitForExit(5000) } catch { }
return [ordered]@{
timed_out = $false
memory_limit_hit = $true
peak_private_bytes = $peakPrivateBytes
exit_code = $null
}
}
}
}
Start-Sleep -Milliseconds 1000
}
# This PID came from our Start-Process call. Never use a name-based kill or a broad process
# query here: an owner's editor may be unrelated and must remain untouched.
try { Stop-Process -Id ([int]$Process.Id) -Force -ErrorAction Stop } catch { }
try { [void]$Process.WaitForExit(5000) } catch { }
return [ordered]@{ timed_out = $true; exit_code = $null }
return [ordered]@{
timed_out = $true
memory_limit_hit = $false
peak_private_bytes = $peakPrivateBytes
exit_code = $null
}
}
function Invoke-UnrealLaunch([string]$Kind, [string[]]$Arguments, [string]$LogPath,
@@ -293,6 +325,8 @@ function Invoke-UnrealLaunch([string]$Kind, [string[]]$Arguments, [string]$LogPa
ended_utc = $null
exit_code = $null
timed_out = $false
memory_limit_hit = $false
peak_private_bytes = 0L
crash_reporters_reaped = @()
new_crashes = @()
log = $LogPath
@@ -309,12 +343,22 @@ function Invoke-UnrealLaunch([string]$Kind, [string[]]$Arguments, [string]$LogPa
# -LocalDataCachePath are supplied. Keep this child process inside the run directory.
$env:LOCALAPPDATA = $isolatedLocalAppData
$process = Start-Process -FilePath $EditorExe -ArgumentList $Arguments -WorkingDirectory $HostRoot `
-RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath -WindowStyle Hidden -PassThru
-RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath -WindowStyle Hidden `
-PassThru
try { $process.PriorityClass = [Diagnostics.ProcessPriorityClass]::BelowNormal }
catch {
try { Stop-Process -Id ([int]$process.Id) -Force -ErrorAction Stop } catch { }
throw "Could not lower owned Unreal PID $($process.Id) to BelowNormal: $($_.Exception.Message)"
}
$ledger.pid = [int]$process.Id
$wait = Wait-OwnedProcess $process $LaunchTimeoutSeconds
$wait = Wait-OwnedProcess $process $LaunchTimeoutSeconds -EnforceMemoryLimit
$ledger.timed_out = [bool]$wait.timed_out
$ledger.memory_limit_hit = [bool]$wait.memory_limit_hit
$ledger.peak_private_bytes = [int64]$wait.peak_private_bytes
$ledger.exit_code = $wait.exit_code
$ledger.status = if ($ledger.timed_out) { 'timeout' } elseif ($ledger.exit_code -eq 0) { 'passed' } else { 'failed' }
$ledger.status = if ($ledger.timed_out) { 'timeout' }
elseif ($ledger.memory_limit_hit) { 'memory_limit' }
elseif ($ledger.exit_code -eq 0) { 'passed' } else { 'failed' }
} catch {
$ledger.status = 'launch_error'
$ledger.error = $_.Exception.Message
@@ -356,7 +400,13 @@ function Invoke-BuildProcess([string[]]$Arguments, [string]$LogPath, [string]$Er
$env:DOTNET_NOLOGO = '1'
$env:LOCALAPPDATA = $isolatedLocalAppData
$process = Start-Process -FilePath $dotnet -ArgumentList $Arguments -WorkingDirectory $PluginRoot `
-RedirectStandardOutput $StdoutPath -RedirectStandardError $ErrorPath -WindowStyle Hidden -PassThru
-RedirectStandardOutput $StdoutPath -RedirectStandardError $ErrorPath -WindowStyle Hidden `
-PassThru
try { $process.PriorityClass = [Diagnostics.ProcessPriorityClass]::BelowNormal }
catch {
try { Stop-Process -Id ([int]$process.Id) -Force -ErrorAction Stop } catch { }
throw "Could not lower owned build PID $($process.Id) to BelowNormal: $($_.Exception.Message)"
}
$wait = Wait-OwnedProcess $process $LaunchTimeoutSeconds
} finally {
if ($null -eq $oldDotnetCliHome) { Remove-Item Env:DOTNET_CLI_HOME -ErrorAction SilentlyContinue } else { $env:DOTNET_CLI_HOME = $oldDotnetCliHome }
@@ -894,7 +944,7 @@ try {
$buildStdout = Join-Path $RunRoot 'Logs\build.stdout.log'
$buildError = Join-Path $RunRoot 'Logs\build.err.log'
$buildArgs = @(('"{0}"' -f $UbtDll), 'UnrealEditor', 'Win64', 'Development', ('-Project="{0}"' -f $HostProject),
'-WaitMutex', '-FromMsBuild', '-architecture=x64', '-NoUBA', '-MaxParallelActions=4',
'-WaitMutex', '-FromMsBuild', '-architecture=x64', '-NoUBA', '-MaxParallelActions=2',
('-Log="{0}"' -f $buildLog))
$BuildRecord = Invoke-BuildProcess $buildArgs $buildLog $buildError $buildStdout
if ($BuildRecord.status -ne 'passed') { Add-Failure "staged build $($BuildRecord.status)" }