From 85bbe13aa969f8368f91591c5d6590410e11d76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 10 Oct 2025 08:48:18 +0200 Subject: [PATCH] Move realm refetches of beatmap in song select wedges off of update thread From local testing on release build (such that online beatmaps are accessible) with a large database it seems that maybe this'll help with recurrent complaints of 'stutters'. Co-authored-by: Dean Herbert --- .../Screens/SelectV2/BeatmapMetadataWedge.cs | 46 +++++++++++++------ .../Screens/SelectV2/BeatmapTitleWedge.cs | 46 +++++++++++++------ 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs b/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs index 818176b3c4..4fd678407a 100644 --- a/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs +++ b/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs @@ -3,6 +3,8 @@ using System; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -402,24 +404,40 @@ namespace osu.Game.Screens.SelectV2 updateSubWedgeVisibility(); } + private CancellationTokenSource? userTagsCancellationSource; + private void updateUserTags() { - string[] tags = realm.Run(r => - { - // need to refetch because `beatmap.Value.BeatmapInfo` is not going to have the latest tags - r.Refresh(); - var refetchedBeatmap = r.Find(beatmap.Value.BeatmapInfo.ID); - return refetchedBeatmap?.Metadata.UserTags.ToArray() ?? []; - }); + userTagsCancellationSource?.Cancel(); + userTagsCancellationSource = new CancellationTokenSource(); - if (tags.Length == 0) - { - userTags.FadeOut(transition_duration, Easing.OutQuint); - return; - } + var token = userTagsCancellationSource.Token; - userTags.FadeIn(transition_duration, Easing.OutQuint); - userTags.Tags = (tags, t => songSelect?.Search($@"tag=""{t}""!")); + Task.Run(() => + { + string[] tags = realm.Run(r => + { + // need to refetch because `beatmap.Value.BeatmapInfo` is not going to have the latest tags + r.Refresh(); + var refetchedBeatmap = r.Find(beatmap.Value.BeatmapInfo.ID); + return refetchedBeatmap?.Metadata.UserTags.ToArray() ?? []; + }); + + Schedule(() => + { + if (token.IsCancellationRequested) + return; + + if (tags.Length == 0) + { + userTags.FadeOut(transition_duration, Easing.OutQuint); + return; + } + + userTags.FadeIn(transition_duration, Easing.OutQuint); + userTags.Tags = (tags, t => songSelect?.Search($@"tag=""{t}""!")); + }); + }, token); } } } diff --git a/osu.Game/Screens/SelectV2/BeatmapTitleWedge.cs b/osu.Game/Screens/SelectV2/BeatmapTitleWedge.cs index 21ac04b18a..427466e366 100644 --- a/osu.Game/Screens/SelectV2/BeatmapTitleWedge.cs +++ b/osu.Game/Screens/SelectV2/BeatmapTitleWedge.cs @@ -278,8 +278,13 @@ namespace osu.Game.Screens.SelectV2 }, token); } + private CancellationTokenSource? onlineDisplayCancellationSource; + private void updateOnlineDisplay() { + onlineDisplayCancellationSource?.Cancel(); + onlineDisplayCancellationSource = null; + if (onlineLookupResult.Value?.Status != SongSelect.BeatmapSetLookupStatus.Completed) { playCount.Value = null; @@ -291,20 +296,35 @@ namespace osu.Game.Screens.SelectV2 playCount.Value = new StatisticPlayCount.Data(onlineBeatmap?.PlayCount ?? -1, onlineBeatmap?.UserPlayCount ?? -1); favouriteButton.SetBeatmapSet(onlineLookupResult.Value.Result); - // the online fetch may have also updated the beatmap's status. - // this needs to be checked against the *local* beatmap model rather than the online one, because it's not known here whether the status change has occurred or not - // (think scenarios like the beatmap being locally modified). - // it also has to be handled explicitly like this because the working beatmap's `BeatmapInfo` will not receive these updates due to being detached - // (and because of https://github.com/ppy/osu/blob/4b73afd1957a9161e2956fc4191c8114d9958372/osu.Game/Screens/SelectV2/SongSelect.cs#L487-L488 - // which prevents working beatmap refetches caused by changes to the realm model of perceived low importance). - var status = realm.Run(r => + onlineDisplayCancellationSource = new CancellationTokenSource(); + var token = onlineDisplayCancellationSource.Token; + + Task.Run(() => { - r.Refresh(); - var refetchedBeatmap = r.Find(working.Value.BeatmapInfo.ID); - return refetchedBeatmap?.Status; - }); - if (status != null) - statusPill.Status = status.Value; + // the online fetch may have also updated the beatmap's status. + // this needs to be checked against the *local* beatmap model rather than the online one, because it's not known here whether the status change has occurred or not + // (think scenarios like the beatmap being locally modified). + // it also has to be handled explicitly like this because the working beatmap's `BeatmapInfo` will not receive these updates due to being detached + // (and because of https://github.com/ppy/osu/blob/4b73afd1957a9161e2956fc4191c8114d9958372/osu.Game/Screens/SelectV2/SongSelect.cs#L487-L488 + // which prevents working beatmap refetches caused by changes to the realm model of perceived low importance). + var status = realm.Run(r => + { + r.Refresh(); + var refetchedBeatmap = r.Find(working.Value.BeatmapInfo.ID); + return refetchedBeatmap?.Status; + }); + + if (status != null) + { + Schedule(() => + { + if (token.IsCancellationRequested) + return; + + statusPill.Status = status.Value; + }); + } + }, token); } } }