Skip to content

Commit

Permalink
Merge pull request #1195 from anatawa12/fix-preview-performance
Browse files Browse the repository at this point in the history
fix(preview): performance issues with preview system
  • Loading branch information
anatawa12 authored Sep 22, 2024
2 parents 726922b + 033fb24 commit 9be304a
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG-PRERELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The format is based on [Keep a Changelog].

### Fixed
- Texture Packing which resolves to the white texture would break the Unity Editor `#1193`
- Performance issues with preview system `#1195`

### Security

Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The format is based on [Keep a Changelog].
- Use UInt16 index buffer if possible even when total vertex count is more than 2^16 `#1178`
- With baseVertex in index buffer, we can use UInt16 index buffer even if total vertex count is more than 2^16.
- Of course, if one submeh references wide range of vertices, we cannot use UInt16 index buffer so we still use UInt32 index buffer in such a case.
- Reimplement Preview system with NDMF Preview System `#1131`
- Reimplement Preview system with NDMF Preview System `#1131` `#1195`
- This will prevent issues relates to Animation Mode bug.
- This allows you to preview Remove Mesh components without selecting Mesh OR while in Animation Mode.

Expand Down
12 changes: 10 additions & 2 deletions Editor/EditModePreview/RemoveMeshByMaskRenderFilter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using nadena.dev.ndmf.preview;
using Unity.Burst;
Expand Down Expand Up @@ -56,8 +58,7 @@ protected override ValueTask Process(SkinnedMeshRenderer original, SkinnedMeshRe
{
textureWidth = context.Observe(materialSetting.mask, m => m.width);
textureHeight = context.Observe(materialSetting.mask, m => m.height);
// TODO: GetPixels32 might be slow?
pixels = context.Observe(materialSetting.mask, m => m.GetPixels32());
pixels = context.Observe(materialSetting.mask, m => m.GetPixels32(), Utils.Color32ArrayEquals);
}
using var pixelsJob = new NativeArray<Color32>(pixels, Allocator.TempJob);

Expand Down Expand Up @@ -138,6 +139,13 @@ protected override ValueTask Process(SkinnedMeshRenderer original, SkinnedMeshRe
return default;
}

private class Color32Comparator : IEqualityComparer<Color32>
{
public static Color32Comparator Instance = new();
public bool Equals(Color32 x, Color32 y) => x.r == y.r && x.g == y.g && x.b == y.b && x.a == y.a;
public int GetHashCode(Color32 obj) => HashCode.Combine(obj.r, obj.g, obj.b, obj.a);
}

[BurstCompile]
struct CheckRemovePolygonJob : IJobParallelFor
{
Expand Down
68 changes: 68 additions & 0 deletions Internal/Utils/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;
using UnityEditor;
using UnityEngine;

Expand Down Expand Up @@ -297,5 +300,70 @@ public static int MostSignificantBit(uint x)
x++;
return debruijn[(int)((x * 0x076be629) >> 27)];
}

/// <summary>
/// Fast compare of <see cref="Color32"/> array.
///
/// This is semantically equivalent to <c>a.SequenceEqual(b)</c> but much faster (about 80x faster).
///
/// Compared to <c>a.SequenceEqual(b, new Color32Comparator())</c>, this is about 10x faster.
/// </summary>
/// <param name="a">first array</param>
/// <param name="b">second array</param>
/// <returns>whether two arrays are equal</returns>
public static bool Color32ArrayEquals(ReadOnlySpan<Color32> a, ReadOnlySpan<Color32> b)
{
if (Color32ArrayEqualsDataHolder.SafeToUseFastImplementation)
{
var aSlice = MemoryMarshal.Cast<Color32, int>(a);
var bSlice = MemoryMarshal.Cast<Color32, int>(b);
if (aSlice.Length != bSlice.Length) return false;
for (var i = 0; i < aSlice.Length; i++)
if (aSlice[i] != bSlice[i])
return false;
return true;
}
else
{
for (var i = 0; i < a.Length; i++)
if (!Color32Equals(a[i], b[i]))
return false;
return true;

bool Color32Equals(Color32 x, Color32 y) => x.r == y.r && x.g == y.g && x.b == y.b && x.a == y.a;
}
}

private static class Color32ArrayEqualsDataHolder
{
public static readonly bool SafeToUseFastImplementation;

static Color32ArrayEqualsDataHolder()
{
SafeToUseFastImplementation = ComputeSafeToUseFastImplementation();

bool ComputeSafeToUseFastImplementation()
{
if (UnsafeUtility.SizeOf<Color32>() != sizeof(int)) return false;
if ((typeof(Color32).Attributes & TypeAttributes.ExplicitLayout) == 0) return false;
var field = typeof(Color32).GetField("rgba", BindingFlags.NonPublic | BindingFlags.Instance);
if (field == null) return false;
if (field.GetCustomAttribute<FieldOffsetAttribute>()?.Value != 0) return false;
return true;
}
}
}

/// <summary>
/// Fast compare of <see cref="Color32"/> array.
///
/// This is semantically equivalent to <c>a.SequenceEqual(b)</c> but much faster (about 80x faster).
///
/// Compared to <c>a.SequenceEqual(b, new Color32Comparator())</c>, this is about 10x faster.
/// </summary>
/// <param name="a">first array</param>
/// <param name="b">second array</param>
/// <returns>whether two arrays are equal</returns>
public static bool Color32ArrayEquals(Color32[] a, Color32[] b) => Color32ArrayEquals(a.AsSpan(), b.AsSpan());
}
}

0 comments on commit 9be304a

Please sign in to comment.