Files
Fr0zkaandClaude Opus 5.5 b1cfaf53b8 wip(assembler): round L11 -- fix 24 m+ crossing crash; near ring ready 2x sooner at spawn
Crash: crossing tiles were coalesced into one huge rectangle, and BuildSpatialBins tried to
resize to ~4.3e17 bytes (std::bad_alloc; the L7 32 m failure had the same cause). Analytic
regions now skip unread dense bins and compile in world-aligned 128-voxel pages, published
together; out-of-range keys error before allocation. 24 m, 32 m and 1 km crossings pass
(1 km: 72 pages, peak RSS 700 MiB).
Spawn: World::isTileShapeFree proves LOD0 SurfaceWorld tiles that never read the cave shape;
those start before shape compile, and all others wait. Reviewer W12 no-hash: near ring 0.337 ->
0.159 s, whole view +2.1%. All SurfaceWorld/FloatingIslands hashes unchanged (W1/W8/W12, both
compilers, forced-overlap run). Unreal VoxelWorld.cpp adapter changed but NOT built here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
2026-10-20 00:28:58 +02:00

8.8 KiB
Raw Permalink Blame History

VoxelForge L11 — Linux report

24 m crossing crash

The crash is real. The original binary threw std::bad_alloc after base shape compilation with both 6 GiB and 16 GiB RLIMIT_AS; the 16 GiB run peaked at 476,128 KiB RSS and 1,542,284 KiB virtual size. The allocation trace identifies a 432,345,551,342,665,824-byte resize in BuildSpatialBins, reached from BuildAnalyticRegion during World::compileTileRegion. The crossing code had coalesced disjoint entering tiles into a large LOD rectangle and tried to allocate dense per-axis bins that the analytic fallback never reads. L10's 263.57 MiB figure is the proxies' reserved virtual capacity; RSS samples record resident usage separately.

Analytic regions now skip those dense bins and compile in world-aligned 128-voxel pages. Pages are built privately and published together; coordinate, bad_alloc, and size failures return errors before partial pages are published. On the final L11 binary, 24 m and 32 m both passed (16 pages each); 1,000 m passed with 2,260 entering keys, 72 pages, 63 ms region-build wall time, 700,232 KiB peak RSS, and 2,578,588 KiB peak virtual size. The fixed 24 m run peaked at 533,420 KiB RSS and 2,578,588 KiB virtual size. Crossing pages build no packed interval nodes (lattice_nodes=0) and do not expand the immutable base sign/proxy arrays; they add analytic primitive/bin data. retainTileRegions prunes pages outside the desired set. The largest empirically verified step is 1,000 m. Work follows the destination's entering tile set; once the old and new views are disjoint, its page count stays bounded by the active view as travel distance increases. Keys outside Core's signed 32-bit voxel range are rejected with tile region coordinate is out of range before page construction. The earlier L7 32 m / 160-million-node failure was the same unbounded region geometry.

Step Entering keys Pages Added interval nodes Base proxy nodes / virtual capacity
24 m 432 16 0 185,024 / 263.57 MiB
1,000 m 2,260 72 0 185,024 / 263.57 MiB

Shape readiness

isTileShapeFree accepts only LOD0 tiles whose padded mesh box maps to one valid SurfaceWorld plan, excludes FloatingIslands, plan features, routes, passages, edits, and overlaps with owner shape primitives or detail bounds. This covers the MC skirt and sample margin. Higher LODs are conservatively shape-dependent; the accepted path cannot use far parents. The native-only context is initialized without building sign arrays, proxies, owner primitive vectors, or spatial bins. Native sampling and classification paths check the padded bounds, reject unsupported fallbacks, and count any attempted shape-dependent read; tile generation then fails instead of returning a partial mesh.

The SurfaceWorld spawn view proved 27/27 near-ring tiles shape-free. A deliberately mis-marked distant tile (0,0,100000,lod0) reached the sampler and was rejected by the guard. After shape publication, native-only and full-shape generation matched all four hashes for all 27 tiles. Shape-dependent tiles waited for the release-published shape; tile jobs continue through the existing Epoch and MPSC result path, and published shape/page data is immutable. The FloatingIslands spawn proof accepted 0/27 tiles, so all waited for its shape. Slow-shape delay defaults to zero; audit and negative-control work is opt-in.

No-hash timing pairs

Five interleaved W12 pairs, camera 0,-35,-487.94, seed 0, Clang -ffp-contract=off, hashing off. Times are from process start. The L9 executable does not emit a first-tile completion event and starts tile workers only after shape compilation; its first-tile cell is therefore unavailable.

Pair First tile L9 / L11 Near ring L9 / L11 Whole view L9 / L11
1 — / 153 ms 343 / 166 ms 1,439 / 1,429 ms
2 — / 160 ms 424 / 173 ms 1,526 / 1,447 ms
3 — / 145 ms 347 / 163 ms 1,447 / 1,443 ms
4 — / 146 ms 353 / 170 ms 1,457 / 1,449 ms
5 — / 153 ms 376 / 173 ms 1,478 / 1,464 ms
Median — / 153 ms 353 / 170 ms 1,457 / 1,447 ms

