mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 17:13:06 +08:00
Add weak WorkingBeatmap cache (#5163)
Add weak WorkingBeatmap cache Co-authored-by: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com>
This commit is contained in:
commit
089eadb008
@ -30,9 +30,9 @@ namespace osu.Game.Tests
|
|||||||
trackStore = audioManager.GetTrackStore(reader);
|
trackStore = audioManager.GetTrackStore(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose();
|
base.Dispose(isDisposing);
|
||||||
stream?.Dispose();
|
stream?.Dispose();
|
||||||
reader?.Dispose();
|
reader?.Dispose();
|
||||||
trackStore?.Dispose();
|
trackStore?.Dispose();
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Lists;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
@ -159,6 +160,8 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <param name="beatmap">The beatmap difficulty to restore.</param>
|
/// <param name="beatmap">The beatmap difficulty to restore.</param>
|
||||||
public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);
|
public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);
|
||||||
|
|
||||||
|
private readonly WeakList<WorkingBeatmap> workingCache = new WeakList<WorkingBeatmap>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a <see cref="WorkingBeatmap"/> instance for the provided <see cref="BeatmapInfo"/>
|
/// Retrieve a <see cref="WorkingBeatmap"/> instance for the provided <see cref="BeatmapInfo"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -173,12 +176,18 @@ namespace osu.Game.Beatmaps
|
|||||||
if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
|
if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
|
||||||
return DefaultBeatmap;
|
return DefaultBeatmap;
|
||||||
|
|
||||||
|
var cached = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID);
|
||||||
|
|
||||||
|
if (cached != null)
|
||||||
|
return cached;
|
||||||
|
|
||||||
if (beatmapInfo.Metadata == null)
|
if (beatmapInfo.Metadata == null)
|
||||||
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
|
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
|
||||||
|
|
||||||
WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager);
|
WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager);
|
||||||
|
|
||||||
previous?.TransferTo(working);
|
previous?.TransferTo(working);
|
||||||
|
workingCache.Add(working);
|
||||||
|
|
||||||
return working;
|
return working;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.IO.File;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Game.IO.Serialization;
|
using osu.Game.IO.Serialization;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@ -38,19 +39,6 @@ namespace osu.Game.Beatmaps
|
|||||||
BeatmapSetInfo = beatmapInfo.BeatmapSet;
|
BeatmapSetInfo = beatmapInfo.BeatmapSet;
|
||||||
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
||||||
|
|
||||||
beatmap = new RecyclableLazy<IBeatmap>(() =>
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
track = new RecyclableLazy<Track>(() => GetTrack() ?? GetVirtualTrack());
|
track = new RecyclableLazy<Track>(() => GetTrack() ?? GetVirtualTrack());
|
||||||
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
|
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
|
||||||
waveform = new RecyclableLazy<Waveform>(GetWaveform);
|
waveform = new RecyclableLazy<Waveform>(GetWaveform);
|
||||||
@ -58,6 +46,11 @@ namespace osu.Game.Beatmaps
|
|||||||
skin = new RecyclableLazy<Skin>(GetSkin);
|
skin = new RecyclableLazy<Skin>(GetSkin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~WorkingBeatmap()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual Track GetVirtualTrack()
|
protected virtual Track GetVirtualTrack()
|
||||||
{
|
{
|
||||||
const double excess_length = 1000;
|
const double excess_length = 1000;
|
||||||
@ -153,10 +146,26 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public override string ToString() => BeatmapInfo.ToString();
|
public override string ToString() => BeatmapInfo.ToString();
|
||||||
|
|
||||||
public bool BeatmapLoaded => beatmap.IsResultAvailable;
|
public bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false;
|
||||||
public IBeatmap Beatmap => beatmap.Value;
|
|
||||||
|
public Task<IBeatmap> 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();
|
protected abstract IBeatmap GetBeatmap();
|
||||||
private readonly RecyclableLazy<IBeatmap> beatmap;
|
private Task<IBeatmap> beatmapLoadTask;
|
||||||
|
|
||||||
public bool BackgroundLoaded => background.IsResultAvailable;
|
public bool BackgroundLoaded => background.IsResultAvailable;
|
||||||
public Texture Background => background.Value;
|
public Texture Background => background.Value;
|
||||||
@ -195,20 +204,33 @@ namespace osu.Game.Beatmaps
|
|||||||
other.track = track;
|
other.track = track;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Dispose()
|
|
||||||
{
|
|
||||||
background.Recycle();
|
|
||||||
waveform.Recycle();
|
|
||||||
storyboard.Recycle();
|
|
||||||
skin.Recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Eagerly dispose of the audio track associated with this <see cref="WorkingBeatmap"/> (if any).
|
/// Eagerly dispose of the audio track associated with this <see cref="WorkingBeatmap"/> (if any).
|
||||||
/// Accessing track again will load a fresh instance.
|
/// Accessing track again will load a fresh instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void RecycleTrack() => track.Recycle();
|
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<T>
|
public class RecyclableLazy<T>
|
||||||
{
|
{
|
||||||
private Lazy<T> lazy;
|
private Lazy<T> lazy;
|
||||||
|
@ -295,6 +295,8 @@ namespace osu.Game
|
|||||||
var nextBeatmap = beatmap.NewValue;
|
var nextBeatmap = beatmap.NewValue;
|
||||||
if (nextBeatmap?.Track != null)
|
if (nextBeatmap?.Track != null)
|
||||||
nextBeatmap.Track.Completed += currentTrackCompleted;
|
nextBeatmap.Track.Completed += currentTrackCompleted;
|
||||||
|
|
||||||
|
nextBeatmap?.LoadBeatmapAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void currentTrackCompleted()
|
private void currentTrackCompleted()
|
||||||
|
@ -137,9 +137,9 @@ namespace osu.Game.Tests.Visual
|
|||||||
track = audio?.Tracks.GetVirtual(length);
|
track = audio?.Tracks.GetVirtual(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose();
|
base.Dispose(isDisposing);
|
||||||
store?.Dispose();
|
store?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user