Без опису
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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