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:
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),
|
||||
|
||||
Reference in new issue
Block a user