From e1a7ac03c6bee68f168f05682d48dadfa4e64ef3 Mon Sep 17 00:00:00 2001 From: RealityProgrammer Date: Mon, 6 Mar 2023 06:49:07 +0700 Subject: [PATCH] Added SpanExtensions and the reinterpret between Unity's structs to System.Numerics structs --- .../src/Numerics/QuaternionExtensions.cs | 5 +- X10D.Unity/src/Numerics/Vector2Extensions.cs | 5 +- X10D.Unity/src/Numerics/Vector3Extensions.cs | 5 +- X10D.Unity/src/Numerics/Vector4Extensions.cs | 6 +- X10D/src/Collections/Int32Extensions.cs | 3 +- X10D/src/Core/SpanExtensions.cs | 89 +++++++++++++++++++ 6 files changed, 104 insertions(+), 9 deletions(-) create mode 100644 X10D/src/Core/SpanExtensions.cs diff --git a/X10D.Unity/src/Numerics/QuaternionExtensions.cs b/X10D.Unity/src/Numerics/QuaternionExtensions.cs index 2c6355e..5511bbc 100644 --- a/X10D.Unity/src/Numerics/QuaternionExtensions.cs +++ b/X10D.Unity/src/Numerics/QuaternionExtensions.cs @@ -1,5 +1,6 @@ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; namespace X10D.Unity.Numerics; @@ -18,7 +19,7 @@ public static class QuaternionExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static System.Numerics.Quaternion ToSystemQuaternion(this Quaternion quaternion) { - return new System.Numerics.Quaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w); + return UnsafeUtility.As(ref quaternion); } /// @@ -30,6 +31,6 @@ public static class QuaternionExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Quaternion ToUnityQuaternion(this System.Numerics.Quaternion quaternion) { - return new Quaternion(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W); + return UnsafeUtility.As(ref quaternion); } } diff --git a/X10D.Unity/src/Numerics/Vector2Extensions.cs b/X10D.Unity/src/Numerics/Vector2Extensions.cs index 32ff372..3ab9045 100644 --- a/X10D.Unity/src/Numerics/Vector2Extensions.cs +++ b/X10D.Unity/src/Numerics/Vector2Extensions.cs @@ -1,5 +1,6 @@ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; namespace X10D.Unity.Numerics; @@ -18,7 +19,7 @@ public static class Vector2Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static System.Numerics.Vector2 ToSystemVector(this Vector2 vector) { - return new System.Numerics.Vector2(vector.x, vector.y); + return UnsafeUtility.As(ref vector); } /// @@ -30,7 +31,7 @@ public static class Vector2Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 ToUnityVector(this System.Numerics.Vector2 vector) { - return new Vector2(vector.X, vector.Y); + return UnsafeUtility.As(ref vector); } /// diff --git a/X10D.Unity/src/Numerics/Vector3Extensions.cs b/X10D.Unity/src/Numerics/Vector3Extensions.cs index 1b725ce..913001a 100644 --- a/X10D.Unity/src/Numerics/Vector3Extensions.cs +++ b/X10D.Unity/src/Numerics/Vector3Extensions.cs @@ -1,5 +1,6 @@ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; namespace X10D.Unity.Numerics; @@ -18,7 +19,7 @@ public static class Vector3Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static System.Numerics.Vector3 ToSystemVector(this Vector3 vector) { - return new System.Numerics.Vector3(vector.x, vector.y, vector.z); + return UnsafeUtility.As(ref vector); } /// @@ -30,7 +31,7 @@ public static class Vector3Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 ToUnityVector(this System.Numerics.Vector3 vector) { - return new Vector3(vector.X, vector.Y, vector.Z); + return UnsafeUtility.As(ref vector); } /// diff --git a/X10D.Unity/src/Numerics/Vector4Extensions.cs b/X10D.Unity/src/Numerics/Vector4Extensions.cs index ca2587f..c1538dc 100644 --- a/X10D.Unity/src/Numerics/Vector4Extensions.cs +++ b/X10D.Unity/src/Numerics/Vector4Extensions.cs @@ -1,5 +1,6 @@ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; namespace X10D.Unity.Numerics; @@ -18,7 +19,8 @@ public static class Vector4Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static System.Numerics.Vector4 ToSystemVector(this Vector4 vector) { - return new System.Numerics.Vector4(vector.x, vector.y, vector.z, vector.w); + return UnsafeUtility.As(ref vector); + } /// @@ -30,7 +32,7 @@ public static class Vector4Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 ToUnityVector(this System.Numerics.Vector4 vector) { - return new Vector4(vector.X, vector.Y, vector.Z, vector.W); + return UnsafeUtility.As(ref vector); } /// diff --git a/X10D/src/Collections/Int32Extensions.cs b/X10D/src/Collections/Int32Extensions.cs index 6fd81b3..7f5b468 100644 --- a/X10D/src/Collections/Int32Extensions.cs +++ b/X10D/src/Collections/Int32Extensions.cs @@ -80,7 +80,7 @@ public static class Int32Extensions var shuffle = Avx2.Shuffle(vec, mask1); var and = Avx2.AndNot(shuffle, mask2); var cmp = Avx2.CompareEqual(and, Vector256.Zero); - var correctness = Avx2.And(cmp, Vector256.Create((byte)0x01)); + var correctness = Avx2.And(cmp, Vector256.Create((byte)0x01)); Avx.Store((byte*)pDestination, correctness); } @@ -110,6 +110,7 @@ public static class Int32Extensions and = Sse2.AndNot(shuffle, mask2); cmp = Sse2.CompareEqual(and, Vector128.Zero); correctness = Sse2.And(cmp, one); + Sse2.Store((byte*)pDestination + 16, correctness); } } diff --git a/X10D/src/Core/SpanExtensions.cs b/X10D/src/Core/SpanExtensions.cs new file mode 100644 index 0000000..4462d2a --- /dev/null +++ b/X10D/src/Core/SpanExtensions.cs @@ -0,0 +1,89 @@ +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace X10D.Core; + +/// +/// Extension methods for and . +/// +public static class SpanExtensions +{ + /// + /// Indicate whether a specific enumeration value is found in a span. + /// + /// + /// The span to search from. + /// The enum to search for. + /// if found, otherwise. + /// Unexpected enum memory size. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static bool Contains(this Span span, T value) where T : Enum + { + return Contains((ReadOnlySpan)span, value); + } + + /// + /// Indicate whether a specific enumeration value is found in a span. + /// + /// + /// The span to search from. + /// The enum to search for. + /// if found, otherwise. + /// Unexpected enum memory size. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static bool Contains(this ReadOnlySpan span, T value) + { + // Use MemoryMarshal.CreateSpan instead of using creating new Span instance from pointer will trim down a lot of instructions + // on Release mode. + // https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBDAzgWwB8ABABgAJiBGAOgCUBXAOwwEt8YaBJFmKCAA4BlPgDdWYGLgDcAWABQZSrUYt2nAMIR8A1gBs+IqOMkyFxAExVzFIQAtsUAQBlsweszYc588wGZyGCYGfHIAFSkMAFFg0JByVhZyAG8FcnTyAEE0cgAhHI0cgBE0BQBfBX9KC3INFLSMgG0AKVYMAHEgvgkACgwATwEYCAAzHojcaNiASmmAXQb0xoBZGAw7CAATLh09HtX1rZ2BPQB5ATYIJlwaTIBzO9hcXFZRGB49RMS78kJyA4221250u11uDyeLzeIPYrAAXthQfNFpQAtQkORmLhsCMYORgBAIHp/mtAVQADxhAB8PSEAmwTEpVPIuHpTByYXIomwegYMGm5AA7nY+HjOfEYiF6vIMrLyLARgkkkEQrhyABeeUwRUAVWuOM4mVwlJyiQwNIVJPw0H6y0cuAcehonQwdG1oqYkh6rIZsx8coyxAA7FabXaoA6eTQNLBETA6QyepaVfhcDkfUwaM4gnd1tNo1cMNhErgenrsbjbsawqaWBbtVyeXy/SiKjKMiiWm1OkxumA+oNhmMJlMQrMFu2lgCjrt9qSZycYVcbvdHlIoe8mJ8mN9fiTDkDFxdWMvwWvnq8YDD8PDESemMjJ6jlBisQb8YTidPNhYmbS2UyLJshyja8vyQoirA4TkBKsTSgG6TBuQvaCuQCaMmaNLlgaVYAAoQGafBJg2qzWlAtr2o6zprG6uKwJ6MDemyszpmyWY5nmBYsMW1xlvqlZGiaSrmsRircmBLZPm2ZRAA=== + + // TODO: Figure out some kind of way to directly pass the Span directly into Contains call, which make method smaller and more prone to inlining... + unsafe + { +#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + switch (sizeof(T)) + { + case 1: + { + ref byte enums = ref Unsafe.As(ref MemoryMarshal.GetReference(span)); + return MemoryMarshal.CreateSpan(ref enums, span.Length).Contains(Unsafe.As(ref value)); + } + + case 2: + { + ref ushort enums = ref Unsafe.As(ref MemoryMarshal.GetReference(span)); + return MemoryMarshal.CreateSpan(ref enums, span.Length).Contains(Unsafe.As(ref value)); + } + + case 4: + { + ref uint enums = ref Unsafe.As(ref MemoryMarshal.GetReference(span)); + return MemoryMarshal.CreateSpan(ref enums, span.Length).Contains(Unsafe.As(ref value)); + } + + case 8: + { + ref ulong enums = ref Unsafe.As(ref MemoryMarshal.GetReference(span)); + return MemoryMarshal.CreateSpan(ref enums, span.Length).Contains(Unsafe.As(ref value)); + } + + default: +#if NET7_0_OR_GREATER + throw new UnreachableException($"Enum with the size of {Unsafe.SizeOf()} bytes is unexpected."); +#else + throw new ArgumentException($"Enum with the size of {Unsafe.SizeOf()} bytes is unexpected."); +#endif + } +#pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + } + } +}