Skip to content

Commit

Permalink
perf: remove allocations during syncvar sync (#946)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulpach authored and miwarnec committed Jul 1, 2019
1 parent 3ee87fe commit d2381ce
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 21 deletions.
28 changes: 11 additions & 17 deletions Assets/Mirror/Runtime/NetworkIdentity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -573,28 +573,21 @@ bool OnSerializeSafely(NetworkBehaviour comp, NetworkWriter writer, bool initial
return result;
}

// OnSerializeAllSafely is in hot path. caching the writer is really
// worth it to avoid large amounts of allocations.
static NetworkWriter onSerializeWriter = new NetworkWriter();

// serialize all components (or only dirty ones if not initial state)
// -> returns serialized data of everything dirty, null if nothing was dirty
internal byte[] OnSerializeAllSafely(bool initialState)
// -> returns true if something was written
internal bool OnSerializeAllSafely(bool initialState, NetworkWriter writer)
{
// reset cached writer length and position
onSerializeWriter.SetLength(0);

if (networkBehavioursCache.Length > 64)
{
Debug.LogError("Only 64 NetworkBehaviour components are allowed for NetworkIdentity: " + name + " because of the dirtyComponentMask");
return null;
return false;
}
ulong dirtyComponentsMask = GetDirtyMask(networkBehavioursCache, initialState);

if (dirtyComponentsMask == 0L)
return null;
return false;

onSerializeWriter.WritePackedUInt64(dirtyComponentsMask); // WritePacked64 so we don't write full 8 bytes if we don't have to
writer.WritePackedUInt64(dirtyComponentsMask); // WritePacked64 so we don't write full 8 bytes if we don't have to

foreach (NetworkBehaviour comp in networkBehavioursCache)
{
Expand All @@ -605,7 +598,7 @@ internal byte[] OnSerializeAllSafely(bool initialState)
{
// serialize the data
if (LogFilter.Debug) Debug.Log("OnSerializeAllSafely: " + name + " -> " + comp.GetType() + " initial=" + initialState);
OnSerializeSafely(comp, onSerializeWriter, initialState);
OnSerializeSafely(comp, writer, initialState);

// Clear dirty bits only if we are synchronizing data and not sending a spawn message.
// This preserves the behavior in HLAPI
Expand All @@ -616,7 +609,7 @@ internal byte[] OnSerializeAllSafely(bool initialState)
}
}

return onSerializeWriter.ToArray();
return true;
}

ulong GetDirtyMask(NetworkBehaviour[] components, bool initialState)
Expand Down Expand Up @@ -1037,17 +1030,18 @@ internal void MirrorUpdate()
if (observers == null || observers.Count == 0)
return;

NetworkWriter writer = NetworkWriterPool.GetWriter();
// serialize all the dirty components and send (if any were dirty)
byte[] payload = OnSerializeAllSafely(false);
if (payload != null)
if (OnSerializeAllSafely(false, writer))
{
// populate cached UpdateVarsMessage and send
varsMessage.netId = netId;
// segment to avoid reader allocations.
// (never null because of our above check)
varsMessage.payload = new ArraySegment<byte>(payload);
varsMessage.payload = writer.ToArraySegment();
NetworkServer.SendToReady(this, varsMessage);
}
NetworkWriterPool.Recycle(writer);
}
}
}
17 changes: 13 additions & 4 deletions Assets/Mirror/Runtime/NetworkServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -836,13 +836,20 @@ internal static void SendSpawnMessage(NetworkIdentity identity, NetworkConnectio

if (LogFilter.Debug) Debug.Log("Server SendSpawnMessage: name=" + identity.name + " sceneId=" + identity.sceneId.ToString("X") + " netid=" + identity.netId); // for easier debugging

// serialize all components with initialState = true
// (can be null if has none)
byte[] serialized = identity.OnSerializeAllSafely(true);
NetworkWriter writer = NetworkWriterPool.GetWriter();



// convert to ArraySegment to avoid reader allocations
// (need to handle null case too)
ArraySegment<byte> segment = serialized != null ? new ArraySegment<byte>(serialized) : default;
ArraySegment<byte> segment = default;

// serialize all components with initialState = true
// (can be null if has none)
if (identity.OnSerializeAllSafely(true, writer))
{
segment = writer.ToArraySegment();
}

// 'identity' is a prefab that should be spawned
if (identity.sceneId == 0)
Expand Down Expand Up @@ -896,6 +903,8 @@ internal static void SendSpawnMessage(NetworkIdentity identity, NetworkConnectio
SendToReady(identity, msg);
}
}

NetworkWriterPool.Recycle(writer);
}

public static void DestroyPlayerForConnection(NetworkConnection conn)
Expand Down

0 comments on commit d2381ce

Please sign in to comment.