Search Unity

Bug FixedUpdate and Time.deltaTime inconsistent since 2020.2

Discussion in 'Physics' started by arkano22, Mar 2, 2021.

  1. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,929
    Hi there,

    Some context: I develop a physics engine for Unity, available in the Asset Store. In this engine, I rely on Unity's fixed timestep scheme, and perform physics interpolation by keeping track of remaining "unsimulated" time to lerp between physics states.

    Recently I've been getting multiple reports from users about very strange behavior when building for mobile platforms (iOS and Android).

    Upon investigation, seems that Time.deltaTime returns completely incorrect values on these platforms. This is what I do:

    Code (CSharp):
    1. float accumulator = 0;
    2. FixedUpdate()
    3. {
    4.     accumulator -= Time.fixedDeltaTime;
    5. }
    6.  
    7. Update()
    8. {
    9.     accumulator += Time.deltaTime;
    10.     Debug.Log(accumulator);
    11. }
    According to Unity's documentation, FixedUpdate() is called at the beginning of the frame zero, one, or more times. Update() is called once per frame, after all calls to FixedUpdate(). So, if the fixed timestep is 0.02 and last frame's duration is 0.07 there will be 3 calls to FixedUpdate(), and accumulator should be (-0.02 -0.02 -0.02)+0.07 = 0.01: the remaining "unsimulated" time. FixedUpdate() won't be called again until the amount of wall clock time accumulated exceeds Time.fixedDeltaTime. A standard fixed timestepping approach, right?

    So the expected result is for accumulator to always oscillate between 0 and Time.fixedDeltaTime, since any time added by Update is eventually consumed by FixedUpdate calls.

    This has worked fine since Unity 5. However in 2020.2, the accumulator is always some large negative number (between -80 and -150-ish). Only possible explanation is that Time.deltaTime is not reporting accurate timings near startup. This is only reproducible in iOS/Android, afaik.

    This completely breaks physics interpolation in these platforms, as I can't get any reliable time estimation to interpolate physics states. Disabling interpolation is the only workaround, and it results in jittery jitters. It's a pretty critical issue for me.

    Does anyone have encountered this before, or has any change been introduced to the way 2020.2 works that would explain this?
     
    Last edited: Mar 2, 2021
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,492
  3. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,929
    Yep, my initial guess was that the changes to Time.deltaTime in 2020.2 were the culprit (if this is indeed a bug). Should this thread be moved to any other subforum?

    Seems strange that it hasn't been reported yet, so I wanted to confirm it's not me or my interpretation of how it should work. Guess I should just file a bug report then.
     
  4. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,510
    I wouldn't develop any logic assuming a specific order of execution between Update and FixedUpdate because both them can occur multiple times in a row unpredictably.

    Update and FixedUpdate may be better seen as two different "execution flows". Several FixedUpdate calls may occur between two Update calls and, most frequently, several Update calls may occur between each two FixedUpdate calls. More details:


    Previously to Unity 2020.2 reading Time.deltaTime from Update returned in the time between each Update call. Starting in Unity 2020.2 Time.deltaTime is the frame presentation time, typically 1 / refesh rate.

    Additionally, you don't need to use Time.fixedDeltaTime directly. When read from FixedUpdate Time.deltaTime returns the fixed delta time (https://docs.unity3d.com/ScriptReference/Time-fixedDeltaTime.html).
     
  5. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,929
    Not assuming any specific order, even though FixedUpdate -when called- is always called before Update: https://docs.unity3d.com/Manual/ExecutionOrder.html.

    As far as I know, FixedUpdate() has to be called multiple times in a row while the amount of non-simulated wall-clock time is larger than the timestep. In pseudo code:

    Code (CSharp):
    1.  
    2. UpdatePhysics()
    3. {
    4.     accumulator += timeSinceLastFrame;
    5.     while (accumulator >= timestep)
    6.     {
    7.         FixedUpdate();
    8.         accumulator -= timestep;
    9.     }
    10. }
    11.  
    This is how fixed timestepping works in general, not just in Unity. Assuming the amount of FixedUpdate calls per frame follows this scheme and Time.deltaTime accurately returns last frame's duration as seen from the user's perspective (and both assumptions held true up to 2020.2), then the accumulator value at the end of a frame should never exceed the timestep value, or go below zero. And it does not in editor, does not in standalone desktop builds, but does (by a huge margin, 80 seconds in the first frame isn't reasonable, nor could have been the result of floating point error accumulation) in iOS/Android, only in 2020.2.
     
    Last edited: Mar 2, 2021
  6. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,510
    Not sure if it's relevant here, but note that there's a missing arrow in that graph, and that may lead to confusions. Here's the corrected graph:

    upload_2021-3-2_16-57-11.png

    Correct. At the same time, Update has also to be called multiple times in a row when the frame rate is faster than the physics rate, which is the most frequent case. Physics by default runs at 50 Hz, while typical display frequencies are at least 60 Hz, with an increasing adoption of the 144 Hz displays. In the later case, this means 2-3 Update calls between each FixedUpdate call.

    Indeed, multiple FixedUpdate calls in a row typically happen only in abnormal situations such as frame rate drops (as the frame rate must fall below 50 Hz).

    Please don't get me wrong, I'm not saying you're doing something incorrect. I don't know why you're having those issues, it may well be a bug in Unity. But I think it could be useful to you to have a precise picture of how Update and FixedUpdate work in Unity, so you could test those cases better.
     
    april_4_short likes this.
  7. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,929
    Thanks Edy :). I’m aware of everything you mentioned, so we’re on the same page. I believe the arrow you added to the graph shouldn’t be there though. It’s not that Update() can be called multiple times per frame, but that FixedUpdate() might not be called at all during a frame so you get several frames in a row with no calls to FixedUpdate(), only Update(). On the other hand, if the fixed timestep is very small (or a frame takes too long to render) you can get frames with multiple calls to FixedUpdate(). In very bad cases this leads to death spiralling, so max fixed timestep is used to limit the amount of wall clock time simulated in a single frame.

    Still think this is a bug in Unity, so I reported it. Will come back to this thread once I hear back from them, and post the outcome.
     
    Last edited: Mar 2, 2021
  8. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,510
    Not sure if I've explained it correctly. Exactly, that's what typically happens. Most of the times you get several frames in a row with no calls to FixedUpdate(), only Update(). But without the arrow I added the graph implies that there will be always at least one FixedUpdate() call before or after each Update() call, which is incorrect.
     
    arkano22 likes this.
  9. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,929
    Edy likes this.
  10. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    By the way, if you're curious what was causing it... The loop you pasted above is exactly how fixed update works. The issue was that the time was accumulating during the startup sequence, and that caused multiple "fixed update" calls to be issued in order to catch up during the first frame of the game.
     
    Edy and arkano22 like this.