Remove FrameStableClock (and redirect usages to FrameStabilityContainer)

This commit is contained in:
Dean Herbert 2022-08-15 18:53:10 +09:00
parent 71961b244e
commit 27569e2ed5
9 changed files with 63 additions and 57 deletions

View File

@ -4,6 +4,7 @@
#nullable disable
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Timing;
@ -30,7 +31,7 @@ namespace osu.Game.Tests.NonVisual
{
public List<Bindable<double>> MutableNonGameplayAdjustments { get; } = new List<Bindable<double>>();
public override IEnumerable<Bindable<double>> NonGameplayAdjustments => MutableNonGameplayAdjustments;
public override IEnumerable<double> NonGameplayAdjustments => MutableNonGameplayAdjustments.Select(b => b.Value);
public TestGameplayClock(IFrameBasedClock underlyingClock)
: base(underlyingClock)

View File

@ -363,7 +363,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private Player player => Stack.CurrentScreen as Player;
private double currentFrameStableTime
=> player.ChildrenOfType<FrameStabilityContainer>().First().FrameStableClock.CurrentTime;
=> player.ChildrenOfType<FrameStabilityContainer>().First().CurrentTime;
private void waitForPlayer() => AddUntilStep("wait for player", () => (Stack.CurrentScreen as Player)?.IsLoaded == true);

View File

@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.UI
public override Container FrameStableComponents { get; } = new Container { RelativeSizeAxes = Axes.Both };
public override IFrameStableClock FrameStableClock => frameStabilityContainer.FrameStableClock;
public override IFrameStableClock FrameStableClock => frameStabilityContainer;
private bool frameStablePlayback = true;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -21,7 +19,9 @@ namespace osu.Game.Rulesets.UI
/// A container which consumes a parent gameplay clock and standardises frame counts for children.
/// Will ensure a minimum of 50 frames per clock second is maintained, regardless of any system lag or seeks.
/// </summary>
public class FrameStabilityContainer : Container, IHasReplayHandler
[Cached(typeof(IGameplayClock))]
[Cached(typeof(IFrameStableClock))]
public class FrameStabilityContainer : Container, IHasReplayHandler, IFrameStableClock, IGameplayClock
{
private readonly double gameplayStartTime;
@ -35,16 +35,17 @@ namespace osu.Game.Rulesets.UI
/// </summary>
internal bool FrameStablePlayback = true;
public IFrameStableClock FrameStableClock => frameStableClock;
public readonly Bindable<bool> IsCatchingUp = new Bindable<bool>();
[Cached(typeof(IGameplayClock))]
private readonly FrameStabilityClock frameStableClock;
public readonly Bindable<bool> WaitingOnFrames = new Bindable<bool>();
public IBindable<bool> IsPaused { get; } = new BindableBool();
public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
{
RelativeSizeAxes = Axes.Both;
frameStableClock = new FrameStabilityClock(framedClock = new FramedClock(manualClock = new ManualClock()));
framedClock = new FramedClock(manualClock = new ManualClock());
this.gameplayStartTime = gameplayStartTime;
}
@ -53,7 +54,7 @@ namespace osu.Game.Rulesets.UI
private readonly FramedClock framedClock;
private IFrameBasedClock parentGameplayClock;
private IGameplayClock? parentGameplayClock;
/// <summary>
/// The current direction of playback to be exposed to frame stable children.
@ -63,13 +64,13 @@ namespace osu.Game.Rulesets.UI
/// </remarks>
private int direction = 1;
[BackgroundDependencyLoader(true)]
private void load(IGameplayClock clock)
[BackgroundDependencyLoader]
private void load(IGameplayClock? clock)
{
if (clock != null)
{
parentGameplayClock = frameStableClock.ParentGameplayClock = clock;
((IBindable<bool>)frameStableClock.IsPaused).BindTo(clock.IsPaused);
parentGameplayClock = clock;
IsPaused.BindTo(parentGameplayClock.IsPaused);
}
}
@ -111,12 +112,12 @@ namespace osu.Game.Rulesets.UI
private void updateClock()
{
if (frameStableClock.WaitingOnFrames.Value)
if (WaitingOnFrames.Value)
{
// if waiting on frames, run one update loop to determine if frames have arrived.
state = PlaybackState.Valid;
}
else if (frameStableClock.IsPaused.Value)
else if (IsPaused.Value)
{
// time should not advance while paused, nor should anything run.
state = PlaybackState.NotValid;
@ -154,8 +155,8 @@ namespace osu.Game.Rulesets.UI
double timeBehind = Math.Abs(proposedTime - parentGameplayClock.CurrentTime);
frameStableClock.IsCatchingUp.Value = timeBehind > 200;
frameStableClock.WaitingOnFrames.Value = state == PlaybackState.NotValid;
IsCatchingUp.Value = timeBehind > 200;
WaitingOnFrames.Value = state == PlaybackState.NotValid;
manualClock.CurrentTime = proposedTime;
manualClock.Rate = Math.Abs(parentGameplayClock.Rate) * direction;
@ -177,6 +178,8 @@ namespace osu.Game.Rulesets.UI
/// <returns>Whether playback is still valid.</returns>
private bool updateReplay(ref double proposedTime)
{
Debug.Assert(ReplayInputHandler != null);
double? newTime;
if (FrameStablePlayback)
@ -238,18 +241,39 @@ namespace osu.Game.Rulesets.UI
private void setClock()
{
if (parentGameplayClock == null)
{
// in case a parent gameplay clock isn't available, just use the parent clock.
parentGameplayClock ??= Clock;
}
else
{
Clock = frameStableClock;
}
if (parentGameplayClock != null)
Clock = this;
}
public ReplayInputHandler ReplayInputHandler { get; set; }
public ReplayInputHandler? ReplayInputHandler { get; set; }
#region Delegation of IFrameStableClock
public double CurrentTime => framedClock.CurrentTime;
public double Rate => framedClock.Rate;
public bool IsRunning => framedClock.IsRunning;
public void ProcessFrame() => framedClock.ProcessFrame();
public double ElapsedFrameTime => framedClock.ElapsedFrameTime;
public double FramesPerSecond => framedClock.FramesPerSecond;
public FrameTimeInfo TimeInfo => framedClock.TimeInfo;
#endregion
#region Delegation of IGameplayClock
public double TrueGameplayRate => parentGameplayClock?.TrueGameplayRate ?? 1;
public double? StartTime => parentGameplayClock?.StartTime;
public IEnumerable<double> NonGameplayAdjustments => parentGameplayClock?.NonGameplayAdjustments ?? Enumerable.Empty<double>();
#endregion
private enum PlaybackState
{
@ -270,24 +294,7 @@ namespace osu.Game.Rulesets.UI
Valid
}
private class FrameStabilityClock : GameplayClock, IFrameStableClock
{
public IGameplayClock ParentGameplayClock;
public readonly Bindable<bool> IsCatchingUp = new Bindable<bool>();
public readonly Bindable<bool> WaitingOnFrames = new Bindable<bool>();
public override IEnumerable<Bindable<double>> NonGameplayAdjustments => ParentGameplayClock?.NonGameplayAdjustments ?? Enumerable.Empty<Bindable<double>>();
public FrameStabilityClock(FramedClock underlyingClock)
: base(underlyingClock)
{
}
IBindable<bool> IFrameStableClock.IsCatchingUp => IsCatchingUp;
IBindable<bool> IFrameStableClock.WaitingOnFrames => WaitingOnFrames;
}
IBindable<bool> IFrameStableClock.IsCatchingUp => IsCatchingUp;
IBindable<bool> IFrameStableClock.WaitingOnFrames => WaitingOnFrames;
}
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Bindables;
using osu.Framework.Timing;

View File

@ -27,7 +27,7 @@ namespace osu.Game.Screens.Play
IBindable<bool> IGameplayClock.IsPaused => IsPaused;
public virtual IEnumerable<Bindable<double>> NonGameplayAdjustments => Enumerable.Empty<Bindable<double>>();
public virtual IEnumerable<double> NonGameplayAdjustments => Enumerable.Empty<double>();
public GameplayClock(IFrameBasedClock underlyingClock)
{
@ -46,12 +46,12 @@ namespace osu.Game.Screens.Play
{
double baseRate = Rate;
foreach (var adjustment in NonGameplayAdjustments)
foreach (double adjustment in NonGameplayAdjustments)
{
if (Precision.AlmostEquals(adjustment.Value, 0))
if (Precision.AlmostEquals(adjustment, 0))
return 0;
baseRate /= adjustment.Value;
baseRate /= adjustment;
}
return baseRate;

View File

@ -61,7 +61,7 @@ namespace osu.Game.Screens.Play
}
}
public IEnumerable<Bindable<double>> NonGameplayAdjustments => GameplayClock.NonGameplayAdjustments;
public IEnumerable<double> NonGameplayAdjustments => GameplayClock.NonGameplayAdjustments;
/// <summary>
/// The final clock which is exposed to gameplay components.

View File

@ -27,7 +27,7 @@ namespace osu.Game.Screens.Play
/// <summary>
/// All adjustments applied to this clock which don't come from gameplay or mods.
/// </summary>
IEnumerable<Bindable<double>> NonGameplayAdjustments { get; }
IEnumerable<double> NonGameplayAdjustments { get; }
IBindable<bool> IsPaused { get; }
}

View File

@ -303,7 +303,7 @@ namespace osu.Game.Screens.Play
private class MasterGameplayClock : GameplayClock
{
public readonly List<Bindable<double>> MutableNonGameplayAdjustments = new List<Bindable<double>>();
public override IEnumerable<Bindable<double>> NonGameplayAdjustments => MutableNonGameplayAdjustments;
public override IEnumerable<double> NonGameplayAdjustments => MutableNonGameplayAdjustments.Select(b => b.Value);
public MasterGameplayClock(FramedOffsetClock underlyingClock)
: base(underlyingClock)