1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 11:42:56 +08:00

Merge pull request #26422 from peppy/allocs-off-the-charts

Preliminary fixes for off-the-charts allocations
This commit is contained in:
Bartłomiej Dach 2024-01-09 16:06:24 +01:00 committed by GitHub
commit 8e133ed3ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 169 additions and 63 deletions

View File

@ -11,6 +11,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Layout;
using osu.Game.Audio;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Objects;
@ -66,6 +67,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private Container<DrawableSliderRepeat> repeatContainer;
private PausableSkinnableSound slidingSample;
private readonly LayoutValue drawSizeLayout;
public DrawableSlider()
: this(null)
{
@ -82,6 +85,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
AlwaysPresent = true,
Alpha = 0
};
AddLayout(drawSizeLayout = new LayoutValue(Invalidation.DrawSize | Invalidation.MiscGeometry));
}
[BackgroundDependencyLoader]
@ -246,21 +250,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Ball.UpdateProgress(completionProgress);
SliderBody?.UpdateProgress(HeadCircle.IsHit ? completionProgress : 0);
foreach (DrawableHitObject hitObject in NestedHitObjects)
{
if (hitObject is ITrackSnaking s)
s.UpdateSnakingPosition(HitObject.Path.PositionAt(SliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(SliderBody?.SnakedEnd ?? 0));
}
foreach (DrawableSliderRepeat repeat in repeatContainer)
repeat.UpdateSnakingPosition(HitObject.Path.PositionAt(SliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(SliderBody?.SnakedEnd ?? 0));
Size = SliderBody?.Size ?? Vector2.Zero;
OriginPosition = SliderBody?.PathOffset ?? Vector2.Zero;
if (DrawSize != Vector2.Zero)
if (!drawSizeLayout.IsValid)
{
var childAnchorPosition = Vector2.Divide(OriginPosition, DrawSize);
Vector2 pos = Vector2.Divide(OriginPosition, DrawSize);
foreach (var obj in NestedHitObjects)
obj.RelativeAnchorPosition = childAnchorPosition;
Ball.RelativeAnchorPosition = childAnchorPosition;
obj.RelativeAnchorPosition = pos;
Ball.RelativeAnchorPosition = pos;
drawSizeLayout.Validate();
}
}

View File

@ -17,7 +17,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public partial class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking
public partial class DrawableSliderRepeat : DrawableOsuHitObject
{
public new SliderRepeat HitObject => (SliderRepeat)base.HitObject;

View File

@ -1,15 +0,0 @@
// 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 osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
/// <summary>
/// A component which tracks the current end snaking position of a slider.
/// </summary>
public interface ITrackSnaking
{
void UpdateSnakingPosition(Vector2 start, Vector2 end);
}
}

View File

@ -599,7 +599,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
float balanceAdjustAmount = positionalHitsoundsLevel.Value * 2;
double returnedValue = balanceAdjustAmount * (position - 0.5f);
return returnedValue;
// Rounded to reduce the overhead of audio adjustments (which are currently bindable heavy).
// Balance is very hard to perceive in small increments anyways.
return Math.Round(returnedValue, 2);
}
/// <summary>

View File

@ -83,12 +83,14 @@ namespace osu.Game.Screens.Play.HUD
},
fractionPart = new ArgonCounterTextComponent(Anchor.TopLeft)
{
RequiredDisplayDigits = { Value = 2 },
WireframeOpacity = { BindTarget = WireframeOpacity },
Scale = new Vector2(0.5f),
},
percentText = new ArgonCounterTextComponent(Anchor.TopLeft)
{
Text = @"%",
RequiredDisplayDigits = { Value = 1 },
WireframeOpacity = { BindTarget = WireframeOpacity }
},
}

View File

@ -57,6 +57,31 @@ namespace osu.Game.Screens.Play.HUD
});
}
public override int DisplayedCount
{
get => base.DisplayedCount;
set
{
base.DisplayedCount = value;
updateWireframe();
}
}
private void updateWireframe()
{
text.RequiredDisplayDigits.Value = getDigitsRequiredForDisplayCount();
}
private int getDigitsRequiredForDisplayCount()
{
// one for the single presumed starting digit, one for the "x" at the end.
int digitsRequired = 2;
long c = DisplayedCount;
while ((c /= 10) > 0)
digitsRequired++;
return digitsRequired;
}
protected override LocalisableString FormatCount(int count) => $@"{count}x";
protected override IHasText CreateText() => text = new ArgonCounterTextComponent(Anchor.TopLeft, MatchesStrings.MatchScoreStatsCombo.ToUpper())

View File

