From d392a8c57a6e70333ef3fa0eee2988461c911aa8 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Mon, 2 Sep 2024 21:00:40 -0700 Subject: [PATCH] Mesh decoding performance improvements --- .../MeshHelper.cs | 130 ++++++++++++------ 1 file changed, 86 insertions(+), 44 deletions(-) diff --git a/Source/AssetRipper.SourceGenerated.Extensions/MeshHelper.cs b/Source/AssetRipper.SourceGenerated.Extensions/MeshHelper.cs index 401bc7219..54f076cf3 100644 --- a/Source/AssetRipper.SourceGenerated.Extensions/MeshHelper.cs +++ b/Source/AssetRipper.SourceGenerated.Extensions/MeshHelper.cs @@ -2,6 +2,7 @@ using System.Buffers.Binary; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace AssetRipper.SourceGenerated.Extensions { @@ -189,20 +190,33 @@ namespace AssetRipper.SourceGenerated.Extensions ArgumentNullException.ThrowIfNull(input); ArgumentOutOfRangeException.ThrowIfLessThan(dimension, 1); + ArgumentOutOfRangeException.ThrowIfGreaterThan(dimension, 4); + ValidateLength(input, dimension); - if (input.Length % dimension != 0) + Vector2[] result = GC.AllocateUninitializedArray(input.Length / dimension); + switch (dimension) { - throw new ArgumentException($"Input array length {input.Length} is not divisible by dimension {dimension}", nameof(input)); - } - - Vector2[] result = new Vector2[input.Length / dimension]; - for (int i = 0; i < result.Length; i++) - { - result[i] = dimension switch - { - 1 => new Vector2(input[dimension * i], 0), - _ => new Vector2(input[dimension * i], input[dimension * i + 1]), - }; + case 1: + for (int i = result.Length - 1; i >= 0; i--) + { + result[i] = new Vector2(input[i], 0); + } + break; + case 2: + MemoryMarshal.Cast(input).CopyTo(result); + break; + case 3: + for (int i = result.Length - 1; i >= 0; i--) + { + result[i] = new Vector2(input[3 * i], input[3 * i + 1]); + } + break; + case 4: + for (int i = result.Length - 1; i >= 0; i--) + { + result[i] = new Vector2(input[4 * i], input[4 * i + 1]); + } + break; } return result; } @@ -213,22 +227,36 @@ namespace AssetRipper.SourceGenerated.Extensions ArgumentNullException.ThrowIfNull(input); ArgumentOutOfRangeException.ThrowIfLessThan(dimension, 1); + ArgumentOutOfRangeException.ThrowIfGreaterThan(dimension, 4); + ValidateLength(input, dimension); - if (input.Length % dimension != 0) - { - throw new ArgumentException($"Input array length {input.Length} is not divisible by dimension {dimension}", nameof(input)); - } + //In the four dimensional case for Normals, the fourth dimension was always zero + //This is seemingly intended to maintain data alignment - Vector3[] result = new Vector3[input.Length / dimension]; - for (int i = 0; i < result.Length; i++) + Vector3[] result = GC.AllocateUninitializedArray(input.Length / dimension); + switch (dimension) { - result[i] = dimension switch - { - 1 => new Vector3(input[dimension * i], input[dimension * i + 1], input[dimension * i + 2]), - 2 => new Vector3(input[dimension * i], input[dimension * i + 1], input[dimension * i + 2]), - _ => new Vector3(input[dimension * i], input[dimension * i + 1], input[dimension * i + 2]), //In the four dimensional case for Normals, the fourth dimension was always zero - //This is seemingly intended to maintain data alignment - }; + case 1: + for (int i = result.Length - 1; i >= 0; i--) + { + result[i] = new Vector3(input[i], 0, 0); + } + break; + case 2: + for (int i = result.Length - 1; i >= 0; i--) + { + result[i] = new Vector3(input[2 * i], input[2 * i + 1], 0); + } + break; + case 3: + MemoryMarshal.Cast(input).CopyTo(result); + break; + case 4: + for (int i = result.Length - 1; i >= 0; i--) + { + result[i] = new Vector3(input[4 * i], input[4 * i + 1], input[4 * i + 2]); + } + break; } return result; } @@ -239,24 +267,43 @@ namespace AssetRipper.SourceGenerated.Extensions ArgumentNullException.ThrowIfNull(input); ArgumentOutOfRangeException.ThrowIfLessThan(dimension, 1); + ArgumentOutOfRangeException.ThrowIfGreaterThan(dimension, 4); + ValidateLength(input, dimension); + Vector4[] result = GC.AllocateUninitializedArray(input.Length / dimension); + switch (dimension) + { + case 1: + for (int i = result.Length - 1; i >= 0; i--) + { + result[i] = new Vector4(input[i], 0, 0, 0); + } + break; + case 2: + for (int i = result.Length - 1; i >= 0; i--) + { + result[i] = new Vector4(input[2 * i], input[2 * i + 1], 0, 0); + } + break; + case 3: + for (int i = result.Length - 1; i >= 0; i--) + { + result[i] = new Vector4(input[3 * i], input[3 * i + 1], input[3 * i + 2], 0); + } + break; + case 4: + MemoryMarshal.Cast(input).CopyTo(result); + break; + } + return result; + } + + private static void ValidateLength(float[] input, int dimension) + { if (input.Length % dimension != 0) { throw new ArgumentException($"Input array length {input.Length} is not divisible by dimension {dimension}", nameof(input)); } - - Vector4[] result = new Vector4[input.Length / dimension]; - for (int i = 0; i < result.Length; i++) - { - result[i] = dimension switch - { - 1 => new Vector4(input[dimension * i], 0, 0, 0), - 2 => new Vector4(input[dimension * i], input[dimension * i + 1], 0, 0), - 3 => new Vector4(input[dimension * i], input[dimension * i + 1], input[dimension * i + 2], 0), - _ => new Vector4(input[dimension * i], input[dimension * i + 1], input[dimension * i + 2], input[dimension * i + 3]), - }; - } - return result; } public static ColorFloat[] FloatArrayToColorFloat(float[] input) @@ -268,12 +315,7 @@ namespace AssetRipper.SourceGenerated.Extensions throw new ArgumentException($"Input array length {input.Length} is not divisible by four", nameof(input)); } - ColorFloat[] result = new ColorFloat[input.Length / 4]; - for (int i = 0; i < result.Length; i++) - { - result[i] = new ColorFloat(input[4 * i], input[4 * i + 1], input[4 * i + 2], input[4 * i + 3]); - } - return result; + return MemoryMarshal.Cast(input).ToArray(); } } }