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