Files
VoxelForge/Standalone/Research/WorldAssemblerM3/Linux60/REPORT.md
T
Fr0zkaandClaude Opus 5.5 c0c204ba50 fix: signed-overflow UB in VoxelHash made cave placement compiler-dependent; all generators LOD0-8; speed matrix (L60)
VoxelHash::Cell/Pair, legacy Cellular3D and the shared Core cellular hash added 0x7FFFFFFF to a signed
int32 before casting to uint32; overflow is undefined, so Clang and GCC placed cave passage 1 differently
(first differing sample at LOD1 tile (0,64,-512)). The bias is now done in unsigned arithmetic, in both the
standalone copy and the Unreal plugin header (Source/VoxelForge/Public/VoxelCaveMorphology.h). Native
caves, motifs and lattice now stream LOD0-8 with skirts and cross-level transitions. New verify.py
--speed-matrix runs every generator on one asserted-identical view (5 interleaved runs): motifs 1.33,
profiles 1.30-1.32, caves 1.41, natural SW 1.67, lattice 1.70, FI 4.93 s whole view. fast/medium/full PASS,
Clang/GCC peer hashes equal. Research R29 (open-edge ledge) and R30 (compiler-divergence suspects).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-26 01:50:15 +01:00

6.7 KiB
Raw Blame History

Linux 60 report

Result and root cause

The continuation is green. The full tier and speed matrix passed after fixing signed-overflow undefined behavior in the shared voxel hash.

The first differing density was LOD1 tile (0,64,-512), step 2, sample order 23,658 at (64,82,-476). Before the fix, Clang returned -4.0078125 (0xc0804000) and GCC returned -4.125 (0xc0840000). The sample follows the mesher's LOD X8/boundary scalar path into DensityAtLattice and the compiled analytic TunnelNetwork field. Its upstream cave passage placement comes from UVoxelStrateManager::GeneratePassages → PassageRandom01 → VoxelHash::Cell(i,c,seed).

VoxelHash::Cell added 0x7fffffff to a signed cell coordinate before casting to uint32; connection c=1 overflowed int32, so the compiler-dependent hash changed the passage coordinates and cave geometry. The trace showed passage pair (0,1) diverging, followed by all 21 control points of passage 1. The fix performs the bias in unsigned arithmetic in Cell, Pair, legacy Cellular3D, and the shared Core cellular hash. It does not quantize density. After the fix both compilers produced the same pair (0,1) coordinates, the same 44 passage points, and 0xc0804000 at the first sample. The evaluator version is now 211.

Before/after traces are in Saved/Linux/l60/diag/caves-{clang,gcc}-probe.log and Saved/Linux/l60/diag/cache/fullcache-{clang,gcc}.log.

Far LOD support and seam checks

All full-view fixtures use max level -1, clip radius 3, and max clip level 8. All three generators streamed LOD0–8 with skirts.

  • Caves: X8 and Z-run gates detect caves in manager and resolved stratum mappings, then use scalar lattice sampling for cave samples. Clang/GCC cave tile hashes match at W1/W8/W12, including random views at LOD1–2. The cave-mouth transition crossed levels 0–8 (432 entering tiles).
  • Motifs: The fixed motif field streams through the existing rings, caps, and skirts. The monolith transition crossed levels 0–8 (295 entering tiles); whole-surface foot, cap, and platform-edge audits passed.
  • Lattice: The fixture selects native lattice stratum 1 and uses the existing ring and skirt path. The helix transition crossed levels 0–8 (380 entering tiles); field, interval, reachability, and shape-free audits passed.

Hard, gradient, and interleaved stratum transitions passed with zero hole columns and zero air samples. Far-LOD cave, motif, and lattice JPEG captures passed their audits and are under 300 KB.

Comparable speed matrix

verify.py full --speed-matrix ran five interleaved repetitions per generator, rotating generator order. Every run used camera (-12,-118,-20 m), rotation (0°,0°), clip radius 3, max clip level 8, max level -1, camera step 24 m, 12 workers, and shared column cache. The stage asserted the shared HeadlessConfig fields, whole-view and per-LOD tile-count ranges, and stable counts across repetitions. It passed for all eight generators. Every listed tile count occurred in all five runs; timings are medians from the final full run.

