Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tidy up and document FrameStabilityContainer #19777

Merged
merged 4 commits into from
Aug 15, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 49 additions & 38 deletions osu.Game/Rulesets/UI/FrameStabilityContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -34,31 +34,35 @@ public class FrameStabilityContainer : Container, IHasReplayHandler, IFrameStabl
/// <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>();

public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
{
RelativeSizeAxes = Axes.Both;
private readonly double gameplayStartTime;

framedClock = new FramedClock(manualClock = new ManualClock());
private IGameplayClock? parentGameplayClock;

this.gameplayStartTime = gameplayStartTime;
}
/// <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;

private IGameplayClock? parentGameplayClock;

private IClock referenceClock = null!;

/// <summary>
/// The current direction of playback to be exposed to frame stable children.
/// </summary>
Expand All @@ -67,6 +71,21 @@ public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
/// </remarks>
private int direction = 1;

private PlaybackState state;

private bool hasReplayAttached => ReplayInputHandler != null;

private bool firstConsumption = true;

public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
{
RelativeSizeAxes = Axes.Both;

framedClock = new FramedClock(manualClock = new ManualClock());

this.gameplayStartTime = gameplayStartTime;
}

[BackgroundDependencyLoader]
private void load(IGameplayClock? gameplayClock)
{
Expand All @@ -80,16 +99,6 @@ private void load(IGameplayClock? gameplayClock)
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;
Expand All @@ -112,7 +121,7 @@ public override bool UpdateSubTree()

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;
Expand Down Expand Up @@ -150,8 +159,8 @@ private void updateClock()

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;
Expand Down Expand Up @@ -211,6 +220,8 @@ private bool updateReplay(ref double proposedTime)
/// <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.
Expand All @@ -234,9 +245,9 @@ private void applyFrameStability(ref double proposedTime)
}
}

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;

Expand All @@ -252,10 +263,6 @@ public void ProcessFrame() { }

public FrameTimeInfo TimeInfo => framedClock.TimeInfo;

#endregion

#region Delegation of IGameplayClock

public double TrueGameplayRate
{
get
Expand All @@ -280,6 +287,13 @@ public double TrueGameplayRate

#endregion

#region Delegation of IFrameStableClock

IBindable<bool> IFrameStableClock.IsCatchingUp => isCatchingUp;
IBindable<bool> IFrameStableClock.WaitingOnFrames => waitingOnFrames;

#endregion

private enum PlaybackState
{
/// <summary>
Expand All @@ -298,8 +312,5 @@ private enum PlaybackState
/// </summary>
Valid
}

IBindable<bool> IFrameStableClock.IsCatchingUp => IsCatchingUp;
IBindable<bool> IFrameStableClock.WaitingOnFrames => WaitingOnFrames;
}
}