1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 15:43:22 +08:00

Merge pull request #19777 from peppy/even-nicer-frame-stability-clock

Tidy up and document `FrameStabilityContainer`
This commit is contained in:
Bartłomiej Dach 2022-08-15 17:06:59 +02:00 committed by GitHub
commit adeabc632b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -22,9 +22,9 @@ namespace osu.Game.Rulesets.UI
/// </summary>
[Cached(typeof(IGameplayClock))]
[Cached(typeof(IFrameStableClock))]
public class FrameStabilityContainer : Container, IHasReplayHandler, IFrameStableClock, IGameplayClock
public sealed class FrameStabilityContainer : Container, IHasReplayHandler, IFrameStableClock, IGameplayClock
{
private readonly double gameplayStartTime;
public ReplayInputHandler? ReplayInputHandler { get; set; }
/// <summary>
/// The number of frames (per parent frame) which can be run in an attempt to catch-up to real-time.
@ -34,13 +34,48 @@ namespace osu.Game.Rulesets.UI
/// <summary>
/// Whether to enable frame-stable playback.
/// </summary>
internal bool FrameStablePlayback = true;
internal bool FrameStablePlayback { get; set; } = true;
public readonly Bindable<bool> IsCatchingUp = new Bindable<bool>();
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && state != PlaybackState.NotValid;
public readonly Bindable<bool> WaitingOnFrames = new Bindable<bool>();
private readonly Bindable<bool> isCatchingUp = new Bindable<bool>();
public IBindable<bool> IsPaused { get; } = new BindableBool();
private readonly Bindable<bool> waitingOnFrames = new Bindable<bool>();
private readonly double gameplayStartTime;
private IGameplayClock? parentGameplayClock;
/// <summary>
/// A clock which is used as reference for time, rate and running state.
/// </summary>
private IClock referenceClock = null!;
/// <summary>
/// A local manual clock which tracks the reference clock.
/// Values are transferred from <see cref="referenceClock"/> each update call.
/// </summary>
private readonly ManualClock manualClock;
/// <summary>
/// The main framed clock which has stability applied to it.
/// This gets exposed to children as an <see cref="IGameplayClock"/>.
/// </summary>
private readonly FramedClock framedClock;
/// <summary>
/// The current direction of playback to be exposed to frame stable children.
/// </summary>
/// <remarks>
/// Initially it is presumed that playback will proceed in the forward direction.
/// </remarks>
private int direction = 1;
private PlaybackState state;
private bool hasReplayAttached => ReplayInputHandler != null;
private bool firstConsumption = true;
public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
{
@ -51,22 +86,6 @@ namespace osu.Game.Rulesets.UI
this.gameplayStartTime = gameplayStartTime;
}
private readonly ManualClock manualClock;
private readonly FramedClock framedClock;
private IGameplayClock? parentGameplayClock;
private IClock referenceClock = null!;
/// <summary>
/// The current direction of playback to be exposed to frame stable children.
/// </summary>
/// <remarks>
/// Initially it is presumed that playback will proceed in the forward direction.
/// </remarks>
private int direction = 1;
[BackgroundDependencyLoader]
private void load(IGameplayClock? gameplayClock)
{
@ -80,16 +99,6 @@ namespace osu.Game.Rulesets.UI
Clock = this;
}
private PlaybackState state;
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && state != PlaybackState.NotValid;
private bool hasReplayAttached => ReplayInputHandler != null;
private const double sixty_frame_time = 1000.0 / 60;
private bool firstConsumption = true;
public override bool UpdateSubTree()
{
int loops = MaxCatchUpFrames;
@ -112,7 +121,7 @@ namespace osu.Game.Rulesets.UI
private void updateClock()
{
if (WaitingOnFrames.Value)
if (waitingOnFrames.Value)
{
// if waiting on frames, run one update loop to determine if frames have arrived.
state = PlaybackState.Valid;
@ -150,8 +159,8 @@ namespace osu.Game.Rulesets.UI
double timeBehind = Math.Abs(proposedTime - referenceClock.CurrentTime);
IsCatchingUp.Value = timeBehind > 200;
WaitingOnFrames.Value = state == PlaybackState.NotValid;
isCatchingUp.Value = timeBehind > 200;
waitingOnFrames.Value = state == PlaybackState.NotValid;
manualClock.CurrentTime = proposedTime;
manualClock.Rate = Math.Abs(referenceClock.Rate) * direction;
@ -211,6 +220,8 @@ namespace osu.Game.Rulesets.UI
/// <param name="proposedTime">The time which is to be displayed.</param>
private void applyFrameStability(ref double proposedTime)
{
const double sixty_frame_time = 1000.0 / 60;
if (firstConsumption)
{
// On the first update, frame-stability seeking would result in unexpected/unwanted behaviour.
@ -234,9 +245,9 @@ namespace osu.Game.Rulesets.UI
}
}
public ReplayInputHandler? ReplayInputHandler { get; set; }
#region Delegation of IGameplayClock
#region Delegation of IFrameStableClock
public IBindable<bool> IsPaused { get; } = new BindableBool();
public double CurrentTime => framedClock.CurrentTime;
@ -252,10 +263,6 @@ namespace osu.Game.Rulesets.UI
public FrameTimeInfo TimeInfo => framedClock.TimeInfo;
#endregion
#region Delegation of IGameplayClock
public double TrueGameplayRate
{
get
@ -280,6 +287,13 @@ namespace osu.Game.Rulesets.UI
#endregion
#region Delegation of IFrameStableClock
IBindable<bool> IFrameStableClock.IsCatchingUp => isCatchingUp;
IBindable<bool> IFrameStableClock.WaitingOnFrames => waitingOnFrames;
#endregion
private enum PlaybackState
{
/// <summary>
@ -298,8 +312,5 @@ namespace osu.Game.Rulesets.UI
/// </summary>
Valid
}
IBindable<bool> IFrameStableClock.IsCatchingUp => IsCatchingUp;
IBindable<bool> IFrameStableClock.WaitingOnFrames => WaitingOnFrames;
}
}