streaming: passage-heavy tiles first, far parents after LOD0-1 (L84, headless only)

Predicted passage-overlap tiles are scheduled earlier (FI worst tile rank 116 -> 69, dispatch
0.95 -> 0.82 s); LOD0-1 publish from the base shape before far parents, which are built before
LOD2 is queued. Every tile hash identical to L83 (all four whole views). Fast tier PASS.
Cold LOD0-2 W8 / W12: SW 1.02 / 0.99, FI 2.26 / 2.18, TN 2.27 / 2.15, VS 1.13 / 1.09 s.
Heaviest tiles (FI/TN junction band, z=-93) still take 0.5-0.7 s each, mostly full-grid fill;
idealized floor ~0.92 s. Unreal scheduler not yet updated.

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-28 01:02:59 +01:00
1 parent a81b5b7d06
commit 0a98da6fd0
6 files changed
+590 -9

No files matched your search

+72
View File
@@ -437,6 +437,13 @@ namespace StandaloneWorldMesher
class FStandaloneStreamingGenerator
{
private:
struct FPassagePrioritySegment
{
std::array<std::int64_t,3> Minimum{};
std::array<std::int64_t,3> Maximum{};
};
public:
static void SetCoarseFirstSchedulingEnabled(bool bEnabled)
{bCoarseFirstSchedulingEnabled.store(bEnabled,std::memory_order_relaxed);}
@@ -553,6 +560,35 @@ public:
void SetSchedulerPolicy(int32 Policy)
{SchedulerPolicy=std::clamp(Policy,-1,3);}
void SetPassageHeavyPriority(const VoxelForgeCore::ResolvedLayoutSnapshotV3* Layout)
{
PassagePrioritySegments.clear();
if(Layout)
{
constexpr std::int64_t SubvoxelsPerVoxel=
VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
constexpr std::int64_t ExtraInfluenceVoxels=32;
for(const auto& Passage:Layout->passages)
for(std::size_t Index=1;Index<Passage.controlPoints.size();++Index)
{
const auto& A=Passage.controlPoints[Index-1];
const auto& B=Passage.controlPoints[Index];
const std::int64_t Radius=std::max(A.radiusSubvoxels,B.radiusSubvoxels)
/SubvoxelsPerVoxel+ExtraInfluenceVoxels;
FPassagePrioritySegment Segment;
for(std::size_t Axis=0;Axis<3;++Axis)
{
const std::int64_t AVoxel=A.positionSubvoxels[Axis]/SubvoxelsPerVoxel;
const std::int64_t BVoxel=B.positionSubvoxels[Axis]/SubvoxelsPerVoxel;
Segment.Minimum[Axis]=std::min(AVoxel,BVoxel)-Radius;
Segment.Maximum[Axis]=std::max(AVoxel,BVoxel)+Radius;
}
PassagePrioritySegments.push_back(Segment);
}
}
std::printf("[PassageHeavyPriority] route_segments=%zu enabled=%d\n",
PassagePrioritySegments.size(),PassagePrioritySegments.empty()?0:1);
}
void SetSchedulerMotionSpeedMetersPerSecond(double Speed)
{
const double ClampedSpeed=std::max(0.0,Speed);
@@ -911,6 +947,16 @@ private:
else if(Key.Level<=2)NearTiles.push_back(Key);
else FarTiles.push_back(Key);
}
std::unordered_map<FVoxelTileKey,std::size_t,FStandaloneTileKeyHash>
PassageScores;
if(!PassagePrioritySegments.empty())
{
PassageScores.reserve((WalkableRing.size()+NearTiles.size())*2+1);
for(const FVoxelTileKey& Key:WalkableRing)
PassageScores.emplace(Key,PassageOverlapScore(Key));
for(const FVoxelTileKey& Key:NearTiles)
PassageScores.emplace(Key,PassageOverlapScore(Key));
}
const auto IsAhead=[&](const FVoxelTileKey& Key)
{
if(TravelDirection.X*TravelDirection.X+TravelDirection.Y*TravelDirection.Y<=1.0e-12f)
@@ -925,6 +971,12 @@ private:
std::sort(Group.begin(),Group.end(),[&](const FVoxelTileKey& A,const FVoxelTileKey& B)
{
if(A.Level!=B.Level)return bNearFirst?A.Level<B.Level:A.Level>B.Level;
if(A.Level<=2&&!PassageScores.empty())
{
const std::size_t ScoreA=PassageScores.at(A);
const std::size_t ScoreB=PassageScores.at(B);
if(ScoreA!=ScoreB)return ScoreA>ScoreB;
}
const bool bAheadA=IsAhead(A),bAheadB=IsAhead(B);
if(bAheadA!=bAheadB)return bAheadA;
const double DA=StandaloneWorldMesher::TileDistanceSquaredChunks(A,NewDesired.CenterChunk);
@@ -1253,6 +1305,25 @@ private:
{
return TimelineFrameOrdinal?TimelineFrameOrdinal->load(std::memory_order_relaxed):0;
}
std::size_t PassageOverlapScore(const FVoxelTileKey& Key) const
{
const FIntVector Origin=Key.OriginVoxels();
const std::int64_t TileMinimum[3]={Origin.X,Origin.Y,Origin.Z};
const std::int64_t TileMaximum[3]={
static_cast<std::int64_t>(Origin.X)+Key.ExtentVoxels(),
static_cast<std::int64_t>(Origin.Y)+Key.ExtentVoxels(),
static_cast<std::int64_t>(Origin.Z)+Key.ExtentVoxels()};
std::size_t Score=0;
for(const FPassagePrioritySegment& Segment:PassagePrioritySegments)
{
bool bOverlap=true;
for(std::size_t Axis=0;Axis<3;++Axis)
bOverlap=bOverlap&&Segment.Maximum[Axis]>=TileMinimum[Axis]
&&Segment.Minimum[Axis]<=TileMaximum[Axis];
Score+=bOverlap?1u:0u;
}
return Score;
}
static void PrintTileStageEvent(const FStandaloneCpuTileTimelineEvent& Event)
{
const auto& D=Event.Diagnostics;
@@ -1386,6 +1457,7 @@ private:
bool bActiveLaneBatching=false;
int32 ProofBlockCells=8;
bool bAuditUniformBlockProofs=false;
std::vector<FPassagePrioritySegment> PassagePrioritySegments;
std::atomic<uint64> CacheHitCount{0};
mutable std::mutex Mutex;
std::condition_variable Wake;