Skip to content

Commit 567d8bd

Browse files
docs: Condense interpolation render time comments and changelog
Comment and changelog wording only, no behavioral or test logic changes. Trims the explanation in UpdateInterpolation from twenty one lines to six and drops the measurement anecdote and the unfilled Jira placeholder, keeping the reason the server clock is the correct one to measure from. Shortens the test's remarks and constant comments to match the density of the surrounding tests. The removed detail, the measurements behind the fix, and the metrics that were tried and rejected while building the test are recorded outside the repository.
1 parent 36bade2 commit 567d8bd

2 files changed

Lines changed: 16 additions & 45 deletions

File tree

com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4259,26 +4259,12 @@ internal BufferedLinearInterpolatorQuaternion GetRotationInterpolator()
42594259
// Non-Authority
42604260
private void UpdateInterpolation()
42614261
{
4262-
// Use the server time, because that is the clock the measurements being interpolated between are
4263-
// stamped on: a state's SentTime is derived from its NetworkTick, which is a server tick.
4264-
//
4265-
// Deriving the render time from LocalTime instead mixes two clocks. LocalTime leads ServerTime by
4266-
// roughly the tick latency, so subtracting the tick latency from it lands the render time back at
4267-
// (approximately) ServerTime rather than behind it. "Approximately" is the problem: the lead is
4268-
// fractional while the subtraction is a whole number of ticks, and a state's SentTime is floored to
4269-
// a tick boundary on top of that. The render time therefore ends up at or slightly ahead of the
4270-
// newest state that can exist, leaving the interpolator with nothing to interpolate towards. A
4271-
// measured session had the render time ahead of ServerTime on 100% of frames, with the interpolator
4272-
// never holding more than one measurement.
4273-
//
4274-
// Measuring from ServerTime instead makes the offset the whole tick latency rather than whatever is
4275-
// left of it, which is self correcting: as the round trip time grows, NetworkTimeSystem.TickLatency
4276-
// grows and the render time moves further back with it.
4277-
//
4278-
// Note this is a no-op on a host or server, where LocalTime and ServerTime are the same.
4279-
// TODO-JIRA-TICKET:
4280-
// Confirm the distributed authority case. Authority instances interpolate nothing, so this should
4281-
// not reach them, but ServerTime's meaning under a CMB service session should be verified.
4262+
// Use the server time, since that is the clock the states being interpolated between are stamped on
4263+
// (a state's SentTime is derived from its NetworkTick). Deriving the render time from LocalTime
4264+
// subtracts the tick latency from a clock that already leads ServerTime by roughly that much, which
4265+
// leaves the render time at or ahead of the newest state that can exist and starves the interpolator.
4266+
// Measuring from ServerTime is also self correcting, as the tick latency grows with the round trip
4267+
// time. This is a no-op on a host or server, where both clocks are the same.
42824268
var timeSystem = m_CachedNetworkManager.ServerTime;
42834269
var currentTime = timeSystem.Time;
42844270
#if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D

com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformInterpolationRenderTimeTests.cs

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,20 @@ namespace Unity.Netcode.RuntimeTests
1313
/// clock that the state updates it is interpolating between are stamped on.
1414
/// </summary>
1515
/// <remarks>
16-
/// A <see cref="NetworkTransform"/> state's SentTime is derived from its NetworkTick, which is a server
17-
/// tick, so the render time has to be measured from ServerTime. Measuring it from LocalTime mixes two
18-
/// clocks: LocalTime leads ServerTime, so subtracting the tick latency from LocalTime lands the render time
19-
/// back at approximately ServerTime rather than a whole tick latency behind it. The interpolator is then
20-
/// asked to render a point in time at (or ahead of) the newest state that can possibly exist, so it has
21-
/// nothing left to interpolate towards.
22-
///
23-
/// What this test measures is how far behind ServerTime the state currently being interpolated towards was
24-
/// sent. Because the target is selected against the render time, this has to be at least the tick latency:
25-
/// the render time is ServerTime minus the tick latency, and only states sent at or before the render time
26-
/// are eligible. Deriving the render time from LocalTime instead eats into that margin by however far the
27-
/// two clocks are apart, and can push the target past ServerTime entirely (a negative value below, meaning
28-
/// the interpolator is chasing a state that the server clock says has not happened yet).
16+
/// Measures how far behind ServerTime the state being interpolated towards was sent. The render time is
17+
/// ServerTime minus the tick latency and only states sent at or before it are eligible, so that measurement
18+
/// can never be less than the tick latency. Deriving the render time from LocalTime eats into that margin by
19+
/// however far the two clocks are apart, and can push the target past ServerTime entirely.
2920
/// </remarks>
3021
[TestFixture(HostOrServer.Host, NetworkTransform.InterpolationTypes.Lerp)]
3122
[TestFixture(HostOrServer.Host, NetworkTransform.InterpolationTypes.SmoothDampening)]
3223
internal class NetworkTransformInterpolationRenderTimeTests : IntegrationTestWithApproximation
3324
{
3425
protected override int NumberOfClients => 1;
3526

36-
// How far LocalTime is pushed ahead of ServerTime, in ticks. An in-process integration test has
37-
// effectively no round trip time and the separation between the two clocks is
38-
// (half RTT + LocalBufferSec + ServerBufferSec), so without widening the local buffer the two clocks
39-
// sit close enough together that which one is used barely shows. This is deliberately large enough to
40-
// exceed NetworkTimeSystem's hard reset threshold (0.2s) so the offset snaps rather than converging at
41-
// the default adjustment ratio of 0.01s per second, which would take over ten seconds.
27+
// How far LocalTime is pushed ahead of ServerTime, in ticks. An in-process test has no round trip time
28+
// to separate the two clocks, and this is large enough to exceed NetworkTimeSystem's hard reset
29+
// threshold so the offset snaps instead of converging at its default adjustment ratio.
4230
private const int k_LocalBufferTicks = 12;
4331

4432
// The separation the clocks must actually reach before any measurement is taken.
@@ -47,11 +35,10 @@ internal class NetworkTransformInterpolationRenderTimeTests : IntegrationTestWit
4735
// Ticks of authority motion after the clocks have separated, so the interpolator reaches steady state.
4836
private const int k_WarmUpTicks = 20;
4937

50-
// The number of rendered frames sampled once the warm up has completed.
5138
private const int k_SampledFrames = 90;
5239

53-
// The distance the authority moves each tick. Large enough that every tick produces a state update
54-
// rather than being filtered out by the position threshold.
40+
// Far enough each tick that every tick produces a state update rather than being filtered out by the
41+
// position threshold.
5542
private const float k_DistancePerTick = 1.37f;
5643

5744
private readonly NetworkTransform.InterpolationTypes m_InterpolationType;
@@ -223,9 +210,7 @@ public IEnumerator RenderTimeTrailsTheServerClock()
223210
var meanBuffered = totalBuffered[instance] / (float)samples[instance];
224211
var tickLatency = networkManager.NetworkTimeSystem.TickLatency;
225212

226-
// Only states sent at or before the render time are eligible to be interpolated towards, and the
227-
// render time is the server clock minus the tick latency, so the target can never be newer than
228-
// that. Anything less means the render time was taken from a clock that runs ahead of the one
213+
// Anything less than the tick latency means the render time came from a clock that leads the one
229214
// the states are stamped on.
230215
Assert.GreaterOrEqual(meanTargetLagTicks, tickLatency,
231216
$"[{m_InterpolationType}] {instance.name} was interpolating towards a state sent " +

0 commit comments

Comments
 (0)