@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -33,14 +33,7 @@ namespace osu.Game.Screens.Play.HUD
public LocalisableString Text
{
get => textPart.Text;
set
{
int remainingCount = RequiredDisplayDigits.Value - value.ToString().Count(char.IsDigit);
string remainingText = remainingCount > 0 ? new string('#', remainingCount) : string.Empty;
wireframesPart.Text = remainingText + value;
textPart.Text = value;
}
set => textPart.Text = value;
}
public ArgonCounterTextComponent(Anchor anchor, LocalisableString? label = null)
@ -81,6 +74,8 @@ namespace osu.Game.Screens.Play.HUD
}
}
};
RequiredDisplayDigits.BindValueChanged(digits => wireframesPart.Text = new string('#', digits.NewValue));
}
private string textLookup(char c)
@ -137,33 +132,49 @@ namespace osu.Game.Screens.Play.HUD
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
const string font_name = @"argon-counter";
Spacing = new Vector2(-2f, 0f);
Font = new FontUsage(@"argon-counter", 1);
glyphStore = new GlyphStore(textures, getLookup);
Font = new FontUsage(font_name, 1);
glyphStore = new GlyphStore(font_name, textures, getLookup);
}
protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore);
private class GlyphStore : ITexturedGlyphLookupStore
{
private readonly string fontName;
private readonly TextureStore textures;
private readonly Func<char, string> getLookup;
public GlyphStore(TextureStore textures, Func<char, string> getLookup)
private readonly Dictionary<char, ITexturedCharacterGlyph?> cache = new Dictionary<char, ITexturedCharacterGlyph?>();
public GlyphStore(string fontName, TextureStore textures, Func<char, string> getLookup)
{
this.fontName = fontName;
this.textures = textures;
this.getLookup = getLookup;
}
public ITexturedCharacterGlyph? Get(string? fontName, char character)
{
// We only service one font.
if (fontName != this.fontName)
return null;
if (cache.TryGetValue(character, out var cached))
return cached;
string lookup = getLookup(character);
var texture = textures.Get($"Gameplay/Fonts/{fontName}-{lookup}");
if (texture == null)
return null;
TexturedCharacterGlyph? glyph = null;
return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 0.125f);
if (texture != null)
glyph = new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 0.125f);
cache[character] = glyph;
return glyph;
}
public Task<ITexturedCharacterGlyph?> GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character));

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
@ -68,11 +69,11 @@ namespace osu.Game.Screens.Play.HUD
get => glowBarValue;
set
{
if (glowBarValue == value)
if (Precision.AlmostEquals(glowBarValue, value, 0.0001))
return;
glowBarValue = value;
Scheduler.AddOnce(updatePathVertices);
pathVerticesCache.Invalidate();
}
}
@ -83,11 +84,11 @@ namespace osu.Game.Screens.Play.HUD
get => healthBarValue;
set
{
if (healthBarValue == value)
if (Precision.AlmostEquals(healthBarValue, value, 0.0001))
return;
healthBarValue = value;
Scheduler.AddOnce(updatePathVertices);
pathVerticesCache.Invalidate();
}
}
@ -100,6 +101,8 @@ namespace osu.Game.Screens.Play.HUD
private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize);
private readonly Cached pathVerticesCache = new Cached();
public ArgonHealthDisplay()
{
AddLayout(drawSizeLayout);
@ -208,6 +211,9 @@ namespace osu.Game.Screens.Play.HUD
drawSizeLayout.Validate();
}
if (!pathVerticesCache.IsValid)
updatePathVertices();
mainBar.Alpha = (float)Interpolation.DampContinuously(mainBar.Alpha, Current.Value > 0 ? 1 : 0, 40, Time.Elapsed);
glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, GlowBarValue > 0 ? 1 : 0, 40, Time.Elapsed);
}
@ -346,6 +352,8 @@ namespace osu.Game.Screens.Play.HUD
mainBar.Vertices = healthBarVertices.Select(v => v - healthBarVertices[0]).ToList();
mainBar.Position = healthBarVertices[0];
pathVerticesCache.Validate();
}
protected override void Dispose(bool isDisposing)

View File

