1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 17:13:06 +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> /// </summary>
[Cached(typeof(IGameplayClock))] [Cached(typeof(IGameplayClock))]
[Cached(typeof(IFrameStableClock))] [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> /// <summary>
/// The number of frames (per parent frame) which can be run in an attempt to catch-up to real-time. /// 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> /// <summary>
/// Whether to enable frame-stable playback. /// Whether to enable frame-stable playback.
/// </summary> /// </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) public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
{ {
@ -51,22 +86,6 @@ namespace osu.Game.Rulesets.UI
this.gameplayStartTime = gameplayStartTime; 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] [BackgroundDependencyLoader]
private void load(IGameplayClock? gameplayClock) private void load(IGameplayClock? gameplayClock)
{ {
@ -80,16 +99,6 @@ namespace osu.Game.Rulesets.UI
Clock = this; 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() public override bool UpdateSubTree()
{ {
int loops = MaxCatchUpFrames; int loops = MaxCatchUpFrames;
@ -112,7 +121,7 @@ namespace osu.Game.Rulesets.UI
private void updateClock() private void updateClock()
{ {
if (WaitingOnFrames.Value) if (waitingOnFrames.Value)
{ {
// if waiting on frames, run one update loop to determine if frames have arrived. // if waiting on frames, run one update loop to determine if frames have arrived.
state = PlaybackState.Valid; state = PlaybackState.Valid;
@ -150,8 +159,8 @@ namespace osu.Game.Rulesets.UI
double timeBehind = Math.Abs(proposedTime - referenceClock.CurrentTime); double timeBehind = Math.Abs(proposedTime - referenceClock.CurrentTime);
IsCatchingUp.Value = timeBehind > 200; isCatchingUp.Value = timeBehind > 200;
WaitingOnFrames.Value = state == PlaybackState.NotValid; waitingOnFrames.Value = state == PlaybackState.NotValid;
manualClock.CurrentTime = proposedTime; manualClock.CurrentTime = proposedTime;
manualClock.Rate = Math.Abs(referenceClock.Rate) * direction; 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> /// <param name="proposedTime">The time which is to be displayed.</param>
private void applyFrameStability(ref double proposedTime) private void applyFrameStability(ref double proposedTime)
{ {
const double sixty_frame_time = 1000.0 / 60;
if (firstConsumption) if (firstConsumption)
{ {
// On the first update, frame-stability seeking would result in unexpected/unwanted behaviour. // 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; public double CurrentTime => framedClock.CurrentTime;
@ -252,10 +263,6 @@ namespace osu.Game.Rulesets.UI
public FrameTimeInfo TimeInfo => framedClock.TimeInfo; public FrameTimeInfo TimeInfo => framedClock.TimeInfo;
#endregion
#region Delegation of IGameplayClock
public double TrueGameplayRate public double TrueGameplayRate
{ {
get get
@ -280,6 +287,13 @@ namespace osu.Game.Rulesets.UI
#endregion #endregion
#region Delegation of IFrameStableClock
IBindable<bool> IFrameStableClock.IsCatchingUp => isCatchingUp;
IBindable<bool> IFrameStableClock.WaitingOnFrames => waitingOnFrames;
#endregion
private enum PlaybackState private enum PlaybackState
{ {
/// <summary> /// <summary>
@ -298,8 +312,5 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
Valid Valid
} }
IBindable<bool> IFrameStableClock.IsCatchingUp => IsCatchingUp;
IBindable<bool> IFrameStableClock.WaitingOnFrames => WaitingOnFrames;
} }
} }