Skip to content

feat(math): Route game logic math through WWMath with 3-mode deterministic support#2670

Open
Okladnoj wants to merge 8 commits intoTheSuperHackers:mainfrom
Okladnoj:okji/feat/deterministic-math-v2
Open

feat(math): Route game logic math through WWMath with 3-mode deterministic support#2670
Okladnoj wants to merge 8 commits intoTheSuperHackers:mainfrom
Okladnoj:okji/feat/deterministic-math-v2

Conversation

@Okladnoj
Copy link
Copy Markdown

@Okladnoj Okladnoj commented May 1, 2026

Rework of #2602, incorporating review feedback:

  • GameMath via FetchContent (per @stephanmeesters, @OmniBlade recommendation)
  • Trig.cpp preserved, redirected to WWMath instead of deleted (per @xezon request for standalone change)
  • 3 math modes: VC6 (x87 inline asm), CRT (standard library), GameMath deterministic (per @Mauller recommendation)
  • USE_DETERMINISTIC_MATH unconditional for non-VC6 — missing gmath.h is now a compile error instead of silent fallback to x87/CRT
  • Clean history from main

CI: win32 + vc6 ✅, replay checks ✅

Open question: Replay checks pass both with and without USE_DETERMINISTIC_MATH, even though golden replays were recorded with an x87 build. The replays may not contain MSG_LOGIC_CRC messages, meaning the check only validates absence of crashes rather than game state CRC parity. If anyone has insight on this — please share.

Testing results

Cross-platform deterministic math parity verified with SimulationMathCrc::runBenchmark — computes CRC over 10 000 iterations of sin/cos/tan/atan2/sqrt/pow across a fixed input set.

System Compiler Math Library CRC Perf (10 000 iters)
Win32 x86 MSVC (modern) fdlibm (deterministic) 🟩 76B53840 ~6 ms
macOS ARM64 Apple Clang fdlibm (deterministic) 🟩 76B53840 ~11 ms
Win32 x86 MSVC (modern) system math (native) 🟦 E8B6385A ~3 ms
macOS ARM64 Apple Clang system math (native) 🟦 E8B6385A ~5 ms
Win32 x86 VC6 (legacy) x87 CRT (no fdlibm) 🟧 B7B83850 ~17 ms
Win32 x86 VC6 (legacy) system math (native) 🟥 8BB5B841 ~5 ms
  • 🟩 cross-platform deterministic parity achieved (Win32 modern = macOS ARM64)
  • 🟦 native system math match (Win32 modern = macOS ARM64)
  • 🟧 VC6 deterministic (x87 CRT, separate group — fdlibm not supported)
  • 🟥 VC6 native (x87 CRT, separate group)

Key fix: -ffp-contract=off in cmake/compilers.cmake — prevents Clang from emitting FMA instructions (fmadd) that skip intermediate rounding, breaking bit-exact parity with MSVC's /fp:precise default.

image

Integrate GameMath library (fdlibm) for bit-perfect cross-platform
floating-point reproducibility. When USE_DETERMINISTIC_MATH is active,
all WWMath functions (Sin, Cos, Sqrt, Inv_Sqrt, Float_To_Long, Acos,
Asin, Atan, Atan2) use fdlibm instead of x87 asm or system CRT.

- Add GameMath via FetchContent with global include paths
- Replace Trig.cpp with inline WWMath::*Trig wrappers
- Add WWMath::*Origin wrappers for bare CRT calls in game logic
- Prioritize USE_DETERMINISTIC_MATH over _MSC_VER/_M_IX86 guards
- Verified: win32 replay CRC matches original x87 output
Add global Sqrt(double) to trig.h/Trig.cpp, routing through
WWMath::SqrtOrigin(). Replace 5 bare CRT sqrt() calls in BaseType.h
(Coord2D::length, Coord2D::toAngle, ICoord2D::length,
Coord3D::length, ICoord3D::length) with the new Sqrt() gateway.

This closes the last known CRT math leak in CRC-critical code paths.
Same pattern as existing Sin/Cos/ACos routing via Trig.cpp.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 1, 2026

Greptile Summary