The delayed maximum-overlap W12 run held shape compilation for 1.5 s: first tile 133 ms, near ring 166 ms, shape ready 1.696 s. Shape-free and full-shape near-ring hashes matched, and whole-view, full-detail, per-LOD, and exact-query hashes matched the baseline.

Hashes

All 28 configurations passed against L10's committed b370908 baseline: SurfaceWorld and FloatingIslands, seed 0, W1/W8/W12, Clang -ffp-contract=off and GCC default, with five W12 repeats per compiler and recipe. Each run matched all 12 rows: full-detail, whole-view, nine LODs, and exact queries. The delayed-overlap run also matched.

Recipe Full-detail density / mesh / payload / topology Whole-view density / mesh / payload / topology Exact-query density & unclamped / detail / collision
SurfaceWorld 0x0ee0579bd15cbbe4 / 0x32e5d4964d610fee / 0x1932b96ee4b1d910 / 0x69743c6bad3447dc 0xc10697a8f48380c7 / 0xe75c9119a1498cd5 / 0x49a9a8d1835a6c8c / 0xedcb23288b5e8724 0xe4d4a8eb95b35933 / 0xefcba20c0c70c415 / 0xe604823a249029bf
FloatingIslands 0x6d842d2878b08ef6 / 0x4b868a3184f08035 / 0x2cedf2236788752d / 0xeac17d5e0cb5044a 0x5be4d8cde79b2a76 / 0x96f75041f6861b3a / 0x3700a516cc89d50c / 0x6b99948d6eab4f21 0x61daded9c303becb / 0xefcba20c0c70c415 / 0x84d37463be9b0573

The per-LOD D/M/P/T rows match exactly in every configuration; the complete comparison is recorded in Saved/Linux/l11_hash_audit.txt.

Failed and retried runs

  • The 24 m reproduction failed with std::bad_alloc under both address-space caps (l11-cross24-as6g.log, l11-cross24-as16g.log); the allocation trace above isolated the oversized bin resize. Final-binary 24 m, 32 m, and 1,000 m reruns passed (l11-final-cross-24m.log, l11-final-cross-32m.log, l11-final-cross-1000m.log).
  • The first readiness proof admitted 512 LOD0 keys (l11-readiness-audit.log, l11-readiness-audit2.log). Although the 27 near-ring equivalence check passed, distant owner-shape tiles changed the whole-view density hash. Tightening the proof to test owner primitive bounds reduced early admission to the 27 safe near-ring tiles and restored every hash (l11-readiness-audit-final.log).
  • One rebuilt audit invocation omitted the owner JSON path and exited 2 before generation (l11-readiness-audit-path-error.log; Standalone/bin/data/owner_world.json was missing). It was rerun with Standalone/data/owner_world.json and passed.
  • The required hash matrix and five timing pairs had no failed runs. All monitored run processes stayed below the 6,000,000,000-byte RSS ceiling; governor remained powersave.

The standalone Core/headless builds passed with Clang and GCC. The Unreal adapter change was source-reviewed but could not be built here because UnrealBuildTool/UnrealEditor are not installed on this Linux machine.

At spawn on SurfaceWorld, the near ring is ready at a 170 ms median versus 353 ms on L9, and the first L11 tile is ready at 153 ms median before shape publication; L9 gates tile work on shape compilation and does not report that milestone. For fast movement, destination-page work stays bounded through the tested 1 km crossing, while invalid Core coordinates return an error before allocation.

Orchestrator review (Claude)

  • Re-run by me, 5 interleaved W12 pairs, no hashing. Near ring (neighborhood_ready) L9 0.338/0.338/0.337/0.326/0.334 s vs L11 0.159/0.159/0.167/0.169/0.159 s (median 0.337 → 0.159 s, −53%). Whole view 1.337 → 1.365 s median (+2.1%, 4 of 5 pairs slower): a small cost, accepted for a 2× faster spawn ring. W12 hash runs give density c10697a8f48380c7.
  • Crossings re-run by me on L11 (--region-crossing --camera-step=S,0,0): 24 m gives 432 tiles, 0.867 s wall, 10.05 worker-s, rc 0; 1,000 m gives 2,260 tiles, 5.90 s wall, 70.4 worker-s, rc 0. The L9 binary lacks the option, so the original crash rests on Luna's reproduction and allocation trace.
  • New finding, for L12: the 24 m crossing costs ~23 ms of work per tile, against ~2 ms for L10's 16 m crossing (288 tiles, 0.633 worker-s). Tiles outside the base shape bounds, served from analytic region pages, look about 10× more expensive. That is a moving-player cost.
  • Concurrency read: World::compile builds the shape privately, then sets impl_->shape and buildRequest before a release store of shapeReady. Every shape reader acquire-checks shapeReady first (short-circuit), and shape-free tiles use a private native-only context. Sound.
  • Unreal: Source/VoxelForge/Private/VoxelWorld.cpp changed (compile outside the writer lock; proven shape-free tiles start before bReady). It is NOT compiled here, so it must be built on the owner's PC before it is trusted in game.