Files
VoxelForge/Standalone/Research/WorldAssemblerM3/Round17/REPORT.md
T
Fr0zkaandClaude Opus 5.5 87b0e5d8cd wip(assembler): round S13 -- tile cost counted per LOD (diagnostics only, no output change)
Exclusive per-stage tile timers and probes. Findings: LOD0 is MC-exclusive
(10.4 of 14 ms p95, 243 ns/sample); LOD4's 3x cost is column-cache misses
(8,224 vs ~1,600-1,900 at LOD3/5), cap refine secondary; per-tile feature lists
cost ~0.001 ms; cross-worker column repeats common (277k at LOD0). Global octave
drop and stride-2 interpolation probes fail the 2 cm gate. Worker cap 1 kept
(cap 0 worse 40 m/s tick tail). Report: Standalone/Research/WorldAssemblerM3/Round17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
2026-10-19 15:40:25 +02:00

278 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# World Assembler S13 — Round 17
**2026-10-19 · What does a tile actually cost?**
## Result
No generation optimization shipped. The measurements show that owner-plan tile time is mostly
column evaluation plus MC, that LOD4 is dominated by column-cache misses, and that cross-worker XY
reuse is common. The interpolation and global octave-drop probes fail the 2 cm quality bar. A shared
column page still needs a measured candidate with bounded memory, synchronization, and lifecycle
proof, so I did not add one. Density, mesh, collision, and recipe output are unchanged; no recipe
version bump is needed.
The current BuildHost 40 m/s trace passed the support gate **75/75**. It reported a 4.825 s
initial-set ready time, 28.760 worker-seconds, and a 0.510 s stationary ring. The last initial LOD0
tile applied at 1.146 s. At 40 m/s, `UWorld::Tick` was 3.133/5.426 ms p95/max at spawn and
30.314/43.142 ms while walking. The worker-second and full-set targets passed in this trace; the
LOD0 and strict S12 tick-tail targets did not. Startup-clock measurements and worker-task intervals
use different origins from the S12 actor report, so I do not treat 4.825 s as an apples-to-apples
replacement for S12's 4.911 s.
## 1. Exclusive tile cost on the owner plan
The BuildHost audit measures eight cold, no-hash tiles per requested LOD, with a separate hashed
full-feature-list versus per-tile-list parity check (`hashes_identical=1`). The table gives p95
stage time and the p95 of each tile's `stage time / density samples`, in `ms / ns per sample`.
Percentiles are computed independently, so stage p95 values do not add to the full-tile p95. The
sample denominator includes the normal halo: LOD0–1 are 32 MC cells / 35³ = 42,875 samples;
LOD3–8 are 16 coarse cells / 19³ = 6,859 samples. The owner configuration is not a 32³ lattice at
every LOD.
| LOD | Full scan → per-tile list p95 (ms) | Classify excl. | MC column eval. | MC excl. | Ground column eval. | Ground excl. | Cap column eval. | Cap excl. | Mesh assembly |
|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| 0 | 13.806 → 14.160 | 0.0064 / 0.15 | 3.708 / 86.48 | 10.436 / 243.40 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0.0251 / 0.59 |
| 1 | 9.616 → 9.584 | 0.0821 / 0.02 | 4.096 / 95.52 | 7.191 / 167.72 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0.0314 / 0.73 |
| 3 | 6.952 → 6.878 | 0.0286 / 9.90 | 3.989 / 1,090.54 | 2.879 / 419.80 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0.0075 / 2.97 |
| 4 | 36.445 → 35.790 | 0.0015 / 0.22 | 29.361 / 4,280.71 | 2.948 / 429.84 | 0 / 0 | 0.0001 / 0.01 | 4.865 / 709.35 | 1.259 / 183.61 | 0.0231 / 3.37 |
| 5 | 10.586 → 10.420 | 0.0010 / 0.15 | 2.958 / 431.27 | 1.195 / 174.25 | 4.894 / 713.57 | 2.234 / 325.63 | 0.405 / 59.06 | 0.241 / 35.08 | 0.0054 / 0.79 |
| 8 | 14.778 → 14.507 | 0.0046 / 0.67 | 5.671 / 826.84 | 2.366 / 345.01 | 1.775 / 258.74 | 1.652 / 240.87 | 1.823 / 265.78 | 1.348 / 196.50 | 0.0252 / 3.67 |
The paired full/candidate timings are the feature-query negative control: the full plan scans feature
bounds; the candidate builds the per-tile feature lists used by Core. All tested hashes matched.
Their timing differences are small and mixed in sign. Feature-list p95 itself was only 0.0009–
0.0015 ms on this owner sample.
There is no separate volumetric 3D density-noise stage in native SurfaceWorld. Its surface recipe
does call `FractalNoiseQ16`/Perlin while evaluating 2D XY columns, so that work is included in
column evaluation. Material/color assignment is not a separate timer; it is included in meshing and
the final mesh/payload path. The current route has no tunnel, room, shaft, or route-density stage.
Classification-exclusive time subtracts column work done while classifying; MC/ground/cap column
time is reported beside the corresponding non-column exclusive time. The `horizon_p95_ms` counter
wraps ground and cap refinement and is inclusive, so it is not another exclusive stage. Far-shell
repair was negligible in this sample (at most 0.0003 ms p95).
### Why LOD4 is expensive
LOD4 full tile p95 is 35.790 ms, versus 6.878 ms at LOD3 and 10.420 ms at LOD5. LOD4 is the first
cap-refined level: it samples cap at 16 voxels (4 m); LOD3 has no cap refinement, and LOD5 cap uses
the 128-voxel (32 m) boundary grid. Ground refinement starts at LOD5. LOD4's cap work costs
4.865 ms of column evaluation and 1.259 ms exclusive at p95.
That cap difference does not explain most of the spike. LOD4 MC performs 29.361 ms p95 of column
evaluation, and its sampled tile has 8,224 column misses versus 1,634 at LOD3 and 1,916 at LOD5.
Thus the measured anomaly is primarily poor column reuse at LOD4, with cap refinement adding a
smaller separate cost. The data does not isolate why the thread-local direct-mapped cache misses
rise at LOD4; tile extent, halo/normal probes, and XY spread all differ with LOD.
## 2. Do far tiles evaluate detail they cannot show?
The LOD8 tile p95 spends 9.203 ms in column evaluation out of 14.507 ms. There is no volumetric
cave-noise loop in this path, but the 2D heightfield recipe still evaluates its full configured
noise octaves at every requested XY column. A measurement-only probe dropped the highest one or two
octaves globally on two LOD8 tiles. It reported 26.50% / 40.31% less column time, but ground-height
p95 errors of 7,545 / 7,581 cm and cap-height errors of 2,427 / 7,859 cm; all 578 cap samples were
over 2 cm. Near LOD0–1 the same probe was slower than baseline, so its timing is not a reliable
frequency cost estimate. It also renormalizes the remaining octaves and does not implement a
per-recipe wavelength cutoff at half the MC cell size.
This rejects global octave removal. A frequency-aware cutoff could change appearance while keeping
determinism, but this probe does not justify a cutoff or quantify its screen-space result. It was
measurement-only and did not affect tile hashes.
## 3. Per-tile feature lists and 2D cache reuse
Core's `World::generateTile` builds bounded per-tile query lists for overlapping
`Heightfield2D` strata and `FloatingIslandFieldV1` plans, including the normal halo. Those two
WorldAssembler families share the scoped feature-list route. The legacy Unreal `UVoxelGenerator`
room/tunnel/shaft families use their own room, tunnel, and generator caches; Round 1's Core tile list
is not one shared list for all legacy generator families. On the owner SurfaceWorld tiles, list-build
time is under 0.002 ms p95.
`Heightfield2D::point(x,y,z)` may be called at many Z/normal samples, but the thread-local XY cache
normally evaluates a column once per worker-cache miss. The cold BuildHost sample shows:
| LOD | Field-point calls p95 | Column misses p95 | Approx. hit rate |
|---:|---:|---:|---:|
| 0 | 44,100 | 2,244 | 94.9% |
| 1 | 44,100 | 2,289 | 94.8% |
| 3 | 4,191 | 1,634 | 61.0% |
| 4 | 9,405 | 8,224 | 12.6% |
| 5 | 8,197 | 1,916 | 76.6% |
| 8 | 4,441 | 2,708 | 39.0% |
The Standalone W8 capture recorded all tiles and all LODs. `repeated` means an XY coordinate was
re-evaluated by a different same-LOD tile; the neighbor count includes any prior adjacent tile,
and the cross-worker column counts a neighbor repeat observed on a different worker.
| LOD | Tiles | Unique XY | Repeated tile evaluations | Neighbor repeats | Cross-worker neighbor repeats |
|---:|---:|---:|---:|---:|---:|
| 0 | 512 | 128,240 | 347,280 | 280,233 | 276,658 |
| 1 | 448 | 103,302 | 300,242 | 249,825 | 244,941 |
| 2 | 448 | 32,274 | 102,713 | 84,318 | 79,402 |
| 3 | 384 | 33,279 | 87,158 | 81,680 | 78,272 |
| 4 | 320 | 91,325 | 230,492 | 229,811 | 202,684 |
| 5 | 144 | 71,950 | 118,630 | 118,580 | 112,191 |
| 6 | 96 | 280,313 | 63,523 | 63,523 | 63,435 |
| 7 | 48 | 71,529 | 7,780 | 7,780 | 7,138 |
| 8 | 153 | 106,219 | 32,879 | 32,879 | 30,956 |
This makes immutable shared column pages worth a candidate A/B, especially for LOD0–5. The counts
do not measure synchronization, page compilation, eviction, memory, or resulting wall-time savings.
No page cache was added on counts alone. The column capture is opt-in, worker-local, and read back
after generation.
## 4. Coarse-lattice interpolation probe
The measurement-only probe evaluated exact ground/cap columns at every MC XY sample, then sampled
every second MC sample and bilinearly interpolated the rest. No near-zero or thin-feature fallback
was included, so this is a lower-cost bound and a failure screen, not a production candidate. The
table is two tiles per LOD. Errors are p95 over sampled columns; counts over 2 cm are explicit.
| LOD | Measured column-compute time saved at stride 2 | Ground p95 / >2 cm | Cap p95 / >2 cm |
|---:|---:|---:|---:|
| 0 | 73.48% | 0.78 cm / 0 of 2,178 | 1.32 cm / 25 of 2,178 |
| 1 | 73.96% | 2.54 cm / 236 of 2,178 | 3.00 cm / 375 of 2,178 |
| 3 | 71.99% | 388.62 cm / 405 of 578 | 37.70 cm / 397 of 578 |
| 4 | 71.39% | 702.29 cm / 403 of 578 | 68.92 cm / 401 of 578 |
| 5 | 71.27% | 1,248.85 cm / 411 of 578 | 87.74 cm / 395 of 578 |
| 8 | 72.81% | 4,369.95 cm / 414 of 578 | 2,201.17 cm / 415 of 578 |
At stride two, exact column evaluations fell from 2,178 to 578 at LOD0–1 and from 578 to 162 at
LOD3–8. The measured column-compute time reduction is shown above; it was 71–74%. Stride four
reduced exact evaluations from 2,178 to 162 at LOD0–1 and from 578 to 50 at LOD3–8, roughly
90–92%. It already gives LOD0 ground/cap p95 errors of 2.72/2.09 cm (293/2,178 and 123/2,178
samples over 2 cm). At LOD1, stride two fails the
2 cm threshold in both fields; coarse LOD errors are much larger. A near-zero exact fallback could
recover surface crossings, but it was not simulated here and could erase much of the nominal saving.
This probe does not exercise volumetric tunnel/wall thickness or the arrival-to-departure route
law. No interpolation was enabled; exact sampled outputs, walkability, and route behavior are
unchanged.
## 5. S12 worker-cap follow-up and current trace
The matched cap comparison kept `voxel.StreamingReadyQueueDepth=8` and changed only
`voxel.StreamingBackgroundWorkerCap`. Both controls were recorded in the JSONL trace, as were the
effective slots and the eight actual background worker count.
| Speed | Background cap | Effective slots / actual tile workers | Initial set ready | Spawn tick p95/max | Walk tick p95/max | Ring |
|---:|---:|---:|---:|---:|---:|---:|
| 6 m/s | 0 | 10 / 8 | 4.669 s | 3.483 / 5.184 ms | 2.708 / 28.581 ms | — |
| 6 m/s | 1 | 8 / 8 | 4.745 s | 3.283 / 14.945 ms | 2.696 / 28.535 ms | — |
| 40 m/s | 0 | 10 / 8 | 4.633 s | 3.455 / 5.468 ms | 32.345 / 55.115 ms | 75/75 |
| 40 m/s | 1, matched | 8 / 8 | 4.712 s | 3.031 / 5.364 ms | 30.541 / 41.598 ms | 75/75 |
| 40 m/s | 1, final build | 8 / 8 | 4.825 s | 3.133 / 5.426 ms | 30.314 / 43.142 ms | 75/75 |
Cap 0 gained only 76 ms at 6 m/s and about 79 ms in the matched 40 m/s pair, while its 40 m/s
walk tick tail was substantially worse. The final cap-1 run confirms the ring at 75/75 gates. Keep
cap 1 as the default; the latest tick p95/max is still 0.752/3.915 ms above Round 16's 29.562 /
39.227 ms 40 m/s result, so the strict tick-tail target is not claimed.
On the final cap-1 40 m/s trace, the initial set contains 2,578 tiles and totals 28.760 worker-
seconds. The worker-seconds by LOD were: LOD0 3.81, LOD1 3.94, LOD2 0.81, LOD3 2.36, LOD4 7.73,
LOD5 1.68, LOD6 2.17, LOD7 1.97, LOD8 4.31. The ring was ready at 0.510 s; full initial set
ready at 4.825 s. LOD0's last worker ended at 1.131 s and its last apply was at 1.146 s.
## Builds, quality, determinism, and files
Build commands:
```powershell
.\Tools\BuildVoxelForgeCoreHost.ps1 -MaxParallelActions 12 -TimeoutSeconds 900
.\Standalone\Build.ps1 -Target All -CompileWorkers 12 -BuildPriority Normal
```
Measurement commands (each was a fresh process; BuildHost runs were serial, Normal priority, and
limited to the 12-CPU affinity):
```powershell
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -SharedFeatureAuditOnly -CoreVoxelSizeCm 25 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 600 -StreamingReadyQueueDepth 8 -StreamingBackgroundWorkerCap 0 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 600 -StreamingReadyQueueDepth 8 -StreamingBackgroundWorkerCap 1 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 4000 -StreamingReadyQueueDepth 8 -StreamingBackgroundWorkerCap 0 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 4000 -StreamingReadyQueueDepth 8 -StreamingBackgroundWorkerCap 1 -TimeoutSeconds 900
```
The final Standalone commands used the same options for each worker count; W8 also enabled the
all-LOD overlap capture:
```powershell
.\Standalone\bin\VoxelForgeCoreStandalone.exe --workers=1 --world-assembler-prototype --stream-benchmark --stream-pipeline --stream-hash --force-generate --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --far-horizon-lod8-step=0 --process-priority=Normal --process-affinity=4095 --frame-limit=60
.\Standalone\bin\VoxelForgeCoreStandalone.exe --workers=8 --world-assembler-prototype --stream-benchmark --stream-pipeline --stream-hash --profile-column-overlap --force-generate --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --far-horizon-lod8-step=0 --process-priority=Normal --process-affinity=4095 --frame-limit=60
.\Standalone\bin\VoxelForgeCoreStandalone.exe --workers=12 --world-assembler-prototype --stream-benchmark --stream-pipeline --stream-hash --force-generate --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --far-horizon-lod8-step=0 --process-priority=Normal --process-affinity=4095 --frame-limit=60
```
Current-build compatibility gates:
```powershell
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -CoreOffTest -CoreVoxelSizeCm 12.5 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -HybridFixturesOnly -CoreMesherRecipe WorldAssemblerHybridV1 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -HybridFixturesOnly -CoreMesherRecipe WorldAssemblerHybridV2 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -FloatingIslandsOnly -CoreVoxelSizeCm 25 -TimeoutSeconds 900
```
The current BuildHost SurfaceWorld actor run passed and reported stable W1/W8/W16 hashes:
density/mesh/payload/topology `0cd2a1a4152ee4aa / a06243aee16b65d5 / c700162935c262d7 /
0af901aa42b452bc`. The current BuildHost cap audit passed S4 agreement (128 points, max 0.0504 cm
against 2 cm) and 1,584/1,584 cap rays (100%). The BuildHost actor run also passed S3 far-horizon
agreement at max 0.0427 cm against 2 cm. Current Standalone W1/W8/W12 each matched full-view hashes:
density/mesh/payload/topology `c10697a8f48380c7 / e75c9119a1498cd5 / 49a9a8d1835a6c8c /
edcb23288b5e8724`. W8's final full-view pipeline was 3.518 s for 2,553/2,553 tiles.
The current-build Core-off gate passed `legacy_unchanged=PASS` at 12.5 cm. M2 Hybrid V1 and V2
each passed all five analytic fixtures and the border suite at 25 cm. FloatingIslands passed at
25 cm with stable W1/W8/W16 hashes `4dcfb37585d7d986 / f14f76fbf065c6ed / 53035c45fd1a082e /
23550100654a4ec3`; its island and span capsule contacts, content anchors, and carve/fill checks
passed. These runs used the current BuildHost binaries. No density output changed, so paired
near/far captures and a recipe bump were not required. No runtime CVar or behavior change was
introduced; the cap-0 negative controls above were trace-read back.
Full BuildHost logs are under
`Saved/BuildHost/HostProject/Plugins/VoxelForge/Saved/CoreWorldValidation/Logs/` with stems
`CoreWorldValidation-core_off_voxel_12.5_applyboost_default_legacy_order_nearreserve_auto`,
`CoreWorldValidation-owner_surface_applyboost_default_legacy_order_nearreserve_auto_worldassemblerhybridv1`,
`CoreWorldValidation-owner_surface_applyboost_default_legacy_order_nearreserve_auto_worldassemblerhybridv2`,
and `CoreWorldValidation-floating_islands_applyboost_default_legacy_order_nearreserve_auto`.
Current binary SHA-256:
| Binary | SHA-256 |
|---|---|
| BuildHost `UnrealEditor-VoxelForge.dll` | `811F83D43EDE47EE870DD608F25C9F902422E1B264901917726CDBECE0EC0A37` |
| BuildHost `UnrealEditor-VoxelForgeCore.dll` | `AA74A8BC8E1C9C59FB7EA94D5A6BA71341EF8208EDE2458A7B5FAC0045FF2520` |
| BuildHost `UnrealEditor-VoxelForgeEditor.dll` | `A851836ED435E8795AF860EBFB49AD84B603FF0D94600637D9B8CE52E0710EE3` |
| Standalone `VoxelForgeCoreStandalone.exe` | `704F433791AF0D483D082CCBB2043DB4194323F40A986DA72F338FC424BF82F3` |
Round 17 changed these existing source files; no new source file was added:
| File | Likely compile spot |
|---|---|
| `Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h`, `Standalone/Core/src/VoxelForgeCore.cpp`, `Standalone/Core/src/WorldAssemblerInternal.h` | BuildHost VoxelForgeCore and Standalone Core |
| `Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp` | BuildHost VoxelForgeEditor commandlet |
| `Standalone/Source/StreamingWorld.h`, `Standalone/Source/StandaloneViewer.cpp` | Standalone viewer executable |
Async-safety review: no actor queues or worker mutation code changed. `Epoch`, MPSC queue modes,
`EndPlay` draining, worker `bShuttingDown` checks, `ModsLock`, and shared region-page lifetime are
unchanged. Coordinate capture is opt-in thread-local diagnostic data copied into each tile result;
UObject and RMC mutation remains on the game thread. RealtimeMeshComponent was read-only. No
`.uproject`, owner editor, `.uasset`, generated header, `Binaries/`, or `Intermediate/` source was
edited. Files over 300 KB are under `Saved/BuildHost/`. No `0xC0000142` occurred and no new
`UECC-*` crash folder appeared.
The first BuildHost compile failed because two probe lambdas omitted the configured tile-size
capture; that was fixed and the next build passed. The Standalone build passed with the existing
`vswhere.exe` lookup warning and `VoxelStrateManager.cpp` C4838 warnings. A final source check and
both current targets passed. No commit, push, or stash.
### Dialogue
**User:** Shouldn't a tile be way cheaper? Measure the stages and explain LOD4 before choosing a fix.
**Codex:** The largest measured cost is XY column evaluation, and LOD4 has a sharp spike in cache
misses plus its first fine cap pass. Two-step interpolation reduced measured column-compute time by
roughly 72%, but moved far ground by tens of meters; global octave dropping also fails badly.
Cross-worker column
reuse is a strong candidate for a future measured page-cache experiment, so Round 17 keeps output
unchanged and records the floor instead of shipping an unproven approximation.