streaming: near LODs first (walkable ring, LOD0, LOD1, LOD2, then far); exact passage trims (L78)

- Headless and Unreal schedulers drain the collision-critical ring, then LOD0-2, then far LODs
  at low priority. LOD0-2 ready (W8 medians): SW 4.36->1.76, FI 6.89->5.26, TN 4.88->3.22,
  VS 2.71->1.56 s. Whole view roughly unchanged (FI/VS slightly later).
  KNOWN RISK: far tiles wait until every near tile is ready, so far LODs may starve while
  moving fast (to address next).
- Exact composer trims: tile-level upper-mouth candidate list, segment-index min/max rejection
  before lower_bound, 64-entry thread-local value-noise corner cache. Passage share 65 -> 63%.
- World exact: 24/24 hash matrix + pinned L75 owner hash. Fast tier PASS. Linux UE host builds.

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 19:13:45 +01:00
1 parent 5e091d254c
commit ec20994a8e
10 files changed
+718 -52

No files matched your search

+74 -6
View File
@@ -201,6 +201,37 @@ public:
passageColumns_=passageRoutes_;
passageMouthSupports_=passageRoutes_;
passageMouthSupportSegments_.resize(passageRoutes_.size());
if(shape.ResolvedLayout&&!shape.ResolvedLayout->passages.empty())
{
constexpr double Q=static_cast<double>(
VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
const double qPerMetre=Q*1000000.0/
std::max<std::uint32_t>(1,
shape.ResolvedLayout->coreVoxelSizeMicrometres);
const double horizontalReach=15.0*qPerMetre;
const double verticalReach=8.0*qPerMetre;
const bool failOpen=minimumSubvoxels_[0]>maximumSubvoxels_[0]
||minimumSubvoxels_[1]>maximumSubvoxels_[1]
||minimumSubvoxels_[2]>maximumSubvoxels_[2];
for(std::size_t passageIndex=0;
passageIndex<shape.ResolvedLayout->passages.size();++passageIndex)
{
if(failOpen)
{
upperMouthSupportPassages_.push_back(passageIndex);
continue;
}
const auto& mouth=shape.ResolvedLayout->passages[
passageIndex].upperMouthSubvoxels;
if(static_cast<double>(mouth[0])+horizontalReach>=minimumSubvoxels_[0]
&&static_cast<double>(mouth[0])-horizontalReach<=maximumSubvoxels_[0]
&&static_cast<double>(mouth[1])+horizontalReach>=minimumSubvoxels_[1]
&&static_cast<double>(mouth[1])-horizontalReach<=maximumSubvoxels_[1]
&&static_cast<double>(mouth[2])+verticalReach>=minimumSubvoxels_[2]
&&static_cast<double>(mouth[2])-verticalReach<=maximumSubvoxels_[2])
upperMouthSupportPassages_.push_back(passageIndex);
}
}
for(const auto& crossing:shape.PassageVoidCrossings)
if(crossing.needsColumn
&&crossing.bottomZSubvoxels<=maximumSubvoxels_[2]
@@ -257,6 +288,10 @@ public:
[](std::uint8_t value){return value!=0;});}
bool hasRouteFeatureOverlap() const{return routeFeatureOverlap_;}
bool hasColumnZOverlap() const{return columnZOverlap_;}
bool mayContainUpperMouthSupport() const
{return !upperMouthSupportPassages_.empty();}
const std::vector<std::size_t>& upperMouthSupportPassages() const
{return upperMouthSupportPassages_;}
bool mayContainResolvedBoundaryBand() const{return mayContainResolvedBoundaryBand_;}
bool mayContainTransitionPassageFade() const{return mayContainTransitionPassageFade_;}
bool mayContainResolvedBoundaryBandAtZ(double zMicrometres) const
@@ -282,6 +317,7 @@ public:
std::vector<std::uint32_t> cavernFeatureIndices_;
std::vector<std::uint8_t> passageRoutes_,passageColumns_,passageMouthSupports_;
std::vector<std::vector<FCandidateSegment>> passageMouthSupportSegments_;
std::vector<std::size_t> upperMouthSupportPassages_;
std::size_t routeSegmentCandidateCount_=0;
bool routeFeatureOverlap_=false,columnZOverlap_=false;
bool mayContainResolvedBoundaryBand_=true;
@@ -1004,6 +1040,11 @@ public:
auto* passageProfile=passageTileScopeMatches
?passageTileQueries->profileStats():nullptr;
if(passageProfile)++passageProfile->passageComposerQueries;
if(passageTileScopeMatches&&density>=0.0f
&&!passageTileQueries->hasRouteFeatureOverlap()
&&!passageTileQueries->hasColumnZOverlap()
&&!passageTileQueries->hasAnyMouthSupport()
&&!passageTileQueries->mayContainUpperMouthSupport())return density;
bool queryNearUpperMouthSupport=false;
const auto checkUpperMouth=[&](const auto& passage)
{
@@ -1015,10 +1056,22 @@ public:
if(dx*dx+dy*dy<=reach*reach&&std::abs(dz)<=8.0*qPerMetre)
queryNearUpperMouthSupport=true;
};
for(const auto& passage:ResolvedLayout->passages)
if(passageTileScopeMatches)
{
checkUpperMouth(passage);
if(queryNearUpperMouthSupport)break;
for(const std::size_t passageIndex:
passageTileQueries->upperMouthSupportPassages())
{
checkUpperMouth(ResolvedLayout->passages[passageIndex]);
if(queryNearUpperMouthSupport)break;
}
}
else
{
for(const auto& passage:ResolvedLayout->passages)
{
checkUpperMouth(passage);
if(queryNearUpperMouthSupport)break;
}
}
if(passageTileScopeMatches&&density>=0.0f
&&!passageTileQueries->hasRouteFeatureOverlap()
@@ -3862,6 +3915,7 @@ private:
{
std::array<double,3> minimum{},maximum{};
double maximumRadius=0.0;
std::size_t minimumSegment=0,maximumSegment=0;
std::vector<std::size_t> segments;
int left=-1,right=-1;
};
@@ -4676,6 +4730,8 @@ private:
node.maximumRadius=std::max({node.maximumRadius,
static_cast<double>(a.radiusSubvoxels),static_cast<double>(b.radiusSubvoxels)});
}
node.minimumSegment=*std::min_element(segments.begin(),segments.end());
node.maximumSegment=*std::max_element(segments.begin(),segments.end());
node.segments=segments;
std::sort(node.segments.begin(),node.segments.end());
const int nodeIndex=static_cast<int>(PassageNearestNodes[passageIndex].size());
@@ -5284,7 +5340,11 @@ private:
if(passageIndex>=PassageNearestNodes.size()||nodeIndex<0)return;
const auto& nodes=PassageNearestNodes[passageIndex];
const auto& node=nodes[static_cast<std::size_t>(nodeIndex)];
const auto candidate=std::lower_bound(node.segments.begin(),node.segments.end(),first);
if(node.maximumSegment<first||node.minimumSegment>=end)return;
const bool allSegmentsInRange=first<=node.minimumSegment
&&node.maximumSegment<end;
const auto candidate=allSegmentsInRange?node.segments.begin()
:std::lower_bound(node.segments.begin(),node.segments.end(),first);
if(candidate==node.segments.end()||*candidate>=end)return;
const double lowerBound=PassageNearestNodeLowerBound(node,qx,qy,qz);
if(lowerBound>bestDistance)return;
@@ -5325,7 +5385,11 @@ private:
if(passageIndex>=PassageNearestNodes.size()||nodeIndex<0)return false;
const auto& nodes=PassageNearestNodes[passageIndex];
const auto& node=nodes[static_cast<std::size_t>(nodeIndex)];
const auto candidate=std::lower_bound(node.segments.begin(),node.segments.end(),first);
if(node.maximumSegment<first||node.minimumSegment>=end)return false;
const bool allSegmentsInRange=first<=node.minimumSegment
&&node.maximumSegment<end;
const auto candidate=allSegmentsInRange?node.segments.begin()
:std::lower_bound(node.segments.begin(),node.segments.end(),first);
if(candidate==node.segments.end()||*candidate>=end)return false;
if(PassageNearestNodeLowerBound(node,qx,qy,qz)>signedDistanceLimit)return false;
if(node.left<0)return true;
@@ -5448,7 +5512,11 @@ private:
if(passageIndex>=PassageNearestNodes.size()||nodeIndex<0)return false;
const auto& nodes=PassageNearestNodes[passageIndex];
const auto& node=nodes[static_cast<std::size_t>(nodeIndex)];
auto candidate=std::lower_bound(node.segments.begin(),node.segments.end(),first);
if(node.maximumSegment<first||node.minimumSegment>=end)return false;
const bool allSegmentsInRange=first<=node.minimumSegment
&&node.maximumSegment<end;
auto candidate=allSegmentsInRange?node.segments.begin()
:std::lower_bound(node.segments.begin(),node.segments.end(),first);
if(candidate==node.segments.end()||*candidate>=end)return false;
const double xyReach=0.39*qPerMetre;
if(qx+xyReach<node.minimum[0]||qx-xyReach>node.maximum[0]