wip(assembler): round L9 -- LOD1 skips proven-uniform blocks: 95% fewer samples, view ~2% faster
Step-2 (LOD1) tiles of native SurfaceWorld now prove 8-cell blocks uniform from exact heightfield column extrema over the padded block (cells + normal halo) and skip their density fill; features, edits, mixed strata and Unknown verdicts still fill. LOD1 samples 42,875 -> 2,063 per tile. Audit: 80,502 skipped blocks (3 seeds) + 9,216 island blocks force-filled, 0 violations. All SurfaceWorld and FloatingIslands hashes unchanged at W1/W8/W12, Clang off and GCC default. Reviewer interleaved W12, no hashing: whole view 1.439 -> 1.409 s. L8 phase probes removed; --profile-lod-work and --audit-uniform-block-proofs are opt-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
1 parent
82813fb13b
commit
7650abb755
10 files changed
+439
-48
No files matched your search
@@ -133,6 +133,11 @@ namespace VoxelForgeCore
|
||||
std::uint64_t columnCacheConflictMisses = 0;
|
||||
std::uint64_t sharedColumnCacheHits = 0;
|
||||
std::uint64_t columnEvaluations = 0;
|
||||
std::uint64_t uniformBlockProofsChecked = 0;
|
||||
std::uint64_t uniformBlockProofSamplesChecked = 0;
|
||||
std::uint64_t uniformBlockProofFallbackBlocks = 0;
|
||||
std::uint64_t uniformBlockProofUnknownBlocks = 0;
|
||||
std::uint64_t uniformBlockProofViolations = 0;
|
||||
double classificationMilliseconds = 0.0;
|
||||
double columnComputeMilliseconds = 0.0;
|
||||
double columnEvaluationExclusiveMilliseconds = 0.0;
|
||||
@@ -1056,6 +1061,9 @@ namespace VoxelForgeCore
|
||||
// Separate cold pass only; samples the MC inner stages without changing output.
|
||||
// Passe froide separee seulement; mesure les etapes internes MC sans changer la sortie.
|
||||
bool profileMesherStages = false;
|
||||
// Diagnostic only; fills and checks every lattice sample in blocks certified uniform.
|
||||
// Diagnostic seulement; evalue et verifie tous les echantillons des blocs uniformes.
|
||||
bool auditUniformBlockProofs = false;
|
||||
// Negative control for the shared XY-column cache; 1 restores the old direct-map indexing.
|
||||
// Controle negatif du cache XY partage; 1 restaure l'ancien indexage direct.
|
||||
bool legacyDirectMappedColumnCache = false;
|
||||
|
||||
@@ -1227,7 +1227,34 @@ public:
|
||||
EVoxelTileClass ClassifyBox(const FIntVector& Origin,int32 Step,int32 Cells) const
|
||||
{ return ClassifyBoxDetailed(Origin,Step,Cells).Verdict; }
|
||||
|
||||
FBoxClassification ClassifyBoxDetailed(const FIntVector& Origin,int32 Step,int32 Cells) const
|
||||
bool IsNativeSurfaceBox(const FIntVector& Origin,int32 Step,int32 Cells) const
|
||||
{
|
||||
if(!WorldAssembly||!Owner||!Owner->Manager||Step<=0||Cells<=0)return false;
|
||||
const int32 Pad=std::max(Step,1);
|
||||
const int32 BMin=Origin.Z-Pad;
|
||||
const int32 BMax=Origin.Z+(Cells+1)*Step;
|
||||
const int32 FirstChunk=FMath::FloorToInt(static_cast<float>(BMin)
|
||||
/static_cast<float>(CHUNK_SIZE));
|
||||
const int32 LastChunk=FMath::FloorToInt(static_cast<float>(BMax)
|
||||
/static_cast<float>(CHUNK_SIZE));
|
||||
const VoxelForgeCore::WorldAssemblyStratumPlanV1* Plan=nullptr;
|
||||
for(int32 ChunkZ=FirstChunk;ChunkZ<=LastChunk;++ChunkZ)
|
||||
{
|
||||
const auto It=std::find_if(WorldAssembly->plan.strates.begin(),
|
||||
WorldAssembly->plan.strates.end(),[ChunkZ](const auto& Entry)
|
||||
{return ChunkZ<=Entry.topChunkZ&&ChunkZ>=Entry.bottomChunkZ;});
|
||||
if(It==WorldAssembly->plan.strates.end())return false;
|
||||
const auto* Candidate=&*It;
|
||||
if(Plan&&Plan!=Candidate)return false;
|
||||
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
|
||||
!=ECaveGeneratorType::SurfaceWorld)return false;
|
||||
Plan=Candidate;
|
||||
}
|
||||
return Plan!=nullptr;
|
||||
}
|
||||
|
||||
FBoxClassification ClassifyBoxDetailed(const FIntVector& Origin,int32 Step,int32 Cells,
|
||||
bool bUseNativeLatticeProof=false) const
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
const auto Start=Clock::now();
|
||||
@@ -1310,27 +1337,37 @@ public:
|
||||
&&EditBoundsMin.Y<=BMax[1]&&EditBoundsMax.Y>=BMin[1]
|
||||
&&EditBoundsMin.Z<=BMax[2]&&EditBoundsMax.Z>=BMin[2])
|
||||
return Finish(EVoxelTileClass::Mixed,true);
|
||||
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)
|
||||
// The native interval path below is the exact field selected first by DensityAtLattice
|
||||
// for SurfaceWorld and FloatingIslands. Legacy analytic primitives/layer bounds cannot
|
||||
// modify that field; only the explicit edit transaction above can. Keep their guards on
|
||||
// the legacy classifier path, where those overlays do participate in density.
|
||||
if(!bUseNativeLatticeProof||(!bSurfaceOverlap&&!bIslandOverlap))
|
||||
{
|
||||
const auto It=SpatialBins.find({BX,BY,BZ});if(It==SpatialBins.end())continue;
|
||||
for(uint32 Id:It->second)
|
||||
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 FPrimitive& P=Primitives[Id];bool Overlap=true;
|
||||
for(int32 A=0;A<3;++A)
|
||||
Overlap &= P.Max[A]+DetailBand>=BMin[A]&&P.Min[A]-DetailBand<=BMax[A];
|
||||
if(Overlap)return Finish(EVoxelTileClass::Mixed);
|
||||
const auto It=SpatialBins.find({BX,BY,BZ});if(It==SpatialBins.end())continue;
|
||||
for(uint32 Id:It->second)
|
||||
{
|
||||
const FPrimitive& P=Primitives[Id];bool Overlap=true;
|
||||
for(int32 A=0;A<3;++A)
|
||||
Overlap &= P.Max[A]+DetailBand>=BMin[A]&&P.Min[A]-DetailBand<=BMax[A];
|
||||
if(Overlap)return Finish(EVoxelTileClass::Mixed);
|
||||
}
|
||||
}
|
||||
for(const FInterval& R:LayerRanges)
|
||||
if(R.Lo<=BMax[2]&&R.Hi>=BMin[2]
|
||||
&&(R.Lo>=BMin[2]||R.Hi<=BMax[2]))
|
||||
return Finish(EVoxelTileClass::Mixed);
|
||||
}
|
||||
for(const FInterval& R:LayerRanges)
|
||||
if(R.Lo<=BMax[2]&&R.Hi>=BMin[2]
|
||||
&&(R.Lo>=BMin[2]||R.Hi<=BMax[2]))
|
||||
return Finish(EVoxelTileClass::Mixed);
|
||||
if(bSurfaceOverlap)
|
||||
{
|
||||
if(!WorldAssembly||!SurfacePlan)return Finish(EVoxelTileClass::Mixed,true);
|
||||
const VoxelForgeCore::Heightfield2D Block(WorldAssembly->recipe,*SurfacePlan);
|
||||
// classifyLatticeBox takes the exact extrema of ground/cap columns at every
|
||||
// MC lattice XY in this box. Since density is min(z-ground, cap-z), those extrema
|
||||
// bound every lattice sample in Z without a noise amplitude guess.
|
||||
const auto Verdict=Block.classifyLatticeBox(
|
||||
static_cast<int64>(BMin[0])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
static_cast<int64>(BMin[1])*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
|
||||
@@ -1570,7 +1570,8 @@ namespace VoxelForgeCore
|
||||
}
|
||||
if((out.diagnostics.tileClass==TileClass::allSolid
|
||||
||out.diagnostics.tileClass==TileClass::allAir)
|
||||
&&!options.hashDensity&&!options.captureDensitySamples&&!bNeedsFarShell)
|
||||
&&!options.hashDensity&&!options.captureDensitySamples&&!bNeedsFarShell
|
||||
&&!options.auditUniformBlockProofs)
|
||||
{
|
||||
out.diagnostics.meshingSkipped=true;
|
||||
const auto Counts=FStandaloneShapeWorld::GetThreadClassificationStats();
|
||||
@@ -1747,10 +1748,20 @@ namespace VoxelForgeCore
|
||||
const double ColumnMillisecondsBeforeMeshing=
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
const auto MarchingCubesStart=std::chrono::steady_clock::now();
|
||||
FVoxelUniformBlockProofAuditStats BlockProofAudit;
|
||||
FVoxelMeshData mesh=mesher.GenerateMesh(origin,MeshStep,MeshCells,nullptr,
|
||||
MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr,
|
||||
(options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr,
|
||||
options.profileMesherStages?&McStageTimings:nullptr);
|
||||
options.profileMesherStages?&McStageTimings:nullptr,
|
||||
options.auditUniformBlockProofs?&BlockProofAudit:nullptr);
|
||||
if(options.auditUniformBlockProofs)
|
||||
{
|
||||
out.diagnostics.uniformBlockProofsChecked=BlockProofAudit.CheckedBlocks;
|
||||
out.diagnostics.uniformBlockProofSamplesChecked=BlockProofAudit.CheckedSamples;
|
||||
out.diagnostics.uniformBlockProofFallbackBlocks=BlockProofAudit.FallbackBlocks;
|
||||
out.diagnostics.uniformBlockProofUnknownBlocks=BlockProofAudit.UnknownBlocks;
|
||||
out.diagnostics.uniformBlockProofViolations=BlockProofAudit.Violations;
|
||||
}
|
||||
if(bDiagnosticColumnSampling)
|
||||
{
|
||||
Heightfield2D::setThreadColumnOctaveDrop(PreviousOctaveDrop);
|
||||
|
||||
Reference in new issue
Block a user