From 22f5a66c029bed6b135cbb3c99b63363e4248ecd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 17 Feb 2024 15:46:38 +0300 Subject: [PATCH] Reduce allocations during beatmap selection --- .../Preprocessing/OsuDifficultyHitObject.cs | 8 ++++- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 8 +++-- osu.Game.Rulesets.Osu/Objects/Slider.cs | 36 +++++++++++++++---- osu.Game/Rulesets/Objects/SliderPath.cs | 15 ++++---- osu.Game/Screens/Select/BeatmapCarousel.cs | 19 ++++++++-- 5 files changed, 67 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 488d1e2e9f..0e537632b1 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -232,7 +232,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing IList nestedObjects = slider.NestedHitObjects; - SliderTick? lastRealTick = slider.NestedHitObjects.OfType().LastOrDefault(); + SliderTick? lastRealTick = null; + + foreach (var hitobject in slider.NestedHitObjects) + { + if (hitobject is SliderTick tick) + lastRealTick = tick; + } if (lastRealTick?.StartTime > trackingEndTime) { diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 74631400ca..6c77d9189c 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -149,8 +148,11 @@ namespace osu.Game.Rulesets.Osu.Objects { StackHeightBindable.BindValueChanged(height => { - foreach (var nested in NestedHitObjects.OfType()) - nested.StackHeight = height.NewValue; + foreach (var nested in NestedHitObjects) + { + if (nested is OsuHitObject osuHitObject) + osuHitObject.StackHeight = height.NewValue; + } }); } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 506145568e..8a87e17089 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -252,18 +252,42 @@ namespace osu.Game.Rulesets.Osu.Objects protected void UpdateNestedSamples() { - var firstSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL) - ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) + HitSampleInfo firstSample = null; + + for (int i = 0; i < Samples.Count; i++) + { + // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) + if (i == 0) + { + firstSample = Samples[i]; + continue; + } + + if (Samples[i].Name != HitSampleInfo.HIT_NORMAL) + continue; + + firstSample = Samples[i]; + break; + } + var sampleList = new List(); if (firstSample != null) sampleList.Add(firstSample.With("slidertick")); - foreach (var tick in NestedHitObjects.OfType()) - tick.Samples = sampleList; + foreach (var nested in NestedHitObjects) + { + switch (nested) + { + case SliderTick tick: + tick.Samples = sampleList; + break; - foreach (var repeat in NestedHitObjects.OfType()) - repeat.Samples = this.GetNodeSamples(repeat.RepeatIndex + 1); + case SliderRepeat repeat: + repeat.Samples = this.GetNodeSamples(repeat.RepeatIndex + 1); + break; + } + } if (HeadCircle != null) HeadCircle.Samples = this.GetNodeSamples(0); diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index dc71608132..f33a07f082 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -61,16 +61,17 @@ namespace osu.Game.Rulesets.Objects case NotifyCollectionChangedAction.Add: Debug.Assert(args.NewItems != null); - foreach (var c in args.NewItems.Cast()) - c.Changed += invalidate; + foreach (object? newItem in args.NewItems) + ((PathControlPoint)newItem).Changed += invalidate; + break; case NotifyCollectionChangedAction.Reset: case NotifyCollectionChangedAction.Remove: Debug.Assert(args.OldItems != null); - foreach (var c in args.OldItems.Cast()) - c.Changed -= invalidate; + foreach (object? oldItem in args.OldItems) + ((PathControlPoint)oldItem).Changed -= invalidate; break; } @@ -269,10 +270,10 @@ namespace osu.Game.Rulesets.Objects { List subPath = calculateSubPath(segmentVertices, segmentType); // Skip the first vertex if it is the same as the last vertex from the previous segment - int skipFirst = calculatedPath.Count > 0 && subPath.Count > 0 && calculatedPath.Last() == subPath[0] ? 1 : 0; + bool skipFirst = calculatedPath.Count > 0 && subPath.Count > 0 && calculatedPath.Last() == subPath[0]; - foreach (Vector2 t in subPath.Skip(skipFirst)) - calculatedPath.Add(t); + for (int j = skipFirst ? 1 : 0; j < subPath.Count; j++) + calculatedPath.Add(subPath[j]); } if (i > 0) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 70ecde3858..ae0f397d52 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -510,12 +510,27 @@ namespace osu.Game.Screens.Select if (beatmapInfo?.Hidden != false) return false; - foreach (CarouselBeatmapSet set in beatmapSets) + foreach (var carouselItem in root.Items) { + if (carouselItem is not CarouselBeatmapSet set) + continue; + if (!bypassFilters && set.Filtered.Value) continue; - var item = set.Beatmaps.FirstOrDefault(p => p.BeatmapInfo.Equals(beatmapInfo)); + CarouselBeatmap? item = null; + + foreach (var setCarouselItem in set.Items) + { + if (setCarouselItem is not CarouselBeatmap setCarouselBeatmap) + continue; + + if (!setCarouselBeatmap.BeatmapInfo.Equals(beatmapInfo)) + continue; + + item = setCarouselBeatmap; + break; + } if (item == null) // The beatmap that needs to be selected doesn't exist in this set