Skip to content

Commit 3716e27

Browse files
perf: Jobify SendBatchedMessages in UnityTransport [MTT-4999] (#2304)
* Make ErrorUtilities usable from Burst * Jobify SendBatchedMessages
1 parent 83096c8 commit 3716e27

File tree

1 file changed

+83
-64
lines changed

1 file changed

+83
-64
lines changed

com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs

Lines changed: 83 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
using UnityEngine;
1111
using NetcodeNetworkEvent = Unity.Netcode.NetworkEvent;
1212
using TransportNetworkEvent = Unity.Networking.Transport.NetworkEvent;
13+
using Unity.Burst;
1314
using Unity.Collections.LowLevel.Unsafe;
1415
using Unity.Collections;
16+
using Unity.Jobs;
1517
using Unity.Networking.Transport;
1618
using Unity.Networking.Transport.Relay;
1719
using Unity.Networking.Transport.Utilities;
@@ -51,50 +53,48 @@ void CreateDriver(
5153
/// </summary>
5254
public static class ErrorUtilities
5355
{
54-
private const string k_NetworkSuccess = "Success";
55-
private const string k_NetworkIdMismatch = "NetworkId is invalid, likely caused by stale connection {0}.";
56-
private const string k_NetworkVersionMismatch = "NetworkVersion is invalid, likely caused by stale connection {0}.";
57-
private const string k_NetworkStateMismatch = "Sending data while connecting on connection {0} is not allowed.";
58-
private const string k_NetworkPacketOverflow = "Unable to allocate packet due to buffer overflow.";
59-
private const string k_NetworkSendQueueFull = "Currently unable to queue packet as there is too many in-flight packets. This could be because the send queue size ('Max Send Queue Size') is too small.";
60-
private const string k_NetworkHeaderInvalid = "Invalid Unity Transport Protocol header.";
61-
private const string k_NetworkDriverParallelForErr = "The parallel network driver needs to process a single unique connection per job, processing a single connection multiple times in a parallel for is not supported.";
62-
private const string k_NetworkSendHandleInvalid = "Invalid NetworkInterface Send Handle. Likely caused by pipeline send data corruption.";
63-
private const string k_NetworkArgumentMismatch = "Invalid NetworkEndpoint Arguments.";
56+
private static readonly FixedString128Bytes k_NetworkSuccess = "Success";
57+
private static readonly FixedString128Bytes k_NetworkIdMismatch = "Invalid connection ID {0}.";
58+
private static readonly FixedString128Bytes k_NetworkVersionMismatch = "Connection ID is invalid. Likely caused by sending on stale connection {0}.";
59+
private static readonly FixedString128Bytes k_NetworkStateMismatch = "Connection state is invalid. Likely caused by sending on connection {0} which is stale or still connecting.";
60+
private static readonly FixedString128Bytes k_NetworkPacketOverflow = "Packet is too large to be allocated by the transport.";
61+
private static readonly FixedString128Bytes k_NetworkSendQueueFull = "Unable to queue packet in the transport. Likely caused by send queue size ('Max Send Queue Size') being too small.";
6462

6563
/// <summary>
66-
/// Convert error code to human readable error message.
64+
/// Convert a UTP error code to human-readable error message.
6765
/// </summary>
68-
/// <param name="error">Status code of the error</param>
69-
/// <param name="connectionId">Subject connection ID of the error</param>
70-
/// <returns>Human readable error message.</returns>
66+
/// <param name="error">UTP error code.</param>
67+
/// <param name="connectionId">ID of the connection on which the error occurred.</param>
68+
/// <returns>Human-readable error message.</returns>
7169
public static string ErrorToString(Networking.Transport.Error.StatusCode error, ulong connectionId)
7270
{
73-
switch (error)
71+
return ErrorToString((int)error, connectionId);
72+
}
73+
74+
internal static string ErrorToString(int error, ulong connectionId)
75+
{
76+
return ErrorToFixedString(error, connectionId).ToString();
77+
}
78+
79+
internal static FixedString128Bytes ErrorToFixedString(int error, ulong connectionId)
80+
{
81+
switch ((Networking.Transport.Error.StatusCode)error)
7482
{
7583
case Networking.Transport.Error.StatusCode.Success:
7684
return k_NetworkSuccess;
7785
case Networking.Transport.Error.StatusCode.NetworkIdMismatch:
78-
return string.Format(k_NetworkIdMismatch, connectionId);
86+
return FixedString.Format(k_NetworkIdMismatch, connectionId);
7987
case Networking.Transport.Error.StatusCode.NetworkVersionMismatch:
80-
return string.Format(k_NetworkVersionMismatch, connectionId);
88+
return FixedString.Format(k_NetworkVersionMismatch, connectionId);
8189
case Networking.Transport.Error.StatusCode.NetworkStateMismatch:
82-
return string.Format(k_NetworkStateMismatch, connectionId);
90+
return FixedString.Format(k_NetworkStateMismatch, connectionId);
8391
case Networking.Transport.Error.StatusCode.NetworkPacketOverflow:
8492
return k_NetworkPacketOverflow;
8593
case Networking.Transport.Error.StatusCode.NetworkSendQueueFull:
8694
return k_NetworkSendQueueFull;
87-
case Networking.Transport.Error.StatusCode.NetworkHeaderInvalid:
88-
return k_NetworkHeaderInvalid;
89-
case Networking.Transport.Error.StatusCode.NetworkDriverParallelForErr:
90-
return k_NetworkDriverParallelForErr;
91-
case Networking.Transport.Error.StatusCode.NetworkSendHandleInvalid:
92-
return k_NetworkSendHandleInvalid;
93-
case Networking.Transport.Error.StatusCode.NetworkArgumentMismatch:
94-
return k_NetworkArgumentMismatch;
95+
default:
96+
return FixedString.Format("Unknown error code {0}.", error);
9597
}
96-
97-
return $"Unknown ErrorCode {Enum.GetName(typeof(Networking.Transport.Error.StatusCode), error)}";
9898
}
9999
}
100100

@@ -676,55 +676,74 @@ private bool StartRelayServer()
676676
}
677677
}
678678

679-
// Send as many batched messages from the queue as possible.
680-
private void SendBatchedMessages(SendTarget sendTarget, BatchedSendQueue queue)
679+
[BurstCompile]
680+
private struct SendBatchedMessagesJob : IJob
681681
{
682-
var clientId = sendTarget.ClientId;
683-
var connection = ParseClientId(clientId);
684-
var pipeline = sendTarget.NetworkPipeline;
682+
public NetworkDriver.Concurrent Driver;
683+
public SendTarget Target;
684+
public BatchedSendQueue Queue;
685+
public NetworkPipeline ReliablePipeline;
685686

686-
while (!queue.IsEmpty)
687+
public void Execute()
687688
{
688-
var result = m_Driver.BeginSend(pipeline, connection, out var writer);
689-
if (result != (int)Networking.Transport.Error.StatusCode.Success)
689+
var clientId = Target.ClientId;
690+
var connection = ParseClientId(clientId);
691+
var pipeline = Target.NetworkPipeline;
692+
693+
while (!Queue.IsEmpty)
690694
{
691-
Debug.LogError("Error sending the message: " +
692-
ErrorUtilities.ErrorToString((Networking.Transport.Error.StatusCode)result, clientId));
693-
return;
694-
}
695+
var result = Driver.BeginSend(pipeline, connection, out var writer);
696+
if (result != (int)Networking.Transport.Error.StatusCode.Success)
697+
{
698+
Debug.LogError($"Error sending message: {ErrorUtilities.ErrorToFixedString(result, clientId)}");
699+
return;
700+
}
695701

696-
// We don't attempt to send entire payloads over the reliable pipeline. Instead we
697-
// fragment it manually. This is safe and easy to do since the reliable pipeline
698-
// basically implements a stream, so as long as we separate the different messages
699-
// in the stream (the send queue does that automatically) we are sure they'll be
700-
// reassembled properly at the other end. This allows us to lift the limit of ~44KB
701-
// on reliable payloads (because of the reliable window size).
702-
var written = pipeline == m_ReliableSequencedPipeline ? queue.FillWriterWithBytes(ref writer) : queue.FillWriterWithMessages(ref writer);
702+
// We don't attempt to send entire payloads over the reliable pipeline. Instead we
703+
// fragment it manually. This is safe and easy to do since the reliable pipeline
704+
// basically implements a stream, so as long as we separate the different messages
705+
// in the stream (the send queue does that automatically) we are sure they'll be
706+
// reassembled properly at the other end. This allows us to lift the limit of ~44KB
707+
// on reliable payloads (because of the reliable window size).
708+
var written = pipeline == ReliablePipeline ? Queue.FillWriterWithBytes(ref writer) : Queue.FillWriterWithMessages(ref writer);
703709

704-
result = m_Driver.EndSend(writer);
705-
if (result == written)
706-
{
707-
// Batched message was sent successfully. Remove it from the queue.
708-
queue.Consume(written);
709-
}
710-
else
711-
{
712-
// Some error occured. If it's just the UTP queue being full, then don't log
713-
// anything since that's okay (the unsent message(s) are still in the queue
714-
// and we'll retry sending the later). Otherwise log the error and remove the
715-
// message from the queue (we don't want to resend it again since we'll likely
716-
// just get the same error again).
717-
if (result != (int)Networking.Transport.Error.StatusCode.NetworkSendQueueFull)
710+
result = Driver.EndSend(writer);
711+
if (result == written)
718712
{
719-
Debug.LogError("Error sending the message: " + ErrorUtilities.ErrorToString((Networking.Transport.Error.StatusCode)result, clientId));
720-
queue.Consume(written);
713+
// Batched message was sent successfully. Remove it from the queue.
714+
Queue.Consume(written);
721715
}
716+
else
717+
{
718+
// Some error occured. If it's just the UTP queue being full, then don't log
719+
// anything since that's okay (the unsent message(s) are still in the queue
720+
// and we'll retry sending them later). Otherwise log the error and remove the
721+
// message from the queue (we don't want to resend it again since we'll likely
722+
// just get the same error again).
723+
if (result != (int)Networking.Transport.Error.StatusCode.NetworkSendQueueFull)
724+
{
725+
Debug.LogError($"Error sending the message: {ErrorUtilities.ErrorToFixedString(result, clientId)}");
726+
Queue.Consume(written);
727+
}
722728

723-
return;
729+
return;
730+
}
724731
}
725732
}
726733
}
727734

735+
// Send as many batched messages from the queue as possible.
736+
private void SendBatchedMessages(SendTarget sendTarget, BatchedSendQueue queue)
737+
{
738+
new SendBatchedMessagesJob
739+
{
740+
Driver = m_Driver.ToConcurrent(),
741+
Target = sendTarget,
742+
Queue = queue,
743+
ReliablePipeline = m_ReliableSequencedPipeline
744+
}.Run();
745+
}
746+
728747
private bool AcceptConnection()
729748
{
730749
var connection = m_Driver.Accept();

0 commit comments

Comments
 (0)