feat: Add cross-platform deterministic math via fdlibm#2602
feat: Add cross-platform deterministic math via fdlibm#2602Okladnoj wants to merge 14 commits intoTheSuperHackers:mainfrom
Conversation
|
I think it would be better to import fdlibm as an external library we can grab with Although there was an imgui PR that also tried to do it directly like this. Maybe discuss first then. Could you make a table of CRC values like in #2100? Using vc6 and win32 and Windows/Mac pairs? |
|
| Filename | Overview |
|---|---|
| Core/Libraries/Source/WWVegas/WWMath/wwmath.h | Core change: gates Sin/Cos/Sqrt/Acos/Asin/Atan/Atan2/Inv_Sqrt behind USE_DETERMINISTIC_MATH with gmath.h wrappers; removes always.h transitive include; adds redundant SinTrig/CosTrig aliases; several P1 issues flagged in prior review threads remain open |
| Core/GameEngine/Source/Common/Diagnostic/SimulationMathCrc.cpp | Only Sin/Cos are routed through WWMath; 10 other math functions (tanf, sqrtf, atan2f, etc.) still call system library directly, making the CRC non-deterministic and defeating the PR's purpose |
| cmake/gamemath.cmake | Integrates TheSuperHackers/GameMath via FetchContent with a full 40-char SHA pin; disables intrinsics and tests; links as PUBLIC so consumers get include paths automatically |
| Generals/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | All atan2/sqrt/sin/cos/asin calls correctly migrated to WWMath equivalents; no issues found |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | All atan2/sqrt/sin/cos/asin calls correctly migrated to WWMath equivalents; no issues found |
| Generals/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp | sqrt/atan2/acos calls correctly replaced with WWMath wrappers; uses WWMath::SinTrig instead of WWMath::Sin (redundant alias) |
| Core/Libraries/Source/WWVegas/WWMath/CMakeLists.txt | Adds PUBLIC gamemath link for non-VS6 builds, correctly propagating include paths to consumers of core_wwmath |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[GameLogic Call Sites\nLocomot / Weapon / Physics / AI] -->|USE_DETERMINISTIC_MATH| B[WWMath wrappers\nSin, Cos, Sqrt, Atan2, ...]
A -->|non-deterministic build| C[System math\nsinf / cosf / sqrtf]
B -->|RETAIL_COMPATIBLE_CRC defined| D[GameMath / gmath.h\ngm_sinf, gm_cosf, gm_sqrtf ...]
D --> E[fdlibm 5.3\nIEEE 754 bit-exact]
B -->|else| C
F[SimulationMathCrc.cpp] -->|Sin, Cos| B
F -->|tanf / sqrtf / atan2f\n+ 7 more| C
style F fill:#f96,color:#000
style E fill:#6c6,color:#fff
Comments Outside Diff (1)
-
Core/GameEngine/Source/Common/Diagnostic/SimulationMathCrc.cpp, line 40-51 (link)Non-deterministic math bypasses
WWMathin the CRC functionOnly
WWMath::SinandWWMath::Cosare routed through the deterministic wrappers; the remaining ten calls (tanf,asinf,acosf,atanf,atan2f,sinhf,coshf,tanhf,sqrtf,expf,log10f,logf,powf) invoke the system math library directly. WhenUSE_DETERMINISTIC_MATHis active, these will still produce platform-divergent results on macOS vs. Windows x87 FPU, meaning the CRC returned byappendSimulationMathCrcwill differ between clients — the exact failure mode this PR is designed to eliminate.Replace each system call with its
WWMathcounterpart (e.g.WWMath::Sqrt,WWMath::Atan2,WWMath::Tan,WWMath::Log10,WWMath::Pow, etc.).Prompt To Fix With AI
This is a comment left during a code review. Path: Core/GameEngine/Source/Common/Diagnostic/SimulationMathCrc.cpp Line: 40-51 Comment: **Non-deterministic math bypasses `WWMath` in the CRC function** Only `WWMath::Sin` and `WWMath::Cos` are routed through the deterministic wrappers; the remaining ten calls (`tanf`, `asinf`, `acosf`, `atanf`, `atan2f`, `sinhf`, `coshf`, `tanhf`, `sqrtf`, `expf`, `log10f`, `logf`, `powf`) invoke the system math library directly. When `USE_DETERMINISTIC_MATH` is active, these will still produce platform-divergent results on macOS vs. Windows x87 FPU, meaning the CRC returned by `appendSimulationMathCrc` will differ between clients — the exact failure mode this PR is designed to eliminate. Replace each system call with its `WWMath` counterpart (e.g. `WWMath::Sqrt`, `WWMath::Atan2`, `WWMath::Tan`, `WWMath::Log10`, `WWMath::Pow`, etc.). How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: Core/GameEngine/Source/Common/Diagnostic/SimulationMathCrc.cpp
Line: 40-51
Comment:
**Non-deterministic math bypasses `WWMath` in the CRC function**
Only `WWMath::Sin` and `WWMath::Cos` are routed through the deterministic wrappers; the remaining ten calls (`tanf`, `asinf`, `acosf`, `atanf`, `atan2f`, `sinhf`, `coshf`, `tanhf`, `sqrtf`, `expf`, `log10f`, `logf`, `powf`) invoke the system math library directly. When `USE_DETERMINISTIC_MATH` is active, these will still produce platform-divergent results on macOS vs. Windows x87 FPU, meaning the CRC returned by `appendSimulationMathCrc` will differ between clients — the exact failure mode this PR is designed to eliminate.
Replace each system call with its `WWMath` counterpart (e.g. `WWMath::Sqrt`, `WWMath::Atan2`, `WWMath::Tan`, `WWMath::Log10`, `WWMath::Pow`, etc.).
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: Core/Libraries/Source/WWVegas/WWMath/wwmath.h
Line: 146-178
Comment:
**`SinTrig`/`CosTrig`/`TanTrig`/`ACosTrig`/`ASinTrig` duplicate existing methods**
`WWMath::Sin`, `WWMath::Cos`, `WWMath::Acos`, and `WWMath::Asin` already exist and are routed through the same `gm_*f` calls under `USE_DETERMINISTIC_MATH`. The new `SinTrig`/`CosTrig` aliases were added to replace deleted `trig.h` free functions, but they create two public API paths for the identical operation. Call sites in `DumbProjectileBehavior.cpp`, `PhysicsUpdate.cpp`, etc. should use `WWMath::Sin`/`WWMath::Cos` directly, and the `*Trig` aliases can be removed to avoid future confusion.
How can I resolve this? If you propose a fix, please make it concise.Reviews (23): Last reviewed commit: "feat: Replace WWMath::Cos/Sin with CosTr..." | Re-trigger Greptile
a3fce09 to
8f7952d
Compare
8f7952d to
93f6fea
Compare
93f6fea to
5dcc907
Compare
Replace hardware-dependent x87 FPU trig functions (fsin, fcos) in WWMath with fdlibm 5.3 — a portable, bit-exact IEEE 754 C implementation. This ensures lockstep CRC parity between macOS ARM64/x64 and Windows x86 clients, eliminating multiplayer desyncs caused by floating-point precision divergence. Changes: - Integrate fdlibm 5.3 via FetchContent from Okladnoj/fdlibm-deterministic - Replace all x87 asm blocks in wwmath.h with fdlibm wrappers - Route ~80+ direct sin/cos/sqrt/atan2 calls in GameLogic through WWMath - Replace Inv_Sqrt Quake-era hack with 1.0f/WWMath::Sqrt() - Gate all changes behind USE_DETERMINISTIC_MATH (RETAIL_COMPATIBLE_CRC) - Clean Weapon.cpp diff to contain only functional WWMath replacements - Preserve Fast_Sin/Fast_Cos LUT (already deterministic) - Leave render/UI layer (GameClient, WW3D2) on system math (no CRC impact) - Add SimulationMathCrc dual-path diagnostic (fdlibm vs system math)
5dcc907 to
3e28af3
Compare
|
There are many other places in the codebase that use Sin and Cos, so the implementation is incomplete. |
|
I evaluated fdlib when I was looking at a replacement math library for Thyme but I decided against it as its very old. I cobbled together a similar library from a newer (though still old at this point) snapshot of the FreeBSD library https://github.com/TheAssemblyArmada/GameMath which also supports float precision (another limitation of the fdmath lib is that it only provides double precision versions of functions). |
You mean ones potentially affecting logic CRC? |
Yes, in quite a few places. |
|
There should be 3 modes of usage here, Retail compatible, standard library and the new deterministic math. One of the first things that really needs doing is having all game code call math library functions from a single place where we can substitute in the various optional variants of the functions. |
@Mauller You were absolutely right. After thoroughly checking the I just pushed a commit that fixes them. To avoid cluttering the codebase with (By the way, the audit confirmed that the standard The math should now be completely ready for testing. |
|
Please look into GameMath as brought forward by OmniBlade. |
Sure! Since I've already abstracted all direct math calls behind WWMath and Trig.h, swapping the backend from fdlibm to GameMath to get native float-precision will be very straightforward. I'll look into substituting it today/tomorrow. |
There are still missed files that will affect the CRC |
186a237 to
e49cf3f
Compare
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
|
Hello again @xezon @stephanmeesters @Mauller @OmniBlade! Lately I've been working almost exclusively in the GameClient repo. Since we currently only support Zero Hour there, my previous adjustments regarding CRC math didn't touch the vanilla Generals engine. Also, in the process of making these edits, I found and fixed a few remaining non-deterministic math operations in the ZH directory as well. Important note: since I develop and test everything on macOS (which only runs the Zero Hour client), I was only able to properly test and confirm network sync for the ZH fixes. For the base game, I just did a pure mirror copy of those exact same fixes. Therefore, it would be great if someone could run vanilla on a 32-bit Windows build and just double-check that I didn't break anything there. Thanks! |
|
@xezon |
Can you give an example for it? Generally it is a bad sign if simplifying code would break something. If so, it needs to be fixed. The only real concern would be if it broke VC6 retail compat. |
…WMath wrappers Replaces non-deterministic trigonometric and generic floating point math function calls with deterministic counterparts from the WWMath library across the core codebase. This ensures mathematical parity to prevent frame desyncs during multiplayer lockstep execution, and includes build-fixes for missing WWMath namespaces and header removals.
The old |
It's not compiling on win32 or vc6 right now. I think it's important you can compile and test both these builds, so not to shift the burden to the reviewers, perhaps use the Docker build or run Parallels on your Mac? |
- Guard gamemath.cmake and link with NOT IS_VS6_BUILD (VC6 lacks long long, stdint.h) - Guard gmath.h include in wwmath.h with _MSC_VER >= 1300 check - Fix ACos -> Acos case mismatch in BaseType.h (3 call sites)
|
Please restore the original spacing, particularly for |
a0a6ec1 to
ea2411c
Compare
1dfb766 to
be7ac16
Compare
@stephanmeesters @xezon The root cause was that our isolated console utilities heavily depended on the math from the old removed Regarding your suggestion about testing locally: unfortunately, Docker and Parallels on Apple Silicon Macs don't support win32 environments. However, iterating through GitHub CI turned out to be a great solution for this!
|
e74de2c to
5edf2b9
Compare
Extracted wwmath.h from tight coupling with the 3D engine's always.h macro definitions to establish WWMath as an independent Level 1 base library interface. Exposes Libraries/Source/WWVegas/WWMath and gamemath directly to corei_libraries_include targets, ensuring low-level utilities like Babylon and versionUpdate can compile BaseType.h without transitive rendering dependencies.
5edf2b9 to
5e3d599
Compare
Nice one. I have tested the replays but they all fail unfortunately. Current PR is hard to review for errors now because it tries to do many things, also there are many unnecessary formatting changes, and you made changes to both Generals and Zero Hour already. But it is valuable exploration So we would need multiple smaller PR's that are checked very carefully. I think @Skyaero42 will be picking this up |
…ig.cpp replacement SinTrig/CosTrig use cosf/sinf (matching original Trig.cpp CRT behavior) without USE_DETERMINISTIC_MATH, and gm_cosf/gm_sinf with it enabled. This preserves CRC compatibility with retail replays in non-deterministic builds while providing cross-platform determinism when USE_DETERMINISTIC_MATH is active.
129eae9 to
d28bcb2
Compare
…erals + GeneralsMD) All game logic files that originally used global Cos()/Sin() from Trig.cpp now use WWMath::CosTrig()/SinTrig() which preserves cosf()/sinf() behavior without USE_DETERMINISTIC_MATH, and routes through gm_cosf()/gm_sinf() when deterministic math is enabled.
c7679c8 to
9daa569
Compare
|
The consolidation attempt of trig and wwmath should be a standalone change, to allow for clean review and testing. |
@xezon Agreed. These past 2 weeks, the fact that replays were breaking on Windows with the new math kept bugging me, so I started fresh on a separate branch ( In the process I realized that a single math function in this project can have 3 variants: VC6 (x87 inline asm), CRT (standard library), and our new Deterministic (GameMath). Right now all replays pass perfectly with deterministic math both enabled and disabled — which is strange to me. With I've opened a clean PR (#2670), but I'm still working on it. If anyone has ideas why win32 doesn't fail with |

Replace hardware-dependent x87 FPU trig functions (fsin, fcos) in WWMath with fdlibm 5.3 — a portable, bit-exact IEEE 754 C implementation. This ensures lockstep CRC parity between macOS ARM64/x64 and Windows x86 clients, eliminating multiplayer desyncs caused by floating-point precision divergence.
Changes:
Tested: 3+ hours of cross-platform multiplayer (macOS Wine × Windows) with zero desyncs. Visual regression (health bars, veterancy icons) verified absent on native macOS Metal port.
Testing results
fdlibm(deterministic)7BE966877BE96687fdlibm(deterministic)