wip(assembler): round S13 -- tile cost counted per LOD (diagnostics only, no output change)

Exclusive per-stage tile timers and probes. Findings: LOD0 is MC-exclusive
(10.4 of 14 ms p95, 243 ns/sample); LOD4's 3x cost is column-cache misses
(8,224 vs ~1,600-1,900 at LOD3/5), cap refine secondary; per-tile feature lists
cost ~0.001 ms; cross-worker column repeats common (277k at LOD0). Global octave
drop and stride-2 interpolation probes fail the 2 cm gate. Worker cap 1 kept
(cap 0 worse 40 m/s tick tail). Report: Standalone/Research/WorldAssemblerM3/Round17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-19 15:40:25 +02:00
1 parent 212814a75c
commit 87b0e5d8cd
9 files changed
+763 -76

No files matched your search

+57 -32
View File
@@ -6976,7 +6976,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
const FVector& CameraPositionVoxels,int32 WorkerCount,int32 MaxLevel,
VoxelForgeCore::DetailVariant DetailVariant,const VoxelForgeCore::JobScheduler& Scheduler,
bool bHashDensity,bool bSkipUvAndColors,bool bSkipMaterialGroups,bool bSkipUV,bool bSkipColors,
std::chrono::steady_clock::time_point ColdStart,bool bProfileFarColumnOverlap,
std::chrono::steady_clock::time_point ColdStart,bool bProfileColumnOverlap,
int32 FarHorizonLOD8InteriorStepVoxels)
{
using Clock=std::chrono::steady_clock;
@@ -7045,7 +7045,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
const auto ShapeReady=Clock::now();
const double ShapeSeconds=std::chrono::duration<double>(ShapeReady-ShapeBuildStart).count();
FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel,
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileFarColumnOverlap,
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileColumnOverlap,
FarHorizonLOD8InteriorStepVoxels);
const auto StreamStart=Clock::now();
const FStandaloneStreamingUpdate Update=Streamer.StartWithDesired(Desired);
@@ -7078,19 +7078,23 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
FVoxelTileKey Tile;
std::uint64_t WorkerHash=0;
};
std::unordered_map<FColumnXY,FFirstColumnUse,FColumnXYHash> LOD8ColumnFirstUse;
std::uint64_t LOD8ColumnDuplicateEvents=0,LOD8NeighbourDuplicateEvents=0;
std::uint64_t LOD8CrossWorkerNeighbourDuplicateEvents=0;
std::size_t LOD8ColumnCaptureTiles=0;
bool bLOD8ColumnCaptureComplete=true;
std::array<std::unordered_map<FColumnXY,std::vector<FFirstColumnUse>,FColumnXYHash>,9> ColumnUsesByLOD;
std::array<std::uint64_t,9> ColumnDuplicateEventsByLOD{},NeighbourDuplicateEventsByLOD{};
std::array<std::uint64_t,9> CrossWorkerNeighbourDuplicateEventsByLOD{};
std::array<std::uint64_t,9> ColumnCaptureTilesByLOD{},ExpectedTilesByLOD{};
std::array<bool,9> ColumnCaptureCompleteByLOD{};
ColumnCaptureCompleteByLOD.fill(true);
std::array<double,9> WorkerSecondsByLOD{};
std::array<double,9> ColumnEvalMsByLOD{},MarchingExclusiveMsByLOD{};
std::array<double,9> GroundRefineExclusiveMsByLOD{},CapRefineExclusiveMsByLOD{};
std::array<double,9> MeshAssemblyMsByLOD{};
std::array<std::uint64_t,9> TileCountByLOD{};
const auto AreLOD8Neighbours=[](const FVoxelTileKey& A,const FVoxelTileKey& B)
for(const FVoxelTileKey& Tile:Desired.Tiles)
if(Tile.Level>=0&&Tile.Level<static_cast<int32>(ExpectedTilesByLOD.size()))
++ExpectedTilesByLOD[static_cast<std::size_t>(Tile.Level)];
const auto AreSameLODNeighbours=[](const FVoxelTileKey& A,const FVoxelTileKey& B)
{
if(A.Level!=8||B.Level!=8)return false;
if(A.Level!=B.Level)return false;
const std::int64_t DX=std::abs(static_cast<std::int64_t>(A.Coord.X)-B.Coord.X);
const std::int64_t DY=std::abs(static_cast<std::int64_t>(A.Coord.Y)-B.Coord.Y);
const std::int64_t DZ=std::abs(static_cast<std::int64_t>(A.Coord.Z)-B.Coord.Z);
@@ -7122,10 +7126,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
CapRefineExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.capRefinementExclusiveMilliseconds;
MeshAssemblyMsByLOD[LOD]+=Tile.CoreDiagnostics.meshAssemblyMilliseconds;
}
if(bProfileFarColumnOverlap&&Tile.Key.Level==8)
if(bProfileColumnOverlap&&Tile.Key.Level>=0
&&Tile.Key.Level<static_cast<int32>(ColumnCaptureTilesByLOD.size()))
{
++LOD8ColumnCaptureTiles;
bLOD8ColumnCaptureComplete=bLOD8ColumnCaptureComplete
const std::size_t LOD=static_cast<std::size_t>(Tile.Key.Level);
++ColumnCaptureTilesByLOD[LOD];
ColumnCaptureCompleteByLOD[LOD]=ColumnCaptureCompleteByLOD[LOD]
&&Tile.CoreDiagnostics.columnCoordinateCaptureComplete;
std::vector<std::array<std::int64_t,2>> Coordinates=
Tile.CoreDiagnostics.evaluatedColumnCoordinates;
@@ -7134,16 +7140,26 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
for(const auto& Coordinate:Coordinates)
{
const FColumnXY XY{Coordinate[0],Coordinate[1]};
const auto [First,Inserted]=LOD8ColumnFirstUse.try_emplace(XY,
FFirstColumnUse{Tile.Key,Tile.CoreDiagnostics.columnEvaluationWorkerHash});
if(Inserted)continue;
++LOD8ColumnDuplicateEvents;
if(AreLOD8Neighbours(First->second.Tile,Tile.Key))
auto [Uses,Inserted]=ColumnUsesByLOD[LOD].try_emplace(XY);
std::vector<FFirstColumnUse>& PriorUses=Uses->second;
if(Inserted)
{
++LOD8NeighbourDuplicateEvents;
if(First->second.WorkerHash!=Tile.CoreDiagnostics.columnEvaluationWorkerHash)
++LOD8CrossWorkerNeighbourDuplicateEvents;
PriorUses.push_back({Tile.Key,Tile.CoreDiagnostics.columnEvaluationWorkerHash});
continue;
}
++ColumnDuplicateEventsByLOD[LOD];
bool bHasNeighbourDuplicate=false,bHasCrossWorkerNeighbourDuplicate=false;
for(const FFirstColumnUse& Prior:PriorUses)
{
if(!AreSameLODNeighbours(Prior.Tile,Tile.Key))continue;
bHasNeighbourDuplicate=true;
bHasCrossWorkerNeighbourDuplicate=bHasCrossWorkerNeighbourDuplicate
||Prior.WorkerHash!=Tile.CoreDiagnostics.columnEvaluationWorkerHash;
}
if(bHasNeighbourDuplicate)++NeighbourDuplicateEventsByLOD[LOD];
if(bHasCrossWorkerNeighbourDuplicate)
++CrossWorkerNeighbourDuplicateEventsByLOD[LOD];
PriorUses.push_back({Tile.Key,Tile.CoreDiagnostics.columnEvaluationWorkerHash});
}
}
WholeViewReady=std::max(WholeViewReady,Tile.CompletedAt);
@@ -7191,14 +7207,23 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
}
Streamer.Stop();
if(bProfileFarColumnOverlap)
bool bColumnOverlapCapturePassing=true;
if(bProfileColumnOverlap)
{
std::printf("[WorldAssemblerLOD8ColumnOverlap] tiles=%zu unique_xy_columns=%zu repeated_tile_evaluations=%llu neighbouring_duplicate_evaluations=%llu cross_worker_neighbouring_duplicates=%llu capture_complete=%d\n",
LOD8ColumnCaptureTiles,LOD8ColumnFirstUse.size(),
static_cast<unsigned long long>(LOD8ColumnDuplicateEvents),
static_cast<unsigned long long>(LOD8NeighbourDuplicateEvents),
static_cast<unsigned long long>(LOD8CrossWorkerNeighbourDuplicateEvents),
bLOD8ColumnCaptureComplete?1:0);
for(std::size_t LOD=0;LOD<ColumnCaptureTilesByLOD.size();++LOD)
{
const bool bLODComplete=ColumnCaptureCompleteByLOD[LOD]
&&ColumnCaptureTilesByLOD[LOD]==ExpectedTilesByLOD[LOD];
bColumnOverlapCapturePassing=bColumnOverlapCapturePassing&&bLODComplete;
std::printf("[WorldAssemblerLODColumnOverlap] lod=%zu tiles=%llu expected_tiles=%llu unique_xy_columns=%zu repeated_tile_evaluations=%llu neighbouring_duplicate_evaluations=%llu cross_worker_neighbouring_duplicates=%llu capture_complete=%d\n",
LOD,static_cast<unsigned long long>(ColumnCaptureTilesByLOD[LOD]),
static_cast<unsigned long long>(ExpectedTilesByLOD[LOD]),
ColumnUsesByLOD[LOD].size(),
static_cast<unsigned long long>(ColumnDuplicateEventsByLOD[LOD]),
static_cast<unsigned long long>(NeighbourDuplicateEventsByLOD[LOD]),
static_cast<unsigned long long>(CrossWorkerNeighbourDuplicateEventsByLOD[LOD]),
bLODComplete?1:0);
}
}
for(std::size_t LOD=0;LOD<TileCountByLOD.size();++LOD)
{
@@ -7253,7 +7278,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
return NeighborhoodFinished==static_cast<int32>(Neighborhood.size())
&&FullDetailFinished==FullDetailExpected&&Finished==Update.DesiredTiles&&bHashesPassing
&&(!bProfileFarColumnOverlap||(bLOD8ColumnCaptureComplete&&LOD8ColumnCaptureTiles>0))?0:6;
&&(!bProfileColumnOverlap||bColumnOverlapCapturePassing)?0:6;
}
int RunStreamingBenchmark(FStandaloneOwnerWorld& World,VoxelForgeCore::World* CoreWorld,
const FVector& CameraPositionVoxels,
@@ -9601,7 +9626,7 @@ int main(int Argc,char** Argv)
std::string WorldAssemblyManifestOutputPath;
bool bStreamBenchmark=false;
bool bStreamPipeline=false;
bool bProfileFarColumnOverlap=false;
bool bProfileColumnOverlap=false;
bool bCoarseFirstStreamOrder=false;
bool bProfileDetailBand=false;
bool bPassageDetailAuditOnly=false;
@@ -9739,8 +9764,8 @@ int main(int Argc,char** Argv)
else if(Arg=="--coarse-first-stream-order")bCoarseFirstStreamOrder=true;
else if(Arg=="--stream-pipeline")
{bStreamPipeline=true;bStreamBenchmark=true;App.bShapeWorld=true;App.bForceGenerate=true;}
else if(Arg=="--profile-far-column-overlap")
{bProfileFarColumnOverlap=true;bStreamPipeline=true;bStreamBenchmark=true;App.bShapeWorld=true;App.bForceGenerate=true;}
else if(Arg=="--profile-column-overlap"||Arg=="--profile-far-column-overlap")
{bProfileColumnOverlap=true;bStreamPipeline=true;bStreamBenchmark=true;App.bShapeWorld=true;App.bForceGenerate=true;}
else if(Arg=="--edit-benchmark")
{bEditBenchmark=true;bStreamBenchmark=true;App.bShapeWorld=true;}
else if(Arg=="--stream-hash")bStreamHash=true;
@@ -9957,7 +9982,7 @@ int main(int Argc,char** Argv)
FVector(App.Position.X*4.0,App.Position.Y*4.0,App.Position.Z*4.0),
App.WorkerCount,StreamBenchmarkMaxLevel,App.ShapeDetailVariant,App.CoreScheduler,
bStreamHash||!BakeMeshCachePath.empty(),bSkipUvAndColors,bSkipMaterialGroups,
bSkipUV,bSkipColors,ColdProcessStart,bProfileFarColumnOverlap,
bSkipUV,bSkipColors,ColdProcessStart,bProfileColumnOverlap,
App.FarHorizonLOD8InteriorStepVoxels);
if(bEditBenchmark)
return RunEditTransactionBenchmark(*App.World,*App.ShapeWorld,
+4 -4
View File
@@ -424,13 +424,13 @@ public:
bool bInSkipUV=false,
bool bInSkipColors=false,
VoxelForgeCore::World* InCoreWorld=nullptr,
bool bInCaptureFarColumnCoordinates=false,
bool bInCaptureColumnCoordinates=false,
int32 InLod8HorizonInteriorStepVoxels=0)
:World(&InWorld),WorkerCount(FMath::Clamp(InWorkerCount,1,16)),bHashDensity(bInHashDensity),
MeshCache(InMeshCache),MaxLevelFilter(InMaxLevelFilter),
CoreWorld(InCoreWorld),bSkipUvAndColors(bInSkipUvAndColors),bSkipMaterialGroups(bInSkipMaterialGroups),
bSkipUV(bInSkipUV),bSkipColors(bInSkipColors),
bCaptureFarColumnCoordinates(bInCaptureFarColumnCoordinates),
bCaptureColumnCoordinates(bInCaptureColumnCoordinates),
Lod8HorizonInteriorStepVoxels(std::max(0,InLod8HorizonInteriorStepVoxels))
{
VoxelGenLOD::InitializeConsoleSwitches();
@@ -799,7 +799,7 @@ private:
Options.bandMinZVoxel=Job.BandZMin;Options.bandMaxZVoxel=Job.BandZMax;
Options.hashDensity=bHashDensity;Options.skipUvAndColors=bSkipUvAndColors;
Options.skipMaterialGroups=bSkipMaterialGroups;Options.skipUv=bSkipUV;Options.skipColors=bSkipColors;
Options.captureColumnCoordinates=bCaptureFarColumnCoordinates&&Level==8;
Options.captureColumnCoordinates=bCaptureColumnCoordinates;
Options.lod8HorizonInteriorStepVoxels=Lod8HorizonInteriorStepVoxels;
std::string Error;
auto Result=CoreWorld->generateTile(
@@ -971,7 +971,7 @@ private:
bool bSkipMaterialGroups=false;
bool bSkipUV=false;
bool bSkipColors=false;
bool bCaptureFarColumnCoordinates=false;
bool bCaptureColumnCoordinates=false;
int32 Lod8HorizonInteriorStepVoxels=0;
std::atomic<uint64> CacheHitCount{0};
mutable std::mutex Mutex;