1
1
using System ;
2
2
using System . Collections . Generic ;
3
+ using System . Runtime . CompilerServices ;
3
4
using UnityEngine ;
4
5
5
6
namespace Unity . Netcode
@@ -14,9 +15,16 @@ public abstract class BufferedLinearInterpolator<T> where T : struct
14
15
// Constant absolute value for max buffer count instead of dynamic time based value. This is in case we have very low tick rates, so
15
16
// that we don't have a very small buffer because of this.
16
17
private const int k_BufferCountLimit = 100 ;
17
- private const float k_AproximatePrecision = 0.0001f ;
18
+ private const float k_ApproximateLowPrecision = 0.000001f ;
19
+ private const float k_ApproximateHighPrecision = 1E-10f ;
18
20
private const double k_SmallValue = 9.999999439624929E-11 ; // copied from Vector3's equal operator
19
21
22
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
23
+ private float GetPrecision ( )
24
+ {
25
+ return m_BufferQueue . Count == 0 ? k_ApproximateHighPrecision : k_ApproximateLowPrecision ;
26
+ }
27
+
20
28
#region Legacy notes
21
29
// Buffer consumption scenarios
22
30
// Perfect case consumption
@@ -132,20 +140,23 @@ internal struct CurrentState
132
140
public float CurrentDeltaTime => m_CurrentDeltaTime ;
133
141
public double FinalTimeToTarget => Math . Max ( 0.0 , TimeToTargetValue - DeltaTime ) ;
134
142
143
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
135
144
public void AddDeltaTime ( float deltaTime )
136
145
{
137
146
m_CurrentDeltaTime = deltaTime ;
138
147
DeltaTime = Math . Min ( DeltaTime + deltaTime , TimeToTargetValue ) ;
139
148
LerpT = ( float ) ( TimeToTargetValue == 0.0 ? 1.0 : DeltaTime / TimeToTargetValue ) ;
140
149
}
141
150
151
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
142
152
public void SetTimeToTarget ( double timeToTarget )
143
153
{
144
154
LerpT = 0.0f ;
145
155
DeltaTime = 0.0f ;
146
156
TimeToTargetValue = timeToTarget ;
147
157
}
148
158
159
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
149
160
public bool TargetTimeAproximatelyReached ( )
150
161
{
151
162
if ( ! Target . HasValue )
@@ -237,6 +248,7 @@ public void ResetTo(T targetValue, double serverTime)
237
248
InternalReset ( targetValue , serverTime ) ;
238
249
}
239
250
251
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
240
252
private void InternalReset ( T targetValue , double serverTime , bool addMeasurement = true )
241
253
{
242
254
m_RateOfChange = default ;
@@ -271,7 +283,7 @@ private void TryConsumeFromBuffer(double renderTime, double minDeltaTime, double
271
283
{
272
284
if ( ! InterpolateState . TargetReached )
273
285
{
274
- InterpolateState . TargetReached = IsAproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item ) ;
286
+ InterpolateState . TargetReached = IsApproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item , GetPrecision ( ) ) ;
275
287
}
276
288
return ;
277
289
}
@@ -291,7 +303,7 @@ private void TryConsumeFromBuffer(double renderTime, double minDeltaTime, double
291
303
potentialItemNeedsProcessing = ( ( potentialItem . TimeSent <= renderTime ) && potentialItem . TimeSent > InterpolateState . Target . Value . TimeSent ) ;
292
304
if ( ! InterpolateState . TargetReached )
293
305
{
294
- InterpolateState . TargetReached = IsAproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item ) ;
306
+ InterpolateState . TargetReached = IsApproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item , GetPrecision ( ) ) ;
295
307
}
296
308
}
297
309
@@ -424,6 +436,7 @@ internal T Update(float deltaTime, double tickLatencyAsTime, double minDeltaTime
424
436
/// This version of TryConsumeFromBuffer adheres to the original BufferedLinearInterpolator buffer consumption pattern.
425
437
/// </remarks>
426
438
/// <param name="renderTime"></param>
439
+ /// <param name="serverTime"></param>
427
440
private void TryConsumeFromBuffer ( double renderTime , double serverTime )
428
441
{
429
442
if ( ! InterpolateState . Target . HasValue || ( InterpolateState . Target . Value . TimeSent <= renderTime ) )
@@ -433,14 +446,16 @@ private void TryConsumeFromBuffer(double renderTime, double serverTime)
433
446
while ( m_BufferQueue . TryPeek ( out BufferedItem potentialItem ) )
434
447
{
435
448
// If we are still on the same buffered item (FIFO Queue), then exit early as there is nothing
436
- // to consume.
449
+ // to consume. (just a safety check but this scenario should never happen based on the below legacy approach of
450
+ // consuming until the most current state)
437
451
if ( previousItem . HasValue && previousItem . Value . TimeSent == potentialItem . TimeSent )
438
452
{
439
453
break ;
440
454
}
441
455
442
- if ( ( potentialItem . TimeSent <= serverTime ) &&
443
- ( ! InterpolateState . Target . HasValue || InterpolateState . Target . Value . TimeSent < potentialItem . TimeSent ) )
456
+ // Continue to processing until we reach the most current state
457
+ if ( ( potentialItem . TimeSent <= serverTime ) && // Inverted logic (below) from original since we have to go from past to present
458
+ ( ! InterpolateState . Target . HasValue || potentialItem . TimeSent > InterpolateState . Target . Value . TimeSent ) )
444
459
{
445
460
if ( m_BufferQueue . TryDequeue ( out BufferedItem target ) )
446
461
{
@@ -449,6 +464,7 @@ private void TryConsumeFromBuffer(double renderTime, double serverTime)
449
464
InterpolateState . Target = target ;
450
465
alreadyHasBufferItem = true ;
451
466
InterpolateState . NextValue = InterpolateState . CurrentValue ;
467
+ InterpolateState . PreviousValue = InterpolateState . CurrentValue ;
452
468
InterpolateState . StartTime = target . TimeSent ;
453
469
InterpolateState . EndTime = target . TimeSent ;
454
470
}
@@ -458,19 +474,15 @@ private void TryConsumeFromBuffer(double renderTime, double serverTime)
458
474
{
459
475
alreadyHasBufferItem = true ;
460
476
InterpolateState . StartTime = InterpolateState . Target . Value . TimeSent ;
461
- InterpolateState . NextValue = InterpolateState . CurrentValue ;
477
+ InterpolateState . PreviousValue = InterpolateState . NextValue ;
462
478
InterpolateState . TargetReached = false ;
463
479
}
464
480
InterpolateState . EndTime = target . TimeSent ;
465
- InterpolateState . Target = target ;
466
481
InterpolateState . TimeToTargetValue = InterpolateState . EndTime - InterpolateState . StartTime ;
482
+ InterpolateState . Target = target ;
467
483
}
468
484
}
469
485
}
470
- else
471
- {
472
- break ;
473
- }
474
486
475
487
if ( ! InterpolateState . Target . HasValue )
476
488
{
@@ -505,19 +517,20 @@ public T Update(float deltaTime, double renderTime, double serverTime)
505
517
InterpolateState . LerpT = Math . Clamp ( ( float ) ( ( renderTime - InterpolateState . StartTime ) / InterpolateState . TimeToTargetValue ) , 0.0f , 1.0f ) ;
506
518
}
507
519
508
- var target = Interpolate ( InterpolateState . NextValue , InterpolateState . Target . Value . Item , InterpolateState . LerpT ) ;
520
+ InterpolateState . NextValue = Interpolate ( InterpolateState . PreviousValue , InterpolateState . Target . Value . Item , InterpolateState . LerpT ) ;
509
521
510
522
if ( LerpSmoothEnabled )
511
523
{
512
524
// Assure our MaximumInterpolationTime is valid and that the second lerp time ranges between deltaTime and 1.0f.
513
- InterpolateState . CurrentValue = Interpolate ( InterpolateState . CurrentValue , target , deltaTime / MaximumInterpolationTime ) ;
525
+ InterpolateState . CurrentValue = Interpolate ( InterpolateState . CurrentValue , InterpolateState . NextValue , deltaTime / MaximumInterpolationTime ) ;
514
526
}
515
527
else
516
528
{
517
- InterpolateState . CurrentValue = target ;
529
+ InterpolateState . CurrentValue = InterpolateState . NextValue ;
518
530
}
531
+
519
532
// Determine if we have reached our target
520
- InterpolateState . TargetReached = IsAproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item ) ;
533
+ InterpolateState . TargetReached = IsApproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item , GetPrecision ( ) ) ;
521
534
}
522
535
else // If the target is reached and we have no more state updates, we want to check to see if we need to reset.
523
536
if ( m_BufferQueue . Count == 0 && InterpolateState . TargetReached )
@@ -601,6 +614,7 @@ public void AddMeasurement(T newMeasurement, double sentTime)
601
614
/// Gets latest value from the interpolator. This is updated every update as time goes by.
602
615
/// </summary>
603
616
/// <returns>The current interpolated value of type 'T'</returns>
617
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
604
618
public T GetInterpolatedValue ( )
605
619
{
606
620
return InterpolateState . CurrentValue ;
@@ -638,6 +652,7 @@ public T GetInterpolatedValue()
638
652
/// <param name="deltaTime">The increasing delta time from when start to finish.</param>
639
653
/// <param name="maxSpeed">Maximum rate of change per pass.</param>
640
654
/// <returns>The smoothed <see cref="T"/> value.</returns>
655
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
641
656
private protected virtual T SmoothDamp ( T current , T target , ref T rateOfChange , float duration , float deltaTime , float maxSpeed = Mathf . Infinity )
642
657
{
643
658
return target ;
@@ -653,7 +668,8 @@ private protected virtual T SmoothDamp(T current, T target, ref T rateOfChange,
653
668
/// <param name="second">Second value of type <see cref="T"/>.</param>
654
669
/// <param name="precision">The precision of the aproximation.</param>
655
670
/// <returns>true if the two values are aproximately the same and false if they are not</returns>
656
- private protected virtual bool IsAproximately ( T first , T second , float precision = k_AproximatePrecision )
671
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
672
+ private protected virtual bool IsApproximately ( T first , T second , float precision = k_ApproximateLowPrecision )
657
673
{
658
674
return false ;
659
675
}
@@ -665,6 +681,7 @@ private protected virtual bool IsAproximately(T first, T second, float precision
665
681
/// <param name="item">The item to convert.</param>
666
682
/// <param name="inLocalSpace">local or world space (true or false).</param>
667
683
/// <returns>The converted value.</returns>
684
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
668
685
protected internal virtual T OnConvertTransformSpace ( Transform transform , T item , bool inLocalSpace )
669
686
{
670
687
return default ;
@@ -675,6 +692,7 @@ protected internal virtual T OnConvertTransformSpace(Transform transform, T item
675
692
/// </summary>
676
693
/// <param name="transform">The transform that the <see cref="Components.NetworkTransform"/> is associated with.</param>
677
694
/// <param name="inLocalSpace">Whether the <see cref="Components.NetworkTransform"/> is now being tracked in local or world spaced.</param>
695
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
678
696
internal void ConvertTransformSpace ( Transform transform , bool inLocalSpace )
679
697
{
680
698
var count = m_BufferQueue . Count ;
0 commit comments