wip(assembler): round S7 -- workers kept busy: in-game cold full view 17.5 s -> 7.8 s

Two admission blockers found by per-frame decline codes: the near reserve held
four slots idle after LOD0 was done (coarse capped at 6), and finished-but-
unapplied results counted against the task cap. Admission now counts queued +
running tasks; near reserve releases once the walkable ring is ready while
stationary (voxel.StreamingDynamicNearReserve, default on). Walkable ring
0.65 -> 0.58 s, full LOD0 2.28 -> 1.44 s, busy workers 2.2 -> 5.6 of 10.
Old behaviour behind voxel.StreamingLegacyPendingResultCap. RMC source reviewed
(read-only): batching/fast-cook ranked, not yet worth it on measured apply cost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-19 08:10:10 +02:00
1 parent b9c13c37c0
commit 5eb9ee76ff
7 files changed
+726 -53

No files matched your search

+15 -2
View File
@@ -443,7 +443,12 @@ 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;
/** Submitted generation tasks still queued or running; completed MPSC results do not occupy admission.
* Taches de generation soumises, en file ou en cours ; les resultats MPSC termines ne bloquent pas l'admission. */
int32 GetActiveGenerationTaskCount() const;
/** Count queued/running work for admission; the legacy CVar can include finished results awaiting apply.
* Compter le travail en file/en cours ; le CVar de compatibilite peut inclure les resultats finis en attente. */
int32 GetGenerationAdmissionCount() const;
/** Reserve task slots for collision-critical/edit/LOD0 work; far work uses the remainder. */
int32 GetNearTaskReserve(int32 MaxTasks) const;
@@ -1093,7 +1098,9 @@ public:
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
// Keys stay pending from task submission until the game thread applies the result; completed results can wait here.
// Les cles restent en attente de la soumission jusqu'a l'application sur le thread de jeu ; les resultats termines y attendent.
TSet<FVoxelTileKey> PendingTiles;
// Game-thread-owned map; workers retain the shared token and only read its atomic flag.
TMap<FVoxelTileKey, TSharedPtr<FVoxelTileCancellationState, ESPMode::ThreadSafe>>
PendingTileCancellation;
@@ -1121,7 +1128,8 @@ public:
// Active pendant les mutations de génération lancées par l'éditeur, sans signifier la destruction.
std::atomic<bool> bGenerationPaused{false};
// Number of async tasks currently running — EndPlay waits for this to reach 0
// Submitted async tasks (including UE-queued work) — EndPlay waits for this to reach 0.
// Taches asynchrones soumises (y compris la file UE) — EndPlay attend que le compteur revienne a 0.
std::atomic<int32> ActiveTaskCount{0};
// Active workers are counted inside the launched tile task, distinct from tasks queued by UE.
// Les travailleurs actifs sont comptes dans la tache de tuile, distincts des taches en attente UE.
@@ -1199,6 +1207,11 @@ public:
int32 EffectiveNearTaskReserve = -1;
uint64 NextStreamingRequestOrdinal = 0;
std::atomic<uint64> NextStreamingGenerationOrdinal{0};
// Cumulative worker-task wall cycles avoid aliasing the instantaneous per-frame counter.
// Les cycles cumules des taches evitent l'aliasing du compteur instantane echantillonne par frame.
std::atomic<uint64> TotalGenerationWorkerTaskCycles{0};
double LastStreamingSchedulerSampleSeconds = 0.0;
uint64 LastStreamingSchedulerWorkerCycles = 0;
struct FReadyTransitionVisibilitySample
{