mirror of
https://github.com/ppy/osu.git
synced 2025-01-19 10:12:53 +08:00
Merge pull request #17244 from peppy/preload-sample-pools
Fix samples not being preloaded before gameplay starts
This commit is contained in:
commit
dc1e5a456a
@ -2,23 +2,22 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osuTK;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Skinning;
|
using osu.Game.Rulesets.Osu.Skinning;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osuTK.Graphics;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -126,18 +125,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
}
|
}
|
||||||
|
|
||||||
Samples.Samples = HitObject.TailSamples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
Samples.Samples = HitObject.TailSamples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
||||||
|
slidingSample.Samples = HitObject.CreateSlidingSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
||||||
var slidingSamples = new List<ISampleInfo>();
|
|
||||||
|
|
||||||
var normalSample = HitObject.Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL);
|
|
||||||
if (normalSample != null)
|
|
||||||
slidingSamples.Add(HitObject.SampleControlPoint.ApplyTo(normalSample).With("sliderslide"));
|
|
||||||
|
|
||||||
var whistleSample = HitObject.Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_WHISTLE);
|
|
||||||
if (whistleSample != null)
|
|
||||||
slidingSamples.Add(HitObject.SampleControlPoint.ApplyTo(whistleSample).With("sliderwhistle"));
|
|
||||||
|
|
||||||
slidingSample.Samples = slidingSamples.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void StopAllSamples()
|
public override void StopAllSamples()
|
||||||
|
@ -74,6 +74,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
|
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadSamples()
|
||||||
|
{
|
||||||
|
// Tail models don't actually get samples, as the playback is handled by DrawableSlider.
|
||||||
|
// This override is only here for visibility in explaining this weird flow.
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PlaySamples()
|
||||||
|
{
|
||||||
|
// Tail models don't actually get samples, as the playback is handled by DrawableSlider.
|
||||||
|
// This override is only here for visibility in explaining this weird flow.
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateInitialTransforms()
|
protected override void UpdateInitialTransforms()
|
||||||
{
|
{
|
||||||
base.UpdateInitialTransforms();
|
base.UpdateInitialTransforms();
|
||||||
|
@ -121,16 +121,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.LoadSamples();
|
base.LoadSamples();
|
||||||
|
|
||||||
var firstSample = HitObject.Samples.FirstOrDefault();
|
spinningSample.Samples = HitObject.CreateSpinningSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
||||||
|
|
||||||
if (firstSample != null)
|
|
||||||
{
|
|
||||||
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample).With("spinnerspin");
|
|
||||||
|
|
||||||
spinningSample.Samples = new ISampleInfo[] { clone };
|
|
||||||
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSpinningSample(ValueChangedEvent<bool> tracking)
|
private void updateSpinningSample(ValueChangedEvent<bool> tracking)
|
||||||
{
|
{
|
||||||
|
@ -29,6 +29,23 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
|
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override IList<HitSampleInfo> AuxiliarySamples => CreateSlidingSamples().Concat(TailSamples).ToArray();
|
||||||
|
|
||||||
|
public IList<HitSampleInfo> CreateSlidingSamples()
|
||||||
|
{
|
||||||
|
var slidingSamples = new List<HitSampleInfo>();
|
||||||
|
|
||||||
|
var normalSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL);
|
||||||
|
if (normalSample != null)
|
||||||
|
slidingSamples.Add(normalSample.With("sliderslide"));
|
||||||
|
|
||||||
|
var whistleSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_WHISTLE);
|
||||||
|
if (whistleSample != null)
|
||||||
|
slidingSamples.Add(whistleSample.With("sliderwhistle"));
|
||||||
|
|
||||||
|
return slidingSamples;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Cached<Vector2> endPositionCache = new Cached<Vector2>();
|
private readonly Cached<Vector2> endPositionCache = new Cached<Vector2>();
|
||||||
|
|
||||||
public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1);
|
public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1);
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
@ -73,5 +77,20 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
public override Judgement CreateJudgement() => new OsuJudgement();
|
public override Judgement CreateJudgement() => new OsuJudgement();
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||||
|
|
||||||
|
public override IList<HitSampleInfo> AuxiliarySamples => CreateSpinningSamples();
|
||||||
|
|
||||||
|
public HitSampleInfo[] CreateSpinningSamples()
|
||||||
|
{
|
||||||
|
var referenceSample = Samples.FirstOrDefault();
|
||||||
|
|
||||||
|
if (referenceSample == null)
|
||||||
|
return Array.Empty<HitSampleInfo>();
|
||||||
|
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
SampleControlPoint.ApplyTo(referenceSample).With("spinnerspin")
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -67,6 +68,12 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any samples which may be used by this hit object that are non-standard.
|
||||||
|
/// This is used only to preload these samples ahead of time.
|
||||||
|
/// </summary>
|
||||||
|
public virtual IList<HitSampleInfo> AuxiliarySamples => ImmutableList<HitSampleInfo>.Empty;
|
||||||
|
|
||||||
public SampleControlPoint SampleControlPoint = SampleControlPoint.DEFAULT;
|
public SampleControlPoint SampleControlPoint = SampleControlPoint.DEFAULT;
|
||||||
public DifficultyControlPoint DifficultyControlPoint = DifficultyControlPoint.DEFAULT;
|
public DifficultyControlPoint DifficultyControlPoint = DifficultyControlPoint.DEFAULT;
|
||||||
|
|
||||||
|
@ -500,12 +500,12 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
=> new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newVolume.GetOr(Volume), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered));
|
=> new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newVolume.GetOr(Volume), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered));
|
||||||
|
|
||||||
public bool Equals(LegacyHitSampleInfo? other)
|
public bool Equals(LegacyHitSampleInfo? other)
|
||||||
=> base.Equals(other) && CustomSampleBank == other.CustomSampleBank && IsLayered == other.IsLayered;
|
=> base.Equals(other) && CustomSampleBank == other.CustomSampleBank;
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
=> obj is LegacyHitSampleInfo other && Equals(other);
|
=> obj is LegacyHitSampleInfo other && Equals(other);
|
||||||
|
|
||||||
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), CustomSampleBank, IsLayered);
|
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), CustomSampleBank);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FileHitSampleInfo : LegacyHitSampleInfo, IEquatable<FileHitSampleInfo>
|
private class FileHitSampleInfo : LegacyHitSampleInfo, IEquatable<FileHitSampleInfo>
|
||||||
|
@ -3,22 +3,22 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Pooling;
|
using osu.Framework.Graphics.Pooling;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.UI
|
namespace osu.Game.Rulesets.UI
|
||||||
{
|
{
|
||||||
@ -264,10 +264,25 @@ namespace osu.Game.Rulesets.UI
|
|||||||
var entry = CreateLifetimeEntry(hitObject);
|
var entry = CreateLifetimeEntry(hitObject);
|
||||||
lifetimeEntryMap[entry.HitObject] = entry;
|
lifetimeEntryMap[entry.HitObject] = entry;
|
||||||
|
|
||||||
|
preloadSamples(hitObject);
|
||||||
|
|
||||||
HitObjectContainer.Add(entry);
|
HitObjectContainer.Add(entry);
|
||||||
OnHitObjectAdded(entry.HitObject);
|
OnHitObjectAdded(entry.HitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void preloadSamples(HitObject hitObject)
|
||||||
|
{
|
||||||
|
// prepare sample pools ahead of time so we're not initialising at runtime.
|
||||||
|
foreach (var sample in hitObject.Samples)
|
||||||
|
prepareSamplePool(hitObject.SampleControlPoint.ApplyTo(sample));
|
||||||
|
|
||||||
|
foreach (var sample in hitObject.AuxiliarySamples)
|
||||||
|
prepareSamplePool(hitObject.SampleControlPoint.ApplyTo(sample));
|
||||||
|
|
||||||
|
foreach (var nestedObject in hitObject.NestedHitObjects)
|
||||||
|
preloadSamples(nestedObject);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a <see cref="HitObjectLifetimeEntry"/> for a pooled <see cref="HitObject"/> from this <see cref="Playfield"/>.
|
/// Removes a <see cref="HitObjectLifetimeEntry"/> for a pooled <see cref="HitObject"/> from this <see cref="Playfield"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -330,22 +345,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
DrawableHitObject IPooledHitObjectProvider.GetPooledDrawableRepresentation(HitObject hitObject, DrawableHitObject parent)
|
DrawableHitObject IPooledHitObjectProvider.GetPooledDrawableRepresentation(HitObject hitObject, DrawableHitObject parent)
|
||||||
{
|
{
|
||||||
var lookupType = hitObject.GetType();
|
var pool = prepareDrawableHitObjectPool(hitObject);
|
||||||
|
|
||||||
IDrawablePool pool;
|
|
||||||
|
|
||||||
// Tests may add derived hitobject instances for which pools don't exist. Try to find any applicable pool and dynamically assign the type if the pool exists.
|
|
||||||
if (!pools.TryGetValue(lookupType, out pool))
|
|
||||||
{
|
|
||||||
foreach (var (t, p) in pools)
|
|
||||||
{
|
|
||||||
if (!t.IsInstanceOfType(hitObject))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
pools[lookupType] = pool = p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (DrawableHitObject)pool?.Get(d =>
|
return (DrawableHitObject)pool?.Get(d =>
|
||||||
{
|
{
|
||||||
@ -372,14 +372,39 @@ namespace osu.Game.Rulesets.UI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IDrawablePool prepareDrawableHitObjectPool(HitObject hitObject)
|
||||||
|
{
|
||||||
|
var lookupType = hitObject.GetType();
|
||||||
|
|
||||||
|
IDrawablePool pool;
|
||||||
|
|
||||||
|
// Tests may add derived hitobject instances for which pools don't exist. Try to find any applicable pool and dynamically assign the type if the pool exists.
|
||||||
|
if (!pools.TryGetValue(lookupType, out pool))
|
||||||
|
{
|
||||||
|
foreach (var (t, p) in pools)
|
||||||
|
{
|
||||||
|
if (!t.IsInstanceOfType(hitObject))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pools[lookupType] = pool = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Dictionary<ISampleInfo, DrawablePool<PoolableSkinnableSample>> samplePools = new Dictionary<ISampleInfo, DrawablePool<PoolableSkinnableSample>>();
|
private readonly Dictionary<ISampleInfo, DrawablePool<PoolableSkinnableSample>> samplePools = new Dictionary<ISampleInfo, DrawablePool<PoolableSkinnableSample>>();
|
||||||
|
|
||||||
public PoolableSkinnableSample GetPooledSample(ISampleInfo sampleInfo)
|
public PoolableSkinnableSample GetPooledSample(ISampleInfo sampleInfo) => prepareSamplePool(sampleInfo).Get();
|
||||||
{
|
|
||||||
if (!samplePools.TryGetValue(sampleInfo, out var existingPool))
|
|
||||||
AddInternal(samplePools[sampleInfo] = existingPool = new DrawableSamplePool(sampleInfo, 1));
|
|
||||||
|
|
||||||
return existingPool.Get();
|
private DrawablePool<PoolableSkinnableSample> prepareSamplePool(ISampleInfo sampleInfo)
|
||||||
|
{
|
||||||
|
if (samplePools.TryGetValue(sampleInfo, out var pool)) return pool;
|
||||||
|
|
||||||
|
AddInternal(samplePools[sampleInfo] = pool = new DrawableSamplePool(sampleInfo, 1));
|
||||||
|
|
||||||
|
return pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DrawableSamplePool : DrawablePool<PoolableSkinnableSample>
|
private class DrawableSamplePool : DrawablePool<PoolableSkinnableSample>
|
||||||
|
Loading…
Reference in New Issue
Block a user