From 6d864cb47e2e5efb0b283d078526b833362d252e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Jun 2019 12:42:21 +0900 Subject: [PATCH 01/21] Load beatmap content asynchronously in the background --- osu.Game/Beatmaps/WorkingBeatmap.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 328763fc9f..3ef7b4feca 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -11,6 +11,7 @@ using osu.Framework.IO.File; using System.IO; using System.Linq; using System.Threading; +using System.Threading.Tasks; using osu.Framework.Audio; using osu.Game.IO.Serialization; using osu.Game.Rulesets; @@ -38,7 +39,7 @@ namespace osu.Game.Beatmaps BeatmapSetInfo = beatmapInfo.BeatmapSet; Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); - beatmap = new RecyclableLazy(() => + beatmapLoadTask = Task.Factory.StartNew(() => { var b = GetBeatmap() ?? new Beatmap(); @@ -49,7 +50,7 @@ namespace osu.Game.Beatmaps b.BeatmapInfo = BeatmapInfo; return b; - }); + }, beatmapCancellation.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); track = new RecyclableLazy(() => GetTrack() ?? GetVirtualTrack()); background = new RecyclableLazy(GetBackground, BackgroundStillValid); @@ -153,10 +154,11 @@ namespace osu.Game.Beatmaps public override string ToString() => BeatmapInfo.ToString(); - public bool BeatmapLoaded => beatmap.IsResultAvailable; - public IBeatmap Beatmap => beatmap.Value; + public bool BeatmapLoaded => beatmapLoadTask.IsCompleted; + public IBeatmap Beatmap => beatmapLoadTask.Result; + private readonly CancellationTokenSource beatmapCancellation = new CancellationTokenSource(); protected abstract IBeatmap GetBeatmap(); - private readonly RecyclableLazy beatmap; + private readonly Task beatmapLoadTask; public bool BackgroundLoaded => background.IsResultAvailable; public Texture Background => background.Value; @@ -201,6 +203,8 @@ namespace osu.Game.Beatmaps waveform.Recycle(); storyboard.Recycle(); skin.Recycle(); + + beatmapCancellation.Cancel(); } /// From 18303623371ab1544d6e26d8458813fa05afff3e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 24 Jun 2019 17:10:50 +0900 Subject: [PATCH 02/21] Move task out of ctor to avoid initialisation ordering issues --- osu.Game/Beatmaps/WorkingBeatmap.cs | 34 +++++++++++++++-------------- osu.Game/OsuGame.cs | 2 ++ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 3ef7b4feca..a1864526d1 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -39,19 +39,6 @@ namespace osu.Game.Beatmaps BeatmapSetInfo = beatmapInfo.BeatmapSet; Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); - beatmapLoadTask = Task.Factory.StartNew(() => - { - var b = GetBeatmap() ?? new Beatmap(); - - // The original beatmap version needs to be preserved as the database doesn't contain it - BeatmapInfo.BeatmapVersion = b.BeatmapInfo.BeatmapVersion; - - // Use the database-backed info for more up-to-date values (beatmap id, ranked status, etc) - b.BeatmapInfo = BeatmapInfo; - - return b; - }, beatmapCancellation.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); - track = new RecyclableLazy(() => GetTrack() ?? GetVirtualTrack()); background = new RecyclableLazy(GetBackground, BackgroundStillValid); waveform = new RecyclableLazy(GetWaveform); @@ -154,11 +141,26 @@ namespace osu.Game.Beatmaps public override string ToString() => BeatmapInfo.ToString(); - public bool BeatmapLoaded => beatmapLoadTask.IsCompleted; - public IBeatmap Beatmap => beatmapLoadTask.Result; + public bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false; + + public Task LoadBeatmapAsync() => (beatmapLoadTask ?? (beatmapLoadTask = Task.Factory.StartNew(() => + { + var b = GetBeatmap() ?? new Beatmap(); + + // The original beatmap version needs to be preserved as the database doesn't contain it + BeatmapInfo.BeatmapVersion = b.BeatmapInfo.BeatmapVersion; + + // Use the database-backed info for more up-to-date values (beatmap id, ranked status, etc) + b.BeatmapInfo = BeatmapInfo; + + return b; + }, beatmapCancellation.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default))); + + public IBeatmap Beatmap => LoadBeatmapAsync().Result; + private readonly CancellationTokenSource beatmapCancellation = new CancellationTokenSource(); protected abstract IBeatmap GetBeatmap(); - private readonly Task beatmapLoadTask; + private Task beatmapLoadTask; public bool BackgroundLoaded => background.IsResultAvailable; public Texture Background => background.Value; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 35684849a3..b018c2b64a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -293,6 +293,8 @@ namespace osu.Game var nextBeatmap = beatmap.NewValue; if (nextBeatmap?.Track != null) nextBeatmap.Track.Completed += currentTrackCompleted; + + nextBeatmap?.LoadBeatmapAsync(); } private void currentTrackCompleted() From 8b0aaccfe618dd9fd92e43718458e7315594ee40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Jun 2019 13:56:36 +0900 Subject: [PATCH 03/21] Add finaliser to WorkingBeatmap --- osu.Game.Tests/WaveformTestBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 36 +++++++++++++++++++-------- osu.Game/Tests/Visual/OsuTestScene.cs | 2 +- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index fdb91b7c5b..c5a2f5be51 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests trackStore = audioManager.GetTrackStore(reader); } - public override void Dispose() + protected override void Dispose(bool isDisposing) { base.Dispose(); stream?.Dispose(); diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index a1864526d1..61390fe51b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -46,6 +46,11 @@ namespace osu.Game.Beatmaps skin = new RecyclableLazy(GetSkin); } + ~WorkingBeatmap() + { + Dispose(false); + } + protected virtual Track GetVirtualTrack() { const double excess_length = 1000; @@ -199,22 +204,33 @@ namespace osu.Game.Beatmaps other.track = track; } - public virtual void Dispose() - { - background.Recycle(); - waveform.Recycle(); - storyboard.Recycle(); - skin.Recycle(); - - beatmapCancellation.Cancel(); - } - /// /// Eagerly dispose of the audio track associated with this (if any). /// Accessing track again will load a fresh instance. /// public virtual void RecycleTrack() => track.Recycle(); + #region Disposal + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool isDisposing) + { + // recycling logic is not here for the time being, as components which use + // retrieved objects from WorkingBeatmap may not hold a reference to the WorkingBeatmap itself. + // this should be fine as each retrieved comopnent do have their own finalizers. + + // cancelling the beatmap load is safe for now since the retrieval is a synchronous + // operation. if we add an async retrieval method this may need to be reconsidered. + beatmapCancellation.Cancel(); + } + + #endregion + public class RecyclableLazy { private Lazy lazy; diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index c8798448ae..9f4532513f 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -137,7 +137,7 @@ namespace osu.Game.Tests.Visual track = audio?.Tracks.GetVirtual(length); } - public override void Dispose() + protected override void Dispose(bool isDisposing) { base.Dispose(); store?.Dispose(); From 1072431fbbc17373f367eb06c8b6de0156f16cb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 Jun 2019 14:08:58 +0900 Subject: [PATCH 04/21] Fix test StackOverflows --- osu.Game.Tests/WaveformTestBeatmap.cs | 2 +- osu.Game/Tests/Visual/OsuTestScene.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index c5a2f5be51..3e0df8d45e 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests protected override void Dispose(bool isDisposing) { - base.Dispose(); + base.Dispose(isDisposing); stream?.Dispose(); reader?.Dispose(); trackStore?.Dispose(); diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 9f4532513f..9b3c15aa91 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -139,7 +139,7 @@ namespace osu.Game.Tests.Visual protected override void Dispose(bool isDisposing) { - base.Dispose(); + base.Dispose(isDisposing); store?.Dispose(); } From ef384b86676510f045b70da9068e222136ed9eb2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 Jun 2019 14:08:19 +0900 Subject: [PATCH 05/21] Add simple (weak) WorkingBeatmap cache --- osu.Game/Beatmaps/BeatmapManager.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index d5b19485de..6c7929d193 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -13,6 +13,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions; using osu.Framework.Graphics.Textures; +using osu.Framework.Lists; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Threading; @@ -157,6 +158,8 @@ namespace osu.Game.Beatmaps /// The beatmap difficulty to restore. public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap); + private readonly WeakList workingCache = new WeakList(); + /// /// Retrieve a instance for the provided /// @@ -171,12 +174,18 @@ namespace osu.Game.Beatmaps if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; + var cached = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); + + if (cached != null) + return cached; + if (beatmapInfo.Metadata == null) beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager); previous?.TransferTo(working); + workingCache.Add(working); return working; } From 9de4bb342311e0573163dba38c41be8878667567 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Jul 2019 16:12:20 +0900 Subject: [PATCH 06/21] Remove all non-transform LogoVisualisation per-frame allocations --- osu.Game/Screens/Menu/LogoVisualisation.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index c6de5857c2..2ba82b5d9b 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -96,13 +96,13 @@ namespace osu.Game.Screens.Menu var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null; var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null; - float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes ?? new float[256]; + float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes; for (int i = 0; i < bars_per_visualiser; i++) { if (track?.IsRunning ?? false) { - float targetAmplitude = temporalAmplitudes[(i + indexOffset) % bars_per_visualiser] * (effect?.KiaiMode == true ? 1 : 0.5f); + float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f); if (targetAmplitude > frequencyAmplitudes[i]) frequencyAmplitudes[i] = targetAmplitude; } @@ -115,7 +115,6 @@ namespace osu.Game.Screens.Menu } indexOffset = (indexOffset + index_change) % bars_per_visualiser; - Scheduler.AddDelayed(updateAmplitudes, time_between_updates); } private void updateColour() @@ -131,7 +130,8 @@ namespace osu.Game.Screens.Menu protected override void LoadComplete() { base.LoadComplete(); - updateAmplitudes(); + + Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); } protected override void Update() From 8e54990f62a72dbd3789549fce975ff02e62a884 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 13:40:40 +0900 Subject: [PATCH 07/21] Add database statistics to GlobalStatistics --- osu.Game/Database/DatabaseContextFactory.cs | 19 ++++++++++++++++++- osu.Game/Database/OsuDbContext.cs | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index 554337c477..bb6bef1c50 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using Microsoft.EntityFrameworkCore.Storage; using osu.Framework.Platform; +using osu.Framework.Statistics; namespace osu.Game.Database { @@ -31,11 +32,20 @@ namespace osu.Game.Database recycleThreadContexts(); } + private static readonly GlobalStatistic reads = GlobalStatistics.Get("Database", "Get (Read)"); + private static readonly GlobalStatistic writes = GlobalStatistics.Get("Database", "Get (Write)"); + private static readonly GlobalStatistic commits = GlobalStatistics.Get("Database", "Commits"); + private static readonly GlobalStatistic rollbacks = GlobalStatistics.Get("Database", "Rollbacks"); + /// /// Get a context for the current thread for read-only usage. /// If a is in progress, the existing write-safe context will be returned. /// - public OsuDbContext Get() => threadContexts.Value; + public OsuDbContext Get() + { + reads.Value++; + return threadContexts.Value; + } /// /// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context). @@ -45,6 +55,7 @@ namespace osu.Game.Database /// A usage containing a usable context. public DatabaseWriteUsage GetForWrite(bool withTransaction = true) { + writes.Value++; Monitor.Enter(writeLock); OsuDbContext context; @@ -90,9 +101,15 @@ namespace osu.Game.Database if (usages == 0) { if (currentWriteDidError) + { + rollbacks.Value++; currentWriteTransaction?.Rollback(); + } else + { + commits.Value++; currentWriteTransaction?.Commit(); + } if (currentWriteDidWrite || currentWriteDidError) { diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index d31d7cbff7..538ec41b3d 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using osu.Framework.Logging; +using osu.Framework.Statistics; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.IO; @@ -34,6 +35,8 @@ namespace osu.Game.Database private static readonly Lazy logger = new Lazy(() => new OsuDbLoggerFactory()); + private static readonly GlobalStatistic contexts = GlobalStatistics.Get("Database", "Contexts"); + static OsuDbContext() { // required to initialise native SQLite libraries on some platforms. @@ -76,6 +79,8 @@ namespace osu.Game.Database connection.Close(); throw; } + + contexts.Value++; } ~OsuDbContext() @@ -85,6 +90,20 @@ namespace osu.Game.Database Dispose(); } + private bool isDisposed; + + public override void Dispose() + { + if (isDisposed) return; + + isDisposed = true; + + base.Dispose(); + + contexts.Value--; + GC.SuppressFinalize(this); + } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); From 451765784a9f5324c1f011e6c9593e714ffe749b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 15:17:15 +0900 Subject: [PATCH 08/21] Centralise SocialOverlay's scheduling logic Just some clean-ups to make it easier to confirm correct logic --- osu.Game/Overlays/SocialOverlay.cs | 91 +++++++++++++++--------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs index 780a80b4fc..4def249200 100644 --- a/osu.Game/Overlays/SocialOverlay.cs +++ b/osu.Game/Overlays/SocialOverlay.cs @@ -66,24 +66,64 @@ namespace osu.Game.Overlays } }; - Header.Tabs.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch); + Header.Tabs.Current.ValueChanged += _ => queueUpdate(); - Filter.Tabs.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch); + Filter.Tabs.Current.ValueChanged += _ => queueUpdate(); Filter.DisplayStyleControl.DisplayStyle.ValueChanged += style => recreatePanels(style.NewValue); - Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch); + Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => queueUpdate(); + currentQuery.BindTo(Filter.Search.Current); currentQuery.ValueChanged += query => { queryChangedDebounce?.Cancel(); if (string.IsNullOrEmpty(query.NewValue)) - Scheduler.AddOnce(updateSearch); + queueUpdate(); else queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500); }; + } - currentQuery.BindTo(Filter.Search.Current); + private APIRequest getUsersRequest; + + private readonly Bindable currentQuery = new Bindable(); + + private ScheduledDelegate queryChangedDebounce; + + private void queueUpdate() => Scheduler.AddOnce(updateSearch); + + private void updateSearch() + { + queryChangedDebounce?.Cancel(); + + if (!IsLoaded) + return; + + Users = null; + clearPanels(); + loading.Hide(); + getUsersRequest?.Cancel(); + + if (API?.IsLoggedIn != true) + return; + + switch (Header.Tabs.Current.Value) + { + case SocialTab.Friends: + var friendRequest = new GetFriendsRequest(); // TODO filter arguments? + friendRequest.Success += updateUsers; + API.Queue(getUsersRequest = friendRequest); + break; + + default: + var userRequest = new GetUsersRequest(); // TODO filter arguments! + userRequest.Success += response => updateUsers(response.Select(r => r.User)); + API.Queue(getUsersRequest = userRequest); + break; + } + + loading.Show(); } private void recreatePanels(PanelDisplayStyle displayStyle) @@ -133,45 +173,6 @@ namespace osu.Game.Overlays }); } - private APIRequest getUsersRequest; - - private readonly Bindable currentQuery = new Bindable(); - - private ScheduledDelegate queryChangedDebounce; - - private void updateSearch() - { - queryChangedDebounce?.Cancel(); - - if (!IsLoaded) - return; - - Users = null; - clearPanels(); - loading.Hide(); - getUsersRequest?.Cancel(); - - if (API?.IsLoggedIn != true) - return; - - switch (Header.Tabs.Current.Value) - { - case SocialTab.Friends: - var friendRequest = new GetFriendsRequest(); // TODO filter arguments? - friendRequest.Success += updateUsers; - API.Queue(getUsersRequest = friendRequest); - break; - - default: - var userRequest = new GetUsersRequest(); // TODO filter arguments! - userRequest.Success += response => updateUsers(response.Select(r => r.User)); - API.Queue(getUsersRequest = userRequest); - break; - } - - loading.Show(); - } - private void updateUsers(IEnumerable newUsers) { Users = newUsers; @@ -193,7 +194,7 @@ namespace osu.Game.Overlays switch (state) { case APIState.Online: - Scheduler.AddOnce(updateSearch); + queueUpdate(); break; default: From 29bb227de28fc264a77ded4cc1180e0a28244b55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 16:28:06 +0900 Subject: [PATCH 09/21] Avoid Intro screen holding references to the intro beatmap --- osu.Game/Screens/Menu/Intro.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index c52e8541c5..dab5066c52 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -86,6 +86,7 @@ namespace osu.Game.Screens.Menu if (!resuming) { Beatmap.Value = introBeatmap; + introBeatmap = null; if (menuVoice.Value) welcome.Play(); @@ -94,7 +95,10 @@ namespace osu.Game.Screens.Menu { // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu. if (menuMusic.Value) + { track.Start(); + track = null; + } LoadComponentAsync(mainMenu = new MainMenu()); From 6c7b97931e2642090c7d28c8779498d4a46959f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 17:45:46 +0900 Subject: [PATCH 10/21] Avoid using a BufferedContainer for backgrounds unless required --- osu.Game/Graphics/Backgrounds/Background.cs | 36 ++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index db055d15e5..8fdb8ebcdd 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -6,23 +6,28 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Transforms; +using osuTK; namespace osu.Game.Graphics.Backgrounds { - public class Background : BufferedContainer + /// + /// A background which offer blurring on demand. + /// + public class Background : CompositeDrawable { public Sprite Sprite; private readonly string textureName; + private BufferedContainer bufferedContainer; + public Background(string textureName = @"") { - CacheDrawnFrameBuffer = true; - this.textureName = textureName; RelativeSizeAxes = Axes.Both; - Add(Sprite = new Sprite + AddInternal(Sprite = new Sprite { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -37,5 +42,28 @@ namespace osu.Game.Graphics.Backgrounds if (!string.IsNullOrEmpty(textureName)) Sprite.Texture = textures.Get(textureName); } + + public Vector2 BlurSigma => bufferedContainer?.BlurSigma ?? Vector2.Zero; + + /// + /// Smoothly adjusts over time. + /// + /// A to which further transforms can be added. + public void BlurTo(Vector2 newBlurSigma, double duration = 0, Easing easing = Easing.None) + { + if (bufferedContainer == null) + { + RemoveInternal(Sprite); + + AddInternal(bufferedContainer = new BufferedContainer + { + CacheDrawnFrameBuffer = true, + RelativeSizeAxes = Axes.Both, + Child = Sprite + }); + } + + bufferedContainer.BlurTo(newBlurSigma, duration, easing); + } } } From 7b2227c5053f78fb3bda55854259fa7a6422e768 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 17:47:19 +0900 Subject: [PATCH 11/21] Fix xmldoc --- osu.Game/Graphics/Backgrounds/Background.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index 8fdb8ebcdd..526b3da8a6 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -12,7 +12,7 @@ using osuTK; namespace osu.Game.Graphics.Backgrounds { /// - /// A background which offer blurring on demand. + /// A background which offers blurring via a on demand. /// public class Background : CompositeDrawable { From 587be955c3e20d667067660be5b8aa9d1f72aa31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 17:57:23 +0900 Subject: [PATCH 12/21] Increase number of backgrounds in line with resources --- osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 7092ac0c4a..55338ea01a 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Backgrounds private Background background; private int currentDisplay; - private const int background_count = 5; + private const int background_count = 7; private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}"; From 79b0deb353c0d13bcf0a86ad46059ef2b82dc05a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 17:59:25 +0900 Subject: [PATCH 13/21] Update resources reference --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d6a998bf55..8c4f5dcb7d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,7 +14,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index de4a14f01f..113874f6f6 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -104,7 +104,7 @@ - + From a26b14a4f89a44ee8d9adb96774b75fa4989e46e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:21:56 +0900 Subject: [PATCH 14/21] Move finaliser inside disposal region --- osu.Game/Beatmaps/WorkingBeatmap.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 61390fe51b..40b3d70262 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -46,11 +46,6 @@ namespace osu.Game.Beatmaps skin = new RecyclableLazy(GetSkin); } - ~WorkingBeatmap() - { - Dispose(false); - } - protected virtual Track GetVirtualTrack() { const double excess_length = 1000; @@ -229,6 +224,11 @@ namespace osu.Game.Beatmaps beatmapCancellation.Cancel(); } + ~WorkingBeatmap() + { + Dispose(false); + } + #endregion public class RecyclableLazy From 0b66f139020b9a13e3816814c76078db25c97a72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:22:33 +0900 Subject: [PATCH 15/21] Add todo about beatmap load cancellation --- osu.Game/Beatmaps/WorkingBeatmap.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 40b3d70262..2f611b8409 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -145,6 +145,7 @@ namespace osu.Game.Beatmaps public Task LoadBeatmapAsync() => (beatmapLoadTask ?? (beatmapLoadTask = Task.Factory.StartNew(() => { + // Todo: Handle cancellation during beatmap parsing var b = GetBeatmap() ?? new Beatmap(); // The original beatmap version needs to be preserved as the database doesn't contain it From a6acc1f99f25e9dfbd344679ba9cc147e5eb9e3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:25:51 +0900 Subject: [PATCH 16/21] Catch exception and return null for safety . --- osu.Game/Beatmaps/WorkingBeatmap.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 2f611b8409..a4324ecb0c 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -157,7 +157,20 @@ namespace osu.Game.Beatmaps return b; }, beatmapCancellation.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default))); - public IBeatmap Beatmap => LoadBeatmapAsync().Result; + public IBeatmap Beatmap + { + get + { + try + { + return LoadBeatmapAsync().Result; + } + catch (TaskCanceledException) + { + return null; + } + } + } private readonly CancellationTokenSource beatmapCancellation = new CancellationTokenSource(); protected abstract IBeatmap GetBeatmap(); From 9e33fb35e9241dda9fa34c33804bae5f7a9e9670 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:24:08 +0900 Subject: [PATCH 17/21] Fix typo --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index a4324ecb0c..00ba8963cb 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -231,7 +231,7 @@ namespace osu.Game.Beatmaps { // recycling logic is not here for the time being, as components which use // retrieved objects from WorkingBeatmap may not hold a reference to the WorkingBeatmap itself. - // this should be fine as each retrieved comopnent do have their own finalizers. + // this should be fine as each retrieved component do have their own finalizers. // cancelling the beatmap load is safe for now since the retrieval is a synchronous // operation. if we add an async retrieval method this may need to be reconsidered. From f31d840c13eecb92af95e69aceb26fad15a69a0f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:25:03 +0900 Subject: [PATCH 18/21] Dispose previous WorkingBeatmap on change --- osu.Game/OsuGame.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index bfa4aeadef..49c543537a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -296,6 +296,8 @@ namespace osu.Game if (nextBeatmap?.Track != null) nextBeatmap.Track.Completed += currentTrackCompleted; + beatmap.OldValue?.Dispose(); + nextBeatmap?.LoadBeatmapAsync(); } From e7a7f2f660c0eed21b1d7696f3236038f59c2edf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 22:39:42 +0900 Subject: [PATCH 19/21] Add statistic for count of alive WorkingBeatmaps --- osu.Game/Beatmaps/WorkingBeatmap.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 00ba8963cb..138d911556 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -13,6 +13,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Audio; +using osu.Framework.Statistics; using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; @@ -32,6 +33,8 @@ namespace osu.Game.Beatmaps protected AudioManager AudioManager { get; } + private static readonly GlobalStatistic total_count = GlobalStatistics.Get(nameof(Beatmaps), $"Total {nameof(WorkingBeatmap)}s"); + protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager) { AudioManager = audioManager; @@ -44,6 +47,8 @@ namespace osu.Game.Beatmaps waveform = new RecyclableLazy(GetWaveform); storyboard = new RecyclableLazy(GetStoryboard); skin = new RecyclableLazy(GetSkin); + + total_count.Value++; } protected virtual Track GetVirtualTrack() @@ -227,8 +232,15 @@ namespace osu.Game.Beatmaps GC.SuppressFinalize(this); } + private bool isDisposed; + protected virtual void Dispose(bool isDisposing) { + if (isDisposed) + return; + + isDisposed = true; + // recycling logic is not here for the time being, as components which use // retrieved objects from WorkingBeatmap may not hold a reference to the WorkingBeatmap itself. // this should be fine as each retrieved component do have their own finalizers. @@ -236,6 +248,8 @@ namespace osu.Game.Beatmaps // cancelling the beatmap load is safe for now since the retrieval is a synchronous // operation. if we add an async retrieval method this may need to be reconsidered. beatmapCancellation.Cancel(); + + total_count.Value--; } ~WorkingBeatmap() From 8e0b5f16225f00266c52c61818636b798502770d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jul 2019 23:21:13 +0900 Subject: [PATCH 20/21] Fix weird merge conflict --- osu.Game/Beatmaps/WorkingBeatmap.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index c6f26423dd..37aa0024da 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -51,11 +51,6 @@ namespace osu.Game.Beatmaps total_count.Value++; } - ~WorkingBeatmap() - { - Dispose(false); - } - protected virtual Track GetVirtualTrack() { const double excess_length = 1000; From f9c24f2281dc82ffd14ec3f47b7fc155112d9a66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jul 2019 00:35:55 +0900 Subject: [PATCH 21/21] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8c4f5dcb7d..b59828a52e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -15,7 +15,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 113874f6f6..ddbdaf3d18 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + +