From dd4d344542a372d570e5e2838dee2e009976db54 Mon Sep 17 00:00:00 2001 From: Coburn Date: Sun, 3 Jan 2021 22:52:34 +1000 Subject: [PATCH] Revert "perf: NetworkWriter/Reader Write/ReadBlittable for 4-6x performance improvement! (#2441)" (#2526) This reverts commit 1947f061add00b9a8b02a8778f5bd9bc2b7cd158 and applies some patches to bring it back up to date without the DOTSNET feature. --- Assets/Mirror/Runtime/Mirror.asmdef | 2 +- Assets/Mirror/Runtime/NetworkReader.cs | 186 +++++++----- Assets/Mirror/Runtime/NetworkWriter.cs | 280 +++++++++++------- Assets/Mirror/Runtime/UNetwork.cs | 35 +++ .../Mirror/Tests/Editor/Mirror.Tests.asmdef | 2 +- .../Mirror/Tests/Editor/NetworkWriterTest.cs | 39 --- 6 files changed, 314 insertions(+), 230 deletions(-) diff --git a/Assets/Mirror/Runtime/Mirror.asmdef b/Assets/Mirror/Runtime/Mirror.asmdef index 0f38055ee..1dedc5a26 100644 --- a/Assets/Mirror/Runtime/Mirror.asmdef +++ b/Assets/Mirror/Runtime/Mirror.asmdef @@ -8,7 +8,7 @@ "optionalUnityReferences": [], "includePlatforms": [], "excludePlatforms": [], - "allowUnsafeCode": true, + "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, diff --git a/Assets/Mirror/Runtime/NetworkReader.cs b/Assets/Mirror/Runtime/NetworkReader.cs index fa7d62a5b..4f4794bf1 100644 --- a/Assets/Mirror/Runtime/NetworkReader.cs +++ b/Assets/Mirror/Runtime/NetworkReader.cs @@ -1,8 +1,15 @@ +// Custom NetworkReader that doesn't use C#'s built in MemoryStream in order to +// avoid allocations. +// +// Benchmark: 100kb byte[] passed to NetworkReader constructor 1000x +// before with MemoryStream +// 0.8% CPU time, 250KB memory, 3.82ms +// now: +// 0.0% CPU time, 32KB memory, 0.02ms using System; using System.Collections.Generic; using System.IO; using System.Text; -using Unity.Collections.LowLevel.Unsafe; using UnityEngine; namespace Mirror @@ -68,59 +75,13 @@ public NetworkReader(ArraySegment segment) buffer = segment; } - // ReadBlittable from DOTSNET - // Benchmark: see NetworkWriter.WriteBlittable! - /// - /// Read blittable type from buffer - /// - /// this is extremely fast, but only works for blittable types. - /// - /// - /// Note: - /// ReadBlittable assumes same endianness for server and client. - /// All Unity 2018+ platforms are little endian. - /// - /// - /// - /// See Blittable and Non-Blittable Types - /// for more info. - /// - /// Needs to be unmanaged, see unmanaged types - /// - public unsafe T ReadBlittable() - where T : unmanaged + public byte ReadByte() { - // check if blittable for safety -#if UNITY_EDITOR - if (!UnsafeUtility.IsBlittable(typeof(T))) + if (Position + 1 > buffer.Count) { - throw new ArgumentException(typeof(T) + " is not blittable!"); + throw new EndOfStreamException("ReadByte out of range:" + ToString()); } -#endif - - // calculate size - // sizeof(T) gets the managed size at compile time. - // Marshal.SizeOf gets the unmanaged size at runtime (slow). - // => our 1mio writes benchmark is 6x slower with Marshal.SizeOf - // => for blittable types, sizeof(T) is even recommended: - // https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices - int size = sizeof(T); - - // enough data to read? - if (Position + size > buffer.Count) - { - throw new EndOfStreamException($"ReadBlittable<{typeof(T)}> out of range: {ToString()}"); - } - - // read blittable - T value; - fixed (byte* ptr = &buffer.Array[buffer.Offset + Position]) - { - // cast buffer to a T* pointer and then read from it. - value = *(T*)ptr; - } - Position += size; - return value; + return buffer.Array[buffer.Offset + Position++]; } /// @@ -183,12 +144,9 @@ public T Read() } } - /// - /// Built in Reader functions for Mirror - /// - /// Weaver automatically detects all extension methods for NetworkWriter - /// - /// + + // Mirror's Weaver automatically detects all NetworkReader function types, + // but they do all need to be extensions. public static class NetworkReaderExtensions { static readonly ILogger logger = LogFactory.GetLogger(typeof(NetworkReaderExtensions)); @@ -198,19 +156,61 @@ public static class NetworkReaderExtensions // 1000 readers after: 0.8MB GC, 18ms static readonly UTF8Encoding encoding = new UTF8Encoding(false, true); - public static byte ReadByte(this NetworkReader reader) => reader.ReadBlittable(); - public static sbyte ReadSByte(this NetworkReader reader) => reader.ReadBlittable(); - public static char ReadChar(this NetworkReader reader) => (char)reader.ReadBlittable(); // char isn't blittable - public static bool ReadBoolean(this NetworkReader reader) => reader.ReadBlittable() != 0; // bool isn't blittable - public static short ReadInt16(this NetworkReader reader) => reader.ReadBlittable(); - public static ushort ReadUInt16(this NetworkReader reader) => reader.ReadBlittable(); - public static int ReadInt32(this NetworkReader reader) => reader.ReadBlittable(); - public static uint ReadUInt32(this NetworkReader reader) => reader.ReadBlittable(); - public static long ReadInt64(this NetworkReader reader) => reader.ReadBlittable(); - public static ulong ReadUInt64(this NetworkReader reader) => reader.ReadBlittable(); - public static float ReadSingle(this NetworkReader reader) => reader.ReadBlittable(); - public static double ReadDouble(this NetworkReader reader) => reader.ReadBlittable(); - public static decimal ReadDecimal(this NetworkReader reader) => reader.ReadBlittable(); + public static byte ReadByte(this NetworkReader reader) => reader.ReadByte(); + public static sbyte ReadSByte(this NetworkReader reader) => (sbyte)reader.ReadByte(); + public static char ReadChar(this NetworkReader reader) => (char)reader.ReadUInt16(); + public static bool ReadBoolean(this NetworkReader reader) => reader.ReadByte() != 0; + public static short ReadInt16(this NetworkReader reader) => (short)reader.ReadUInt16(); + public static ushort ReadUInt16(this NetworkReader reader) + { + ushort value = 0; + value |= reader.ReadByte(); + value |= (ushort)(reader.ReadByte() << 8); + return value; + } + public static int ReadInt32(this NetworkReader reader) => (int)reader.ReadUInt32(); + public static uint ReadUInt32(this NetworkReader reader) + { + uint value = 0; + value |= reader.ReadByte(); + value |= (uint)(reader.ReadByte() << 8); + value |= (uint)(reader.ReadByte() << 16); + value |= (uint)(reader.ReadByte() << 24); + return value; + } + public static long ReadInt64(this NetworkReader reader) => (long)reader.ReadUInt64(); + public static ulong ReadUInt64(this NetworkReader reader) + { + ulong value = 0; + value |= reader.ReadByte(); + value |= ((ulong)reader.ReadByte()) << 8; + value |= ((ulong)reader.ReadByte()) << 16; + value |= ((ulong)reader.ReadByte()) << 24; + value |= ((ulong)reader.ReadByte()) << 32; + value |= ((ulong)reader.ReadByte()) << 40; + value |= ((ulong)reader.ReadByte()) << 48; + value |= ((ulong)reader.ReadByte()) << 56; + return value; + } + public static float ReadSingle(this NetworkReader reader) + { + UIntFloat converter = new UIntFloat(); + converter.intValue = reader.ReadUInt32(); + return converter.floatValue; + } + public static double ReadDouble(this NetworkReader reader) + { + UIntDouble converter = new UIntDouble(); + converter.longValue = reader.ReadUInt64(); + return converter.doubleValue; + } + public static decimal ReadDecimal(this NetworkReader reader) + { + UIntDecimal converter = new UIntDecimal(); + converter.longValue1 = reader.ReadUInt64(); + converter.longValue2 = reader.ReadUInt64(); + return converter.decimalValue; + } /// if an invalid utf8 string is sent public static string ReadString(this NetworkReader reader) @@ -256,18 +256,40 @@ public static ArraySegment ReadBytesAndSizeSegment(this NetworkReader read return count == 0 ? default : reader.ReadBytesSegment(checked((int)(count - 1u))); } - public static Vector2 ReadVector2(this NetworkReader reader) => reader.ReadBlittable(); - public static Vector3 ReadVector3(this NetworkReader reader) => reader.ReadBlittable(); - public static Vector4 ReadVector4(this NetworkReader reader) => reader.ReadBlittable(); - public static Vector2Int ReadVector2Int(this NetworkReader reader) => reader.ReadBlittable(); - public static Vector3Int ReadVector3Int(this NetworkReader reader) => reader.ReadBlittable(); - public static Color ReadColor(this NetworkReader reader) => reader.ReadBlittable(); - public static Color32 ReadColor32(this NetworkReader reader) => reader.ReadBlittable(); - public static Quaternion ReadQuaternion(this NetworkReader reader) => reader.ReadBlittable(); - public static Rect ReadRect(this NetworkReader reader) => reader.ReadBlittable(); - public static Plane ReadPlane(this NetworkReader reader) => reader.ReadBlittable(); - public static Ray ReadRay(this NetworkReader reader) => reader.ReadBlittable(); - public static Matrix4x4 ReadMatrix4x4(this NetworkReader reader) => reader.ReadBlittable(); + public static Vector2 ReadVector2(this NetworkReader reader) => new Vector2(reader.ReadSingle(), reader.ReadSingle()); + public static Vector3 ReadVector3(this NetworkReader reader) => new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); + public static Vector4 ReadVector4(this NetworkReader reader) => new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); + public static Vector2Int ReadVector2Int(this NetworkReader reader) => new Vector2Int(reader.ReadInt32(), reader.ReadInt32()); + public static Vector3Int ReadVector3Int(this NetworkReader reader) => new Vector3Int(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32()); + public static Color ReadColor(this NetworkReader reader) => new Color(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); + public static Color32 ReadColor32(this NetworkReader reader) => new Color32(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); + public static Quaternion ReadQuaternion(this NetworkReader reader) => new Quaternion(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); + public static Rect ReadRect(this NetworkReader reader) => new Rect(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); + public static Plane ReadPlane(this NetworkReader reader) => new Plane(reader.ReadVector3(), reader.ReadSingle()); + public static Ray ReadRay(this NetworkReader reader) => new Ray(reader.ReadVector3(), reader.ReadVector3()); + + public static Matrix4x4 ReadMatrix4x4(this NetworkReader reader) + { + return new Matrix4x4 + { + m00 = reader.ReadSingle(), + m01 = reader.ReadSingle(), + m02 = reader.ReadSingle(), + m03 = reader.ReadSingle(), + m10 = reader.ReadSingle(), + m11 = reader.ReadSingle(), + m12 = reader.ReadSingle(), + m13 = reader.ReadSingle(), + m20 = reader.ReadSingle(), + m21 = reader.ReadSingle(), + m22 = reader.ReadSingle(), + m23 = reader.ReadSingle(), + m30 = reader.ReadSingle(), + m31 = reader.ReadSingle(), + m32 = reader.ReadSingle(), + m33 = reader.ReadSingle() + }; + } public static byte[] ReadBytes(this NetworkReader reader, int count) { diff --git a/Assets/Mirror/Runtime/NetworkWriter.cs b/Assets/Mirror/Runtime/NetworkWriter.cs index 8674e17ac..6125866bd 100644 --- a/Assets/Mirror/Runtime/NetworkWriter.cs +++ b/Assets/Mirror/Runtime/NetworkWriter.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; -using Unity.Collections.LowLevel.Unsafe; using UnityEngine; namespace Mirror @@ -139,85 +138,14 @@ public ArraySegment ToArraySegment() return new ArraySegment(buffer, 0, length); } - // WriteBlittable from DOTSNET. - // Benchmark: - // WriteQuaternion x 100k, Macbook Pro 2015 @ 2.2Ghz, Unity 2018 LTS (debug mode) - // - // | Median | Min | Max | Avg | Std | (ms) - // before | 30.35 | 29.86 | 48.99 | 32.54 | 4.93 | - // blittable* | 5.69 | 5.52 | 27.51 | 7.78 | 5.65 | - // - // * without IsBlittable check - // => 4-6x faster! - // - // WriteQuaternion x 100k, Macbook Pro 2015 @ 2.2Ghz, Unity 2020.1 (release mode) - // - // | Median | Min | Max | Avg | Std | (ms) - // before | 9.41 | 8.90 | 23.02 | 10.72 | 3.07 | - // blittable* | 1.48 | 1.40 | 16.03 | 2.60 | 2.71 | - // - // * without IsBlittable check - // => 6x faster! - /// - /// Copies blittable type to buffer - /// - /// This is extremely fast, but only works for blittable types. - /// - /// - /// Note: - /// WriteBlittable assumes same endianness for server and client. - /// All Unity 2018+ platforms are little endian.
- /// => run NetworkWriterTests.BlittableOnThisPlatform() to verify! - ///
- ///
- /// - /// See Blittable and Non-Blittable Types - /// for more info. - /// - /// Needs to be unmanaged, see unmanaged types - /// - public unsafe void WriteBlittable(T value) - where T : unmanaged + public void WriteByte(byte value) { - // check if blittable for safety -#if UNITY_EDITOR - if (!UnsafeUtility.IsBlittable(typeof(T))) - { - Debug.LogError(typeof(T) + " is not blittable!"); - return; - } -#endif - // calculate size - // sizeof(T) gets the managed size at compile time. - // Marshal.SizeOf gets the unmanaged size at runtime (slow). - // => our 1mio writes benchmark is 6x slower with Marshal.SizeOf - // => for blittable types, sizeof(T) is even recommended: - // https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices - int size = sizeof(T); - - // ensure length - EnsureLength(position + size); - - // write blittable - fixed (byte* ptr = &buffer[Position]) - { - // cast buffer to T* pointer, then assign value to the area - *(T*)ptr = value; - } - Position += size; + EnsureLength(position + 1); + buffer[position++] = value; } - - /// - /// Copys bytes from given array to - /// - /// useful for byte arrays with consistent size, where the reader knows how many to read - /// (like a packet opcode that's always the same) - /// - /// - /// - /// - /// + // for byte arrays with consistent size, where the reader knows how many to read + // (like a packet opcode that's always the same) public void WriteBytes(byte[] buffer, int offset, int count) { EnsureLength(position + count); @@ -245,12 +173,8 @@ public void Write(T value) } - /// - /// Built in Writer functions for Mirror - /// - /// Weaver automatically decects all extension methods for NetworkWriter - /// - /// + // Mirror's Weaver automatically detects all NetworkWriter function types, + // but they do all need to be extensions. public static class NetworkWriterExtensions { static readonly ILogger logger = LogFactory.GetLogger(typeof(NetworkWriterExtensions)); @@ -261,19 +185,76 @@ public static class NetworkWriterExtensions static readonly UTF8Encoding encoding = new UTF8Encoding(false, true); static readonly byte[] stringBuffer = new byte[NetworkWriter.MaxStringLength]; - public static void WriteByte(this NetworkWriter writer, byte value) => writer.WriteBlittable(value); - public static void WriteSByte(this NetworkWriter writer, sbyte value) => writer.WriteBlittable(value); - public static void WriteChar(this NetworkWriter writer, char value) => writer.WriteBlittable((short)value); // char isn't blittable - public static void WriteBoolean(this NetworkWriter writer, bool value) => writer.WriteBlittable((byte)(value ? 1 : 0)); // bool isn't blittable - public static void WriteUInt16(this NetworkWriter writer, ushort value) => writer.WriteBlittable(value); + public static void WriteByte(this NetworkWriter writer, byte value) => writer.WriteByte(value); + + public static void WriteSByte(this NetworkWriter writer, sbyte value) => writer.WriteByte((byte)value); + + public static void WriteChar(this NetworkWriter writer, char value) => writer.WriteUInt16(value); + + public static void WriteBoolean(this NetworkWriter writer, bool value) => writer.WriteByte((byte)(value ? 1 : 0)); + + public static void WriteUInt16(this NetworkWriter writer, ushort value) + { + writer.WriteByte((byte)value); + writer.WriteByte((byte)(value >> 8)); + } + public static void WriteInt16(this NetworkWriter writer, short value) => writer.WriteUInt16((ushort)value); - public static void WriteUInt32(this NetworkWriter writer, uint value) => writer.WriteBlittable(value); - public static void WriteInt32(this NetworkWriter writer, int value) => writer.WriteBlittable(value); - public static void WriteUInt64(this NetworkWriter writer, ulong value) => writer.WriteBlittable(value); - public static void WriteInt64(this NetworkWriter writer, long value) => writer.WriteBlittable(value); - public static void WriteSingle(this NetworkWriter writer, float value) => writer.WriteBlittable(value); - public static void WriteDouble(this NetworkWriter writer, double value) => writer.WriteBlittable(value); - public static void WriteDecimal(this NetworkWriter writer, decimal value) => writer.WriteBlittable(value); + + public static void WriteUInt32(this NetworkWriter writer, uint value) + { + writer.WriteByte((byte)value); + writer.WriteByte((byte)(value >> 8)); + writer.WriteByte((byte)(value >> 16)); + writer.WriteByte((byte)(value >> 24)); + } + + public static void WriteInt32(this NetworkWriter writer, int value) => writer.WriteUInt32((uint)value); + + public static void WriteUInt64(this NetworkWriter writer, ulong value) + { + writer.WriteByte((byte)value); + writer.WriteByte((byte)(value >> 8)); + writer.WriteByte((byte)(value >> 16)); + writer.WriteByte((byte)(value >> 24)); + writer.WriteByte((byte)(value >> 32)); + writer.WriteByte((byte)(value >> 40)); + writer.WriteByte((byte)(value >> 48)); + writer.WriteByte((byte)(value >> 56)); + } + + public static void WriteInt64(this NetworkWriter writer, long value) => writer.WriteUInt64((ulong)value); + + public static void WriteSingle(this NetworkWriter writer, float value) + { + UIntFloat converter = new UIntFloat + { + floatValue = value + }; + writer.WriteUInt32(converter.intValue); + } + + public static void WriteDouble(this NetworkWriter writer, double value) + { + UIntDouble converter = new UIntDouble + { + doubleValue = value + }; + writer.WriteUInt64(converter.longValue); + } + + public static void WriteDecimal(this NetworkWriter writer, decimal value) + { + // the only way to read it without allocations is to both read and + // write it with the FloatConverter (which is not binary compatible + // to writer.Write(decimal), hence why we use it here too) + UIntDecimal converter = new UIntDecimal + { + decimalValue = value + }; + writer.WriteUInt64(converter.longValue1); + writer.WriteUInt64(converter.longValue2); + } public static void WriteString(this NetworkWriter writer, string value) { @@ -331,18 +312,103 @@ public static void WriteBytesAndSizeSegment(this NetworkWriter writer, ArraySegm writer.WriteBytesAndSize(buffer.Array, buffer.Offset, buffer.Count); } - public static void WriteVector2(this NetworkWriter writer, Vector2 value) => writer.WriteBlittable(value); - public static void WriteVector3(this NetworkWriter writer, Vector3 value) => writer.WriteBlittable(value); - public static void WriteVector4(this NetworkWriter writer, Vector4 value) => writer.WriteBlittable(value); - public static void WriteVector2Int(this NetworkWriter writer, Vector2Int value) => writer.WriteBlittable(value); - public static void WriteVector3Int(this NetworkWriter writer, Vector3Int value) => writer.WriteBlittable(value); - public static void WriteColor(this NetworkWriter writer, Color value) => writer.WriteBlittable(value); - public static void WriteColor32(this NetworkWriter writer, Color32 value) => writer.WriteBlittable(value); - public static void WriteQuaternion(this NetworkWriter writer, Quaternion value) => writer.WriteBlittable(value); - public static void WriteRect(this NetworkWriter writer, Rect value) => writer.WriteBlittable(value); - public static void WritePlane(this NetworkWriter writer, Plane value) => writer.WriteBlittable(value); - public static void WriteRay(this NetworkWriter writer, Ray value) => writer.WriteBlittable(value); - public static void WriteMatrix4x4(this NetworkWriter writer, Matrix4x4 value) => writer.WriteBlittable(value); + public static void WriteVector2(this NetworkWriter writer, Vector2 value) + { + writer.WriteSingle(value.x); + writer.WriteSingle(value.y); + } + + public static void WriteVector3(this NetworkWriter writer, Vector3 value) + { + writer.WriteSingle(value.x); + writer.WriteSingle(value.y); + writer.WriteSingle(value.z); + } + + public static void WriteVector4(this NetworkWriter writer, Vector4 value) + { + writer.WriteSingle(value.x); + writer.WriteSingle(value.y); + writer.WriteSingle(value.z); + writer.WriteSingle(value.w); + } + + public static void WriteVector2Int(this NetworkWriter writer, Vector2Int value) + { + writer.WriteInt32(value.x); + writer.WriteInt32(value.y); + } + + public static void WriteVector3Int(this NetworkWriter writer, Vector3Int value) + { + writer.WriteInt32(value.x); + writer.WriteInt32(value.y); + writer.WriteInt32(value.z); + } + + public static void WriteColor(this NetworkWriter writer, Color value) + { + writer.WriteSingle(value.r); + writer.WriteSingle(value.g); + writer.WriteSingle(value.b); + writer.WriteSingle(value.a); + } + + public static void WriteColor32(this NetworkWriter writer, Color32 value) + { + writer.WriteByte(value.r); + writer.WriteByte(value.g); + writer.WriteByte(value.b); + writer.WriteByte(value.a); + } + + public static void WriteQuaternion(this NetworkWriter writer, Quaternion value) + { + writer.WriteSingle(value.x); + writer.WriteSingle(value.y); + writer.WriteSingle(value.z); + writer.WriteSingle(value.w); + } + + public static void WriteRect(this NetworkWriter writer, Rect value) + { + writer.WriteSingle(value.xMin); + writer.WriteSingle(value.yMin); + writer.WriteSingle(value.width); + writer.WriteSingle(value.height); + } + + public static void WritePlane(this NetworkWriter writer, Plane value) + { + writer.WriteVector3(value.normal); + writer.WriteSingle(value.distance); + } + + public static void WriteRay(this NetworkWriter writer, Ray value) + { + writer.WriteVector3(value.origin); + writer.WriteVector3(value.direction); + } + + public static void WriteMatrix4x4(this NetworkWriter writer, Matrix4x4 value) + { + writer.WriteSingle(value.m00); + writer.WriteSingle(value.m01); + writer.WriteSingle(value.m02); + writer.WriteSingle(value.m03); + writer.WriteSingle(value.m10); + writer.WriteSingle(value.m11); + writer.WriteSingle(value.m12); + writer.WriteSingle(value.m13); + writer.WriteSingle(value.m20); + writer.WriteSingle(value.m21); + writer.WriteSingle(value.m22); + writer.WriteSingle(value.m23); + writer.WriteSingle(value.m30); + writer.WriteSingle(value.m31); + writer.WriteSingle(value.m32); + writer.WriteSingle(value.m33); + } public static void WriteGuid(this NetworkWriter writer, Guid value) { diff --git a/Assets/Mirror/Runtime/UNetwork.cs b/Assets/Mirror/Runtime/UNetwork.cs index adb8d458e..282f32c80 100644 --- a/Assets/Mirror/Runtime/UNetwork.cs +++ b/Assets/Mirror/Runtime/UNetwork.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using UnityEngine; namespace Mirror @@ -31,4 +32,38 @@ public static class Channels public const int DefaultReliable = 0; public const int DefaultUnreliable = 1; } + + // -- helpers for float conversion without allocations -- + [StructLayout(LayoutKind.Explicit)] + internal struct UIntFloat + { + [FieldOffset(0)] + public float floatValue; + + [FieldOffset(0)] + public uint intValue; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct UIntDouble + { + [FieldOffset(0)] + public double doubleValue; + + [FieldOffset(0)] + public ulong longValue; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct UIntDecimal + { + [FieldOffset(0)] + public ulong longValue1; + + [FieldOffset(8)] + public ulong longValue2; + + [FieldOffset(0)] + public decimal decimalValue; + } } diff --git a/Assets/Mirror/Tests/Editor/Mirror.Tests.asmdef b/Assets/Mirror/Tests/Editor/Mirror.Tests.asmdef index afeebc634..56cf7f2dd 100644 --- a/Assets/Mirror/Tests/Editor/Mirror.Tests.asmdef +++ b/Assets/Mirror/Tests/Editor/Mirror.Tests.asmdef @@ -15,7 +15,7 @@ "Editor" ], "excludePlatforms": [], - "allowUnsafeCode": true, + "allowUnsafeCode": false, "overrideReferences": true, "precompiledReferences": [ "NSubstitute.dll", diff --git a/Assets/Mirror/Tests/Editor/NetworkWriterTest.cs b/Assets/Mirror/Tests/Editor/NetworkWriterTest.cs index 944874f97..677181849 100644 --- a/Assets/Mirror/Tests/Editor/NetworkWriterTest.cs +++ b/Assets/Mirror/Tests/Editor/NetworkWriterTest.cs @@ -26,45 +26,6 @@ public void Benchmark() } */ - struct TestStruct - { -#pragma warning disable 649 - public int data; - public byte data2; -#pragma warning restore 649 - } - - [Test] - public unsafe void BlittableOnThisPlatform() - { - // we assume NetworkWriter.WriteBlittable to behave the same on - // all platforms: - // - need to be little endian (atm all Unity platforms are) - // - padded structs need to be same size across all platforms - // (C# int, byte, etc. should be same on all platforms, and - // C# should do the same padding on all platforms) - // https://kalapos.net/Blog/ShowPost/DotNetConceptOfTheWeek13_DotNetMemoryLayout - // => let's have a test that we can run on different platforms to - // be 100% sure - - // let's assume little endian. - // it would also be ok if server and client are both big endian, - // but that's extremely unlikely. - Assert.That(BitConverter.IsLittleEndian, Is.True); - - // TestStruct biggest member is 'int' = 4 bytes. - // so C# aligns it to 4 bytes, hence does padding for the byte: - // 0 int - // 1 int - // 2 int - // 3 int - // 4 byte - // 5 padding - // 6 padding - // 7 padding - Assert.That(sizeof(TestStruct), Is.EqualTo(8)); - } - [Test] public void TestWritingSmallMessage() {