11using System ;
22using System . Collections . Generic ;
3+ using System . Runtime . CompilerServices ;
34using UnityEngine ;
45
56namespace Unity . Netcode
@@ -14,9 +15,16 @@ public abstract class BufferedLinearInterpolator<T> where T : struct
1415 // Constant absolute value for max buffer count instead of dynamic time based value. This is in case we have very low tick rates, so
1516 // that we don't have a very small buffer because of this.
1617 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 ;
1820 private const double k_SmallValue = 9.999999439624929E-11 ; // copied from Vector3's equal operator
1921
22+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
23+ private float GetPrecision ( )
24+ {
25+ return m_BufferQueue . Count == 0 ? k_ApproximateHighPrecision : k_ApproximateLowPrecision ;
26+ }
27+
2028 #region Legacy notes
2129 // Buffer consumption scenarios
2230 // Perfect case consumption
@@ -132,20 +140,23 @@ internal struct CurrentState
132140 public float CurrentDeltaTime => m_CurrentDeltaTime ;
133141 public double FinalTimeToTarget => Math . Max ( 0.0 , TimeToTargetValue - DeltaTime ) ;
134142
143+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
135144 public void AddDeltaTime ( float deltaTime )
136145 {
137146 m_CurrentDeltaTime = deltaTime ;
138147 DeltaTime = Math . Min ( DeltaTime + deltaTime , TimeToTargetValue ) ;
139148 LerpT = ( float ) ( TimeToTargetValue == 0.0 ? 1.0 : DeltaTime / TimeToTargetValue ) ;
140149 }
141150
151+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
142152 public void SetTimeToTarget ( double timeToTarget )
143153 {
144154 LerpT = 0.0f ;
145155 DeltaTime = 0.0f ;
146156 TimeToTargetValue = timeToTarget ;
147157 }
148158
159+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
149160 public bool TargetTimeAproximatelyReached ( )
150161 {
151162 if ( ! Target . HasValue )
@@ -188,6 +199,7 @@ public void Reset(T currentValue)
188199 /// The current buffered items received by the authority.
189200 /// </summary>
190201 protected internal readonly Queue < BufferedItem > m_BufferQueue = new Queue < BufferedItem > ( k_BufferCountLimit ) ;
202+ protected internal readonly List < BufferedItem > m_BufferList = new List < BufferedItem > ( k_BufferCountLimit ) ;
191203
192204 /// <summary>
193205 /// The current interpolation state
@@ -237,6 +249,7 @@ public void ResetTo(T targetValue, double serverTime)
237249 InternalReset ( targetValue , serverTime ) ;
238250 }
239251
252+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
240253 private void InternalReset ( T targetValue , double serverTime , bool addMeasurement = true )
241254 {
242255 m_RateOfChange = default ;
@@ -271,7 +284,7 @@ private void TryConsumeFromBuffer(double renderTime, double minDeltaTime, double
271284 {
272285 if ( ! InterpolateState . TargetReached )
273286 {
274- InterpolateState . TargetReached = IsAproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item ) ;
287+ InterpolateState . TargetReached = IsApproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item , GetPrecision ( ) ) ;
275288 }
276289 return ;
277290 }
@@ -291,7 +304,7 @@ private void TryConsumeFromBuffer(double renderTime, double minDeltaTime, double
291304 potentialItemNeedsProcessing = ( ( potentialItem . TimeSent <= renderTime ) && potentialItem . TimeSent > InterpolateState . Target . Value . TimeSent ) ;
292305 if ( ! InterpolateState . TargetReached )
293306 {
294- InterpolateState . TargetReached = IsAproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item ) ;
307+ InterpolateState . TargetReached = IsApproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item , GetPrecision ( ) ) ;
295308 }
296309 }
297310
@@ -424,23 +437,27 @@ internal T Update(float deltaTime, double tickLatencyAsTime, double minDeltaTime
424437 /// This version of TryConsumeFromBuffer adheres to the original BufferedLinearInterpolator buffer consumption pattern.
425438 /// </remarks>
426439 /// <param name="renderTime"></param>
440+ /// <param name="serverTime"></param>
427441 private void TryConsumeFromBuffer ( double renderTime , double serverTime )
428442 {
429443 if ( ! InterpolateState . Target . HasValue || ( InterpolateState . Target . Value . TimeSent <= renderTime ) )
430444 {
431445 BufferedItem ? previousItem = null ;
432446 var alreadyHasBufferItem = false ;
447+ var count = 0 ;
433448 while ( m_BufferQueue . TryPeek ( out BufferedItem potentialItem ) )
434449 {
435450 // If we are still on the same buffered item (FIFO Queue), then exit early as there is nothing
436- // to consume.
451+ // to consume. (just a safety check but this scenario should never happen based on the below legacy approach of
452+ // consuming until the most current state)
437453 if ( previousItem . HasValue && previousItem . Value . TimeSent == potentialItem . TimeSent )
438454 {
439455 break ;
440456 }
441457
442- if ( ( potentialItem . TimeSent <= serverTime ) &&
443- ( ! InterpolateState . Target . HasValue || InterpolateState . Target . Value . TimeSent < potentialItem . TimeSent ) )
458+ // Continue to processing until we reach the most current state
459+ if ( ( potentialItem . TimeSent <= serverTime ) && // Inverted logic (below) from original since we have to go from past to present
460+ ( ! InterpolateState . Target . HasValue || potentialItem . TimeSent > InterpolateState . Target . Value . TimeSent ) )
444461 {
445462 if ( m_BufferQueue . TryDequeue ( out BufferedItem target ) )
446463 {
@@ -449,6 +466,7 @@ private void TryConsumeFromBuffer(double renderTime, double serverTime)
449466 InterpolateState . Target = target ;
450467 alreadyHasBufferItem = true ;
451468 InterpolateState . NextValue = InterpolateState . CurrentValue ;
469+ InterpolateState . PreviousValue = InterpolateState . CurrentValue ;
452470 InterpolateState . StartTime = target . TimeSent ;
453471 InterpolateState . EndTime = target . TimeSent ;
454472 }
@@ -458,19 +476,15 @@ private void TryConsumeFromBuffer(double renderTime, double serverTime)
458476 {
459477 alreadyHasBufferItem = true ;
460478 InterpolateState . StartTime = InterpolateState . Target . Value . TimeSent ;
461- InterpolateState . NextValue = InterpolateState . CurrentValue ;
479+ InterpolateState . PreviousValue = InterpolateState . NextValue ;
462480 InterpolateState . TargetReached = false ;
463481 }
464482 InterpolateState . EndTime = target . TimeSent ;
465- InterpolateState . Target = target ;
466483 InterpolateState . TimeToTargetValue = InterpolateState . EndTime - InterpolateState . StartTime ;
484+ InterpolateState . Target = target ;
467485 }
468486 }
469487 }
470- else
471- {
472- break ;
473- }
474488
475489 if ( ! InterpolateState . Target . HasValue )
476490 {
@@ -505,19 +519,20 @@ public T Update(float deltaTime, double renderTime, double serverTime)
505519 InterpolateState . LerpT = Math . Clamp ( ( float ) ( ( renderTime - InterpolateState . StartTime ) / InterpolateState . TimeToTargetValue ) , 0.0f , 1.0f ) ;
506520 }
507521
508- var target = Interpolate ( InterpolateState . NextValue , InterpolateState . Target . Value . Item , InterpolateState . LerpT ) ;
522+ InterpolateState . NextValue = Interpolate ( InterpolateState . PreviousValue , InterpolateState . Target . Value . Item , InterpolateState . LerpT ) ;
509523
510524 if ( LerpSmoothEnabled )
511525 {
512526 // 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 ) ;
527+ InterpolateState . CurrentValue = Interpolate ( InterpolateState . CurrentValue , InterpolateState . NextValue , deltaTime / MaximumInterpolationTime ) ;
514528 }
515529 else
516530 {
517- InterpolateState . CurrentValue = target ;
531+ InterpolateState . CurrentValue = InterpolateState . NextValue ;
518532 }
533+
519534 // Determine if we have reached our target
520- InterpolateState . TargetReached = IsAproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item ) ;
535+ InterpolateState . TargetReached = IsApproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item , GetPrecision ( ) ) ;
521536 }
522537 else // If the target is reached and we have no more state updates, we want to check to see if we need to reset.
523538 if ( m_BufferQueue . Count == 0 && InterpolateState . TargetReached )
@@ -601,6 +616,7 @@ public void AddMeasurement(T newMeasurement, double sentTime)
601616 /// Gets latest value from the interpolator. This is updated every update as time goes by.
602617 /// </summary>
603618 /// <returns>The current interpolated value of type 'T'</returns>
619+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
604620 public T GetInterpolatedValue ( )
605621 {
606622 return InterpolateState . CurrentValue ;
@@ -638,6 +654,7 @@ public T GetInterpolatedValue()
638654 /// <param name="deltaTime">The increasing delta time from when start to finish.</param>
639655 /// <param name="maxSpeed">Maximum rate of change per pass.</param>
640656 /// <returns>The smoothed <see cref="T"/> value.</returns>
657+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
641658 private protected virtual T SmoothDamp ( T current , T target , ref T rateOfChange , float duration , float deltaTime , float maxSpeed = Mathf . Infinity )
642659 {
643660 return target ;
@@ -653,7 +670,8 @@ private protected virtual T SmoothDamp(T current, T target, ref T rateOfChange,
653670 /// <param name="second">Second value of type <see cref="T"/>.</param>
654671 /// <param name="precision">The precision of the aproximation.</param>
655672 /// <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 )
673+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
674+ private protected virtual bool IsApproximately ( T first , T second , float precision = k_ApproximateLowPrecision )
657675 {
658676 return false ;
659677 }
@@ -665,6 +683,7 @@ private protected virtual bool IsAproximately(T first, T second, float precision
665683 /// <param name="item">The item to convert.</param>
666684 /// <param name="inLocalSpace">local or world space (true or false).</param>
667685 /// <returns>The converted value.</returns>
686+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
668687 protected internal virtual T OnConvertTransformSpace ( Transform transform , T item , bool inLocalSpace )
669688 {
670689 return default ;
@@ -675,6 +694,7 @@ protected internal virtual T OnConvertTransformSpace(Transform transform, T item
675694 /// </summary>
676695 /// <param name="transform">The transform that the <see cref="Components.NetworkTransform"/> is associated with.</param>
677696 /// <param name="inLocalSpace">Whether the <see cref="Components.NetworkTransform"/> is now being tracked in local or world spaced.</param>
697+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
678698 internal void ConvertTransformSpace ( Transform transform , bool inLocalSpace )
679699 {
680700 var count = m_BufferQueue . Count ;
0 commit comments