Skip to content

Commit c2d0a5b

Browse files
committed
Seal InterpolatingFramedClock
I forgot that this was the whole point of removing the obsoleted methods in ppy#6528.
1 parent 508827c commit c2d0a5b

File tree

2 files changed

+32
-35
lines changed

2 files changed

+32
-35
lines changed

osu.Framework.Tests/Clocks/InterpolatingFramedClockTest.cs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ namespace osu.Framework.Tests.Clocks
1313
public class InterpolatingFramedClockTest
1414
{
1515
private TestClock source = null!;
16-
private TestInterpolatingFramedClock interpolating = null!;
16+
private InterpolatingFramedClock interpolating = null!;
1717

1818
[SetUp]
1919
public void SetUp()
2020
{
2121
source = new TestClock();
2222

23-
interpolating = new TestInterpolatingFramedClock();
23+
interpolating = new InterpolatingFramedClock();
2424
interpolating.ChangeSource(source);
2525
}
2626

@@ -147,7 +147,7 @@ public void NeverInterpolatesBackwardsOnInterpolationFail()
147147
interpolatedCount++;
148148

149149
Assert.GreaterOrEqual(interpolating.CurrentTime, lastValue, "Interpolating should not jump against rate.");
150-
Assert.LessOrEqual(Math.Abs(interpolating.CurrentTime - source.CurrentTime), interpolating.AllowableErrorMilliseconds, "Interpolating should be within allowance.");
150+
Assert.LessOrEqual(Math.Abs(interpolating.CurrentTime - source.CurrentTime), interpolating.AllowableErrorMilliseconds * source.Rate, "Interpolating should be within allowance.");
151151

152152
Thread.Sleep(sleep_time);
153153
lastValue = interpolating.CurrentTime;
@@ -179,7 +179,7 @@ public void CanSeekForwardsOnInterpolationFail()
179179

180180
if (skipSourceForwards) // seek forward once at a random point.
181181
{
182-
source.CurrentTime += interpolating.AllowableErrorMilliseconds * 10;
182+
source.CurrentTime += interpolating.AllowableErrorMilliseconds * 10 * source.Rate;
183183
interpolating.ProcessFrame();
184184
Assert.That(interpolating.IsInterpolating, Is.False);
185185
Assert.That(interpolating.CurrentTime, Is.EqualTo(source.CurrentTime));
@@ -194,7 +194,7 @@ public void CanSeekForwardsOnInterpolationFail()
194194
interpolatedCount++;
195195

196196
Assert.GreaterOrEqual(interpolating.CurrentTime, lastValue, "Interpolating should not jump against rate.");
197-
Assert.LessOrEqual(Math.Abs(interpolating.CurrentTime - source.CurrentTime), interpolating.AllowableErrorMilliseconds, "Interpolating should be within allowance.");
197+
Assert.LessOrEqual(Math.Abs(interpolating.CurrentTime - source.CurrentTime), interpolating.AllowableErrorMilliseconds * source.Rate, "Interpolating should be within allowance.");
198198

199199
Thread.Sleep(sleep_time);
200200
lastValue = interpolating.CurrentTime;
@@ -225,7 +225,7 @@ public void CanSeekBackwards()
225225
public void TestInterpolationAfterSourceStoppedThenSeeked()
226226
{
227227
// Just to make sure this works even when still in interpolation allowance.
228-
interpolating.CustomErrorMilliseconds = 100000;
228+
interpolating.AllowableErrorMilliseconds = 100000;
229229

230230
source.Start();
231231

@@ -277,12 +277,5 @@ public void InterpolationStaysWithinBounds()
277277
Assert.IsFalse(interpolating.IsRunning);
278278
Assert.That(source.CurrentTime, Is.EqualTo(interpolating.CurrentTime).Within(interpolating.AllowableErrorMilliseconds));
279279
}
280-
281-
public class TestInterpolatingFramedClock : InterpolatingFramedClock
282-
{
283-
public double? CustomErrorMilliseconds { get; set; }
284-
285-
public override double AllowableErrorMilliseconds => CustomErrorMilliseconds ?? base.AllowableErrorMilliseconds;
286-
}
287280
}
288281
}

