Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: [Part 3] NetworkIdentity.Serialize now clears dirty bits while iterating. avoids O(N) ClearALlComponentsDirtyBits call after each Serialize call. #3267

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ void SendToServer()
double now = NetworkTime.localTime; // Unity 2019 doesn't have Time.timeAsDouble yet
if (now > nextSyncTime)
{
nextSyncTime = now + syncInterval;
nextSyncTime = now + NetworkClient.sendInterval;
CmdSendState(target.velocity, target.position);
}
}
Expand Down
2 changes: 1 addition & 1 deletion Assets/Mirror/Components/Experimental/NetworkRigidbody.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ void SendVelocity()
// only update syncTime if either has changed
if (angularVelocityChanged || velocityChanged)
{
previousValue.nextSyncTime = now + syncInterval;
previousValue.nextSyncTime = now + NetworkClient.sendInterval;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ void SendVelocity()
// only update syncTime if either has changed
if (angularVelocityChanged || velocityChanged)
{
previousValue.nextSyncTime = now + syncInterval;
previousValue.nextSyncTime = now + NetworkClient.sendInterval;
}
}

Expand Down
4 changes: 2 additions & 2 deletions Assets/Mirror/Components/NetworkAnimator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,9 @@ bool CheckAnimStateChanged(out int stateHash, out float normalizedTime, int laye
void CheckSendRate()
{
double now = NetworkTime.localTime;
if (SendMessagesAllowed && syncInterval >= 0 && now > nextSendTime)
if (SendMessagesAllowed && NetworkServer.sendInterval >= 0 && now > nextSendTime)
{
nextSendTime = now + syncInterval;
nextSendTime = now + NetworkServer.sendInterval;

using (NetworkWriterPooled writer = NetworkWriterPool.Get())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,6 @@ protected virtual void OnValidate()
{
// set target to self if none yet
if (target == null) target = transform;

// time snapshot interpolation happens globally.
// value (transform) happens in here.
// both always need to be on the same send interval.
// force the setting to '0' in OnValidate to make it obvious that we
// actually use NetworkServer.sendInterval.
syncInterval = 0;
}

// snapshot functions //////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,6 @@ protected virtual void OnValidate()
// set target to self if none yet
if (target == null) target = transform;

// time snapshot interpolation happens globally.
// value (transform) happens in here.
// both always need to be on the same send interval.
// force the setting to '0' in OnValidate to make it obvious that we
// actually use NetworkServer.sendInterval.
syncInterval = 0;

// obsolete clientAuthority compatibility:
// if it was used, then set the new SyncDirection automatically.
// if it wasn't used, then don't touch syncDirection.
Expand Down
26 changes: 17 additions & 9 deletions Assets/Mirror/Core/NetworkBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ public abstract class NetworkBehaviour : MonoBehaviour
/// <summary>sync interval for OnSerialize (in seconds)</summary>
// hidden because NetworkBehaviourInspector shows it only if has OnSerialize.
// [0,2] should be enough. anything >2s is too laggy anyway.
[Tooltip("Time in seconds until next change is synchronized to the client. '0' means send immediately if changed. '0.5' means only send changes every 500ms.\n(This is for state synchronization like SyncVars, SyncLists, OnSerialize. Not for Cmds, Rpcs, etc.)")]
// DEPRECATED 2022-11-11
[Obsolete("NetworkBehaviour.syncInterval is not used anymore. Please configure the NetworkManager's send/tickRate globally instead. This has two advantages: snapshot interpolation is easier (no overlapping intervals), and it allows for a noticeable performance improvements because OnSerialize doesn't need to check dirty components anymore.")]
[Range(0, 2)]
[HideInInspector] public float syncInterval = 0.1f;
internal double lastSyncTime;

/// <summary>True if this object is on the server and has been spawned.</summary>
// This is different from NetworkServer.active, which is true if the
Expand Down Expand Up @@ -150,18 +150,31 @@ protected void SetSyncVarHookGuard(ulong dirtyBit, bool value)
syncVarHookGuard &= ~dirtyBit;
}

// callback for both SyncObject and SyncVar dirty bit setters.
// called once it becomes dirty, not called again while already dirty.
// -> we only want to follow the .netIdentity memory indirection once
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void OnBecameDirty()
{
netIdentity.OnBecameDirty(this);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void SetSyncObjectDirtyBit(ulong dirtyBit)
{
bool clean = syncObjectDirtyBits == 0;
syncObjectDirtyBits |= dirtyBit;
if (clean) OnBecameDirty();
}

/// <summary>Set as dirty so that it's synced to clients again.</summary>
// these are masks, not bit numbers, ie. 110011b not '2' for 2nd bit.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetSyncVarDirtyBit(ulong dirtyBit)
{
bool clean = syncVarDirtyBits == 0;
syncVarDirtyBits |= dirtyBit;
if (clean) OnBecameDirty();
}

/// <summary>Set as dirty to trigger OnSerialize & send. Dirty bits are cleared after the send.</summary>
Expand All @@ -172,21 +185,16 @@ public void SetSyncVarDirtyBit(ulong dirtyBit)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetDirty() => SetSyncVarDirtyBit(ulong.MaxValue);

// true if syncInterval elapsed and any SyncVar or SyncObject is dirty
// true if any SyncVar or SyncObject is dirty.
// OR both bitmasks. != 0 if either was dirty.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsDirty() =>
// check bits first. this is basically free.
(syncVarDirtyBits | syncObjectDirtyBits) != 0UL &&
// only check time if bits were dirty. this is more expensive.
NetworkTime.localTime - lastSyncTime >= syncInterval;
public bool IsDirty() => (syncVarDirtyBits | syncObjectDirtyBits) != 0UL;

/// <summary>Clears all the dirty bits that were set by SetDirtyBits()</summary>
// automatically invoked when an update is sent for this object, but can
// be called manually as well.
public void ClearAllDirtyBits()
{
lastSyncTime = NetworkTime.localTime;
syncVarDirtyBits = 0L;
syncObjectDirtyBits = 0L;

Expand Down
5 changes: 1 addition & 4 deletions Assets/Mirror/Core/NetworkClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1467,7 +1467,7 @@ static void Broadcast()
// nothing to do in host mode. server already knows the state.
if (NetworkServer.active) return;

// send time snapshot every sendInterval.
// send time snapshot every sendInterval
BroadcastTimeSnapshot();

// for each entity that the client owns
Expand All @@ -1493,9 +1493,6 @@ static void Broadcast()
payload = writer.ToArraySegment()
};
Send(message);

// reset dirty bits so it's not resent next time.
identity.ClearDirtyComponentsDirtyBits();
}
}
}
Expand Down
Loading