1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-13 07:13:39 +08:00
Files
osu-lazer/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs
T
Bartłomiej Dach 4ebd97b804 Slightly delay retrieval of working beatmaps in song select panels
Beatmap panels can be visible for very brief instants.
`PanelSetBackground` has a backstop to prevent expensive background
loads which is based on the position of the panel relative to centre of
screen.

However, retrieving the working beatmap that *precedes* any of that
expensive background load logic, is *also* expensive, and *always* runs
even if a panel is visible on screen for only a brief second. Therefore,
by moving some of that background load delay towards delaying retrieving
the working beatmap, we can save on doing even more work, which has
beneficial implications for performance.
2025-10-22 11:00:20 +02:00

334 lines
14 KiB
C#

// 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.
using System;
using System.Collections.Generic;
using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Localisation;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Carousel;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osuTK;
namespace osu.Game.Screens.SelectV2
{
public partial class PanelBeatmapStandalone : Panel
{
public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT * 1.6f;
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
[Resolved]
private IBindable<IReadOnlyList<Mod>> mods { get; set; } = null!;
[Resolved]
private OverlayColourProvider colourProvider { get; set; } = null!;
[Resolved]
private ISongSelect? songSelect { get; set; }
[Resolved]
private BeatmapManager beatmaps { get; set; } = null!;
[Resolved]
private OsuColour colours { get; set; } = null!;
[Resolved]
private BeatmapDifficultyCache difficultyCache { get; set; } = null!;
private IBindable<StarDifficulty>? starDifficultyBindable;
private CancellationTokenSource? starDifficultyCancellationSource;
private PanelSetBackground beatmapBackground = null!;
private ScheduledDelegate? scheduledBackgroundRetrieval;
private OsuSpriteText titleText = null!;
private OsuSpriteText artistText = null!;
private PanelUpdateBeatmapButton updateButton = null!;
private BeatmapSetOnlineStatusPill statusPill = null!;
private ConstrainedIconContainer difficultyIcon = null!;
private StarRatingDisplay starRatingDisplay = null!;
private StarCounter starCounter = null!;
private PanelLocalRankDisplay localRank = null!;
private OsuSpriteText keyCountText = null!;
private OsuSpriteText difficultyText = null!;
private OsuSpriteText authorText = null!;
private FillFlowContainer mainFill = null!;
private Box backgroundBorder = null!;
private BeatmapInfo beatmap => ((GroupedBeatmap)Item!.Model).Beatmap;
public PanelBeatmapStandalone()
{
PanelXOffset = 20;
}
[BackgroundDependencyLoader]
private void load()
{
Height = HEIGHT;
Icon = difficultyIcon = new ConstrainedIconContainer
{
Size = new Vector2(12),
Margin = new MarginPadding { Left = 4f, Right = 3f },
Colour = colourProvider.Background5,
};
Background = backgroundBorder = new Box
{
RelativeSizeAxes = Axes.Both,
};
Content.Children = new Drawable[]
{
beatmapBackground = new PanelSetBackground(),
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Spacing = new Vector2(5),
Margin = new MarginPadding { Left = 6.5f },
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
localRank = new PanelLocalRankDisplay
{
Scale = new Vector2(0.8f),
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
},
mainFill = new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Bottom = 4.8f },
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
titleText = new OsuSpriteText
{
Font = OsuFont.Style.Heading2.With(typeface: Typeface.TorusAlternate, weight: FontWeight.Bold),
},
artistText = new OsuSpriteText
{
Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold),
Padding = new MarginPadding { Top = -2 },
},
new FillFlowContainer
{
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 2, Bottom = 2 },
Children = new Drawable[]
{
statusPill = new BeatmapSetOnlineStatusPill
{
Animated = false,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
TextSize = OsuFont.Style.Caption2.Size,
Margin = new MarginPadding { Right = 4f },
},
updateButton = new PanelUpdateBeatmapButton
{
Scale = new Vector2(0.8f),
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Right = 4f, Bottom = -1f },
},
keyCountText = new OsuSpriteText
{
Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold),
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Alpha = 0,
},
difficultyText = new OsuSpriteText
{
Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold),
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Right = 3f },
},
authorText = new OsuSpriteText
{
Colour = colourProvider.Content2,
Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold),
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft
}
}
},
new FillFlowContainer
{
Direction = FillDirection.Horizontal,
Spacing = new Vector2(3),
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
starRatingDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Small, animated: true)
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Scale = new Vector2(0.875f),
},
starCounter = new StarCounter
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Scale = new Vector2(0.4f)
}
},
}
}
}
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
ruleset.BindValueChanged(_ => updateKeyCount());
mods.BindValueChanged(_ => updateKeyCount(), true);
Selected.BindValueChanged(s => Expanded.Value = s.NewValue, true);
}
protected override void PrepareForUse()
{
base.PrepareForUse();
var beatmapSet = beatmap.BeatmapSet!;
scheduledBackgroundRetrieval = Scheduler.AddDelayed(b => beatmapBackground.Beatmap = beatmaps.GetWorkingBeatmap(b), beatmap, 50);
titleText.Text = new RomanisableString(beatmapSet.Metadata.TitleUnicode, beatmapSet.Metadata.Title);
artistText.Text = new RomanisableString(beatmapSet.Metadata.ArtistUnicode, beatmapSet.Metadata.Artist);
updateButton.BeatmapSet = beatmapSet;
statusPill.Status = beatmap.Status;
difficultyIcon.Icon = beatmap.Ruleset.CreateInstance().CreateIcon();
difficultyIcon.Show();
localRank.Beatmap = beatmap;
difficultyText.Text = beatmap.DifficultyName;
authorText.Text = BeatmapsetsStrings.ShowDetailsMappedBy(beatmap.Metadata.Author.Username);
computeStarRating();
updateKeyCount();
}
protected override void FreeAfterUse()
{
base.FreeAfterUse();
scheduledBackgroundRetrieval?.Cancel();
scheduledBackgroundRetrieval = null;
beatmapBackground.Beatmap = null;
updateButton.BeatmapSet = null;
localRank.Beatmap = null;
starDifficultyBindable = null;
starDifficultyCancellationSource?.Cancel();
}
private void computeStarRating()
{
starDifficultyCancellationSource?.Cancel();
starDifficultyCancellationSource = new CancellationTokenSource();
if (Item == null)
return;
starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, starDifficultyCancellationSource.Token, SongSelect.DIFFICULTY_CALCULATION_DEBOUNCE);
starDifficultyBindable.BindValueChanged(starDifficulty =>
{
starRatingDisplay.Current.Value = starDifficulty.NewValue;
starCounter.Current = (float)starDifficulty.NewValue.Stars;
}, true);
}
protected override void Update()
{
base.Update();
if (Item?.IsVisible != true)
{
starDifficultyCancellationSource?.Cancel();
starDifficultyCancellationSource = null;
}
// Dirty hack to make sure we don't take up spacing in parent fill flow when not displaying a rank.
// I can't find a better way to do this.
mainFill.Margin = new MarginPadding { Left = 1 / starRatingDisplay.Scale.X * (localRank.HasRank ? 0 : -3) };
var diffColour = starRatingDisplay.DisplayedDifficultyColour;
AccentColour = diffColour;
starCounter.Colour = diffColour;
backgroundBorder.Colour = diffColour;
difficultyIcon.Colour = starRatingDisplay.DisplayedStars.Value > OsuColour.STAR_DIFFICULTY_DEFINED_COLOUR_CUTOFF ? colours.Orange1 : colourProvider.Background5;
}
private void updateKeyCount()
{
if (Item == null)
return;
if (ruleset.Value.OnlineID == 3)
{
// Account for mania differences locally for now.
// Eventually this should be handled in a more modular way, allowing rulesets to add more information to the panel.
ILegacyRuleset legacyRuleset = (ILegacyRuleset)ruleset.Value.CreateInstance();
int keyCount = legacyRuleset.GetKeyCount(beatmap, mods.Value);
keyCountText.Alpha = 1;
keyCountText.Text = $"[{keyCount}K] ";
}
else
keyCountText.Alpha = 0;
}
public override MenuItem[] ContextMenuItems
{
get
{
if (Item == null)
return Array.Empty<MenuItem>();
List<MenuItem> items = new List<MenuItem>();
if (songSelect != null)
items.AddRange(songSelect.GetForwardActions(beatmap));
return items.ToArray();
}
}
}
}