Routes all game-logic floating-point math through a new WWMath abstraction layer that selects between three modes at compile time: fdlibm deterministic (non-VC6), CRT native (non-VC6 fallback when gmath.h is absent), and x87 inline-asm (VC6 legacy). GameMath is fetched via CMake FetchContent and pinned to a specific commit; -ffp-contract=off is added for Clang to prevent FMA contraction that would break cross-platform CRC parity with MSVC /fp:precise.

  • wwmath.h gains two inline wrapper families: *_Trig (replacing bare CRT calls from Trig.cpp) and *_Origin (replacing standalone sqrt/fabs/atan2 etc. across ~40 game-logic files); USE_DETERMINISTIC_MATH is set via __has_include(\"gmath.h\") so targets without the include path fall back silently.
  • cmake/gamemath.cmake declares the FetchContent dependency, forces GM_ENABLE_INTRINSICS=OFF for bit-exact cross-arch parity, and uses include_directories globally so the __has_include guard fires consistently.
  • SimulationMathCrc is split into a deterministic and a native CRC path, with a new runBenchmark diagnostic (defaulted off via RUN_MATH_BENCHMARK_REPLAY400_FLAG=0) used to produce the CRC table in the PR description.

Confidence Score: 5/5

Safe to merge; the routing changes are mechanical substitutions with no altered semantics, verified by cross-platform CRC benchmarks in the PR description.

The diff is a large but uniform mechanical rerouting of math calls through well-tested wrapper functions. The -ffp-contract=off flag and pinned GameMath commit address the key cross-platform reproducibility risk. All call sites pass float-width values so the documented double-to-float narrowing in the _Origin double overloads is lossless. CI passes for both win32 and VC6 paths.

No files require special attention; wwmath.h is the most complex changed file but its structure is straightforward conditional compilation.

Important Files Changed

Filename Overview
Core/Libraries/Source/WWVegas/WWMath/wwmath.h Core of the PR: adds conditional gmath.h include via __has_include, USE_DETERMINISTIC_MATH macro, and two new families of inline wrappers (*_Trig and *_Origin) routing all trig/math calls to fdlibm in deterministic mode or CRT otherwise.
cmake/gamemath.cmake New FetchContent declaration for the GameMath fdlibm fork; pins to a specific commit SHA, disables intrinsics (FORCE) for cross-arch determinism, and uses global include_directories so __has_include fires consistently.
cmake/compilers.cmake Adds -ffp-contract=off for non-MSVC compilers to prevent FMA contraction that would break bit-exact CRC parity between MSVC /fp:precise and Clang.
Core/GameEngine/Source/Common/Diagnostic/SimulationMathCrc.cpp Splits into two distinct CRC functions (deterministic vs native) and adds a runBenchmark utility gated behind RUN_MATH_BENCHMARK_REPLAY400_FLAG=0. Two if (i == 0) guards have statement bodies on the same line as the condition.
Core/Libraries/Include/Lib/BaseType.h Routes sqrt calls in Coord2D, ICoord2D, Coord3D, ICoord3D helper methods to the new Sqrt() free function from trig.h, which delegates to WWMath::Sqrt_Origin.
Core/Libraries/Include/Lib/trig.h Adds double Sqrt(double x) declaration alongside the existing trig function declarations; implementation lives in Trig.cpp.
Core/GameEngine/Source/GameLogic/AI/AIPathfind.cpp Mechanical substitution of raw sqrt/fabs calls with WWMath::Sqrt_Origin/FAbs_Origin throughout pathfinding cost calculations.
Core/Libraries/Source/WWVegas/WWMath/CMakeLists.txt Links core_wwmath against the gamemath FetchContent target (PUBLIC, non-VC6 only), propagating deterministic math headers to all consumers.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Game Logic call site] --> B{WWMath wrapper e.g. Sqrt_Origin / Sin_Trig}
    B --> C{USE_DETERMINISTIC_MATH defined?}
    C -- Yes non-VC6 + gmath.h found --> D[fdlibm / GameMath gm_sqrtf / gm_sinf CRC: 76B53840]
    C -- No non-VC6 gmath.h absent --> E[CRT system math sqrtf / sinf CRC: E8B6385A]
    C -- VC6 build IS_VS6_BUILD --> F[x87 inline asm or CRT CRC: B7B83850]
    D --> G[float result]
    E --> G
    F --> G
    G --> H[Game State / CRC]
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
Core/GameEngine/Source/Common/Diagnostic/SimulationMathCrc.cpp:121-138
Both `if (i == 0)` checks place the statement body on the same line as the condition, violating the project's style rule requiring bodies on a separate line for precise debugger breakpoint placement.

```suggestion
        if (i == 0)
            crcDet = xfer.getCRC();
    }
    _fpreset();
    clock_t endDet = clock();
    double timeDetMs = (double)(endDet - startDet) / CLOCKS_PER_SEC * 1000.0;

    clock_t startNat = clock();
    UnsignedInt crcNat = 0;
    
    setFPMode();

    for (i = 0; i < iterations; ++i)
    {
        XferCRC xfer;
        xfer.open("SimMathNat");
        appendSimulationMathCrc_Native(xfer);
        xfer.close();
        if (i == 0)
            crcNat = xfer.getCRC();
```

Reviews (7): Last reviewed commit: "refactor(math): Address xezon review fee..." | Re-trigger Greptile

Comment thread Generals/Code/GameEngine/Source/Common/System/Trig.cpp Outdated
Comment thread Core/GameEngine/Source/Common/Diagnostic/SimulationMathCrc.cpp Outdated
Comment thread Core/Libraries/Source/WWVegas/WWMath/wwmath.h Outdated
Comment thread Core/Libraries/Source/WWVegas/WWMath/wwmath.h Outdated
@Okladnoj
Copy link
Copy Markdown
Author

