1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-30 08:40:06 +08:00

Do not emit multiple consequent replay frames with the exact same data

With judgements occurring forcing emission of important frames, there's
the question of emitting redundant frames. In a majority of cases
still, a judgement *will* be tied to a specific user input, which means
that there's a high likelihood of duplicate frames, as one will be
emitted by the input change, and another by the judgement.

To a degree this is a pre-existing issue. The replay recording code
responds to both mouse movement and key presses by recording frames, so
depending on the intrinsic (basically undefined) ordering between
handling mouse move and key presses, it was possible for multiple frames
to be emitted with the same timestamp to the replay, each containing
partial input state changes (e.g. first frame with mouse position
changed, and the second with a key pressed).

The extent to which this increases the frame count will obviously depend
on the beatmap, but in my ballpark testing across all rulesets on a
single "relatively standard" beatmap (think no aspire chicanery), around
4-10% of frames can end up being duplicates / not meaningful. This has
implications both on replay size and on playback performance.

This commit therefore performs de-duplication of the frames based on
timestamp - only the last frame with a given timestamp ends up in the
final replay.

The CPU cost of this is negligible to the point of not being useful to
profile - it's a single condition gated behind a single float comparison
This commit is contained in:
Bartłomiej Dach
2025-05-16 08:37:41 +02:00
Unverified
parent 9f91c2e25c
commit cd4ab9307c
+10 -1
View File
@@ -86,8 +86,17 @@ namespace osu.Game.Rulesets.UI
if (frame != null)
{
target.Replay.Frames.Add(frame);
// only keep the last recorded frame for a given timestamp.
// this reduces redundancy of frames in the resulting replay.
if (last?.Time == frame.Time)
target.Replay.Frames[^1] = frame;
else
target.Replay.Frames.Add(frame);
// the above de-duplication is not done for spectator client because it's more complicated to do
// (not possible to "un-send" a sent frame).
// a similar solution to the above could be applied at `FrameDataBundle` buffering level in `SpectatorClient` if deemed necessary,
// but it'd still not be completely matching (consider situation where buffer happens to be flushed between frames with the same timestamp).
spectatorClient?.HandleFrame(frame);
}
}