1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-13 20:33:35 +08:00

Fix song select scrolling performance when user has many beatmaps loaded (#37666)

Also fixes wrong rank showing briefly in some scenarios.

---

I'm quite confused why the overhead is in the post-async-filter
collection access, but it is. It occurs when using `MaxBy` (realm
snapshot creation), but also when calling `.Count` on the collection, or
even just accessing `sender[0]`. I tried everything, and ended up
settling on simplifying the realm part enough that we can do
post-filtering without much sweat or mess.

Before:


https://github.com/user-attachments/assets/59aed895-03ed-4923-9515-9a5426156f7e

After:


https://github.com/user-attachments/assets/9a37f34a-c955-45bf-877f-89f248d8ea72

Tested using [this realm](https://screvillshot.s-ul.eu/YJUJ4SR1).

- Closes https://github.com/ppy/osu/issues/37574.
- Closes https://github.com/ppy/osu/issues/37661.
This commit is contained in:
Dean Herbert
2026-05-08 20:25:55 +09:00
committed by GitHub
Unverified
parent 0457cb924a
commit 08c02e29b9
3 changed files with 20 additions and 6 deletions
@@ -76,6 +76,12 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestLocalRank()
{
AddStep("set null rank", () => this.ChildrenOfType<UpdateableRank>().ForEach(p =>
{
p.Hide();
p.Rank = null;
}));
foreach (var rank in Enum.GetValues<ScoreRank>())
{
AddStep($"set {rank.GetDescription()} rank", () => this.ChildrenOfType<UpdateableRank>().ForEach(p =>
+1
View File
@@ -55,6 +55,7 @@ namespace osu.Game.Scoring
/// <summary>
/// The <see cref="osu.Game.Beatmaps.BeatmapInfo.Hash"/> at the point in time when the score was set.
/// </summary>
[Indexed]
public string BeatmapHash { get; set; } = string.Empty;
public RulesetInfo Ruleset { get; set; } = null!;
@@ -72,15 +72,12 @@ namespace osu.Game.Screens.Select
private void updateSubscription()
{
scoreSubscription?.Dispose();
setRankFromScore(null);
if (beatmap == null)
return;
scoreSubscription = realm.RegisterForNotifications(r =>
r.GetAllLocalScoresForUser(api.LocalUser.Value.Id)
.Filter($@"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $0"
+ $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1", beatmap.ID, ruleset.Value.ShortName),
localScoresChanged);
scoreSubscription = realm.RegisterForNotifications(r => r.All<ScoreInfo>().Where(s => s.BeatmapHash == beatmap.Hash && !s.DeletePending), localScoresChanged);
}
private void localScoresChanged(IRealmCollection<ScoreInfo> sender, ChangeSet? changes)
@@ -90,7 +87,17 @@ namespace osu.Game.Screens.Select
if (changes?.HasCollectionChanges() == false)
return;
ScoreInfo? topScore = sender.MaxBy(info => (info.TotalScore, -info.Date.UtcDateTime.Ticks));
ScoreInfo? topScore = sender
// doing these post realm filter is most efficient.
.Where(s => s.UserID == api.LocalUser.Value.Id || s.UserID <= 1)
.Where(s => s.Ruleset.ShortName == ruleset.Value.ShortName)
.MaxBy(info => (info.TotalScore, -info.Date.UtcDateTime.Ticks));
setRankFromScore(topScore);
}
private void setRankFromScore(ScoreInfo? topScore)
{
updateable.Rank = topScore?.Rank;
updateable.Alpha = topScore != null ? 1 : 0;
}