Okladnoj commented May 1, 2026

image Here is what replay playback looks like at the moment.

I’m testing this on a separate branch:
https://github.com/Okladnoj/GeneralsGameCode/okji/test/deterministic-math-v2

I slightly adjusted the CI there so I can run Win32 and get access to the game resources.

@Okladnoj Okladnoj force-pushed the okji/feat/deterministic-math-v2 branch from 854cc7b to 779f714 Compare May 1, 2026 00:49
@Skyaero42
Copy link
Copy Markdown

You did not review the changes you made with AI. It has issues that you should fix before asking it to be reviewed.

…erage

- Restore original Trig.cpp code (author comments, defines, REGENERATE_TRIG_TABLES)
- Keep only functional changes: sinf->WWMath::SinTrig redirects + Sqrt(double)
- Restore original tab alignment in wwmath.h #define block
- Route remaining CRT calls in SimulationMathCrc through WWMath (sinh/cosh/tanh/logf)
- Add cmake comments explaining FORCE usage
Copy link
Copy Markdown
Author

@Okladnoj Okladnoj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviwed all changes

@xezon
Copy link
Copy Markdown

xezon commented May 2, 2026

This change does too many things. It is better to first consolidate trig and wwmath and maybe other sources of math, before going into gamemath territory.

- Add RUN_MATH_BENCHMARK_REPLAY400_FLAG to SimulationMathCrc.h
- Implement 10000-iteration dual-path benchmark in SimulationMathCrc.cpp
- Inject benchmark trigger into GameLogic::update at replay frame 400
- Benchmark measures CRT vs WWMath precision and performance
@Okladnoj Okladnoj force-pushed the okji/feat/deterministic-math-v2 branch from 4b5675d to ddea128 Compare May 3, 2026 15:09
Okladnoj added 2 commits May 3, 2026 18:09
- Declare loop iterator outside of for loop in runBenchmark to support legacy MSVC scoping rules
@Okladnoj
Copy link
Copy Markdown
Author

Okladnoj commented May 3, 2026

This change does too many things. It is better to first consolidate trig and wwmath and maybe other sources of math, before going into gamemath territory.

@xezon Hey! I understand your point, but the reason I didn't fully consolidate trig and wwmath in this PR is exactly to avoid doing too many things at once.

As we saw in PR #2602, fully removing trig.h and replacing it with WWMath across the codebase touches over 120 files. Mixing a massive 120+ file architectural refactoring with a core feature addition (GameMath) made the previous PR extremely difficult to review and broke compilation for some standalone utilities, because trig.h is used outside of just game math.

That's exactly why I chose this "routing" approach for this PR. By keeping the trig.h interface intact and just routing its internal implementation to WWMath, we achieve the deterministic math goals with a much smaller and safer footprint.

Perhaps the best option would be to test this PR first, and if everything is fine — merge it. And only after that, we can focus on a second PR dedicated purely to the architectural cleanup (removing trig.h across all 120+ files)?

Comment thread Core/Libraries/Source/WWVegas/WWMath/wwmath.h Outdated
Comment thread Core/Libraries/Source/WWVegas/WWMath/wwmath.h Outdated
Comment thread Core/Libraries/Source/WWVegas/WWMath/wwmath.h Outdated
Comment thread Core/Libraries/Source/WWVegas/WWMath/wwmath.h
Comment thread Core/Libraries/Source/WWVegas/WWMath/wwmath.h
Comment thread Core/Libraries/Include/Lib/BaseType.h
Comment thread cmake/gamemath.cmake Outdated
Comment thread Core/GameEngine/Source/Common/Diagnostic/SimulationMathCrc.cpp
Comment thread Core/GameEngine/Source/Common/Diagnostic/SimulationMathCrc.cpp
Comment thread Core/GameEngine/Source/Common/Diagnostic/SimulationMathCrc.cpp Outdated
Okladnoj added a commit to Okladnoj/GeneralsGameCode that referenced this pull request May 5, 2026


- Merge gmath.h include + USE_DETERMINISTIC_MATH into single __has_include block
- Replace all #ifdef/#if defined() with #if USE_DETERMINISTIC_MATH
- Remove TheSuperHackers @fix prefix from cmake comment
- Expand ODR abbreviation in gamemath.cmake comment
- Add blank lines after setFPMode() in benchmark
- Fix iters abbreviation in printf
- Simplify benchmark: remove replay dependency, auto-trigger at frame 400


- Merge gmath.h include + USE_DETERMINISTIC_MATH into single __has_include block
- Replace all #ifdef/#if defined() with #if USE_DETERMINISTIC_MATH
- Remove TheSuperHackers @fix prefix from cmake comment
- Expand ODR abbreviation in gamemath.cmake comment
- Add blank lines after setFPMode() in benchmark
- Fix iters abbreviation in printf
- Simplify benchmark: remove replay dependency, auto-trigger at frame 400
- Rename WWMath wrappers to Function_Name convention (578 replacements, 79 files)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants