diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index dafe7c19e6..3ca0b47121 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -11,11 +11,11 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Lists; using osu.Framework.Logging; using osu.Framework.Threading; using osu.Framework.Utils; +using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; @@ -27,14 +27,11 @@ namespace osu.Game.Beatmaps /// A component which performs and acts as a central cache for difficulty calculations of beatmap/ruleset/mod combinations. /// Currently not persisted between game sessions. /// - public class BeatmapDifficultyCache : Component + public class BeatmapDifficultyCache : MemoryCachingComponent { // Too many simultaneous updates can lead to stutters. One thread seems to work fine for song select display purposes. private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(1, nameof(BeatmapDifficultyCache)); - // A permanent cache to prevent re-computations. - private readonly ConcurrentDictionary difficultyCache = new ConcurrentDictionary(); - // All bindables that should be updated along with the current ruleset + mods. private readonly LockedWeakList trackedBindables = new LockedWeakList(); @@ -243,7 +240,7 @@ private StarDifficulty computeDifficulty(in DifficultyCacheLookup key, BeatmapIn var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(beatmapInfo)); var attributes = calculator.Calculate(key.Mods); - return difficultyCache[key] = new StarDifficulty(attributes); + return Cache[key] = new StarDifficulty(attributes); } catch (BeatmapInvalidForRulesetException e) { @@ -254,7 +251,7 @@ private StarDifficulty computeDifficulty(in DifficultyCacheLookup key, BeatmapIn if (rulesetInfo.Equals(beatmapInfo.Ruleset)) { Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineBeatmapID} to the beatmap's default ruleset ({beatmapInfo.Ruleset})."); - return difficultyCache[key] = new StarDifficulty(); + return Cache[key] = new StarDifficulty(); } // Check the cache first because this is now a different ruleset than the one previously guarded against. @@ -265,7 +262,7 @@ private StarDifficulty computeDifficulty(in DifficultyCacheLookup key, BeatmapIn } catch { - return difficultyCache[key] = new StarDifficulty(); + return Cache[key] = new StarDifficulty(); } } @@ -294,7 +291,7 @@ private bool tryGetExisting(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo, IE } key = new DifficultyCacheLookup(beatmapInfo.ID, rulesetInfo.ID.Value, mods); - return difficultyCache.TryGetValue(key, out existingDifficulty); + return Cache.TryGetValue(key, out existingDifficulty); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Database/MemoryCachingComponent.cs b/osu.Game/Database/MemoryCachingComponent.cs new file mode 100644 index 0000000000..85cf3b8af1 --- /dev/null +++ b/osu.Game/Database/MemoryCachingComponent.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Concurrent; +using osu.Framework.Graphics; + +namespace osu.Game.Database +{ + /// + /// A component which performs lookups (or calculations) and caches the results. + /// Currently not persisted between game sessions. + /// + public abstract class MemoryCachingComponent : Component + { + protected readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); + } +} diff --git a/osu.Game/Scoring/ScorePerformanceCache.cs b/osu.Game/Scoring/ScorePerformanceCache.cs index 8b764c75b7..435b93d7af 100644 --- a/osu.Game/Scoring/ScorePerformanceCache.cs +++ b/osu.Game/Scoring/ScorePerformanceCache.cs @@ -2,13 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Game.Beatmaps; +using osu.Game.Database; namespace osu.Game.Scoring { @@ -16,12 +15,8 @@ namespace osu.Game.Scoring /// A component which performs and acts as a central cache for performance calculations of locally databased scores. /// Currently not persisted between game sessions. /// - public class ScorePerformanceCache : Component + public class ScorePerformanceCache : MemoryCachingComponent { - // this cache will grow indefinitely per session and should be considered temporary. - // this whole component should likely be replaced with database persistence. - private readonly ConcurrentDictionary performanceCache = new ConcurrentDictionary(); - [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } @@ -34,7 +29,7 @@ public class ScorePerformanceCache : Component { var lookupKey = new PerformanceCacheLookup(score); - if (performanceCache.TryGetValue(lookupKey, out double performance)) + if (Cache.TryGetValue(lookupKey, out double performance)) return Task.FromResult((double?)performance); return computePerformanceAsync(score, lookupKey, token); @@ -54,7 +49,7 @@ public class ScorePerformanceCache : Component var total = calculator?.Calculate(); if (total.HasValue) - performanceCache[lookupKey] = total.Value; + Cache[lookupKey] = total.Value; return total; } diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs index 730221cc4b..68da4ec724 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs @@ -37,7 +37,7 @@ private void load(ScorePerformanceCache performanceCache) else { performanceCache.CalculatePerformanceAsync(score, cancellationTokenSource.Token) - .ContinueWith(t => Schedule(() => setPerformanceValue(t.Result)), cancellationTokenSource.Token); + .ContinueWith(t => Schedule(() => setPerformanceValue(t.Result)), cancellationTokenSource.Token); } }