No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

InputDevice.cs 56KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.InteropServices;
  4. using UnityEngine.InputSystem.LowLevel;
  5. using UnityEngine.InputSystem.Utilities;
  6. using Unity.Collections.LowLevel.Unsafe;
  7. using UnityEngine.InputSystem.Layouts;
  8. ////TODO: runtime remapping of control usages on a per-device basis
  9. ////TODO: finer-grained control over what devices deliver input while running in background
  10. //// (e.g. get gamepad input but do *not* get mouse and keyboard input)
  11. ////REVIEW: should be possible to completely hijack the input stream of a device such that its original input is suppressed
  12. ////REVIEW: can we construct the control tree of devices on demand so that the user never has to pay for
  13. //// the heap objects of devices that aren't used?
  14. // per device functions:
  15. // - update/poll
  16. // - IOCTL
  17. // - text input
  18. // - configuration change
  19. // - make current
  20. // - on remove (also resets current)
  21. //
  22. // Ideally, these would *not* be virtual methods on InputDevice but use a different process (which?)
  23. // for associating responses with devices
  24. namespace UnityEngine.InputSystem
  25. {
  26. /// <summary>
  27. /// Represents an input device which is always the root of a hierarchy of <see cref="InputControl"/> instances.
  28. /// </summary>
  29. /// <remarks>
  30. /// Input devices act as the container for control hierarchies. Every hierarchy has to have
  31. /// a device at the root. Devices cannot occur as children of other controls.
  32. ///
  33. /// Devices are usually created automatically in response to hardware being discovered by the Unity
  34. /// runtime. However, it is possible to manually add devices using methods such as <see
  35. /// cref="InputSystem.AddDevice{TDevice}(string)"/>.
  36. ///
  37. /// <example>
  38. /// <code>
  39. /// // Add a "synthetic" gamepad that isn't actually backed by hardware.
  40. /// var gamepad = InputSystem.AddDevice&lt;Gamepad&gt;();
  41. /// </code>
  42. /// </example>
  43. ///
  44. /// There are subclasses representing the most common types of devices, like <see cref="Mouse"/>,
  45. /// <see cref="Keyboard"/>, <see cref="Gamepad"/>, and <see cref="Touchscreen"/>.
  46. ///
  47. /// To create your own types of devices, you can derive from InputDevice and register your device
  48. /// as a new "layout".
  49. ///
  50. /// <example>
  51. /// <code>
  52. /// // InputControlLayoutAttribute attribute is only necessary if you want
  53. /// // to override default behavior that occurs when registering your device
  54. /// // as a layout.
  55. /// // The most common use of InputControlLayoutAttribute is to direct the system
  56. /// // to a custom "state struct" through the `stateType` property. See below for details.
  57. /// [InputControlLayout(displayName = "My Device", stateType = typeof(MyDeviceState))]
  58. /// #if UNITY_EDITOR
  59. /// [InitializeOnLoad]
  60. /// #endif
  61. /// public class MyDevice : InputDevice
  62. /// {
  63. /// public ButtonControl button { get; private set; }
  64. /// public AxisControl axis { get; private set; }
  65. ///
  66. /// // Register the device.
  67. /// static MyDevice()
  68. /// {
  69. /// // In case you want instance of your device to automatically be created
  70. /// // when specific hardware is detected by the Unity runtime, you have to
  71. /// // add one or more "device matchers" (InputDeviceMatcher) for the layout.
  72. /// // These matchers are compared to an InputDeviceDescription received from
  73. /// // the Unity runtime when a device is connected. You can add them either
  74. /// // using InputSystem.RegisterLayoutMatcher() or by directly specifying a
  75. /// // matcher when registering the layout.
  76. /// InputSystem.RegisterLayout&lt;MyDevice&gt;(
  77. /// // For the sake of demonstration, let's assume your device is a HID
  78. /// // and you want to match by PID and VID.
  79. /// matches: new InputDeviceMatcher()
  80. /// .WithInterface("HID")
  81. /// .WithCapability("PID", 1234)
  82. /// .WithCapability("VID", 5678));
  83. /// }
  84. ///
  85. /// // This is only to trigger the static class constructor to automatically run
  86. /// // in the player.
  87. /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
  88. /// private static void InitializeInPlayer() {}
  89. ///
  90. /// protected override void FinishSetup()
  91. /// {
  92. /// base.FinishSetup();
  93. /// button = GetChildControl&lt;ButtonControl&gt;("button");
  94. /// axis = GetChildControl&lt;AxisControl&gt;("axis");
  95. /// }
  96. /// }
  97. ///
  98. /// // A "state struct" describes the memory format used by a device. Each device can
  99. /// // receive and store memory in its custom format. InputControls are then connected
  100. /// // the individual pieces of memory and read out values from them.
  101. /// [StructLayout(LayoutKind.Explicit, Size = 32)]
  102. /// public struct MyDeviceState : IInputStateTypeInfo
  103. /// {
  104. /// // In the case of a HID (which we assume for the sake of this demonstration),
  105. /// // the format will be "HID". In practice, the format will depend on how your
  106. /// // particular device is connected and fed into the input system.
  107. /// // The format is a simple FourCC code that "tags" state memory blocks for the
  108. /// // device to give a base level of safety checks on memory operations.
  109. /// public FourCC format => return new FourCC('H', 'I', 'D');
  110. ///
  111. /// // InputControlAttributes on fields tell the input system to create controls
  112. /// // for the public fields found in the struct.
  113. ///
  114. /// // Assume a 16bit field of buttons. Create one button that is tied to
  115. /// // bit #3 (zero-based). Note that buttons do not need to be stored as bits.
  116. /// // They can also be stored as floats or shorts, for example.
  117. /// [InputControl(name = "button", layout = "Button", bit = 3)]
  118. /// public ushort buttons;
  119. ///
  120. /// // Create a floating-point axis. The name, if not supplied, is taken from
  121. /// // the field.
  122. /// [InputControl(layout = "Axis")]
  123. /// public short axis;
  124. /// }
  125. /// </code>
  126. /// </example>
  127. ///
  128. /// Devices can have usages like any other control (<see cref="InputControl.usages"/>). Unlike other controls,
  129. /// however, usages of InputDevices are allowed to be changed on the fly without requiring a change to the
  130. /// device layout (see <see cref="InputSystem.SetDeviceUsage(InputDevice,string)"/>).
  131. ///
  132. /// For a more complete example of how to implement custom input devices, check out the "Custom Device"
  133. /// sample which you can install from the Unity package manager.
  134. ///
  135. /// And, as always, you can also find more information in the <a href="../manual/Devices.html">manual</a>.
  136. /// </remarks>
  137. /// <seealso cref="InputControl"/>
  138. /// <seealso cref="Mouse"/>
  139. /// <seealso cref="Keyboard"/>
  140. /// <seealso cref="Gamepad"/>
  141. /// <seealso cref="Touchscreen"/>
  142. public class InputDevice : InputControl
  143. {
  144. /// <summary>
  145. /// Value of an invalid <see cref="deviceId"/>.
  146. /// </summary>
  147. /// <remarks>
  148. /// The input system will not assigned this ID to any device.
  149. /// </remarks>
  150. public const int InvalidDeviceId = 0;
  151. internal const int kLocalParticipantId = 0;
  152. internal const int kInvalidDeviceIndex = -1;
  153. /// <summary>
  154. /// Metadata describing the device (product name etc.).
  155. /// </summary>
  156. /// <remarks>
  157. /// The description of a device is unchanging over its lifetime and does not
  158. /// comprise data about a device's configuration (which is considered mutable).
  159. ///
  160. /// In most cases, the description for a device is supplied by the Unity runtime.
  161. /// This it the case for all <see cref="native"/> input devices. However, it is
  162. /// also possible to inject new devices in the form of device descriptions into
  163. /// the system using <see cref="InputSystem.AddDevice(InputDeviceDescription)"/>.
  164. ///
  165. /// The description of a device is what is matched by an <see cref="InputDeviceMatcher"/>
  166. /// to find the <see cref="InputControl.layout"/> to use for a device.
  167. /// </remarks>
  168. public InputDeviceDescription description => m_Description;
  169. ////REVIEW: When we can break the API, probably makes sense to replace this single bool with one for sending and one for receiving events
  170. /// <summary>
  171. /// Whether the device is currently enabled (that is, sends and receives events).
  172. /// </summary>
  173. /// <remarks>
  174. /// A device that is disabled will not receive events. I.e. events that are being sent to the device
  175. /// will be ignored.
  176. ///
  177. /// When disabling a <see cref="native"/> device, a <see cref="DisableDeviceCommand">disable command</see> will
  178. /// also be sent to the <see cref="IInputRuntime">runtime</see>. It depends on the specific runtime whether the
  179. /// device command is supported but if it is, the device will be disabled in the runtime and no longer send
  180. /// events. This is especially important for devices such as <see cref="Sensor">sensors</see> that incur both
  181. /// computation and battery consumption overhead while enabled.
  182. ///
  183. /// Specific types of devices can choose to start out in disabled state by default. This is generally the
  184. /// case for <see cref="Sensor">sensors</see> to ensure that their overhead is only incurred when actually
  185. /// being used by the application.
  186. /// </remarks>
  187. /// <seealso cref="InputSystem.EnableDevice"/>
  188. /// <seealso cref="InputSystem.DisableDevice"/>
  189. public bool enabled
  190. {
  191. get
  192. {
  193. #if UNITY_EDITOR
  194. if (InputState.currentUpdateType == InputUpdateType.Editor && (m_DeviceFlags & DeviceFlags.DisabledWhileInBackground) != 0)
  195. return true;
  196. #endif
  197. if ((m_DeviceFlags & (DeviceFlags.DisabledInFrontend | DeviceFlags.DisabledWhileInBackground)) != 0)
  198. return false;
  199. return QueryEnabledStateFromRuntime();
  200. }
  201. }
  202. ////TODO: rename this to canReceiveInputInBackground (once we can break API)
  203. /// <summary>
  204. /// If true, the device is capable of delivering input while the application is running in the background, i.e.
  205. /// while <c>Application.isFocused</c> is false.
  206. /// </summary>
  207. /// <value>Whether the device can generate input while in the background.</value>
  208. /// <remarks>
  209. /// The value of this property is determined by three separator factors.
  210. ///
  211. /// For one, <see cref="native"/> devices have an inherent value for this property that can be retrieved through
  212. /// <see cref="QueryCanRunInBackground"/>. This determines whether at the input collection level, the device is
  213. /// capable of producing input independent of application. This is rare and only a select set of hardware, platform,
  214. /// and SDK/API combinations support this. The prominent class of input devices that in general do support this
  215. /// behavior are VR devices.
  216. ///
  217. /// Furthermore, the property may be force-set through a device's <see cref="InputControl.layout"/> by
  218. /// means of <see cref="InputControlLayout.canRunInBackground"/>.
  219. ///
  220. /// Lastly, in the editor, the value of the property may be overridden depending on <see cref="InputSettings.editorInputBehaviorInPlayMode"/>
  221. /// in case certain devices are automatically kept running in play mode even when no Game View has focus.
  222. ///
  223. /// Be aware that as far as players are concerned, only certain platforms support running Unity while not having focus.
  224. /// On mobile platforms, for example, this is generally not supported. In this case, the value of this property
  225. /// has no impact on input while the application does not have focus. See <see cref="InputSettings.backgroundBehavior"/>
  226. /// for more details.
  227. /// </remarks>
  228. /// <seealso cref="InputSettings.backgroundBehavior"/>
  229. /// <seealso cref="InputControlLayout.canRunInBackground"/>
  230. public bool canRunInBackground
  231. {
  232. get
  233. {
  234. // In the editor, "background" refers to "game view not focused", not to the editor not being active.
  235. // So, we modulate canRunInBackground depending on how input should behave WRT game view according
  236. // to the input settings.
  237. #if UNITY_EDITOR
  238. var gameViewFocus = InputSystem.settings.editorInputBehaviorInPlayMode;
  239. if (gameViewFocus == InputSettings.EditorInputBehaviorInPlayMode.AllDevicesRespectGameViewFocus)
  240. return false; // No device considered being able to run without game view focus.
  241. if (gameViewFocus == InputSettings.EditorInputBehaviorInPlayMode.PointersAndKeyboardsRespectGameViewFocus)
  242. return !(this is Pointer || this is Keyboard); // Anything but pointers and keyboards considered as being able to run in background.
  243. #endif
  244. if ((m_DeviceFlags & DeviceFlags.CanRunInBackgroundHasBeenQueried) != 0)
  245. return (m_DeviceFlags & DeviceFlags.CanRunInBackground) != 0;
  246. var command = QueryCanRunInBackground.Create();
  247. m_DeviceFlags |= DeviceFlags.CanRunInBackgroundHasBeenQueried;
  248. if (ExecuteCommand(ref command) >= 0 && command.canRunInBackground)
  249. {
  250. m_DeviceFlags |= DeviceFlags.CanRunInBackground;
  251. return true;
  252. }
  253. m_DeviceFlags &= ~DeviceFlags.CanRunInBackground;
  254. return false;
  255. }
  256. }
  257. /// <summary>
  258. /// Whether the device has been added to the system.
  259. /// </summary>
  260. /// <value>If true, the device is currently among the devices in <see cref="InputSystem.devices"/>.</value>
  261. /// <remarks>
  262. /// Devices may be removed at any time. Either when their hardware is unplugged or when they
  263. /// are manually removed through <see cref="InputSystem.RemoveDevice"/> or by being excluded
  264. /// through <see cref="InputSettings.supportedDevices"/>. When a device is removed, its instance,
  265. /// however, will not disappear. This property can be used to check whether the device is part
  266. /// of the current set of active devices.
  267. /// </remarks>
  268. /// <seealso cref="InputSystem.devices"/>
  269. public bool added => m_DeviceIndex != kInvalidDeviceIndex;
  270. /// <summary>
  271. /// Whether the device is mirrored from a remote input system and not actually present
  272. /// as a "real" device in the local system.
  273. /// </summary>
  274. /// <value>Whether the device mirrors a device from a remotely connected input system.</value>
  275. /// <seealso cref="InputSystem.remoting"/>
  276. /// <seealso cref="InputRemoting"/>
  277. public bool remote => (m_DeviceFlags & DeviceFlags.Remote) == DeviceFlags.Remote;
  278. /// <summary>
  279. /// Whether the device comes from the <see cref="IInputRuntime">runtime</see>
  280. /// </summary>
  281. /// <value>Whether the device has been discovered by the Unity runtime.</value>
  282. /// <remarks>
  283. /// Devices can be discovered when <see cref="IInputRuntime.onDeviceDiscovered">reported</see>
  284. /// by the runtime or they can be added manually through the various <see cref="InputSystem.AddDevice(InputDevice)">
  285. /// AddDevice</see> APIs. Devices reported by the runtime will return true for this
  286. /// property whereas devices added manually will return false.
  287. ///
  288. /// Devices reported by the runtime will usually come from the Unity engine itself.
  289. /// </remarks>
  290. /// <seealso cref="IInputRuntime"/>
  291. /// <seealso cref="IInputRuntime.onDeviceDiscovered"/>
  292. public bool native => (m_DeviceFlags & DeviceFlags.Native) == DeviceFlags.Native;
  293. /// <summary>
  294. /// Whether the device requires an extra update before rendering.
  295. /// </summary>
  296. /// <remarks>
  297. /// The value of this property is determined by <see cref="InputControlLayout.updateBeforeRender"/> in
  298. /// the device's <see cref="InputControlLayout">control layout</see>.
  299. ///
  300. /// The extra update is necessary for tracking devices that are used in rendering code. For example,
  301. /// the eye transforms of an HMD should be refreshed right before rendering as refreshing only in the
  302. /// beginning of the frame will lead to a noticeable lag.
  303. /// </remarks>
  304. /// <seealso cref="InputUpdateType.BeforeRender"/>
  305. public bool updateBeforeRender => (m_DeviceFlags & DeviceFlags.UpdateBeforeRender) == DeviceFlags.UpdateBeforeRender;
  306. /// <summary>
  307. /// Unique numeric ID for the device.
  308. /// </summary>
  309. /// <remarks>
  310. /// This is only assigned once a device has been added to the system. No two devices will receive the same
  311. /// ID and no device will receive an ID that another device used before even if the device was removed. The
  312. /// only exception to this is if a device gets re-created as part of a layout change. For example, if a new
  313. /// layout is registered that replaces the <see cref="Mouse"/> layout, all <see cref="Mouse"/> devices will
  314. /// get recreated but will keep their existing device IDs.
  315. ///
  316. /// IDs are assigned by the input runtime.
  317. /// </remarks>
  318. /// <seealso cref="IInputRuntime.AllocateDeviceId"/>
  319. public int deviceId => m_DeviceId;
  320. /// <summary>
  321. /// Timestamp of last state event used to update the device.
  322. /// </summary>
  323. /// <remarks>
  324. /// Events other than <see cref="LowLevel.StateEvent"/> and <see cref="LowLevel.DeltaStateEvent"/> will
  325. /// not cause lastUpdateTime to be changed.
  326. /// The "timeline" is reset to 0 when entering play mode. If there are any events incoming or device
  327. /// updates which occur prior to entering play mode, these will appear negative.
  328. /// </remarks>
  329. public double lastUpdateTime => m_LastUpdateTimeInternal - InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup;
  330. public bool wasUpdatedThisFrame => m_CurrentUpdateStepCount == InputUpdate.s_UpdateStepCount;
  331. /// <summary>
  332. /// A flattened list of controls that make up the device.
  333. /// </summary>
  334. /// <remarks>
  335. /// Does not allocate.
  336. /// </remarks>
  337. public ReadOnlyArray<InputControl> allControls =>
  338. // Since m_ChildrenForEachControl contains the device's children as well as the children
  339. // of each control in the hierarchy, and since each control can only have a single parent,
  340. // this list will actually deliver a flattened list of all controls in the hierarchy (and without
  341. // the device itself being listed).
  342. new ReadOnlyArray<InputControl>(m_ChildrenForEachControl);
  343. ////REVIEW: This violates the constraint of controls being required to not have reference types as value types.
  344. /// <inheritdoc/>
  345. public override Type valueType => typeof(byte[]);
  346. /// <inheritdoc/>
  347. public override int valueSizeInBytes => (int)m_StateBlock.alignedSizeInBytes;
  348. // This one just leads to confusion as you can access it from subclasses and then be surprised
  349. // that it doesn't only include members of those classes.
  350. [Obsolete("Use 'InputSystem.devices' instead. (UnityUpgradable) -> InputSystem.devices", error: false)]
  351. public static ReadOnlyArray<InputDevice> all => InputSystem.devices;
  352. /// <summary>
  353. /// This constructor is public for the sake of <c>Activator.CreateInstance</c> only. To construct
  354. /// devices, use methods such as <see cref="InputSystem.AddDevice{TDevice}(string)"/>. Manually
  355. /// using <c>new</c> on InputDevice will not result in a usable device.
  356. /// </summary>
  357. public InputDevice()
  358. {
  359. m_DeviceId = InvalidDeviceId;
  360. m_ParticipantId = kLocalParticipantId;
  361. m_DeviceIndex = kInvalidDeviceIndex;
  362. }
  363. ////REVIEW: Is making devices be byte[] values really all that useful? Seems better than returning nulls but
  364. //// at the same time, seems questionable.
  365. /// <inheritdoc/>
  366. public override unsafe object ReadValueFromBufferAsObject(void* buffer, int bufferSize)
  367. {
  368. throw new NotImplementedException();
  369. }
  370. /// <inheritdoc/>
  371. public override unsafe object ReadValueFromStateAsObject(void* statePtr)
  372. {
  373. if (m_DeviceIndex == kInvalidDeviceIndex)
  374. return null;
  375. var numBytes = stateBlock.alignedSizeInBytes;
  376. var array = new byte[numBytes];
  377. fixed(byte* arrayPtr = array)
  378. {
  379. var adjustedStatePtr = (byte*)statePtr + m_StateBlock.byteOffset;
  380. UnsafeUtility.MemCpy(arrayPtr, adjustedStatePtr, numBytes);
  381. }
  382. return array;
  383. }
  384. /// <inheritdoc/>
  385. public override unsafe void ReadValueFromStateIntoBuffer(void* statePtr, void* bufferPtr, int bufferSize)
  386. {
  387. if (statePtr == null)
  388. throw new ArgumentNullException(nameof(statePtr));
  389. if (bufferPtr == null)
  390. throw new ArgumentNullException(nameof(bufferPtr));
  391. if (bufferSize < valueSizeInBytes)
  392. throw new ArgumentException($"Buffer too small (expected: {valueSizeInBytes}, actual: {bufferSize}");
  393. var adjustedStatePtr = (byte*)statePtr + m_StateBlock.byteOffset;
  394. UnsafeUtility.MemCpy(bufferPtr, adjustedStatePtr, m_StateBlock.alignedSizeInBytes);
  395. }
  396. /// <inheritdoc/>
  397. public override unsafe bool CompareValue(void* firstStatePtr, void* secondStatePtr)
  398. {
  399. if (firstStatePtr == null)
  400. throw new ArgumentNullException(nameof(firstStatePtr));
  401. if (secondStatePtr == null)
  402. throw new ArgumentNullException(nameof(secondStatePtr));
  403. var adjustedFirstStatePtr = (byte*)firstStatePtr + m_StateBlock.byteOffset;
  404. var adjustedSecondStatePtr = (byte*)firstStatePtr + m_StateBlock.byteOffset;
  405. return UnsafeUtility.MemCmp(adjustedFirstStatePtr, adjustedSecondStatePtr,
  406. m_StateBlock.alignedSizeInBytes) == 0;
  407. }
  408. /// <summary>
  409. /// Called by the system when the configuration of the device has changed.
  410. /// </summary>
  411. /// <seealso cref="DeviceConfigurationEvent"/>
  412. internal void NotifyConfigurationChanged()
  413. {
  414. // Mark all controls in the hierarchy as having their config out of date.
  415. // We don't want to update configuration right away but rather wait until
  416. // someone actually depends on it.
  417. isConfigUpToDate = false;
  418. for (var i = 0; i < m_ChildrenForEachControl.Length; ++i)
  419. m_ChildrenForEachControl[i].isConfigUpToDate = false;
  420. // Make sure we fetch the enabled/disabled state again.
  421. m_DeviceFlags &= ~DeviceFlags.DisabledStateHasBeenQueriedFromRuntime;
  422. OnConfigurationChanged();
  423. }
  424. /// <summary>
  425. /// Make this the current device of its type.
  426. /// </summary>
  427. /// <remarks>
  428. /// This method is called automatically by the input system when a device is
  429. /// added or when input is received on it. Many types of devices have <c>.current</c>
  430. /// getters that allow querying the last used device of a specific type directly (for
  431. /// example, see <see cref="Gamepad.current"/>).
  432. ///
  433. /// There is one special case, however, related to noise. A device that has noisy controls
  434. /// (i.e. controls for which <see cref="InputControl.noisy"/> is true) may receive input events
  435. /// that contain no meaningful user interaction but are simply just noise from the device. A
  436. /// good example of this is the PS4 gamepad which has a built-in gyro and may thus constantly
  437. /// feed events into the input system even if not being actually in use. If, for example, an
  438. /// Xbox gamepad and PS4 gamepad are both connected to a PC and the user is playing with the
  439. /// Xbox gamepad, the PS4 gamepad would still constantly make itself <see cref="Gamepad.current"/>
  440. /// by simply flooding the system with events. Hence why by default, noise on <c>.current</c> getters
  441. /// will be filtered out and a device will only see <c>MakeCurrent</c> getting called if their input
  442. /// was detected on non-noisy controls.
  443. /// </remarks>
  444. /// <seealso cref="Pointer.current"/>
  445. /// <seealso cref="Gamepad.current"/>
  446. /// <seealso cref="Mouse.current"/>
  447. /// <seealso cref="Pen.current"/>
  448. public virtual void MakeCurrent()
  449. {
  450. }
  451. /// <summary>
  452. /// Called by the system when the device is added to <see cref="InputSystem.devices"/>.
  453. /// </summary>
  454. /// <remarks>
  455. /// This is called <em>after</em> the device has already been added.
  456. /// </remarks>
  457. /// <seealso cref="InputSystem.devices"/>
  458. /// <seealso cref="InputDeviceChange.Added"/>
  459. /// <seealso cref="OnRemoved"/>
  460. protected virtual void OnAdded()
  461. {
  462. }
  463. /// <summary>
  464. /// Called by the system when the device is removed from <see cref="InputSystem.devices"/>.
  465. /// </summary>
  466. /// <remarks>
  467. /// This is called <em>after</em> the device has already been removed.
  468. /// </remarks>
  469. /// <seealso cref="InputSystem.devices"/>
  470. /// <seealso cref="InputDeviceChange.Removed"/>
  471. /// <seealso cref="OnRemoved"/>
  472. protected virtual void OnRemoved()
  473. {
  474. }
  475. /// <summary>
  476. /// Called by the system when the device configuration is changed. This happens when the backend sends
  477. /// a <see cref="DeviceConfigurationEvent"/> for the device.
  478. /// </summary>
  479. /// <remarks>
  480. /// This method can be used to flush out cached information. An example of where this happens is <see cref="Controls.KeyControl"/>
  481. /// caching information about the display name of a control. As this depends on the current keyboard layout, the information
  482. /// has to be fetched dynamically (this happens using <see cref="QueryKeyNameCommand"/>). Whenever the keyboard layout changes,
  483. /// the system sends a <see cref="DeviceConfigurationEvent"/> for the <see cref="Keyboard"/> at which point the device flushes
  484. /// all cached key names.
  485. /// </remarks>
  486. /// <seealso cref="InputManager.OnUpdate"/>
  487. /// <seealso cref="InputDeviceChange.ConfigurationChanged"/>
  488. /// <seealso cref="OnConfigurationChanged"/>///
  489. protected virtual void OnConfigurationChanged()
  490. {
  491. }
  492. ////TODO: add overridable OnDisable/OnEnable that fire the device commands
  493. ////REVIEW: return just bool instead of long and require everything else to go in the command?
  494. /// <summary>
  495. /// Perform a device-specific command.
  496. /// </summary>
  497. /// <param name="command">Data for the command to be performed.</param>
  498. /// <returns>A transfer-specific return code. Negative values are considered failure codes.</returns>
  499. /// <remarks>
  500. /// Commands allow devices to set up custom protocols without having to extend
  501. /// the device API. This is most useful for devices implemented in the native Unity runtime
  502. /// which, through the command interface, may provide custom, device-specific functions.
  503. ///
  504. /// This is a low-level API. It works in a similar way to <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216%28v=vs.85%29.aspx?f=255&amp;MSPPError=-2147217396" target="_blank">
  505. /// DeviceIoControl</a> on Windows and <a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/ioctl.2.html#//apple_ref/doc/man/2/ioctl" target="_blank">ioctl</a>
  506. /// on UNIX-like systems.
  507. /// </remarks>
  508. public unsafe long ExecuteCommand<TCommand>(ref TCommand command)
  509. where TCommand : struct, IInputDeviceCommandInfo
  510. {
  511. var commandPtr = (InputDeviceCommand*)UnsafeUtility.AddressOf(ref command);
  512. // Give callbacks first shot.
  513. var manager = InputSystem.s_Manager;
  514. manager.m_DeviceCommandCallbacks.LockForChanges();
  515. for (var i = 0; i < manager.m_DeviceCommandCallbacks.length; ++i)
  516. {
  517. try
  518. {
  519. var result = manager.m_DeviceCommandCallbacks[i](this, commandPtr);
  520. if (result.HasValue)
  521. return result.Value;
  522. }
  523. catch (Exception exception)
  524. {
  525. Debug.LogError($"{exception.GetType().Name} while executing 'InputSystem.onDeviceCommand' callbacks");
  526. Debug.LogException(exception);
  527. }
  528. }
  529. manager.m_DeviceCommandCallbacks.UnlockForChanges();
  530. return ExecuteCommand((InputDeviceCommand*)UnsafeUtility.AddressOf(ref command));
  531. }
  532. protected virtual unsafe long ExecuteCommand(InputDeviceCommand* commandPtr)
  533. {
  534. return InputRuntime.s_Instance.DeviceCommand(deviceId, commandPtr);
  535. }
  536. internal bool QueryEnabledStateFromRuntime()
  537. {
  538. // Fetch state from runtime, if necessary.
  539. if ((m_DeviceFlags & DeviceFlags.DisabledStateHasBeenQueriedFromRuntime) == 0)
  540. {
  541. var command = QueryEnabledStateCommand.Create();
  542. if (ExecuteCommand(ref command) >= 0)
  543. {
  544. if (command.isEnabled)
  545. m_DeviceFlags &= ~DeviceFlags.DisabledInRuntime;
  546. else
  547. m_DeviceFlags |= DeviceFlags.DisabledInRuntime;
  548. }
  549. else
  550. {
  551. // We got no response on the enable/disable state. Assume device is enabled.
  552. m_DeviceFlags &= ~DeviceFlags.DisabledInRuntime;
  553. }
  554. // Only fetch enable/disable state again if we get a configuration change event.
  555. m_DeviceFlags |= DeviceFlags.DisabledStateHasBeenQueriedFromRuntime;
  556. }
  557. return (m_DeviceFlags & DeviceFlags.DisabledInRuntime) == 0;
  558. }
  559. [Serializable]
  560. [Flags]
  561. internal enum DeviceFlags
  562. {
  563. UpdateBeforeRender = 1 << 0,
  564. HasStateCallbacks = 1 << 1,
  565. HasControlsWithDefaultState = 1 << 2,
  566. HasDontResetControls = 1 << 10,
  567. HasEventMerger = 1 << 13,
  568. HasEventPreProcessor = 1 << 14,
  569. Remote = 1 << 3, // It's a local mirror of a device from a remote player connection.
  570. Native = 1 << 4, // It's a device created from data surfaced by NativeInputRuntime.
  571. DisabledInFrontend = 1 << 5, // Explicitly disabled on the managed side.
  572. DisabledInRuntime = 1 << 7, // Disabled in the native runtime.
  573. DisabledWhileInBackground = 1 << 8, // Disabled while the player is running in the background.
  574. DisabledStateHasBeenQueriedFromRuntime = 1 << 6, // Whether we have fetched the current enable/disable state from the runtime.
  575. CanRunInBackground = 1 << 11,
  576. CanRunInBackgroundHasBeenQueried = 1 << 12,
  577. }
  578. internal bool disabledInFrontend
  579. {
  580. get => (m_DeviceFlags & DeviceFlags.DisabledInFrontend) != 0;
  581. set
  582. {
  583. if (value)
  584. m_DeviceFlags |= DeviceFlags.DisabledInFrontend;
  585. else
  586. m_DeviceFlags &= ~DeviceFlags.DisabledInFrontend;
  587. }
  588. }
  589. internal bool disabledInRuntime
  590. {
  591. get => (m_DeviceFlags & DeviceFlags.DisabledInRuntime) != 0;
  592. set
  593. {
  594. if (value)
  595. m_DeviceFlags |= DeviceFlags.DisabledInRuntime;
  596. else
  597. m_DeviceFlags &= ~DeviceFlags.DisabledInRuntime;
  598. }
  599. }
  600. internal bool disabledWhileInBackground
  601. {
  602. get => (m_DeviceFlags & DeviceFlags.DisabledWhileInBackground) != 0;
  603. set
  604. {
  605. if (value)
  606. m_DeviceFlags |= DeviceFlags.DisabledWhileInBackground;
  607. else
  608. m_DeviceFlags &= ~DeviceFlags.DisabledWhileInBackground;
  609. }
  610. }
  611. internal DeviceFlags m_DeviceFlags;
  612. internal int m_DeviceId;
  613. internal int m_ParticipantId;
  614. internal int m_DeviceIndex; // Index in InputManager.m_Devices.
  615. internal InputDeviceDescription m_Description;
  616. /// <summary>
  617. /// Timestamp of last event we received.
  618. /// </summary>
  619. /// <seealso cref="InputEvent.time"/>
  620. internal double m_LastUpdateTimeInternal;
  621. // Update count corresponding to the current front buffers that are active on the device.
  622. // We use this to know when to flip buffers.
  623. internal uint m_CurrentUpdateStepCount;
  624. // List of aliases for all controls. Each control gets a slice of this array.
  625. // See 'InputControl.aliases'.
  626. // NOTE: The device's own aliases are part of this array as well.
  627. internal InternedString[] m_AliasesForEachControl;
  628. // List of usages for all controls. Each control gets a slice of this array.
  629. // See 'InputControl.usages'.
  630. // NOTE: The device's own usages are part of this array as well. They are always
  631. // at the *end* of the array.
  632. internal InternedString[] m_UsagesForEachControl;
  633. // This one does NOT contain the device itself, i.e. it only contains controls on the device
  634. // and may this be shorter than m_UsagesForEachControl.
  635. internal InputControl[] m_UsageToControl;
  636. // List of children for all controls. Each control gets a slice of this array.
  637. // See 'InputControl.children'.
  638. // NOTE: The device's own children are part of this array as well.
  639. internal InputControl[] m_ChildrenForEachControl;
  640. // An ordered list of ints each containing a bit offset into the state of the device (*without* the added global
  641. // offset), a bit count for the size of the state of the control, and an associated index into m_ChildrenForEachControl
  642. // for the corresponding control.
  643. // NOTE: This contains *leaf* controls only.
  644. internal uint[] m_StateOffsetToControlMap;
  645. // Holds the nodes that represent the tree of memory ranges that each control occupies. This is used when
  646. // determining what controls have changed given a state event or partial state update.
  647. internal ControlBitRangeNode[] m_ControlTreeNodes;
  648. // An indirection table for control bit range nodes to point at zero or more controls. Indices are used to
  649. // point into the m_ChildrenForEachControl array.
  650. internal ushort[] m_ControlTreeIndices;
  651. // When a device gets built from a layout, we create a binary tree from its controls where each node in the tree
  652. // represents the range of bits that cover the left or right section of the parent range. For example, starting
  653. // with the entire device state block as the parent, where the state block is 100 bits long, the left node will
  654. // cover from bits 0-50, and the right from bits 51-99. For the left node, we'll get two more child nodes where
  655. // the left will cover bits 0-25, and the right bits 26-49 and so on. Each node will point at any controls that
  656. // either fit exactly into its range, or overlap the splitting point between both nodes. In reality, picking the
  657. // mid-point to split each parent node is a little convoluted and will rarely be the absolute mid-point, but that's
  658. // the basic idea.
  659. //
  660. // At runtime, when state events come in, we can then really quickly perform a bunch of memcmps on both sides of
  661. // the tree and recurse down the branches that have changed. When nodes have controls, we can then check if those
  662. // controls have changes, and mark them as stale so their cached values get updated the next time their values
  663. // are read.
  664. [StructLayout(LayoutKind.Sequential, Pack = 1)]
  665. internal struct ControlBitRangeNode
  666. {
  667. // only store the end bit offset of each range because we always do a full tree traversal so
  668. // the start offset is always calculated at each level.
  669. public ushort endBitOffset;
  670. // points to the location in the nodes array where the left child of this node lives, or -1 if there
  671. // is no child. The right child is always at the next index.
  672. public short leftChildIndex;
  673. // each node can point at multiple controls (because multiple controls can use the same range in memory and
  674. // also because of overlaps in bit ranges). The control indicies for each node are stored contiguously in the
  675. // m_ControlTreeIndicies array on the device, which acts as an indirection table, and these two values tell
  676. // us where to start for each node and how many controls this node points at. This is an unsigned short so that
  677. // we could in theory support devices with up to 65535 controls. Each node however can only support 255 controls.
  678. public ushort controlStartIndex;
  679. public byte controlCount;
  680. public ControlBitRangeNode(ushort endOffset)
  681. {
  682. controlStartIndex = 0;
  683. controlCount = 0;
  684. endBitOffset = endOffset;
  685. leftChildIndex = -1;
  686. }
  687. }
  688. // ATM we pack everything into 32 bits. Given we're operating on bit offsets and counts, this imposes some tight limits
  689. // on controls and their associated state memory. Should this turn out to be a problem, bump m_StateOffsetToControlMap
  690. // to a ulong[] and up the counts here to account for having 64 bits available instead of only 32.
  691. internal const int kControlIndexBits = 10; // 1024 controls max.
  692. internal const int kStateOffsetBits = 13; // 1024 bytes max state size for entire device.
  693. internal const int kStateSizeBits = 9; // 64 bytes max for an individual leaf control.
  694. internal static uint EncodeStateOffsetToControlMapEntry(uint controlIndex, uint stateOffsetInBits, uint stateSizeInBits)
  695. {
  696. Debug.Assert(kControlIndexBits < 32, $"Expected kControlIndexBits < 32, so we fit into the 32 bit wide bitmask");
  697. Debug.Assert(kStateOffsetBits < 32, $"Expected kStateOffsetBits < 32, so we fit into the 32 bit wide bitmask");
  698. Debug.Assert(kStateSizeBits < 32, $"Expected kStateSizeBits < 32, so we fit into the 32 bit wide bitmask");
  699. Debug.Assert(controlIndex < (1U << kControlIndexBits), "Control index beyond what is supported");
  700. Debug.Assert(stateOffsetInBits < (1U << kStateOffsetBits), "State offset beyond what is supported");
  701. Debug.Assert(stateSizeInBits < (1U << kStateSizeBits), "State size beyond what is supported");
  702. return stateOffsetInBits << (kControlIndexBits + kStateSizeBits) | stateSizeInBits << kControlIndexBits | controlIndex;
  703. }
  704. internal static void DecodeStateOffsetToControlMapEntry(uint entry, out uint controlIndex,
  705. out uint stateOffset, out uint stateSize)
  706. {
  707. controlIndex = entry & (1U << kControlIndexBits) - 1;
  708. stateOffset = entry >> (kControlIndexBits + kStateSizeBits);
  709. stateSize = (entry >> kControlIndexBits) & (((1U << (kControlIndexBits + kStateSizeBits)) - 1) >> kControlIndexBits);
  710. }
  711. // NOTE: We don't store processors in a combined array the same way we do for
  712. // usages and children as that would require lots of casting from 'object'.
  713. /// <summary>
  714. /// If true, the device has at least one control that has an explicit default state.
  715. /// </summary>
  716. internal bool hasControlsWithDefaultState
  717. {
  718. get => (m_DeviceFlags & DeviceFlags.HasControlsWithDefaultState) == DeviceFlags.HasControlsWithDefaultState;
  719. set
  720. {
  721. if (value)
  722. m_DeviceFlags |= DeviceFlags.HasControlsWithDefaultState;
  723. else
  724. m_DeviceFlags &= ~DeviceFlags.HasControlsWithDefaultState;
  725. }
  726. }
  727. internal bool hasDontResetControls
  728. {
  729. get => (m_DeviceFlags & DeviceFlags.HasDontResetControls) == DeviceFlags.HasDontResetControls;
  730. set
  731. {
  732. if (value)
  733. m_DeviceFlags |= DeviceFlags.HasDontResetControls;
  734. else
  735. m_DeviceFlags &= ~DeviceFlags.HasDontResetControls;
  736. }
  737. }
  738. internal bool hasStateCallbacks
  739. {
  740. get => (m_DeviceFlags & DeviceFlags.HasStateCallbacks) == DeviceFlags.HasStateCallbacks;
  741. set
  742. {
  743. if (value)
  744. m_DeviceFlags |= DeviceFlags.HasStateCallbacks;
  745. else
  746. m_DeviceFlags &= ~DeviceFlags.HasStateCallbacks;
  747. }
  748. }
  749. internal bool hasEventMerger
  750. {
  751. get => (m_DeviceFlags & DeviceFlags.HasEventMerger) == DeviceFlags.HasEventMerger;
  752. set
  753. {
  754. if (value)
  755. m_DeviceFlags |= DeviceFlags.HasEventMerger;
  756. else
  757. m_DeviceFlags &= ~DeviceFlags.HasEventMerger;
  758. }
  759. }
  760. internal bool hasEventPreProcessor
  761. {
  762. get => (m_DeviceFlags & DeviceFlags.HasEventPreProcessor) == DeviceFlags.HasEventPreProcessor;
  763. set
  764. {
  765. if (value)
  766. m_DeviceFlags |= DeviceFlags.HasEventPreProcessor;
  767. else
  768. m_DeviceFlags &= ~DeviceFlags.HasEventPreProcessor;
  769. }
  770. }
  771. internal void AddDeviceUsage(InternedString usage)
  772. {
  773. var controlUsageCount = m_UsageToControl.LengthSafe();
  774. var totalUsageCount = controlUsageCount + m_UsageCount;
  775. if (m_UsageCount == 0)
  776. m_UsageStartIndex = totalUsageCount;
  777. ArrayHelpers.AppendWithCapacity(ref m_UsagesForEachControl, ref totalUsageCount, usage);
  778. ++m_UsageCount;
  779. }
  780. internal void RemoveDeviceUsage(InternedString usage)
  781. {
  782. var controlUsageCount = m_UsageToControl.LengthSafe();
  783. var totalUsageCount = controlUsageCount + m_UsageCount;
  784. var index = ArrayHelpers.IndexOfValue(m_UsagesForEachControl, usage, m_UsageStartIndex, totalUsageCount);
  785. if (index == -1)
  786. return;
  787. Debug.Assert(m_UsageCount > 0);
  788. ArrayHelpers.EraseAtWithCapacity(m_UsagesForEachControl, ref totalUsageCount, index);
  789. --m_UsageCount;
  790. if (m_UsageCount == 0)
  791. m_UsageStartIndex = default;
  792. }
  793. internal void ClearDeviceUsages()
  794. {
  795. for (var i = m_UsageStartIndex; i < m_UsageCount; ++i)
  796. m_UsagesForEachControl[i] = default;
  797. m_UsageCount = default;
  798. }
  799. internal bool RequestSync()
  800. {
  801. SetOptimizedControlDataTypeRecursively();
  802. var syncCommand = RequestSyncCommand.Create();
  803. return device.ExecuteCommand(ref syncCommand) >= 0;
  804. }
  805. internal bool RequestReset()
  806. {
  807. SetOptimizedControlDataTypeRecursively();
  808. var resetCommand = RequestResetCommand.Create();
  809. return device.ExecuteCommand(ref resetCommand) >= 0;
  810. }
  811. internal bool ExecuteEnableCommand()
  812. {
  813. SetOptimizedControlDataTypeRecursively();
  814. var command = EnableDeviceCommand.Create();
  815. return device.ExecuteCommand(ref command) >= 0;
  816. }
  817. internal bool ExecuteDisableCommand()
  818. {
  819. var command = DisableDeviceCommand.Create();
  820. return device.ExecuteCommand(ref command) >= 0;
  821. }
  822. internal void NotifyAdded()
  823. {
  824. OnAdded();
  825. }
  826. internal void NotifyRemoved()
  827. {
  828. OnRemoved();
  829. }
  830. internal static TDevice Build<TDevice>(string layoutName = default, string layoutVariants = default, InputDeviceDescription deviceDescription = default, bool noPrecompiledLayouts = false)
  831. where TDevice : InputDevice
  832. {
  833. var internedLayoutName = new InternedString(layoutName);
  834. if (internedLayoutName.IsEmpty())
  835. {
  836. internedLayoutName = InputControlLayout.s_Layouts.TryFindLayoutForType(typeof(TDevice));
  837. if (internedLayoutName.IsEmpty())
  838. internedLayoutName = new InternedString(typeof(TDevice).Name);
  839. }
  840. // Fast path: see if we can use a precompiled version.
  841. // NOTE: We currently do not support layout variants with precompiled layouts.
  842. // NOTE: We remove precompiled layouts when they are invalidated by layout changes. So, we don't have to perform
  843. // checks here.
  844. if (!noPrecompiledLayouts &&
  845. string.IsNullOrEmpty(layoutVariants) &&
  846. InputControlLayout.s_Layouts.precompiledLayouts.TryGetValue(internedLayoutName, out var precompiledLayout))
  847. {
  848. // Yes. This is pretty much a direct new() of the device.
  849. return (TDevice)precompiledLayout.factoryMethod();
  850. }
  851. // Slow path: use InputDeviceBuilder to construct the device from the InputControlLayout.
  852. using (InputDeviceBuilder.Ref())
  853. {
  854. InputDeviceBuilder.instance.Setup(internedLayoutName, new InternedString(layoutVariants),
  855. deviceDescription: deviceDescription);
  856. var device = InputDeviceBuilder.instance.Finish();
  857. if (!(device is TDevice deviceOfType))
  858. throw new ArgumentException(
  859. $"Expected device of type '{typeof(TDevice).Name}' but got device of type '{device.GetType().Name}' instead",
  860. "TDevice");
  861. return deviceOfType;
  862. }
  863. }
  864. internal unsafe void WriteChangedControlStates(byte* deviceStateBuffer, void* statePtr, uint stateSizeInBytes,
  865. uint stateOffsetInDevice)
  866. {
  867. Debug.Assert(m_ControlTreeNodes != null && m_ControlTreeIndices != null);
  868. if (m_ControlTreeNodes.Length == 0)
  869. return;
  870. // if we're dealing with a delta state event or just an individual control update through InputState.ChangeState
  871. // the size of the new data will not be the same size as the device state block, so use the 'partial' change state
  872. // method to update just those controls that overlap with the changed state.
  873. if (m_StateBlock.sizeInBits != stateSizeInBytes * 8)
  874. {
  875. if (m_ControlTreeNodes[0].leftChildIndex != -1)
  876. WritePartialChangedControlStatesInternal(statePtr, stateSizeInBytes * 8,
  877. stateOffsetInDevice * 8, deviceStateBuffer, m_ControlTreeNodes[0], 0);
  878. }
  879. else
  880. {
  881. if (m_ControlTreeNodes[0].leftChildIndex != -1)
  882. WriteChangedControlStatesInternal(statePtr, stateSizeInBytes * 8,
  883. deviceStateBuffer, m_ControlTreeNodes[0], 0);
  884. }
  885. }
  886. private unsafe void WritePartialChangedControlStatesInternal(void* statePtr, uint stateSizeInBits,
  887. uint stateOffsetInDeviceInBits, byte* deviceStatePtr, ControlBitRangeNode parentNode, uint startOffset)
  888. {
  889. var leftNode = m_ControlTreeNodes[parentNode.leftChildIndex];
  890. // TODO recheck
  891. if (Math.Max(stateOffsetInDeviceInBits, startOffset) <=
  892. Math.Min(stateOffsetInDeviceInBits + stateSizeInBits, leftNode.endBitOffset))
  893. {
  894. var controlEndIndex = leftNode.controlStartIndex + leftNode.controlCount;
  895. for (int i = leftNode.controlStartIndex; i < controlEndIndex; i++)
  896. {
  897. var controlIndex = m_ControlTreeIndices[i];
  898. m_ChildrenForEachControl[controlIndex].MarkAsStale();
  899. }
  900. if (leftNode.leftChildIndex != -1)
  901. WritePartialChangedControlStatesInternal(statePtr, stateSizeInBits, stateOffsetInDeviceInBits,
  902. deviceStatePtr, leftNode, startOffset);
  903. }
  904. var rightNode = m_ControlTreeNodes[parentNode.leftChildIndex + 1];
  905. // TODO recheck
  906. if (Math.Max(stateOffsetInDeviceInBits, leftNode.endBitOffset) <=
  907. Math.Min(stateOffsetInDeviceInBits + stateSizeInBits, rightNode.endBitOffset))
  908. {
  909. var controlEndIndex = rightNode.controlStartIndex + rightNode.controlCount;
  910. for (int i = rightNode.controlStartIndex; i < controlEndIndex; i++)
  911. {
  912. var controlIndex = m_ControlTreeIndices[i];
  913. m_ChildrenForEachControl[controlIndex].MarkAsStale();
  914. }
  915. if (rightNode.leftChildIndex != -1)
  916. WritePartialChangedControlStatesInternal(statePtr, stateSizeInBits, stateOffsetInDeviceInBits,
  917. deviceStatePtr, rightNode, leftNode.endBitOffset);
  918. }
  919. }
  920. private void DumpControlBitRangeNode(int nodeIndex, ControlBitRangeNode node, uint startOffset, uint sizeInBits, List<string> output)
  921. {
  922. var names = new List<string>();
  923. for (var i = 0; i < node.controlCount; i++)
  924. {
  925. var controlIndex = m_ControlTreeIndices[node.controlStartIndex + i];
  926. var control = m_ChildrenForEachControl[controlIndex];
  927. names.Add(control.path);
  928. }
  929. var namesStr = string.Join(", ", names);
  930. var children = node.leftChildIndex != -1 ? $" <{node.leftChildIndex}, {node.leftChildIndex + 1}>" : "";
  931. output.Add($"{nodeIndex} [{startOffset}, {startOffset + sizeInBits}]{children}->{namesStr}");
  932. }
  933. private void DumpControlTree(ControlBitRangeNode parentNode, uint startOffset, List<string> output)
  934. {
  935. var leftNode = m_ControlTreeNodes[parentNode.leftChildIndex];
  936. var rightNode = m_ControlTreeNodes[parentNode.leftChildIndex + 1];
  937. DumpControlBitRangeNode(parentNode.leftChildIndex, leftNode, startOffset, leftNode.endBitOffset - startOffset, output);
  938. DumpControlBitRangeNode(parentNode.leftChildIndex + 1, rightNode, leftNode.endBitOffset, (uint)(rightNode.endBitOffset - leftNode.endBitOffset), output);
  939. if (leftNode.leftChildIndex != -1)
  940. DumpControlTree(leftNode, startOffset, output);
  941. if (rightNode.leftChildIndex != -1)
  942. DumpControlTree(rightNode, leftNode.endBitOffset, output);
  943. }
  944. internal string DumpControlTree()
  945. {
  946. var output = new List<string>();
  947. DumpControlTree(m_ControlTreeNodes[0], 0, output);
  948. return string.Join("\n", output);
  949. }
  950. private unsafe void WriteChangedControlStatesInternal(void* statePtr, uint stateSizeInBits,
  951. byte* deviceStatePtr, ControlBitRangeNode parentNode, uint startOffset)
  952. {
  953. var leftNode = m_ControlTreeNodes[parentNode.leftChildIndex];
  954. // have any bits in the region defined by the left node changed?
  955. // TODO recheck
  956. if (HasDataChangedInRange(deviceStatePtr, statePtr, startOffset, leftNode.endBitOffset - startOffset + 1))
  957. {
  958. // update the state of any controls pointed to by the left node
  959. var controlEndIndex = leftNode.controlStartIndex + leftNode.controlCount;
  960. for (int i = leftNode.controlStartIndex; i < controlEndIndex; i++)
  961. {
  962. var controlIndex = m_ControlTreeIndices[i];
  963. var control = m_ChildrenForEachControl[controlIndex];
  964. // nodes aren't always an exact fit for control memory ranges so check here if the control pointed
  965. // at by this node has actually changed state so we don't mark controls as stale needlessly.
  966. // We need to offset the device and new state pointers by the byte offset of the device state block
  967. // because all controls have this offset baked into them, but deviceStatePtr points at the already
  968. // offset block of device memory (remember, all devices share one big block of memory) and statePtr
  969. // points at a block of memory of the same size as the device state.
  970. if (!control.CompareState(deviceStatePtr - m_StateBlock.byteOffset,
  971. (byte*)statePtr - m_StateBlock.byteOffset, null))
  972. control.MarkAsStale();
  973. }
  974. // process the left child node if it exists
  975. if (leftNode.leftChildIndex != -1)
  976. WriteChangedControlStatesInternal(statePtr, stateSizeInBits, deviceStatePtr,
  977. leftNode, startOffset);
  978. }
  979. // process the right child node if it exists
  980. var rightNode = m_ControlTreeNodes[parentNode.leftChildIndex + 1];
  981. Debug.Assert(leftNode.endBitOffset + (rightNode.endBitOffset - leftNode.endBitOffset) < m_StateBlock.sizeInBits,
  982. "Tried to check state memory outside the bounds of the current device.");
  983. // if no bits in the range defined by the right node have changed, return
  984. // TODO recheck
  985. if (!HasDataChangedInRange(deviceStatePtr, statePtr, leftNode.endBitOffset,
  986. (uint)(rightNode.endBitOffset - leftNode.endBitOffset + 1)))
  987. return;
  988. // update the state of any controls pointed to by the right node
  989. var rightNodeControlEndIndex = rightNode.controlStartIndex + rightNode.controlCount;
  990. for (int i = rightNode.controlStartIndex; i < rightNodeControlEndIndex; i++)
  991. {
  992. var controlIndex = m_ControlTreeIndices[i];
  993. var control = m_ChildrenForEachControl[controlIndex];
  994. if (!control.CompareState(deviceStatePtr - m_StateBlock.byteOffset,
  995. (byte*)statePtr - m_StateBlock.byteOffset, null))
  996. control.MarkAsStale();
  997. }
  998. if (rightNode.leftChildIndex != -1)
  999. WriteChangedControlStatesInternal(statePtr, stateSizeInBits, deviceStatePtr,
  1000. rightNode, leftNode.endBitOffset);
  1001. }
  1002. private static unsafe bool HasDataChangedInRange(byte* deviceStatePtr, void* statePtr, uint startOffset, uint sizeInBits)
  1003. {
  1004. if (sizeInBits == 1)
  1005. return MemoryHelpers.ReadSingleBit(deviceStatePtr, startOffset) !=
  1006. MemoryHelpers.ReadSingleBit(statePtr, startOffset);
  1007. return !MemoryHelpers.MemCmpBitRegion(deviceStatePtr, statePtr,
  1008. startOffset, sizeInBits);
  1009. }
  1010. }
  1011. }