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

+60 -8
View File
@@ -578,7 +578,12 @@ driven by `EditorBrush*` props.
the result through the normal MPSC queue; the game thread reads the token again before applying.
`FChunkResult::Epoch` still guards the generation epoch. Desired work is sorted from the true pawn
position, with the occupied/support LOD0 tile and the next tile along the heading in an absolute
high-priority prefix. Stationary player ≈ free. (Old per-frame O(loaded×desired) scan = 22ms.)
high-priority prefix. Admission counts queued/running `ActiveTaskCount`; `PendingTiles` stays until
game-thread apply for duplicate suppression and result bookkeeping, but completed MPSC results do
not occupy a worker slot. `voxel.StreamingLegacyPendingResultCap=1` restores the old result-inclusive
admission count. `voxel.StreamingDynamicNearReserve=1` holds the authored near reserve while moving
or before the walkable gate, then releases it to coarse work after the stationary ring is ready.
Stationary player ≈ free. (Old per-frame O(loaded×desired) scan = 22ms.)
- **LOD** changes HOT-SWAP (`LoadTile` only, never unload-first) → no holes. LOD
reconciliation lives in the PERSISTENT per-frame submit loop (same loop as new-chunk loads),
NOT as a one-shot on the boundary-cross frame — a one-shot drops every chunk past the task
@@ -1454,8 +1459,10 @@ opt-in because it did not beat the legacy order on the gates and the viewer coul
presentation: set `voxel.StreamingCoarseFirst=1` to admit coarse desired levels from the maximum LOD
down to `max(2, FullResClipLevels)`, favoring tiles ahead of travel and then nearer tiles within each
LOD; LOD0/1 refinement follows. Default `0` preserves the S4 LOD0-to-LOD8 order.
`voxel.StreamingDynamicNearReserve=1` opts into keeping the authored near-task reserve while the pawn
moves or until the stationary walkable ring is ready; default `0` preserves the previous task budget.
`voxel.StreamingDynamicNearReserve=1` keeps the authored near-task reserve while the pawn moves or
until the stationary walkable ring is ready; this is now the default. Set it to `0` only for the
legacy-admission negative control. After the stationary nine-tile ring is ready, coarse work can use
the formerly reserved slots.
The standalone queue defaults to legacy order; `--coarse-first-stream-order` opts into the S5 tiers
and `--legacy-stream-order` explicitly selects the legacy order.
@@ -1498,12 +1505,10 @@ with a 30 second ceiling; at `0`, the authored budget is unchanged. On the match
the boost changed full-set readiness from 17.412 s to 17.346 s and removed count-cap frames, but did
not improve end-to-end residency. Keep the normal 4 / 2 ms guard for play.
The trace found the shared worker pool was not saturated: the 10-slot idle estimate was about 132
The S6 trace found the shared pool was not saturated: its 10-slot idle estimate was about 132
slot-seconds on legacy order and 73 on gated coarse-first, while game-thread tile applies totaled only
about 0.24 seconds during cold residency. Results waited in the result queue, and some frames reached
the four-mesh cap, but expanding the apply budget alone did not move the wall clock. The remaining
floor is worker-side work supply/admission; this trace does not isolate why available worker slots
stay idle. The 1.57 s target remains unmet; the headless trace is not a render-time measurement.
about 0.24 seconds. S7's frame-level admission and cumulative worker-task accounting revisited that
open question; see §8.28.
The report records per-LOD worker, queue, result-queue and apply totals, collision latency, frame
percentiles, hashes, regressions and the command lines. The standalone 60 m/s viewer now counts
@@ -1513,6 +1518,53 @@ W1/W8/W16 world and LOD hashes are unchanged, as are `Epoch`, cancellation, `Mod
shutdown-drain and game-thread UObject-creation rules. See
[`Standalone/Research/WorldAssemblerM3/Round10/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round10/REPORT.md).
### 8.28 Shared admission accounting and worker supply — World Assembler S7 / M3 Round 11
`PendingTiles` is a game-thread result-bookkeeping set: its entry lasts from task submission until
`ApplyTileResult`, so it can include work that has finished and is waiting in one of the MPSC result
queues. It is not the count of active generation tasks. The S6 admission path used
`PendingTiles.Num()` for that cap. On the S7 old-path control, after LOD0 had no missing keys, 877
admission frames still had LOD1+ work missing and hit the four-slot near reserve; at a representative
frame, occupancy was 6/10 while only four tasks were queued/running and two completed results were
waiting to apply. 806 of those 877 frames had a positive completed-result estimate.
Admission now uses `ActiveTaskCount`, which is incremented before launching the UE task and released
by its RAII guard when the worker lambda exits. This still counts queued and running work, but completed
results do not consume an admission slot. `voxel.StreamingLegacyPendingResultCap=1` restores the old
`PendingTiles` accounting; the test harness's `NearReserveMode=Legacy` also sets
`voxel.StreamingDynamicNearReserve=0`, reproducing the S6 path. `StreamingDynamicNearReserve` now
defaults to `1`; `StreamingCoarseFirst` remains `0`, and the apply boost remains `0` in the matched
measurement.
On the 2,578-key owner SurfaceWorld trace, the old-path control reached the full set in 17.527 s,
walkable ring in 0.650 s, and full LOD0 in 2.281 s. Dynamic admission reached them in 7.780 s,
0.581 s, and 1.439 s. Its first-request-to-ready interval was 7.342 s with 41.364 worker-seconds,
or 5.63 integrated busy-worker equivalents. Using the S6 10-slot idle formula on that same interval
gives 32.06 slot-seconds, versus 133.01 on the S7 old-path control and 132.1 in S6. The ten S7
spawn samples that ended with free admission capacity and missing keys all recorded a submission
stop at the 10-task cap (one worker could finish before the end-of-pass sample); the frame trace
found no near-reserve decline, all-loaded latch, walkable gate failure, or inner `LoadTile` rejection
in those passes. Result-aware admission therefore removes the measured cold-set blocker and
reproduces the legacy control within 0.7%, but the 7.78 s full-set result still misses the rough
4.14 s `worker-seconds / 10 slots` estimate. Across the full 45 s trace, free-capacity frames with
missing keys were classified by LOD as near reserve or full cap; none was unclassified.
The per-frame `running_workers` value is biased high when sampled only at admission/apply boundaries;
S7 added cumulative task intervals and found 5.63 busy-worker equivalents over the first-request-to-
ready span, with eight sustained overlaps in the reconstructed intervals; a zero-duration endpoint
tie briefly counts nine. This locates
the remaining gap downstream of an admission refusal, but does not distinguish UE task-pool dispatch
from work internal to a tile. Measured worker task time rose 9.7% versus the negative control
(16.04 ms versus 14.62 ms per initial tile); hashes match and the trace records elapsed task time,
not CPU cycles. During the later +Y move, the loader's `bCoreRegionCompilePending` guard also
blocked 10 legacy and 11 dynamic submission passes despite free tasks; each inner-decline record
had that flag set. The tick trace reports aggregate `TestWorld->Tick()` only: spawn p95/max was
2.262/5.230 ms and walk p95/max was 2.512/28.893 ms; no subsystem marker names the walk
maximum. The nine-tile ring and full LOD0 are faster than S6. Tile output hashes did not change, so
the recipe version remains unchanged. Null-RHI runs do not prove player-visible enclosure. See
[`Standalone/Research/WorldAssemblerM3/Round11/REPORT.md`](Standalone/Research/WorldAssemblerM3/Round11/REPORT.md)
for the audit counts, RMC review, quality matrix, commands and binary hashes.
## 9. Multiplayer model (listen-server first, dedicated-friendly)
> **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a