Search Unity

State of Determinism in Unity

Discussion in 'Scripting' started by Iron-Warrior, Jan 10, 2022.

  1. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    838
    Determinism is a subject that gets talked about a lot in various threads, but I haven't seen a central location to discuss it so thought I'd make one. (N.B. I'm not an expert in software determinism, just interested in its uses for netcode. If there are any errors in the below please feel free to correct me.)

    Tl;dr, some questions about determinism in Unity
    • Is cross platform float determinism in all code (not just Burst) feasible as a feature Unity could investigate (either through IL2CPP or other means)? Does Unity control the entire toolchain with IL2CPP (to be able to enable strict modes etc)?
    • Are there any ongoing efforts/investigations into determinism/deterministic games frameworks at Unity?
    ...but what is determinism, and why does it matter?

    Overview

    In the context of software and specifically games, determinism means that, for a given set of inputs a simulation will always give the exact same outputs. For games applications where determinism is needed it is essential that the outputs are identical down to the individual bit—"close enough" is not enough.

    In games, determinism is most valuable for networking. Without determinism, in a standard server-client setup servers must a) simulate the game and b) send the resulting state of the simulation to all clients.

    With determinism, servers do not need necessarily need run the simulation at all (though they still may), and to replicate the state of the game they need to only send the clients' inputs. This is since if every client receives the same input set and inserts them into the simulation in the same order, all clients will end up with the exact same simulation. This means that bandwidth scales only with player count and input size, rather than with overall game state.

    In terms of genres determinism is mostly widely used for RTS (allowing games like StarCraft in 1998 to support hundreds of units in a single match) and fighting games, but general purpose cross platform engines like Photon Quantum show that this paradigm can be used for a wide variety of genres.

    Deterministic Math

    The first consideration for making a deterministic game is ensuring that your basic math operations are all deterministic. This can be challenging if you are looking for cross platform play (e.g., PC (x64) and Android (ARM) playing matches together) and using floating point arithmetic. Based on my research it seems that it is achievable with a few conditions. IEEE 754 required arithmetic operations seem to be deterministic cross platform for modern consumer hardware (xoofx notes here than ARMv7 has inconsistencies with subnormals). Non conforming operations like trig would need to be implemented in software. An example of this approach can be seen in the Rust physics engine Rapier, which advertises cross platform determinism. Rust using LLVM also makes this easier.

    I also made a small repo to test cross platform float determinism, comparing arithmetic done in C# with calls to a native binary (made using Rust.) Tested what I think (?) are the main edge cases, actually found no desyncs between PC and Android (other than some NaN orderings, some comments in the README about that).

    Deterministic floats test repo.

    It might be challenging for Unity to guarantee cross platform float determinism with Mono builds, since .NET may have some issues with float determinism. IL2CPP would be another option, since I assume with it Unity controls the entire pipeline there, so maybe a feasible problem?

    More narrowly Burst compiler was initially advertised as looking to offer cross platform determinism, but from the latest news it looks like that feature is on hold.

    An alternative to floats for determinism is fixed point math. Fixed point math uses integers with a fixed radix point to emulate real numbers, performing arithmetic in software. Performance for arithmetic can be quite good with setups like 48.16 (48 bits for the integer part, 16 for the fraction). Drawbacks with fixed point are precision issues that floats do not run into, and needing to rewrite any existing libraries that use floats.

    Fixed Point Sharp, C# 48.16 fixed point math library

    Deterministic Game Framework

    Once your basic operations are all deterministic, the next step is to ensure that every other action in your game is deterministic. The best way to manage this is to be able to exactly compare the game state on one device to the game state on another to be able to immediately detect divergences every tick. This usually means building a checksum by iterating over every piece of data used for gameplay and sending it between clients. There's lots of little gotchas for determinism (.NET's dictionary iteration is non-deterministic, tying any logic to the variable update loop), so being able to detect desyncs immediately makes it a tractable problem.

    It can be a lot of work to build a deterministic framework (none of the MonoBehaviour workflow is written to be deterministic), but providing basic math operations are deterministic this is something that can be done by the Unity community.

    Unity DOTS Physics

    Unity DOTS Physics is apparently deterministic, which is pretty neat. It's also stateless, making it ideal for rollback networking. Although it has a bunch of ECS authoring and runtime tools, it's actually entirely decoupled from ECS. I wrote a prototype game object wrapper for it, since it seems like a really strong tool, and being accessible to MonoBehaviour projects would make it more widely used.

    Link to thread about DOTS Physics with Game Objects

    Probably took a lot of effort to ensure it was deterministic, so having cross platform float determinism would heavily increase its offering (as would giving the engine a proper name lol).






    Hope this has provided a good idea of what determinism is and its applications. Interested to know others' thoughts (and any corrections to the above!) and areas of interest on the subject. (Pinging @GabrieleUnity @Joachim_Ante and @xoofx since I think you have all looked into deterministic floats with Burst, thought this might be of interest. @TheOtherMonarch I know you were also interested in this subject in the DOTS thread too.)
     
    Last edited: Jan 13, 2022
  2. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    838
    In case anyone was interested in following up on this topic, I got around to updating my deterministic float test repo. I tested on Windows/Android/WebGL (running WebGL on the mentioned platforms + Mac and iOS) to results that more or less matched what I expected; float arithmetic, casting and comparisons are deterministic, while System.Math trig is not.

    I don't have access to Apple hardware, so I can't test the native side of that, but anyone interested could build it out and see how the tests go.
     
    msfredb7 and Menion-Leah like this.
  3. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    838
    To (mostly) finish it off, built out to Mac and iOS native, to similar results--inconsistencies with trig only.

    Only platforms not tested are Linux and the various consoles. Not sure if this is quite enough for me to feel confident to actually use this for anything in production, since Unity could simply change the C++ code generation at will and break everything, but it is pretty encouraging.
     
    msfredb7 and Menion-Leah like this.
  4. MoDDiB

    MoDDiB

    Joined:
    Jun 25, 2013
    Posts:
    9
    Hello, I'm trying to achieve physics determinism too

    See you soon for the next step of my adventure ;)
     
    Last edited: Mar 3, 2023
    msfredb7 likes this.
  5. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    838
    My repo matches how ECS handles the physics updating fairly closely, in that it performs a rebuild prior to every simulation, but I doubt you really need to. In any case, note that

    As an aside, I added some tests of using Unity.Mathematics trig (wrapping calls in [BurstCompile]), and it looks like it's probably deterministic cross platform, though I haven't gone to the trouble of rebuilding everything to all platforms again.
     
    TheOtherMonarch likes this.