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

@@ -15940,13 +15940,282 @@ bool RunCoreSharedFeatureCostAudit(VoxelForgeCore::World& World,
const std::vector<VoxelForgeCore::TileKey>& Keys,const TCHAR* Label,
const VoxelForgeCore::WorldAssemblyManifest* Assembly,FString& OutError)
{
for (const int32 LOD : {0,1,7,8})
if(Assembly!=nullptr&&FCString::Stricmp(Label,TEXT("surface"))==0)
{
struct FProbeColumn
{
std::int64_t Ground=0;
std::int64_t Cap=0;
};
const auto P95=[](TArray<double> Values)
{ Values.Sort(); return Values.IsEmpty() ? 0.0 : Values[FMath::Clamp(
FMath::CeilToInt(Values.Num()*0.95f)-1,0,Values.Num()-1)]; };
const auto MaxValue=[](const TArray<double>& Values)
{ return Values.IsEmpty() ? 0.0 : *std::max_element(Values.GetData(),Values.GetData()+Values.Num()); };
const std::int32_t TileVoxelCount=static_cast<std::int32_t>(
World.configuration().tileVoxelCount);
constexpr std::int64_t Subvoxels=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
constexpr double CoreVoxelSizeCm=25.0;
for(const std::int32_t LOD:{0,1,3,4,5,8})
{
for(const std::int32_t Stride:{2,4})
{
const std::int32_t Cells=LOD<World.configuration().fullResolutionLodCount
?TileVoxelCount:std::max(1,World.configuration().coarseTileCells);
TArray<double> GroundErrorsCm,CapErrorsCm,BaselineColumnMs,CandidateColumnMs;
std::uint64_t BaselineColumns=0,CandidateColumns=0,FiniteFeatureTiles=0;
std::int32_t Tiles=0;
for(const auto& Key:Keys)
{
if(Key.lod!=LOD||Tiles>=2)continue;
const std::int64_t TileExtent=static_cast<std::int64_t>(TileVoxelCount)<<LOD;
const std::int64_t TileStep=TileExtent/Cells;
const std::int64_t OriginX=static_cast<std::int64_t>(Key.coordinate.x)*TileExtent;
const std::int64_t OriginY=static_cast<std::int64_t>(Key.coordinate.y)*TileExtent;
const std::int64_t OriginZ=static_cast<std::int64_t>(Key.coordinate.z)*TileExtent;
const std::int64_t TileMinimumZ=OriginZ;
const std::int64_t TileMaximumZ=OriginZ+TileExtent;
const auto PlanIt=std::find_if(Assembly->plan.strates.begin(),
Assembly->plan.strates.end(),[TileMinimumZ,TileMaximumZ,TileVoxelCount](const auto& Plan)
{
const std::int64_t PlanMinimumZ=static_cast<std::int64_t>(
std::min(Plan.bottomChunkZ,Plan.topChunkZ))*TileVoxelCount;
const std::int64_t PlanMaximumZ=(static_cast<std::int64_t>(
std::max(Plan.bottomChunkZ,Plan.topChunkZ))+1)*TileVoxelCount;
return TileMinimumZ<=PlanMaximumZ&&TileMaximumZ>=PlanMinimumZ;
});
if(PlanIt==Assembly->plan.strates.end())continue;
const VoxelForgeCore::Heightfield2D Field(Assembly->recipe,*PlanIt);
if(!Field.isValid())continue;
const std::array<std::int64_t,3> Minimum={
(OriginX-TileStep)*Subvoxels,(OriginY-TileStep)*Subvoxels,
(OriginZ-TileStep)*Subvoxels};
const std::array<std::int64_t,3> Maximum={
(OriginX+TileExtent+TileStep)*Subvoxels,
(OriginY+TileExtent+TileStep)*Subvoxels,
(OriginZ+TileExtent+TileStep)*Subvoxels};
std::vector<VoxelForgeCore::WorldAssemblyFeatureQueryV1> Queries;
Queries.push_back({Field.recipe(),Field.plan(),Field.buildFeatureTileList(Minimum,Maximum)});
const VoxelForgeCore::ScopedWorldAssemblyFeatureQueriesV1 QueryScope{
std::span<const VoxelForgeCore::WorldAssemblyFeatureQueryV1>(Queries)};
const bool bHasFiniteFeature=std::any_of(PlanIt->features.begin(),
PlanIt->features.end(),[&](const auto& Feature)
{
return (Feature.type==2||Feature.type==4)
&&Feature.minimumSubvoxels[0]<=Maximum[0]
&&Feature.maximumSubvoxels[0]>=Minimum[0]
&&Feature.minimumSubvoxels[1]<=Maximum[1]
&&Feature.maximumSubvoxels[1]>=Minimum[1]
&&Feature.minimumSubvoxels[2]<=Maximum[2]
&&Feature.maximumSubvoxels[2]>=Minimum[2];
});
if(bHasFiniteFeature)++FiniteFeatureTiles;
std::vector<FProbeColumn> Exact(static_cast<std::size_t>(Cells+1)*(Cells+1));
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(false,true);
for(std::int32_t Y=0;Y<=Cells;++Y)
for(std::int32_t X=0;X<=Cells;++X)
{
const auto Sample=Field.point((OriginX+X*TileStep)*Subvoxels,
(OriginY+Y*TileStep)*Subvoxels,0);
Exact[static_cast<std::size_t>(Y)*(Cells+1)+X]={
Sample.groundHeightSubvoxels,Sample.capHeightSubvoxels};
}
const auto& BaselineStats=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
BaselineColumnMs.Add(BaselineStats.columnComputeMilliseconds);
BaselineColumns+=BaselineStats.columnMisses;
std::vector<std::int32_t> Axis;
for(std::int32_t Index=0;Index<=Cells;Index+=Stride)Axis.push_back(Index);
if(Axis.empty()||Axis.back()!=Cells)Axis.push_back(Cells);
std::vector<FProbeColumn> Coarse(static_cast<std::size_t>(Axis.size())*Axis.size());
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(false,true);
for(std::size_t Y=0;Y<Axis.size();++Y)
for(std::size_t X=0;X<Axis.size();++X)
{
const auto Sample=Field.point((OriginX+Axis[X]*TileStep)*Subvoxels,
(OriginY+Axis[Y]*TileStep)*Subvoxels,0);
Coarse[Y*Axis.size()+X]={Sample.groundHeightSubvoxels,
Sample.capHeightSubvoxels};
}
const auto& CandidateStats=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
CandidateColumnMs.Add(CandidateStats.columnComputeMilliseconds);
CandidateColumns+=CandidateStats.columnMisses;
const auto Bracket=[&Axis](std::int32_t Value)
{
const auto Upper=std::upper_bound(Axis.begin(),Axis.end(),Value);
const std::size_t High=Upper==Axis.end()?Axis.size()-1
:static_cast<std::size_t>(Upper-Axis.begin());
const std::size_t Low=High==0?0:High-1;
return std::pair<std::size_t,std::size_t>{Low,High};
};
const auto Lerp=[](double A,double B,double T){return A+(B-A)*T;};
for(std::int32_t Y=0;Y<=Cells;++Y)
for(std::int32_t X=0;X<=Cells;++X)
{
const auto [X0,X1]=Bracket(X);
const auto [Y0,Y1]=Bracket(Y);
const double TX=Axis[X1]==Axis[X0]?0.0
:static_cast<double>(X-Axis[X0])/(Axis[X1]-Axis[X0]);
const double TY=Axis[Y1]==Axis[Y0]?0.0
:static_cast<double>(Y-Axis[Y0])/(Axis[Y1]-Axis[Y0]);
const auto& C00=Coarse[Y0*Axis.size()+X0];
const auto& C10=Coarse[Y0*Axis.size()+X1];
const auto& C01=Coarse[Y1*Axis.size()+X0];
const auto& C11=Coarse[Y1*Axis.size()+X1];
const double Ground=Lerp(Lerp(static_cast<double>(C00.Ground),
static_cast<double>(C10.Ground),TX),Lerp(static_cast<double>(C01.Ground),
static_cast<double>(C11.Ground),TX),TY);
const double Cap=Lerp(Lerp(static_cast<double>(C00.Cap),
static_cast<double>(C10.Cap),TX),Lerp(static_cast<double>(C01.Cap),
static_cast<double>(C11.Cap),TX),TY);
const auto& Reference=Exact[static_cast<std::size_t>(Y)*(Cells+1)+X];
GroundErrorsCm.Add(std::abs(Ground-Reference.Ground)*CoreVoxelSizeCm/Subvoxels);
CapErrorsCm.Add(std::abs(Cap-Reference.Cap)*CoreVoxelSizeCm/Subvoxels);
}
++Tiles;
}
std::uint64_t GroundOver2Cm=0,CapOver2Cm=0;
for(const double Error:GroundErrorsCm)if(Error>2.0)++GroundOver2Cm;
for(const double Error:CapErrorsCm)if(Error>2.0)++CapOver2Cm;
double BaselineAverageMs=0.0,CandidateAverageMs=0.0;
for(const double Value:BaselineColumnMs)BaselineAverageMs+=Value;
for(const double Value:CandidateColumnMs)CandidateAverageMs+=Value;
if(!BaselineColumnMs.IsEmpty())BaselineAverageMs/=BaselineColumnMs.Num();
if(!CandidateColumnMs.IsEmpty())CandidateAverageMs/=CandidateColumnMs.Num();
UE_LOG(LogTemp,Display,
TEXT("[WorldAssemblerInterpolationProbe] label=%s lod=%d tiles=%d stride_samples=%d exact_column_evals=%llu coarse_column_evals=%llu column_eval_saving_pct=%.2f baseline_column_avg_ms=%.5f coarse_column_avg_ms=%.5f ground_error_p95_cm=%.4f ground_error_max_cm=%.4f ground_over_2cm=%llu/%d cap_error_p95_cm=%.4f cap_error_max_cm=%.4f cap_over_2cm=%llu/%d finite_feature_tiles=%llu fallback=none"),
TEXT("surface"),LOD,Tiles,Stride,
static_cast<unsigned long long>(BaselineColumns),
static_cast<unsigned long long>(CandidateColumns),
BaselineAverageMs>0.0?100.0*(1.0-CandidateAverageMs/BaselineAverageMs):0.0,
BaselineAverageMs,CandidateAverageMs,
P95(GroundErrorsCm),MaxValue(GroundErrorsCm),
static_cast<unsigned long long>(GroundOver2Cm),GroundErrorsCm.Num(),
P95(CapErrorsCm),MaxValue(CapErrorsCm),
static_cast<unsigned long long>(CapOver2Cm),CapErrorsCm.Num(),
static_cast<unsigned long long>(FiniteFeatureTiles));
}
}
for(const std::int32_t LOD:{0,1,3,4,5,8})
for(const std::uint32_t Drop:{1u,2u})
{
const std::int32_t Cells=LOD<World.configuration().fullResolutionLodCount
?TileVoxelCount:std::max(1,World.configuration().coarseTileCells);
TArray<double> GroundErrorsCm,CapErrorsCm,BaselineColumnMs,CandidateColumnMs;
std::uint64_t BaselineColumns=0,CandidateColumns=0,FiniteFeatureTiles=0;
std::int32_t Tiles=0;
for(const auto& Key:Keys)
{
if(Key.lod!=LOD||Tiles>=2)continue;
const std::int64_t TileExtent=static_cast<std::int64_t>(TileVoxelCount)<<LOD;
const std::int64_t TileStep=TileExtent/Cells;
const std::int64_t OriginX=static_cast<std::int64_t>(Key.coordinate.x)*TileExtent;
const std::int64_t OriginY=static_cast<std::int64_t>(Key.coordinate.y)*TileExtent;
const std::int64_t OriginZ=static_cast<std::int64_t>(Key.coordinate.z)*TileExtent;
const std::int64_t TileMinimumZ=OriginZ,TileMaximumZ=OriginZ+TileExtent;
const auto PlanIt=std::find_if(Assembly->plan.strates.begin(),
Assembly->plan.strates.end(),[TileMinimumZ,TileMaximumZ,TileVoxelCount](const auto& Plan)
{
const std::int64_t PlanMinimumZ=static_cast<std::int64_t>(
std::min(Plan.bottomChunkZ,Plan.topChunkZ))*TileVoxelCount;
const std::int64_t PlanMaximumZ=(static_cast<std::int64_t>(
std::max(Plan.bottomChunkZ,Plan.topChunkZ))+1)*TileVoxelCount;
return TileMinimumZ<=PlanMaximumZ&&TileMaximumZ>=PlanMinimumZ;
});
if(PlanIt==Assembly->plan.strates.end())continue;
const VoxelForgeCore::Heightfield2D Field(Assembly->recipe,*PlanIt);
if(!Field.isValid())continue;
const std::array<std::int64_t,3> Minimum={
(OriginX-TileStep)*Subvoxels,(OriginY-TileStep)*Subvoxels,
(OriginZ-TileStep)*Subvoxels};
const std::array<std::int64_t,3> Maximum={
(OriginX+TileExtent+TileStep)*Subvoxels,
(OriginY+TileExtent+TileStep)*Subvoxels,
(OriginZ+TileExtent+TileStep)*Subvoxels};
std::vector<VoxelForgeCore::WorldAssemblyFeatureQueryV1> Queries;
Queries.push_back({Field.recipe(),Field.plan(),Field.buildFeatureTileList(Minimum,Maximum)});
const VoxelForgeCore::ScopedWorldAssemblyFeatureQueriesV1 QueryScope{
std::span<const VoxelForgeCore::WorldAssemblyFeatureQueryV1>(Queries)};
const bool bHasFiniteFeature=std::any_of(PlanIt->features.begin(),
PlanIt->features.end(),[&](const auto& Feature)
{
return (Feature.type==2||Feature.type==4)
&&Feature.minimumSubvoxels[0]<=Maximum[0]
&&Feature.maximumSubvoxels[0]>=Minimum[0]
&&Feature.minimumSubvoxels[1]<=Maximum[1]
&&Feature.maximumSubvoxels[1]>=Minimum[1]
&&Feature.minimumSubvoxels[2]<=Maximum[2]
&&Feature.maximumSubvoxels[2]>=Minimum[2];
});
if(bHasFiniteFeature)++FiniteFeatureTiles;
std::vector<FProbeColumn> Exact(static_cast<std::size_t>(Cells+1)*(Cells+1));
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(false,true,0);
for(std::int32_t Y=0;Y<=Cells;++Y)
for(std::int32_t X=0;X<=Cells;++X)
{
const auto Sample=Field.point((OriginX+X*TileStep)*Subvoxels,
(OriginY+Y*TileStep)*Subvoxels,0);
Exact[static_cast<std::size_t>(Y)*(Cells+1)+X]={
Sample.groundHeightSubvoxels,Sample.capHeightSubvoxels};
}
const auto& BaselineStats=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
BaselineColumnMs.Add(BaselineStats.columnComputeMilliseconds);
BaselineColumns+=BaselineStats.columnMisses;
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(false,true,Drop);
for(std::int32_t Y=0;Y<=Cells;++Y)
for(std::int32_t X=0;X<=Cells;++X)
{
const auto Sample=Field.point((OriginX+X*TileStep)*Subvoxels,
(OriginY+Y*TileStep)*Subvoxels,0);
GroundErrorsCm.Add(std::abs(static_cast<double>(Sample.groundHeightSubvoxels)
-Exact[static_cast<std::size_t>(Y)*(Cells+1)+X].Ground)
*CoreVoxelSizeCm/Subvoxels);
CapErrorsCm.Add(std::abs(static_cast<double>(Sample.capHeightSubvoxels)
-Exact[static_cast<std::size_t>(Y)*(Cells+1)+X].Cap)
*CoreVoxelSizeCm/Subvoxels);
}
const auto& CandidateStats=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
CandidateColumnMs.Add(CandidateStats.columnComputeMilliseconds);
CandidateColumns+=CandidateStats.columnMisses;
++Tiles;
}
std::uint64_t GroundOver2Cm=0,CapOver2Cm=0;
for(const double Error:GroundErrorsCm)if(Error>2.0)++GroundOver2Cm;
for(const double Error:CapErrorsCm)if(Error>2.0)++CapOver2Cm;
double BaselineAverageMs=0.0,CandidateAverageMs=0.0;
for(const double Value:BaselineColumnMs)BaselineAverageMs+=Value;
for(const double Value:CandidateColumnMs)CandidateAverageMs+=Value;
if(!BaselineColumnMs.IsEmpty())BaselineAverageMs/=BaselineColumnMs.Num();
if(!CandidateColumnMs.IsEmpty())CandidateAverageMs/=CandidateColumnMs.Num();
UE_LOG(LogTemp,Display,
TEXT("[WorldAssemblerFrequencyProbe] label=surface lod=%d tiles=%d drop_highest_octaves=%d exact_column_evals=%llu reduced_column_evals=%llu column_eval_saving_pct=%.2f baseline_column_avg_ms=%.5f reduced_column_avg_ms=%.5f ground_error_p95_cm=%.4f ground_error_max_cm=%.4f ground_over_2cm=%llu/%d cap_error_p95_cm=%.4f cap_error_max_cm=%.4f cap_over_2cm=%llu/%d finite_feature_tiles=%llu"),
LOD,Tiles,static_cast<std::int32_t>(Drop),
static_cast<unsigned long long>(BaselineColumns),
static_cast<unsigned long long>(CandidateColumns),
BaselineAverageMs>0.0?100.0*(1.0-CandidateAverageMs/BaselineAverageMs):0.0,
BaselineAverageMs,CandidateAverageMs,
P95(GroundErrorsCm),MaxValue(GroundErrorsCm),
static_cast<unsigned long long>(GroundOver2Cm),GroundErrorsCm.Num(),
P95(CapErrorsCm),MaxValue(CapErrorsCm),
static_cast<unsigned long long>(CapOver2Cm),CapErrorsCm.Num(),
static_cast<unsigned long long>(FiniteFeatureTiles));
}
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(false,true,0);
}
for (const int32 LOD : {0,1,3,4,5,8})
{
TArray<double> FullTimes,CandidateTimes,FullShellTimes,CandidateShellTimes,ListTimes;
TArray<double> UnknownBoxes,FieldCalls,ColumnMisses,StratumShellTimes,ConnectorShellTimes;
TArray<double> ClassificationTimes,ColumnComputeTimes,MeshingTimes,HorizonTimes,RepairSkipped;
TArray<double> DensitySamples,ClassificationTimes,ClassificationExclusiveTimes;
TArray<double> ColumnComputeTimes,MeshingTimes,HorizonTimes,RepairSkipped;
TArray<double> ColumnEvalExclusiveTimes,MarchingExclusiveTimes,GroundRefineExclusiveTimes;
TArray<double> CapRefineExclusiveTimes,MeshAssemblyTimes;
TArray<double> MarchingColumnTimes,GroundColumnTimes,CapColumnTimes;
TArray<double> ClassificationNsPerSample,ColumnNsPerSample,MarchingNsPerSample;
TArray<double> GroundNsPerSample,CapNsPerSample,AssemblyNsPerSample,ListNsPerSample;
TArray<double> MarchingColumnNsPerSample,GroundColumnNsPerSample,CapColumnNsPerSample;
TArray<double> StratumFieldCalls,ConnectorFieldCalls,ShellTriangles,GroundTriangles,GroundRemoved,CapTriangles;
for (const auto& Key : Keys)
{
@@ -15955,57 +16224,98 @@ bool RunCoreSharedFeatureCostAudit(VoxelForgeCore::World& World,
Options.hashDensity=true;
Options.useAssemblyFeatureQueries=false;
std::string Error;
// Warm both routes before measuring: column/page caches must not favor the second route.
// Chauffer les deux parcours : les caches ne doivent pas favoriser le second.
if (!World.generateTile(Key,Options,Error)) return false;
// Check density and mesh parity with hashes, then time uncached runtime generation.
// Verifier la parite avec hashes, puis mesurer la generation sans cache de colonnes.
const auto FullParity=World.generateTile(Key,Options,Error);
if (!FullParity) return false;
Options.useAssemblyFeatureQueries=true;
if (!World.generateTile(Key,Options,Error)) return false;
const auto CandidateParity=World.generateTile(Key,Options,Error);
if (!CandidateParity||FullParity->densityHash!=CandidateParity->densityHash
||FullParity->meshHash!=CandidateParity->meshHash
||FullParity->payloadHash!=CandidateParity->payloadHash
||FullParity->topologyHash!=CandidateParity->topologyHash)
{ OutError=TEXT("Shared feature full/candidate tile hashes differ."); return false; }
Options.hashDensity=false;
Options.useAssemblyFeatureQueries=false;
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(false,true);
const double FullStart=FPlatformTime::Seconds();
auto Full=World.generateTile(Key,Options,Error);
FullTimes.Add((FPlatformTime::Seconds()-FullStart)*1000.0);
Options.useAssemblyFeatureQueries=true;
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(false,true);
const double CandidateStart=FPlatformTime::Seconds();
auto Candidate=World.generateTile(Key,Options,Error);
CandidateTimes.Add((FPlatformTime::Seconds()-CandidateStart)*1000.0);
if (!Full||!Candidate||Full->densityHash!=Candidate->densityHash
||Full->meshHash!=Candidate->meshHash||Full->payloadHash!=Candidate->payloadHash
||Full->topologyHash!=Candidate->topologyHash)
{ OutError=TEXT("Shared feature full/candidate tile hashes differ."); return false; }
if (!Full||!Candidate)
{ OutError=TEXT("Shared feature full/candidate measured generation failed."); return false; }
FullShellTimes.Add(Full->diagnostics.farShellMilliseconds);
CandidateShellTimes.Add(Candidate->diagnostics.farShellMilliseconds);
ListTimes.Add(Candidate->diagnostics.featureListMilliseconds);
UnknownBoxes.Add(static_cast<double>(Full->diagnostics.unknownBoxes));
FieldCalls.Add(static_cast<double>(Full->diagnostics.fieldCalls));
ColumnMisses.Add(static_cast<double>(Full->diagnostics.columnCacheMisses));
ClassificationTimes.Add(Full->diagnostics.classificationMilliseconds);
ColumnComputeTimes.Add(Full->diagnostics.columnComputeMilliseconds);
ColumnEvalExclusiveTimes.Add(Full->diagnostics.columnEvaluationExclusiveMilliseconds);
MarchingExclusiveTimes.Add(Full->diagnostics.marchingCubesExclusiveMilliseconds);
GroundRefineExclusiveTimes.Add(Full->diagnostics.groundRefinementExclusiveMilliseconds);
CapRefineExclusiveTimes.Add(Full->diagnostics.capRefinementExclusiveMilliseconds);
MeshAssemblyTimes.Add(Full->diagnostics.meshAssemblyMilliseconds);
MeshingTimes.Add(Full->diagnostics.meshingMilliseconds);
HorizonTimes.Add(Full->diagnostics.farHorizonMilliseconds);
RepairSkipped.Add(Full->diagnostics.farShellRepairSkipped?1.0:0.0);
StratumShellTimes.Add(Full->diagnostics.farStratumShellMilliseconds);
ConnectorShellTimes.Add(Full->diagnostics.farConnectorShellMilliseconds);
StratumFieldCalls.Add(static_cast<double>(Full->diagnostics.farStratumFieldCalls));
ConnectorFieldCalls.Add(static_cast<double>(Full->diagnostics.farConnectorFieldCalls));
ShellTriangles.Add(static_cast<double>(Full->diagnostics.farShellTrianglesAdded));
GroundTriangles.Add(static_cast<double>(Full->diagnostics.horizonGroundTriangles));
GroundRemoved.Add(static_cast<double>(Full->diagnostics.horizonGroundTrianglesRemoved));
CapTriangles.Add(static_cast<double>(Full->diagnostics.horizonCapTriangles));
const auto& Diagnostics=Candidate->diagnostics;
const double SampleCount=static_cast<double>(Candidate->densitySampleCount);
DensitySamples.Add(static_cast<double>(Candidate->densitySampleCount));
UnknownBoxes.Add(static_cast<double>(Diagnostics.unknownBoxes));
FieldCalls.Add(static_cast<double>(Diagnostics.fieldCalls));
ColumnMisses.Add(static_cast<double>(Diagnostics.columnCacheMisses));
ClassificationTimes.Add(Diagnostics.classificationMilliseconds);
ClassificationExclusiveTimes.Add(Diagnostics.classificationExclusiveMilliseconds);
ColumnComputeTimes.Add(Diagnostics.columnComputeMilliseconds);
ColumnEvalExclusiveTimes.Add(Diagnostics.columnEvaluationExclusiveMilliseconds);
MarchingExclusiveTimes.Add(Diagnostics.marchingCubesExclusiveMilliseconds);
MarchingColumnTimes.Add(Diagnostics.marchingCubesColumnComputeMilliseconds);
GroundRefineExclusiveTimes.Add(Diagnostics.groundRefinementExclusiveMilliseconds);
GroundColumnTimes.Add(Diagnostics.groundRefinementColumnComputeMilliseconds);
CapRefineExclusiveTimes.Add(Diagnostics.capRefinementExclusiveMilliseconds);
CapColumnTimes.Add(Diagnostics.capRefinementColumnComputeMilliseconds);
MeshAssemblyTimes.Add(Diagnostics.meshAssemblyMilliseconds);
MeshingTimes.Add(Diagnostics.meshingMilliseconds);
HorizonTimes.Add(Diagnostics.farHorizonMilliseconds);
RepairSkipped.Add(Diagnostics.farShellRepairSkipped?1.0:0.0);
StratumShellTimes.Add(Diagnostics.farStratumShellMilliseconds);
ConnectorShellTimes.Add(Diagnostics.farConnectorShellMilliseconds);
StratumFieldCalls.Add(static_cast<double>(Diagnostics.farStratumFieldCalls));
ConnectorFieldCalls.Add(static_cast<double>(Diagnostics.farConnectorFieldCalls));
ShellTriangles.Add(static_cast<double>(Diagnostics.farShellTrianglesAdded));
GroundTriangles.Add(static_cast<double>(Diagnostics.horizonGroundTriangles));
GroundRemoved.Add(static_cast<double>(Diagnostics.horizonGroundTrianglesRemoved));
CapTriangles.Add(static_cast<double>(Diagnostics.horizonCapTriangles));
if(SampleCount>0.0)
{
ClassificationNsPerSample.Add(Diagnostics.classificationExclusiveMilliseconds
*1000000.0/SampleCount);
ColumnNsPerSample.Add(Diagnostics.columnEvaluationExclusiveMilliseconds
*1000000.0/SampleCount);
MarchingColumnNsPerSample.Add(Diagnostics.marchingCubesColumnComputeMilliseconds
*1000000.0/SampleCount);
MarchingNsPerSample.Add(Diagnostics.marchingCubesExclusiveMilliseconds
*1000000.0/SampleCount);
GroundColumnNsPerSample.Add(Diagnostics.groundRefinementColumnComputeMilliseconds
*1000000.0/SampleCount);
GroundNsPerSample.Add(Diagnostics.groundRefinementExclusiveMilliseconds
*1000000.0/SampleCount);
CapColumnNsPerSample.Add(Diagnostics.capRefinementColumnComputeMilliseconds
*1000000.0/SampleCount);
CapNsPerSample.Add(Diagnostics.capRefinementExclusiveMilliseconds
*1000000.0/SampleCount);
AssemblyNsPerSample.Add(Diagnostics.meshAssemblyMilliseconds
*1000000.0/SampleCount);
ListNsPerSample.Add(Diagnostics.featureListMilliseconds*1000000.0/SampleCount);
}
}
const auto P95=[](TArray<double> Values)
{ Values.Sort(); return Values.IsEmpty() ? 0.0 : Values[FMath::Clamp(
FMath::CeilToInt(Values.Num()*0.95f)-1,0,Values.Num()-1)]; };
UE_LOG(LogTemp,Display,
TEXT("[VoxelForgeSharedFeatureCost] label=%s lod=%d tiles=%d hashes_identical=1 full_p95_ms=%.5f candidate_p95_ms=%.5f list_p95_ms=%.5f classification_p95_ms=%.5f column_compute_p95_ms=%.5f column_eval_exclusive_p95_ms=%.5f mc_exclusive_p95_ms=%.5f ground_refine_exclusive_p95_ms=%.5f cap_refine_exclusive_p95_ms=%.5f mesh_assembly_p95_ms=%.5f meshing_p95_ms=%.5f horizon_p95_ms=%.5f full_shell_p95_ms=%.5f candidate_shell_p95_ms=%.5f repair_skipped_p95=%.0f unknown_boxes_p95=%.0f field_calls_p95=%.0f column_misses_p95=%.0f stratum_shell_p95_ms=%.5f stratum_field_calls_p95=%.0f connector_shell_p95_ms=%.5f connector_field_calls_p95=%.0f shell_triangles_p95=%.0f horizon_ground_triangles_p95=%.0f horizon_ground_removed_p95=%.0f horizon_cap_triangles_p95=%.0f"),
TEXT("[VoxelForgeSharedFeatureCost] label=%s lod=%d tiles=%d hashes_identical=1 full_p95_ms=%.5f candidate_p95_ms=%.5f list_p95_ms=%.5f classification_wall_p95_ms=%.5f classification_exclusive_p95_ms=%.5f column_compute_p95_ms=%.5f column_eval_exclusive_p95_ms=%.5f mc_column_compute_p95_ms=%.5f mc_exclusive_p95_ms=%.5f ground_column_compute_p95_ms=%.5f ground_refine_exclusive_p95_ms=%.5f cap_column_compute_p95_ms=%.5f cap_refine_exclusive_p95_ms=%.5f mesh_assembly_p95_ms=%.5f meshing_p95_ms=%.5f horizon_p95_ms=%.5f samples_p95=%.0f classification_ns_per_sample_p95=%.2f column_ns_per_sample_p95=%.2f mc_column_ns_per_sample_p95=%.2f mc_ns_per_sample_p95=%.2f ground_column_ns_per_sample_p95=%.2f ground_ns_per_sample_p95=%.2f cap_column_ns_per_sample_p95=%.2f cap_ns_per_sample_p95=%.2f assembly_ns_per_sample_p95=%.2f feature_list_ns_per_sample_p95=%.2f full_shell_p95_ms=%.5f candidate_shell_p95_ms=%.5f repair_skipped_p95=%.0f unknown_boxes_p95=%.0f field_calls_p95=%.0f column_misses_p95=%.0f stratum_shell_p95_ms=%.5f stratum_field_calls_p95=%.0f connector_shell_p95_ms=%.5f connector_field_calls_p95=%.0f shell_triangles_p95=%.0f horizon_ground_triangles_p95=%.0f horizon_ground_removed_p95=%.0f horizon_cap_triangles_p95=%.0f"),
Label,LOD,FullTimes.Num(),P95(FullTimes),P95(CandidateTimes),P95(ListTimes),
P95(ClassificationTimes),P95(ColumnComputeTimes),P95(ColumnEvalExclusiveTimes),
P95(MarchingExclusiveTimes),P95(GroundRefineExclusiveTimes),P95(CapRefineExclusiveTimes),
P95(MeshAssemblyTimes),P95(MeshingTimes),P95(HorizonTimes),
P95(ClassificationTimes),P95(ClassificationExclusiveTimes),P95(ColumnComputeTimes),
P95(ColumnEvalExclusiveTimes),P95(MarchingColumnTimes),P95(MarchingExclusiveTimes),
P95(GroundColumnTimes),P95(GroundRefineExclusiveTimes),P95(CapColumnTimes),
P95(CapRefineExclusiveTimes),
P95(MeshAssemblyTimes),P95(MeshingTimes),P95(HorizonTimes),P95(DensitySamples),
P95(ClassificationNsPerSample),P95(ColumnNsPerSample),P95(MarchingColumnNsPerSample),
P95(MarchingNsPerSample),P95(GroundColumnNsPerSample),P95(GroundNsPerSample),
P95(CapColumnNsPerSample),P95(CapNsPerSample),P95(AssemblyNsPerSample),P95(ListNsPerSample),
P95(FullShellTimes),P95(CandidateShellTimes),P95(RepairSkipped),P95(UnknownBoxes),P95(FieldCalls),
P95(ColumnMisses),P95(StratumShellTimes),P95(StratumFieldCalls),
P95(ConnectorShellTimes),P95(ConnectorFieldCalls),P95(ShellTriangles),