perf(core): tile-drain instrumentation; parallel FI sign-edge repair, FI first tile 1.28 -> 0.85 s (L55); R24

Opt-in --instrument-tile-drain records per-tile queue/dispatch/finish timelines, stage spans and counters
(proof skips, X8 lanes, GradAt/midpoint memo, skirts, FI cache stats); <2% when off. Measured: SW 13.3 and
FI 50.2 worker-s across LOD0-8, tails are work-saturated. FI sign-edge repair now computes in parallel and
publishes in sorted serial order (exact output): sign-edge 535 -> 47 ms, FI first tile 1.28 -> 0.85 s, near
ring 1.46 -> 1.04 s, whole view 6.09 -> 5.65 s. Proof block size, lane batching, SW midpoint memo and helper
cap 12 measured and left off. fast/medium/full PASS, Clang and GCC. Research R24: slab + motif generator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-25 06:16:07 +01:00
1 parent e35a77f676
commit aee1487410
13 files changed
+882 -74

No files matched your search

@@ -4,6 +4,7 @@
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <chrono>
#include <cstring>
#include <memory>
#include <thread>
@@ -12,6 +13,15 @@
namespace VoxelForgeCoreShared
{
struct FColumnCacheMetrics
{
std::uint64_t hits = 0;
std::uint64_t misses = 0;
std::uint64_t computes = 0;
std::uint64_t conflictReplacements = 0;
std::uint64_t lockWaitNanoseconds = 0;
};
// Exact input identity for a pure XY column value. LOD is deliberately not part of this key:
// the same recipe/plan column has the same bits at every sampling step.
struct FColumnCacheKey
@@ -69,13 +79,21 @@ namespace VoxelForgeCoreShared
struct FSpinGuard
{
explicit FSpinGuard(std::atomic_flag& value) : Flag(value)
explicit FSpinGuard(std::atomic_flag& value, FColumnCacheMetrics* metrics)
: Flag(value), Metrics(metrics)
{
const auto started = Metrics ? std::chrono::steady_clock::now()
: std::chrono::steady_clock::time_point{};
while (Flag.test_and_set(std::memory_order_acquire))
std::this_thread::yield();
if (Metrics)
Metrics->lockWaitNanoseconds += static_cast<std::uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now() - started).count());
}
~FSpinGuard() { Flag.clear(std::memory_order_release); }
std::atomic_flag& Flag;
FColumnCacheMetrics* Metrics;
};
public:
@@ -94,7 +112,7 @@ namespace VoxelForgeCoreShared
template <typename TFactory>
TValue getOrCompute(const FColumnCacheKey& key, TFactory&& factory,
bool& computed)
bool& computed, FColumnCacheMetrics* metrics = nullptr)
{
computed = false;
const FKeyWords keyWords = key.words();
@@ -102,13 +120,23 @@ namespace VoxelForgeCoreShared
const std::size_t setIndex = static_cast<std::size_t>(tag) & (SetCount - 1);
FSet& set = Sets[setIndex];
TValue value{};
if (TryReadSet(set, keyWords, tag, value)) return value;
if (TryReadSet(set, keyWords, tag, value))
{
if (metrics) ++metrics->hits;
return value;
}
if (metrics) ++metrics->misses;
FSpinGuard lock(set.Writer);
if (TryReadSet(set, keyWords, tag, value)) return value;
FSpinGuard lock(set.Writer, metrics);
if (TryReadSet(set, keyWords, tag, value))
{
if (metrics) ++metrics->hits;
return value;
}
value = std::forward<TFactory>(factory)();
computed = true;
if (metrics) ++metrics->computes;
FValueWords valueWords{};
std::memcpy(valueWords.data(), &value, sizeof(TValue));
@@ -118,6 +146,7 @@ namespace VoxelForgeCoreShared
{ replacement = way; break; }
if (replacement == Ways)
{
if (metrics) ++metrics->conflictReplacements;
replacement = set.NextWay;
set.NextWay = static_cast<std::uint8_t>((set.NextWay + 1) % Ways);
}
@@ -139,6 +139,14 @@ namespace VoxelForgeCore
std::uint64_t columnCacheConflictMisses = 0;
std::uint64_t sharedColumnCacheHits = 0;
std::uint64_t columnEvaluations = 0;
std::uint64_t islandLocalCacheHits = 0;
std::uint64_t islandLocalCacheMisses = 0;
std::uint64_t islandLocalCacheConflictReplacements = 0;
std::uint64_t islandSharedCacheHits = 0;
std::uint64_t islandSharedCacheMisses = 0;
std::uint64_t islandSharedCacheComputes = 0;
std::uint64_t islandSharedCacheConflictReplacements = 0;
std::uint64_t islandSharedCacheLockWaitNanoseconds = 0;
std::uint64_t uniformBlockProofsChecked = 0;
std::uint64_t uniformBlockProofSamplesChecked = 0;
std::uint64_t uniformBlockProofFallbackBlocks = 0;
@@ -160,8 +168,22 @@ namespace VoxelForgeCore
double marchingCubesSkirtMilliseconds = 0.0;
double marchingCubesOtherMilliseconds = 0.0;
std::uint64_t marchingCubesGradientFallbackDensityCalls = 0;
std::uint64_t marchingCubesGradientCalls = 0;
std::uint64_t marchingCubesEdgeMidpointDensityCalls = 0;
std::uint64_t marchingCubesEdgeMidpointDensityMemoHits = 0;
std::uint64_t marchingCubesGradientMemoHits = 0;
std::uint64_t marchingCubesX8DensityCalls = 0;
std::uint64_t marchingCubesX8ActiveLanes = 0;
std::uint64_t marchingCubesCandidateCells = 0;
std::uint64_t marchingCubesZBandRejectedCells = 0;
std::uint64_t marchingCubesCellClassificationCalls = 0;
std::uint64_t marchingCubesSurfaceCellCalls = 0;
std::uint64_t marchingCubesProofSkippedSolidCells = 0;
std::uint64_t marchingCubesProofSkippedAirCells = 0;
std::uint64_t marchingCubesProofSkippedDensitySamples = 0;
std::uint64_t marchingCubesSkirtEdgeCandidates = 0;
std::uint64_t marchingCubesSkirtBoundaryCandidates = 0;
std::uint64_t marchingCubesSkirtDensityProbes = 0;
double groundRefinementExclusiveMilliseconds = 0.0;
double capRefinementExclusiveMilliseconds = 0.0;
double meshAssemblyMilliseconds = 0.0;
@@ -1475,6 +1497,7 @@ namespace VoxelForgeCore
bool roundTunnels = false;
bool enhancedTunnelWalls = true;
bool profileDetailBand = false;
bool parallelSignEdgeRepair = false;
// World-wide walkability is a validation pass, not an input to the compiled field.
// Keep it enabled for benchmarks/audits; the interactive viewer can defer it.
bool validatePassageRoutes = true;
@@ -1677,6 +1700,12 @@ 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;
// Research-only: collect tile-drain kernel counters and floating-island cache metrics.
// Inactive on ordinary generation so the hot path only pays the option check.
bool instrumentTileDrain = false;
bool exactMidpointDensityMemo = false;
bool activeLaneBatching = false;
std::uint32_t proofBlockCells = 8;
// 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;
+36 -3
View File
@@ -471,6 +471,26 @@ namespace floating_island_detail
double DeltaX=0.0,DeltaY=0.0,Radial=0.0,Angle=0.0,LobePhase=0.0,TopSurface=0.0;
};
struct FIslandColumnCacheInstrumentation
{
std::uint64_t localHits=0,localMisses=0,localConflictReplacements=0;
VoxelForgeCoreShared::FColumnCacheMetrics shared;
};
inline FIslandColumnCacheInstrumentation*& ActiveIslandColumnCacheInstrumentation() noexcept
{
static thread_local FIslandColumnCacheInstrumentation* active=nullptr;
return active;
}
inline FIslandColumnCacheInstrumentation* SetIslandColumnCacheInstrumentation(
FIslandColumnCacheInstrumentation* value) noexcept
{
FIslandColumnCacheInstrumentation* previous=ActiveIslandColumnCacheInstrumentation();
ActiveIslandColumnCacheInstrumentation()=value;
return previous;
}
using FSharedIslandColumnCache =
VoxelForgeCoreShared::TSharedColumnValueCache<FIslandColumnCacheEntry,16384,4>;
@@ -501,6 +521,7 @@ namespace floating_island_detail
IndexKey^=IndexKey>>33;
const std::size_t Base=static_cast<std::size_t>(IndexKey&(SetCount-1))*Ways;
FIslandColumnCacheEntry* Replacement=&cache[Base+((IndexKey>>11)&(Ways-1))];
bool bFoundInvalid=false;
for(std::size_t Way=0;Way<Ways;++Way)
{
FIslandColumnCacheEntry& Entry=cache[Base+Way];
@@ -516,8 +537,18 @@ namespace floating_island_detail
&&Entry.TopHalf==island.topHalfSubvoxels
&&Entry.TopFlattenQ16==recipe.topFlattenQ16
&&Entry.ContourWarp==recipe.contourWarpSubvoxels;
if(bKeyMatches)return Entry;
if(!Entry.bValid)Replacement=&Entry;
if(bKeyMatches)
{
if(auto* stats=ActiveIslandColumnCacheInstrumentation())++stats->localHits;
return Entry;
}
if(!Entry.bValid&&!bFoundInvalid)
{Replacement=&Entry;bFoundInvalid=true;}
}
if(auto* stats=ActiveIslandColumnCacheInstrumentation())
{
++stats->localMisses;
if(!bFoundInvalid)++stats->localConflictReplacements;
}
const auto build=[&]()
@@ -562,7 +593,9 @@ namespace floating_island_detail
static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(&plan)),
plan.contentHash,SampleX,SampleY,island.stableId};
bool computed=false;
Entry=shared->getOrCompute(key,build,computed);
auto* stats=ActiveIslandColumnCacheInstrumentation();
Entry=shared->getOrCompute(key,build,computed,
stats?&stats->shared:nullptr);
}
else Entry=build();
*Replacement=Entry;
+145 -1
View File
@@ -2246,7 +2246,8 @@ public:
const std::vector<VoxelForgeCore::DetailBandTileRegion>& InGpuVisualDetailTiles={},
const std::vector<VoxelForgeCore::DetailBandTileRegion>& InCpuVisualDetailTiles={},
const VoxelForgeCore::WorldAssemblyManifest* InWorldAssembly=nullptr,
const VoxelForgeCore::ResolvedLayoutSnapshotV3* InResolvedLayout=nullptr)
const VoxelForgeCore::ResolvedLayoutSnapshotV3* InResolvedLayout=nullptr,
bool bInParallelSignEdgeRepair=false)
{
using Clock=std::chrono::steady_clock;
const auto TotalStart=Clock::now();
@@ -2257,6 +2258,7 @@ public:
BindResolvedStratumFields();
bAnalyticOnly=false;
bProfileDetailBand=bInProfileDetailBand;
bParallelSignEdgeRepair=bInParallelSignEdgeRepair;
for(int32 A=0;A<3;++A)
{
Min[A]=(&MinVoxels.X)[A]-2;
@@ -9084,6 +9086,8 @@ private:
uint64 CompleteSignEdgeDistances()
{
if(bParallelSignEdgeRepair&&Scheduler)
return CompleteSignEdgeDistancesParallel();
uint64 AddedTotal=0,SignCorrections=0;
const size_t Columns=static_cast<size_t>(Size[0])*Size[1];
for(int32 Pass=0;Pass<4;++Pass)
@@ -9183,6 +9187,145 @@ private:
return AddedTotal;
}
uint64 CompleteSignEdgeDistancesParallel()
{
uint64 AddedTotal=0,SignCorrections=0;
const size_t Columns=static_cast<size_t>(Size[0])*Size[1];
for(int32 Pass=0;Pass<4;++Pass)
{
std::vector<size_t> MissingNodes;
const auto AddMissing=[&](int32 X,int32 Y,int32 Z)
{
if(X<Min[0]||X>Max[0]||Y<Min[1]||Y>Max[1]||Z<Min[2]||Z>Max[2])return;
const size_t Column=static_cast<size_t>(Y-Min[1])*Size[0]
+static_cast<size_t>(X-Min[0]);
const size_t Zi=static_cast<size_t>(Z-Min[2]);
const size_t Word=Column*WordsPerZ+(Zi>>6);
const uint64 Mask=uint64{1}<<(Zi&63u);
if((CandidateWords[Word]&Mask)==0)
MissingNodes.push_back((Zi*static_cast<size_t>(Size[1])
+static_cast<size_t>(Y-Min[1]))*Size[0]
+static_cast<size_t>(X-Min[0]));
};
// Discover against a stable pass snapshot. Density work is then independent per
// lattice node; packed candidates and occupancy corrections publish in node order.
for(size_t Line=0;Line<Columns;++Line)
{
const int32 X=Min[0]+static_cast<int32>(Line%Size[0]);
const int32 Y=Min[1]+static_cast<int32>(Line/Size[0]);
const size_t Base=Line*WordsPerZ;
for(size_t W=0;W<WordsPerZ;++W)
{
const size_t Word=Base+W;
const uint64 Rock=RockWords[Word],Candidates=CandidateWords[Word];
const uint64 PreviousRock=W?RockWords[Word-1]>>63:0;
const uint64 PreviousCandidates=W?CandidateWords[Word-1]>>63:0;
uint64 Valid=(Size[2]-W*64)>=64?~uint64{0}
:((uint64{1}<<(Size[2]-W*64))-1);
if(W==0)Valid&=~uint64{1};
const uint64 ShiftedRock=(Rock<<1)|PreviousRock;
const uint64 ShiftedCandidates=(Candidates<<1)|PreviousCandidates;
uint64 Missing=((Rock^ShiftedRock)&Valid)
&~(Candidates&ShiftedCandidates);
while(Missing)
{
const uint32 Bit=std::countr_zero(Missing);Missing&=Missing-1;
const int32 Z=Min[2]+static_cast<int32>(W*64+Bit);
AddMissing(X,Y,Z);AddMissing(X,Y,Z-1);
}
if(X<Max[0])
{
const size_t Other=Word+WordsPerZ;
Missing=(Rock^RockWords[Other])&Valid
&~(Candidates&CandidateWords[Other]);
while(Missing)
{
const uint32 Bit=std::countr_zero(Missing);Missing&=Missing-1;
AddMissing(X,Y,Min[2]+static_cast<int32>(W*64+Bit));
AddMissing(X+1,Y,Min[2]+static_cast<int32>(W*64+Bit));
}
}
if(Y<Max[1])
{
const size_t Other=Word+static_cast<size_t>(Size[0])*WordsPerZ;
Missing=(Rock^RockWords[Other])&Valid
&~(Candidates&CandidateWords[Other]);
while(Missing)
{
const uint32 Bit=std::countr_zero(Missing);Missing&=Missing-1;
AddMissing(X,Y,Min[2]+static_cast<int32>(W*64+Bit));
AddMissing(X,Y+1,Min[2]+static_cast<int32>(W*64+Bit));
}
}
}
}
std::sort(MissingNodes.begin(),MissingNodes.end());
MissingNodes.erase(std::unique(MissingNodes.begin(),MissingNodes.end()),
MissingNodes.end());
std::vector<float> Densities(MissingNodes.size());
struct FParallelContext
{
FStandaloneShapeWorld* Shape;
const std::vector<size_t>* Nodes;
std::vector<float>* Values;
int32 Minimum[3];
int32 Extent[3];
} Context{this,&MissingNodes,&Densities,{Min[0],Min[1],Min[2]},
{Size[0],Size[1],Size[2]}};
const auto EvaluateNode=[](void* Raw,size_t Index,size_t)
{
auto& C=*static_cast<FParallelContext*>(Raw);
const size_t Node=(*C.Nodes)[Index];
const int32 X=static_cast<int32>(Node%static_cast<size_t>(C.Extent[0]));
const size_t YZ=Node/static_cast<size_t>(C.Extent[0]);
const int32 Y=static_cast<int32>(YZ%static_cast<size_t>(C.Extent[1]));
const int32 Z=static_cast<int32>(YZ/static_cast<size_t>(C.Extent[1]));
(*C.Values)[Index]=C.Shape->AnalyticDensityAt(
static_cast<float>(C.Minimum[0]+X),static_cast<float>(C.Minimum[1]+Y),
static_cast<float>(C.Minimum[2]+Z));
};
Scheduler->parallel_for(MissingNodes.size(),64,
static_cast<size_t>(std::max(1,WorkerCount)),&Context,EvaluateNode,nullptr,0);
uint64 AddedThisPass=0;
bool bSignChanged=false;
for(size_t Index=0;Index<MissingNodes.size();++Index)
{
const size_t Node=MissingNodes[Index];
const int32 X=Min[0]+static_cast<int32>(Node%static_cast<size_t>(Size[0]));
const size_t YZ=Node/static_cast<size_t>(Size[0]);
const int32 Y=Min[1]+static_cast<int32>(YZ%static_cast<size_t>(Size[1]));
const int32 Z=Min[2]+static_cast<int32>(YZ/static_cast<size_t>(Size[1]));
const size_t Column=static_cast<size_t>(Y-Min[1])*Size[0]
+static_cast<size_t>(X-Min[0]);
const size_t Zi=static_cast<size_t>(Z-Min[2]);
const size_t Word=Column*WordsPerZ+(Zi>>6);
const uint64 Mask=uint64{1}<<(Zi&63u);
if(CandidateWords[Word]&Mask)continue;
const float Density=Densities[Index];
StoreSurfaceDistance(Node,Density);
CandidateWords[Word]|=Mask;
++AddedThisPass;
const bool bExactRock=Density<0.0f;
const bool bPackedRock=(RockWords[Word]&Mask)!=0;
if(bExactRock!=bPackedRock)
{
if(bExactRock)RockWords[Word]|=Mask;
else RockWords[Word]&=~Mask;
++SignCorrections;
bSignChanged=true;
}
}
AddedTotal+=AddedThisPass;
if(!bSignChanged)break;
}
std::printf("[GridStairDistanceRepair] exact_endpoint_proxies=%llu occupancy_corrections=%llu mode=parallel_deterministic\n",
static_cast<unsigned long long>(AddedTotal),
static_cast<unsigned long long>(SignCorrections));
return AddedTotal;
}
void StoreSurfaceDistance(size_t Node,float Density)
{
int32 Q=static_cast<int32>(std::lround(Density*256.0f));
@@ -10191,6 +10334,7 @@ private:
const VoxelForgeCore::JobScheduler* Scheduler=nullptr;
bool RoundTunnelsEnabled=false;
bool bProfileDetailBand=false;
bool bParallelSignEdgeRepair=false;
int32 Min[3]{},Max[3]{},ClipMin[3]{},ClipMax[3]{},Size[3]{};
FIntVector EditBoundsMin=FIntVector(MAX_int32,MAX_int32,MAX_int32);
FIntVector EditBoundsMax=FIntVector(MIN_int32,MIN_int32,MIN_int32);
+58 -1
View File
@@ -1069,6 +1069,18 @@ namespace VoxelForgeCore
}
};
struct FScopedIslandColumnCacheInstrumentation
{
floating_island_detail::FIslandColumnCacheInstrumentation* Previous=nullptr;
explicit FScopedIslandColumnCacheInstrumentation(
floating_island_detail::FIslandColumnCacheInstrumentation* Current,bool Enabled)
:Previous(Enabled?floating_island_detail::SetIslandColumnCacheInstrumentation(Current):nullptr),
bEnabled(Enabled){}
~FScopedIslandColumnCacheInstrumentation()
{if(bEnabled)floating_island_detail::SetIslandColumnCacheInstrumentation(Previous);}
bool bEnabled=false;
};
World::World(ResolvedWorldSnapshot snapshot):impl_(std::make_unique<Impl>())
{
if(snapshot.data_)
@@ -1394,7 +1406,8 @@ namespace VoxelForgeCore
static_cast<int32>(std::clamp<std::uint32_t>(request.workerLimit,1,16)),ToNative(request.detail),
request.includeFarWorld,error,&scheduler,request.roundTunnels,request.profileDetailBand,
request.validatePassageRoutes,request.gpuVisualDetailTiles,
request.cpuVisualDetailTiles,impl_->worldAssembly.get(),impl_->resolvedLayout.get());
request.cpuVisualDetailTiles,impl_->worldAssembly.get(),impl_->resolvedLayout.get(),
request.parallelSignEdgeRepair);
if(ok)
{
impl_->shape=std::move(shape);
@@ -1999,6 +2012,9 @@ namespace VoxelForgeCore
FScopedSharedColumnCaches sharedColumnCaches(options.useSharedColumnCache
?impl_->sharedHeightfieldColumns.get():nullptr,
options.useSharedColumnCache?impl_->sharedIslandColumns.get():nullptr);
floating_island_detail::FIslandColumnCacheInstrumentation islandCacheInstrumentation;
FScopedIslandColumnCacheInstrumentation islandCacheScope(
&islandCacheInstrumentation,options.instrumentTileDrain);
const auto& world=*impl_->owner;
FStandaloneShapeWorld nativeOnlyShape;
FIntVector nativeOnlyMinimum,nativeOnlyMaximum;
@@ -2028,6 +2044,10 @@ namespace VoxelForgeCore
mesher.bStandaloneSkipUV=options.skipUv;mesher.bStandaloneSkipColors=options.skipColors;
mesher.bStandaloneSkipMaterialGroups=options.skipMaterialGroups;
mesher.bStandaloneEnableGradientMemoization=options.memoizeMcGradients;
mesher.bStandaloneEnableExactMidpointDensityMemo=options.exactMidpointDensityMemo;
mesher.bStandaloneEnableActiveLaneBatching=options.activeLaneBatching;
mesher.bStandaloneProofBlockCells=static_cast<int32>(std::clamp<std::uint32_t>(
options.proofBlockCells,1u,static_cast<std::uint32_t>(std::max(1,CHUNK_SIZE))));
mesher.bGenerateSkirts=world.bGenerateSkirts;
mesher.SkirtCells=static_cast<float>(world.SkirtCells)
*static_cast<float>(world.OwnerVoxelSizeMicrons)
@@ -2138,6 +2158,21 @@ namespace VoxelForgeCore
int64 sampleCount=0;std::vector<float> densityValues;
FVoxelMarchingCubesStageTimings McStageTimings;
TileMesh out;out.key=key;
const auto CopyIslandCacheMetrics=[&]()
{
if(!options.instrumentTileDrain)return;
out.diagnostics.islandLocalCacheHits=islandCacheInstrumentation.localHits;
out.diagnostics.islandLocalCacheMisses=islandCacheInstrumentation.localMisses;
out.diagnostics.islandLocalCacheConflictReplacements=
islandCacheInstrumentation.localConflictReplacements;
out.diagnostics.islandSharedCacheHits=islandCacheInstrumentation.shared.hits;
out.diagnostics.islandSharedCacheMisses=islandCacheInstrumentation.shared.misses;
out.diagnostics.islandSharedCacheComputes=islandCacheInstrumentation.shared.computes;
out.diagnostics.islandSharedCacheConflictReplacements=
islandCacheInstrumentation.shared.conflictReplacements;
out.diagnostics.islandSharedCacheLockWaitNanoseconds=
islandCacheInstrumentation.shared.lockWaitNanoseconds;
};
out.diagnostics.featureRefinedResolution=bFeatureRefinedResolution;
if(options.profileMesherStages)
{
@@ -2291,6 +2326,7 @@ namespace VoxelForgeCore
out.diagnostics.columnCoordinateCaptureComplete=Columns.coordinateCaptureComplete;
out.diagnostics.evaluatedColumnCoordinates=Columns.evaluatedCoordinates;
}
CopyIslandCacheMetrics();
out.densitySampleCount=0;
return out;
}
@@ -2558,9 +2594,29 @@ namespace VoxelForgeCore
out.diagnostics.marchingCubesOtherMilliseconds=McStageTimings.OtherMilliseconds;
out.diagnostics.marchingCubesGradientFallbackDensityCalls=
McStageTimings.GradientFallbackDensityCalls;
out.diagnostics.marchingCubesGradientCalls=McStageTimings.GradientCalls;
out.diagnostics.marchingCubesEdgeMidpointDensityCalls=
McStageTimings.EdgeMidpointDensityCalls;
out.diagnostics.marchingCubesEdgeMidpointDensityMemoHits=
McStageTimings.EdgeMidpointDensityMemoHits;
out.diagnostics.marchingCubesGradientMemoHits=McStageTimings.GradientMemoHits;
out.diagnostics.marchingCubesX8DensityCalls=McStageTimings.X8DensityCalls;
out.diagnostics.marchingCubesX8ActiveLanes=McStageTimings.X8ActiveLanes;
out.diagnostics.marchingCubesCandidateCells=McStageTimings.CandidateCells;
out.diagnostics.marchingCubesZBandRejectedCells=McStageTimings.ZBandRejectedCells;
out.diagnostics.marchingCubesCellClassificationCalls=
McStageTimings.CellClassificationCalls;
out.diagnostics.marchingCubesSurfaceCellCalls=McStageTimings.SurfaceCellCalls;
out.diagnostics.marchingCubesProofSkippedSolidCells=
McStageTimings.ProofSkippedSolidCells;
out.diagnostics.marchingCubesProofSkippedAirCells=
McStageTimings.ProofSkippedAirCells;
out.diagnostics.marchingCubesProofSkippedDensitySamples=
McStageTimings.ProofSkippedDensitySamples;
out.diagnostics.marchingCubesSkirtEdgeCandidates=McStageTimings.SkirtEdgeCandidates;
out.diagnostics.marchingCubesSkirtBoundaryCandidates=
McStageTimings.SkirtBoundaryCandidates;
out.diagnostics.marchingCubesSkirtDensityProbes=McStageTimings.SkirtDensityProbes;
}
const double MeshingMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-MeshingStart).count();
@@ -2822,6 +2878,7 @@ namespace VoxelForgeCore
}
out.diagnostics.meshAssemblyMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-MeshAssemblyStart).count();
CopyIslandCacheMetrics();
return out;
}