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