Generator Tiles Whole view (s) First tile (s) Near ring (s) Moving p50 (ms) Moving p95 (ms)
Natural SW 2,130 1.669 0.502 0.519 63.971 119.891
periodicHills 1,986 1.311 0.454 0.479 60.267 115.648
mirroredRelief 1,986 1.317 0.471 0.490 60.568 115.474
flatField 1,986 1.303 0.465 0.485 60.627 117.144
Floating Islands 2,130 4.926 0.817 0.977 528.939 871.290
Caves 2,130 1.408 0.489 0.507 61.099 118.508
Motifs 1,986 1.326 0.476 0.493 60.107 116.104
Lattice 2,130 1.701 0.539 0.561 69.572 137.585

Full run records and common config are in Saved/Linux/l60/speed-matrix.json.

Captures

  • Saved/Linux/l60/captures/caves-far-lod.jpg — 16,642 bytes; SHA-256 55f8d3d2de76541e9f1c1d09b771af40bfbb37b1ce5afc0b2f70419b84ed71a3
  • Saved/Linux/l60/captures/motifs-far-lod.jpg — 24,926 bytes; SHA-256 bfc5d8ddebc3706fedd6108653b4777fac4bac44e71f60c8e58b3508c14c3aa6
  • Saved/Linux/l60/captures/lattice-far-lod.jpg — 30,714 bytes; SHA-256 c261728e48975f01c712e31594b4284eede289b73e793e6204b78cb8ff2a2947

Hashes and tiers

Clang 19 and GCC 14 builds used FP contraction off and 12 jobs. Final source stamp and source-manifest SHA-256: 902a0d8e45f895d1b53c0a86999adee5598ff77e1752aa7ea90397520cf7b82d.

  • Clang headless: dcf6286bcecac382f9cce2458e7c327485e9b8b993c3d544e627a8fc964947fa
  • GCC headless: be86f90f993ba27bfde235e0850ea783298396dc03456360cf4bb3fd4815988b
  • Offscreen capture: b8f7a441c0a41be6ebd61721ff903832a51291df7a886e669b9084d84441d4c5
Tier Status Checks / runs Elapsed
Fast PASS before hash fix 453 / 113 335.7 s
Medium PASS before hash fix 968 / 289 1,559.1 s
Full continuation trial 1 FAIL: stale binary stamp; no checks ran — 0.0 s
Full continuation trial 2 PASS on source stamp 914af3... 2,304 / 660 3,256.9 s
Full continuation trial 3 PASS on final source stamp, including speed matrix 2,304 / 660 3,260.6 s

Fast and medium were not rerun as standalone tiers after the hash change; the passing full run executed their checks at the current source stamp.

Trials and failed runs

The prior L60 work used 15 trials. This continuation used 3 of its 10-trial budget. Trial 1 stopped before checks because it found stale Clang/GCC executables. Trial 2 passed, then the final source review restored the mesher's existing <bit> include and changed the source stamp, so trial 3 rebuilt and reran full against the exact final source. The pre-fix trial 15 had failed peer determinism on the same six LOD1 and eight LOD2 tile keys across four seeded views. Trial 3 had no failed checks; negative controls reported expected failures.

Orchestrator review (Claude)

Accepted. The stamp matches the final Clang and GCC builds (902a0d8e…); full PASS, including the speed matrix. The root cause is a real cross-platform determinism bug: VoxelHash::Cell/Pair/Cellular3D added 0x7FFFFFFF to a signed int32 (signed overflow is undefined behaviour), so passage placement became compiler-dependent. The same code lives in the Unreal plugin (Source/VoxelForge/Public/VoxelCaveMorphology.h, identical to the standalone copy before the fix), and only the standalone copy had been fixed. I applied the identical fix to the plugin header and confirmed the two copies match. MSVC typically wraps there, so the owner's existing worlds are most likely unchanged, but this needs his Windows build. No other signed-bias pattern remains in Source/ or Core. The comparable speed matrix is now a standing verify.py full --speed-matrix stage.