mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 08:52:55 +08:00
Merge pull request #26422 from peppy/allocs-off-the-charts
Preliminary fixes for off-the-charts allocations
This commit is contained in:
commit
8e133ed3ab
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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 }
|
||||
},
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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!;
|
||||
|
@ -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 (maxSize != null)
|
||||
texture = texture.WithMaximumSize(maxSize.Value);
|
||||
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)
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user