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

@@ -126,6 +126,10 @@ namespace VoxelForgeCore
double classificationMilliseconds = 0.0;
double columnComputeMilliseconds = 0.0;
double columnEvaluationExclusiveMilliseconds = 0.0;
double marchingCubesColumnComputeMilliseconds = 0.0;
double groundRefinementColumnComputeMilliseconds = 0.0;
double capRefinementColumnComputeMilliseconds = 0.0;
double classificationExclusiveMilliseconds = 0.0;
double marchingCubesExclusiveMilliseconds = 0.0;
double groundRefinementExclusiveMilliseconds = 0.0;
double capRefinementExclusiveMilliseconds = 0.0;
@@ -687,7 +691,8 @@ namespace VoxelForgeCore
std::int64_t minimumYSubvoxels, std::int64_t minimumZSubvoxels,
std::int64_t maximumXSubvoxels, std::int64_t maximumYSubvoxels,
std::int64_t maximumZSubvoxels, std::int32_t stepVoxels) const noexcept;
static void resetThreadColumnCacheStats(bool captureCoordinates = false) noexcept;
static void resetThreadColumnCacheStats(bool captureCoordinates = false,
bool clearCache = false, std::uint32_t octaveDrop = 0) noexcept;
static const HeightfieldColumnCacheStats& threadColumnCacheStats() noexcept;
FieldInterval interval(std::int64_t minimumXSubvoxels,
std::int64_t minimumYSubvoxels,
+25 -2
View File
@@ -1479,12 +1479,18 @@ namespace VoxelForgeCore
const bool bSkipFarShellRepair=key.lod>=7&&bUseNativeSurfaceHorizon;
FStandaloneShapeWorld::ResetThreadClassificationStats();
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(
options.captureColumnCoordinates&&key.lod==8);
options.captureColumnCoordinates);
bool bNativeSurfaceTile=false;
bool bNativeSurfaceBandSafe=false;
double classificationColumnComputeMilliseconds=0.0;
if(bHasWorldAssembly)
{
const double columnMillisecondsBeforeClassification=
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
const auto Classification=tileShape.ClassifyBoxDetailed(origin,step,cells);
classificationColumnComputeMilliseconds=std::max(0.0,
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds
-columnMillisecondsBeforeClassification);
bNativeSurfaceTile=Classification.bNativeSurface;
// NativeSurfaceDensityAt consumes only the bounded manifest heightfield and its
// bounded type-2/type-4 features. Unknown tile occupancy does not make that Z support
@@ -1518,7 +1524,16 @@ namespace VoxelForgeCore
out.diagnostics.columnCacheHits=Columns.columnHits;
out.diagnostics.columnCacheMisses=Columns.columnMisses;
out.diagnostics.classificationMilliseconds=Counts.Milliseconds;
out.diagnostics.classificationExclusiveMilliseconds=std::max(0.0,
Counts.Milliseconds-classificationColumnComputeMilliseconds);
out.diagnostics.columnComputeMilliseconds=Columns.columnComputeMilliseconds;
if(options.captureColumnCoordinates)
{
out.diagnostics.columnEvaluationWorkerHash=static_cast<std::uint64_t>(
std::hash<std::thread::id>{}(std::this_thread::get_id()))+1u;
out.diagnostics.columnCoordinateCaptureComplete=Columns.coordinateCaptureComplete;
out.diagnostics.evaluatedColumnCoordinates=Columns.evaluatedCoordinates;
}
out.densitySampleCount=0;
return out;
}
@@ -1662,6 +1677,8 @@ namespace VoxelForgeCore
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
out.diagnostics.marchingCubesExclusiveMilliseconds=std::max(0.0,
MarchingCubesWallMilliseconds-(ColumnMillisecondsAfterMeshing-ColumnMillisecondsBeforeMeshing));
out.diagnostics.marchingCubesColumnComputeMilliseconds=std::max(0.0,
ColumnMillisecondsAfterMeshing-ColumnMillisecondsBeforeMeshing);
const double MeshingMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-MeshingStart).count();
out.diagnostics.farHorizonVersion=key.lod>=4?options.farHorizonVersion:0;
@@ -1712,6 +1729,8 @@ namespace VoxelForgeCore
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
out.diagnostics.groundRefinementExclusiveMilliseconds=std::max(0.0,
GroundWallMilliseconds-(ColumnMillisecondsAfterGround-ColumnMillisecondsBeforeGround));
out.diagnostics.groundRefinementColumnComputeMilliseconds=std::max(0.0,
ColumnMillisecondsAfterGround-ColumnMillisecondsBeforeGround);
const double ColumnMillisecondsBeforeCap=
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
const auto CapStart=std::chrono::steady_clock::now();
@@ -1723,6 +1742,8 @@ namespace VoxelForgeCore
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
out.diagnostics.capRefinementExclusiveMilliseconds=std::max(0.0,
CapWallMilliseconds-(ColumnMillisecondsAfterCap-ColumnMillisecondsBeforeCap));
out.diagnostics.capRefinementColumnComputeMilliseconds=std::max(0.0,
ColumnMillisecondsAfterCap-ColumnMillisecondsBeforeCap);
out.diagnostics.farHorizonMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-HorizonStart).count();
out.diagnostics.capRefined=CapStats.Refined;
@@ -1747,7 +1768,9 @@ namespace VoxelForgeCore
out.diagnostics.columnComputeMilliseconds=Columns.columnComputeMilliseconds;
out.diagnostics.columnEvaluationExclusiveMilliseconds=Columns.columnComputeMilliseconds;
out.diagnostics.classificationMilliseconds=Counts.Milliseconds;
if(options.captureColumnCoordinates&&key.lod==8)
out.diagnostics.classificationExclusiveMilliseconds=std::max(0.0,
Counts.Milliseconds-classificationColumnComputeMilliseconds);
if(options.captureColumnCoordinates)
{
out.diagnostics.columnEvaluationWorkerHash=static_cast<std::uint64_t>(
std::hash<std::thread::id>{}(std::this_thread::get_id()))+1u;
+15 -2
View File
@@ -1027,11 +1027,18 @@ namespace world_assembly_detail
std::int64_t wavelengthSubvoxels)
{ return FloorDiv(positionSubvoxels * Q16, wavelengthSubvoxels); }
// Audit-only override; normal tile generation resets this to zero before evaluating a recipe.
// Surcharge reservee aux mesures; la generation remet zero avant d'evaluer une recette.
inline thread_local std::uint32_t HeightfieldColumnOctaveDrop = 0;
inline std::int32_t FractalNoiseQ16(std::int64_t xQ16, std::int64_t yQ16,
std::int64_t zQ16, std::uint32_t octaves, bool ridged = false)
{
std::int64_t total = 0, weightSum = 0, weight = Q16, ridgeWeight = Q16;
for (std::uint32_t octave = 0; octave < octaves; ++octave)
std::uint32_t effectiveOctaves=octaves;
if(HeightfieldColumnOctaveDrop!=0&&octaves!=0)
effectiveOctaves=std::max(1u,octaves-std::min(octaves-1,HeightfieldColumnOctaveDrop));
for (std::uint32_t octave = 0; octave < effectiveOctaves; ++octave)
{
const std::int64_t scale = 1ll << octave;
const std::int64_t noise = Perlin3DQ16(xQ16 * scale,yQ16 * scale,zQ16 * scale);
@@ -1965,9 +1972,15 @@ HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y,
return out;
}
void Heightfield2D::resetThreadColumnCacheStats(bool captureCoordinates) noexcept
void Heightfield2D::resetThreadColumnCacheStats(bool captureCoordinates, bool clearCache,
std::uint32_t octaveDrop) noexcept
{
// Diagnostic probes may clear old tile entries before comparing isolated sample grids.
// Les sondes de mesure peuvent vider les entrees d'une tuile avant de comparer deux grilles.
if(clearCache)world_assembly_detail::HeightfieldColumnCache.fill(
world_assembly_detail::HeightfieldColumnCacheEntry{});
auto& Stats=world_assembly_detail::HeightfieldColumnStats;
world_assembly_detail::HeightfieldColumnOctaveDrop=octaveDrop;
Stats.pointCalls=0;
Stats.columnHits=0;
Stats.columnMisses=0;