CoreNamespacePreamble.h applies per-compiler contraction-off pragmas (Clang, MSVC, GCC; unknown compiler = #error) and a fused-multiply-add self-test that World::compile runs before building. Clang -ffp-contract=on and GCC's default (fast) now reproduce the L1/Windows hashes (they diverged in L2); Clang -ffp-contract=fast ignores the pragma and is refused at startup (negative control). Seed sweep with every cube on real terrain: 48 seeds x 2 recipes x 3 builds x W1/W12, 576 runs, 0 mismatches, 0 empty meshes. Cost +0.5% (2.326 -> 2.337 s, interleaved). World unchanged. MSVC/UBT not compiled here: Windows to-do list in Linux3/REPORT.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 KiB
WorldAssemblerM3 Linux3 — FP contraction
Unreal flags: documented and inferred
UE 5.7's Target Reference documents FPSemantics; Epic's Module Properties page documents the matching module-level property and says it overrides FP semantics. Epic Support says module and target overrides have existed since UE 5.4. This repo's VoxelForgeCore.Build.cs already sets FPSemantics = FPSemanticsMode.Precise and PCHUsage = PCHUsageMode.NoPCHs (existing lines 44–45). UE 5.7 Target Reference, Module Properties, Epic Support answer
Public UBT documentation does not publish one exact compiler argv for every plugin, platform, and configuration. Epic Support reports Windows DebugGame uses /fp:precise and Development uses /fp:fast by default. MSVC 2022 17+ defaults /fp:precise to contraction off; explicit /fp:contract can enable it under /fp:precise, and /fp:fast permits it by default. Earlier MSVC versions defaulted precise contraction on. Explicit #pragma fp_contract(off) overrides /fp:fast. Clang's precise model allows same-expression -ffp-contract=on; -ffp-contract=fast can fuse across statements and ignores #pragma clang fp contract(off). GCC defaults to -ffp-contract=fast; its standard FP_CONTRACT pragma is not implemented, so this patch uses GCC's function-option pragma instead. MSVC /fp, MSVC pragma, Clang FP pragmas and model, GCC FP options, GCC function-specific pragmas, GCC FP implementation
Inference for this repo: The standalone BuildHost is safe-by-configuration on current VS 2022: Build.ps1 explicitly uses /fp:precise, whose 17.0+ default is contraction off. The owner's UBT Windows build is likely safe for the same reason because the module sets Precise, but it was not captured here; an older MSVC or an explicit /fp:contract could put it at risk. On Linux, Precise alone does not mean contraction off for Clang, so the UBT build was at risk before this change. UE 5.7's supported Linux compiler is Clang 20.1.8; this host tested Clang 19.1.7. Epic's UE 5.7 PS5 notes also show Clang flags vary by platform: PS5 removed -ffp-contract=off because its -ffast-math setting conflicted with it. This does not establish that Linux UBT passes -ffast-math; capture that module's actual command line on the owner PC. UE Linux requirements, UE 5.7 PS5 notes
Mechanism and Core lines touched
Each Core translation unit loads CoreNamespacePreamble.h before its implementation. The preamble applies Clang contract(off), MSVC fp_contract(off), or GCC optimize("fp-contract=off"); unknown compilers fail at compile time. Its noinline probe evaluates (1 + 2^-27) * (1 - 2^-27) - 1 using volatile inputs. Separate multiply/add rounds to 0; a fused operation returns -2^-54.
World::compile runs that probe before building the shape and returns an error before density generation if it fuses. The check and density implementation receive the same preamble/FP pragma in Core. Clang fast is deliberately refused because Clang documents that mode as ignoring the source pragma. GCC's default and the explicit Clang on mode are instead compiled with the source-level off control.
Only these Core lines changed:
Standalone/Core/src/CoreNamespacePreamble.h:58–93— per-compiler pragma and shared self-test.Standalone/Core/src/VoxelForgeCore.cpp:972–976— refusal before shape build.
For this sweep, the standalone headless runner also gained --auto-surface-camera and --mesh-coverage in Standalone/Source/StreamBenchmarkHeadless.cpp and Standalone/Source/StreamBenchmarkPipeline.h.
The probe guards FP contraction. It does not establish immunity to unrelated -ffast-math transformations such as reassociation; those flags must be captured and handled separately if present.
Hash immunity
All successful rows cover the L1 whole view (2,553 tiles); each cell below lists density / mesh / payload / topology hashes. Runs used Standalone/data/owner_world.json and --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. Clang fast was built with -ffp-contract=fast and -fno-fast-math.
| Compiler and setting | W1 | W12 |
|---|---|---|
Clang 19 -ffp-contract=off |
c10697a8f48380c7 / e75c9119a1498cd5 / 49a9a8d1835a6c8c / edcb23288b5e8724 |
same |
Clang 19 -ffp-contract=on |
same | same |
Clang 19 -ffp-contract=fast |
refused before generation | refused before generation |
| GCC 14 default (effective contract=fast) | c10697a8f48380c7 / e75c9119a1498cd5 / 49a9a8d1835a6c8c / edcb23288b5e8724 |
same |
All three supported configurations (Clang off/on, GCC default) match the L1 reference. Both Clang fast runs returned exit code 2 in about 0.066 s with the explicit diagnostic, before generating a density hash. Logs: W1 refusal, W12 refusal; successful per-run logs are linux3-final-hash-*.log under Saved/Linux.
Self-test negative control
The test is in the shared Core preamble, not a separate differently compiled test file. In the deliberate Clang fast build it detected the fused result and the Core API rejected world generation at W1 and W12. A compiler-only probe also returned 0 for Clang off/on and GCC with its off pragma, and -0x1p-54 for Clang fast (probe log). The production-build refusal is the negative control.
Corrected seed sweep
The sweep used 48 unique seeds, both recipes, Clang off/on and GCC default, and W1/W12: 576 runs in 432.977 s. Each SurfaceWorld 96³-voxel cube was centered at the original XY and moved to that seed's nearest heightfield surface; each FloatingIslands cube was centered on that seed's largest planned island. Every run covered 27 LOD0 tiles and contained triangles.
| Recipe | Per-seed coverage across the sweep |
|---|---|
| SurfaceWorld | 9–18 nonempty mesh tiles; 26,182–52,468 indexed triangles |
| FloatingIslands | 7–15 nonempty mesh tiles; 15,728–46,224 indexed triangles |
All seeds had distinct density hashes within each recipe. Cross-build comparisons: 192, mismatches 0. W1/W12 comparisons: 288, mismatches 0. Empty-mesh rows: 0. Peak run RSS: 306,580 KiB. The full compiler/worker results are in seed_mesh_coverage.csv; one row per seed and recipe, including the nonempty tile and triangle counts and placed camera, is in seed_mesh_coverage_per_seed.csv. Summary: seed_mesh_coverage_summary.txt.
Cost
Three interleaved W12 runs per binary used the same 2,553-tile view, without mesh-coverage counting. “Before” is the S14 executable; “after” is the Clang-off L3 executable.
| Build | Whole-view ready times (s) | Median |
|---|---|---|
| S14 before | 2.260, 2.326, 2.339 | 2.326 s |
| L3 after | 2.329, 2.384, 2.337 | 2.337 s |
The interleaved median change is +0.011 s (+0.5%). The agreed owner-provided before baseline was 2.42 s; L3's 2.337 s median is 0.083 s below it. All six runs retained the L1 hashes. Maximum run RSS was about 550 MiB. Governor remained powersave; EPP remained balance_performance.
Windows work still needed
MSVC was not available here. On the Windows BuildHost:
- Build the standalone Core and viewer from the updated tree:
powershell -ExecutionPolicy Bypass -File Standalone\Build.ps1 -Target All -CompileWorkers 12. This uses/fp:precise; inspectStandalone\Build\VoxelForgeCore\core-compile.rspto confirm. - Run the resulting viewer on the L1 view twice, once with
--workers=1and once with--workers=12, using--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. Require the four L1 hashes both times and no self-test error. - To validate the MSVC pragma under an explicitly contracted setting, make a temporary copy of
Build.ps1, change its single/fp:preciseentry to/fp:fast, and build it with-OutputName VoxelForgeCoreStandalone-msvc-fast.exe. Run the same W1/W12 L1 checks and require unchanged hashes and a passing self-test. - Build the owner project's UE 5.7 Win64 Development Editor target (and its Server target if configured) with this plugin enabled. Save the actual UBT
cl.execommands forVoxelForgeCoreand record/fp:*,/fp:contract, and any/fp:fastor/fp:preciseoptions. Run the owner's existing seed-0 Core world/hash path at W1 and W12 and confirm the L1 result and passing self-test.
MSVC-specific risks: the pragma has not been compiled here; /fp:precise contraction defaults changed at VS 2022 17.0; target configuration may add /fp:fast or /fp:contract; and UBT's module override must reach every Core source. Microsoft documents that the explicit pragma overrides /fp:fast, but the Windows build must confirm that the actual toolset honors it. VoxelForgeCore.Build.cs already disables PCH and unity builds, and each Core wrapper includes the pragma preamble.
Runs and limits
No build, hash, sweep, or timing run failed or needed a retry. The two Clang fast exits were the expected negative control. Build variants were Clang 19.1.7 and GCC 14.2; builds and runs used CPUs 0–11, at most 12 jobs, normal priority, and a 6 GiB per-process address-space limit. Build peak RSS: 895,640 KiB (GCC); L1 hash-run peak: 551,004 KiB; seed-sweep peak: 306,580 KiB. The 8 MiB process stack was unchanged; Linux workers retain their existing 32 MiB stacks. Build artifacts are under Standalone/Build/linux-l3-* and Standalone/bin/linux/; logs are under Saved/Linux/.
Note for the owner
Core now disables expression contraction for Clang, GCC, and MSVC, and rejects Clang fast when that compiler ignores the off pragma; all supported Linux settings retained the L1 hashes. Please validate the source pragma on native MSVC and capture UE 5.7 UBT's actual Windows and Linux compiler arguments, since those builds were not available here.
Orchestrator review (Claude)
- Re-run by me on Luna's binaries, W12, L1 view: Clang
off2.299 s and Clangon2.302 s, both densityc10697a8f48380c7; GCC default 2.469 s, alsoc10697a8f48380c7; Clangfastexits 2 with the self-test refusal. Clangonand GCC default gave different worlds in L2, so the pragma is demonstrably effective. - Known gap (small): the pragma sits after the standard headers,
Json.hand the public API header in the preamble, so inline/template code defined there is not covered. All Core world math (Compat,*Internal.h, legacy sources) comes after it. - The probe guards contraction only;
-ffast-mathreassociation is a separate risk (as Luna says). The Windows to-do list above is the gate before claiming this on MSVC/UBT.