From 8503e3c6c93df0f4ec374a4612a91d0e4daae270 Mon Sep 17 00:00:00 2001 From: matsutaka Date: Wed, 28 May 2025 13:26:19 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20ImportMeshIndependentVertexBuffer?= =?UTF-8?q?=E3=81=AEGC=20Alloc=E3=82=92=E5=89=8A=E6=B8=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 以下のようなListへの追加処理について blendShape.Positions.AddRange(array.Select(...).ToArray()) .ToArray() および AddRange() によって大量の GC Alloc が発生する場合がある。 これを軽減するため、以下のような変更を行う (1) .ToArray() を削除 (2) List の Capacity を事前に算出し、AddRange 時の GC Alloc を削減 (3) BlendShape の position, normal, tangents の Capacity を独立して指定できるコンストラクタを追加 --- .../Runtime/Extensions/UnityExtensions.cs | 8 ++ .../Runtime/UniGLTF/IO/MeshIO/MeshData.cs | 86 +++++++++++++++++-- 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/Assets/UniGLTF/Runtime/Extensions/UnityExtensions.cs b/Assets/UniGLTF/Runtime/Extensions/UnityExtensions.cs index 43b7da04b..ba9218ff5 100644 --- a/Assets/UniGLTF/Runtime/Extensions/UnityExtensions.cs +++ b/Assets/UniGLTF/Runtime/Extensions/UnityExtensions.cs @@ -66,6 +66,14 @@ namespace UniGLTF } } + public BlendShape(string name, int numPositions, int numNormals, int numTangents) + { + Name = name; + Positions = new List(numPositions); + Normals = new List(numNormals); + Tangents = new List(numTangents); + } + public List Positions { get; private set; } public List Normals { get; private set; } public List Tangents { get; private set; } diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs index ed312613a..9cb09fd4e 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs @@ -203,7 +203,10 @@ namespace UniGLTF return (vertexCount, indexCount); } - private BlendShape GetOrCreateBlendShape(int i) + private BlendShape GetOrCreateBlendShape(int i) => + GetOrCreateBlendShape(i: i, numPositions: 0, numNormals: 0, numTangents: 0); + + private BlendShape GetOrCreateBlendShape(int i, int numPositions, int numNormals, int numTangents) { if (i < _blendShapes.Count && _blendShapes[i] != null) { @@ -215,7 +218,7 @@ namespace UniGLTF _blendShapes.Add(null); } - var blendShape = new BlendShape(i.ToString()); + var blendShape = new BlendShape(i.ToString(), numPositions, numNormals, numTangents); _blendShapes[i] = blendShape; return blendShape; } @@ -293,6 +296,79 @@ namespace UniGLTF { bool isOldVersion = data.GLTF.IsGeneratedUniGLTFAndOlder(1, 16); + { + // 事前に blendShape.{Positions,Normals,Tangents} のサイズを設定することで、GC Alloc を減少させる + int maxTargetsCount = 0; + Dictionary numPositions = new(); + Dictionary numNormals = new(); + Dictionary numTangents = new(); + + foreach (var primitives in gltfMesh.primitives) + { + if (primitives.targets != null && primitives.targets.Count > 0) + { + maxTargetsCount = Math.Max(maxTargetsCount, primitives.targets.Count); + for (var i = 0; i < primitives.targets.Count; i++) + { + gltfMorphTarget primTarget = primitives.targets[i]; + glTF GLTF = data.GLTF; + List accessors = GLTF.accessors; + if (primTarget.POSITION != -1) + { + numPositions.TryAdd(i, 0); + numPositions[i] += GetAccessorElementCount(GLTF, accessors[primTarget.POSITION]); + } + if (primTarget.NORMAL != -1) + { + numNormals.TryAdd(i, 0); + numNormals[i] += GetAccessorElementCount(GLTF, accessors[primTarget.NORMAL]); + } + if (primTarget.TANGENT != -1) + { + numTangents.TryAdd(i, 0); + numTangents[i] += GetAccessorElementCount(GLTF, accessors[primTarget.TANGENT]); + } + continue; + + // GetTypedFromAccessor 相当の、エレメント数のみを取得する関数 + static int GetTypedArrayElementCount(glTFAccessor accessor, glTFBufferView view) + { + if (view.byteStride == 0 || view.byteStride == accessor.GetStride()) + { + // planar layout + return accessor.CalcByteSize() / accessor.GetStride(); + } + else + { + // interleaved layout + return accessor.count; + } + } + + // GetArrayFromAccessor 相当の、エレメント数のみを取得する関数 + static int GetAccessorElementCount(glTF GLTF, glTFAccessor accessor) + { + if (accessor.count <= 0) return 0; + if (accessor.bufferView is null) return 0; + return accessor.bufferView.HasValidIndex() + ? GetTypedArrayElementCount(accessor, GLTF.bufferViews[accessor.bufferView.Value]) + : accessor.count; + } + } + } + } + // 実際のサイズを確定 + for (var i = 0; i < maxTargetsCount; i++) + { + GetOrCreateBlendShape( + i, + numPositions.GetValueOrDefault(i, 0), + numNormals.GetValueOrDefault(i, 0), + numTangents.GetValueOrDefault(i, 0) + ); + } + } + foreach (var primitives in gltfMesh.primitives) { var vertexOffset = _currentVertexCount; @@ -368,7 +444,7 @@ namespace UniGLTF throw new Exception("different length"); } - blendShape.Positions.AddRange(array.Select(inverter.InvertVector3).ToArray()); + blendShape.Positions.AddRange(array.Select(inverter.InvertVector3)); } if (primTarget.NORMAL != -1) @@ -379,7 +455,7 @@ namespace UniGLTF throw new Exception("different length"); } - blendShape.Normals.AddRange(array.Select(inverter.InvertVector3).ToArray()); + blendShape.Normals.AddRange(array.Select(inverter.InvertVector3)); } if (primTarget.TANGENT != -1) @@ -390,7 +466,7 @@ namespace UniGLTF throw new Exception("different length"); } - blendShape.Tangents.AddRange(array.Select(inverter.InvertVector3).ToArray()); + blendShape.Tangents.AddRange(array.Select(inverter.InvertVector3)); } } }