@ -1,6 +1,7 @@
// 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 osu.Framework.Bindables;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
@ -15,6 +16,8 @@ namespace osu.Game.Screens.Play.HUD
{
public partial class ArgonScoreCounter : GameplayScoreCounter, ISerialisableDrawable
{
private ArgonScoreTextComponent scoreText = null!;
protected override double RollingDuration => 500;
protected override Easing RollingEasing => Easing.OutQuint;
@ -33,13 +36,42 @@ namespace osu.Game.Screens.Play.HUD
protected override LocalisableString FormatCount(long count) => count.ToLocalisableString();
protected override IHasText CreateText() => new ArgonScoreTextComponent(Anchor.TopRight, BeatmapsetsStrings.ShowScoreboardHeadersScore.ToUpper())
protected override IHasText CreateText() => scoreText = new ArgonScoreTextComponent(Anchor.TopRight, BeatmapsetsStrings.ShowScoreboardHeadersScore.ToUpper())
{
RequiredDisplayDigits = { BindTarget = RequiredDisplayDigits },
WireframeOpacity = { BindTarget = WireframeOpacity },
ShowLabel = { BindTarget = ShowLabel },
};
public ArgonScoreCounter()
{
RequiredDisplayDigits.BindValueChanged(_ => updateWireframe());
}
public override long DisplayedCount
{
get => base.DisplayedCount;
set
{
base.DisplayedCount = value;
updateWireframe();
}
}
private void updateWireframe()
{
scoreText.RequiredDisplayDigits.Value =
Math.Max(RequiredDisplayDigits.Value, getDigitsRequiredForDisplayCount());
}
private int getDigitsRequiredForDisplayCount()
{
int digitsRequired = 1;
long c = DisplayedCount;
while ((c /= 10) > 0)
digitsRequired++;
return digitsRequired;
}
private partial class ArgonScoreTextComponent : ArgonCounterTextComponent
{
public ArgonScoreTextComponent(Anchor anchor, LocalisableString? label = null)

View File

@ -11,11 +11,6 @@ namespace osu.Game.Screens.Play.HUD
{
public bool UsesFixedAnchor { get; set; }
protected ComboCounter()
{
Current.Value = DisplayedCount = 0;
}
protected override double GetProportionalDuration(int currentValue, int newValue)
{
return Math.Abs(currentValue - newValue) * RollingDuration * 100.0f;

View File

@ -30,7 +30,7 @@ namespace osu.Game.Screens.Play.HUD
public Bindable<double> Current { get; } = new BindableDouble
{
MinValue = 0,
MaxValue = 1
MaxValue = 1,
};
private BindableNumber<double> health = null!;

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
@ -44,10 +45,11 @@ namespace osu.Game.Skinning
[BackgroundDependencyLoader]
private void load(ISkinSource skin)
{
base.Font = new FontUsage(skin.GetFontPrefix(font), 1, fixedWidth: FixedWidth);
string fontPrefix = skin.GetFontPrefix(font);
base.Font = new FontUsage(fontPrefix, 1, fixedWidth: FixedWidth);
Spacing = new Vector2(-skin.GetFontOverlap(font), 0);
glyphStore = new LegacyGlyphStore(skin, MaxSizePerGlyph);
glyphStore = new LegacyGlyphStore(fontPrefix, skin, MaxSizePerGlyph);
}
protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore);
@ -57,25 +59,42 @@ namespace osu.Game.Skinning
private readonly ISkin skin;
private readonly Vector2? maxSize;
public LegacyGlyphStore(ISkin skin, Vector2? maxSize)
private readonly string fontName;
private readonly Dictionary<char, ITexturedCharacterGlyph?> cache = new Dictionary<char, ITexturedCharacterGlyph?>();
public LegacyGlyphStore(string fontName, ISkin skin, Vector2? maxSize)
{
this.fontName = fontName;
this.skin = skin;
this.maxSize = maxSize;
}
public ITexturedCharacterGlyph? Get(string? fontName, char character)
{
// We only service one font.
if (fontName != this.fontName)
return null;
if (cache.TryGetValue(character, out var cached))
return cached;
string lookup = getLookupName(character);
var texture = skin.GetTexture($"{fontName}-{lookup}");
if (texture == null)
return null;
TexturedCharacterGlyph? glyph = null;
if (texture != null)
{
if (maxSize != null)
texture = texture.WithMaximumSize(maxSize.Value);
return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 1f / texture.ScaleAdjust);
glyph = new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 1f / texture.ScaleAdjust);
}
cache[character] = glyph;
return glyph;
}
private static string getLookupName(char character)

View File

@ -194,9 +194,33 @@ namespace osu.Game.Skinning
/// <summary>
/// Whether any samples are currently playing.
/// </summary>
public bool IsPlaying => samplesContainer.Any(s => s.Playing);
public bool IsPlaying
{
get
{
foreach (PoolableSkinnableSample s in samplesContainer)
{
if (s.Playing)
return true;
}
public bool IsPlayed => samplesContainer.Any(s => s.Played);
return false;
}
}
public bool IsPlayed
{
get
{
foreach (PoolableSkinnableSample s in samplesContainer)
{
if (s.Played)
return true;
}
return false;
}
}
public IBindable<double> AggregateVolume => samplesContainer.AggregateVolume;