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:
1 parent
370c39e473
commit
f36bc1f9b2
3 files changed
+232
-28
No files matched your search
@@ -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.
|
||||
|
||||
Reference in new issue
Block a user