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:
1 parent
a3cb26b5c8
commit
9ffe296cd1
10 files changed
+399
-36
No files matched your search
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user