From 22d4fc7ddc1d59cbfde62438691e7c577f2b5af6 Mon Sep 17 00:00:00 2001 From: bd_ Date: Sun, 3 Nov 2024 20:23:13 -0800 Subject: [PATCH 1/2] 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. --- CHANGELOG-PRERELEASE.md | 1 + CHANGELOG.md | 2 +- Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 454a227a..a0831125 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog]. ### Added ### Changed +- Performance improvements for AutoMergeBlendShape `#1327` ### Deprecated diff --git a/CHANGELOG.md b/CHANGELOG.md index 3192a6df..69873d36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,7 +89,7 @@ The format is based on [Keep a Changelog]. - Renamed debug options internally `#1228` - This will lose previously configured debug options. - However, debug options are not considered as Public API as stated in documents so this is not backward incompatible changes in semver 2.0.0 section 8. -- Performance Improvements `#1234` `#1243` `#1240` `#1288` `#1304` `#1307` `#1314` +- Performance Improvements `#1234` `#1243` `#1240` `#1288` `#1304` `#1307` `#1314` `#1327` - Transform gizmo are now hidden while you're editing box of Remove Mesh in Box `#1259` - This prevents mistakenly moving the Skinned Mesh Renderer while editing the box. - Make MergePhysBone implement `INetworkID` `#1260` diff --git a/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs b/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs index 96be8006..d2bb960e 100644 --- a/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs +++ b/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs @@ -46,7 +46,7 @@ private static void DoAutoMerge(MeshInfo2 meshInfo2, BuildContext context) if (groups.Values.All(x => x.Count <= 1)) return; // prepare merge - var buffers = meshInfo2.Vertices.Select(x => x.BlendShapeBuffer).ToArray(); + var buffers = meshInfo2.Vertices.Select(x => x.BlendShapeBuffer).Distinct().ToArray(); // bulk remove to optimize removing blendshape process var removeNames = new HashSet(); From 9b99c3cb758aab0e0ba13ef88c60b62ede91283a Mon Sep 17 00:00:00 2001 From: bd_ Date: Sun, 3 Nov 2024 21:56:15 -0800 Subject: [PATCH 2/2] chore: add additional profiling annotations --- .../TraceAndOptimize/AutoMergeBlendShape.cs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs b/Editor/Processors/TraceAndOptimize/AutoMergeBlendShape.cs index d2bb960e..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).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: ; }