Conversation
Greptile SummaryThis PR routes all game-simulation math through a new
Confidence Score: 5/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Call site: Sin / Sqrt / Atan2 / …] --> B[WWMath::Sinf / Sqrtf / Atan2f]
B --> C{USE_DETERMINISTIC_MATH?}
C -- Yes --> D[gm_sinf / gm_sqrtf / gm_atan2f\nGameMath / fdlibm]
C -- No --> E{VC6 _Legacy path?}
E -- Yes --> F[x87 inline asm\nfsin / fsqrt]
E -- No --> G[CRT: sinf / sqrtf / atan2f]
H[BaseDefines.h] --> I{__has_include gmath.h?}
I -- No --> J[HAS_GAMEMATH undefined\n→ USE_DETERMINISTIC_MATH undef]
I -- Yes --> K{RETAIL_COMPATIBLE_CRC?}
K -- 1 default --> J
K -- 0 --> L[USE_DETERMINISTIC_MATH active]
L --> C
M[cmake/gamemath.cmake\nFetchContent GameMath SHA pinned] --> N[gmath.h on include path]
N --> I
Reviews (21): Last reviewed commit: "refactor(neutronmissile): Route debug bl..." | Re-trigger Greptile |
Here is what replay playback looks like at the moment.
I’m testing this on a separate branch: I slightly adjusted the CI there so I can run Win32 and get access to the game resources. |
854cc7b to
779f714
Compare
|
You did not review the changes you made with AI. It has issues that you should fix before asking it to be reviewed. |
|
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. |
4b5675d to
ddea128
Compare
@xezon Hey! I understand your point, but the reason I didn't fully consolidate As we saw in PR #2602, fully removing That's exactly why I chose this "routing" approach for this PR. By keeping the 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 |
- 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)
* feat(deterministic-math): scaffold phase 4 routing Port the first deterministic math batch derived from TheSuperHackers PR TheSuperHackers#2670 with incremental gating and attribution compliance. - add non-MSVC anti-FMA compile flag (-ffp-contract=off) - route trig and sqrt gateways through WWMath wrappers - add gamemath.cmake integration scaffold with deterministic flag - update project rule for upstream PR attribution comments - update lessons learned and May dev diary * fix(headless): stabilize replay simulation on macOS - Override ParticleSystemManagerDummy::update() as no-op to prevent headless replay from executing the full particle update path, which caused EXC_BAD_ACCESS crash at ParticleSystemManager::update()+560 - Route SDL3GameEngine::createRadar() and createParticleSystemManager() to their Dummy counterparts when dummy=true (headless mode), matching upstream Win32GameEngine factory behavior - Guard ParticleSystemManager::update() loop against stale null entries with early continue before sys->update() dispatch - Skip smudge rendering path in headless via m_headless guard in ParticleSystemManager::update() - Add null-file guards in RecorderClass::readNextFrame(), appendNextCommand(), and updatePlayback() for both Generals and ZH to prevent null dereference when playback file is closed mid-loop * fix(replay-headless): harden texture creation flow Guard D3DX8 and DX8 wrapper texture allocation paths when device or caps are unavailable in headless replay windows. Fail texture load tasks safely instead of dereferencing null state. Also harden missing texture fallback handling and record session notes in May diary and lessons. * fix(replay-recording): handle mixed path separators correctly when serializing map name The loop condition checking for path separators was incomplete on Linux/macOS paths: - realMapPathToPortableMapPath() converts platform paths to portable format - Portable paths may contain forward slashes (Linux/macOS standard) - Loop condition find(backslash) never matched forward-slash-only paths - This left newMapName EMPTY when writing replay header - Result: replays stored with corrupted map name field Fix: Check !isEmpty() AND (find(backslash) OR find(forward slash)) - Loop correctly terminates when last token (filename) is reached - Works with both Windows (backslash) and Unix (forward slash) separators - Applies to both GameInfoToAsciiString() and GameInfo::setMap() Test results: - macos_skirmish_1v1.rep: PASS - macos_6p_custom_map_2.rep: PASS (CRC fallback resolves map) - macos_1v1_custom_map_1.rep: CRC mismatch (expected, data incompatible) * fix(replay-mapcache): normalize map cache path and replay map field Fix cross-platform replay/map issues found on macOS:\n- write/read MapCache.ini using portable path join (no literal \ filename)\n- keep replay header path handling for absolute and directory-based -replay inputs\n- add explicit replay CRC mismatch diagnostics for headless runs\n- encode/decode replay map field to preserve special characters in map names\n\nValidation:\n- macOS z_generals build completed successfully\n- replay tests: official/custom map cases load natively; incompatible replay reports frame-0 CRC mismatch * fix(particle-emitter): null-safe strdup in copy constructor ParticleEmitterClass copy constructor called ::_strdup() on NameString and UserString without null checks, causing SIGSEGV when either field was null. Crash observed at: ParticleEmitterClass::Clone() -> copy ctor -> ::_strdup(nullptr) -> strlen(nullptr) -> SIGSEGV (KERN_INVALID_ADDRESS at 0x0) Triggered by W3DGhostObject::snapShot() during normal gameplay. Fix: guard strdup calls with null check before dereferencing. Applied to both GeneralsMD and Generals variants. * docs(replay): add headless testing reference and tech debt notes - HEADLESS_REPLAY_TESTING.md: commands, parameters, output interpretation, platform notes, debug tips (GDB/lldb) for macOS and Linux - REPLAY_MAPCACHE_TECH_DEBT.md: tracked known issues for custom map CRC fallback and (resolved) MapCache.ini backslash filename bug
|
Hi @xezon! I have addressed all your review feedback points and updated the PR. CI Status: To save you from hunting through all the comment threads, here is a consolidated list of the answers and solutions to your review points:
|
|
Hi @xezon! Thanks for the detailed review. I agree with some of your points regarding code cleanliness (I will remove the However, there are a couple of critical architectural points concerning the preservation of old replays (suffixes) and determinism ( 1. C++ Overloads vs Explicit types (why suffixes are needed)I want to explain why I had to come to an explicit separation of functions via suffixes instead of using C++ overloads. This is tied to the necessity of preserving 100% backwards compatibility for old builds (VC6 Retail Compatibility). I introduced 3 types of functions because they reflect 3 completely different mathematical paths (math paths) in the original EA engine. Our codebase serves three build modes at once (VC6, Win32, and Deterministic), and if we don't strictly fix the paths, we will lose Retail compatibility on old compilers:
Explicit suffixes strictly lock the original execution path. They guarantee that the exact function intended in the original game is called, avoiding unpredictable compiler behavior during overload resolution. Examples (The mechanics of overload conflicts)Here is, with examples, how the overload mechanism breaks the original branches when compiling under VC6: Example A: Conflicting identical signatures (
C++ overloads only work with different argument types. How is the compiler supposed to know which of the two Example B: Path substitution via typing ( float myVal = 0.5f;
float result = acos(myVal); // In the original, this is a call to <math.h> double acos(double)Since What happens if we introduce the overloads 2. Sqrt(double) in BaseType.h:391
Yes, in the original game it fell back to the system CRT 3. "Trampolines" in Trig.cpp
The fact is that I was acting exactly according to your original task from the previous PR (#2602). I did exactly that. But But I moved the implementation itself to 4. Duplicates (Ceil / Floor)Regarding |
It is a bit tough to fight through this much AI generated text. Please push the last state of the code and then I can take a look at it in Visual Studio and try to polish it up if it needs polishing. I expect this is faster than chatting about where to go with this. Generally, try to not trust the AI generated code too much. It generates code that is for machines, not humans. |
I wrote every point personally — I only asked AI to format it properly, fix spelling, and translate it into English, exactly like I’m asking now, because my English is not very strong. I personally worked through every point of that long text, so it would be better to read it carefully and understand the reasoning behind it — there is nothing unnecessary there. The main point is that suffixes like In the original project, before deterministic math was introduced, there were places with mixed math inside the game logic that affects the CRC. When If we could simply remove |
@xezon The project’s math was not always written with a clean and transparent architecture — or at least not all parts of it were. Maybe this was even done intentionally to make it harder to reverse-engineer the CRC logic. At the moment, all workflows build successfully, and all replays also play successfully both with deterministic math enabled and disabled. Above, I sent a screenshot of your job, plus one additional replay run that I configured specifically to verify Win32. |
|
Ok fair comments. I was under the impression I was chatting with AI generated text because of all the polished formatting. Can you push the latest state to the branch that you have now? I would like to take a look at it in Visual Studio next. Btw, Replay Check is currently broken. We need to wait until after that is fixed. |
The branch is already up to date — I haven't made any changes since the last push, I was waiting for your feedback. Feel free to take the current branch and work on it in VS. If you need my help — push your changes and I'll pick up from there. Regarding the broken Replay Check — the CI runner has no way to obtain the game data. I solved this by extracting a minimal set of files from the Steam distribution (no textures, audio, or GUI — just enough for replay verification), uploaded them as a release to a private repository ( |
The last push in from 08 May |
This comment was marked as resolved.
This comment was marked as resolved.
TheSuperHackers#2670) Keeps the GameLogic sources free of direct libm calls after the nuke radius debug draw was added.
…erHackers#2670) The override was a precaution and never had a measurement behind it. A Windows replay run built with intrinsics enabled produced CRC logs that are byte-identical to the run with them disabled, so the override only cost speed.
…ckers#2670) Four divisions in Generals were left unguarded while Zero Hour already routed the same places through WWMath::Div_Safe. Fallback values match the Zero Hour side so the two games behave alike.
TheSuperHackers#2670) Routing simulation math through WWMath took three pieces of surrounding logic with it that had nothing to do with math. SpecialPowerModule lost the guard that holds a special power unavailable while its object is still under construction, added upstream in TheSuperHackers#1218. Without it m_availableOnFrame starts at zero rather than 0xFFFFFFFF when RETAIL_COMPATIBLE_CRC is off, and isReady reports the power ready until the creation callback sets the timer. Both games had it, both lost it. DeliverPayloadAIUpdate lost the explicit maxTurnRate > 0 test around the turn radius division. Div_Safe only stands in for it where deterministic math is compiled in, and only for a divisor of exactly zero, so the retail path was left dividing by zero where it used to fall back to 999999. Removing that test changes game logic rather than math, so the retail form is now the original expression and the guarded division sits in the other branch. ObjectCreationList had NO_DEBUG_CRC commented out, which lets CRCDebug.h define DEBUG_CRC and wakes the 27 DUMP calls further down the file in any build with debug logging.
224e94e to
4631335
Compare
|
Hi! @xezon My main concern is that regrouping the finished solution by function family across 188 files again risks breaking what has been verified with dozens of network replays, and losing a stable state. I raised this on 05.09 (comment), and as I understood it, we agreed to only make each commit build on its own (comment) — all 16 do now. I'm happy to do small moves between commits that leave the final code unchanged, and to add explanatory comments. I strongly recommend testing retail compatibility on the last commit, since that is the state that was actually tested. |
|
The problem is when we commit the individual commits to main branch and an earlier commit knowingly breaks retail compatibility and stays broken for 10 commits or so, then it there is a gap of 10 broken commits and it is not easy to narrow which one of them intruduced a real issue. I would expect if you ask an LLM to rearrange commits and edits it can do that without error. |
…ckers#2670) Four divisions in Generals were left unguarded while Zero Hour already routed the same places through WWMath::Div_Safe. Fallback values match the Zero Hour side so the two games behave alike.
4631335 to
134c535
Compare
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review. WalkthroughThe pull request integrates GameMath, expands ChangesDeterministic math integration
Priority: ➖ Normal Estimated code review effort: 5 (Critical) | ~90 minutes Change: Feature Sequence Diagram(s)sequenceDiagram
participant CMake
participant GameMath
participant WWMath
participant GameLogic
CMake->>GameMath: Fetch pinned dependency
GameMath->>WWMath: Provide deterministic math headers
GameLogic->>WWMath: Call shared math wrappers
WWMath-->>GameLogic: Return deterministic or native results
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
|
@xezon Rearranged. Commit 4 (836e59bc0f) now also moves all existing callers of the functions it changes to the Replays ran on every commit in my fork, with only a CI commit on top that swaps the game data source. On all 13 commits |
There was a problem hiding this comment.
Actionable comments posted: 10
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Advanced
Run ID: b0ff1d9c-4096-4f05-8c62-e681147dc5fc
📒 Files selected for processing (188)
CMakeLists.txtCore/CMakeLists.txtCore/GameEngine/Include/Common/BezierSegment.hCore/GameEngine/Include/Common/Diagnostic/SimulationMathCrc.hCore/GameEngine/Include/Common/GameDefines.hCore/GameEngine/Source/Common/Bezier/BezFwdIterator.cppCore/GameEngine/Source/Common/Bezier/BezierSegment.cppCore/GameEngine/Source/Common/Diagnostic/SimulationMathCrc.cppCore/GameEngine/Source/Common/INI/INI.cppCore/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cppCore/GameEngine/Source/GameLogic/AI/AIPathfind.cppCore/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cppCore/GameEngineDevice/Source/W3DDevice/GameClient/CameraShakeSystem.cppCore/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cppCore/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cppCore/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cppCore/GameEngineDevice/Source/W3DDevice/GameClient/W3DMouse.cppCore/GameEngineDevice/Source/W3DDevice/GameClient/W3DParticleSys.cppCore/GameEngineDevice/Source/W3DDevice/GameClient/W3DProfilerFrameCapture.cppCore/GameEngineDevice/Source/W3DDevice/GameClient/W3DTreeBuffer.cppCore/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cppCore/Libraries/Include/Lib/BaseDefines.hCore/Libraries/Include/Lib/BaseType.hCore/Libraries/Include/Lib/trig.hCore/Libraries/Source/WWVegas/WW3D2/animatedsoundmgr.cppCore/Libraries/Source/WWVegas/WW3D2/colorspace.hCore/Libraries/Source/WWVegas/WW3D2/coltest.cppCore/Libraries/Source/WWVegas/WW3D2/hanim.cppCore/Libraries/Source/WWVegas/WW3D2/htree.cppCore/Libraries/Source/WWVegas/WW3D2/inttest.hCore/Libraries/Source/WWVegas/WW3D2/metalmap.cppCore/Libraries/Source/WWVegas/WW3D2/ringobj.cppCore/Libraries/Source/WWVegas/WW3D2/segline.cppCore/Libraries/Source/WWVegas/WW3D2/shattersystem.cppCore/Libraries/Source/WWVegas/WW3D2/streak.cppCore/Libraries/Source/WWVegas/WW3D2/texproject.cppCore/Libraries/Source/WWVegas/WW3D2/visrasterizer.cppCore/Libraries/Source/WWVegas/WWLib/WWDefines.hCore/Libraries/Source/WWVegas/WWLib/visualc.hCore/Libraries/Source/WWVegas/WWMath/CMakeLists.txtCore/Libraries/Source/WWVegas/WWMath/aabox.hCore/Libraries/Source/WWVegas/WWMath/colmathaabox.cppCore/Libraries/Source/WWVegas/WWMath/colmathaabox.hCore/Libraries/Source/WWVegas/WWMath/colmathaabtri.cppCore/Libraries/Source/WWVegas/WWMath/colmathobbobb.cppCore/Libraries/Source/WWVegas/WWMath/colmathobbox.cppCore/Libraries/Source/WWVegas/WWMath/colmathobbtri.cppCore/Libraries/Source/WWVegas/WWMath/colmathsphere.cppCore/Libraries/Source/WWVegas/WWMath/euler.cppCore/Libraries/Source/WWVegas/WWMath/lookuptable.hCore/Libraries/Source/WWVegas/WWMath/matrix3.cppCore/Libraries/Source/WWVegas/WWMath/matrix3.hCore/Libraries/Source/WWVegas/WWMath/matrix3d.cppCore/Libraries/Source/WWVegas/WWMath/matrix3d.hCore/Libraries/Source/WWVegas/WWMath/matrix4.hCore/Libraries/Source/WWVegas/WWMath/obbox.cppCore/Libraries/Source/WWVegas/WWMath/obbox.hCore/Libraries/Source/WWVegas/WWMath/quat.cppCore/Libraries/Source/WWVegas/WWMath/quat.hCore/Libraries/Source/WWVegas/WWMath/sphere.hCore/Libraries/Source/WWVegas/WWMath/tri.cppCore/Libraries/Source/WWVegas/WWMath/v3_rnd.cppCore/Libraries/Source/WWVegas/WWMath/vector2.hCore/Libraries/Source/WWVegas/WWMath/vector3.hCore/Libraries/Source/WWVegas/WWMath/vector4.hCore/Libraries/Source/WWVegas/WWMath/vehiclecurve.cppCore/Libraries/Source/WWVegas/WWMath/wwmath.cppCore/Libraries/Source/WWVegas/WWMath/wwmath.hCore/Tools/W3DView/RingSizePropPage.cppCore/Tools/W3DView/SphereSizePropPage.cppGenerals/Code/GameEngine/Source/Common/RTS/Player.cppGenerals/Code/GameEngine/Source/Common/System/BuildAssistant.cppGenerals/Code/GameEngine/Source/Common/System/Geometry.cppGenerals/Code/GameEngine/Source/Common/System/Trig.cppGenerals/Code/GameEngine/Source/GameClient/InGameUI.cppGenerals/Code/GameEngine/Source/GameLogic/AI/AI.cppGenerals/Code/GameEngine/Source/GameLogic/AI/AIGroup.cppGenerals/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cppGenerals/Code/GameEngine/Source/GameLogic/AI/AISkirmishPlayer.cppGenerals/Code/GameEngine/Source/GameLogic/AI/AIStates.cppGenerals/Code/GameEngine/Source/GameLogic/AI/TurretAI.cppGenerals/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cppGenerals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Behavior/BridgeBehavior.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Behavior/GenerateMinefieldBehavior.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Behavior/MinefieldBehavior.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Behavior/SlowDeathBehavior.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Body/ActiveBody.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Contain/ParachuteContain.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Locomotor.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Object.cppGenerals/Code/GameEngine/Source/GameLogic/Object/ObjectCreationList.cppGenerals/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DeliverPayloadAIUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/MissileAIUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/POWTruckAIUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/RailroadGuideAIUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/CleanupHazardUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/CommandButtonHuntUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/SupplyWarehouseDockUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/DynamicShroudClearingRangeUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/FloatUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/NeutronMissileSlowDeathUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/NeutronMissileUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/PointDefenseLaserUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/SlavedUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/TensileFormationUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Update/ToppleUpdate.cppGenerals/Code/GameEngine/Source/GameLogic/Object/Weapon.cppGenerals/Code/GameEngine/Source/GameLogic/System/GameLogic.cppGenerals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cppGenerals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cppGenerals/Code/Libraries/Source/WWVegas/WW3D2/camera.cppGenerals/Code/Libraries/Source/WWVegas/WW3D2/mapper.cppGenerals/Code/Libraries/Source/WWVegas/WW3D2/motchan.cppGenerals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.cppGenerals/Code/Libraries/Source/WWVegas/WW3D2/render2d.cppGenerals/Code/Tools/WorldBuilder/src/GlobalLightOptions.cppGeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cppGeneralsMD/Code/GameEngine/Source/Common/System/BuildAssistant.cppGeneralsMD/Code/GameEngine/Source/Common/System/Geometry.cppGeneralsMD/Code/GameEngine/Source/Common/System/Trig.cppGeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cppGeneralsMD/Code/GameEngine/Source/GameLogic/AI/AI.cppGeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGroup.cppGeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cppGeneralsMD/Code/GameEngine/Source/GameLogic/AI/AISkirmishPlayer.cppGeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cppGeneralsMD/Code/GameEngine/Source/GameLogic/AI/TurretAI.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Map/PolygonTrigger.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/BridgeBehavior.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GenerateMinefieldBehavior.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/MinefieldBehavior.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/SlowDeathBehavior.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Body/ActiveBody.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/ParachuteContain.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Locomotor.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/ObjectCreationList.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DeliverPayloadAIUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/MissileAIUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/POWTruckAIUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/RailroadGuideAIUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/CleanupHazardUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/CommandButtonHuntUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/SupplyWarehouseDockUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DynamicShroudClearingRangeUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/FloatUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/NeutronMissileSlowDeathUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/NeutronMissileUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PointDefenseLaserUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SlavedUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipDeploymentUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/TensileFormationUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ToppleUpdate.cppGeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cppGeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cppGeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cppGeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cppGeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/camera.cppGeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/lightenvironment.cppGeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/linegrp.cppGeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/mapper.cppGeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/motchan.cppGeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/part_emt.cppGeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/render2d.cppGeneralsMD/Code/Tools/WorldBuilder/src/GlobalLightOptions.cppcmake/compilers.cmakecmake/config-retail.cmakecmake/gamemath.cmake
💤 Files with no reviewable changes (1)
- Core/Libraries/Source/WWVegas/WWLib/visualc.h
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.
134c535 to
c992309
Compare
xezon
left a comment
There was a problem hiding this comment.
Next wave. I am not of fan of the Div_Safe usage. Is protects from division by zero, but it does so in an illogical way.
…ns (TheSuperHackers#2670) Introduces the gamemath.cmake module and wires HAS_GAMEMATH / USE_DETERMINISTIC_MATH through the compiler configuration.
…ATH switches (TheSuperHackers#2670) Moves RETAIL_COMPATIBLE_CRC into BaseDefines.h so that WWMath can see it without depending on GameDefines.h, and adds USE_DETERMINISTIC_MATH which is disabled automatically when retail CRC compatibility is required. BaseType.h includes BaseDefines.h, which enables the RETAIL_COMPATIBLE_CRC condition of REAL_TO_INT_CEIL and REAL_TO_INT_FLOOR. config-retail.cmake collects the RETAIL_COMPATIBLE_ defines of BaseDefines.h as well, so RTS_BUILD_OPTION_RETAIL_COMPATIBLE_GAME keeps controlling RETAIL_COMPATIBLE_CRC.
…ers#2670) Moves the WWMath declarations and definitions into the layout used by the deterministic math entry points, so that the next commit only adds and changes functions in place. Merges the per-platform duplicate definitions into one body with the platform branches inside, moves the inline bodies of Fabs, Atan, Atan2, Ceil and Floor out of the class and drops the section banners. No functional change.
Adds the WWMath wrappers that dispatch between the deterministic gamemath implementation and the platform libm, plus the _Legacy variants used by rendering code that must stay outside the simulation. Existing callers of the changed functions move to the _Legacy and float variants in this commit, so retail behaviour holds between commits.
…ckers#2670) Replaces direct libm calls in the game simulation of both Generals and Zero Hour with the WWMath wrappers, so that the simulation uses the deterministic implementation when it is enabled.
TheSuperHackers#2670) Keeps the GameLogic sources free of direct libm calls after the nuke radius debug draw was added.
| #endif | ||
|
|
||
| #if !HAS_GAMEMATH || RETAIL_COMPATIBLE_CRC | ||
| #undef USE_DETERMINISTIC_MATH // Cannot actually use deterministic math :( |
There was a problem hiding this comment.
nit: use 'unfortunately' if you want to express disappointment instead of a smiley.
| // Must not touch this function because it affects its inline-ability | ||
| // and therefore changes the logic at an unknown call site that relies on it. It is a bug. |
There was a problem hiding this comment.
This comment should be amended. The issue that you're seeing is that the value gets truncated from 80 bit to 32 bit for the Sqrt version. Whether Coord3D::length is inlined doesn't appear relevant per se.
FYI, this is what VC6 does for the original (Real)sqrt( x*x + y*y + z*z ):
- Load x coordinate twice and multiply, keep result in x87 register.
- Repeat for y and z coordinates.
- Add the 3 results.
- Clean the registers and keep the final result in the first register (st0).
- Call
fsqrt(and return function if not inlined) - Use returned value.
Contrast that to what VC6 does for Sqrt( x*x + y*y + z*z ):
- Load x coordinate twice and multiply, keep result in x87 register.
- Repeat for y and z coordinates.
- Add the 3 results.
- Clean the registers and store the final result on the stack.
- Call
Sqrtand load value from stack to first x87 register (st0). - Call
fsqrt(and return function(s) if not inlined) - Use returned value.
The original uses the full 80 bit value, the new version uses just 32 bits.
There was a problem hiding this comment.
If you make it so that the above call uses the original
sqrt version, then all other call sites can use the new Sqrt version.
Not sure if this is guaranteed to work for all replays and will keep working with future code changes, but it passes golden replay 1. GR1 would otherwise mismatch at frame 102635.

Merge by rebase
Rework of #2602, incorporating review feedback:
USE_DETERMINISTIC_MATHdefaults on for non-VC6.BaseDefines.hturns it off automatically whengmath.his not available or whenRETAIL_COMPATIBLE_CRCis set, so a build without GameMath falls back to the CRT path rather than failingGM_ENABLE_INTRINSICS=OFFoverride was dropped after a Windows replay run showed byte-identical CRC logs with intrinsics on and offmain, no merge commitsOpen 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 containMSG_LOGIC_CRCmessages, 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.fdlibm(deterministic)76B53840fdlibm(deterministic)76B53840E8B6385AE8B6385AB7B838508BB5B841Key fix:
-ffp-contract=offincmake/compilers.cmake— prevents Clang from emitting FMA instructions (fmadd) that skip intermediate rounding, breaking bit-exact parity with MSVC's/fp:precisedefault.