streaming: far LODs keep a share while near work is pending (L79); no more horizon starvation

L78's strict near-first order starved far LODs at 30 m/s (100% far holes, never completed).
Unreal policy 2: once the collision ring is ready, LOD0-2 keep their reserve and far tiles are
admitted as low-priority Horizon work; headless gives far work 1 of every 16 requests.
Moving camera, far holes while moving: 1.4 m/s L77 8% / L78 29% / L79 31%; 30 m/s L77 8% /
L78 100% / L79 72% (completes on dwell). Near readiness while moving at 30 m/s: 52 / 51 / 67%.
Still worse than L77 for fast travel -> speed-adaptive policy next. World unchanged (v213).
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 20:17:35 +01:00
1 parent ec20994a8e
commit 2c8a26c0d0
11 files changed
+833 -30

No files matched your search

+150 -1
View File
@@ -1874,7 +1874,9 @@ namespace StandaloneStreamBenchmark
bool bParallelSignEdgeRepair=false,bool bAuditNativeLattice=false,
bool bLatticeOrphanControl=false,bool bCaptureSkipWalkability=false,
bool bProfilePassageComposer=false,bool bAllowUniformSkipsWhenHashing=false,
bool bLinux77Profile=false)
bool bLinux77Profile=false,int32 SchedulerPolicy=-1,
bool bMovingCameraTrace=false,double MovingCameraSpeedMetersPerSecond=0.0,
double MovingCameraDurationSeconds=12.0,double MovingCameraDwellSeconds=5.0)
{
using Clock=std::chrono::steady_clock;
FStandaloneOwnerWorld& World=*OwnerWorld;
@@ -2038,6 +2040,7 @@ namespace StandaloneStreamBenchmark
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileColumnOverlap,
FarHorizonLOD8InteriorStepVoxels,bUseSharedColumnCache,
bAllowUniformSkipsWhenHashing);
Streamer.SetSchedulerPolicy(SchedulerPolicy);
Streamer.SetUniformBlockProofAuditEnabled(bAuditUniformBlockProofs);
Streamer.SetTileDrainInstrumentationEnabled(bInstrumentTileDrain);
Streamer.SetLinux77ProfileEnabled(bLinux77Profile);
@@ -4975,6 +4978,152 @@ namespace StandaloneStreamBenchmark
std::fprintf(stderr,"[StreamPipeline] expected %zu tiles after shape publication but queued %d.\n",
Desired.Tiles.size(),Update.DesiredTiles);return 7;
}
if(bMovingCameraTrace)
{
constexpr double SamplePeriodSeconds=0.25;
using FTileSet=std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash>;
FTileSet Resident;
std::unordered_map<FVoxelTileKey,Clock::time_point,FStandaloneTileKeyHash> FarMissingSince;
const double VoxelMetres=static_cast<double>(World.CoreVoxelSizeMicrons)/1000000.0;
const auto TotalSeconds=MovingCameraDurationSeconds+MovingCameraDwellSeconds;
const auto TraceStart=Clock::now();
int32 SampleIndex=0;
int32 GenerationFailures=0;
double FirstAllFarReadySeconds=-1.0;
int32 Samples=0;
std::printf("[L79MovingCheckpoint] stage=start desired=%zu policy=%d speed_mps=%.3f\n",
Desired.Tiles.size(),SchedulerPolicy,MovingCameraSpeedMetersPerSecond);
std::fflush(stdout);
auto DrainReady=[&]()
{
std::vector<FStandaloneCpuTile> Ready=Streamer.TakeReady();
for(const FStandaloneCpuTile& Tile:Ready)
{
if(Tile.bGenerationFailed)++GenerationFailures;
else Resident.insert(Tile.Key);
}
};
for(;;++SampleIndex)
{
const double ScheduledSeconds=std::min(TotalSeconds,
static_cast<double>(SampleIndex)*SamplePeriodSeconds);
std::this_thread::sleep_until(TraceStart+std::chrono::duration<double>(ScheduledSeconds));
const auto SampleAt=Clock::now();
const double ElapsedSeconds=std::min(TotalSeconds,
std::chrono::duration<double>(SampleAt-TraceStart).count());
const double MovingSeconds=std::min(MovingCameraDurationSeconds,ElapsedSeconds);
FVector Camera=CameraPositionVoxels;
Camera.X+=static_cast<float>(MovingCameraSpeedMetersPerSecond*MovingSeconds/VoxelMetres);
DrainReady();
if(SampleIndex==0)
{
std::printf("[L79MovingCheckpoint] stage=drained resident=%zu failures=%d\n",
Resident.size(),GenerationFailures);
std::fflush(stdout);
}
FStandaloneDesiredTileSet CurrentDesired=Desired;
CurrentDesired.CenterChunk=FIntVector(
static_cast<int32>(std::floor(Camera.X/CHUNK_SIZE)),
static_cast<int32>(std::floor(Camera.Y/CHUNK_SIZE)),
static_cast<int32>(std::floor(Camera.Z/CHUNK_SIZE)));
const auto CoreDesired=Shape.desiredTiles({Camera.X,Camera.Y,Camera.Z});
CurrentDesired.Tiles.clear();CurrentDesired.Tiles.reserve(CoreDesired.size());
for(const auto& Key:CoreDesired)
{
if(MaxLevel>=0&&Key.lod>MaxLevel)continue;
CurrentDesired.Tiles.emplace_back(
FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod);
}
const auto BeforeUpdate=Clock::now();
const std::vector<FVoxelTileKey> ResidentKeys(Resident.begin(),Resident.end());
const FStandaloneStreamingUpdate MovingUpdate=Streamer.UpdateDesiredWithSet(
CurrentDesired,ResidentKeys,static_cast<uint64>(SampleIndex),Camera);
if(SampleIndex==0)
{
std::printf("[L79MovingCheckpoint] stage=updated desired=%d new_jobs=%d\n",
MovingUpdate.DesiredTiles,MovingUpdate.NewJobs);
std::fflush(stdout);
}
const double UpdateMilliseconds=std::chrono::duration<double,std::milli>(
Clock::now()-BeforeUpdate).count();
if(MovingUpdate.DesiredTiles!=static_cast<int32>(CurrentDesired.Tiles.size()))
{
Streamer.Stop();
std::fprintf(stderr,"[MovingCameraTrace] desired-set mismatch expected=%zu actual=%d\n",
CurrentDesired.Tiles.size(),MovingUpdate.DesiredTiles);
return 9;
}
std::size_t NearDesired=0,NearResident=0,FarDesired=0,FarResident=0,FarCovered=0;
double FarStaleAgeSum=0.0,FarStaleAgeMax=0.0;
const auto FloorDiv=[](int32 Value,int32 Divisor)
{
const int32 Quotient=Value/Divisor, Remainder=Value%Divisor;
return Quotient-((Remainder!=0&&Remainder<0)?1:0);
};
std::unordered_map<FVoxelTileKey,Clock::time_point,FStandaloneTileKeyHash> CurrentFarMissing;
for(const FVoxelTileKey& Key:CurrentDesired.Tiles)
{
const bool bResident=Resident.contains(Key);
if(Key.Level<=2)
{
++NearDesired;
NearResident+=bResident?1u:0u;
}
else
{
++FarDesired;
FarResident+=bResident?1u:0u;
bool bFarCovered=bResident;
for(int32 ParentLOD=Key.Level+1;
!bFarCovered&&ParentLOD<=World.MaxClipLevel;++ParentLOD)
{
const int32 Scale=1<<(ParentLOD-Key.Level);
const FVoxelTileKey Parent(FIntVector(
FloorDiv(Key.Coord.X,Scale),FloorDiv(Key.Coord.Y,Scale),
FloorDiv(Key.Coord.Z,Scale)),ParentLOD);
bFarCovered=Resident.contains(Parent);
}
FarCovered+=bFarCovered?1u:0u;
if(!bResident)
{
auto [It,bInserted]=FarMissingSince.try_emplace(Key,SampleAt);
(void)bInserted;
const double Age=std::chrono::duration<double>(SampleAt-It->second).count();
FarStaleAgeSum+=Age;
FarStaleAgeMax=std::max(FarStaleAgeMax,Age);
CurrentFarMissing.emplace(Key,It->second);
}
}
}
FarMissingSince.swap(CurrentFarMissing);
const double NearReadyPercent=100.0*static_cast<double>(NearResident)
/static_cast<double>(std::max<std::size_t>(1,NearDesired));
const double FarMissingPercent=100.0*static_cast<double>(FarDesired-FarResident)
/static_cast<double>(std::max<std::size_t>(1,FarDesired));
const double FarHolePercent=100.0*static_cast<double>(FarDesired-FarCovered)
/static_cast<double>(std::max<std::size_t>(1,FarDesired));
const double FarStaleAgeMean=FarDesired>FarResident
?FarStaleAgeSum/static_cast<double>(FarDesired-FarResident):0.0;
if(FarDesired>0&&FarDesired==FarResident&&FirstAllFarReadySeconds<0.0)
FirstAllFarReadySeconds=ElapsedSeconds;
const char* Phase=ElapsedSeconds<MovingCameraDurationSeconds?"move":"dwell";
std::printf("[L79MovingSample] policy=%d speed_mps=%.3f t_s=%.3f phase=%s x_m=%.3f near=%zu/%zu near_ready_pct=%.3f far=%zu/%zu far_missing_pct=%.3f far_covered=%zu/%zu far_hole_pct=%.3f far_stale_mean_s=%.3f far_stale_max_s=%.3f queued_jobs=%d queue_update_ms=%.3f generation_failures=%d\n",
SchedulerPolicy,MovingCameraSpeedMetersPerSecond,ElapsedSeconds,Phase,
Camera.X*VoxelMetres,NearResident,NearDesired,NearReadyPercent,
FarResident,FarDesired,FarMissingPercent,FarCovered,FarDesired,
FarHolePercent,FarStaleAgeMean,FarStaleAgeMax,
MovingUpdate.NewJobs,UpdateMilliseconds,GenerationFailures);
std::fflush(stdout);
++Samples;
if(ElapsedSeconds>=TotalSeconds)break;
}
std::printf("[L79MovingSummary] policy=%d speed_mps=%.3f move_s=%.3f dwell_s=%.3f samples=%d resident_tiles=%zu first_all_far_ready_s=%.3f generation_failures=%d priority=background_workers_only status=%s\n",
SchedulerPolicy,MovingCameraSpeedMetersPerSecond,MovingCameraDurationSeconds,
MovingCameraDwellSeconds,Samples,Resident.size(),FirstAllFarReadySeconds,
GenerationFailures,GenerationFailures==0?"PASS":"FAIL");
Streamer.Stop();
return GenerationFailures==0?0:9;
}
FSceneExportWriter SceneExport;
const bool bExportRequested=!MeshExportPath.empty();
bool bSceneExportPassing=true;