public unsafe void Utilities_CanCompareMemoryBitRegions() { using (var array1 = new NativeArray <byte>(6, Allocator.Temp)) using (var array2 = new NativeArray <byte>(6, Allocator.Temp)) { var array1Ptr = (byte *)array1.GetUnsafePtr(); var array2Ptr = (byte *)array2.GetUnsafePtr(); MemoryHelpers.SetBitsInBuffer(array1Ptr, 0, 2, 1, true); Assert.That(MemoryHelpers.MemCmpBitRegion(array1Ptr, array2Ptr, 2, 1), Is.False); MemoryHelpers.SetBitsInBuffer(array2Ptr, 0, 2, 1, true); Assert.That(MemoryHelpers.MemCmpBitRegion(array1Ptr, array2Ptr, 2, 1), Is.True); UnsafeUtility.MemClear(array1Ptr, 6); UnsafeUtility.MemClear(array2Ptr, 6); MemoryHelpers.SetBitsInBuffer(array1Ptr, 0, 5, 24, true); Assert.That(MemoryHelpers.MemCmpBitRegion(array1Ptr, array2Ptr, 5, 24), Is.False); MemoryHelpers.SetBitsInBuffer(array2Ptr, 0, 5, 24, true); Assert.That(MemoryHelpers.MemCmpBitRegion(array1Ptr, array2Ptr, 5, 24), Is.True); } }
/// <summary> /// Compare the control's stored state in <paramref name="firstStatePtr"/> to <paramref name="secondStatePtr"/>. /// </summary> /// <param name="firstStatePtr">Memory containing the control's <see cref="InputControl.stateBlock"/>.</param> /// <param name="secondStatePtr">Memory containing the control's <see cref="InputControl.stateBlock"/></param> /// <param name="maskPtr">Optional mask. If supplied, it will be used to mask the comparison between /// <paramref name="firstStatePtr"/> and <paramref name="secondStatePtr"/> such that any bit not set in the /// mask will be ignored even if different between the two states. This can be used, for example, to ignore /// noise in the state (<see cref="InputControl.noiseMaskPtr"/>).</param> /// <returns>True if the state is equivalent in both memory buffers.</returns> /// <remarks> /// Unlike <see cref="InputControl.CompareValue"/>, this method only compares raw memory state. If used on a stick, for example, /// it may mean that this method returns false for two stick values that would compare equal using <see cref="CompareValue"/> /// (e.g. if both stick values fall below the deadzone). /// </remarks> /// <seealso cref="InputControl.CompareValue"/> public static unsafe bool CompareState(this InputControl control, void *firstStatePtr, void *secondStatePtr, void *maskPtr = null) { ////REVIEW: for compound controls, do we want to go check leaves so as to not pick up on non-control noise in the state? //// e.g. from HID input reports; or should we just leave that to maskPtr? var firstPtr = (byte *)firstStatePtr + (int)control.m_StateBlock.byteOffset; var secondPtr = (byte *)secondStatePtr + (int)control.m_StateBlock.byteOffset; var mask = maskPtr != null ? (byte *)maskPtr + (int)control.m_StateBlock.byteOffset : null; if (control.m_StateBlock.sizeInBits == 1) { // If we have a mask and the bit is set in the mask, the control is to be ignored // and thus we consider it at default value. if (mask != null && MemoryHelpers.ReadSingleBit(mask, control.m_StateBlock.bitOffset)) { return(true); } return(MemoryHelpers.ReadSingleBit(secondPtr, control.m_StateBlock.bitOffset) == MemoryHelpers.ReadSingleBit(firstPtr, control.m_StateBlock.bitOffset)); } return(MemoryHelpers.MemCmpBitRegion(firstPtr, secondPtr, control.m_StateBlock.bitOffset, control.m_StateBlock.sizeInBits, mask)); }
////TODO: expose the checks for default state /// <summary> /// Check if the given state corresponds to the default state of the control/device. /// </summary> /// <param name="statePtr">Pointer to a state buffer or null to use <see cref="currentStatePtr"/>.</param> /// <param name="maskPtr">If not null, any bits set to true in the buffer will be ignored. This can be used /// to mask out noise.</param> /// <returns>True if the control/device is in its default state.</returns> /// <remarks> /// Note that default does not equate all zeroes. Stick axes, for example, that are stored as unsigned byte /// values will have their resting position at 127 and not at 0. This is why we explicitly store default /// state in a memory buffer instead of assuming zeroes. /// </remarks> /// <seealso cref="InputStateBuffers.defaultStateBuffer"/> internal unsafe bool CheckStateIsAtDefault(void *statePtr = null, void *maskPtr = null) { ////REVIEW: for compound controls, do we want to go check leaves so as to not pick up on non-control noise in the state? //// e.g. from HID input reports if (statePtr == null) { statePtr = currentStatePtr; } var defaultValuePtr = (byte *)defaultStatePtr + (int)m_StateBlock.byteOffset; var valuePtr = (byte *)statePtr + (int)m_StateBlock.byteOffset; if (m_StateBlock.sizeInBits == 1) { // If we have a mask and the bit is set in the mask, the control is to be ignored // and thus we consider it at default value. if (maskPtr != null && MemoryHelpers.ReadSingleBit(maskPtr, m_StateBlock.bitOffset)) { return(true); } return(MemoryHelpers.ReadSingleBit(valuePtr, m_StateBlock.bitOffset) == MemoryHelpers.ReadSingleBit(defaultValuePtr, m_StateBlock.bitOffset)); } return(MemoryHelpers.MemCmpBitRegion(defaultValuePtr, valuePtr, m_StateBlock.bitOffset, m_StateBlock.sizeInBits)); }
public unsafe void Utilities_CanCompareMemoryBitRegions_AndIgnoreBitsUsingMask() { using (var array1 = new NativeArray <byte>(8, Allocator.Temp)) using (var array2 = new NativeArray <byte>(8, Allocator.Temp)) using (var mask = new NativeArray <byte>(8, Allocator.Temp)) { var array1Ptr = (byte *)array1.GetUnsafePtr(); var array2Ptr = (byte *)array2.GetUnsafePtr(); var maskPtr = (byte *)mask.GetUnsafePtr(); // Set bit #2 in array1. MemoryHelpers.SetBitsInBuffer(array1Ptr, 0, 2, 1, true); Assert.That(MemoryHelpers.MemCmpBitRegion(array1Ptr, array2Ptr, 2, 1, maskPtr), Is.True); // Set bit #2 in mask. MemoryHelpers.SetBitsInBuffer(maskPtr, 0, 2, 1, true); Assert.That(MemoryHelpers.MemCmpBitRegion(array1Ptr, array2Ptr, 2, 1, maskPtr), Is.False); UnsafeUtility.MemClear(array1Ptr, 8); UnsafeUtility.MemClear(array2Ptr, 8); UnsafeUtility.MemClear(maskPtr, 8); // Set 24 bits in array1 starting at bit #5. MemoryHelpers.SetBitsInBuffer(array1Ptr, 0, 5, 24, true); Assert.That(MemoryHelpers.MemCmpBitRegion(array1Ptr, array2Ptr, 5, 24, maskPtr), Is.True); // Set 20 bits in mask starting at bit #5. MemoryHelpers.SetBitsInBuffer(maskPtr, 0, 5, 20, true); // Set 24 bits in array2 starting at bit #5. MemoryHelpers.SetBitsInBuffer(array2Ptr, 0, 5, 24, true); Assert.That(MemoryHelpers.MemCmpBitRegion(array1Ptr, array2Ptr, 5, 24, maskPtr), Is.True); // Set 21 bits in mask starting at bit #7. MemoryHelpers.SetBitsInBuffer(maskPtr, 0, 7, 21, true); Assert.That(MemoryHelpers.MemCmpBitRegion(array1Ptr, array2Ptr, 5, 24, maskPtr), Is.True); } }
////TODO: pass state ptr *NOT* value ptr (it's confusing) // NOTE: The given argument should point directly to the value *not* to the // base state to which the state block offset has to be added. internal unsafe bool CheckStateIsAtDefault(IntPtr valuePtr = new IntPtr()) { ////REVIEW: for compound controls, do we want to go check leaves so as to not pick up on non-control noise in the state? //// e.g. from HID input reports var defaultPtr = new IntPtr((byte *)defaultStatePtr.ToPointer() + (int)m_StateBlock.byteOffset); if (valuePtr == IntPtr.Zero) { valuePtr = new IntPtr(currentStatePtr.ToInt64() + (int)m_StateBlock.byteOffset); } if (m_StateBlock.sizeInBits == 1) { return(MemoryHelpers.ReadSingleBit(valuePtr, m_StateBlock.bitOffset) == MemoryHelpers.ReadSingleBit(defaultPtr, m_StateBlock.bitOffset)); } return(MemoryHelpers.MemCmpBitRegion(defaultPtr.ToPointer(), valuePtr.ToPointer(), m_StateBlock.bitOffset, m_StateBlock.sizeInBits)); }