From 3fad481a96799fecc55bb1819a4a28ed2f12972a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Jul 2022 22:27:22 +0900 Subject: [PATCH] Avoid using `RollingCounter` in fps counter It wasn't made to be updated every frame, and it shows. Inaccurate for reasons I'm not really interested in investigating, because I don't want to incur the `Transorm` overhead in the first place for an fps counter. Was only used originally out of convenience. --- osu.Game/Graphics/UserInterface/FPSCounter.cs | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/FPSCounter.cs b/osu.Game/Graphics/UserInterface/FPSCounter.cs index 91e0429c85..6982f94abc 100644 --- a/osu.Game/Graphics/UserInterface/FPSCounter.cs +++ b/osu.Game/Graphics/UserInterface/FPSCounter.cs @@ -22,8 +22,8 @@ namespace osu.Game.Graphics.UserInterface { public class FPSCounter : VisibilityContainer, IHasCustomTooltip { - private RollingCounter counterUpdateFrameTime = null!; - private RollingCounter counterDrawFPS = null!; + private OsuSpriteText counterUpdateFrameTime = null!; + private OsuSpriteText counterDrawFPS = null!; private Container mainContent = null!; @@ -35,6 +35,9 @@ namespace osu.Game.Graphics.UserInterface private readonly BindableBool showFpsDisplay = new BindableBool(true); + private double displayedFpsCount; + private double displayedFrameTime; + [Resolved] private OsuColour colours { get; set; } = null!; @@ -77,20 +80,23 @@ namespace osu.Game.Graphics.UserInterface AutoSizeAxes = Axes.Both, Children = new Drawable[] { - counterUpdateFrameTime = new FrameTimeCounter + counterUpdateFrameTime = new OsuSpriteText { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Margin = new MarginPadding(1), + Font = OsuFont.Default.With(fixedWidth: true, size: 16, weight: FontWeight.SemiBold), + Spacing = new Vector2(-1), Y = -2, }, - counterDrawFPS = new FramesPerSecondCounter + counterDrawFPS = new OsuSpriteText { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Margin = new MarginPadding(2), + Font = OsuFont.Default.With(fixedWidth: true, size: 13, weight: FontWeight.SemiBold), + Spacing = new Vector2(-2), Y = 10, - Scale = new Vector2(0.8f), } } }, @@ -183,37 +189,48 @@ namespace osu.Game.Graphics.UserInterface const double spike_time_ms = 20; - bool hasUpdateSpike = counterUpdateFrameTime.Current.Value < spike_time_ms && newUpdateFrameTime > spike_time_ms; + bool hasUpdateSpike = displayedFrameTime < spike_time_ms && newUpdateFrameTime > spike_time_ms; // use elapsed frame time rather then FramesPerSecond to better catch stutter frames. - bool hasDrawSpike = counterDrawFPS.Current.Value > (1000 / spike_time_ms) && newDrawFrameTime > spike_time_ms; + bool hasDrawSpike = displayedFpsCount > (1000 / spike_time_ms) && newDrawFrameTime > spike_time_ms; - // If the frame time spikes up, make sure it shows immediately on the counter. - if (hasUpdateSpike) - counterUpdateFrameTime.SetCountWithoutRolling(newUpdateFrameTime); - else - counterUpdateFrameTime.Current.Value = newUpdateFrameTime; + // note that we use an elapsed time here of 1 intentionally. + // this weights all updates equally. if we passed in the elapsed time, longer frames would be weighted incorrectly lower. + displayedFrameTime = Interpolation.DampContinuously(displayedFrameTime, newUpdateFrameTime, hasUpdateSpike ? 0 : 200, 1); if (hasDrawSpike) // show spike time using raw elapsed value, to account for `FramesPerSecond` being so averaged spike frames don't show. - counterDrawFPS.SetCountWithoutRolling(1000 / newDrawFrameTime); + displayedFpsCount = 1000 / newDrawFrameTime; else - counterDrawFPS.Current.Value = newDrawFps; + displayedFpsCount = Interpolation.DampContinuously(displayedFpsCount, newDrawFps, 200, Time.Elapsed); - counterDrawFPS.Colour = getColour(counterDrawFPS.DisplayedCount / aimDrawFPS); - - double displayedUpdateFPS = 1000 / counterUpdateFrameTime.DisplayedCount; - counterUpdateFrameTime.Colour = getColour(displayedUpdateFPS / aimUpdateFPS); + updateFpsDisplay(); + updateFrameTimeDisplay(); bool hasSignificantChanges = aimRatesChanged || hasDrawSpike || hasUpdateSpike - || counterDrawFPS.DisplayedCount < aimDrawFPS * 0.8 - || displayedUpdateFPS < aimUpdateFPS * 0.8; + || displayedFpsCount < aimDrawFPS * 0.8 + || 1000 / displayedFrameTime < aimUpdateFPS * 0.8; if (hasSignificantChanges) displayTemporarily(); } + private void updateFpsDisplay() + { + counterDrawFPS.Colour = getColour(displayedFpsCount / aimDrawFPS); + counterDrawFPS.Text = $"{displayedFpsCount:#,0}fps"; + } + + private void updateFrameTimeDisplay() + { + counterUpdateFrameTime.Text = displayedFrameTime < 5 + ? $"{displayedFrameTime:N1}ms" + : $"{displayedFrameTime:N0}ms"; + + counterUpdateFrameTime.Colour = getColour((1000 / displayedFrameTime) / aimUpdateFPS); + } + private bool updateAimFPS() { if (gameHost.UpdateThread.Clock.Throttling)