From bd9d8a6bf9f0fb4ef5e13bba7f060e22375808bd Mon Sep 17 00:00:00 2001 From: bd_ Date: Sun, 3 Nov 2024 20:23:13 -0800 Subject: [PATCH] perf: improve performance of AutoMergeBlendShape This pass was obtaining the blendshape buffer for each vertex, then processing those buffers. However, in practice, most vertices share the same buffer; this pass ignored this, and redid the same work thousands of times as a result. This reduces processing time from ~370ms to ~22ms on my main avatar. --- .../TraceAndOptimize/AutoMergeBlendShape.cs | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs b/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs index 96be8006..f4c64eba 100644 --- a/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs +++ b/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs @@ -7,6 +7,7 @@ using Unity.Collections; using Unity.Jobs; using UnityEngine; +using UnityEngine.Profiling; namespace Anatawa12.AvatarOptimizer.Processors.TraceAndOptimizes; @@ -23,13 +24,22 @@ protected override void Execute(BuildContext context, TraceAndOptimizeState stat { if (state.Exclusions.Contains(skinnedMeshRenderer.gameObject)) continue; // manual exclusion - ErrorReport.WithContextObject(skinnedMeshRenderer, () => DoAutoMerge(context.GetMeshInfoFor(skinnedMeshRenderer), context)); + ErrorReport.WithContextObject(skinnedMeshRenderer, () => + { + Profiler.BeginSample("GetMeshInfoFor", skinnedMeshRenderer); + var meshInfo = context.GetMeshInfoFor(skinnedMeshRenderer); + Profiler.EndSample(); + + DoAutoMerge(meshInfo, context); + }); } } private static void DoAutoMerge(MeshInfo2 meshInfo2, BuildContext context) { var animationComponent = context.GetAnimationComponent(meshInfo2.SourceRenderer); + + Profiler.BeginSample("Compute merge keys"); var groups = new Dictionary>(); foreach (var (name, weight) in meshInfo2.BlendShapes) @@ -43,10 +53,17 @@ private static void DoAutoMerge(MeshInfo2 meshInfo2, BuildContext context) } // nothing to merge - if (groups.Values.All(x => x.Count <= 1)) return; + if (groups.Values.All(x => x.Count <= 1)) + { + Profiler.EndSample(); + return; + } + Profiler.EndSample(); + Profiler.BeginSample("Prepare merge"); // prepare merge - var buffers = meshInfo2.Vertices.Select(x => x.BlendShapeBuffer).ToArray(); + var buffers = meshInfo2.Vertices.Select(x => x.BlendShapeBuffer).Distinct().ToArray(); + Profiler.EndSample(); // bulk remove to optimize removing blendshape process var removeNames = new HashSet(); @@ -59,6 +76,7 @@ private static void DoAutoMerge(MeshInfo2 meshInfo2, BuildContext context) { // validate the blendShapes are simple enough to merge // if not, skip + Profiler.BeginSample("AutoMergeBlendShape: Validate"); foreach (var buffer in buffers) { float? commonFrameWeight = null; @@ -80,6 +98,7 @@ private static void DoAutoMerge(MeshInfo2 meshInfo2, BuildContext context) } } } + Profiler.EndSample(); // validation passed, merge var newName = $"AAO_Merged_{string.Join("_", names)}_{i++}"; @@ -94,6 +113,7 @@ private static void DoAutoMerge(MeshInfo2 meshInfo2, BuildContext context) meshInfo2.BlendShapes.Add((newName, key.defaultWeight)); removeNames.UnionWith(names); + Profiler.BeginSample("AutoMergeBlendShape: Merge"); // actually merge data foreach (var buffer in buffers) { @@ -136,6 +156,8 @@ private static void DoAutoMerge(MeshInfo2 meshInfo2, BuildContext context) } } + Profiler.EndSample(); + next_shape: ; }