standalone: speed round 3 -- local detail envelope + 8-voxel owner roughness lattice (band ~590 -> ~500 ms)

- Profiled the band: detail noise ~70% of worker time, workers balanced (2.5% spread).
- Local amplitude envelope skips detail on 34% of candidates; owner cave roughness evaluated on
  global 8-voxel Z anchors and interpolated. Detail evaluations 7.07 M -> 3.53 M.
- The field changes slightly (mean channel delta ~1.7-5.4 /255 in the six captures); all look
  audits pass, routes 5642/5642, W1/W8 hashes equal on all nine outputs.
- Whole view 1.953 s @8w full payload: no end-to-end gain shown (stream timing noise ~10%).
- Rejected: detail band radius 5 (fails GridStair).

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-04 18:12:08 +02:00
1 parent a3cb26b5c8
commit 9ffe296cd1
10 files changed
+399 -36

No files matched your search

+273 -32
View File
@@ -19,6 +19,7 @@
#include <mutex>
#include <immintrin.h>
#include <thread>
#include <type_traits>
#include <unordered_map>
#include <vector>
@@ -48,6 +49,17 @@ public:
double WorkerSeconds = 0.0;
};
struct FDetailWorkerProfile
{
uint64 Lines=0,Candidates=0,DetailEvaluations=0,Flips=0;
uint64 LocalBandSkips=0;
uint64 DistanceSamples=0,DistanceSampleNanoseconds=0;
uint64 DetailSamples=0,DetailSampleNanoseconds=0;
uint64 ReliefSampleNanoseconds=0,LayerLookupSampleNanoseconds=0;
uint64 OwnerRoughnessSampleNanoseconds=0,OwnerRoughnessFieldCalls=0;
uint64 OwnerRoughnessActiveCalls=0,OwnerNoiseCacheHits=0,OwnerNoiseCacheMisses=0;
};
struct FBuildStats
{
double ShapeSeconds = 0.0;
@@ -233,7 +245,7 @@ public:
static constexpr int32 QScale = 1 << 28;
static constexpr int32 SubPerVoxel = 16;
static constexpr int32 DetailBand = 8;
static constexpr int32 DetailBand = 6;
static constexpr float DetailSupremum = 4.0f;
static constexpr double JoinBlendRadius = 1.5;
static constexpr double RoomUnionBlendRadius = 12.0;
@@ -242,6 +254,9 @@ public:
static constexpr int32 LineBinSize = 32;
static constexpr int32 SpatialBinSize = 256;
static void SetDetailProfileEnabled(bool bEnabled)
{ProfileDetailBand.store(bEnabled,std::memory_order_release);}
bool Build(FStandaloneOwnerWorld& World, const FIntVector& MinVoxels,
const FIntVector& MaxVoxels, const FIntVector& ClipMinVoxels,
const FIntVector& ClipMaxVoxels, int32 Workers, EDetailVariant Variant,
@@ -496,8 +511,11 @@ private:
&bFloorSurface,&CaveInfluence,&FloorInfluence,&bCriticalAir);
if(bInsideFine&&DetailVariant!=EDetailVariant::Smooth
&&std::abs(RockDistance)<=DetailSupremum+0.125f)
RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)
*DetailBandFade(RockDistance);
{
const float Fade=DetailBandFadeAt(X,Y,Z,RockDistance,SurfaceSDF,
CaveInfluence,FloorInfluence);
if(Fade>0.0f)RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)*Fade;
}
if(bCriticalAir)RockDistance=std::min(RockDistance,-1.0f/16.0f);
const bool bRock=RockDistance>=0.0f;
float Density=-RockDistance;
@@ -601,8 +619,10 @@ public:
const float Raw=EffectiveRockDistance(X,Y,Z,&Family,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence);
if(std::abs(Raw)>=0.5f)continue;
const float Detail=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)
*DetailBandFade(Raw);
const float Fade=DetailBandFadeAt(X,Y,Z,Raw,SurfaceSDF,
CaveInfluence,FloorInfluence);
const float Detail=Fade>0.0f
?DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)*Fade:0.0f;
const size_t FamilyIndex=static_cast<size_t>(Family);
FFamilyStats& F=Families[FamilyIndex];++F.Count;F.Sum+=Detail;F.SumSq+=Detail*Detail;
if(std::abs(Detail)<0.05f)++F.NearZero;
@@ -674,8 +694,11 @@ public:
&&Z>=Min[2]&&Z<=Max[2];
const bool bCriticalAir=IsInsideCriticalAir(X,Y,Z);
if(bInside&&DetailVariant!=EDetailVariant::Smooth&&std::abs(D)<=DetailSupremum+0.125f)
{Detail=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)
*DetailBandFade(D);D+=Detail;}
{
const float Fade=DetailBandFadeAt(X,Y,Z,D,SurfaceSDF,CaveInfluence,FloorInfluence);
if(Fade>0.0f)
{Detail=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)*Fade;D+=Detail;}
}
if(bCriticalAir)D=std::min(D,-1.0f/16.0f);
if(OutFamily)*OutFamily=Family;
if(OutRaw)*OutRaw=D-Detail;
@@ -958,6 +981,51 @@ private:
constexpr float Width=1.5f;
return SmoothUnit((Edge-std::abs(SignedDistance))/Width);
}
static constexpr float DetailBandInnerRadius=1.5f;
static float DetailBandFade(float SignedDistance,float Edge)
{
const float Distance=std::abs(SignedDistance);
if(Distance<=DetailBandInnerRadius)return 1.0f;
const float Width=Edge-DetailBandInnerRadius;
return Width>0.0f?SmoothUnit((Edge-Distance)/Width):0.0f;
}
static float LocalDetailBandEdge(float MacroValue,float SurfaceSDF,
float CaveInfluence,float FloorInfluence,const FLayerField* Field)
{
float OwnerBound=0.0f;
if(Field)
{
const FStrateGenerationParams& Params=Field->Params;
const float Depth=Params.SurfaceRoughness*2.0f;
if(Depth>0.0f&&std::abs(SurfaceSDF)<Depth&&Params.RoughnessFrequency>0.0f)
{
const float Fade=1.0f-std::abs(SurfaceSDF)/Depth;
// Perlin is bounded by 2; the squared ridged fold by 2.25. This
// uses the largest type bound and remains valid under domain warp.
OwnerBound=3.15f*VOXEL_NOISE_SCALE*Params.SurfaceRoughness*Fade*Fade;
}
}
const float ReliefBound=std::abs(MacroValue)
+(0.80f+0.25f*MacroValue)*(0.85f+0.18f);
const float Contrast=1.35f-0.80f*std::clamp(CaveInfluence,0.0f,1.0f)
+1.85f*std::clamp(FloorInfluence,0.0f,1.0f);
const float LocalSupremum=std::min(3.9f,(ReliefBound+0.5f*OwnerBound)*Contrast);
return std::clamp(LocalSupremum+0.125f,DetailBandInnerRadius,DetailSupremum+0.125f);
}
float DetailBandFadeAt(double X,double Y,double Z,float SignedDistance,float SurfaceSDF,
float CaveInfluence,float FloorInfluence) const
{
if(DetailVariant!=EDetailVariant::Coherent
||std::abs(SignedDistance)<=DetailBandInnerRadius)
return DetailBandFade(SignedDistance);
const float MacroValue=FStandaloneResearchTunnels::ValueNoise(
static_cast<float>(X),static_cast<float>(Y),static_cast<float>(Z),32,
static_cast<uint32>(Owner->Seed)^0xB5297A4Du);
const FLayerField* Field=FindLayerField(Z);
return DetailBandFade(SignedDistance,LocalDetailBandEdge(MacroValue,SurfaceSDF,
CaveInfluence,FloorInfluence,Field));
}
void BuildLayerBounds()
{
@@ -2337,13 +2405,19 @@ private:
void ApplyDetailBand(FParallelTiming& Timing)
{
using Clock=std::chrono::steady_clock;
const bool bProfile=ProfileDetailBand.load(std::memory_order_acquire);
std::array<FDetailWorkerProfile,16> WorkerProfile{};
std::array<double,16> WorkerActiveSeconds{};
std::atomic<uint64> CandidateCount{0},ProxyCount{0},FlipCount{0};
ParallelLines(static_cast<size_t>(Size[0])*Size[1],[&](size_t Line)
ParallelLines(static_cast<size_t>(Size[0])*Size[1],[&](size_t Line,int32 Worker)
{
const int32 X=Min[0]+static_cast<int32>(Line%Size[0]);
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));
FDetailWorkerProfile& Profile=WorkerProfile[static_cast<size_t>(Worker)];
if(bProfile)++Profile.Lines;
uint64 LocalCandidates=0,LocalProxies=0,LocalFlips=0;
const size_t WordBase=Line*WordsPerZ;
for(size_t W=0;W<WordsPerZ;++W)
@@ -2356,14 +2430,49 @@ private:
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 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);
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 Displaced=SDF+DetailNoiseOnLine(X,Y,Z,SurfaceSDF,CaveInfluence,
FloorInfluence,NoiseLine)
*DetailBandFade(SDF);
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;
@@ -2371,6 +2480,7 @@ private:
{
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]);
@@ -2383,10 +2493,63 @@ private:
CandidateCount.fetch_add(LocalCandidates,std::memory_order_relaxed);
ProxyCount.fetch_add(LocalProxies,std::memory_order_relaxed);
FlipCount.fetch_add(LocalFlips,std::memory_order_relaxed);
},&Timing);
},&Timing,&WorkerActiveSeconds);
Stats.SurfaceCandidates=CandidateCount.load(std::memory_order_relaxed);
Stats.SurfaceProxyNodes=ProxyCount.load(std::memory_order_relaxed);
Stats.DetailFlips=FlipCount.load(std::memory_order_relaxed);
if(bProfile)
{
uint64 DistanceCalls=0,DetailCalls=0,DistanceSamples=0,DistanceSampleNs=0;
uint64 DetailSamples=0,DetailSampleNs=0,ReliefSampleNs=0,LocalBandSkips=0;
uint64 LayerLookupSampleNs=0,OwnerSampleNs=0,OwnerFieldCalls=0,OwnerActiveCalls=0;
uint64 OwnerCacheHits=0,OwnerCacheMisses=0;
for(int32 Worker=0;Worker<WorkerCount;++Worker)
{
const FDetailWorkerProfile& P=WorkerProfile[static_cast<size_t>(Worker)];
DistanceCalls+=P.Candidates;DetailCalls+=P.DetailEvaluations;
DistanceSamples+=P.DistanceSamples;DistanceSampleNs+=P.DistanceSampleNanoseconds;
DetailSamples+=P.DetailSamples;DetailSampleNs+=P.DetailSampleNanoseconds;
LocalBandSkips+=P.LocalBandSkips;
ReliefSampleNs+=P.ReliefSampleNanoseconds;
LayerLookupSampleNs+=P.LayerLookupSampleNanoseconds;
OwnerSampleNs+=P.OwnerRoughnessSampleNanoseconds;
OwnerFieldCalls+=P.OwnerRoughnessFieldCalls;
OwnerActiveCalls+=P.OwnerRoughnessActiveCalls;
OwnerCacheHits+=P.OwnerNoiseCacheHits;
OwnerCacheMisses+=P.OwnerNoiseCacheMisses;
}
const double DistanceAverageNs=DistanceSamples?static_cast<double>(DistanceSampleNs)/DistanceSamples:0.0;
const double DetailAverageNs=DetailSamples?static_cast<double>(DetailSampleNs)/DetailSamples:0.0;
const double CandidateWorkerNs=Stats.SurfaceCandidates
?Timing.WorkerSeconds*1.0e9/static_cast<double>(Stats.SurfaceCandidates):0.0;
std::printf("[DetailBandProfile] candidates=%llu distance_evaluations=%llu detail_noise_evaluations=%llu proxies=%llu flips=%llu candidate_worker_ns=%.1f distance_sample_ns=%.1f distance_estimated_worker_ms=%.1f detail_sample_ns=%.1f detail_estimated_worker_ms=%.1f worker_sum_ms=%.3f wall_ms=%.3f sampling=(distance_1_of_256,detail_1_of_64)\n",
static_cast<unsigned long long>(Stats.SurfaceCandidates),
static_cast<unsigned long long>(DistanceCalls),static_cast<unsigned long long>(DetailCalls),
static_cast<unsigned long long>(Stats.SurfaceProxyNodes),static_cast<unsigned long long>(Stats.DetailFlips),
CandidateWorkerNs,DistanceAverageNs,DistanceAverageNs*static_cast<double>(DistanceCalls)/1.0e6,
DetailAverageNs,DetailAverageNs*static_cast<double>(DetailCalls)/1.0e6,
Timing.WorkerSeconds*1000.0,Timing.WallSeconds*1000.0);
std::printf("[DetailBandSubprofile] relief_sample_ns=%.1f relief_estimated_worker_ms=%.1f layer_lookup_sample_ns=%.1f layer_lookup_estimated_worker_ms=%.1f owner_sample_ns=%.1f owner_estimated_worker_ms=%.1f owner_sampled_field_calls=%llu owner_sampled_noise_calls=%llu owner_sampled_core_cache_hits=%llu owner_sampled_core_cache_misses=%llu local_band_skipped=%llu\n",
DetailSamples?static_cast<double>(ReliefSampleNs)/DetailSamples:0.0,
DetailSamples?static_cast<double>(ReliefSampleNs)/DetailSamples*DetailCalls/1.0e6:0.0,
DetailSamples?static_cast<double>(LayerLookupSampleNs)/DetailSamples:0.0,
DetailSamples?static_cast<double>(LayerLookupSampleNs)/DetailSamples*DetailCalls/1.0e6:0.0,
DetailSamples?static_cast<double>(OwnerSampleNs)/DetailSamples:0.0,
DetailSamples?static_cast<double>(OwnerSampleNs)/DetailSamples*DetailCalls/1.0e6:0.0,
static_cast<unsigned long long>(OwnerFieldCalls),
static_cast<unsigned long long>(OwnerActiveCalls),
static_cast<unsigned long long>(OwnerCacheHits),
static_cast<unsigned long long>(OwnerCacheMisses),
static_cast<unsigned long long>(LocalBandSkips));
for(int32 Worker=0;Worker<WorkerCount;++Worker)
{
const FDetailWorkerProfile& P=WorkerProfile[static_cast<size_t>(Worker)];
std::printf("[DetailBandWorker] id=%d active_ms=%.3f lines=%llu candidates=%llu detail_evaluations=%llu flips=%llu\n",
Worker,WorkerActiveSeconds[static_cast<size_t>(Worker)]*1000.0,
static_cast<unsigned long long>(P.Lines),static_cast<unsigned long long>(P.Candidates),
static_cast<unsigned long long>(P.DetailEvaluations),static_cast<unsigned long long>(P.Flips));
}
}
}
void StoreSurfaceDistance(size_t Node,float Density)
@@ -2625,12 +2788,16 @@ private:
return SmoothMinDistance(Base,std::max(Cave,Overlay),StrataCaveBlendRadius);
}
float OwnerCaveRoughness(double X,double Y,double Z,float SurfaceSDF,
const FStrateGenerationParams& Params,
float CaveInfluence) const
struct FOwnerNoiseLineContext
{
const FLayerField* Field=nullptr;
int32 GridZ0=MIN_int32;
float LowerCore=0.0f,UpperCore=0.0f;
};
float OwnerCaveRoughnessCore(double X,double Y,double Z,
const FStrateGenerationParams& Params) const
{
const float Depth=Params.SurfaceRoughness*2.0f;
if(!(Depth>0.0f)||std::abs(SurfaceSDF)>=Depth||!(Params.RoughnessFrequency>0.0f))return 0.0f;
const uint32 Seed=static_cast<uint32>(Owner->Seed);
const float RF=Params.RoughnessFrequency;
const float EffectiveZ=(Params.VerticalScale>0.0f&&Params.VerticalScale!=1.0f)
@@ -2676,7 +2843,43 @@ private:
Rough=VoxelNoise::FBM(Main.X,Main.Y,Main.Z,3);
FineValue=VoxelNoise::FBM(Fine.X,Fine.Y,Fine.Z,2);break;
}
float Total=(Rough+FineValue*0.4f)*VOXEL_NOISE_SCALE*Params.SurfaceRoughness;
return (Rough+FineValue*0.4f)*VOXEL_NOISE_SCALE*Params.SurfaceRoughness;
}
float OwnerCaveRoughness(double X,double Y,double Z,float SurfaceSDF,
const FLayerField& Field,float CaveInfluence,
FOwnerNoiseLineContext& LineContext,
FDetailWorkerProfile* Profile=nullptr) const
{
const FStrateGenerationParams& Params=Field.Params;
const float Depth=Params.SurfaceRoughness*2.0f;
if(!(Depth>0.0f)||std::abs(SurfaceSDF)>=Depth||!(Params.RoughnessFrequency>0.0f))return 0.0f;
if(Profile)++Profile->OwnerRoughnessActiveCalls;
constexpr int32 ZStep=8;
const int32 GridZ0=static_cast<int32>(std::floor(Z/static_cast<double>(ZStep)))*ZStep;
const int32 SampleZ0=std::max(GridZ0,Field.Lo);
const int32 SampleZ1=std::min(GridZ0+ZStep,Field.Hi);
float Total=0.0f;
if(SampleZ1>SampleZ0)
{
const bool bCacheHit=LineContext.Field==&Field&&LineContext.GridZ0==GridZ0;
if(Profile)
{
if(bCacheHit)++Profile->OwnerNoiseCacheHits;
else ++Profile->OwnerNoiseCacheMisses;
}
if(!bCacheHit)
{
LineContext.Field=&Field;
LineContext.GridZ0=GridZ0;
LineContext.LowerCore=OwnerCaveRoughnessCore(X,Y,SampleZ0,Params);
LineContext.UpperCore=OwnerCaveRoughnessCore(X,Y,SampleZ1,Params);
}
const float Alpha=static_cast<float>((Z-SampleZ0)/(SampleZ1-SampleZ0));
Total=FMath::Lerp(LineContext.LowerCore,LineContext.UpperCore,Alpha);
}
else
Total=OwnerCaveRoughnessCore(X,Y,Z,Params);
if(SurfaceSDF<0.0f)
Total=std::lerp(Total,std::min(Total,0.0f),std::clamp(CaveInfluence,0.0f,1.0f));
const float Fade=1.0f-std::abs(SurfaceSDF)/Depth;
@@ -2750,6 +2953,7 @@ private:
struct FDetailNoiseLineContext
{
FValueNoiseLineContext Macro,Mid,Fine;
FOwnerNoiseLineContext OwnerNoise;
FDetailNoiseLineContext(float X,float Y,uint32 Seed)
:Macro(X,Y,32,Seed^0xB5297A4Du),Mid(X,Y,16,Seed^0x63D83595u),
Fine(X,Y,6,Seed^0xA511E9B3u)
@@ -2777,8 +2981,9 @@ private:
const float Relief=SurfaceRelief(static_cast<float>(X),
static_cast<float>(Y),static_cast<float>(Z),static_cast<uint32>(Owner->Seed));
const FLayerField* Field=FindLayerField(Z);
const float OwnerRelief=Field?OwnerCaveRoughness(X,Y,Z,SurfaceSDF,Field->Params,
CaveInfluence):0.0f;
FOwnerNoiseLineContext OwnerNoise;
const float OwnerRelief=Field?OwnerCaveRoughness(X,Y,Z,SurfaceSDF,*Field,
CaveInfluence,OwnerNoise):0.0f;
const float Contrast=1.35f-0.80f*std::clamp(CaveInfluence,0.0f,1.0f)
+1.85f*std::clamp(FloorInfluence,0.0f,1.0f);
return std::clamp((Relief+OwnerRelief*0.5f)*Contrast,-3.9f,3.9f);
@@ -2786,14 +2991,31 @@ private:
float DetailNoiseOnLine(double X,double Y,double Z,float SurfaceSDF,
float CaveInfluence,float FloorInfluence,
FDetailNoiseLineContext& NoiseLine) const
FDetailNoiseLineContext& NoiseLine,
FDetailWorkerProfile* Profile=nullptr,
const FLayerField* KnownField=nullptr,
bool bFieldKnown=false) 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 FLayerField* Field=FindLayerField(Z);
const float OwnerRelief=Field?OwnerCaveRoughness(X,Y,Z,SurfaceSDF,Field->Params,
CaveInfluence):0.0f;
if(Profile)
Profile->ReliefSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-ReliefStart).count());
const auto LookupStart=Profile?Clock::now():Clock::time_point{};
const FLayerField* Field=bFieldKnown?KnownField:FindLayerField(Z);
if(Profile)
Profile->LayerLookupSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-LookupStart).count());
const auto OwnerStart=Profile?Clock::now():Clock::time_point{};
if(Profile&&Field)++Profile->OwnerRoughnessFieldCalls;
const float OwnerRelief=Field?OwnerCaveRoughness(X,Y,Z,SurfaceSDF,*Field,
CaveInfluence,NoiseLine.OwnerNoise,Profile):0.0f;
if(Profile)
Profile->OwnerRoughnessSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-OwnerStart).count());
const float Contrast=1.35f-0.80f*std::clamp(CaveInfluence,0.0f,1.0f)
+1.85f*std::clamp(FloorInfluence,0.0f,1.0f);
return std::clamp((Relief+OwnerRelief*0.5f)*Contrast,-3.9f,3.9f);
@@ -2808,8 +3030,11 @@ private:
const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]&&Z>=Min[2]&&Z<=Max[2];
if(bInsideFine&&DetailVariant!=EDetailVariant::Smooth
&&std::abs(RockDistance)<=DetailSupremum+0.125f)
RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)
*DetailBandFade(RockDistance);
{
const float Fade=DetailBandFadeAt(X,Y,Z,RockDistance,SurfaceSDF,
CaveInfluence,FloorInfluence);
if(Fade>0.0f)RockDistance+=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,FloorInfluence)*Fade;
}
if(bCriticalAir)RockDistance=std::min(RockDistance,-1.0f/16.0f);
return RockDistance>=0.0f;
}
@@ -2866,13 +3091,21 @@ private:
return Count?PairAB/std::sqrt(std::max(1.0e-12,PairAA*PairBB)):0.0;
}
template<class Fn> void ParallelLines(size_t Count,Fn&& Work,FParallelTiming* Timing=nullptr) const
template<class Fn> void ParallelLines(size_t Count,Fn&& Work,FParallelTiming* Timing=nullptr,
std::array<double,16>* WorkerActiveSeconds=nullptr) const
{
const auto WallStart=std::chrono::steady_clock::now();
if(WorkerCount<=1||Count<512)
{
const auto WorkerStart=std::chrono::steady_clock::now();
for(size_t I=0;I<Count;++I)Work(I);
for(size_t I=0;I<Count;++I)
{
if constexpr(std::is_invocable_v<Fn&,size_t,int32>)Work(I,0);
else Work(I);
}
if(WorkerActiveSeconds)
(*WorkerActiveSeconds)[0]=std::chrono::duration<double>(
std::chrono::steady_clock::now()-WorkerStart).count();
if(Timing)
{
const double WorkerSeconds=std::chrono::duration<double>(std::chrono::steady_clock::now()-WorkerStart).count();
@@ -2884,12 +3117,19 @@ private:
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)
Threads.emplace_back([&]()
Threads.emplace_back([&,W]()
{
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;Work(I);}
WorkerNanoseconds.fetch_add(static_cast<uint64>(std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now()-WorkerStart).count()),std::memory_order_relaxed);
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 uint64 WorkerNs=static_cast<uint64>(std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now()-WorkerStart).count());
if(WorkerActiveSeconds)(*WorkerActiveSeconds)[static_cast<size_t>(W)]=static_cast<double>(WorkerNs)*1.0e-9;
WorkerNanoseconds.fetch_add(WorkerNs,std::memory_order_relaxed);
});
for(std::thread& T:Threads)T.join();
if(Timing)
@@ -2920,6 +3160,7 @@ private:
std::unordered_map<FBinKey,FBinItems,FBinHash> CriticalAirBins;
std::vector<uint64> RockWords;
std::unique_ptr<int8[]> SurfaceDistanceQ;
inline static std::atomic<bool> ProfileDetailBand{false};
std::array<std::vector<uint64>,3> CandidateAxisWords;
std::vector<uint64> CandidateWords;
FBuildStats Stats;