1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-13 19:54:15 +08:00

Fix dragging difficulty filter slider on a very large collection causing lag (#37701)

The delayed collection evaluation (making it async) is the main fix, but
the debounce seems like good to have from a sanity angle. Alternative
would be `Scheduler.AddOnce` to avoid multiple input events per frame
being handled.

Closes https://github.com/ppy/osu/issues/37615.
This commit is contained in:
Dean Herbert
2026-05-11 19:12:00 +09:00
committed by GitHub
Unverified
parent c84777d7bf
commit 6fab856b45
11 changed files with 50 additions and 22 deletions
@@ -144,7 +144,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("4 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 4);
AddStep("filter one room", () => container.Filter.Value = new FilterCriteria { SearchString = rooms.First().Name });
AddStep("filter one room", () => container.Filter.Value = new LoungeFilterCriteria { SearchString = rooms.First().Name });
AddUntilStep("1 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 1);
@@ -160,13 +160,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("add rooms", () => rooms.AddRange(GenerateRooms(3, new CatchRuleset().RulesetInfo)));
// Todo: What even is this case...?
AddStep("set empty filter criteria", () => container.Filter.Value = new FilterCriteria());
AddStep("set empty filter criteria", () => container.Filter.Value = new LoungeFilterCriteria());
AddUntilStep("5 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 5);
AddStep("filter osu! rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo });
AddStep("filter osu! rooms", () => container.Filter.Value = new LoungeFilterCriteria { Ruleset = new OsuRuleset().RulesetInfo });
AddUntilStep("2 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 2);
AddStep("filter catch rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new CatchRuleset().RulesetInfo });
AddStep("filter catch rooms", () => container.Filter.Value = new LoungeFilterCriteria { Ruleset = new CatchRuleset().RulesetInfo });
AddUntilStep("3 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 3);
}
@@ -183,11 +183,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("both rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 2);
AddStep("filter public rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Public });
AddStep("filter public rooms", () => container.Filter.Value = new LoungeFilterCriteria { Permissions = RoomPermissionsFilter.Public });
AddUntilStep("private room hidden", () => container.DrawableRooms.All(r => !r.Room.HasPassword));
AddStep("filter private rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Private });
AddStep("filter private rooms", () => container.Filter.Value = new LoungeFilterCriteria { Permissions = RoomPermissionsFilter.Private });
AddUntilStep("public room hidden", () => container.DrawableRooms.All(r => r.Room.HasPassword));
}
+1 -1
View File
@@ -15,7 +15,7 @@ namespace osu.Game.Online.Rooms
private readonly RoomStatusFilter? status;
private readonly string category;
public GetRoomsRequest(FilterCriteria filterCriteria)
public GetRoomsRequest(LoungeFilterCriteria filterCriteria)
{
mode = filterCriteria.Mode;
category = filterCriteria.Category;
@@ -5,7 +5,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
public class FilterCriteria
public class LoungeFilterCriteria
{
public string SearchString = string.Empty;
public RoomModeFilter Mode;
@@ -31,7 +31,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
/// <summary>
/// The current filter criteria. Should be managed externally.
/// </summary>
public readonly Bindable<FilterCriteria?> Filter = new Bindable<FilterCriteria?>();
public readonly Bindable<LoungeFilterCriteria?> Filter = new Bindable<LoungeFilterCriteria?>();
/// <summary>
/// The currently user-selected room.
@@ -87,7 +87,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
Filter.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true);
}
private void applyFilterCriteria(FilterCriteria? criteria)
private void applyFilterCriteria(LoungeFilterCriteria? criteria)
{
roomFlow.Children.ForEach(r =>
{
@@ -22,7 +22,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private IAPIProvider api { get; set; } = null!;
public required Action<Room[]> RoomsReceived { get; init; }
public readonly IBindable<FilterCriteria?> Filter = new Bindable<FilterCriteria?>();
public readonly IBindable<LoungeFilterCriteria?> Filter = new Bindable<LoungeFilterCriteria?>();
private GetRoomsRequest? lastPollRequest;
@@ -73,7 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private IDisposable? joiningRoomOperation;
private readonly Bindable<FilterCriteria?> filter = new Bindable<FilterCriteria?>();
private readonly Bindable<LoungeFilterCriteria?> filter = new Bindable<LoungeFilterCriteria?>();
private readonly Bindable<bool> hasListingResults = new Bindable<bool>();
private readonly IBindable<bool> operationInProgress = new Bindable<bool>();
private readonly IBindable<bool> isIdle = new BindableBool();
@@ -262,7 +262,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
filter.Value = CreateFilterCriteria();
}
protected virtual FilterCriteria CreateFilterCriteria() => new FilterCriteria
protected virtual LoungeFilterCriteria CreateFilterCriteria() => new LoungeFilterCriteria
{
SearchString = searchTextBox.Current.Value,
Ruleset = ruleset.Value,
@@ -56,7 +56,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
StatusDropdown.Current.BindValueChanged(_ => showInProgress.Alpha = StatusDropdown.Current.Value == RoomModeFilter.Open ? 1 : 0, true);
}
protected override FilterCriteria CreateFilterCriteria()
protected override LoungeFilterCriteria CreateFilterCriteria()
{
var criteria = base.CreateFilterCriteria();
criteria.Category = @"realtime";
@@ -38,7 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
return base.CreateFilterControls().Append(categoryDropdown);
}
protected override FilterCriteria CreateFilterCriteria()
protected override LoungeFilterCriteria CreateFilterCriteria()
{
var criteria = base.CreateFilterCriteria();
@@ -45,7 +45,7 @@ namespace osu.Game.Screens.Select
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider, OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
SliderContainer.AddRange(new Drawable[]
{
+17 -5
View File
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -13,6 +12,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Collections;
using osu.Game.Configuration;
@@ -244,8 +244,20 @@ namespace osu.Game.Screens.Select
});
searchTextBox.Current.BindValueChanged(_ => updateCriteria());
difficultyRangeSlider.LowerBound.BindValueChanged(_ => updateCriteria());
difficultyRangeSlider.UpperBound.BindValueChanged(_ => updateCriteria());
ScheduledDelegate? sliderDebounce = null;
// For slider dragging (where input events can arrive very often), even creating criteria can have
// overhead, especially when a collection is selected (see ToImmutableHashSet() call).
void debouncedUpdateCriteria()
{
sliderDebounce?.Cancel();
sliderDebounce = Scheduler.AddDelayed(() => updateCriteria(), 50);
}
difficultyRangeSlider.LowerBound.BindValueChanged(_ => debouncedUpdateCriteria());
difficultyRangeSlider.UpperBound.BindValueChanged(_ => debouncedUpdateCriteria());
showConvertedBeatmapsButton.Active.BindValueChanged(_ => updateCriteria());
sortDropdown.Current.BindValueChanged(_ => updateCriteria());
groupDropdown.Current.BindValueChanged(_ => updateCriteria());
@@ -258,7 +270,7 @@ namespace osu.Game.Screens.Select
updateCriteria();
});
collectionsSubscription = realm.RegisterForNotifications(r => r.All<BeatmapCollection>(), (collections, changeSet) =>
collectionsSubscription = realm.RegisterForNotifications(r => r.All<BeatmapCollection>(), (_, changeSet) =>
{
if (changeSet != null && groupDropdown.Current.Value.Value == GroupMode.Collections)
updateCriteria();
@@ -293,7 +305,7 @@ namespace osu.Game.Screens.Select
AllowConvertedBeatmaps = showConvertedBeatmapsButton.Active.Value,
Ruleset = ruleset.Value,
Mods = mods.Value,
CollectionBeatmapMD5Hashes = collectionDropdown.Current.Value?.Collection?.PerformRead(c => c.BeatmapMD5Hashes).ToImmutableHashSet(),
Collection = collectionDropdown.Current.Value?.Collection,
LocalUserId = isValidUser ? localUser.Value.Id : null,
LocalUserUsername = isValidUser ? localUser.Value.Username : null,
};
+17 -1
View File
@@ -3,11 +3,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using osu.Game.Beatmaps;
using osu.Game.Collections;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Filter;
using osu.Game.Rulesets.Mods;
@@ -111,10 +113,24 @@ namespace osu.Game.Screens.Select
}
}
private ImmutableHashSet<string>? collectionBeatmapMD5Hashes;
private Live<BeatmapCollection>? collection;
/// <summary>
/// Hashes from the <see cref="BeatmapCollection"/> to filter to.
/// </summary>
public IEnumerable<string>? CollectionBeatmapMD5Hashes { get; set; }
public IEnumerable<string>? CollectionBeatmapMD5Hashes =>
collectionBeatmapMD5Hashes ??= Collection?.PerformRead(c => c.BeatmapMD5Hashes.ToImmutableHashSet());
public Live<BeatmapCollection>? Collection
{
get => collection;
set
{
collection = value;
collectionBeatmapMD5Hashes = null;
}
}
public IRulesetFilterCriteria? RulesetCriteria { get; set; }