skip: sound whole-tile proofs for legacy FI/TN/VS fields (L74); FI -30%, TN -22%
The compiled-analytic stratum interval now certifies a legacy tile uniform only when it stays beyond the detail envelope (and smooth-min slack for solid) with no intersecting primitive, edit, native field, composer candidate, passage or active transition; everything else fails closed. The stream layer uses the proof for whole-tile skips; legacy sub-tile skips stay off (two block proofs failed the falsifier in TN). Falsifier: full-grid reference vs skip-enabled run, identical mesh/payload/topology hashes on every tile of all four owner views, Clang and GCC, W1/W8/W12; 20-camera sweep per strate, 0 violations; a too-tight control changed 3/184/233/238 tiles and was caught. Whole view (8 workers): SW 2.61->2.48, FI 7.18->5.05, TN 5.97->4.63, VS 2.70->2.40 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
bd1b2a8f1d
commit
c67b2006aa
15 files changed
+1024
-21
No files matched your search
@@ -18,6 +18,7 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@@ -2155,6 +2156,7 @@ public:
|
||||
EVoxelTileClass Verdict=EVoxelTileClass::Mixed;
|
||||
bool bNativeSurface=false;
|
||||
bool bUnknown=false;
|
||||
bool bSkipSafe=false;
|
||||
};
|
||||
struct FClassificationThreadStats
|
||||
{
|
||||
@@ -5965,6 +5967,14 @@ public:
|
||||
return SharedDensitySource->ClassifyBoxDetailed(Origin,Step,Cells,bUseNativeLatticeProof);
|
||||
using Clock=std::chrono::steady_clock;
|
||||
const auto Start=Clock::now();
|
||||
static const bool bL74TightNegativeControl=[]
|
||||
{
|
||||
const char* Value=std::getenv("VOXELFORGE_L74_TIGHT_PROOF_CONTROL");
|
||||
const bool Enabled=Value&&Value[0]=='1'&&Value[1]=='\0';
|
||||
if(Enabled)
|
||||
std::printf("[L74NegativeControl] enabled=1 bound=base-sign-only-detail-and-primitive-envelope-omitted\n");
|
||||
return Enabled;
|
||||
}();
|
||||
const int32 Pad=std::max(Step,1);
|
||||
int32 BMin[3]={Origin.X-Pad,Origin.Y-Pad,Origin.Z-Pad};
|
||||
int32 BMax[3]={Origin.X+(Cells+1)*Step,Origin.Y+(Cells+1)*Step,Origin.Z+(Cells+1)*Step};
|
||||
@@ -6025,15 +6035,19 @@ public:
|
||||
bCaveOtherStrateOverlap=bCavesOverlap
|
||||
&&(bSurfaceOverlap||bIslandOverlap||bLegacyOverlap);
|
||||
}
|
||||
const auto Finish=[&](EVoxelTileClass Verdict,bool bUnknown=false)
|
||||
const auto Finish=[&](EVoxelTileClass Verdict,bool bUnknown=false,
|
||||
bool bLegacyAnalyticProof=false)
|
||||
{
|
||||
const bool bNative=(bSurfaceOverlap&&!bOtherStrateOverlap)
|
||||
||(bIslandOverlap&&!bIslandOtherStrateOverlap)
|
||||
||(bCavesOverlap&&!bCaveOtherStrateOverlap);
|
||||
FBoxClassification Result{Verdict,bNative,bUnknown};
|
||||
const bool bUniform=Verdict==EVoxelTileClass::AllAir
|
||||
||Verdict==EVoxelTileClass::AllSolid;
|
||||
FBoxClassification Result{Verdict,bNative,bUnknown,
|
||||
bLegacyAnalyticProof||(bNative&&!bUnknown&&bUniform)};
|
||||
auto& Counts=ThreadClassificationStats();
|
||||
Counts.Milliseconds+=std::chrono::duration<double,std::milli>(Clock::now()-Start).count();
|
||||
if(!Result.bNativeSurface||bUnknown)++Counts.UnknownBoxes;
|
||||
if(bUnknown)++Counts.UnknownBoxes;
|
||||
else if(Verdict==EVoxelTileClass::AllSolid)++Counts.SolidBoxes;
|
||||
else if(Verdict==EVoxelTileClass::AllAir)++Counts.AirBoxes;
|
||||
else ++Counts.MixedBoxes;
|
||||
@@ -6076,7 +6090,7 @@ public:
|
||||
static_cast<std::int64_t>(BMax[1])*Q,
|
||||
static_cast<std::int64_t>(BMax[2])*Q};
|
||||
caveCandidates=BuildCavernFeatureTileList(minimum,maximum);
|
||||
if(!caveCandidates.empty()&&!bCavesOverlap)
|
||||
if(!caveCandidates.empty()&&!bCavesOverlap&&!bL74TightNegativeControl)
|
||||
return Finish(EVoxelTileClass::Mixed,true);
|
||||
}
|
||||
if(ResolvedPassageIntersectsBox(BMin,BMax))
|
||||
@@ -6116,6 +6130,110 @@ public:
|
||||
return Finish(EVoxelTileClass::Mixed,true);
|
||||
}
|
||||
}
|
||||
// Boundary transitions can replace otherwise-air density with a solid seal and
|
||||
// can append a separate face mesh. Their authored bands were checked above; this
|
||||
// expanded window also covers the maximum XY displacement at every active seam.
|
||||
if(ResolvedLayout&&Owner&&Owner->CoreVoxelSizeMicrons>0)
|
||||
{
|
||||
const double ZMinimumMicrometres=static_cast<double>(BMin[2])
|
||||
*Owner->CoreVoxelSizeMicrons;
|
||||
const double ZMaximumMicrometres=static_cast<double>(BMax[2])
|
||||
*Owner->CoreVoxelSizeMicrons;
|
||||
for(const auto& Upper:ResolvedLayout->strates)
|
||||
{
|
||||
const auto* Lower=FindResolvedLayoutStratumByOrder(Upper.order+1);
|
||||
if(!Lower||!IsResolvedLayoutTransitionActive(Upper))continue;
|
||||
const double MaximumDisplacement=
|
||||
ResolvedTransitionMaximumFaceDisplacementMicrometres(Upper,*Lower);
|
||||
const double WindowMinimum=std::min(Upper.bedrock.bottomMicrometres,
|
||||
Lower->ceiling.bottomMicrometres)-MaximumDisplacement;
|
||||
const double WindowMaximum=std::max(Upper.bedrock.topMicrometres,
|
||||
Lower->ceiling.topMicrometres)+MaximumDisplacement;
|
||||
if(ZMaximumMicrometres>=WindowMinimum
|
||||
&&ZMinimumMicrometres<=WindowMaximum)
|
||||
return Finish(EVoxelTileClass::Mixed,true);
|
||||
}
|
||||
}
|
||||
const auto TryLegacyAnalyticProof=[&]() -> std::optional<EVoxelTileClass>
|
||||
{
|
||||
// The shared base shape may use packed occupancy, while its selected legacy
|
||||
// field remains the compiled strata distance plus bounded primitive/detail
|
||||
// terms. SurfaceWorld op stacks, native adapters, and any active composer
|
||||
// feature fail closed and keep using their existing path.
|
||||
if(!WorldAssembly||!Owner||!Owner->Manager
|
||||
||!ResolvedLayout||!Owner->Generator||bSurfaceOverlap||bCavesOverlap
|
||||
||(!bL74TightNegativeControl
|
||||
&&(!stampCandidates.empty()||!caveCandidates.empty()))
|
||||
||!EditBrushes.empty()||LayerRanges.empty()
|
||||
||ActiveSpatialBinSize<=0)return std::nullopt;
|
||||
const double LayoutWidth=GetResolvedLayoutChunkWidthVoxels();
|
||||
if(!(LayoutWidth>0.0)||!std::isfinite(LayoutWidth))return std::nullopt;
|
||||
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin[2])/LayoutWidth);
|
||||
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax[2])/LayoutWidth);
|
||||
for(int32 ChunkZ=FirstChunk;ChunkZ<=LastChunk;++ChunkZ)
|
||||
{
|
||||
const FBoundStratumField* Bound=FindBoundStratumForChunk(ChunkZ);
|
||||
if(!Bound||Bound->Kind==FBoundStratumField::EKind::SurfaceWorld)
|
||||
return std::nullopt;
|
||||
if(Bound->Kind==FBoundStratumField::EKind::FloatingIslands)
|
||||
{
|
||||
// A missing FI plan is the owner's compiled-analytic fallback. Any
|
||||
// installed plan selects the native FI field and is outside this proof.
|
||||
if(Bound->IslandPlan)return std::nullopt;
|
||||
}
|
||||
if(Bound->Kind==FBoundStratumField::EKind::Caves)return std::nullopt;
|
||||
}
|
||||
for(int32 BZ=FloorDiv(BMin[2],ActiveSpatialBinSize);
|
||||
BZ<=FloorDiv(BMax[2],ActiveSpatialBinSize);++BZ)
|
||||
for(int32 BY=FloorDiv(BMin[1],ActiveSpatialBinSize);
|
||||
BY<=FloorDiv(BMax[1],ActiveSpatialBinSize);++BY)
|
||||
for(int32 BX=FloorDiv(BMin[0],ActiveSpatialBinSize);
|
||||
BX<=FloorDiv(BMax[0],ActiveSpatialBinSize);++BX)
|
||||
{
|
||||
const auto It=SpatialBins.find({BX,BY,BZ});
|
||||
if(It==SpatialBins.end())continue;
|
||||
for(const uint32 Id:It->second)
|
||||
{
|
||||
const FPrimitive& Primitive=Primitives[Id];
|
||||
bool Overlap=true;
|
||||
for(int32 Axis=0;Axis<3;++Axis)
|
||||
Overlap=Overlap
|
||||
&&Primitive.Max[Axis]+DetailBand>=BMin[Axis]
|
||||
&&Primitive.Min[Axis]-DetailBand<=BMax[Axis];
|
||||
if(Overlap&&!bL74TightNegativeControl)return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
const FInterval& Layer=LayerRanges.front();
|
||||
if(Layer.Hi<=Layer.Lo)return std::nullopt;
|
||||
const auto BaseAt=[&](double Z)
|
||||
{return std::min(Z-static_cast<double>(Layer.Lo),
|
||||
static_cast<double>(Layer.Hi)-Z);};
|
||||
const double Z0=static_cast<double>(BMin[2]);
|
||||
const double Z1=static_cast<double>(BMax[2]);
|
||||
const double Base0=BaseAt(Z0),Base1=BaseAt(Z1);
|
||||
const double BaseMinimum=std::min(Base0,Base1);
|
||||
double BaseMaximum=std::max(Base0,Base1);
|
||||
const double Peak=(static_cast<double>(Layer.Lo)+Layer.Hi)*0.5;
|
||||
if(Peak>=Z0&&Peak<=Z1)BaseMaximum=std::max(BaseMaximum,BaseAt(Peak));
|
||||
// With no active primitive, smooth-min sees only its +100000 sentinel;
|
||||
// its largest possible downward correction is 12/6 = 2 voxels. Keep
|
||||
// the entire resulting field outside the measured detail support (4.125).
|
||||
constexpr double DetailSupport=DetailSupremum+0.125;
|
||||
constexpr double SmoothMinSlack=StrataCaveBlendRadius/6.0;
|
||||
constexpr double SafetyEpsilon=1.0e-3;
|
||||
const double SolidMargin=bL74TightNegativeControl
|
||||
?SafetyEpsilon:DetailSupport+SmoothMinSlack+SafetyEpsilon;
|
||||
const double AirMargin=bL74TightNegativeControl
|
||||
?SafetyEpsilon:DetailSupport+SafetyEpsilon;
|
||||
if(BaseMinimum>SolidMargin)
|
||||
return EVoxelTileClass::AllSolid;
|
||||
if(BaseMaximum<-AirMargin)
|
||||
return EVoxelTileClass::AllAir;
|
||||
return std::nullopt;
|
||||
};
|
||||
if(const auto LegacyVerdict=TryLegacyAnalyticProof())
|
||||
return Finish(*LegacyVerdict,false,true);
|
||||
// A box crossing recipes cannot use one field interval; unknown composition stays Mixed.
|
||||
// Une boite qui traverse plusieurs recettes reste Mixed, car l'intervalle d'un seul champ ne suffit pas.
|
||||
if(bBadPlan||(bSurfaceOverlap&&(bIslandOverlap||bOtherStrateOverlap))
|
||||
|
||||
@@ -2356,6 +2356,7 @@ namespace VoxelForgeCore
|
||||
static_cast<std::int64_t>(origin.Y+extent)*SubvoxelsPerVoxel);
|
||||
}
|
||||
bool bNativeSurfaceTile=false;
|
||||
bool bTileProofSkipSafe=false;
|
||||
bool bNativeSurfaceBandSafe=false;
|
||||
double classificationColumnComputeMilliseconds=0.0;
|
||||
if(bHasWorldAssembly)
|
||||
@@ -2367,6 +2368,7 @@ namespace VoxelForgeCore
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds
|
||||
-columnMillisecondsBeforeClassification);
|
||||
bNativeSurfaceTile=Classification.bNativeSurface;
|
||||
bTileProofSkipSafe=Classification.bSkipSafe;
|
||||
if(options.profileMesherStages)
|
||||
out.diagnostics.nativeSurfaceTile=bNativeSurfaceTile;
|
||||
// NativeSurfaceDensityAt consumes only the bounded manifest heightfield and its
|
||||
@@ -2374,7 +2376,7 @@ namespace VoxelForgeCore
|
||||
// unknown; edits and cross-strate tiles are still excluded below.
|
||||
// NativeSurfaceDensityAt ne consomme que le champ et les volumes bornes du manifeste.
|
||||
bNativeSurfaceBandSafe=bNativeSurfaceTile;
|
||||
if(bNativeSurfaceTile)
|
||||
if(bNativeSurfaceTile||bTileProofSkipSafe)
|
||||
{
|
||||
out.diagnostics.tileWasClassified=true;
|
||||
switch(Classification.bUnknown?EVoxelTileClass::Mixed:Classification.Verdict)
|
||||
@@ -2383,12 +2385,30 @@ namespace VoxelForgeCore
|
||||
case EVoxelTileClass::AllAir:out.diagnostics.tileClass=TileClass::allAir;break;
|
||||
case EVoxelTileClass::Mixed:out.diagnostics.tileClass=TileClass::mixed;break;
|
||||
}
|
||||
if((out.diagnostics.tileClass==TileClass::allSolid
|
||||
if(bTileProofSkipSafe&&(out.diagnostics.tileClass==TileClass::allSolid
|
||||
||out.diagnostics.tileClass==TileClass::allAir)
|
||||
&&!options.hashDensity&&!options.captureDensitySamples&&!bNeedsFarShell
|
||||
&&(!options.hashDensity||options.allowUniformSkipsWhenHashing)
|
||||
&&!options.captureDensitySamples&&!bNeedsFarShell
|
||||
&&!options.auditUniformBlockProofs)
|
||||
{
|
||||
out.diagnostics.meshingSkipped=true;
|
||||
if(options.hashDensity)
|
||||
{
|
||||
const FVoxelMeshData EmptyMesh;
|
||||
out.densityHash=14695981039346656037ull;
|
||||
out.meshHash=HashViewerMesh(EmptyMesh);
|
||||
out.payloadHash=HashPayload(EmptyMesh);
|
||||
out.topologyHash=HashTopology(EmptyMesh);
|
||||
out.materialHash=HashMaterialSamples(out.vertices);
|
||||
if(key.lod>=4)
|
||||
{
|
||||
out.meshHash=SaltFarHorizonHash(out.meshHash,options.farHorizonVersion);
|
||||
out.payloadHash=SaltFarHorizonHash(out.payloadHash,options.farHorizonVersion);
|
||||
out.topologyHash=SaltFarHorizonHash(out.topologyHash,options.farHorizonVersion);
|
||||
}
|
||||
out.hasDensityHash=out.hasMeshHash=out.hasPayloadHash=out.hasTopologyHash=true;
|
||||
out.hasMaterialHash=true;
|
||||
}
|
||||
const auto Counts=FStandaloneShapeWorld::GetThreadClassificationStats();
|
||||
const auto& Columns=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
|
||||
out.diagnostics.solidBoxes=Counts.SolidBoxes;
|
||||
@@ -2422,10 +2442,13 @@ namespace VoxelForgeCore
|
||||
out.densitySampleCount=0;
|
||||
return out;
|
||||
}
|
||||
// Native SurfaceWorld classifies each 8-cell block against its exact sample
|
||||
// lattice. This lets the mesher avoid filling density grids for proven air/rock.
|
||||
mesher.bUseBlockEarlyOut=true;
|
||||
}
|
||||
// Keep sub-tile early-outs on native fields whose block interval is complete.
|
||||
// The legacy compiled field has sparse tunnel/composer terms that can change a
|
||||
// small block without making the current whole-tile bound fail; leave those blocks
|
||||
// to the exact grid and use only the separately falsified whole-tile proof above.
|
||||
if(bHasWorldAssembly&&step<=2&&bNativeSurfaceTile)
|
||||
mesher.bUseBlockEarlyOut=true;
|
||||
}
|
||||
const auto MeshingStart=std::chrono::steady_clock::now();
|
||||
int32 MeshBandMinZVoxel=options.bandMinZVoxel;
|
||||
@@ -2995,6 +3018,72 @@ namespace VoxelForgeCore
|
||||
const auto& values=options.captureDensitySamples?out.debugDensitySamples:densityValues;
|
||||
for(const float value:values)
|
||||
{ densityHash^=std::bit_cast<std::uint32_t>(value);densityHash*=1099511628211ull; }
|
||||
|
||||
// A stream-hash run is the full-grid reference path. Record the exact sign
|
||||
// uniformity ceiling from the same captured lattice that feeds Marching Cubes.
|
||||
// Blocks are disjoint sample cubes; partial cubes at tile edges are included.
|
||||
const std::size_t gridWidth=static_cast<std::size_t>(
|
||||
std::clamp(MeshCells,2,CHUNK_SIZE)+3);
|
||||
const std::size_t xySamples=gridWidth*gridWidth;
|
||||
const std::size_t zSamples=xySamples>0&&values.size()%xySamples==0
|
||||
?values.size()/xySamples:0;
|
||||
bool tileHasSign=false,tileUniform=true;
|
||||
bool tileAir=false;
|
||||
for(const float value:values)
|
||||
{
|
||||
if(!std::isfinite(value)||value==0.0f)
|
||||
{tileUniform=false;continue;}
|
||||
const bool air=value>0.0f;
|
||||
if(!tileHasSign){tileHasSign=true;tileAir=air;}
|
||||
else if(tileAir!=air)tileUniform=false;
|
||||
}
|
||||
const auto CountUniformBlocks=[&](std::size_t blockWidth)
|
||||
{
|
||||
std::uint64_t uniformBlocks=0,uniformSamples=0;
|
||||
if(zSamples==0)return std::pair{uniformBlocks,uniformSamples};
|
||||
for(std::size_t z0=0;z0<zSamples;z0+=blockWidth)
|
||||
for(std::size_t y0=0;y0<gridWidth;y0+=blockWidth)
|
||||
for(std::size_t x0=0;x0<gridWidth;x0+=blockWidth)
|
||||
{
|
||||
const std::size_t z1=std::min(zSamples,z0+blockWidth);
|
||||
const std::size_t y1=std::min(gridWidth,y0+blockWidth);
|
||||
const std::size_t x1=std::min(gridWidth,x0+blockWidth);
|
||||
bool hasSign=false,uniform=true,air=false;
|
||||
for(std::size_t z=z0;z<z1&&uniform;++z)
|
||||
for(std::size_t y=y0;y<y1&&uniform;++y)
|
||||
for(std::size_t x=x0;x<x1;++x)
|
||||
{
|
||||
const float value=values[(z*gridWidth+y)*gridWidth+x];
|
||||
if(!std::isfinite(value)||value==0.0f)
|
||||
{uniform=false;break;}
|
||||
const bool sampleAir=value>0.0f;
|
||||
if(!hasSign){hasSign=true;air=sampleAir;}
|
||||
else if(air!=sampleAir){uniform=false;break;}
|
||||
}
|
||||
if(uniform&&hasSign)
|
||||
{
|
||||
++uniformBlocks;
|
||||
uniformSamples+=static_cast<std::uint64_t>(z1-z0)
|
||||
*static_cast<std::uint64_t>(y1-y0)
|
||||
*static_cast<std::uint64_t>(x1-x0);
|
||||
}
|
||||
}
|
||||
return std::pair{uniformBlocks,uniformSamples};
|
||||
};
|
||||
const auto [uniformBlocks8,uniformSamples8]=CountUniformBlocks(8);
|
||||
const auto [uniformBlocks4,uniformSamples4]=CountUniformBlocks(4);
|
||||
const double centerZ=(static_cast<double>(origin.Z)
|
||||
+static_cast<double>(extent)*0.5)*static_cast<double>(world.CoreVoxelSizeMicrons)
|
||||
/10000.0;
|
||||
const int32 stratum=world.Manager
|
||||
?world.Manager->GetStrateIndex(static_cast<float>(centerZ)):-1;
|
||||
std::printf("[UniformGridCeiling] strate=%d lod=%d key=(%d,%d,%d) samples=%zu uniform=%d air=%d blocks8=%llu samples8=%llu blocks4=%llu samples4=%llu\n",
|
||||
stratum,key.lod,key.coordinate.x,key.coordinate.y,key.coordinate.z,values.size(),
|
||||
tileUniform&&tileHasSign?1:0,tileUniform&&tileHasSign&&tileAir?1:0,
|
||||
static_cast<unsigned long long>(uniformBlocks8),
|
||||
static_cast<unsigned long long>(uniformSamples8),
|
||||
static_cast<unsigned long long>(uniformBlocks4),
|
||||
static_cast<unsigned long long>(uniformSamples4));
|
||||
out.densityHash=densityHash;out.meshHash=HashViewerMesh(mesh);
|
||||
out.payloadHash=HashPayload(mesh);out.topologyHash=HashTopology(mesh);
|
||||
out.materialHash=HashMaterialSamples(out.vertices);
|
||||
|
||||
Reference in new issue
Block a user