Skip to content

Commit

Permalink
perf: optimize InternalAutoFreezeMeaninglessBlendShapeProcessor
Browse files Browse the repository at this point in the history
This saves over 250ms on my main avatar.
  • Loading branch information
bdunderscore committed Nov 4, 2024
1 parent 9ea7c9a commit 796a644
Showing 1 changed file with 74 additions and 12 deletions.
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);

NativeArray<bool> 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 796a644

Please sign in to comment.