wip(streaming): priority by class and level -- near ring 53.6s -> 1.2s, but crossing test fails

Scheduling rule: CollisionCritical -> Edited -> LOD0 -> Horizon, with coarse work capped so 4 task slots stay reserved for near work; result queues follow the same order; epoch and cancellation handling unchanged.

Measured at MaxClipLevel=5 / RenderDistanceChunks=768: spawn ring request-to-ready 53.632 s -> 1.171 s; spawn ring from trace start 68.252 -> 62.549 s; moving first complete ring 66.808 -> 63.917 s, warm ring p50 0.267 s, per-tile moving LOD0 p50 69.4 -> 62.0 ms; full settle of 10,841 tiles 110.7 -> 106.9 s. Gate stress PASS (139.99 m, 0 full stops); static perf PASS.

⚠️ NOT SAFE TO MERGE YET: the strate-crossing test fails in both patched runs with capsule_overlaps_pending_floor -- the exact guarantee the gate exists for. Codex suspects the owner's current DA_Settings (seal 50, clip level 5) rather than this change, since older passing captures used different settings. Control needed: same test, same assets, on 370c39e.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-23 19:04:13 +02:00
1 parent 370c39e473
commit f36bc1f9b2
3 files changed
+232 -28

No files matched your search

+27 -1
View File
@@ -88,6 +88,14 @@ struct FVoxelStreamingAnchor
* - BeginPlay: Initialize generator, mesher
* - Tick: Update chunks around player position
*/
enum class EVoxelTileWorkPriority : uint8
{
Horizon = 0,
LOD0 = 1,
Edited = 2,
CollisionCritical = 3
};
struct FChunkResult
{
FVoxelTileKey Tile; // which clipmap tile this mesh is for (carries coord + level)
@@ -108,6 +116,7 @@ struct FChunkResult
uint64 GenerationStartCycles = 0;
uint64 GenerationEndCycles = 0;
uint64 ApplyStartCycles = 0;
EVoxelTileWorkPriority WorkPriority = EVoxelTileWorkPriority::Horizon;
int32 ClassifyVerdict = -1; double ClassifySeconds = 0.0; double MeshSeconds = 0.0; double StreamSeconds = 0.0; int32 NumTriangles = 0; int32 NumCeilingTriangles = 0;
int32 NumVertices = 0;
uint64 GeometryBytes = 0;
@@ -313,6 +322,8 @@ public:
* capped to (logical cores − 2) so small CPUs don't thrash on a flat 16 (background
* priority stops frame starvation, not the context-switch overhead). */
int32 GetMaxConcurrentTasks() const;
/** Reserve task slots for collision-critical/edit/LOD0 work; far work uses the remainder. */
int32 GetNearTaskReserve(int32 MaxTasks) const;
//=========================================================================
// TERRAIN MODIFICATION (player carving & filling)
@@ -738,7 +749,8 @@ public:
*
* @param ChunkCoord - Which chunk to load
*/
void LoadTile(const FVoxelTileKey& Tile, bool bHighPriority = false);
void LoadTile(const FVoxelTileKey& Tile,
EVoxelTileWorkPriority WorkPriority = EVoxelTileWorkPriority::Horizon);
/**
* Worker-side gen for one tile: classify → GenerateMesh/GenerateSheetMesh → BuildTileStreamSet.
@@ -871,6 +883,7 @@ public:
void RecordLOD0ReadySample(uint64 RequestStartCycles, uint64 GenerationStartCycles,
uint64 GenerationEndCycles, uint64 ApplyStartCycles,
uint64 ReadyCycles, uint64 CollisionSubmittedCycles);
void RecordLOD0RingReadyIfComplete();
void LogStreamingLatencySummary() const;
// (GetLODForChunk / LODToStep / IsChunkInRange removed — dead since the clipmap
@@ -884,6 +897,12 @@ public:
// mode is NOT safe for multiple producers — concurrent Enqueues race on the tail
// link and silently drop results, which leaks PendingChunkCoord slots until the
// budget is exhausted and streaming stalls permanently. Mpsc guards the producer side.
// Result queues mirror the scheduling order: collision support first, then edited tiles,
// then ordinary LOD0, then the horizon. This keeps a support result from waiting behind a
// completed near-field backlog while preserving one consumer on the game thread.
TQueue<FChunkResult, EQueueMode::Mpsc> CriticalProcessQueue;
TQueue<FChunkResult, EQueueMode::Mpsc> EditedProcessQueue;
TQueue<FChunkResult, EQueueMode::Mpsc> NearProcessQueue;
TQueue<FChunkResult, EQueueMode::Mpsc> ProcessQueue;
TSet<FVoxelTileKey> PendingTiles; // tiles with a gen task in flight
// Game-thread-owned map; workers retain the shared token and only read its atomic flag.
@@ -1005,6 +1024,13 @@ public:
double CollisionCookSeconds = 0.0;
};
TArray<FStreamingLatencySample> LOD0ReadySamples;
// Trace-only ring timing: each desired-set rebuild starts a new epoch. A ring is complete
// when every current desired level-0 key has reached LoadedTiles and no level-0 request for
// that epoch remains pending. This measures visible near-field readiness independently of the
// much larger horizon settle point.
uint64 DesiredEpochStartCycles = 0;
FIntVector DesiredEpochCenterChunk = FIntVector::ZeroValue;
uint32 LastLOD0RingReadyEpoch = MAX_uint32;
// Headless validation hook. These are intentionally non-UPROPERTY state and are enabled only
// by explicit command-line switches; they do not alter ordinary player movement or streaming.