profile: per-term legacy FI/TN sample profiler (L76); passage/support is ~75% of field time

Opt-in at build time (VOXELFORGE_PROFILE_LEGACY_TERMS=1; compiled out by default, ~2% when on).
Per legacy sample: passage/support composer ~1.05 us vs primitive scan ~0.19 us, boundary
band ~0.06 us, noise ~0.01 us. Resolved-passage constants are cached at field binding
(arithmetic order unchanged); whole-view effect within noise. Per-tile hashes identical to L75
in all 24 compiler x worker x view comparisons; fast tier PASS; Linux UE host builds.
R40 researcher notes attached.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-27 16:25:19 +01:00
1 parent 9d5455ec68
commit 90378a585f
13 files changed
+1070 -86

No files matched your search

+424 -86
View File
@@ -345,7 +345,8 @@ public:
BoundStratumFields.clear();
BoundManagerLayoutChunkWidthVoxels=Owner&&Owner->Manager
?Owner->Manager->GetStrateLayoutChunkWidthVoxels():0.0f;
if(!ResolvedLayout||ResolvedLayout->strateLayoutChunkSizeMicrometres<=0)return;
if(!ResolvedLayout||ResolvedLayout->strateLayoutChunkSizeMicrometres<=0)
{BuildPassageComposerConstants();return;}
const int64 ChunkWidthMicrometres=ResolvedLayout->strateLayoutChunkSizeMicrometres;
const auto FloorDiv64=[](int64 Value,int64 Divisor)
{
@@ -430,6 +431,7 @@ public:
std::sort(BoundStratumFields.begin(),BoundStratumFields.end(),
[](const auto& A,const auto& B){return A.BottomChunkZ<B.BottomChunkZ;});
BuildOreVeinSpatialIndex();
BuildPassageComposerConstants();
}
const FBoundStratumField* FindBoundStratumForChunk(int32 ChunkZ) const
@@ -456,6 +458,14 @@ public:
return Bound&&Bound->Kind==FBoundStratumField::EKind::FloatingIslands;
}
bool IsLegacyFloatingIslandFieldAtZ(float Z) const
{
const FBoundStratumField* Bound=FindBoundStratumForZ(Z);
return Bound&&Bound->Resolved
&&Bound->Resolved->generatorKind=="FloatingIslands"
&&Bound->IslandPlan==nullptr;
}
const FBoundStratumField* FindBoundStratumForManagerZ(float Z) const
{
if(!std::isfinite(Z)||!Owner||!(BoundManagerLayoutChunkWidthVoxels>0.0f))
@@ -992,7 +1002,7 @@ public:
?passageTileQueries->profileStats():nullptr;
if(passageProfile)++passageProfile->passageComposerQueries;
bool queryNearUpperMouthSupport=false;
for(const auto& passage:ResolvedLayout->passages)
const auto checkUpperMouth=[&](const auto& passage)
{
if(passageProfile)++passageProfile->passageUpperMouthFilterChecks;
const double dx=qx-static_cast<double>(passage.upperMouthSubvoxels[0]);
@@ -1000,7 +1010,12 @@ public:
const double dz=qz-static_cast<double>(passage.upperMouthSubvoxels[2]);
const double reach=15.0*qPerMetre;
if(dx*dx+dy*dy<=reach*reach&&std::abs(dz)<=8.0*qPerMetre)
{queryNearUpperMouthSupport=true;break;}
queryNearUpperMouthSupport=true;
};
for(const auto& passage:ResolvedLayout->passages)
{
checkUpperMouth(passage);
if(queryNearUpperMouthSupport)break;
}
if(passageTileScopeMatches&&density>=0.0f
&&!passageTileQueries->hasRouteFeatureOverlap()
@@ -1088,40 +1103,19 @@ public:
static_cast<float>(mouthBlendWidth/Q));
};
// The connected passage tread continues the surface route into the tunnel.
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();++passageIndex)
const std::size_t passageIterationCount=ResolvedLayout->passages.size();
for(std::size_t passageIteration=0;passageIteration<passageIterationCount;
++passageIteration)
{
const std::size_t passageIndex=passageIteration;
if(passageProfile)++passageProfile->passageIterations;
const auto& passage=ResolvedLayout->passages[passageIndex];
if(passage.controlPoints.size()<2)continue;
const auto supportStratum=[&](std::int32_t strateIndex)
->const VoxelForgeCore::ResolvedLayoutStratumV2*
{
const auto it=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[strateIndex](const auto& stratum)
{return stratum.strateIndex==strateIndex;});
return it==ResolvedLayout->strates.end()?nullptr:&*it;
};
const auto* supportUpper=supportStratum(passage.upperStrateIndex);
const auto* supportLower=supportStratum(passage.lowerStrateIndex);
const auto mouthInsideContent=[&](
const VoxelForgeCore::ResolvedLayoutStratumV2* stratum,
const std::array<std::int64_t,3>& mouth)
{
if(!stratum||!stratum->content.presentInCurrentField)return false;
const auto zMicrometres=static_cast<std::int64_t>(std::llround(
static_cast<double>(mouth[2])
*ResolvedLayout->coreVoxelSizeMicrometres/Q));
return zMicrometres>=stratum->content.bottomMicrometres
&&zMicrometres<stratum->content.topMicrometres;
};
const auto& passageConstants=PassageComposerConstants[passageIndex];
// A passage is internal when both mouths are bounded by their strate
// content and neither endpoint is a SurfaceWorld landing. Route order
// and the total number of strata are irrelevant.
const bool internalChainPassage=supportUpper&&supportLower
&&!supportUpper->passageCapabilities.hasSurfaceLanding
&&!supportLower->passageCapabilities.hasSurfaceLanding
&&mouthInsideContent(supportUpper,passage.upperMouthSubvoxels)
&&mouthInsideContent(supportLower,passage.lowerMouthSubvoxels);
const bool internalChainPassage=passageConstants.internalChainPassage;
const auto laterPassageMayCarve=[&](std::ptrdiff_t supportSegment,
double sampleX,double sampleY,double sampleZ)
{
@@ -1129,14 +1123,10 @@ public:
return PassageSupportMayBeCarvedByLaterBore(passageIndex,
supportSegment,sampleX,sampleY,sampleZ,0.35*qPerMetre);
};
const bool floatingIslandPassage=supportUpper
&&supportUpper->passageCapabilities.hasFloatingIslandLanding;
const bool surfaceToTunnelSupport=supportUpper&&supportLower
&&supportUpper->lowerInterfaceCapabilities.surfaceToTunnelLandingSupport;
const double firstFloorQ=passage.controlPoints.front().positionSubvoxels[2]
-passage.controlPoints.front().radiusSubvoxels+playerCenterAboveFloorQ;
const double lastFloorQ=passage.controlPoints.back().positionSubvoxels[2]
-passage.controlPoints.back().radiusSubvoxels+playerCenterAboveFloorQ;
const bool floatingIslandPassage=passageConstants.floatingIslandPassage;
const bool surfaceToTunnelSupport=passageConstants.surfaceToTunnelSupport;
const double firstFloorQ=passageConstants.firstFloorQ;
const double lastFloorQ=passageConstants.lastFloorQ;
for(const auto& crossing:PassageVoidCrossings)
{
if(!crossing.needsColumn||crossing.passageIndex!=passageIndex
@@ -1202,15 +1192,13 @@ public:
}
const auto floorProfile=[&](const auto& point)
{return point.positionSubvoxels[2]-point.radiusSubvoxels+playerCenterAboveFloorQ;};
const std::ptrdiff_t lastIndex=static_cast<std::ptrdiff_t>(passage.controlPoints.size())-1;
const std::ptrdiff_t lowerMouthIndex=std::clamp<std::ptrdiff_t>(
static_cast<std::ptrdiff_t>(passage.lowerMouthControlIndex),1,lastIndex);
const std::ptrdiff_t lastIndex=passageConstants.lastIndex;
const std::ptrdiff_t lowerMouthIndex=passageConstants.lowerMouthIndex;
const auto mainEnd=passage.controlPoints.begin()+lowerMouthIndex+1;
// The tube radius is measured from its centerline, while floorProfile is
// one player-center offset away. Search the full vertical radius of the SDF;
// a narrow point window misses earlier turns of a low-pitch helix.
const double carveSearchMargin=std::max(passage.mouthRadiusSubvoxels,
passage.midRadiusSubvoxels)+0.25*qPerMetre;
const double carveSearchMargin=passageConstants.carveSearchMargin;
const auto carveHigh=std::lower_bound(passage.controlPoints.begin(),mainEnd,
qz+carveSearchMargin,
[&](const auto& point,double value){return floorProfile(point)>value;});
@@ -1221,9 +1209,8 @@ public:
(carveHigh-passage.controlPoints.begin())-1);
const std::ptrdiff_t carveEnd=std::min<std::ptrdiff_t>(lowerMouthIndex-1,
(carveLow-passage.controlPoints.begin())+1);
const std::ptrdiff_t mouthSearchBegin=std::max<std::ptrdiff_t>(0,lowerMouthIndex-8);
const std::ptrdiff_t mouthSearchEnd=std::min<std::ptrdiff_t>(lastIndex,
lowerMouthIndex+8);
const std::ptrdiff_t mouthSearchBegin=passageConstants.mouthSearchBegin;
const std::ptrdiff_t mouthSearchEnd=passageConstants.mouthSearchEnd;
if(density<0.0f&&tilePassageFree)
{
if(passageProfile)++passageProfile->passageNegativeBoundChecks;
@@ -1984,8 +1971,11 @@ public:
}
// Restore the surface route where the passage cut crossed otherwise solid
// native ground. The support stays native-solid-only, so it cannot float.
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();++passageIndex)
const std::size_t mouthSupportIterationCount=ResolvedLayout->passages.size();
for(std::size_t passageIteration=0;
passageIteration<mouthSupportIterationCount;++passageIteration)
{
const std::size_t passageIndex=passageIteration;
const auto& passage=ResolvedLayout->passages[passageIndex];
if(passage.controlPoints.size()<2)continue;
const bool tileHasSupport=passageTileScopeMatches;
@@ -2002,7 +1992,11 @@ public:
float ApplyResolvedStrateComposition(float density,float X,float Y,float Z) const
{
const float graphDensity=ApplyResolvedCavernGraph(density,X,Y,Z);
float graphDensity=0.0f;
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::ComposerCavern);
graphDensity=ApplyResolvedCavernGraph(density,X,Y,Z);
}
const auto* bound=FindBoundStratumForManagerZ(Z);
const bool nativeLattice=bound&&bound->Resolved
&&VoxelForgeCore::IsNativeLatticeFieldStratum(*bound->Resolved)
@@ -2012,14 +2006,28 @@ public:
// the generic stratum transition distance creates a discontinuity at a
// mouth that crosses a wide transition band. Keep that opt-in field whole;
// its host-rock value already supplies the ceiling and bedrock.
const float boundary=nativeLattice?graphDensity
:ApplyResolvedStrateBoundaryBand(graphDensity,X,Y,Z);
float boundary=graphDensity;
if(!nativeLattice)
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::ComposerBoundary);
boundary=ApplyResolvedStrateBoundaryBand(graphDensity,X,Y,Z);
}
// The native lattice owns its resolved arrival/departure portals and their
// supported spine. Reapplying legacy passage carves here can erase the
// tread immediately above a broad legacy mouth or support column.
const float composed=nativeLattice?boundary
:ApplyResolvedStratePassageCarves(boundary,X,Y,Z);
const float stamped=StampRuntime.empty()?composed:ApplyResolvedSdfStamps(composed,X,Y,Z);
float composed=boundary;
if(!nativeLattice)
{
FScopedLegacyFieldTermTimer TermTimer(
ELegacyFieldTerm::ComposerPassageAndSupport);
composed=ApplyResolvedStratePassageCarves(boundary,X,Y,Z);
}
float stamped=composed;
if(!StampRuntime.empty())
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::ComposerStamps);
stamped=ApplyResolvedSdfStamps(composed,X,Y,Z);
}
static const bool bL75TraceTnComposition=[]
{
const char* Value=std::getenv("VOXELFORGE_L75_TN_BLOCK_REGRESSION");
@@ -2040,7 +2048,12 @@ public:
}
// The legacy landmark demo retains its compatibility floor cap. Native caves
// already combine each floor with its wall before the single void carve above.
const float final=ApplyResolvedCavernFloorCaps(stamped,X,Y,Z);
float final=0.0f;
{
FScopedLegacyFieldTermTimer TermTimer(
ELegacyFieldTerm::ComposerPassageAndSupport);
final=ApplyResolvedCavernFloorCaps(stamped,X,Y,Z);
}
if(bTraceTnComposition)
std::printf("[L75TNCompositionTrace] input=%.9g graph=%.9g boundary=%.9g passage=%.9g stamp=%.9g final=%.9g native=0\n",
density,graphDensity,boundary,composed,stamped,final);
@@ -2212,6 +2225,14 @@ public:
DensityUnclampedPointCalls=0,DensityX8PointCalls=0;
uint64 DensityFieldEvalNanoseconds=0,DensityFieldEvalSamplePoints=0;
std::array<uint64,4> DensityFieldEvalNanosecondsByKind{},DensityFieldEvalSamplePointsByKind{};
static constexpr std::size_t LegacyFieldTermCount=9;
uint64 LegacyFieldEvalCalls=0;
std::array<uint64,LegacyFieldTermCount> LegacyFieldTermCalls{};
std::array<uint64,LegacyFieldTermCount> LegacyFieldTermNanoseconds{};
std::array<uint64,LegacyFieldTermCount> LegacyFieldTermTimedCalls{};
uint64 LegacyPrimitiveCandidateVisits=0,LegacyPrimitiveDistanceEvaluations=0;
uint64 LegacySmoothMinCalls=0,LegacySmoothMinNanoseconds=0,
LegacySmoothMinTimedCalls=0;
double Milliseconds=0.0;
#if defined(VF_L30_ENABLE_COST_COUNTERS)
uint64 BoundaryBandCalls=0,TransitionWindowChecks=0,TransitionWindowHits=0;
@@ -2232,6 +2253,105 @@ public:
{static thread_local uint32 Depth=0;return Depth;}
static uint64& DensityQuerySampleSequence()
{static thread_local uint64 Sequence=0;return Sequence;}
static bool& DensityQueryTermTimingActive()
{static thread_local bool Active=false;return Active;}
static bool& LegacyFieldTermScopeActive()
{static thread_local bool Active=false;return Active;}
enum class ELegacyFieldTerm : std::size_t
{
PrimitiveScan,
Detail,
SurfaceReliefNoise,
OwnerNoiseAndWarp,
ComposerCavern,
ComposerBoundary,
ComposerPassageAndSupport,
ComposerStamps,
EditBrushes,
Count
};
struct FScopedLegacyFieldTermTimer
{
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
explicit FScopedLegacyFieldTermTimer(ELegacyFieldTerm InTerm,
uint64 InMultiplicity=1)
:Term(static_cast<std::size_t>(InTerm)),Multiplicity(InMultiplicity)
{
if(!LegacyFieldTermScopeActive())return;
FClassificationThreadStats& Stats=ThreadClassificationStats();
Stats.LegacyFieldTermCalls[Term]+=Multiplicity;
if(!DensityQueryTermTimingActive())return;
Start=std::chrono::steady_clock::now();
bTimed=true;
}
~FScopedLegacyFieldTermTimer()
{
if(!bTimed)return;
const uint64 Nanoseconds=static_cast<uint64>(std::max<std::int64_t>(0,
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now()-Start).count()));
FClassificationThreadStats& Stats=ThreadClassificationStats();
Stats.LegacyFieldTermNanoseconds[Term]+=Nanoseconds;
Stats.LegacyFieldTermTimedCalls[Term]+=Multiplicity;
}
std::size_t Term=0;
uint64 Multiplicity=1;
std::chrono::steady_clock::time_point Start{};
bool bTimed=false;
#else
explicit FScopedLegacyFieldTermTimer(ELegacyFieldTerm,uint64=1) noexcept {}
#endif
};
struct FScopedLegacyFieldTermScope
{
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
explicit FScopedLegacyFieldTermScope(bool bEligible,uint64 InMultiplicity=1)
:Previous(LegacyFieldTermScopeActive()),Multiplicity(InMultiplicity)
{
const bool bEnabled=ThreadClassificationStats().CaptureDensityQueryProfile
&&bEligible;
LegacyFieldTermScopeActive()=bEnabled;
if(bEnabled)ThreadClassificationStats().LegacyFieldEvalCalls+=Multiplicity;
}
~FScopedLegacyFieldTermScope()
{LegacyFieldTermScopeActive()=Previous;}
bool Previous=false;
uint64 Multiplicity=1;
#else
explicit FScopedLegacyFieldTermScope(bool,uint64=1) noexcept {}
#endif
};
struct FScopedLegacySmoothMinTimer
{
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
FScopedLegacySmoothMinTimer()
{
if(!LegacyFieldTermScopeActive())return;
++ThreadClassificationStats().LegacySmoothMinCalls;
if(!DensityQueryTermTimingActive())return;
Start=std::chrono::steady_clock::now();
bTimed=true;
}
~FScopedLegacySmoothMinTimer()
{
if(!bTimed)return;
auto& Stats=ThreadClassificationStats();
Stats.LegacySmoothMinNanoseconds+=static_cast<uint64>(std::max<std::int64_t>(0,
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now()-Start).count()));
++Stats.LegacySmoothMinTimedCalls;
}
std::chrono::steady_clock::time_point Start{};
bool bTimed=false;
#else
FScopedLegacySmoothMinTimer() noexcept {}
#endif
};
struct FScopedDensityQueryProfile
{
FScopedDensityQueryProfile(EDensityQueryKind Kind,uint64 PointCount=1)
@@ -2240,6 +2360,10 @@ public:
{
if(!stats->CaptureDensityQueryProfile||DensityQuerySuppressionDepth()!=0)
{stats=nullptr;return;}
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
PreviousTermTiming=DensityQueryTermTimingActive();
DensityQueryTermTimingActive()=false;
#endif
stats->DensityPointCalls+=pointCount;
switch(Kind)
{
@@ -2248,20 +2372,30 @@ public:
case EDensityQueryKind::Unclamped:stats->DensityUnclampedPointCalls+=pointCount;break;
case EDensityQueryKind::X8:stats->DensityX8PointCalls+=pointCount;break;
}
const uint64 Sequence=++DensityQuerySampleSequence();
if((Sequence&255u)!=0)return;
const uint64 Sequence=DensityQuerySampleSequence();
DensityQuerySampleSequence()+=pointCount;
if(((Sequence+pointCount)>>8)==(Sequence>>8))return;
sampled=true;start=std::chrono::steady_clock::now();
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
DensityQueryTermTimingActive()=true;
#endif
}
~FScopedDensityQueryProfile()
{
if(!sampled)return;
const uint64 nanoseconds=static_cast<uint64>(std::max<std::int64_t>(0,
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now()-start).count()));
stats->DensityFieldEvalNanoseconds+=nanoseconds;
stats->DensityFieldEvalSamplePoints+=pointCount;
stats->DensityFieldEvalNanosecondsByKind[kindIndex]+=nanoseconds;
stats->DensityFieldEvalSamplePointsByKind[kindIndex]+=pointCount;
if(stats==nullptr)return;
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
DensityQueryTermTimingActive()=PreviousTermTiming;
#endif
if(sampled)
{
const uint64 nanoseconds=static_cast<uint64>(std::max<std::int64_t>(0,
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now()-start).count()));
stats->DensityFieldEvalNanoseconds+=nanoseconds;
stats->DensityFieldEvalSamplePoints+=pointCount;
stats->DensityFieldEvalNanosecondsByKind[kindIndex]+=nanoseconds;
stats->DensityFieldEvalSamplePointsByKind[kindIndex]+=pointCount;
}
}
bool IsActive()const noexcept{return stats!=nullptr;}
FClassificationThreadStats* stats=nullptr;
@@ -2269,6 +2403,9 @@ public:
uint64 pointCount=0;
std::chrono::steady_clock::time_point start{};
bool sampled=false;
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
bool PreviousTermTiming=false;
#endif
};
struct FScopedDensityQueryBatch
{
@@ -3577,6 +3714,15 @@ private:
std::size_t passageIndex=0,segmentIndex=0;
bool bColumn=false,bMouthSupport=false;
};
struct FPassageComposerConstants
{
bool internalChainPassage=false;
bool floatingIslandPassage=false;
bool surfaceToTunnelSupport=false;
double firstFloorQ=0.0,lastFloorQ=0.0,carveSearchMargin=0.0;
std::ptrdiff_t lowerMouthIndex=1,lastIndex=1;
std::ptrdiff_t mouthSearchBegin=0,mouthSearchEnd=0;
};
struct FPassageMouthSupportSegment
{
std::array<std::int64_t,4> a{},b{};
@@ -4549,6 +4695,68 @@ private:
return result;
}
void BuildPassageComposerConstants()
{
PassageComposerConstants.clear();
if(!ResolvedLayout)return;
PassageComposerConstants.resize(ResolvedLayout->passages.size());
constexpr double Q=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
const double qPerMetre=Q*1000000.0/
std::max<std::uint32_t>(1,ResolvedLayout->coreVoxelSizeMicrometres);
const double playerCenterAboveFloorQ=1.13*qPerMetre;
for(std::size_t passageIndex=0;passageIndex<ResolvedLayout->passages.size();
++passageIndex)
{
const auto& passage=ResolvedLayout->passages[passageIndex];
if(passage.controlPoints.size()<2)continue;
const auto supportStratum=[&](std::int32_t strateIndex)
->const VoxelForgeCore::ResolvedLayoutStratumV2*
{
const auto it=std::find_if(ResolvedLayout->strates.begin(),
ResolvedLayout->strates.end(),[strateIndex](const auto& stratum)
{return stratum.strateIndex==strateIndex;});
return it==ResolvedLayout->strates.end()?nullptr:&*it;
};
const auto* supportUpper=supportStratum(passage.upperStrateIndex);
const auto* supportLower=supportStratum(passage.lowerStrateIndex);
const auto mouthInsideContent=[&](
const VoxelForgeCore::ResolvedLayoutStratumV2* stratum,
const std::array<std::int64_t,3>& mouth)
{
if(!stratum||!stratum->content.presentInCurrentField)return false;
const auto zMicrometres=static_cast<std::int64_t>(std::llround(
static_cast<double>(mouth[2])
*ResolvedLayout->coreVoxelSizeMicrometres/Q));
return zMicrometres>=stratum->content.bottomMicrometres
&&zMicrometres<stratum->content.topMicrometres;
};
auto& constants=PassageComposerConstants[passageIndex];
constants.internalChainPassage=supportUpper&&supportLower
&&!supportUpper->passageCapabilities.hasSurfaceLanding
&&!supportLower->passageCapabilities.hasSurfaceLanding
&&mouthInsideContent(supportUpper,passage.upperMouthSubvoxels)
&&mouthInsideContent(supportLower,passage.lowerMouthSubvoxels);
constants.floatingIslandPassage=supportUpper
&&supportUpper->passageCapabilities.hasFloatingIslandLanding;
constants.surfaceToTunnelSupport=supportUpper&&supportLower
&&supportUpper->lowerInterfaceCapabilities.surfaceToTunnelLandingSupport;
constants.firstFloorQ=passage.controlPoints.front().positionSubvoxels[2]
-passage.controlPoints.front().radiusSubvoxels+playerCenterAboveFloorQ;
constants.lastFloorQ=passage.controlPoints.back().positionSubvoxels[2]
-passage.controlPoints.back().radiusSubvoxels+playerCenterAboveFloorQ;
constants.lastIndex=static_cast<std::ptrdiff_t>(passage.controlPoints.size())-1;
constants.lowerMouthIndex=std::clamp<std::ptrdiff_t>(
static_cast<std::ptrdiff_t>(passage.lowerMouthControlIndex),1,
constants.lastIndex);
constants.mouthSearchBegin=std::max<std::ptrdiff_t>(0,
constants.lowerMouthIndex-8);
constants.mouthSearchEnd=std::min<std::ptrdiff_t>(constants.lastIndex,
constants.lowerMouthIndex+8);
constants.carveSearchMargin=std::max(passage.mouthRadiusSubvoxels,
passage.midRadiusSubvoxels)+0.25*qPerMetre;
}
}
void BuildPassageSpatialIndices()
{
BuildStampSpatialIndex();
@@ -5756,12 +5964,24 @@ private:
?ApplyResolvedStrateComposition(Density,X,Y,Z):Density;
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
}
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
const FBoundStratumField* LegacyTermBound=FindBoundStratumForZ(Z);
const bool bLegacyFiOrTnField=LegacyTermBound&&LegacyTermBound->Resolved
&&(LegacyTermBound->Resolved->generatorKind=="TunnelNetwork"
||(LegacyTermBound->Resolved->generatorKind=="FloatingIslands"
&&LegacyTermBound->IslandPlan==nullptr));
FScopedLegacyFieldTermScope LegacyFieldTermScope(bLegacyFiOrTnField);
#endif
const bool bInsideFine=X>=Min[0]&&X<=Max[0]&&Y>=Min[1]&&Y<=Max[1]
&&Z>=Min[2]&&Z<=Max[2];
if(!bInsideFine)
{
bool bCriticalAir=false;
float RockDistance=CoarseRockDistance(X,Y,Z,&bCriticalAir);
float RockDistance=0.0f;
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::PrimitiveScan);
RockDistance=CoarseRockDistance(X,Y,Z,&bCriticalAir);
}
if(bCriticalAir)RockDistance=std::min(RockDistance,-1.0f/16.0f);
const bool bRock=RockDistance>=0.0f;
float Density=-RockDistance;
@@ -5770,14 +5990,23 @@ private:
bClampNearZero?0.05f:1.0e-7f);
const float composed=bApplyComposition
?ApplyResolvedStrateComposition(Density,X,Y,Z):Density;
return bApplyEdits?ApplyEditBrushes(composed,X,Y,Z):composed;
if(bApplyEdits)
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::EditBrushes);
return ApplyEditBrushes(composed,X,Y,Z);
}
return composed;
}
ESurfaceFamily SurfaceFamily=ESurfaceFamily::Strata;float SurfaceSDF=100000.0f;
bool bFloorSurface=false,bCriticalAir=false;float CaveInfluence=0.0f,FloorInfluence=0.0f;
float PassageInfluence=0.0f;
float RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence,&bCriticalAir,nullptr,false,
&PassageInfluence);
float RockDistance=0.0f;
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::PrimitiveScan);
RockDistance=EffectiveRockDistance(X,Y,Z,&SurfaceFamily,&SurfaceSDF,
&bFloorSurface,&CaveInfluence,&FloorInfluence,&bCriticalAir,nullptr,false,
&PassageInfluence);
}
if(OutCaveInfluence)*OutCaveInfluence=CaveInfluence;
if(OutFloorInfluence)*OutFloorInfluence=FloorInfluence;
if(bInsideFine&&DetailVariant!=EDetailVariant::Smooth
@@ -5787,8 +6016,13 @@ private:
CaveInfluence,FloorInfluence);
if(Fade>0.0f)
{
const float Detail=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,
FloorInfluence,PassageInfluence)*Fade;
float DetailNoiseValue=0.0f;
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::Detail);
DetailNoiseValue=DetailNoise(X,Y,Z,SurfaceSDF,CaveInfluence,
FloorInfluence,PassageInfluence);
}
const float Detail=DetailNoiseValue*Fade;
RockDistance+=Detail;
if(OutDetailDisplacement)*OutDetailDisplacement=Detail;
}
@@ -5800,7 +6034,12 @@ private:
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),
bClampNearZero?0.05f:1.0e-7f);
if(bApplyComposition)Density=ApplyResolvedStrateComposition(Density,X,Y,Z);
return bApplyEdits?ApplyEditBrushes(Density,X,Y,Z):Density;
if(bApplyEdits)
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::EditBrushes);
return ApplyEditBrushes(Density,X,Y,Z);
}
return Density;
}
public:
@@ -7649,7 +7888,11 @@ private:
{return N>=0?(N+D/2)/D:-((-N+D/2)/D);}
static float SmoothMinDistance(float A,float B,float Width)
{
if(Width<=0.0f)return std::min(A,B);
FScopedLegacySmoothMinTimer SmoothMinTimer;
if(Width<=0.0f)
{
return std::min(A,B);
}
const float H=std::max(Width-std::abs(A-B),0.0f)/Width;
return std::min(A,B)-(H*H*H)*(Width/6.0f);
}
@@ -10911,6 +11154,10 @@ private:
{
for(uint32 Id:*BinItems)
{
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
if(LegacyFieldTermScopeActive())
++ThreadClassificationStats().LegacyPrimitiveCandidateVisits;
#endif
const FPrimitive& P=Primitives[Id];
if(P.bCarve)
{
@@ -10921,6 +11168,10 @@ private:
if(X<P.Min[0]-DetailBand||X>P.Max[0]+DetailBand
||Y<P.Min[1]-DetailBand||Y>P.Max[1]+DetailBand
||Z<P.Min[2]-DetailBand||Z>P.Max[2]+DetailBand)continue;
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
if(LegacyFieldTermScopeActive())
++ThreadClassificationStats().LegacyPrimitiveDistanceEvaluations;
#endif
float Distance=ShapeDistance(P,X,Y,Z);
float PrimitiveFloorInfluence=0.0f;
if(P.bHasFloorClip)
@@ -10944,7 +11195,13 @@ private:
}
else if(X>=P.Min[0]&&X<=P.Max[0]&&Y>=P.Min[1]&&Y<=P.Max[1]
&&Z>=P.Min[2]&&Z<=P.Max[2])
{
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
if(LegacyFieldTermScopeActive())
++ThreadClassificationStats().LegacyPrimitiveDistanceEvaluations;
#endif
Overlay=std::max(Overlay,-ShapeDistance(P,X,Y,Z));
}
}
}
if(OutCriticalAir)*OutCriticalAir=bCriticalAir;
@@ -11093,6 +11350,11 @@ private:
{
for(uint32 Id:*BinItems)
{
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
if(LegacyFieldTermScopeActive())
ThreadClassificationStats().LegacyPrimitiveCandidateVisits+=
static_cast<uint64>(Count);
#endif
const FPrimitive& P=Primitives[Id];
bool Eligible[8]{};
bool AnyEligible=false;
@@ -11127,6 +11389,11 @@ private:
}
if(!AnyEligible)continue;
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
if(LegacyFieldTermScopeActive())
ThreadClassificationStats().LegacyPrimitiveDistanceEvaluations+=
static_cast<uint64>(Count);
#endif
float Distances[8];
ShapeDistanceZ8(P,X,Y,Z,Count,Distances);
if(P.bCarve)
@@ -11201,6 +11468,9 @@ public:
return;
}
if(Count<=0||OutDensity==nullptr)return;
FScopedDensityQueryProfile queryProfile(EDensityQueryKind::Lattice,
static_cast<uint64>(Count));
FScopedDensityQueryBatch queryBatch(queryProfile.IsActive());
if(bNativeOnly)
{
for(int32 Lane=0;Lane<Count;++Lane)
@@ -11260,7 +11530,27 @@ public:
&&FloorDiv(Z[BatchCount],ActiveSpatialBinSize)==BinZ)++BatchCount;
FEffectiveRockSample Samples[8]{};
EffectiveRockDistanceZ8(X,Y,Z,BatchCount,BinItems,Samples);
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
bool bLegacyTermBatch=true;
for(int32 Lane=0;Lane<BatchCount;++Lane)
{
const FBoundStratumField* Bound=FindBoundStratumForZ(
static_cast<float>(Z[Lane]));
bLegacyTermBatch=bLegacyTermBatch&&Bound&&Bound->Resolved
&&(Bound->Resolved->generatorKind=="TunnelNetwork"
||(Bound->Resolved->generatorKind=="FloatingIslands"
&&Bound->IslandPlan==nullptr));
}
#else
const bool bLegacyTermBatch=false;
#endif
FScopedLegacyFieldTermScope LegacyTermScope(bLegacyTermBatch,
static_cast<uint64>(BatchCount));
{
FScopedLegacyFieldTermTimer TermTimer(
ELegacyFieldTerm::PrimitiveScan,static_cast<uint64>(BatchCount));
EffectiveRockDistanceZ8(X,Y,Z,BatchCount,BinItems,Samples);
}
for(int32 Lane=0;Lane<BatchCount;++Lane)
{
const int32 SampleZ=Z[Lane];
@@ -11274,11 +11564,15 @@ public:
static_cast<float>(Y),static_cast<float>(SampleZ),RockDistance,
Samples[Lane].SurfaceSDF,Samples[Lane].CaveInfluence,
Samples[Lane].FloorInfluence);
if(Fade>0.0f)RockDistance+=DetailNoiseOnLine(static_cast<float>(X),
static_cast<float>(Y),static_cast<float>(SampleZ),
Samples[Lane].SurfaceSDF,Samples[Lane].CaveInfluence,
Samples[Lane].FloorInfluence,Samples[Lane].PassageInfluence,
DetailNoiseLine)*Fade;
if(Fade>0.0f)
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::Detail);
RockDistance+=DetailNoiseOnLine(static_cast<float>(X),
static_cast<float>(Y),static_cast<float>(SampleZ),
Samples[Lane].SurfaceSDF,Samples[Lane].CaveInfluence,
Samples[Lane].FloorInfluence,Samples[Lane].PassageInfluence,
DetailNoiseLine)*Fade;
}
}
if(Samples[Lane].bCriticalAir)
RockDistance=std::min(RockDistance,-1.0f/16.0f);
@@ -11286,7 +11580,10 @@ public:
float Density=-RockDistance;
if((Density<0.0f)!=bRock||std::abs(Density)<0.05f)
Density=(bRock?-1.0f:1.0f)*std::max(std::abs(Density),0.05f);
OutDensity[Offset+Lane]=ApplyEditBrushes(Density,X,Y,SampleZ);
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::EditBrushes);
OutDensity[Offset+Lane]=ApplyEditBrushes(Density,X,Y,SampleZ);
}
}
Offset+=BatchCount;
}
@@ -11312,6 +11609,10 @@ private:
{
for(uint32 Id:*QueryItems)
{
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
if(LegacyFieldTermScopeActive())
++ThreadClassificationStats().LegacyPrimitiveCandidateVisits;
#endif
const FPrimitive& P=Primitives[Id];
const VF_FieldPrimitive& Shared=SharedFieldPrimitives[Id];
if(P.bCarve)
@@ -11323,6 +11624,10 @@ private:
if(X<P.Min[0]-DetailBand||X>P.Max[0]+DetailBand
||Y<P.Min[1]-DetailBand||Y>P.Max[1]+DetailBand
||Z<P.Min[2]-DetailBand||Z>P.Max[2]+DetailBand)continue;
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
if(LegacyFieldTermScopeActive())
++ThreadClassificationStats().LegacyPrimitiveDistanceEvaluations;
#endif
float Distance=VF_FieldPrimitiveDistance(Shared,X,Y,Z);
if(P.bHasFloorClip)
{
@@ -11330,15 +11635,33 @@ private:
Distance=VF_FieldSmoothMax(Distance,static_cast<float>(Floor-Z),
static_cast<float>(P.FloorBlendRadius));
}
Cave=VF_FieldSmoothMin(Cave,Distance,static_cast<float>(P.BlendRadius));
float Combined=0.0f;
{
FScopedLegacySmoothMinTimer SmoothMinTimer;
Combined=VF_FieldSmoothMin(Cave,Distance,
static_cast<float>(P.BlendRadius));
}
Cave=Combined;
}
else if(X>=P.Min[0]&&X<=P.Max[0]&&Y>=P.Min[1]&&Y<=P.Max[1]
&&Z>=P.Min[2]&&Z<=P.Max[2])
{
#if defined(VF_L76_PROFILE_LEGACY_TERMS)
if(LegacyFieldTermScopeActive())
++ThreadClassificationStats().LegacyPrimitiveDistanceEvaluations;
#endif
Overlay=std::max(Overlay,-VF_FieldPrimitiveDistance(Shared,X,Y,Z));
}
}
}
if(OutCriticalAir)*OutCriticalAir=bCriticalAir;
return VF_FieldSmoothMin(Base,std::max(Cave,Overlay),StrataCaveBlendRadius);
float RockDistance=0.0f;
{
FScopedLegacySmoothMinTimer SmoothMinTimer;
RockDistance=VF_FieldSmoothMin(Base,std::max(Cave,Overlay),
StrataCaveBlendRadius);
}
return RockDistance;
}
struct FOwnerNoiseLineContext
@@ -11543,15 +11866,22 @@ private:
*DetailSupremum;
}
float PassageBandRelief=0.0f;
const float Relief=SurfaceRelief(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z),static_cast<uint32>(Owner->Seed),&PassageBandRelief);
float Relief=0.0f;
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::SurfaceReliefNoise);
Relief=SurfaceRelief(static_cast<float>(X),static_cast<float>(Y),
static_cast<float>(Z),static_cast<uint32>(Owner->Seed),&PassageBandRelief);
}
const float PassageWeight=std::clamp(PassageInfluence,0.0f,1.0f);
const FLayerField* Field=FindLayerField(Z);
FOwnerNoiseLineContext OwnerNoise;
float OwnerRelief=0.0f;
if(Field)
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::OwnerNoiseAndWarp);
OwnerRelief=OwnerCaveRoughnessForDetail(X,Y,Z,SurfaceSDF,*Field,
CaveInfluence,PassageWeight,OwnerNoise);
}
const float Detail=std::clamp(BlendDetailFamilies(Relief,PassageBandRelief,
OwnerRelief,CaveInfluence,FloorInfluence,PassageWeight),-3.9f,3.9f);
return Detail;
@@ -11570,8 +11900,12 @@ private:
using Clock=std::chrono::steady_clock;
const auto ReliefStart=Profile?Clock::now():Clock::time_point{};
float PassageBandRelief=0.0f;
const float Relief=NoiseLine.SurfaceReliefAt(static_cast<float>(Z),
bMacroKnown,KnownMacro,&PassageBandRelief);
float Relief=0.0f;
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::SurfaceReliefNoise);
Relief=NoiseLine.SurfaceReliefAt(static_cast<float>(Z),
bMacroKnown,KnownMacro,&PassageBandRelief);
}
if(Profile)
Profile->ReliefSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-ReliefStart).count());
@@ -11585,8 +11919,11 @@ private:
const float PassageWeight=std::clamp(PassageInfluence,0.0f,1.0f);
float OwnerRelief=0.0f;
if(Field)
{
FScopedLegacyFieldTermTimer TermTimer(ELegacyFieldTerm::OwnerNoiseAndWarp);
OwnerRelief=OwnerCaveRoughnessForDetail(X,Y,Z,SurfaceSDF,*Field,
CaveInfluence,PassageWeight,NoiseLine.OwnerNoise,Profile);
}
if(Profile)
Profile->OwnerRoughnessSampleNanoseconds+=static_cast<uint64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now()-OwnerStart).count());
@@ -11885,6 +12222,7 @@ private:
bool PassageFeatureBoundsFailOpen=false;
std::vector<std::vector<FPassageNearestNode>> PassageNearestNodes;
std::vector<std::vector<FPassageMouthSupportSegment>> PassageMouthSupportRoutes;
std::vector<FPassageComposerConstants> PassageComposerConstants;
std::vector<std::vector<FPassageLaterBoreSource>> PassageLaterBoreSources;
std::vector<FPassageProofBound> PassageProofBounds;
std::vector<FPassageProofNode> PassageProofNodes;