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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user