osu-framework icon indicating copy to clipboard operation
osu-framework copied to clipboard

`GameThread.Clock.CurrentTime` is set at the end of a frame

Open Susko3 opened this issue 5 months ago • 5 comments

  • Noticed when looking into https://github.com/ppy/osu-framework/issues/4808

I would expect the current time to be set when a new frame begins.

This doesn't matter in multi-threaded mode, as a new frame begins immediately after the previous ends.

But I think it may cause unnecessary animation jitter in single thread mode. Probably less visible if all threads have constant frame time. It's really hard to think about this "CurrentTime" which is not current, but in the past; but it's always in the past, so it kinda works out.

Code that is updating the clock at the end:

https://github.com/ppy/osu-framework/blob/3dbcf0154797ee8977b6278c91c8071421210b21/osu.Framework/Threading/GameThread.cs#L457-L461

Susko3 avatar Jul 03 '25 23:07 Susko3

First, please explain why this matters. From what I gather, you're saying "the time should be set at the start of the frame, not the end".

But frames are updated in a loop, importantly with any delay or sleep in the middle. How does setting the time at the end of the method or the start of the next method call change anything?

peppy avatar Jul 04 '25 10:07 peppy

It doesn't matter in multi-threaded, but it matters in single-threaded as a different thread starts when the current is finished. If the threads are run at a constant rate, it's just an offset, but if there is lag in one of the threads, the timing is kinda off.

As show in the graph below, "code running here" will observe the specified CurrentTime and ElapsedFrameTime. This can cause animation jitter if a thread has a lag spike or is jittering in general.

Usual scenario, the orange dot is transforming based on the current time. Thread timings are measured from a real game running @ 240 Hz single thread via Stopwatch.GetTimestamp().

Image

Scenario with lag, notice how the dot is in the wrong place after the lag spike. Lag spike simulated, I've just drawn it on the graph.

Image

It's kinda rendering/calculating "one frame in the past". The code is completely oblivious to the lag, it'll only get noticed in the next frame. I've not actually measured anything with a slomo camera, but this is how I think it should look like.

It's really hard to think about what could/should be wrong. And perhaps something is making use of this "wrong" behaviour.

Susko3 avatar Jul 04 '25 22:07 Susko3

Where is the "lag" coming from in this image?

peppy avatar Jul 06 '25 13:07 peppy

Doesn't matter where the lag is coming from. In this image, it could be a GC pause, or the call to SDL_PumpEvents taking long.

Susko3 avatar Jul 06 '25 14:07 Susko3

For the GC pause case, I believe it has just as high a chance of running before the time update to after, so isn't a valid consideration.

For pump events I would have to re-evaluate order of execution to discern whether this is a valid consideration, but the hope is that this would never take long enough for it to be an issue.

peppy avatar Jul 06 '25 15:07 peppy