Search Unity

Unity Physics package (DOTS) is not frame-rate independent?

Discussion in 'Physics' started by zardini123, Jun 13, 2019.

  1. zardini123

    zardini123

    Joined:
    Jan 5, 2013
    Posts:
    68
    I have been playing with the new Unity Physics package (currently version 0.1.0). It seems the Unity Physics solution is not frame-rate independent. I found this by comparing two falling cubes, one using PhysX and one using Unity Physics. Upon a laggy scene startup, the Unity Physics cube does not fall constantly in regards to time, and therefore lands later than the PhysX cube.

    (Blue cube: PhysX, Red Cube: Unity Physics)
    framerate dependent.gif

    Is the frame-rate independentness (ehh, english) simply the consequence of using deterministic physics simulations? Or, would it be possible to force the simulation to run every fixed tilmestep using the current API?

    It'd be cool to know if the people working on Unity Physics are already planning on some easy way to make the simulation rate constant.
     
    ysxdead likes this.
  2. EdRowlett-Barbu

    EdRowlett-Barbu

    Joined:
    Mar 16, 2015
    Posts:
    88
    up, encountering this same issue!!!! What's the solution?!
     
  3. zardini123

    zardini123

    Joined:
    Jan 5, 2013
    Posts:
    68
    The issue has (already) been addressed by Unity
    The developers at Unity understand that the physics are tied to variable frame rate, as its been mentioned in the changelog under Known Issues since Unity Physics 0.0.2 (ever since April 2019) (see the version entry here or the image below).


    The FixedTimestep example in the ECS Samples project does provide an approach for making a system follow a fixed update. But note, applying the concepts shown in FixedTimestep to Unity Physics is no easy feat (I'll explain why later).

    In the FixedTimestep example, the ComponentSystem FixedRateSpawnerSystem has a [DisableAutoCreation] attribute preceding its declaration. Due to that, the FixedRateSpawnerSystem will not be added to the World during automatic world bootstrapping, and therefore not introduced to Unity's PlayerLoop automatically. For the FixedRateSpawnerSystem to be updated, a independent MonoBehaviour called FixedTimestepUpdater creates the system using this line, which I've copied here:
    Code (CSharp):
    1. spawnerSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<FixedRateSpawnerSystem>();
    Calling this line DOES NOT introduce the system into the PlayerLoop. The only way the system can be updated is via a .Update() call. With that, FixedTimestepUpdater calls the system's .Update() in its FixedUpdate declaration, therefore making the system run every FixedUpdate.

    The Solution (that will eventually work)
    Before continuing onto talking about the solution, FixedTimestepUpdater.cs has a block comment saying the following:

    // NOTE: Updating a manually-created system in FixedUpdate() as demonstrated below
    // is intended as a short-term workaround; the entire `SimulationSystemGroup` will
    // eventually use a fixed timestep by default.


    When using default bootstrapping, all of Unity Physics' simulations are placed in the SimulationSystemGroup. Therefore, if this prophecy becomes true, Unity Physics will eventually, and automatically, follow a fixed update. YAY!! But, this comment has been in that file since the file's introduction in the GitHub repo (since July 2019, ~3 months after that changelog entry was posted). I'm not aware of all the challenges the developers at Unity have to face to make that group follow a fixed update. I can imagine, especially with all the new systems falling under the SimulationSystemGroup, that making this a reality is not going to be like flipping a switch.

    The Solution (that works today)
    Unity Physics is open source, and the installed package is entirely editable, so you can apply the [DisableAutoCreation] every major system under the Unity.Physics package and add the systems via a secondary script. There is two issues with this. One: Any edits done to scripts in any unity package does not persist (!!!). Two: it is now up to you to you to execute all of Unity Physics systems in the right order, at the right time.

    Tracking down and adding [DisableAutoCreation] every ComponentSystem in the Unity.Physics is annoying and time consuming. Thankfully, the people at Unity made the attribute enact on entire namespaces. In any script that will be compiled into the Unity.Physics assembly, place the following code in there (I personally chose AssemblyInfo.cs):
    Code (CSharp):
    1. using Unity.Entities;
    2.  
    3. [assembly: DisableAutoCreation]
    4. namespace Unity.Physics { }
    Upon running a scene, you should be met with 15 warnings all stating "Ignoring Invalid [UpdateAfter/Before]", many of them being Unity.Physics debug systems. I don't know why the Debug systems are still being auto created, especially because those debug systems are under the Unity.Physics namespace (NOTE: as described earlier, this edit will not persist between Unity sessions. Upon closing and opening the editor, this change will not exist).

    Those warnings are also a great indication that what you're doing is probably not the best route for getting Unity Physics to run like you want it to. So many of these systems depend on one another, that picking some out and slapping it "somewhere else" might make them kick and scream.

    Speaking of kicking and screaming, let's try adding the Unity Physics systems manually, like in the FixedTimestep example, and calling .Update() on each of them! Nothing should go wrong, right?
    The four main systems Unity Physics uses for doing physics stuff is are updated in the following order:
    (EDIT: Turns out I originally had the order wrong here, and in my code. This solution actually works!!!!)
    1. BuildPhysicsWorld
    2. EndFramePhysicsSystem
    3. StepPhysicsWorld
    4. ExportPhysicsWorld
    For our update shenanigans to work, we should respect that update order.
    Make a new script called FixedTimestepUpdater.cs anywhere in your project and smack this totally-not-jank-code in there:
    Code (CSharp):
    1.  
    2. using Unity.Entities;
    3. using UnityEngine;
    4. using Unity.Physics.Systems;
    5.  
    6. public class FixedTimestepUpdater : MonoBehaviour
    7. {
    8.   JobComponentSystem[] spawnerSystems = new JobComponentSystem[4];
    9.  
    10.   void FixedUpdate()
    11.   {
    12.     if (spawnerSystems[0] == null)
    13.     {
    14.       spawnerSystems[0] = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<BuildPhysicsWorld>();
    15.       spawnerSystems[1] = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<EndFramePhysicsSystem>();
    16.       spawnerSystems[2] = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<StepPhysicsWorld>();
    17.       spawnerSystems[3] = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<ExportPhysicsWorld>();
    18.     }
    19.     foreach (JobComponentSystem job in spawnerSystems)
    20.     {
    21.       job.Update();
    22.     }
    23.   }
    24. }
    25.  
    Place this script on a blank object in your scene, and run.

    Voila. Magic.

    The cubes fall in harmony.
    (Blue: PhysX. Red: Unity Physics)


    This solution is jank, though. The physics debug drawing systems are in a component system group that is independent of the physics systems. Therefore, enabling some physics debug options results in spewing errors. I'd assume the way to fix this is also get the debug systems in a custom ComponentSystemGroup with the physics systems, adding that group to the world, and then running .Update() on that group. Having the systems in the group could allow for correct sorting, and I'd suspect correct update timing as well.

    If I get the ComponentSystemGroup version working, I'll post it in a new post.

    DOTS Sample
    Unity recently released a FPS sample using pure DOTS stuff, which includes Unity Physics. When playing the sample, the character (which is a Unity Physics capsule) moves at a constant rate, even with extremely varying frame rates. What confuses me is the sample does not have any Unity Physics systems running on the default world, but has it running on multiple non-default worlds (Client and Server world). What confuses me even more is Unity did not put UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP in Scripting Define Symbols, which would disable default world creation. I wonder if they've accomplished updating like I've shown above, but I can't be sure without seeing the code. The source code for this sample is large, so finding the culprit code that makes both statements work is going to take a bunch of time, unless some fine person from the Unity dev team provides insight.

    (P.S. Please let me know if any of my information on ECS and its inner workings is incorrect)
     

    Attached Files:

    Last edited: Feb 1, 2020
    Pr0x1d and Edy like this.
  4. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,510
    Thank you very much for such great contribution! Not having a proper way to run physics at a fixed rate is one of the major issues that keeps me out of using Unity Physics.

    Please keep posting your findings on this issue. I'll surely make good use of them if some day I decide to give it a try.
     
  5. zardini123

    zardini123

    Joined:
    Jan 5, 2013
    Posts:
    68
    Thank you Edy for your kind words! :)

    ComponentSystemGroup Solution
    I've gotten a implementation of the fixed updater I posted above, but instead using ComponentSystemGroup. It's fairly identical to my original implementation, except ComponentSystemGroup can give you finer control on adding, removing, and sorting systems. Having a [DisableAutoCreation] attribute in the Unity.Physics is still required for this method. The instructions for adding that attribute is in my previous post.

    Code (CSharp):
    1. using Unity.Entities;
    2. using UnityEngine;
    3. using Unity.Physics.Systems;
    4.  
    5. public class PhysicsGroup : ComponentSystemGroup { }
    6.  
    7. public class FixedTimestepUpdater : MonoBehaviour
    8. {
    9.   PhysicsGroup group;
    10.   World defaultWorld;
    11.  
    12.   void FixedUpdate()
    13.   {
    14.     if (group == null)
    15.     {
    16.       defaultWorld = World.DefaultGameObjectInjectionWorld;
    17.       group = defaultWorld.GetOrCreateSystem<PhysicsGroup>();
    18.  
    19.       group.AddSystemToUpdateList(defaultWorld.GetOrCreateSystem<BuildPhysicsWorld>());
    20.       group.AddSystemToUpdateList(defaultWorld.GetOrCreateSystem<EndFramePhysicsSystem>());
    21.       group.AddSystemToUpdateList(defaultWorld.GetOrCreateSystem<StepPhysicsWorld>());
    22.       group.AddSystemToUpdateList(defaultWorld.GetOrCreateSystem<ExportPhysicsWorld>());
    23.     }
    24.  
    25.     group.Update();
    26.   }
    27. }
    The PhysicsGroup ComponentSystemGroup only really acts as a container in this instance, as we add the systems in the order that they need to function properly. What you can do is add the physics systems in any order, along with any other system you want (maybe a system to interact with physics objects), and run
    group.SortSystemUpdateList()
    once to sort them in their rightful order, as defined by attributes [UpdateBefore] and [UpdateAfter].

    Even though the PhysicsGroup group and the four physics systems are added to the world, they will not show up in the EntityDebugger, as they are not associated with any update functions in PlayerLoop. This is also true for my earlier implementation. I'll explain what that all means in a future post.

    This solution is still pretty jank though. It still requires all Unity.Physics systems to be not auto created. Making that happen in itself is not permanent. Also, [Speculation] I believe Unity Physics has a dependency for the TransformSystemGroup. Being that they are now both updating at different times, Unity Physics may not operate like designed. [/Speculation] I'm currently researching a better, permanent solution that ensures correct compatibility with other systems via adjusting the PlayerLoop. I'll explain why and how in a future post :)

    ComponentSystemGroup will eventually be able to follow a fixed time-step?

    As of Entities 0.5.1, ComponentSystemGroup.cs has definition for a function called SetFixedTimeStep. The function header has this note above it:
    Code (CSharp):
    1.  
    2. // Fixed-Rate Support
    3. // internal for now, until we figure out proper API
    4. internal void SetFixedTimeStep(float step)
    5. {
    6.    // code and stuff...
    7. }
    8.  
    9. internal void DisableFixedTimeStep()
    10. {
    11.     // code and stuff...
    12. }
    13.  
    This is a good indication that the statement in my previous post about SimulationSystemGroup following a fixed timestep will be a reality, eventually.
     
    Last edited: Feb 2, 2020
    Edy likes this.
  6. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,510
    That's another interesting point. In the GameObject world we configure interpolation in the rigidbodies so they visually move at the Update rate, while their physics is updating at FixedUpdate rate. Evidently, there should be something equivalent in Unity Physics when running at FixedUpdate rate. Otherwise, objects will exhibit a noticeable jittering.
     
  7. zardini123

    zardini123

    Joined:
    Jan 5, 2013
    Posts:
    68
    Thanks for bringing that to my attention Edy! It seems Unity Physics doesn't have an interpolation system. TransformSystemGroup seems to be for adjusting transforms from local to global, and other transform utilities. I could guess if Unity Physics did have interpolation, it would expose a parameter to enable or disable interpolation. Also, Unity Physics is currently running on the Update loop, having interpolation would be overkill. Though, it would make total sense if they implemented interpolation in the future once they move Unity Physics to the FixedUpdate loop!
     
    Edy likes this.
  8. zardini123

    zardini123

    Joined:
    Jan 5, 2013
    Posts:
    68
    Best solution

    Let's assume we need all the systems surrounding Unity Physics in SimulationSystemGroup needs to be with Unity Physics for Unity Physics to work completely like designed. Therefore, if we want Unity Physics to run on FixedUpdate, we need the entire SimulationSystemGroup to be ran every FixedUpdate, not every Update.

    Now the question is how do we get SimulationSystemGroup to run every FixedUpdate? Calling SimulationSystemGroup's .Update() every FixedUpdate would result in it running every Update, plus every FixedUpdate. There is no function to force a SimulationSystemGroup to stop running. Even then, forcing to stop would give no opportunity to update it manually, lol.

    Then how does SimulationSystemGroup even update every Update? Is there some magical system or class calling its .Update() every Update? Nope. SimulationSystemGroup, PresentationSystemGroup, and InitializationSystemGroup are updated by coupling them into Unity's PlayerLoop.

    It turns out, one does not need to define UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_RUNTIME_WORLD in the scripting defines to override default world creation (see the World documentation for more about that define). All one needs for world creation overriding is any class/struct inheriting type ICustomBootstrap defined anywhere with a function override for
    public bool Initialize(string defaultWorldName)
    . Just like for systems, usages of ICustomBootstrap is found via reflection (see
    5argon's article on worlds and system groups, and how reflection affects performance). ICustomBootstrap is found when AutomaticWorldBootstrap.cs calls DefaultWorldInitalization.Initalize, and in that function, CreateBootStrap is called, where in that function, types of ICustomBootstrap is found and used. AutomaticWorldBootstrap doesn't call that DefaultWorldInitalization.Initalize if the disable world bootstrap defines are used.

    Back to talking about making a ICustomBootstrap. A blank Initialize function does not initialize any systems into any world. ICustomBootstrap does not even provide any functions for finding and setting up Systems. I've found by studying DOTS Sample that a default world and setting up all the systems can be done using these functions:
    Code (CSharp):
    1. using Unity.Entities;
    2.  
    3. public struct ADefaultBootstrap : ICustomBootstrap
    4. {
    5.     public bool Initialize(string defaultWorldName)
    6.     {
    7.         // Assembly reflection to find systems happens here
    8.         var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default);
    9.  
    10.         var world = new World(defaultWorldName);
    11.         World.DefaultGameObjectInjectionWorld = world;
    12.  
    13.         // The three root level groups are added into our blank world, and then the
    14.         // systems are sorted into the three root level groups from associated [UpdateInGroup] attributes.
    15.         // That is all done in this one function call.
    16.         DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, systems);
    17.  
    18.         // Couple the three root level groups to Unity's PlayerLoop
    19.         ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world);
    20.  
    21.         return true;
    22.     }
    23. }
    The call
    ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world);
    is what gets SimulationSystemGroup to run every Update. Before understanding how SimulationSystemGroup can be added to the PlayerLoop, let's look at the Entity Debugger, and check the option "Show Full Player Loop" (or look at the image below). Notice, the "root layer" is that of Unity's update functions you can use in MonoBehaviours! And under groups Initialization, Update, and PreLateUpdate contains systems InitializationSystemGroup, SimulationSystemGroup, and PresentationSystemGroup respectively! Each of these loop points are actually children of the current instance of the PlayerLoop. Each of those loops are of type PlayerLoopSystem, and contain references to the systems that make Unity run. Unity then go through the PlayerLoopSystem instance set by PlayerLoop.SetPlayerLoop, and update the systems in order based on if the PlayerLoopSystem type is one of these structs. Trying to type out how this works makes me more confused, but I think y'all would get a better sense how the nested structure is set up based on this picture.

    PlayerLoopSystem.jpg

    How
    ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world);
    adds these groups to the PlayerLoop is using some array manipulation. You create a new
    PlayerLoopSystem of one size bigger than before, relocate all instances in the original PlayerLoopSystem to the new one, and then add the SimulationSystemGroup at the end of the new PlayerLoopSystem. So to move SimulationSystemGroup to FixedUpdate, it'd be easy as slightly modifying ScriptBehaviourUpdateOrder.UpdatePlayerLoop to say when
    playerLoop.subSystemList[i].type == typeof(FixedUpdate)
    , then smack it to the end of the FixedUpdate's PlayerLoopSystem. This is a poor idea, because modifying any package doesn't persist, and UpdatePlayerLoop uses some code that is marked for FIXME.

    Therefore, the ultimate solution is to initialize the world completely normally. After that, iterate over the current PlayerLoopSystem structure, and move SimulationSystemGroup from the Update PlayerLoopSystem to the FixedUpdate PlayerLoopSystem. See the solution below:

    Code (CSharp):
    1. using Unity.Entities;
    2. using UnityEngine;
    3. using UnityEngine.LowLevel;
    4. using UnityEngine.PlayerLoop;
    5.  
    6. /// <summary>
    7. /// Migrates the premade (default) SimulationSystemGroup containing all
    8. /// automatically added systems from the Update loop to the FixedUpdate loop.
    9. /// This happens during world bootstrapping.
    10. /// </summary>
    11. public struct SimulationSystemGroupFixedUpdateMigration : ICustomBootstrap
    12. {
    13.   public bool Initialize(string defaultWorldName)
    14.   {
    15.     // Initalize the world normally
    16.     var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default);
    17.  
    18.     var world = new World(defaultWorldName);
    19.     World.DefaultGameObjectInjectionWorld = world;
    20.  
    21.     DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, systems);
    22.     ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world);
    23.  
    24.     // Moving SimulationSystemGroup to FixedUpdate is done in two parts.
    25.     // The PlayerLoopSystem of type SimulationSystemGroup has to be found,
    26.     // stored, and removed before adding it to the FixedUpdate PlayerLoopSystem.
    27.  
    28.     PlayerLoopSystem playerLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
    29.  
    30.     // simulationSystem has to be constructed or compiler will complain due to
    31.     //    using non-assigned variables.
    32.     PlayerLoopSystem simulationSystem = new PlayerLoopSystem();
    33.     bool simSysFound = false;
    34.  
    35.     // Find the location of the SimulationSystemGroup under the Update Loop
    36.     for (var i = 0; i < playerLoop.subSystemList.Length; ++i)
    37.     {
    38.       int subsystemListLength = playerLoop.subSystemList[i].subSystemList.Length;
    39.  
    40.       // Find Update loop...
    41.       if (playerLoop.subSystemList[i].type == typeof(Update))
    42.       {
    43.         // Pop out SimulationSystemGroup and store it temporarily
    44.         var newSubsystemList = new PlayerLoopSystem[subsystemListLength - 1];
    45.         int k = 0;
    46.  
    47.         for (var j = 0; j < subsystemListLength; ++j)
    48.         {
    49.           if (playerLoop.subSystemList[i].subSystemList[j].type == typeof(SimulationSystemGroup))
    50.           {
    51.             simulationSystem = playerLoop.subSystemList[i].subSystemList[j];
    52.             simSysFound = true;
    53.           }
    54.           else
    55.           {
    56.             newSubsystemList[k] = playerLoop.subSystemList[i].subSystemList[j];
    57.             k++;
    58.           }
    59.         }
    60.         playerLoop.subSystemList[i].subSystemList = newSubsystemList;
    61.       }
    62.     }
    63.  
    64.     // This should never happen if SimulationSystemGroup was created like usual
    65.     // (or at least I think it might not happen :P )
    66.     if (!simSysFound)
    67.       throw new System.Exception("SimulationSystemGroup was not found!");
    68.  
    69.     // Round 2: find FixedUpdate...
    70.     for (var i = 0; i < playerLoop.subSystemList.Length; ++i)
    71.     {
    72.       int subsystemListLength = playerLoop.subSystemList[i].subSystemList.Length;
    73.  
    74.       // Found FixedUpdate
    75.       if (playerLoop.subSystemList[i].type == typeof(FixedUpdate))
    76.       {
    77.         // Allocate new space for stored SimulationSystemGroup
    78.         //    PlayerLoopSystem, and place simulation group at index defined by
    79.         //    temporary variable.
    80.         var newSubsystemList = new PlayerLoopSystem[subsystemListLength + 1];
    81.         int k = 0;
    82.  
    83.         int indexToPlaceSimulationSystemGroupIn = subsystemListLength;
    84.  
    85.         for (var j = 0; j < subsystemListLength + 1; ++j)
    86.         {
    87.           if (j == indexToPlaceSimulationSystemGroupIn)
    88.             newSubsystemList[j] = simulationSystem;
    89.           else
    90.           {
    91.             newSubsystemList[j] = playerLoop.subSystemList[i].subSystemList[k];
    92.             k++;
    93.           }
    94.         }
    95.         playerLoop.subSystemList[i].subSystemList = newSubsystemList;
    96.       }
    97.     }
    98.  
    99.     // Set the beautiful, new player loop
    100.     ScriptBehaviourUpdateOrder.SetPlayerLoop(playerLoop);
    101.  
    102.     //PlayerLoopSystem newLoop = ScriptBehaviourUpdateOrder.CurrentPlayerLoop;
    103.     //DeepDive(ref newLoop);
    104.  
    105.     return true;
    106.   }
    107.  
    108.   void DeepDive(ref PlayerLoopSystem sys)
    109.   {
    110.     for (var i = 0; i < sys.subSystemList.Length; ++i)
    111.     {
    112.       try
    113.       {
    114.         Debug.Log(sys.subSystemList[i].type);
    115.         DeepDive(ref sys.subSystemList[i]);
    116.       }
    117.       catch { }
    118.     }
    119.   }
    120. }
    (NOTE: I've included this function
    DeepDive for anybody curious to see how the PlayerLoopSystem is really structured, and how to iterate over it. It's pretty jank. Its just a recursive method of iteration (I think that's how the Entity Debugger does it as well, but idk))

    SimulationSystemGroup, along with all of its respective sub-systems, now show under FixedUpdate in EntityDebugger. YAY!!

    Screen Shot 2020-02-06 at 4.28.27 PM.png

    Also, any systems manually added to SimulationSystemGroup after world bootstrapping will be also in FixedUpdate. Its nice how this works out!
     
    Last edited: Feb 8, 2020
    Lernom, Syneh, tarahugger and 2 others like this.
  9. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,510
    zardini123 likes this.
  10. zardini123

    zardini123

    Joined:
    Jan 5, 2013
    Posts:
    68
    This is incredibly exciting!! That interpolation looks fantastic!

    There is some users that have been developing their own fixed update + interpolation solution here. I'm happy to see that there is a official solution in the works!
     
    Edy likes this.
  11. rochg

    rochg

    Joined:
    Jan 14, 2017
    Posts:
    4
    I noticed the 0.7.0 version of Entities included this changelog comment:

    Users can control the update behaviour of a ComponentSystemGroup via an update callback. See the documentation for ComponentSystemGroup.UpdateCallback, as well as examples in FixedRateUtils.

    From a quick experiment, adding something like the following...

    Code (CSharp):
    1.  
    2. FixedRateUtils.EnableFixedRateWithCatchUp(World.GetOrCreateSystem<SimulationSystemGroup>(),
    3.                                     UnityEngine.Time.fixedDeltaTime);
    4.  
    ... will get the physics (and the rest of the simulation group) running at a fixed framerate. An ECS cube will now fall at the same rate as a traditional Unity cube.

    As you'd imagine, if you do this, things will not be interpolated.

    Anyway, just posting in case this is useful to others. There may be more caveats yet to be discovered.
     
  12. diesoftgames

    diesoftgames

    Joined:
    Nov 27, 2018
    Posts:
    122
    I'm fiddling with this right now in a fresh project in 2020.1 and whenever I call this on a system without [DisableAutoCreation] it simply hangs Unity when I hit play, and calling it on a system with [DisableAutoCreation] doesn't seem to do anything (that system still never gets updated afaict). Were you testing this with 2019.3? I didn't try downgrading Unity, but I did test both 0.8.0 and 0.7.0 of Unity.Entities and I see the same behavior with both.

    UPDATE: I zero'd in a little more on the problem. This only seems to work if I call it from a system's OnUpdate and that system needs to be in the system group which you're attempting to set to fixed rate. Any other scenarios I tried resulted in everything just hanging forever. The good news is, it appears to be working correctly in those conditions so I should be able to get my fixed time step physics working with this. Thanks for your posts about all this, they were very helpful.
     
    Last edited: Apr 7, 2020
    edalbeci likes this.
  13. rochg

    rochg

    Joined:
    Jan 14, 2017
    Posts:
    4
    Hi @diesoftgames. Glad you got it to work. My posts ended up across two threads - I mentioned about needing to call from OnUpdate etc here : How to do frame-rate independent physics in DOTS. Sorry I should have linked this thread to the other one.

    Be interesting to hear anything else you discover or conclude in your experiments. I have a project that I’d like to switch back to fixed rate physics, but ideally without taking the whole simulation group to fixed rate.
     
    diesoftgames likes this.
  14. diesoftgames

    diesoftgames

    Joined:
    Nov 27, 2018
    Posts:
    122
    Yeah I was looking at that other thread too. What got me on the right track was looking at *exactly* what you did. So in your example you're calling EnableFixedRateWithCatchUp during the update of the system group you want to run fixed rate. That was the crucial bit that got it to work. So what I'm doing right now that I'm excited about (having come from Entities 0.1.0 where I had two distinct fixed and simulation loops) is something like this:
    • Create a DefaultSimulationSystemGroup for general use instead of using automatic grouping of systems (this will help me order things how I want)
    • Create a FixedRateSystemGroup which runs before DefaultSimulationSystemGroup.
    • Inside FixedRateSystemGroup, run a system which calls EnableFixedRateWithCatchUp on the FixedRateSystemGroup.
    • Add however many SystemGroups to the FixedRateSystemGroup I need
    And now I have a fixed update loop, but it runs AFTER ECS initialization and before default simulation, which is ideal for me. Previously I was checking inputs in fixed update initialization and then again in simulation initialization which was annoying and obviously not the best for perf.

    I run this like you would legacy Unity with anything that is physics related going in the FixedRateSystemGroup, and everything else that doesn't need to be fixed rate just going in the default simulation group. I'm still digging into it so I'm not sure how happy I am with it yet, but it looks like it works just based on logging what's going on and ensuring that the FixedRateSystemGroup gets called multiple times when needed to play catch up.
     
  15. diesoftgames

    diesoftgames

    Joined:
    Nov 27, 2018
    Posts:
    122
    What's also really appealing about this new design possible in 0.7.0+ that wasn't in the old version I was on (aside from having the fixed group run at a more appropriate time and being visible in the entity debugger) is that I no longer need to used the FixedUpdate method of a MonoBehaviour to set up those system groups. I really like using the system attributes for ordering, and now that's one less MonoBehaviour instance requirement in my scene :)
     
  16. diesoftgames

    diesoftgames

    Joined:
    Nov 27, 2018
    Posts:
    122
    Something kind of important I'm noticing with EnableFixedRateWithCatchUp: as far as I can tell it's not guaranteed to be called the same number of times per frames as FixedUpdate() is. I ran into this while adapting Rewired to work with my solution (it can only be updated in Update or FixedUpdate, so I was queueing up the inputs in FixedUpdate and then dequeuing in my FixedRateSystemGroup). On most frames it will work as expected and we'll see the same number of calls in FixedUpdate and fixed rate groups, but then every couple of frames we'll see a discrepancy where we have one less FixedRate update on one frame, and one extra on the next.
     
  17. edalbeci

    edalbeci

    Joined:
    Jan 21, 2018
    Posts:
    36
    Thanks @rochg and @diesoftgames for all of your detective work. Just wanted to pitch in something else I realized. When switching scenes, I had to `DisableFixedRate` on the old system group:

    Code (CSharp):
    1. FixedRateUtils.DisableFixedRate(World.GetOrCreateSystem<SimulationSystemGroup>());
    2. SceneManager.LoadScene("Cart");  // your existing scene switch
    If I don't do that, I get an assertion error. Full traceback included below, so that anyone looking for the assertion error can find this thread. I suspect the OnUpdate callback on the old system group lingers, even if the system group is destroyed.

    AssertionException: Assertion failure. Value was False
    Expected: True
    PopTime without a matching PushTime
    UnityEngine.Assertions.Assert.Fail (System.String message, System.String userMessage) (at /Users/builduser/buildslave/unity/build/Runtime/Export/Assertions/Assert/AssertBase.cs:29)
    UnityEngine.Assertions.Assert.IsTrue (System.Boolean condition, System.String message) (at /Users/builduser/buildslave/unity/build/Runtime/Export/Assertions/Assert/AssertBool.cs:20)
    Unity.Entities.World.PopTime () (at Library/PackageCache/com.unity.entities@0.11.1-preview.4/Unity.Entities/World.cs:510)
    Unity.Entities.FixedRateUtils+FixedRateCatchUpManager.UpdateCallback (Unity.Entities.ComponentSystemGroup group) (at Library/PackageCache/com.unity.entities@0.11.1-preview.4/Unity.Entities/FixedRateUtils.cs:93)
    Unity.Entities.ComponentSystemGroup.OnUpdate () (at Library/PackageCache/com.unity.entities@0.11.1-preview.4/Unity.Entities/ComponentSystemGroup.cs:402)
    Unity.Entities.ComponentSystem.Update () (at Library/PackageCache/com.unity.entities@0.11.1-preview.4/Unity.Entities/ComponentSystem.cs:109)
    Unity.Entities.ScriptBehaviourUpdateOrder+DummyDelegateWrapper.TriggerUpdate () (at Library/PackageCache/com.unity.entities@0.11.1-preview.4/Unity.Entities/ScriptBehaviourUpdateOrder.cs:192)

    And for those just scrolling to the bottom of this thread, make sure to only invoke `EnableFixedRateWithCatchup` only once, as @rothg did (and as I'm assuming @diesoftgames did). If you don't, Unity loops forever. In summary, to enable a fixed update physics system, this is all I added.

    Code (CSharp):
    1. using Unity.Entities;
    2.  
    3. // In theory, I should've attached/removed a FixedTimeStepHackInitComponent. Using class var so this script is standalone.
    4. [UpdateInGroup(typeof(SimulationSystemGroup))]
    5. public class FixedTimeStepHack : SystemBase
    6. {
    7.     public bool _IsInitialized;
    8.  
    9.     protected override void OnUpdate()
    10.     {
    11.         if (!_IsInitialized)
    12.         {
    13.             FixedRateUtils.EnableFixedRateWithCatchUp(
    14.                 World.GetOrCreateSystem<SimulationSystemGroup>(),
    15.                 UnityEngine.Time.fixedDeltaTime);
    16.             _IsInitialized = true;
    17.         }
    18.     }
    19. }
    20.  
    21.  
     
  18. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,487
    So you know, the DOTS physics forum is here.
     
    edalbeci likes this.