osu.Framework/Timing/InterpolatingFramedClock.cs

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@ namespace osu.Framework.Timing
99
/// <summary>
1010
/// A clock which uses an internal stopwatch to interpolate (smooth out) a source.
1111
/// </summary>
12-
public class InterpolatingFramedClock : IFrameBasedClock, ISourceChangeableClock // TODO: seal when DecoupleableInterpolatingFramedClock is gone.
12+
public sealed class InterpolatingFramedClock : IFrameBasedClock, ISourceChangeableClock
1313
{
1414
/// <summary>
1515
/// The amount of error that is allowed between the source and interpolated time before the interpolated time is ignored and the source time is used.
16+
/// Defaults to two 60 fps frames (~33.3 ms).
1617
/// </summary>
17-
public virtual double AllowableErrorMilliseconds => 1000.0 / 60 * 2 * Rate;
18+
/// <remarks>
19+
/// This is internally adjusted for the current playback rate (so that the actual precision is constant regardless of the rate applied).
20+
/// </remarks>
21+
public double AllowableErrorMilliseconds { get; set; } = 1000.0 / 60 * 2;
1822

1923
/// <summary>
2024
/// Whether interpolation was applied at the last processed frame.
@@ -28,19 +32,19 @@ public class InterpolatingFramedClock : IFrameBasedClock, ISourceChangeableClock
2832
/// <summary>
2933
/// The drift in milliseconds between the source and interpolation at the last processed frame.
3034
/// </summary>
31-
public double Drift => CurrentTime - FramedSourceClock.CurrentTime;
35+
public double Drift => CurrentTime - framedSourceClock.CurrentTime;
3236

33-
public virtual double Rate => FramedSourceClock.Rate;
37+
public double Rate => framedSourceClock.Rate;
3438

35-
public virtual bool IsRunning { get; private set; }
39+
public bool IsRunning { get; private set; }
3640

37-
public virtual double ElapsedFrameTime { get; private set; }
41+
public double ElapsedFrameTime { get; private set; }
3842

3943
public IClock Source { get; private set; }
4044

41-
protected IFrameBasedClock FramedSourceClock;
45+
private IFrameBasedClock framedSourceClock;
4246

43-
public virtual double CurrentTime { get; private set; }
47+
public double CurrentTime { get; private set; }
4448

4549
private readonly FramedClock realtimeClock = new FramedClock(new StopwatchClock(true));
4650

@@ -50,10 +54,10 @@ public InterpolatingFramedClock(IFrameBasedClock? source = null)
5054
{
5155
ChangeSource(source);
5256
Debug.Assert(Source != null);
53-
Debug.Assert(FramedSourceClock != null);
57+
Debug.Assert(framedSourceClock != null);
5458
}
5559

56-
public virtual void ChangeSource(IClock? source)
60+
public void ChangeSource(IClock? source)
5761
{
5862
if (source != null && source == Source)
5963
return;
@@ -62,22 +66,22 @@ public virtual void ChangeSource(IClock? source)
6266

6367
// We need a frame-based source to correctly process interpolation.
6468
// If the provided source is not already a framed clock, encapsulate it in one.
65-
FramedSourceClock = Source as IFrameBasedClock ?? new FramedClock(source);
69+
framedSourceClock = Source as IFrameBasedClock ?? new FramedClock(source);
6670

6771
IsInterpolating = false;
68-
currentTime = FramedSourceClock.CurrentTime;
72+
currentTime = framedSourceClock.CurrentTime;
6973
}
7074

71-
public virtual void ProcessFrame()
75+
public void ProcessFrame()
7276
{
7377
double lastTime = currentTime;
7478

7579
realtimeClock.ProcessFrame();
76-
FramedSourceClock.ProcessFrame();
80+
framedSourceClock.ProcessFrame();
7781

78-
bool sourceIsRunning = FramedSourceClock.IsRunning;
82+
bool sourceIsRunning = framedSourceClock.IsRunning;
7983

80-
bool sourceHasElapsed = FramedSourceClock.ElapsedFrameTime != 0;
84+
bool sourceHasElapsed = framedSourceClock.ElapsedFrameTime != 0;
8185

8286
try
8387
{
@@ -88,7 +92,7 @@ public virtual void ProcessFrame()
8892
if (sourceHasElapsed)
8993
{
9094
IsInterpolating = false;
91-
currentTime = FramedSourceClock.CurrentTime;
95+
currentTime = framedSourceClock.CurrentTime;
9296
}
9397

9498
return;
@@ -100,20 +104,20 @@ public virtual void ProcessFrame()
100104
currentTime += realtimeClock.ElapsedFrameTime * Rate;
101105
// if we differ from the elapsed time of the source, let's adjust for the difference.
102106
// TODO: this is frame rate depending, and can result in unexpected results.
103-
currentTime += (FramedSourceClock.CurrentTime - currentTime) / 8;
107+
currentTime += (framedSourceClock.CurrentTime - currentTime) / 8;
104108

105-
bool withinAllowableError = Math.Abs(FramedSourceClock.CurrentTime - currentTime) <= AllowableErrorMilliseconds;
109+
bool withinAllowableError = Math.Abs(framedSourceClock.CurrentTime - currentTime) <= AllowableErrorMilliseconds * Rate;
106110

107111
if (!withinAllowableError)
108112
{
109113
// if we've exceeded the allowable error, we should use the source clock's time value.
110114
IsInterpolating = false;
111-
currentTime = FramedSourceClock.CurrentTime;
115+
currentTime = framedSourceClock.CurrentTime;
112116
}
113117
}
114118
else
115119
{
116-
currentTime = FramedSourceClock.CurrentTime;
120+
currentTime = framedSourceClock.CurrentTime;
117121

118122
// Of importance, only start interpolating from the next frame.
119123
// The first frame after a clock starts may give very incorrect results, ie. due to a seek in the frame before.
@@ -122,7 +126,7 @@ public virtual void ProcessFrame()
122126
}
123127

124128
// seeking backwards should only be allowed if the source is explicitly doing that.
125-
bool elapsedInOpposingDirection = FramedSourceClock.ElapsedFrameTime != 0 && Math.Sign(FramedSourceClock.ElapsedFrameTime) != Math.Sign(Rate);
129+
bool elapsedInOpposingDirection = framedSourceClock.ElapsedFrameTime != 0 && Math.Sign(framedSourceClock.ElapsedFrameTime) != Math.Sign(Rate);
126130
if (!elapsedInOpposingDirection)
127131
currentTime = Rate >= 0 ? Math.Max(lastTime, currentTime) : Math.Min(lastTime, currentTime);
128132
}

0 commit comments

Comments
 (0)