Skip to content

Commit

Permalink
Merge pull request #1325 from bdunderscore/meaningless-perf
Browse files Browse the repository at this point in the history
perf: optimize InternalAutoFreezeMeaninglessBlendShapeProcessor
  • Loading branch information
anatawa12 authored Nov 4, 2024
2 parents 9ea7c9a + 0d0fd8f commit 43ca9ef
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG-PRERELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog].
### Added

### Changed
- Improved performance in InternalAutoFreezeMeaninglessBlendShapeProcessor [`#1325`](https://github.com/anatawa12/AvatarOptimizer/pull/1325)

### Deprecated

Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` `#1325`
- 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`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using nadena.dev.ndmf;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEditor;
using UnityEngine;
using UnityEngine.Profiling;

namespace Anatawa12.AvatarOptimizer.Processors.SkinnedMeshes
{
Expand Down Expand Up @@ -35,35 +40,63 @@ public override void Process(BuildContext context, MeshInfo2 target)
value[vertex.BlendShapeBufferVertexIndex] = true;
}

Profiler.BeginSample("buffers loop");

var shapeCount = buffers.Sum(x => x.Key.Shapes.Count);

using var isMeaningfulArray = new NativeArray<bool>(shapeCount, Allocator.TempJob);
List<(string, JobHandle, int)> meaningfulIndex = new List<(string, JobHandle, int)>(shapeCount);

int bufIndexNext = 0;
foreach (var (shapeBuffer, useIndices) in buffers)
{
foreach (var (shapeName, shapeShape) in shapeBuffer.Shapes)
{
var bufIndex = bufIndexNext++;

if (!meaninglessBlendShapes.Contains(shapeName)) continue;

var meaningfull = false;
JobHandle completionHandle = default;

foreach (var bufferIndex in shapeShape.FramesBufferIndices)
{
var deltaVertices = shapeBuffer.DeltaVertices[bufferIndex];
var deltaNormals = shapeBuffer.DeltaNormals[bufferIndex];
var deltaTangents = shapeBuffer.DeltaTangents[bufferIndex];

for (var i = 0; i < deltaVertices.Length; i++)
if (bufIndex >= isMeaningfulArray.Length)
{
if (!useIndices[i]) continue;
if (deltaVertices[i] != Vector3.zero || deltaNormals[i] != Vector3.zero || deltaTangents[i] != Vector3.zero)
{
meaningfull = true;
break;
}
throw new IndexOutOfRangeException("bufIndex >= isMeaningfulArray.Length");
}

var job = new ShapeAnalysisJob
{
isMeaningfulArray = isMeaningfulArray,
resultIndex = bufIndex,
deltaVertices = deltaVertices,
deltaNormals = deltaNormals,
deltaTangents = deltaTangents,
useIndices = useIndices,
}.Schedule(deltaVertices.Length, 64);
completionHandle = JobHandle.CombineDependencies(job, completionHandle);
}

if (meaningfull)
meaninglessBlendShapes.Remove(shapeName);

meaningfulIndex.Add((shapeName, completionHandle, bufIndex));
}
}
Profiler.BeginSample("Await completion");
foreach (var (_, handle, _) in meaningfulIndex)
{
handle.Complete();
}
foreach (var (shapeName, _, bufIndex) in meaningfulIndex)
{
if (isMeaningfulArray[bufIndex])
meaninglessBlendShapes.Remove(shapeName);
}
Profiler.EndSample();

Profiler.EndSample();

foreach (var meaninglessBlendShape in meaninglessBlendShapes)
context.RecordRemoveProperty(Target, $"blendShape.{meaninglessBlendShape}");
Expand All @@ -73,6 +106,35 @@ public override void Process(BuildContext context, MeshInfo2 target)
set.UnionWith(meaninglessBlendShapes);
freezeBlendShape.shapeKeysSet.SetValueNonPrefab(set);
}

[BurstCompile]
struct ShapeAnalysisJob : IJobParallelFor
{
// We will issue a separate job for each blend shape frame, and want them to run in parallel. However,
// normally unity will prevent us from using the same NativeArray in multiple such jobs. Since we're
// writing only, and the order in which the writes occurs doesn't matter, _and_ this is an atomic primitive
// type, we can get away with disabling this safety check.
[NativeDisableContainerSafetyRestriction]
[WriteOnly]
public NativeArray<bool> isMeaningfulArray;

public int resultIndex;

[ReadOnly] public NativeArray<Vector3> deltaVertices;
[ReadOnly] public NativeArray<Vector3> deltaNormals;
[ReadOnly] public NativeArray<Vector3> deltaTangents;
[ReadOnly] public NativeArray<bool> useIndices;

public void Execute(int vertIndex)
{
if (!useIndices[vertIndex]) return;

if (deltaVertices[vertIndex] != Vector3.zero || deltaNormals[vertIndex] != Vector3.zero || deltaTangents[vertIndex] != Vector3.zero)
{
isMeaningfulArray[resultIndex] = true;
}
}
}

// nothing to do
public override IMeshInfoComputer GetComputer(IMeshInfoComputer upstream) => upstream;
Expand Down

0 comments on commit 43ca9ef

Please sign in to comment.