standalone: whole view under one second -- 0.955 s cold @8 workers, full payload, world unchanged

- Eight-sample X batching of fine density rows, reuse of solved Z intervals in the candidate mask,
  AVX2 marching-cubes cell classification. LOD0/1 density-grid time -61/-62%; interval/sign stage
  140 -> 97 ms; candidate-axis allocation -18 MiB. Shape stage ~413 ms, detail band ~219 ms.
- W1/W8 hashes equal across 44 fields and equal to round 8; routes 5642/5642; look and far-survival
  audits pass; bad-shell control still fails; six fixed captures pixel-identical.
  Remote-view recapture pending (camera 500,0,0).
Verified by me, interleaved (8 workers, cold, BelowNormal 0xFF00, 2,226 tiles, 110.9 MiB payload):
1.012 / 0.927 / 0.926 s vs round 8 1.100 / 1.099 / 1.088 s.

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-05 05:15:19 +02:00
1 parent 16612acba4
commit 2656d81ac9
4 files changed
+667 -109

No files matched your search

+423 -77
View File
@@ -22,6 +22,7 @@
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
// Standalone-only world compiler. It compiles the runtime room/tunnel/passages into
@@ -510,6 +511,55 @@ public:
static_cast<float>(Z));
}
void DensityAtLatticeX8(int32 XStart,int32 XStep,int32 Y,int32 Z,
int32 Count,float OutDensity[8]) const
{
Count=std::clamp(Count,0,8);
if(Count==0)return;
bool bFast=EditBrushes.empty()&&Y>=Min[1]&&Y<=Max[1]
&&Z>=Min[2]&&Z<=Max[2]
&&XStart>=Min[0]
&&XStart+static_cast<int64>(Count-1)*XStep<=Max[0];
if(bFast)
{
for(int32 Lane=0;Lane<Count;++Lane)
if(IsForcedFarAirSample(XStart+Lane*XStep,Y,Z))
{bFast=false;break;}
}
if(!bFast)
{
for(int32 Lane=0;Lane<Count;++Lane)
OutDensity[Lane]=DensityAtLattice(XStart+Lane*XStep,Y,Z);
return;
}
const size_t ColumnStart=static_cast<size_t>(Y-Min[1])*Size[0]
+static_cast<size_t>(XStart-Min[0]);
const size_t Zi=static_cast<size_t>(Z-Min[2]);
const size_t WordOffset=Zi>>6;
const uint64 Mask=uint64{1}<<(Zi&63u);
const size_t NodeBase=Zi*static_cast<size_t>(Size[1])*Size[0]+ColumnStart;
constexpr float ProxyBandEdge=DetailSupremum+0.125f;
for(int32 Lane=0;Lane<Count;++Lane)
{
const size_t Column=ColumnStart+static_cast<size_t>(Lane*XStep);
const size_t Word=Column*WordsPerZ+WordOffset;
const bool bRock=(RockWords[Word]&Mask)!=0;
float Density;
if(CandidateWords[Word]&Mask)
{
const int8 Proxy=SurfaceDistanceQ[NodeBase
+static_cast<size_t>(Lane*XStep)];
Density=static_cast<float>(Proxy)*(1.0f/16.0f);
if((Density<0.0f)!=bRock)
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),1.0f/16.0f);
}
else
Density=bRock?-ProxyBandEdge:ProxyBandEdge;
OutDensity[Lane]=Density;
}
}
private:
static uint64 PackLatticeKey(int32 X,int32 Y,int32 Z)
{
@@ -2378,17 +2428,44 @@ private:
void BuildPackedRockIntervals(FParallelTiming& Timing)
{
const int32 NX=Size[0],NY=Size[1],NZ=Size[2];
ParallelLines(static_cast<size_t>(NX)*NY,[&](size_t Line)
CandidateAxisWords[2].assign(static_cast<size_t>(NX)*NY*WordsPerZ,0);
struct FIntervalScratch
{
std::vector<FInterval> Carve,Solid,ProtectedAir;
};
std::array<FIntervalScratch,16> WorkerScratch;
for(int32 Worker=0;Worker<WorkerCount;++Worker)
{
WorkerScratch[static_cast<size_t>(Worker)].Carve.reserve(24);
WorkerScratch[static_cast<size_t>(Worker)].Solid.reserve(8);
WorkerScratch[static_cast<size_t>(Worker)].ProtectedAir.reserve(4);
}
ParallelLines(static_cast<size_t>(NX)*NY,[&](size_t Line,int32 Worker)
{
const int32 X=Min[0]+static_cast<int32>(Line%NX),Y=Min[1]+static_cast<int32>(Line/NX);
std::vector<FInterval> Carve,Solid,ProtectedAir;
Carve.reserve(24);Solid.reserve(8);ProtectedAir.reserve(4);
FIntervalScratch& Scratch=WorkerScratch[static_cast<size_t>(Worker)];
std::vector<FInterval>& Carve=Scratch.Carve;
std::vector<FInterval>& Solid=Scratch.Solid;
std::vector<FInterval>& ProtectedAir=Scratch.ProtectedAir;
Carve.clear();Solid.clear();ProtectedAir.clear();
const int32 Fixed[3]={X,Y,0};
for(uint32 Id:GetLineCandidates(2,X,Y))
{
const FPrimitive& P=Primitives[Id];FInterval R;
if(SolveLine(P,2,Fixed,R))
{
if(!P.bCritical)
{
const size_t CandidateBase=Line*WordsPerZ;
const int32 CandidateLo=std::max(Min[2],R.Lo-DetailBand);
const int32 CandidateHi=std::min(Max[2],R.Lo+DetailBand);
const int32 CandidateLo2=std::max(Min[2],R.Hi-DetailBand);
const int32 CandidateHi2=std::min(Max[2],R.Hi+DetailBand);
FillBits(CandidateAxisWords[2],CandidateBase,
CandidateLo-Min[2],CandidateHi-Min[2],true);
FillBits(CandidateAxisWords[2],CandidateBase,
CandidateLo2-Min[2],CandidateHi2-Min[2],true);
}
R.Lo=std::max(R.Lo,Min[2])-Min[2];R.Hi=std::min(R.Hi,Max[2])-Min[2];
if(R.Lo<=R.Hi)
{
@@ -2407,7 +2484,7 @@ private:
for(const FInterval& R:Solid)FillBits(RockWords,Base,R.Lo,R.Hi,true);
// Critical passage air is the final authored override, including at landing floors.
for(const FInterval& R:ProtectedAir)FillBits(RockWords,Base,R.Lo,R.Hi,false);
},&Timing);
},&Timing,nullptr,16);
}
void BuildCandidateBands(FParallelTiming& AxisTiming,FParallelTiming& MergeTiming,
@@ -2416,6 +2493,7 @@ private:
const int32 N[3]={Size[0],Size[1],Size[2]};
for(int32 Axis=0;Axis<3;++Axis)
{
if(Axis==2)continue;
const int32 A=(Axis+1)%3,B=(Axis+2)%3;
const size_t Lines=static_cast<size_t>(Size[A])*Size[B];
const size_t Words=(static_cast<size_t>(N[Axis])+63u)/64u;
@@ -2437,10 +2515,10 @@ private:
FillBits(CandidateAxisWords[Axis],Base,Lo-Min[Axis],Hi-Min[Axis],true);
FillBits(CandidateAxisWords[Axis],Base,Lo2-Min[Axis],Hi2-Min[Axis],true);
}
},&AxisTiming);
},&AxisTiming,nullptr,16);
}
const auto MergeStart=std::chrono::steady_clock::now();
CandidateWords=CandidateAxisWords[2];
CandidateWords=std::move(CandidateAxisWords[2]);
for(int32 Axis=0;Axis<2;++Axis)
{
const int32 A=(Axis+1)%3,B=(Axis+2)%3;
@@ -2465,7 +2543,7 @@ private:
.fetch_or(uint64{1}<<(Zi&63u),std::memory_order_relaxed);
}
}
},&MergeTiming);
},&MergeTiming,nullptr,16);
}
const int32 LayerLo=LayerRanges.empty()?1:LayerRanges[0].Lo;
const int32 LayerHi=LayerRanges.empty()?0:LayerRanges[0].Hi;
@@ -2500,6 +2578,18 @@ private:
const int32 Y=Min[1]+static_cast<int32>(Line/Size[0]);
FDetailNoiseLineContext NoiseLine(static_cast<float>(X),static_cast<float>(Y),
static_cast<uint32>(Owner->Seed));
const FLayerField* LineField=nullptr;
bool bLineFieldKnown=false;
int32 LineFieldLo=0,LineFieldHi=-1;
const auto FindLineLayerField=[&](double Z)->const FLayerField*
{
if(bLineFieldKnown&&LineField&&Z>=LineFieldLo&&Z<=LineFieldHi)
return LineField;
LineField=FindLayerField(Z);
bLineFieldKnown=LineField!=nullptr;
if(LineField){LineFieldLo=LineField->Lo;LineFieldHi=LineField->Hi;}
return LineField;
};
const int32 QueryBinX=FloorDiv(X,ActiveSpatialBinSize);
const int32 QueryBinY=FloorDiv(Y,ActiveSpatialBinSize);
int32 CachedQueryBinZ=std::numeric_limits<int32>::min();
@@ -2511,80 +2601,114 @@ private:
for(size_t W=0;W<WordsPerZ;++W)
{
uint64 Bits=CandidateWords[WordBase+W];
const uint64 OriginalBits=Bits;
uint64 ValidBits=0;
int32 BatchZ[8]{};
int32 BatchCount=0,BatchQueryBinZ=std::numeric_limits<int32>::min();
const FBinItems* BatchBinItems=nullptr;
const auto ProcessBatch=[&]()
{
if(BatchCount==0)return;
FEffectiveRockSample Samples[8];
int32 ProfileDistanceSamples=0;
if(bProfile)
{
for(int32 Lane=0;Lane<BatchCount;++Lane)
if((Profile.Candidates++&255u)==0)++ProfileDistanceSamples;
}
const auto DistanceStart=ProfileDistanceSamples>0
?Clock::now():Clock::time_point{};
EffectiveRockDistanceZ8(X,Y,BatchZ,BatchCount,BatchBinItems,Samples);
if(ProfileDistanceSamples>0)
{
Profile.DistanceSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(
Clock::now()-DistanceStart).count());
Profile.DistanceSamples+=static_cast<uint64>(BatchCount);
}
for(int32 Lane=0;Lane<BatchCount;++Lane)
{
const int32 Z=BatchZ[Lane];
const size_t Zi=static_cast<size_t>(Z-Min[2]);
++LocalCandidates;
const FEffectiveRockSample& Sample=Samples[Lane];
const float SDF=Sample.Distance;
if(std::abs(SDF)>DetailSupremum+0.125f)continue;
const float SurfaceSDF=Sample.SurfaceSDF;
const float CaveInfluence=Sample.CaveInfluence;
const float FloorInfluence=Sample.FloorInfluence;
const bool bCriticalAir=Sample.bCriticalAir;
float DetailFade=DetailBandFade(SDF);
float MacroValue=0.0f;
bool bMacroKnown=false;
const FLayerField* DetailField=nullptr;
bool bDetailFieldKnown=false;
if(DetailVariant==EDetailVariant::Coherent
&&std::abs(SDF)>DetailBandInnerRadius)
{
MacroValue=NoiseLine.Macro.Sample(static_cast<float>(Z));
bMacroKnown=true;
DetailField=FindLineLayerField(Z);
bDetailFieldKnown=true;
DetailFade=DetailBandFade(SDF,LocalDetailBandEdge(MacroValue,
SurfaceSDF,CaveInfluence,FloorInfluence,DetailField));
}
if(DetailFade<=0.0f&&bProfile)++Profile.LocalBandSkips;
const bool bTimeDetail=DetailFade>0.0f&&bProfile
&&((Profile.DetailEvaluations&63u)==0);
const auto DetailStart=bTimeDetail?Clock::now():Clock::time_point{};
float Displaced=SDF;
if(DetailFade>0.0f)
{
if(bProfile)++Profile.DetailEvaluations;
Displaced+=DetailNoiseOnLine(X,Y,Z,Sample.SurfaceSDF,
CaveInfluence,FloorInfluence,NoiseLine,bTimeDetail?&Profile:nullptr,
DetailField,bDetailFieldKnown,bMacroKnown,MacroValue)*DetailFade;
}
if(bTimeDetail)
{
Profile.DetailSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(
Clock::now()-DetailStart).count());
++Profile.DetailSamples;
}
if(bCriticalAir)Displaced=std::min(Displaced,-1.0f/16.0f);
const bool bNewRock=Displaced>=0.0f;
const bool bOldRock=((RockWords[WordBase+(Zi>>6)]>>(Zi&63u))&1u)!=0;
if(bNewRock!=bOldRock)
{
RockWords[WordBase+(Zi>>6)]^=uint64{1}<<(Zi&63u);
++LocalFlips;
if(bProfile)++Profile.Flips;
}
const size_t Node=(Zi*static_cast<size_t>(Size[1])
+static_cast<size_t>(Y-Min[1]))*Size[0]
+static_cast<size_t>(X-Min[0]);
StoreSurfaceDistance(Node,-Displaced);
ValidBits|=uint64{1}<<(Zi&63u);
++LocalProxies;
}
BatchCount=0;
};
while(Bits)
{
const uint32 Bit=std::countr_zero(Bits);Bits&=Bits-1;
const size_t Zi=W*64+Bit;if(Zi>=static_cast<size_t>(Size[2]))continue;
const int32 Z=Min[2]+static_cast<int32>(Zi);
++LocalCandidates;
const int32 QueryBinZ=FloorDiv(Z,ActiveSpatialBinSize);
if(BatchCount>0&&QueryBinZ!=BatchQueryBinZ)ProcessBatch();
if(QueryBinZ!=CachedQueryBinZ)
{
const auto Bin=SpatialBins.find({QueryBinX,QueryBinY,QueryBinZ});
CachedQueryBinItems=Bin==SpatialBins.end()?nullptr:&Bin->second;
CachedQueryBinZ=QueryBinZ;
}
const bool bTimeDistance=bProfile&&((Profile.Candidates++&255u)==0);
const auto DistanceStart=bTimeDistance?Clock::now():Clock::time_point{};
ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false,bCriticalAir=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
const float SDF=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence,&bCriticalAir,
CachedQueryBinItems,true);
if(bTimeDistance)
{
Profile.DistanceSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-DistanceStart).count());
++Profile.DistanceSamples;
}
if(std::abs(SDF)>DetailSupremum+0.125f)continue;
float DetailFade=DetailBandFade(SDF);
const FLayerField* DetailField=nullptr;
bool bDetailFieldKnown=false;
if(DetailVariant==EDetailVariant::Coherent
&&std::abs(SDF)>DetailBandInnerRadius)
{
const float MacroValue=NoiseLine.Macro.Sample(static_cast<float>(Z));
DetailField=FindLayerField(Z);
bDetailFieldKnown=true;
DetailFade=DetailBandFade(SDF,LocalDetailBandEdge(MacroValue,SurfaceSDF,
CaveInfluence,FloorInfluence,DetailField));
}
if(DetailFade<=0.0f&&bProfile)++Profile.LocalBandSkips;
const bool bTimeDetail=DetailFade>0.0f&&bProfile
&&((Profile.DetailEvaluations&63u)==0);
const auto DetailStart=bTimeDetail?Clock::now():Clock::time_point{};
float Displaced=SDF;
if(DetailFade>0.0f)
{
if(bProfile)++Profile.DetailEvaluations;
Displaced+=DetailNoiseOnLine(X,Y,Z,SurfaceSDF,CaveInfluence,
FloorInfluence,NoiseLine,bTimeDetail?&Profile:nullptr,
DetailField,bDetailFieldKnown)*DetailFade;
}
if(bTimeDetail)
{
Profile.DetailSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-DetailStart).count());
++Profile.DetailSamples;
}
if(bCriticalAir)Displaced=std::min(Displaced,-1.0f/16.0f);
const bool bNewRock=Displaced>=0.0f;
const bool bOldRock=((RockWords[WordBase+(Zi>>6)]>>(Zi&63u))&1u)!=0;
if(bNewRock!=bOldRock)
{
RockWords[WordBase+(Zi>>6)]^=uint64{1}<<(Zi&63u);
++LocalFlips;
if(bProfile)++Profile.Flips;
}
const size_t Node=(Zi*static_cast<size_t>(Size[1])+static_cast<size_t>(Y-Min[1]))
*Size[0]+static_cast<size_t>(X-Min[0]);
StoreSurfaceDistance(Node,-Displaced);
ValidBits|=uint64{1}<<Bit;
++LocalProxies;
if(BatchCount==0)BatchQueryBinZ=QueryBinZ,BatchBinItems=CachedQueryBinItems;
BatchZ[BatchCount++]=Z;
if(BatchCount==8)ProcessBatch();
}
CandidateWords[WordBase+W]=ValidBits;
ProcessBatch();
if(OriginalBits!=ValidBits)CandidateWords[WordBase+W]=ValidBits;
}
CandidateCount.fetch_add(LocalCandidates,std::memory_order_relaxed);
ProxyCount.fetch_add(LocalProxies,std::memory_order_relaxed);
@@ -2853,6 +2977,220 @@ private:
return RockDistance;
}
struct FEffectiveRockSample
{
float Distance=0.0f;
float SurfaceSDF=0.0f;
float CaveInfluence=0.0f;
float FloorInfluence=0.0f;
ESurfaceFamily Family=ESurfaceFamily::Strata;
bool bFloorSurface=false;
bool bCriticalAir=false;
};
void ShapeDistanceZ8(const FPrimitive& P,int32 X,int32 Y,const int32 Z[8],int32 Count,
float OutDistance[8]) const
{
const __m256d Zero=_mm256_setzero_pd();
const __m256d One=_mm256_set1_pd(1.0);
const __m256d SignMask=_mm256_set1_pd(-0.0);
for(int32 Base=0;Base<Count;Base+=4)
{
double ZLane[4];
const int32 Lanes=std::min(4,Count-Base);
for(int32 Lane=0;Lane<4;++Lane)
ZLane[Lane]=static_cast<double>(Z[Base+std::min(Lane,Lanes-1)]);
const __m256d VZ=_mm256_setr_pd(ZLane[0],ZLane[1],ZLane[2],ZLane[3]);
__m256d Distance;
if(P.bCapsule)
{
const double VX=static_cast<double>(X)-P.CapsuleStartWorld[0];
const double VY=static_cast<double>(Y)-P.CapsuleStartWorld[1];
const __m256d VXv=_mm256_set1_pd(VX);
const __m256d VYv=_mm256_set1_pd(VY);
const __m256d VZv=_mm256_sub_pd(VZ,_mm256_set1_pd(P.CapsuleStartWorld[2]));
__m256d VD=_mm256_mul_pd(VXv,_mm256_set1_pd(P.CapsuleAxisDelta[0]));
VD=_mm256_add_pd(VD,_mm256_mul_pd(VYv,_mm256_set1_pd(P.CapsuleAxisDelta[1])));
VD=_mm256_add_pd(VD,_mm256_mul_pd(VZv,_mm256_set1_pd(P.CapsuleAxisDelta[2])));
const __m256d T=P.CapsuleAxisLengthSquared>1.0e-12
?_mm256_max_pd(Zero,_mm256_min_pd(One,
_mm256_div_pd(VD,_mm256_set1_pd(P.CapsuleAxisLengthSquared))))
:Zero;
const __m256d EX=_mm256_sub_pd(VXv,
_mm256_mul_pd(T,_mm256_set1_pd(P.CapsuleAxisDelta[0])));
const __m256d EY=_mm256_sub_pd(VYv,
_mm256_mul_pd(T,_mm256_set1_pd(P.CapsuleAxisDelta[1])));
const __m256d EZ=_mm256_sub_pd(VZv,
_mm256_mul_pd(T,_mm256_set1_pd(P.CapsuleAxisDelta[2])));
__m256d DistanceSquared=_mm256_mul_pd(EX,EX);
DistanceSquared=_mm256_add_pd(DistanceSquared,_mm256_mul_pd(EY,EY));
DistanceSquared=_mm256_add_pd(DistanceSquared,_mm256_mul_pd(EZ,EZ));
const __m256d Root=_mm256_sqrt_pd(DistanceSquared);
double TValues[4],RootValues[4];
_mm256_storeu_pd(TValues,T);
_mm256_storeu_pd(RootValues,Root);
for(int32 Lane=0;Lane<Lanes;++Lane)
OutDistance[Base+Lane]=static_cast<float>(RootValues[Lane]
-CapsuleRadiusAtT(P,TValues[Lane],P.CapsuleAxisLength));
continue;
}
if(P.bRoundedBox)
{
const double DX=std::abs(static_cast<double>(X)-P.CenterSub[0]/16.0)
-P.RoundedHalfExtent[0];
const double DY=std::abs(static_cast<double>(Y)-P.CenterSub[1]/16.0)
-P.RoundedHalfExtent[1];
const double DZCenter=P.CenterSub[2]/16.0;
const __m256d DZv=_mm256_sub_pd(_mm256_andnot_pd(SignMask,
_mm256_sub_pd(VZ,_mm256_set1_pd(DZCenter))),
_mm256_set1_pd(P.RoundedHalfExtent[2]));
const __m256d DXv=_mm256_set1_pd(DX),DYv=_mm256_set1_pd(DY);
const __m256d Ox=_mm256_max_pd(DXv,Zero),Oy=_mm256_max_pd(DYv,Zero);
const __m256d Oz=_mm256_max_pd(DZv,Zero);
__m256d OutsideSquared=_mm256_mul_pd(Ox,Ox);
OutsideSquared=_mm256_add_pd(OutsideSquared,_mm256_mul_pd(Oy,Oy));
OutsideSquared=_mm256_add_pd(OutsideSquared,_mm256_mul_pd(Oz,Oz));
const __m256d MaxXY=_mm256_max_pd(DXv,DYv);
const __m256d MaxQ=_mm256_max_pd(MaxXY,DZv);
const __m256d Inside=_mm256_min_pd(MaxQ,Zero);
Distance=_mm256_sub_pd(_mm256_add_pd(_mm256_sqrt_pd(OutsideSquared),Inside),
_mm256_set1_pd(P.Rounding));
}
else
{
const __m256d D[3]={
_mm256_set1_pd(static_cast<double>(X)-P.CenterSub[0]/16.0),
_mm256_set1_pd(static_cast<double>(Y)-P.CenterSub[1]/16.0),
_mm256_sub_pd(VZ,_mm256_set1_pd(P.CenterSub[2]/16.0))};
__m256d Q=_mm256_setzero_pd();
for(int32 I=0;I<3;++I)for(int32 J=0;J<3;++J)
{
const double Coefficient=static_cast<double>(P.Q[I][J])/QScale;
__m256d Term=_mm256_mul_pd(_mm256_set1_pd(Coefficient),D[I]);
Term=_mm256_mul_pd(Term,D[J]);
Term=_mm256_mul_pd(Term,_mm256_set1_pd(256.0));
Q=_mm256_add_pd(Q,Term);
}
Distance=_mm256_mul_pd(_mm256_sub_pd(
_mm256_sqrt_pd(_mm256_max_pd(Q,Zero)),_mm256_set1_pd(1.0)),
_mm256_set1_pd(P.MinRadius));
}
double Values[4];
_mm256_storeu_pd(Values,Distance);
for(int32 Lane=0;Lane<Lanes;++Lane)
OutDistance[Base+Lane]=static_cast<float>(Values[Lane]);
}
}
void EffectiveRockDistanceZ8(int32 X,int32 Y,const int32 Z[8],int32 Count,
const FBinItems* BinItems,FEffectiveRockSample Out[8]) const
{
float Base[8],Cave[8],Overlay[8],CaveFloorInfluence[8];
bool CriticalAir[8]{};
for(int32 Lane=0;Lane<Count;++Lane)
{
Base[Lane]=StrataDistance(Z[Lane]);
Cave[Lane]=100000.0f;
Overlay[Lane]=-100000.0f;
CaveFloorInfluence[Lane]=0.0f;
}
if(BinItems)
{
for(uint32 Id:*BinItems)
{
const FPrimitive& P=Primitives[Id];
bool Eligible[8]{};
bool AnyEligible=false;
if(P.bCarve)
{
for(int32 Lane=0;Lane<Count;++Lane)
{
const int32 ZValue=Z[Lane];
if(P.bCritical&&X>=P.Min[0]&&X<=P.Max[0]
&&Y>=P.Min[1]&&Y<=P.Max[1]
&&ZValue>=P.Min[2]&&ZValue<=P.Max[2]
&&ShapeContains(P,X,Y,ZValue))
CriticalAir[Lane]=true;
if(X<P.Min[0]-DetailBand||X>P.Max[0]+DetailBand
||Y<P.Min[1]-DetailBand||Y>P.Max[1]+DetailBand
||ZValue<P.Min[2]-DetailBand||ZValue>P.Max[2]+DetailBand)
continue;
Eligible[Lane]=true;
AnyEligible=true;
}
}
else
{
for(int32 Lane=0;Lane<Count;++Lane)
{
const int32 ZValue=Z[Lane];
Eligible[Lane]=X>=P.Min[0]&&X<=P.Max[0]
&&Y>=P.Min[1]&&Y<=P.Max[1]
&&ZValue>=P.Min[2]&&ZValue<=P.Max[2];
AnyEligible=AnyEligible||Eligible[Lane];
}
}
if(!AnyEligible)continue;
float Distances[8];
ShapeDistanceZ8(P,X,Y,Z,Count,Distances);
if(P.bCarve)
{
const double Floor=P.bHasFloorClip?FloorAt(P,X,Y):0.0;
const float MetadataWidth=static_cast<float>(std::max(P.BlendRadius,6.0));
for(int32 Lane=0;Lane<Count;++Lane)
{
if(!Eligible[Lane])continue;
float Distance=Distances[Lane];
float PrimitiveFloorInfluence=0.0f;
if(P.bHasFloorClip)
{
const float FloorT=(static_cast<float>(std::abs(Z[Lane]-Floor))
-static_cast<float>(P.FloorBlendRadius))
/static_cast<float>(std::max(DetailBand*2,1));
PrimitiveFloorInfluence=1.0f-SmoothUnit(FloorT);
Distance=SmoothMaxDistance(Distance,static_cast<float>(Floor-Z[Lane]),
static_cast<float>(P.FloorBlendRadius));
}
const float PrimitiveWeight=SmoothUnit(0.5f
+(Cave[Lane]-Distance)/(2.0f*MetadataWidth));
const float Combined=SmoothMinDistance(Cave[Lane],Distance,
static_cast<float>(P.BlendRadius));
CaveFloorInfluence[Lane]=std::lerp(CaveFloorInfluence[Lane],
PrimitiveFloorInfluence,PrimitiveWeight);
Cave[Lane]=Combined;
}
}
else
{
for(int32 Lane=0;Lane<Count;++Lane)
if(Eligible[Lane])Overlay[Lane]=std::max(Overlay[Lane],-Distances[Lane]);
}
}
}
for(int32 Lane=0;Lane<Count;++Lane)
{
const float CarveOrOverlay=std::max(Cave[Lane],Overlay[Lane]);
const ESurfaceFamily Family=Base[Lane]<=CarveOrOverlay?ESurfaceFamily::Strata:
(Cave[Lane]>=Overlay[Lane]?ESurfaceFamily::Cave:ESurfaceFamily::SolidOverlay);
constexpr float FamilyBlendWidth=6.0f;
const float CaveVsOverlay=SmoothUnit(0.5f
+(Cave[Lane]-Overlay[Lane])/(2.0f*FamilyBlendWidth));
const float CarveInfluence=SmoothUnit(0.5f
+(Base[Lane]-CarveOrOverlay)/(2.0f*FamilyBlendWidth));
const float CaveInfluence=CarveInfluence*CaveVsOverlay;
const float FloorInfluence=CaveInfluence*CaveFloorInfluence[Lane];
Out[Lane].Distance=SmoothMinDistance(Base[Lane],CarveOrOverlay,StrataCaveBlendRadius);
Out[Lane].Family=Family;
Out[Lane].SurfaceSDF=Family==ESurfaceFamily::Strata?Base[Lane]:
(Family==ESurfaceFamily::Cave?Cave[Lane]:Overlay[Lane]);
Out[Lane].CaveInfluence=CaveInfluence;
Out[Lane].FloorInfluence=FloorInfluence;
Out[Lane].bFloorSurface=FloorInfluence>=0.5f;
Out[Lane].bCriticalAir=CriticalAir[Lane];
}
}
float CoarseRockDistance(double X,double Y,double Z,bool* OutCriticalAir) const
{
const float Base=StrataDistance(Z);
@@ -3065,8 +3403,8 @@ private:
{
if(NewZ0==Z0+1)
{
Corners[0]=Corners[4];Corners[1]=Corners[5];
Corners[2]=Corners[6];Corners[3]=Corners[7];
Corners[0]=Corners[4];Corners[2]=Corners[6];
Corners[1]=Corners[5];Corners[3]=Corners[7];
SetPlane(NewZ0+1,true);
}
else
@@ -3092,14 +3430,15 @@ private:
Fine(X,Y,6,Seed^0xA511E9B3u)
{}
float SurfaceReliefAt(float Z)
float SurfaceReliefAt(float Z,bool bMacroKnown=false,float KnownMacro=0.0f)
{
const float MacroValue=Macro.Sample(Z);
const float MacroValue=bMacroKnown?KnownMacro:Macro.Sample(Z);
const float MidValue=Mid.Sample(Z);
const float FineValue=Fine.Sample(Z);
return 1.00f*MacroValue+(0.80f+0.25f*MacroValue)
*(0.85f*MidValue+0.18f*FineValue);
}
};
float DetailNoise(double X,double Y,double Z,float SurfaceSDF,
@@ -3127,13 +3466,14 @@ private:
FDetailNoiseLineContext& NoiseLine,
FDetailWorkerProfile* Profile=nullptr,
const FLayerField* KnownField=nullptr,
bool bFieldKnown=false) const
bool bFieldKnown=false,bool bMacroKnown=false,
float KnownMacro=0.0f) const
{
if(DetailVariant!=EDetailVariant::Coherent)
return DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence);
using Clock=std::chrono::steady_clock;
const auto ReliefStart=Profile?Clock::now():Clock::time_point{};
const float Relief=NoiseLine.SurfaceReliefAt(static_cast<float>(Z));
const float Relief=NoiseLine.SurfaceReliefAt(static_cast<float>(Z),bMacroKnown,KnownMacro);
if(Profile)
Profile->ReliefSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-ReliefStart).count());
@@ -3225,7 +3565,7 @@ private:
}
template<class Fn> void ParallelLines(size_t Count,Fn&& Work,FParallelTiming* Timing=nullptr,
std::array<double,16>* WorkerActiveSeconds=nullptr) const
std::array<double,16>* WorkerActiveSeconds=nullptr,size_t WorkBatch=1) const
{
const auto WallStart=std::chrono::steady_clock::now();
if(WorkerCount<=1||Count<512)
@@ -3247,6 +3587,7 @@ private:
}
return;
}
WorkBatch=std::max<size_t>(WorkBatch,1);
std::atomic<size_t> Next{0};std::atomic<uint64> WorkerNanoseconds{0};
std::vector<std::thread> Threads;Threads.reserve(WorkerCount);
for(int32 W=0;W<WorkerCount;++W)
@@ -3255,9 +3596,14 @@ private:
const auto WorkerStart=std::chrono::steady_clock::now();
for(;;)
{
const size_t I=Next.fetch_add(1,std::memory_order_relaxed);if(I>=Count)break;
if constexpr(std::is_invocable_v<Fn&,size_t,int32>)Work(I,W);
else Work(I);
const size_t First=Next.fetch_add(WorkBatch,std::memory_order_relaxed);
if(First>=Count)break;
const size_t Last=First+std::min(WorkBatch,Count-First);
for(size_t I=First;I<Last;++I)
{
if constexpr(std::is_invocable_v<Fn&,size_t,int32>)Work(I,W);
else Work(I);
}
}
const uint64 WorkerNs=static_cast<uint64>(std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now()-WorkerStart).count());