Neos-Metaverse / NeosPublic

A public issue/wiki only repository for the NeosVR project
193 stars 9 forks source link

Long Host Uptime Results in General "Issues" (T Floating Point Inaccuracy) #3609

Open Enverex opened 2 years ago

Enverex commented 2 years ago

Describe the bug?

Honestly couldn't think of a better title due to the myriad of weirdness this can cause, may be worth adjusting later.

Currently a lot of things in Neos appear to rely on the session (which in reality is the host server, not session) uptime for calculations. This may be using created content using the T node, or internal components such as panners. The problem with this is that it seems to suffer from the classic Unity float inaccuracy and loses precision pretty rapidly. After a little more than a day of uptime, precision will be down to only one decimal place. As an example: After a few days, anything using a panner will have become a jittery mess.

While not a fix, a good start to help mitigate this would be for T to be based on the session uptime, not the host service uptime. At least this way when sessions recycle due to inactivity, their T will also be reset back to a more accurate state. As it stands, the entire Neos service needs to be restarted, bringing down all sessions it hosts, not just ones that may need cycling.

Relevant issues

None found.

To Reproduce

Leave a host running for a few days and examine the output of T. Panning an object up and down using a 1/2/3D panner should also produce a very noticeable (bad) output at this point.

Expected behavior

For accuracy of timers to remain... accurate.

Log Files

No response

Screenshots

No response

How often does it happen?

Always

Does the bug persist after restarting Neos?

Yes

Neos Version Number

2022.1.28.1335

What Platforms does this occur on?

Windows, Linux, Android / Quest

Link to Reproduction Item/World

No response

Did this work before?

No

If it worked before, on which build?

No response

Additional context

No response

Reporters

No response

iamgreaser commented 2 years ago

Easy fix: Make T output a double. Make a video on it. Bring the video into a time machine and show it to past-Froox. Problem: Needs a time machine. Video potentially prone to grandfather paradox.

Best fix: Purge all uses of 32-bit floats from LogiX as well as the component and slot hierarchy, and stick to 64-bit doubles instead. The only place you should be using 32-bit floats is when you are passing information to and from the GPU.


I recall predicting this was a problem around the "Let It Snow" Creator Jam event (happened I think just after Christmas 2021 for me) and deciding to crunch some numbers pertaining to single-precision floating-point and its actual precision.

From my experience, the dT node remains decently accurate even when T is in floating-point land. While you'll potentially experience extra clock drift, if you accumulate into a double variable, you should retain smooth timing of things.

Frozenreflex commented 2 years ago

Best fix: Purge all uses of 32-bit floats from LogiX as well as the component and slot hierarchy, and stick to 64-bit doubles instead. The only place you should be using 32-bit floats is when you are passing information to and from the GPU.

Floats are standard for 3D math in games, switching them all to doubles would kill performance for no real gain, not to mention the effort needed to migrate everything, both in code and for users. Time is one of the few things that should be represented by a different type,

Easy fix: Make T output a double. Make a video on it. Bring the video into a time machine and show it to past-Froox. Problem: Needs a time machine. Video potentially prone to grandfather paradox.

and is already represented by a double, but is being cast to a float too early for Panners:

// FrooxEngine.TimeController
public double WorldTime { get; private set; }
    public float Position 
    {
        get 
        {
            return RawPosition + _offset;
        }
        set 
        {
            _offset.Value = value - RawPosition;
        }
    }
    public float UnwrappedPosition => _speed.Value * (float)base.Time.WorldTime + _preOffset.Value;
    public float RawPosition 
    {
        get 
        {
            if (!PingPong.Value) return UnwrappedPosition % Repeat;
            return MathX.PingPong(UnwrappedPosition, Repeat);
        }
    }

And for T, an output in double is not being provided. The simpler solutions are the best solutions here: provide a double version of T, and cast to float for components later.