| using System; |
| using UnityEngine.InputSystem.Utilities; |
| using Unity.Collections; |
| using Unity.Collections.LowLevel.Unsafe; |
|
|
| |
| |
|
|
| namespace UnityEngine.InputSystem.LowLevel |
| { |
| |
| |
| |
| |
| |
| internal unsafe struct InputStateBuffers |
| { |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
|
|
| public uint sizePerBuffer; |
| public uint totalSize; |
|
|
| |
| |
| |
| public void* defaultStateBuffer; |
|
|
| |
| |
| |
| public void* noiseMaskBuffer; |
|
|
| |
| |
| |
| public void* resetMaskBuffer; |
|
|
| |
| |
| private void* m_AllBuffers; |
|
|
| |
| [Serializable] |
| internal struct DoubleBuffers |
| { |
| |
| |
| |
| |
| |
| public void** deviceToBufferMapping; |
|
|
| public bool valid => deviceToBufferMapping != null; |
|
|
| public void SetFrontBuffer(int deviceIndex, void* ptr) |
| { |
| deviceToBufferMapping[deviceIndex * 2] = ptr; |
| } |
|
|
| public void SetBackBuffer(int deviceIndex, void* ptr) |
| { |
| deviceToBufferMapping[deviceIndex * 2 + 1] = ptr; |
| } |
|
|
| public void* GetFrontBuffer(int deviceIndex) |
| { |
| return deviceToBufferMapping[deviceIndex * 2]; |
| } |
|
|
| public void* GetBackBuffer(int deviceIndex) |
| { |
| return deviceToBufferMapping[deviceIndex * 2 + 1]; |
| } |
|
|
| public void SwapBuffers(int deviceIndex) |
| { |
| |
| |
| if (!valid) |
| return; |
|
|
| var front = GetFrontBuffer(deviceIndex); |
| var back = GetBackBuffer(deviceIndex); |
|
|
| SetFrontBuffer(deviceIndex, back); |
| SetBackBuffer(deviceIndex, front); |
| } |
| } |
|
|
| internal DoubleBuffers m_PlayerStateBuffers; |
|
|
| #if UNITY_EDITOR |
| internal DoubleBuffers m_EditorStateBuffers; |
| #endif |
|
|
| public DoubleBuffers GetDoubleBuffersFor(InputUpdateType updateType) |
| { |
| switch (updateType) |
| { |
| case InputUpdateType.BeforeRender: |
| case InputUpdateType.Fixed: |
| case InputUpdateType.Dynamic: |
| case InputUpdateType.Manual: |
| return m_PlayerStateBuffers; |
| #if UNITY_EDITOR |
| case InputUpdateType.Editor: |
| return m_EditorStateBuffers; |
| #endif |
| } |
|
|
| throw new ArgumentException("Unrecognized InputUpdateType: " + updateType, nameof(updateType)); |
| } |
|
|
| internal static void* s_DefaultStateBuffer; |
| internal static void* s_NoiseMaskBuffer; |
| internal static void* s_ResetMaskBuffer; |
| internal static DoubleBuffers s_CurrentBuffers; |
|
|
| public static void* GetFrontBufferForDevice(int deviceIndex) |
| { |
| return s_CurrentBuffers.GetFrontBuffer(deviceIndex); |
| } |
|
|
| public static void* GetBackBufferForDevice(int deviceIndex) |
| { |
| return s_CurrentBuffers.GetBackBuffer(deviceIndex); |
| } |
|
|
| |
| public static void SwitchTo(InputStateBuffers buffers, InputUpdateType update) |
| { |
| s_CurrentBuffers = buffers.GetDoubleBuffersFor(update); |
| } |
|
|
| |
| |
| |
| public void AllocateAll(InputDevice[] devices, int deviceCount) |
| { |
| sizePerBuffer = ComputeSizeOfSingleStateBuffer(devices, deviceCount); |
| if (sizePerBuffer == 0) |
| return; |
| sizePerBuffer = sizePerBuffer.AlignToMultipleOf(4); |
|
|
| |
| var mappingTableSizePerBuffer = (uint)(deviceCount * sizeof(void*) * 2); |
|
|
| totalSize = 0; |
|
|
| totalSize += sizePerBuffer * 2; |
| totalSize += mappingTableSizePerBuffer; |
|
|
| #if UNITY_EDITOR |
| totalSize += sizePerBuffer * 2; |
| totalSize += mappingTableSizePerBuffer; |
| #endif |
|
|
| |
| totalSize += sizePerBuffer * 3; |
|
|
| |
| m_AllBuffers = UnsafeUtility.Malloc(totalSize, 4, Allocator.Persistent); |
| UnsafeUtility.MemClear(m_AllBuffers, totalSize); |
|
|
| |
| var ptr = (byte*)m_AllBuffers; |
| m_PlayerStateBuffers = |
| SetUpDeviceToBufferMappings(deviceCount, ref ptr, sizePerBuffer, |
| mappingTableSizePerBuffer); |
|
|
| #if UNITY_EDITOR |
| m_EditorStateBuffers = |
| SetUpDeviceToBufferMappings(deviceCount, ref ptr, sizePerBuffer, mappingTableSizePerBuffer); |
| #endif |
|
|
| |
| defaultStateBuffer = ptr; |
| noiseMaskBuffer = ptr + sizePerBuffer; |
| resetMaskBuffer = ptr + sizePerBuffer * 2; |
| } |
|
|
| private static DoubleBuffers SetUpDeviceToBufferMappings(int deviceCount, ref byte* bufferPtr, uint sizePerBuffer, uint mappingTableSizePerBuffer) |
| { |
| var front = bufferPtr; |
| var back = bufferPtr + sizePerBuffer; |
| var mappings = (void**)(bufferPtr + sizePerBuffer * 2); |
| bufferPtr += sizePerBuffer * 2 + mappingTableSizePerBuffer; |
|
|
| var buffers = new DoubleBuffers {deviceToBufferMapping = mappings}; |
|
|
| for (var i = 0; i < deviceCount; ++i) |
| { |
| var deviceIndex = i; |
| buffers.SetFrontBuffer(deviceIndex, front); |
| buffers.SetBackBuffer(deviceIndex, back); |
| } |
|
|
| return buffers; |
| } |
|
|
| public void FreeAll() |
| { |
| if (m_AllBuffers != null) |
| { |
| UnsafeUtility.Free(m_AllBuffers, Allocator.Persistent); |
| m_AllBuffers = null; |
| } |
|
|
| m_PlayerStateBuffers = new DoubleBuffers(); |
|
|
| #if UNITY_EDITOR |
| m_EditorStateBuffers = new DoubleBuffers(); |
| #endif |
|
|
| s_CurrentBuffers = new DoubleBuffers(); |
|
|
| if (s_DefaultStateBuffer == defaultStateBuffer) |
| s_DefaultStateBuffer = null; |
|
|
| defaultStateBuffer = null; |
|
|
| if (s_NoiseMaskBuffer == noiseMaskBuffer) |
| s_NoiseMaskBuffer = null; |
|
|
| if (s_ResetMaskBuffer == resetMaskBuffer) |
| s_ResetMaskBuffer = null; |
|
|
| noiseMaskBuffer = null; |
| resetMaskBuffer = null; |
|
|
| totalSize = 0; |
| sizePerBuffer = 0; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public void MigrateAll(InputDevice[] devices, int deviceCount, InputStateBuffers oldBuffers) |
| { |
| |
| if (oldBuffers.totalSize > 0) |
| { |
| MigrateDoubleBuffer(m_PlayerStateBuffers, devices, deviceCount, oldBuffers.m_PlayerStateBuffers); |
|
|
| #if UNITY_EDITOR |
| MigrateDoubleBuffer(m_EditorStateBuffers, devices, deviceCount, oldBuffers.m_EditorStateBuffers); |
| #endif |
|
|
| MigrateSingleBuffer(defaultStateBuffer, devices, deviceCount, oldBuffers.defaultStateBuffer); |
| MigrateSingleBuffer(noiseMaskBuffer, devices, deviceCount, oldBuffers.noiseMaskBuffer); |
| MigrateSingleBuffer(resetMaskBuffer, devices, deviceCount, oldBuffers.resetMaskBuffer); |
| } |
|
|
| |
| |
| var newOffset = 0u; |
| for (var i = 0; i < deviceCount; ++i) |
| { |
| var device = devices[i]; |
| var oldOffset = device.m_StateBlock.byteOffset; |
|
|
| if (oldOffset == InputStateBlock.InvalidOffset) |
| { |
| |
| device.m_StateBlock.byteOffset = 0; |
| if (newOffset != 0) |
| device.BakeOffsetIntoStateBlockRecursive(newOffset); |
| } |
| else |
| { |
| |
| |
| var delta = newOffset - oldOffset; |
| if (delta != 0) |
| device.BakeOffsetIntoStateBlockRecursive(delta); |
| } |
|
|
| Debug.Assert(device.m_StateBlock.byteOffset == newOffset, "Device state offset not set correctly"); |
|
|
| newOffset = NextDeviceOffset(newOffset, device); |
| } |
| } |
|
|
| private static void MigrateDoubleBuffer(DoubleBuffers newBuffer, InputDevice[] devices, int deviceCount, DoubleBuffers oldBuffer) |
| { |
| |
| if (!newBuffer.valid) |
| return; |
|
|
| |
| if (!oldBuffer.valid) |
| return; |
|
|
| |
| var newStateBlockOffset = 0u; |
| for (var i = 0; i < deviceCount; ++i) |
| { |
| var device = devices[i]; |
|
|
| |
| |
| |
| if (device.m_StateBlock.byteOffset == InputStateBlock.InvalidOffset) |
| { |
| #if DEVELOPMENT_BUILD || UNITY_EDITOR |
| for (var n = i + 1; n < deviceCount; ++n) |
| Debug.Assert(devices[n].m_StateBlock.byteOffset == InputStateBlock.InvalidOffset, |
| "New devices must be appended to the array; found an old device coming in the array after a newly added device"); |
| #endif |
| break; |
| } |
|
|
| var oldDeviceIndex = device.m_DeviceIndex; |
| var newDeviceIndex = i; |
| var numBytes = device.m_StateBlock.alignedSizeInBytes; |
|
|
| var oldFrontPtr = (byte*)oldBuffer.GetFrontBuffer(oldDeviceIndex) + (int)device.m_StateBlock.byteOffset; |
| var oldBackPtr = (byte*)oldBuffer.GetBackBuffer(oldDeviceIndex) + (int)device.m_StateBlock.byteOffset; |
|
|
| var newFrontPtr = (byte*)newBuffer.GetFrontBuffer(newDeviceIndex) + (int)newStateBlockOffset; |
| var newBackPtr = (byte*)newBuffer.GetBackBuffer(newDeviceIndex) + (int)newStateBlockOffset; |
|
|
| |
| UnsafeUtility.MemCpy(newFrontPtr, oldFrontPtr, numBytes); |
| UnsafeUtility.MemCpy(newBackPtr, oldBackPtr, numBytes); |
|
|
| newStateBlockOffset = NextDeviceOffset(newStateBlockOffset, device); |
| } |
| } |
|
|
| private static void MigrateSingleBuffer(void* newBuffer, InputDevice[] devices, int deviceCount, void* oldBuffer) |
| { |
| |
| var newDeviceCount = deviceCount; |
| var newStateBlockOffset = 0u; |
| for (var i = 0; i < newDeviceCount; ++i) |
| { |
| var device = devices[i]; |
|
|
| |
| if (device.m_StateBlock.byteOffset == InputStateBlock.InvalidOffset) |
| break; |
|
|
| var numBytes = device.m_StateBlock.alignedSizeInBytes; |
| var oldStatePtr = (byte*)oldBuffer + (int)device.m_StateBlock.byteOffset; |
| var newStatePtr = (byte*)newBuffer + (int)newStateBlockOffset; |
|
|
| UnsafeUtility.MemCpy(newStatePtr, oldStatePtr, numBytes); |
|
|
| newStateBlockOffset = NextDeviceOffset(newStateBlockOffset, device); |
| } |
| } |
|
|
| private static uint ComputeSizeOfSingleStateBuffer(InputDevice[] devices, int deviceCount) |
| { |
| var sizeInBytes = 0u; |
| for (var i = 0; i < deviceCount; ++i) |
| sizeInBytes = NextDeviceOffset(sizeInBytes, devices[i]); |
| return sizeInBytes; |
| } |
|
|
| private static uint NextDeviceOffset(uint currentOffset, InputDevice device) |
| { |
| var sizeOfDevice = device.m_StateBlock.alignedSizeInBytes; |
| if (sizeOfDevice == 0) |
| throw new ArgumentException($"Device '{device}' has a zero-size state buffer", nameof(device)); |
| return currentOffset + sizeOfDevice.AlignToMultipleOf(4); |
| } |
| } |
| } |
|
|