InputStateBuffers.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. using System;
  2. using UnityEngine.InputSystem.Utilities;
  3. using Unity.Collections;
  4. using Unity.Collections.LowLevel.Unsafe;
  5. ////REVIEW: Can we change this into a setup where the buffering depth isn't fixed to 2 but rather
  6. //// can be set on a per device basis?
  7. namespace UnityEngine.InputSystem.LowLevel
  8. {
  9. // The raw memory blocks which are indexed by InputStateBlocks.
  10. //
  11. // Internally, we perform only a single combined unmanaged allocation for all state
  12. // buffers needed by the system. Externally, we expose them as if they are each separate
  13. // buffers.
  14. internal unsafe struct InputStateBuffers
  15. {
  16. // State buffers are set up in a double buffering scheme where the "back buffer"
  17. // represents the previous state of devices and the "front buffer" represents
  18. // the current state.
  19. //
  20. // Edit mode and play mode each get their own double buffering. Updates to them
  21. // are tied to focus and only one mode will actually receive state events while the
  22. // other mode is dormant. In the player, we only get play mode buffers, of course.
  23. ////TODO: need to clear the current buffers when switching between edit and play mode
  24. //// (i.e. if you click an editor window while in play mode, the play mode
  25. //// device states will all go back to default)
  26. //// actually, if we really reset on mode change, can't we just keep a single set buffers?
  27. public uint sizePerBuffer;
  28. public uint totalSize;
  29. /// <summary>
  30. /// Buffer that has state for each device initialized with default values.
  31. /// </summary>
  32. public void* defaultStateBuffer;
  33. /// <summary>
  34. /// Buffer that contains bitflags for noisy and non-noisy controls, to identify significant device changes.
  35. /// </summary>
  36. public void* noiseMaskBuffer;
  37. // Secretly we perform only a single allocation.
  38. // This allocation also contains the device-to-state mappings.
  39. private void* m_AllBuffers;
  40. // Contains information about a double buffer setup.
  41. [Serializable]
  42. internal struct DoubleBuffers
  43. {
  44. ////REVIEW: store timestamps along with each device-to-buffer mapping?
  45. // An array of pointers that maps devices to their respective
  46. // front and back buffer. Mapping is [deviceIndex*2] is front
  47. // buffer and [deviceIndex*2+1] is back buffer. Each device
  48. // has its buffers swapped individually with SwapDeviceBuffers().
  49. public void** deviceToBufferMapping;
  50. public bool valid => deviceToBufferMapping != null;
  51. public void SetFrontBuffer(int deviceIndex, void* ptr)
  52. {
  53. deviceToBufferMapping[deviceIndex * 2] = ptr;
  54. }
  55. public void SetBackBuffer(int deviceIndex, void* ptr)
  56. {
  57. deviceToBufferMapping[deviceIndex * 2 + 1] = ptr;
  58. }
  59. public void* GetFrontBuffer(int deviceIndex)
  60. {
  61. return deviceToBufferMapping[deviceIndex * 2];
  62. }
  63. public void* GetBackBuffer(int deviceIndex)
  64. {
  65. return deviceToBufferMapping[deviceIndex * 2 + 1];
  66. }
  67. public void SwapBuffers(int deviceIndex)
  68. {
  69. // Ignore if the double buffer set has not been initialized.
  70. // Means the respective update type is disabled.
  71. if (!valid)
  72. return;
  73. var front = GetFrontBuffer(deviceIndex);
  74. var back = GetBackBuffer(deviceIndex);
  75. SetFrontBuffer(deviceIndex, back);
  76. SetBackBuffer(deviceIndex, front);
  77. }
  78. }
  79. internal DoubleBuffers m_PlayerStateBuffers;
  80. #if UNITY_EDITOR
  81. internal DoubleBuffers m_EditorStateBuffers;
  82. #endif
  83. public DoubleBuffers GetDoubleBuffersFor(InputUpdateType updateType)
  84. {
  85. switch (updateType)
  86. {
  87. case InputUpdateType.BeforeRender:
  88. case InputUpdateType.Fixed:
  89. case InputUpdateType.Dynamic:
  90. case InputUpdateType.Manual:
  91. return m_PlayerStateBuffers;
  92. #if UNITY_EDITOR
  93. case InputUpdateType.Editor:
  94. return m_EditorStateBuffers;
  95. #endif
  96. }
  97. throw new ArgumentException("Unrecognized InputUpdateType: " + updateType, nameof(updateType));
  98. }
  99. internal static void* s_DefaultStateBuffer;
  100. internal static void* s_NoiseMaskBuffer;
  101. internal static DoubleBuffers s_CurrentBuffers;
  102. public static void* GetFrontBufferForDevice(int deviceIndex)
  103. {
  104. return s_CurrentBuffers.GetFrontBuffer(deviceIndex);
  105. }
  106. public static void* GetBackBufferForDevice(int deviceIndex)
  107. {
  108. return s_CurrentBuffers.GetBackBuffer(deviceIndex);
  109. }
  110. // Switch the current set of buffers used by the system.
  111. public static void SwitchTo(InputStateBuffers buffers, InputUpdateType update)
  112. {
  113. s_CurrentBuffers = buffers.GetDoubleBuffersFor(update);
  114. }
  115. // Allocates all buffers to serve the given updates and comes up with a spot
  116. // for the state block of each device. Returns the new state blocks for the
  117. // devices (it will *NOT* install them on the devices).
  118. public void AllocateAll(InputDevice[] devices, int deviceCount)
  119. {
  120. sizePerBuffer = ComputeSizeOfSingleStateBuffer(devices, deviceCount);
  121. if (sizePerBuffer == 0)
  122. return;
  123. sizePerBuffer = sizePerBuffer.AlignToMultipleOf(4);
  124. // Determine how much memory we need.
  125. var mappingTableSizePerBuffer = (uint)(deviceCount * sizeof(void*) * 2);
  126. totalSize = 0;
  127. totalSize += sizePerBuffer * 2;
  128. totalSize += mappingTableSizePerBuffer;
  129. #if UNITY_EDITOR
  130. totalSize += sizePerBuffer * 2;
  131. totalSize += mappingTableSizePerBuffer;
  132. #endif
  133. // Plus 2 more buffers (1 for default states, and one for noise masks).
  134. totalSize += sizePerBuffer * 2;
  135. // Allocate.
  136. m_AllBuffers = UnsafeUtility.Malloc(totalSize, 4, Allocator.Persistent);
  137. UnsafeUtility.MemClear(m_AllBuffers, totalSize);
  138. // Set up device to buffer mappings.
  139. var ptr = (byte*)m_AllBuffers;
  140. m_PlayerStateBuffers =
  141. SetUpDeviceToBufferMappings(deviceCount, ref ptr, sizePerBuffer,
  142. mappingTableSizePerBuffer);
  143. #if UNITY_EDITOR
  144. m_EditorStateBuffers =
  145. SetUpDeviceToBufferMappings(deviceCount, ref ptr, sizePerBuffer, mappingTableSizePerBuffer);
  146. #endif
  147. // Default state and noise filter buffers go last.
  148. defaultStateBuffer = ptr;
  149. noiseMaskBuffer = ptr + sizePerBuffer;
  150. }
  151. private static DoubleBuffers SetUpDeviceToBufferMappings(int deviceCount, ref byte* bufferPtr, uint sizePerBuffer, uint mappingTableSizePerBuffer)
  152. {
  153. var front = bufferPtr;
  154. var back = bufferPtr + sizePerBuffer;
  155. var mappings = (void**)(bufferPtr + sizePerBuffer * 2); // Put mapping table at end.
  156. bufferPtr += sizePerBuffer * 2 + mappingTableSizePerBuffer;
  157. var buffers = new DoubleBuffers {deviceToBufferMapping = mappings};
  158. for (var i = 0; i < deviceCount; ++i)
  159. {
  160. var deviceIndex = i;
  161. buffers.SetFrontBuffer(deviceIndex, front);
  162. buffers.SetBackBuffer(deviceIndex, back);
  163. }
  164. return buffers;
  165. }
  166. public void FreeAll()
  167. {
  168. if (m_AllBuffers != null)
  169. {
  170. UnsafeUtility.Free(m_AllBuffers, Allocator.Persistent);
  171. m_AllBuffers = null;
  172. }
  173. m_PlayerStateBuffers = new DoubleBuffers();
  174. #if UNITY_EDITOR
  175. m_EditorStateBuffers = new DoubleBuffers();
  176. #endif
  177. s_CurrentBuffers = new DoubleBuffers();
  178. if (s_DefaultStateBuffer == defaultStateBuffer)
  179. s_DefaultStateBuffer = null;
  180. defaultStateBuffer = null;
  181. if (s_NoiseMaskBuffer == noiseMaskBuffer)
  182. s_NoiseMaskBuffer = null;
  183. noiseMaskBuffer = null;
  184. totalSize = 0;
  185. sizePerBuffer = 0;
  186. }
  187. // Migrate state data for all devices from a previous set of buffers to the current set of buffers.
  188. // Copies all state from their old locations to their new locations and bakes the new offsets into
  189. // the control hierarchies of the given devices.
  190. // NOTE: When having oldBuffers, this method only works properly if the only alteration compared to the
  191. // new buffers is that either devices have been removed or devices have been added. Cannot be
  192. // a mix of the two. Also, new devices MUST be added to the end and cannot be inserted in the middle.
  193. // NOTE: Also, state formats MUST not change from before. A device that has changed its format must
  194. // be treated as a newly device that didn't exist before.
  195. public void MigrateAll(InputDevice[] devices, int deviceCount, InputStateBuffers oldBuffers)
  196. {
  197. // If we have old data, perform migration.
  198. if (oldBuffers.totalSize > 0)
  199. {
  200. MigrateDoubleBuffer(m_PlayerStateBuffers, devices, deviceCount, oldBuffers.m_PlayerStateBuffers);
  201. #if UNITY_EDITOR
  202. MigrateDoubleBuffer(m_EditorStateBuffers, devices, deviceCount, oldBuffers.m_EditorStateBuffers);
  203. #endif
  204. MigrateSingleBuffer(defaultStateBuffer, devices, deviceCount, oldBuffers.defaultStateBuffer);
  205. MigrateSingleBuffer(noiseMaskBuffer, devices, deviceCount, oldBuffers.noiseMaskBuffer);
  206. }
  207. // Assign state blocks. This is where devices will receive their updates state offsets. Up
  208. // until now we've left any previous m_StateBlocks alone.
  209. var newOffset = 0u;
  210. for (var i = 0; i < deviceCount; ++i)
  211. {
  212. var device = devices[i];
  213. var oldOffset = device.m_StateBlock.byteOffset;
  214. if (oldOffset == InputStateBlock.InvalidOffset)
  215. {
  216. // Device is new and has no offset yet baked into it.
  217. device.m_StateBlock.byteOffset = 0;
  218. if (newOffset != 0)
  219. device.BakeOffsetIntoStateBlockRecursive(newOffset);
  220. }
  221. else
  222. {
  223. // Device is not new and still has its old offset baked into it. We could first unbake the old offset
  224. // and then bake the new one but instead just bake a relative offset.
  225. var delta = newOffset - oldOffset;
  226. if (delta != 0)
  227. device.BakeOffsetIntoStateBlockRecursive(delta);
  228. }
  229. Debug.Assert(device.m_StateBlock.byteOffset == newOffset, "Device state offset not set correctly");
  230. newOffset = NextDeviceOffset(newOffset, device);
  231. }
  232. }
  233. private static void MigrateDoubleBuffer(DoubleBuffers newBuffer, InputDevice[] devices, int deviceCount, DoubleBuffers oldBuffer)
  234. {
  235. // Nothing to migrate if we no longer keep a buffer of the corresponding type.
  236. if (!newBuffer.valid)
  237. return;
  238. // We do the same if we don't had a corresponding buffer before.
  239. if (!oldBuffer.valid)
  240. return;
  241. // Migrate every device that has allocated state blocks.
  242. var newStateBlockOffset = 0u;
  243. for (var i = 0; i < deviceCount; ++i)
  244. {
  245. var device = devices[i];
  246. // Stop as soon as we're hitting a new device. Newly added devices *must* be *appended* to the
  247. // array as otherwise our computing of offsets into the old buffer may be wrong.
  248. // NOTE: This also means that device indices of
  249. if (device.m_StateBlock.byteOffset == InputStateBlock.InvalidOffset)
  250. {
  251. #if DEVELOPMENT_BUILD || UNITY_EDITOR
  252. for (var n = i + 1; n < deviceCount; ++n)
  253. Debug.Assert(devices[n].m_StateBlock.byteOffset == InputStateBlock.InvalidOffset,
  254. "New devices must be appended to the array; found an old device coming in the array after a newly added device");
  255. #endif
  256. break;
  257. }
  258. var oldDeviceIndex = device.m_DeviceIndex;
  259. var newDeviceIndex = i;
  260. var numBytes = device.m_StateBlock.alignedSizeInBytes;
  261. var oldFrontPtr = (byte*)oldBuffer.GetFrontBuffer(oldDeviceIndex) + (int)device.m_StateBlock.byteOffset; // m_StateBlock still refers to oldBuffer.
  262. var oldBackPtr = (byte*)oldBuffer.GetBackBuffer(oldDeviceIndex) + (int)device.m_StateBlock.byteOffset;
  263. var newFrontPtr = (byte*)newBuffer.GetFrontBuffer(newDeviceIndex) + (int)newStateBlockOffset;
  264. var newBackPtr = (byte*)newBuffer.GetBackBuffer(newDeviceIndex) + (int)newStateBlockOffset;
  265. // Copy state.
  266. UnsafeUtility.MemCpy(newFrontPtr, oldFrontPtr, numBytes);
  267. UnsafeUtility.MemCpy(newBackPtr, oldBackPtr, numBytes);
  268. newStateBlockOffset = NextDeviceOffset(newStateBlockOffset, device);
  269. }
  270. }
  271. private static void MigrateSingleBuffer(void* newBuffer, InputDevice[] devices, int deviceCount, void* oldBuffer)
  272. {
  273. // Migrate every device that has allocated state blocks.
  274. var newDeviceCount = deviceCount;
  275. var newStateBlockOffset = 0u;
  276. for (var i = 0; i < newDeviceCount; ++i)
  277. {
  278. var device = devices[i];
  279. // Stop if we've reached newly added devices.
  280. if (device.m_StateBlock.byteOffset == InputStateBlock.InvalidOffset)
  281. break;
  282. var numBytes = device.m_StateBlock.alignedSizeInBytes;
  283. var oldStatePtr = (byte*)oldBuffer + (int)device.m_StateBlock.byteOffset;
  284. var newStatePtr = (byte*)newBuffer + (int)newStateBlockOffset;
  285. UnsafeUtility.MemCpy(newStatePtr, oldStatePtr, numBytes);
  286. newStateBlockOffset = NextDeviceOffset(newStateBlockOffset, device);
  287. }
  288. }
  289. private static uint ComputeSizeOfSingleStateBuffer(InputDevice[] devices, int deviceCount)
  290. {
  291. var sizeInBytes = 0u;
  292. for (var i = 0; i < deviceCount; ++i)
  293. sizeInBytes = NextDeviceOffset(sizeInBytes, devices[i]);
  294. return sizeInBytes;
  295. }
  296. private static uint NextDeviceOffset(uint currentOffset, InputDevice device)
  297. {
  298. var sizeOfDevice = device.m_StateBlock.alignedSizeInBytes;
  299. if (sizeOfDevice == 0) // Shouldn't happen as we don't allow empty layouts but make sure we catch this if something slips through.
  300. throw new ArgumentException($"Device '{device}' has a zero-size state buffer", nameof(device));
  301. return currentOffset + sizeOfDevice.AlignToMultipleOf(4);
  302. }
  303. }
  304. }