diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 379bd838cd..a9829fbf0c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -141,7 +141,7 @@ namespace osu.Game.Tests.Visual.SongSelect } } - public override async Task GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo? rulesetInfo = null, IEnumerable? mods = null, CancellationToken cancellationToken = default) + public override async Task GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo? rulesetInfo = null, IEnumerable? mods = null, CancellationToken cancellationToken = default, int debounceDelay = 0) { if (blockCalculation) { @@ -149,7 +149,7 @@ namespace osu.Game.Tests.Visual.SongSelect await calculationBlocker.Task.ConfigureAwait(false); } - return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken).ConfigureAwait(false); + return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken, 0).ConfigureAwait(false); } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs index cdb6900f06..5bb590a247 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs @@ -125,7 +125,7 @@ namespace osu.Game.Tests.Visual.UserInterface public StarDifficulty? Difficulty { get; set; } public override Task GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo? rulesetInfo = null, IEnumerable? mods = null, - CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default, int computationDelay = 0) => Task.FromResult(Difficulty); } } diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index fc4175415c..9a36388d5d 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -98,12 +98,13 @@ namespace osu.Game.Beatmaps /// /// The to get the difficulty of. /// An optional which stops updating the star difficulty for the given . + /// A delay in milliseconds before performing the /// A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state (but not during updates to ruleset and mods if a stale value is already propagated). - public IBindable GetBindableDifficulty(IBeatmapInfo beatmapInfo, CancellationToken cancellationToken = default) + public IBindable GetBindableDifficulty(IBeatmapInfo beatmapInfo, CancellationToken cancellationToken = default, int computationDelay = 0) { var bindable = new BindableStarDifficulty(beatmapInfo, cancellationToken); - updateBindable(bindable, currentRuleset.Value, currentMods.Value, cancellationToken); + updateBindable(bindable, currentRuleset.Value, currentMods.Value, cancellationToken, computationDelay); lock (bindableUpdateLock) trackedBindables.Add(bindable); @@ -118,13 +119,14 @@ namespace osu.Game.Beatmaps /// The to get the difficulty with. /// The s to get the difficulty with. /// An optional which stops computing the star difficulty. + /// In the case a cached lookup was not possible, a value in milliseconds of to wait until performing potentially intensive lookup. /// /// The requested , if non-. /// A return value indicates that the difficulty process failed or was interrupted early, /// and as such there is no usable star difficulty value to be returned. /// - public virtual Task GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo? rulesetInfo = null, - IEnumerable? mods = null, CancellationToken cancellationToken = default) + public virtual Task GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo? rulesetInfo = null, IEnumerable? mods = null, + CancellationToken cancellationToken = default, int computationDelay = 0) { // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. rulesetInfo ??= beatmapInfo.Ruleset; @@ -139,7 +141,7 @@ namespace osu.Game.Beatmaps return Task.FromResult(new StarDifficulty(beatmapInfo.StarRating, (beatmapInfo as IBeatmapOnlineInfo)?.MaxCombo ?? 0)); } - return GetAsync(new DifficultyCacheLookup(localBeatmapInfo, localRulesetInfo, mods), cancellationToken); + return GetAsync(new DifficultyCacheLookup(localBeatmapInfo, localRulesetInfo, mods), cancellationToken, computationDelay); } protected override Task ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken cancellationToken = default) @@ -206,11 +208,12 @@ namespace osu.Game.Beatmaps /// The to update with. /// The s to update with. /// A token that may be used to cancel this update. - private void updateBindable(BindableStarDifficulty bindable, IRulesetInfo? rulesetInfo, IEnumerable? mods, CancellationToken cancellationToken = default) + /// In the case a cached lookup was not possible, a value in milliseconds of to wait until performing potentially intensive lookup. + private void updateBindable(BindableStarDifficulty bindable, IRulesetInfo? rulesetInfo, IEnumerable? mods, CancellationToken cancellationToken = default, int computationDelay = 0) { // GetDifficultyAsync will fall back to existing data from IBeatmapInfo if not locally available // (contrary to GetAsync) - GetDifficultyAsync(bindable.BeatmapInfo, rulesetInfo, mods, cancellationToken) + GetDifficultyAsync(bindable.BeatmapInfo, rulesetInfo, mods, cancellationToken, computationDelay) .ContinueWith(task => { // We're on a threadpool thread, but we should exit back to the update thread so consumers can safely handle value-changed events. diff --git a/osu.Game/Database/MemoryCachingComponent.cs b/osu.Game/Database/MemoryCachingComponent.cs index e98475efae..a91c608279 100644 --- a/osu.Game/Database/MemoryCachingComponent.cs +++ b/osu.Game/Database/MemoryCachingComponent.cs @@ -35,8 +35,9 @@ namespace osu.Game.Database /// Retrieve the cached value for the given lookup. /// /// The lookup to retrieve. - /// An optional to cancel the operation. - protected async Task GetAsync(TLookup lookup, CancellationToken token = default) + /// An optional to cancel the operation. + /// In the case a cached lookup was not possible, a value in milliseconds of to wait until performing potentially intensive lookup. + protected async Task GetAsync(TLookup lookup, CancellationToken cancellationToken = default, int computationDelay = 0) { if (CheckExists(lookup, out TValue? existing)) { @@ -44,7 +45,10 @@ namespace osu.Game.Database return existing; } - var computed = await ComputeValueAsync(lookup, token).ConfigureAwait(false); + if (computationDelay > 0) + await Task.Delay(computationDelay, cancellationToken).ConfigureAwait(false); + + var computed = await ComputeValueAsync(lookup, cancellationToken).ConfigureAwait(false); statistics.Value.MissCount++; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index b99f046f4b..b4eac5cdac 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -246,7 +246,7 @@ namespace osu.Game.Screens.Select.Carousel if (Item?.State.Value != CarouselItemState.Collapsed) { // We've potentially cancelled the computation above so a new bindable is required. - starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmapInfo, (starDifficultyCancellationSource = new CancellationTokenSource()).Token); + starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmapInfo, (starDifficultyCancellationSource = new CancellationTokenSource()).Token, 200); starDifficultyBindable.BindValueChanged(d => { starCounter.Current = (float)(d.NewValue?.Stars ?? 0); diff --git a/osu.Game/Screens/SelectV2/PanelBeatmap.cs b/osu.Game/Screens/SelectV2/PanelBeatmap.cs index 20c27dba92..e6ad373bab 100644 --- a/osu.Game/Screens/SelectV2/PanelBeatmap.cs +++ b/osu.Game/Screens/SelectV2/PanelBeatmap.cs @@ -202,7 +202,7 @@ namespace osu.Game.Screens.SelectV2 var beatmap = (BeatmapInfo)Item.Model; - starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, starDifficultyCancellationSource.Token); + starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, starDifficultyCancellationSource.Token, 200); starDifficultyBindable.BindValueChanged(_ => updateDisplay(), true); } diff --git a/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs b/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs index 9a61ce998c..d03f6b7433 100644 --- a/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs +++ b/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs @@ -238,7 +238,7 @@ namespace osu.Game.Screens.SelectV2 var beatmap = (BeatmapInfo)Item.Model; - starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, starDifficultyCancellationSource.Token); + starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, starDifficultyCancellationSource.Token, 200); starDifficultyBindable.BindValueChanged(_ => updateDisplay(), true); }