using UnityEngine.Rendering; using System; using System.Reflection; using System.Linq.Expressions; namespace UnityEditor.Rendering { /// Serialisation of BitArray, Utility class public static class SerializedBitArrayUtilities { /// Convert to 8bit /// The SerializedProperty /// A SerializedBitArray8 public static SerializedBitArray8 ToSerializeBitArray8(this SerializedProperty serializedProperty) { if (!IsBitArrayOfCapacity(serializedProperty, 8u)) throw new Exception("Cannot get SerializeBitArray of this Capacity"); return new SerializedBitArray8(serializedProperty); } /// Try convert to 8bit /// The SerializedProperty /// Out SerializedBitArray8 /// True if convertion was a success public static bool TryGetSerializeBitArray8(this SerializedProperty serializedProperty, out SerializedBitArray8 serializedBitArray) { serializedBitArray = null; if (!IsBitArrayOfCapacity(serializedProperty, 8u)) return false; serializedBitArray = new SerializedBitArray8(serializedProperty); return true; } /// Convert to 16bit /// The SerializedProperty /// A SerializedBitArray16 public static SerializedBitArray16 ToSerializeBitArray16(this SerializedProperty serializedProperty) { if (!IsBitArrayOfCapacity(serializedProperty, 16u)) throw new Exception("Cannot get SerializeBitArray of this Capacity"); return new SerializedBitArray16(serializedProperty); } /// Try convert to 16bit /// The SerializedProperty /// Out SerializedBitArray16 /// True if convertion was a success public static bool TryGetSerializeBitArray16(this SerializedProperty serializedProperty, out SerializedBitArray16 serializedBitArray) { serializedBitArray = null; if (!IsBitArrayOfCapacity(serializedProperty, 16u)) return false; serializedBitArray = new SerializedBitArray16(serializedProperty); return true; } /// Convert to 32bit /// The SerializedProperty /// A SerializedBitArray32 public static SerializedBitArray32 ToSerializeBitArray32(this SerializedProperty serializedProperty) { if (!IsBitArrayOfCapacity(serializedProperty, 32u)) throw new Exception("Cannot get SerializeBitArray of this Capacity"); return new SerializedBitArray32(serializedProperty); } /// Try convert to 32bit /// The SerializedProperty /// Out SerializedBitArray32 /// True if convertion was a success public static bool TryGetSerializeBitArray32(this SerializedProperty serializedProperty, out SerializedBitArray32 serializedBitArray) { serializedBitArray = null; if (!IsBitArrayOfCapacity(serializedProperty, 32u)) return false; serializedBitArray = new SerializedBitArray32(serializedProperty); return true; } /// Convert to 64bit /// The SerializedProperty /// A SerializedBitArray64 public static SerializedBitArray64 ToSerializeBitArray64(this SerializedProperty serializedProperty) { if (!IsBitArrayOfCapacity(serializedProperty, 64u)) throw new Exception("Cannot get SerializeBitArray of this Capacity"); return new SerializedBitArray64(serializedProperty); } /// Try convert to 64bit /// The SerializedProperty /// Out SerializedBitArray64 /// True if convertion was a success public static bool TryGetSerializeBitArray64(this SerializedProperty serializedProperty, out SerializedBitArray64 serializedBitArray) { serializedBitArray = null; if (!IsBitArrayOfCapacity(serializedProperty, 64u)) return false; serializedBitArray = new SerializedBitArray64(serializedProperty); return true; } /// Convert to 128bit /// The SerializedProperty /// A SerializedBitArray128 public static SerializedBitArray128 ToSerializeBitArray128(this SerializedProperty serializedProperty) { if (!IsBitArrayOfCapacity(serializedProperty, 128u)) throw new Exception("Cannot get SerializeBitArray of this Capacity"); return new SerializedBitArray128(serializedProperty); } /// Try convert to 128bit /// The SerializedProperty /// Out SerializedBitArray128 /// True if convertion was a success public static bool TryGetSerializeBitArray128(this SerializedProperty serializedProperty, out SerializedBitArray128 serializedBitArray) { serializedBitArray = null; if (!IsBitArrayOfCapacity(serializedProperty, 128u)) return false; serializedBitArray = new SerializedBitArray128(serializedProperty); return true; } /// Convert to 256bit /// The SerializedProperty /// A SerializedBitArray256 public static SerializedBitArray256 ToSerializeBitArray256(this SerializedProperty serializedProperty) { if (!IsBitArrayOfCapacity(serializedProperty, 256u)) throw new Exception("Cannot get SerializeBitArray of this Capacity"); return new SerializedBitArray256(serializedProperty); } /// Try convert to 256bit /// The SerializedProperty /// Out SerializedBitArray256 /// True if convertion was a success public static bool TryGetSerializeBitArray256(this SerializedProperty serializedProperty, out SerializedBitArray256 serializedBitArray) { serializedBitArray = null; if (!IsBitArrayOfCapacity(serializedProperty, 256u)) return false; serializedBitArray = new SerializedBitArray256(serializedProperty); return true; } static bool IsBitArrayOfCapacity(SerializedProperty serializedProperty, uint capacity) { const string baseTypeName = "BitArray"; string type = serializedProperty.type; uint serializedCapacity; return type.StartsWith(baseTypeName) && uint.TryParse(type.Substring(baseTypeName.Length), out serializedCapacity) && capacity == serializedCapacity; } } /// interface to handle generic SerializedBitArray public interface ISerializedBitArray { /// Capacity of the bitarray uint capacity { get; } /// Get the bit at given index /// The index /// Bit value bool GetBitAt(uint bitIndex); /// Set the bit at given index /// The index /// The value void SetBitAt(uint bitIndex, bool value); /// Does the bit at given index have multiple different values? /// The index /// True: Multiple different value bool HasBitMultipleDifferentValue(uint bitIndex); } /// Abstract base classe of all SerializedBitArray public abstract class SerializedBitArray : ISerializedBitArray { // Note: this should be exposed at the same time as issue with type other than Int32 is fixed on C++ side /// Set the bit at given index protected static Action SetBitAtIndexForAllTargetsImmediate; /// Has multiple differente value bitwise protected static Func HasMultipleDifferentValuesBitwise; static SerializedBitArray() { var type = typeof(SerializedProperty); var setBitAtIndexForAllTargetsImmediateMethodInfo = type.GetMethod("SetBitAtIndexForAllTargetsImmediate", BindingFlags.Instance | BindingFlags.NonPublic); var hasMultipleDifferentValuesBitwisePropertyInfo = type.GetProperty("hasMultipleDifferentValuesBitwise", BindingFlags.Instance | BindingFlags.NonPublic); var serializedPropertyParameter = Expression.Parameter(typeof(SerializedProperty), "property"); var indexParameter = Expression.Parameter(typeof(int), "index"); var valueParameter = Expression.Parameter(typeof(bool), "value"); var hasMultipleDifferentValuesBitwiseProperty = Expression.Property(serializedPropertyParameter, hasMultipleDifferentValuesBitwisePropertyInfo); var setBitAtIndexForAllTargetsImmediateCall = Expression.Call(serializedPropertyParameter, setBitAtIndexForAllTargetsImmediateMethodInfo, indexParameter, valueParameter); var setBitAtIndexForAllTargetsImmediateLambda = Expression.Lambda>(setBitAtIndexForAllTargetsImmediateCall, serializedPropertyParameter, indexParameter, valueParameter); var hasMultipleDifferentValuesBitwiseLambda = Expression.Lambda>(hasMultipleDifferentValuesBitwiseProperty, serializedPropertyParameter); SetBitAtIndexForAllTargetsImmediate = setBitAtIndexForAllTargetsImmediateLambda.Compile(); HasMultipleDifferentValuesBitwise = hasMultipleDifferentValuesBitwiseLambda.Compile(); } /// The underlying serialized property protected SerializedProperty m_SerializedProperty; SerializedProperty[] m_SerializedProperties; /// Capacity of the bitarray public uint capacity { get; } internal SerializedBitArray(SerializedProperty serializedProperty, uint capacity) { this.capacity = capacity; m_SerializedProperty = serializedProperty; } /// Initialisation of dedicated SerializedPropertiws /// Arrays of SerializedProperty protected SerializedProperty[] GetOrInitializeSerializedProperties() { if (m_SerializedProperties == null) { UnityEngine.Object[] targets = m_SerializedProperty.serializedObject.targetObjects; int size = targets.Length; if (size == 1) m_SerializedProperties = new[] { m_SerializedProperty }; else { string propertyPath = m_SerializedProperty.propertyPath; m_SerializedProperties = new SerializedProperty[size]; for (int i = 0; i < size; ++i) { m_SerializedProperties[i] = new SerializedObject(targets[i]).FindProperty(propertyPath); } } } return m_SerializedProperties; } /// Does the bit at given index have multiple different values? /// The index /// True: Multiple different value public bool HasBitMultipleDifferentValue(uint bitIndex) { if (bitIndex >= capacity) throw new IndexOutOfRangeException("Index out of bound in BitArray" + capacity); return HasBitMultipleDifferentValue_Internal(bitIndex); } /// Say if the properties have differente values /// The index /// True: properties have different value abstract protected bool HasBitMultipleDifferentValue_Internal(uint bitIndex); /// /// Safety: serializedProperty must match its path /// /// serializedProperty must match its path /// serializedProperty must match its path /// /// unsafe protected bool HasBitMultipleDifferentValue_For64Bits(string propertyPath, SerializedProperty serializedProperty, uint bitIndex) { if (!serializedProperty.hasMultipleDifferentValues) return false; var serializedProperties = GetOrInitializeSerializedProperties(); int length = serializedProperties.Length; ulong mask = 1uL << (int)bitIndex; bool value = ((ulong)m_SerializedProperties[0].FindPropertyRelative(propertyPath).longValue & mask) != 0uL; for (int i = 1; i < length; ++i) { if ((((ulong)m_SerializedProperties[i].FindPropertyRelative(propertyPath).longValue & mask) != 0uL) ^ value) return true; } return false; } /// Get the bit at given index /// The index /// Bit value public bool GetBitAt(uint bitIndex) { if (bitIndex >= capacity) throw new IndexOutOfRangeException("Index out of bound in BitArray" + capacity); return GetBitAt_Internal(bitIndex); } /// Get the value at index /// The index /// Value at the index abstract protected bool GetBitAt_Internal(uint bitIndex); /// Set the bit at given index /// The index /// The value public void SetBitAt(uint bitIndex, bool value) { if (bitIndex >= capacity) throw new IndexOutOfRangeException("Index out of bound in BitArray" + capacity); SetBitAt_Internal(bitIndex, value); } /// Set the bit at given index /// The index /// The value abstract protected void SetBitAt_Internal(uint bitIndex, bool value); /// Sync again every serializedProperty protected void ResyncSerialization() { foreach (var property in m_SerializedProperties) property.serializedObject.ApplyModifiedProperties(); Update(); } /// Sync the reflected value with target value change public void Update() { foreach (var property in m_SerializedProperties) property.serializedObject.Update(); m_SerializedProperty.serializedObject.Update(); } } /// SerializedBitArray spetialized for 8bit capacity public sealed class SerializedBitArray8 : SerializedBitArray { SerializedProperty m_Data; /// Constructor /// The SerializedProperty public SerializedBitArray8(SerializedProperty serializedProperty) : base(serializedProperty, 8u) => m_Data = m_SerializedProperty.FindPropertyRelative("data"); /// Say if the properties have differente values /// The index /// True: properties have different value protected override bool HasBitMultipleDifferentValue_Internal(uint bitIndex) => (HasMultipleDifferentValuesBitwise(m_Data) & (1 << (int)bitIndex)) != 0; /// Get the value at index /// The index /// Value at the index protected override bool GetBitAt_Internal(uint bitIndex) => BitArrayUtilities.Get8(bitIndex, (byte)m_Data.intValue); /// Set the bit at given index /// The index /// The value protected override void SetBitAt_Internal(uint bitIndex, bool value) { foreach (var property in GetOrInitializeSerializedProperties()) { byte versionedData = (byte)property.FindPropertyRelative("data").intValue; BitArrayUtilities.Set8(bitIndex, ref versionedData, value); property.FindPropertyRelative("data").intValue = versionedData; } ResyncSerialization(); } } /// SerializedBitArray spetialized for 16bit capacity public sealed class SerializedBitArray16 : SerializedBitArray { SerializedProperty m_Data; /// Constructor /// The SerializedProperty public SerializedBitArray16(SerializedProperty serializedProperty) : base(serializedProperty, 16u) => m_Data = m_SerializedProperty.FindPropertyRelative("data"); /// Say if the properties have differente values /// The index /// True: properties have different value protected override bool HasBitMultipleDifferentValue_Internal(uint bitIndex) => (HasMultipleDifferentValuesBitwise(m_Data) & (1 << (int)bitIndex)) != 0; /// Get the value at index /// The index /// Value at the index protected override bool GetBitAt_Internal(uint bitIndex) => BitArrayUtilities.Get16(bitIndex, (ushort)m_Data.intValue); /// Set the bit at given index /// The index /// The value protected override void SetBitAt_Internal(uint bitIndex, bool value) { foreach (var property in GetOrInitializeSerializedProperties()) { ushort versionedData = (ushort)property.FindPropertyRelative("data").intValue; BitArrayUtilities.Set16(bitIndex, ref versionedData, value); property.FindPropertyRelative("data").intValue = versionedData; } ResyncSerialization(); } } /// SerializedBitArray spetialized for 32bit capacity public sealed class SerializedBitArray32 : SerializedBitArray { SerializedProperty m_Data; /// Constructor /// The SerializedProperty public SerializedBitArray32(SerializedProperty serializedProperty) : base(serializedProperty, 32u) => m_Data = m_SerializedProperty.FindPropertyRelative("data"); /// Say if the properties have differente values /// The index /// True: properties have different value protected override bool HasBitMultipleDifferentValue_Internal(uint bitIndex) => (HasMultipleDifferentValuesBitwise(m_Data) & (1 << (int)bitIndex)) != 0; /// Get the value at index /// The index /// Value at the index protected override bool GetBitAt_Internal(uint bitIndex) => BitArrayUtilities.Get32(bitIndex, (uint)m_Data.intValue); /// Set the bit at given index /// The index /// The value protected override void SetBitAt_Internal(uint bitIndex, bool value) { foreach (var property in GetOrInitializeSerializedProperties()) { int versionedData = property.FindPropertyRelative("data").intValue; uint trueData; unsafe { trueData = *(uint*)(&versionedData); } BitArrayUtilities.Set32(bitIndex, ref trueData, value); unsafe { versionedData = *(int*)(&trueData); } property.FindPropertyRelative("data").intValue = versionedData; } ResyncSerialization(); } } /// SerializedBitArray spetialized for 64bit capacity public sealed class SerializedBitArray64 : SerializedBitArray { SerializedProperty m_Data; /// Constructor /// The SerializedProperty public SerializedBitArray64(SerializedProperty serializedProperty) : base(serializedProperty, 64u) => m_Data = m_SerializedProperty.FindPropertyRelative("data"); /// Say if the properties have differente values /// The index /// True: properties have different value protected override bool HasBitMultipleDifferentValue_Internal(uint bitIndex) => HasBitMultipleDifferentValue_For64Bits("data", m_Data, bitIndex); /// Get the value at index /// The index /// Value at the index protected override bool GetBitAt_Internal(uint bitIndex) => BitArrayUtilities.Get64(bitIndex, (ulong)m_Data.longValue); /// Set the bit at given index /// The index /// The value protected override void SetBitAt_Internal(uint bitIndex, bool value) { foreach (var property in GetOrInitializeSerializedProperties()) { long versionedData = property.FindPropertyRelative("data").longValue; ulong trueData; unsafe { trueData = *(ulong*)(&versionedData); } BitArrayUtilities.Set64(bitIndex, ref trueData, value); unsafe { versionedData = *(long*)(&trueData); } property.FindPropertyRelative("data").longValue = versionedData; } ResyncSerialization(); } } /// SerializedBitArray spetialized for 128bit capacity public sealed class SerializedBitArray128 : SerializedBitArray { SerializedProperty m_Data1; SerializedProperty m_Data2; /// Constructor /// The SerializedProperty public SerializedBitArray128(SerializedProperty serializedProperty) : base(serializedProperty, 128u) { m_Data1 = m_SerializedProperty.FindPropertyRelative("data1"); m_Data2 = m_SerializedProperty.FindPropertyRelative("data2"); } /// Say if the properties have differente values /// The index /// True: properties have different value protected override bool HasBitMultipleDifferentValue_Internal(uint bitIndex) => bitIndex < 64u ? HasBitMultipleDifferentValue_For64Bits("data1", m_Data1, bitIndex) : HasBitMultipleDifferentValue_For64Bits("data2", m_Data2, bitIndex - 64u); /// Get the value at index /// The index /// Value at the index protected override bool GetBitAt_Internal(uint bitIndex) => BitArrayUtilities.Get128( bitIndex, (ulong)m_SerializedProperty.FindPropertyRelative("data1").longValue, (ulong)m_SerializedProperty.FindPropertyRelative("data2").longValue); /// Set the bit at given index /// The index /// The value protected override void SetBitAt_Internal(uint bitIndex, bool value) { foreach (var property in GetOrInitializeSerializedProperties()) { long versionedData1 = property.FindPropertyRelative("data1").longValue; long versionedData2 = property.FindPropertyRelative("data2").longValue; ulong trueData1; ulong trueData2; unsafe { trueData1 = *(ulong*)(&versionedData1); trueData2 = *(ulong*)(&versionedData2); } BitArrayUtilities.Set128(bitIndex, ref trueData1, ref trueData2, value); unsafe { versionedData1 = *(long*)(&trueData1); versionedData2 = *(long*)(&trueData2); } property.FindPropertyRelative("data1").longValue = versionedData1; property.FindPropertyRelative("data2").longValue = versionedData2; } ResyncSerialization(); } } /// SerializedBitArray spetialized for 256bit capacity public sealed class SerializedBitArray256 : SerializedBitArray { SerializedProperty m_Data1; SerializedProperty m_Data2; SerializedProperty m_Data3; SerializedProperty m_Data4; /// Constructor /// The SerializedProperty public SerializedBitArray256(SerializedProperty serializedProperty) : base(serializedProperty, 128u) { m_Data1 = m_SerializedProperty.FindPropertyRelative("data1"); m_Data2 = m_SerializedProperty.FindPropertyRelative("data2"); m_Data3 = m_SerializedProperty.FindPropertyRelative("data3"); m_Data4 = m_SerializedProperty.FindPropertyRelative("data4"); } /// Say if the properties have differente values /// The index /// True: properties have different value protected override bool HasBitMultipleDifferentValue_Internal(uint bitIndex) => bitIndex < 128u ? bitIndex < 64u ? HasBitMultipleDifferentValue_For64Bits("data1", m_Data1, bitIndex) : HasBitMultipleDifferentValue_For64Bits("data2", m_Data2, bitIndex - 64u) : bitIndex < 192u ? HasBitMultipleDifferentValue_For64Bits("data3", m_Data3, bitIndex - 128u) : HasBitMultipleDifferentValue_For64Bits("data4", m_Data4, bitIndex - 192u); /// Get the value at index /// The index /// Value at the index protected override bool GetBitAt_Internal(uint bitIndex) => BitArrayUtilities.Get256( bitIndex, (ulong)m_SerializedProperty.FindPropertyRelative("data1").longValue, (ulong)m_SerializedProperty.FindPropertyRelative("data2").longValue, (ulong)m_SerializedProperty.FindPropertyRelative("data3").longValue, (ulong)m_SerializedProperty.FindPropertyRelative("data4").longValue); /// Set the bit at given index /// The index /// The value protected override void SetBitAt_Internal(uint bitIndex, bool value) { foreach (var property in GetOrInitializeSerializedProperties()) { long versionedData1 = property.FindPropertyRelative("data1").longValue; long versionedData2 = property.FindPropertyRelative("data2").longValue; long versionedData3 = property.FindPropertyRelative("data3").longValue; long versionedData4 = property.FindPropertyRelative("data4").longValue; ulong trueData1; ulong trueData2; ulong trueData3; ulong trueData4; unsafe { trueData1 = *(ulong*)(&versionedData1); trueData2 = *(ulong*)(&versionedData2); trueData3 = *(ulong*)(&versionedData3); trueData4 = *(ulong*)(&versionedData4); } BitArrayUtilities.Set256(bitIndex, ref trueData1, ref trueData2, ref trueData3, ref trueData4, value); unsafe { versionedData1 = *(long*)(&trueData1); versionedData2 = *(long*)(&trueData2); versionedData3 = *(long*)(&trueData3); versionedData4 = *(long*)(&trueData4); } property.FindPropertyRelative("data1").longValue = versionedData1; property.FindPropertyRelative("data2").longValue = versionedData2; property.FindPropertyRelative("data3").longValue = versionedData3; property.FindPropertyRelative("data4").longValue = versionedData4; } ResyncSerialization(); } } }