暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

InputBindingResolver.cs 39KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using Unity.Collections;
  5. using UnityEngine.InputSystem.Utilities;
  6. ////TODO: reuse interaction, processor, and composite instances from prior resolves
  7. namespace UnityEngine.InputSystem
  8. {
  9. /// <summary>
  10. /// Heart of the binding resolution machinery. Consumes lists of bindings
  11. /// and spits out out a list of resolved bindings together with their needed
  12. /// execution state.
  13. /// </summary>
  14. /// <remarks>
  15. /// One or more <see cref="InputActionMap">action maps</see> can be added to the same
  16. /// resolver. The result is a combination of the binding state of all maps.
  17. ///
  18. /// The data set up by a resolver is for consumption by <see cref="InputActionState"/>.
  19. /// Essentially, InputBindingResolver does all the wiring and <see cref="InputActionState"/>
  20. /// does all the actual execution based on the resulting data.
  21. /// </remarks>
  22. /// <seealso cref="InputActionState.Initialize"/>
  23. internal struct InputBindingResolver : IDisposable
  24. {
  25. public int totalProcessorCount;
  26. public int totalCompositeCount;
  27. public int totalInteractionCount;
  28. public int totalMapCount => memory.mapCount;
  29. public int totalActionCount => memory.actionCount;
  30. public int totalBindingCount => memory.bindingCount;
  31. public int totalControlCount => memory.controlCount;
  32. public InputActionMap[] maps;
  33. public InputControl[] controls;
  34. public InputActionState.UnmanagedMemory memory;
  35. public IInputInteraction[] interactions;
  36. public InputProcessor[] processors;
  37. public InputBindingComposite[] composites;
  38. /// <summary>
  39. /// Binding mask used to globally mask out bindings.
  40. /// </summary>
  41. /// <remarks>
  42. /// This is empty by default.
  43. ///
  44. /// The bindings of each map will be <see cref="InputBinding.Matches">matched</see> against this
  45. /// binding. Any bindings that don't match will get skipped and not resolved to controls.
  46. ///
  47. /// Note that regardless of whether a binding will be resolved to controls or not, it will get
  48. /// an entry in <see cref="memory"/>. Otherwise we would have to have a more complicated
  49. /// mapping from <see cref="InputActionMap.bindings"/> to a binding state in <see cref="memory"/>.
  50. /// </remarks>
  51. public InputBinding? bindingMask;
  52. private bool m_IsControlOnlyResolve;
  53. /// <summary>
  54. /// Release native memory held by the resolver.
  55. /// </summary>
  56. public void Dispose()
  57. {
  58. memory.Dispose();
  59. }
  60. /// <summary>
  61. /// Steal the already allocated arrays from the given state.
  62. /// </summary>
  63. /// <param name="state">Action map state that was previously created.</param>
  64. /// <param name="isFullResolve">If false, the only thing that is allowed to change in the re-resolution
  65. /// is the list of controls. In other words, devices may have been added or removed but otherwise the configuration
  66. /// is exactly the same as in the last resolve. If true, anything may have changed and the resolver will only reuse
  67. /// allocations but not contents.</param>
  68. public void StartWithPreviousResolve(InputActionState state, bool isFullResolve)
  69. {
  70. Debug.Assert(state != null, "Received null state");
  71. Debug.Assert(!state.isProcessingControlStateChange,
  72. "Cannot re-resolve bindings for an InputActionState that is currently executing an action callback; binding resolution must be deferred to until after the callback has completed");
  73. m_IsControlOnlyResolve = !isFullResolve;
  74. maps = state.maps;
  75. interactions = state.interactions;
  76. processors = state.processors;
  77. composites = state.composites;
  78. controls = state.controls;
  79. // Clear the arrays so that we don't leave references around.
  80. if (isFullResolve)
  81. {
  82. if (maps != null)
  83. Array.Clear(maps, 0, state.totalMapCount);
  84. if (interactions != null)
  85. Array.Clear(interactions, 0, state.totalInteractionCount);
  86. if (processors != null)
  87. Array.Clear(processors, 0, state.totalProcessorCount);
  88. if (composites != null)
  89. Array.Clear(composites, 0, state.totalCompositeCount);
  90. }
  91. if (controls != null) // Always clear this one as every resolve will change it.
  92. Array.Clear(controls, 0, state.totalControlCount);
  93. // Null out the arrays on the state so that there is no strange bugs with
  94. // the state reading from arrays that no longer belong to it.
  95. state.maps = null;
  96. state.interactions = null;
  97. state.processors = null;
  98. state.composites = null;
  99. state.controls = null;
  100. }
  101. /// <summary>
  102. /// Resolve and add all bindings and actions from the given map.
  103. /// </summary>
  104. /// <param name="actionMap"></param>
  105. /// <remarks>
  106. /// This is where all binding resolution happens for actions. The method walks through the binding array
  107. /// in <paramref name="actionMap"/> and adds any controls, interactions, processors, and composites as it goes.
  108. /// </remarks>
  109. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1809:AvoidExcessiveLocals", Justification = "TODO: Refactor later.")]
  110. public unsafe void AddActionMap(InputActionMap actionMap)
  111. {
  112. Debug.Assert(actionMap != null, "Received null map");
  113. InputSystem.EnsureInitialized();
  114. var actionsInThisMap = actionMap.m_Actions;
  115. var bindingsInThisMap = actionMap.m_Bindings;
  116. var bindingCountInThisMap = bindingsInThisMap?.Length ?? 0;
  117. var actionCountInThisMap = actionsInThisMap?.Length ?? 0;
  118. var mapIndex = totalMapCount;
  119. // Keep track of indices for this map.
  120. var actionStartIndex = totalActionCount;
  121. var bindingStartIndex = totalBindingCount;
  122. var controlStartIndex = totalControlCount;
  123. var interactionStartIndex = totalInteractionCount;
  124. var processorStartIndex = totalProcessorCount;
  125. var compositeStartIndex = totalCompositeCount;
  126. // Allocate an initial block of memory. We probably will have to re-allocate once
  127. // at the end to accommodate interactions and controls added from the map.
  128. var newMemory = new InputActionState.UnmanagedMemory();
  129. newMemory.Allocate(
  130. mapCount: totalMapCount + 1,
  131. actionCount: totalActionCount + actionCountInThisMap,
  132. bindingCount: totalBindingCount + bindingCountInThisMap,
  133. // We reallocate for the following once we know the final count.
  134. interactionCount: totalInteractionCount,
  135. compositeCount: totalCompositeCount,
  136. controlCount: totalControlCount);
  137. if (memory.isAllocated)
  138. newMemory.CopyDataFrom(memory);
  139. ////TODO: make sure composite objects get all the bindings they need
  140. ////TODO: handle case where we have bindings resolving to the same control
  141. //// (not so clear cut what to do there; each binding may have a different interaction setup, for example)
  142. var currentCompositeBindingIndex = InputActionState.kInvalidIndex;
  143. var currentCompositeIndex = InputActionState.kInvalidIndex;
  144. var currentCompositePartCount = 0;
  145. var currentCompositeActionIndexInMap = InputActionState.kInvalidIndex;
  146. InputAction currentCompositeAction = null;
  147. var bindingMaskOnThisMap = actionMap.m_BindingMask;
  148. var devicesForThisMap = actionMap.devices;
  149. var isSingletonAction = actionMap.m_SingletonAction != null;
  150. // Can't use `using` as we need to use it with `ref`.
  151. var resolvedControls = new InputControlList<InputControl>(Allocator.Temp);
  152. // We gather all controls in temporary memory and then move them over into newMemory once
  153. // we're done resolving.
  154. try
  155. {
  156. for (var n = 0; n < bindingCountInThisMap; ++n)
  157. {
  158. var bindingStatesPtr = newMemory.bindingStates;
  159. ref var unresolvedBinding = ref bindingsInThisMap[n];
  160. var bindingIndex = bindingStartIndex + n;
  161. var isComposite = unresolvedBinding.isComposite;
  162. var isPartOfComposite = !isComposite && unresolvedBinding.isPartOfComposite;
  163. var bindingState = &bindingStatesPtr[bindingIndex];
  164. try
  165. {
  166. ////TODO: if it's a composite, check if any of the children matches our binding masks (if any) and skip composite if none do
  167. var firstControlIndex = 0; // numControls dictates whether this is a valid index or not.
  168. var firstInteractionIndex = InputActionState.kInvalidIndex;
  169. var firstProcessorIndex = InputActionState.kInvalidIndex;
  170. var actionIndexForBinding = InputActionState.kInvalidIndex;
  171. var partIndex = InputActionState.kInvalidIndex;
  172. var numControls = 0;
  173. var numInteractions = 0;
  174. var numProcessors = 0;
  175. // Make sure that if it's part of a composite, we are actually part of a composite.
  176. if (isPartOfComposite && currentCompositeBindingIndex == InputActionState.kInvalidIndex)
  177. throw new InvalidOperationException(
  178. $"Binding '{unresolvedBinding}' is marked as being part of a composite but the preceding binding is not a composite");
  179. // Try to find action.
  180. //
  181. // NOTE: We ignore actions on bindings that are part of composites. We only allow
  182. // actions to be triggered from the composite itself.
  183. var actionIndexInMap = InputActionState.kInvalidIndex;
  184. var actionName = unresolvedBinding.action;
  185. InputAction action = null;
  186. if (!isPartOfComposite)
  187. {
  188. if (isSingletonAction)
  189. {
  190. // Singleton actions always ignore names.
  191. actionIndexInMap = 0;
  192. }
  193. else if (!string.IsNullOrEmpty(actionName))
  194. {
  195. ////REVIEW: should we fail here if we don't manage to find the action
  196. actionIndexInMap = actionMap.FindActionIndex(actionName);
  197. }
  198. if (actionIndexInMap != InputActionState.kInvalidIndex)
  199. action = actionsInThisMap[actionIndexInMap];
  200. }
  201. else
  202. {
  203. actionIndexInMap = currentCompositeActionIndexInMap;
  204. action = currentCompositeAction;
  205. }
  206. // If it's a composite, start a chain.
  207. if (isComposite)
  208. {
  209. currentCompositeBindingIndex = bindingIndex;
  210. currentCompositeAction = action;
  211. currentCompositeActionIndexInMap = actionIndexInMap;
  212. }
  213. // Determine if the binding is disabled.
  214. // Disabled if path is empty.
  215. var path = unresolvedBinding.effectivePath;
  216. var bindingIsDisabled = string.IsNullOrEmpty(path)
  217. // Also, if we can't find the action to trigger for the binding, we just go and disable
  218. // the binding.
  219. || action == null
  220. // Also, disabled if binding doesn't match with our binding mask (might be empty).
  221. || (!isComposite && bindingMask != null &&
  222. !bindingMask.Value.Matches(ref unresolvedBinding,
  223. InputBinding.MatchOptions.EmptyGroupMatchesAny))
  224. // Also, disabled if binding doesn't match the binding mask on the map (might be empty).
  225. || (!isComposite && bindingMaskOnThisMap != null &&
  226. !bindingMaskOnThisMap.Value.Matches(ref unresolvedBinding,
  227. InputBinding.MatchOptions.EmptyGroupMatchesAny))
  228. // Finally, also disabled if binding doesn't match the binding mask on the action (might be empty).
  229. || (!isComposite && action?.m_BindingMask != null &&
  230. !action.m_BindingMask.Value.Matches(ref unresolvedBinding,
  231. InputBinding.MatchOptions.EmptyGroupMatchesAny));
  232. // If the binding isn't disabled, look up controls now. We do this first as we may still disable the
  233. // binding if it doesn't resolve to any controls or resolves only to controls already bound to by
  234. // other bindings.
  235. //
  236. // NOTE: We continuously add controls here to `resolvedControls`. Once we've completed our
  237. // pass over the bindings in the map, `resolvedControls` will have all the controls for
  238. // the current map.
  239. if (!bindingIsDisabled && !isComposite)
  240. {
  241. firstControlIndex = memory.controlCount + resolvedControls.Count;
  242. if (devicesForThisMap != null)
  243. {
  244. // Search in devices for only this map.
  245. var list = devicesForThisMap.Value;
  246. for (var i = 0; i < list.Count; ++i)
  247. {
  248. var device = list[i];
  249. if (!device.added)
  250. continue; // Skip devices that have been removed.
  251. numControls += InputControlPath.TryFindControls(device, path, 0, ref resolvedControls);
  252. }
  253. }
  254. else
  255. {
  256. // Search globally.
  257. numControls = InputSystem.FindControls(path, ref resolvedControls);
  258. }
  259. }
  260. // If the binding isn't disabled, resolve its controls, processors, and interactions.
  261. if (!bindingIsDisabled)
  262. {
  263. // NOTE: When isFullResolve==false, it is *imperative* that we do count processor and interaction
  264. // counts here come out exactly the same as in the previous full resolve.
  265. // Instantiate processors.
  266. var processorString = unresolvedBinding.effectiveProcessors;
  267. if (!string.IsNullOrEmpty(processorString))
  268. {
  269. // Add processors from binding.
  270. firstProcessorIndex = InstantiateWithParameters(InputProcessor.s_Processors, processorString,
  271. ref processors, ref totalProcessorCount, actionMap, ref unresolvedBinding);
  272. if (firstProcessorIndex != InputActionState.kInvalidIndex)
  273. numProcessors = totalProcessorCount - firstProcessorIndex;
  274. }
  275. if (!string.IsNullOrEmpty(action.m_Processors))
  276. {
  277. // Add processors from action.
  278. var index = InstantiateWithParameters(InputProcessor.s_Processors, action.m_Processors, ref processors,
  279. ref totalProcessorCount, actionMap, ref unresolvedBinding);
  280. if (index != InputActionState.kInvalidIndex)
  281. {
  282. if (firstProcessorIndex == InputActionState.kInvalidIndex)
  283. firstProcessorIndex = index;
  284. numProcessors += totalProcessorCount - index;
  285. }
  286. }
  287. // Instantiate interactions.
  288. var interactionString = unresolvedBinding.effectiveInteractions;
  289. if (!string.IsNullOrEmpty(interactionString))
  290. {
  291. // Add interactions from binding.
  292. firstInteractionIndex = InstantiateWithParameters(InputInteraction.s_Interactions, interactionString,
  293. ref interactions, ref totalInteractionCount, actionMap, ref unresolvedBinding);
  294. if (firstInteractionIndex != InputActionState.kInvalidIndex)
  295. numInteractions = totalInteractionCount - firstInteractionIndex;
  296. }
  297. if (!string.IsNullOrEmpty(action.m_Interactions))
  298. {
  299. // Add interactions from action.
  300. var index = InstantiateWithParameters(InputInteraction.s_Interactions, action.m_Interactions,
  301. ref interactions, ref totalInteractionCount, actionMap, ref unresolvedBinding);
  302. if (index != InputActionState.kInvalidIndex)
  303. {
  304. if (firstInteractionIndex == InputActionState.kInvalidIndex)
  305. firstInteractionIndex = index;
  306. numInteractions += totalInteractionCount - index;
  307. }
  308. }
  309. // If it's the start of a composite chain, create the composite.
  310. if (isComposite)
  311. {
  312. // The composite binding entry itself does not resolve to any controls.
  313. // It creates a composite binding object which is then populated from
  314. // subsequent bindings.
  315. // Instantiate. For composites, the path is the name of the composite.
  316. var composite = InstantiateBindingComposite(ref unresolvedBinding, actionMap);
  317. currentCompositeIndex =
  318. ArrayHelpers.AppendWithCapacity(ref composites, ref totalCompositeCount, composite);
  319. // Record where the controls for parts of the composite start.
  320. firstControlIndex = memory.controlCount + resolvedControls.Count;
  321. }
  322. else
  323. {
  324. // If we've reached the end of a composite chain, finish
  325. // off the current composite.
  326. if (!isPartOfComposite && currentCompositeBindingIndex != InputActionState.kInvalidIndex)
  327. {
  328. currentCompositePartCount = 0;
  329. currentCompositeBindingIndex = InputActionState.kInvalidIndex;
  330. currentCompositeIndex = InputActionState.kInvalidIndex;
  331. currentCompositeAction = null;
  332. currentCompositeActionIndexInMap = InputActionState.kInvalidIndex;
  333. }
  334. }
  335. }
  336. // If the binding is part of a composite, pass the resolved controls
  337. // on to the composite.
  338. if (isPartOfComposite && currentCompositeBindingIndex != InputActionState.kInvalidIndex && numControls > 0)
  339. {
  340. // Make sure the binding is named. The name determines what in the composite
  341. // to bind to.
  342. if (string.IsNullOrEmpty(unresolvedBinding.name))
  343. throw new InvalidOperationException(
  344. $"Binding '{unresolvedBinding}' that is part of composite '{composites[currentCompositeIndex]}' is missing a name");
  345. // Assign an index to the current part of the composite which
  346. // can be used by the composite to read input from this part.
  347. partIndex = AssignCompositePartIndex(composites[currentCompositeIndex], unresolvedBinding.name,
  348. ref currentCompositePartCount);
  349. // Keep track of total number of controls bound in the composite.
  350. bindingStatesPtr[currentCompositeBindingIndex].controlCount += numControls;
  351. // Force action index on part binding to be same as that of composite.
  352. actionIndexForBinding = bindingStatesPtr[currentCompositeBindingIndex].actionIndex;
  353. }
  354. else if (actionIndexInMap != InputActionState.kInvalidIndex)
  355. {
  356. actionIndexForBinding = actionStartIndex + actionIndexInMap;
  357. }
  358. // Store resolved binding.
  359. *bindingState = new InputActionState.BindingState
  360. {
  361. controlStartIndex = firstControlIndex,
  362. // For composites, this will be adjusted as we add each part.
  363. controlCount = numControls,
  364. interactionStartIndex = firstInteractionIndex,
  365. interactionCount = numInteractions,
  366. processorStartIndex = firstProcessorIndex,
  367. processorCount = numProcessors,
  368. isComposite = isComposite,
  369. isPartOfComposite = unresolvedBinding.isPartOfComposite,
  370. partIndex = partIndex,
  371. actionIndex = actionIndexForBinding,
  372. compositeOrCompositeBindingIndex = isComposite ? currentCompositeIndex : currentCompositeBindingIndex,
  373. mapIndex = totalMapCount,
  374. wantsInitialStateCheck = action?.wantsInitialStateCheck ?? false
  375. };
  376. }
  377. catch (Exception exception)
  378. {
  379. Debug.LogError(
  380. $"{exception.GetType().Name} while resolving binding '{unresolvedBinding}' in action map '{actionMap}'");
  381. Debug.LogException(exception);
  382. // Don't swallow exceptions that indicate something is wrong in the code rather than
  383. // in the data.
  384. if (exception.IsExceptionIndicatingBugInCode())
  385. throw;
  386. }
  387. }
  388. // Re-allocate memory to accommodate controls and interaction states. The count for those
  389. // we only know once we've completed all resolution.
  390. var controlCountInThisMap = resolvedControls.Count;
  391. var newTotalControlCount = memory.controlCount + controlCountInThisMap;
  392. if (newMemory.interactionCount != totalInteractionCount ||
  393. newMemory.compositeCount != totalCompositeCount ||
  394. newMemory.controlCount != newTotalControlCount)
  395. {
  396. var finalMemory = new InputActionState.UnmanagedMemory();
  397. finalMemory.Allocate(
  398. mapCount: newMemory.mapCount,
  399. actionCount: newMemory.actionCount,
  400. bindingCount: newMemory.bindingCount,
  401. controlCount: newTotalControlCount,
  402. interactionCount: totalInteractionCount,
  403. compositeCount: totalCompositeCount);
  404. finalMemory.CopyDataFrom(newMemory);
  405. newMemory.Dispose();
  406. newMemory = finalMemory;
  407. }
  408. // Add controls to array.
  409. var controlCountInArray = memory.controlCount;
  410. ArrayHelpers.AppendListWithCapacity(ref controls, ref controlCountInArray, resolvedControls);
  411. Debug.Assert(controlCountInArray == newTotalControlCount,
  412. "Control array should have combined count of old and new controls");
  413. // Set up control to binding index mapping.
  414. for (var i = 0; i < bindingCountInThisMap; ++i)
  415. {
  416. var bindingStatesPtr = newMemory.bindingStates;
  417. var bindingState = &bindingStatesPtr[bindingStartIndex + i];
  418. var numControls = bindingState->controlCount;
  419. var startIndex = bindingState->controlStartIndex;
  420. for (var n = 0; n < numControls; ++n)
  421. newMemory.controlIndexToBindingIndex[startIndex + n] = bindingStartIndex + i;
  422. }
  423. // Initialize initial interaction states.
  424. for (var i = memory.interactionCount; i < newMemory.interactionCount; ++i)
  425. {
  426. ref var interactionState = ref newMemory.interactionStates[i];
  427. interactionState.phase = InputActionPhase.Waiting;
  428. interactionState.triggerControlIndex = InputActionState.kInvalidIndex;
  429. }
  430. // Initialize action data.
  431. var runningIndexInBindingIndices = memory.bindingCount;
  432. for (var i = 0; i < actionCountInThisMap; ++i)
  433. {
  434. var action = actionsInThisMap[i];
  435. var actionIndex = actionStartIndex + i;
  436. // Correlate action with its trigger state.
  437. action.m_ActionIndexInState = actionIndex;
  438. Debug.Assert(runningIndexInBindingIndices < ushort.MaxValue, "Binding start index on action exceeds limit");
  439. newMemory.actionBindingIndicesAndCounts[actionIndex * 2] = (ushort)runningIndexInBindingIndices;
  440. // Collect bindings for action.
  441. var firstBindingIndexForAction = -1;
  442. var bindingCountForAction = 0;
  443. var numPossibleConcurrentActuations = 0;
  444. for (var n = 0; n < bindingCountInThisMap; ++n)
  445. {
  446. var bindingIndex = bindingStartIndex + n;
  447. var bindingState = &newMemory.bindingStates[bindingIndex];
  448. if (bindingState->actionIndex != actionIndex)
  449. continue;
  450. if (bindingState->isPartOfComposite)
  451. continue;
  452. Debug.Assert(bindingIndex <= ushort.MaxValue, "Binding index exceeds limit");
  453. newMemory.actionBindingIndices[runningIndexInBindingIndices] = (ushort)bindingIndex;
  454. ++runningIndexInBindingIndices;
  455. ++bindingCountForAction;
  456. if (firstBindingIndexForAction == -1)
  457. firstBindingIndexForAction = bindingIndex;
  458. // Keep track of how many concurrent actuations we may be seeing on the action so that
  459. // we know whether we need to enable conflict resolution or not.
  460. if (bindingState->isComposite)
  461. {
  462. // Composite binding. Actuates as a whole. Check if the composite has successfully
  463. // resolved any controls. If so, it adds one possible actuation.
  464. if (bindingState->controlCount > 0)
  465. ++numPossibleConcurrentActuations;
  466. }
  467. else
  468. {
  469. // Normal binding. Every successfully resolved control results in one possible actuation.
  470. numPossibleConcurrentActuations += bindingState->controlCount;
  471. }
  472. }
  473. if (firstBindingIndexForAction == -1)
  474. firstBindingIndexForAction = 0;
  475. Debug.Assert(bindingCountForAction < ushort.MaxValue, "Binding count on action exceeds limit");
  476. newMemory.actionBindingIndicesAndCounts[actionIndex * 2 + 1] = (ushort)bindingCountForAction;
  477. // See if we may need conflict resolution on this action. Never needed for pass-through actions.
  478. // Otherwise, if we have more than one bound control or have several bindings and one of them
  479. // is a composite, we enable it.
  480. var isPassThroughAction = action.type == InputActionType.PassThrough;
  481. var isButtonAction = action.type == InputActionType.Button;
  482. var mayNeedConflictResolution = !isPassThroughAction && numPossibleConcurrentActuations > 1;
  483. // Initialize initial trigger state.
  484. newMemory.actionStates[actionIndex] =
  485. new InputActionState.TriggerState
  486. {
  487. phase = InputActionPhase.Disabled,
  488. mapIndex = mapIndex,
  489. controlIndex = InputActionState.kInvalidIndex,
  490. interactionIndex = InputActionState.kInvalidIndex,
  491. isPassThrough = isPassThroughAction,
  492. isButton = isButtonAction,
  493. mayNeedConflictResolution = mayNeedConflictResolution,
  494. bindingIndex = firstBindingIndexForAction
  495. };
  496. }
  497. // Store indices for map.
  498. newMemory.mapIndices[mapIndex] =
  499. new InputActionState.ActionMapIndices
  500. {
  501. actionStartIndex = actionStartIndex,
  502. actionCount = actionCountInThisMap,
  503. controlStartIndex = controlStartIndex,
  504. controlCount = controlCountInThisMap,
  505. bindingStartIndex = bindingStartIndex,
  506. bindingCount = bindingCountInThisMap,
  507. interactionStartIndex = interactionStartIndex,
  508. interactionCount = totalInteractionCount - interactionStartIndex,
  509. processorStartIndex = processorStartIndex,
  510. processorCount = totalProcessorCount - processorStartIndex,
  511. compositeStartIndex = compositeStartIndex,
  512. compositeCount = totalCompositeCount - compositeStartIndex,
  513. };
  514. actionMap.m_MapIndexInState = mapIndex;
  515. var finalActionMapCount = memory.mapCount;
  516. ArrayHelpers.AppendWithCapacity(ref maps, ref finalActionMapCount, actionMap, capacityIncrement: 4);
  517. Debug.Assert(finalActionMapCount == newMemory.mapCount,
  518. "Final action map count should match old action map count plus one");
  519. // As a final act, swap the new memory in.
  520. memory.Dispose();
  521. memory = newMemory;
  522. }
  523. catch (Exception)
  524. {
  525. // Don't leak our native memory when we throw an exception.
  526. newMemory.Dispose();
  527. throw;
  528. }
  529. finally
  530. {
  531. resolvedControls.Dispose();
  532. }
  533. }
  534. private List<NameAndParameters> m_Parameters; // We retain this to reuse the allocation.
  535. private int InstantiateWithParameters<TType>(TypeTable registrations, string namesAndParameters, ref TType[] array, ref int count, InputActionMap actionMap, ref InputBinding binding)
  536. {
  537. if (!NameAndParameters.ParseMultiple(namesAndParameters, ref m_Parameters))
  538. return InputActionState.kInvalidIndex;
  539. var firstIndex = count;
  540. for (var i = 0; i < m_Parameters.Count; ++i)
  541. {
  542. // Look up type.
  543. var objectRegistrationName = m_Parameters[i].name;
  544. var type = registrations.LookupTypeRegistration(objectRegistrationName);
  545. if (type == null)
  546. {
  547. Debug.LogError(
  548. $"No {typeof(TType).Name} with name '{objectRegistrationName}' (mentioned in '{namesAndParameters}') has been registered");
  549. continue;
  550. }
  551. if (!m_IsControlOnlyResolve)
  552. {
  553. // Instantiate it.
  554. if (!(Activator.CreateInstance(type) is TType instance))
  555. {
  556. Debug.LogError(
  557. $"Type '{type.Name}' registered as '{objectRegistrationName}' (mentioned in '{namesAndParameters}') is not an {typeof(TType).Name}");
  558. continue;
  559. }
  560. // Pass parameters to it.
  561. ApplyParameters(m_Parameters[i].parameters, instance, actionMap, ref binding, objectRegistrationName,
  562. namesAndParameters);
  563. // Add to list.
  564. ArrayHelpers.AppendWithCapacity(ref array, ref count, instance);
  565. }
  566. else
  567. {
  568. Debug.Assert(type.IsInstanceOfType(array[count]), "Type of instance in array does not match expected type");
  569. ++count;
  570. }
  571. }
  572. return firstIndex;
  573. }
  574. private static InputBindingComposite InstantiateBindingComposite(ref InputBinding binding, InputActionMap actionMap)
  575. {
  576. var nameAndParametersParsed = NameAndParameters.Parse(binding.effectivePath);
  577. // Look up.
  578. var type = InputBindingComposite.s_Composites.LookupTypeRegistration(nameAndParametersParsed.name);
  579. if (type == null)
  580. throw new InvalidOperationException(
  581. $"No binding composite with name '{nameAndParametersParsed.name}' has been registered");
  582. // Instantiate.
  583. if (!(Activator.CreateInstance(type) is InputBindingComposite instance))
  584. throw new InvalidOperationException(
  585. $"Registered type '{type.Name}' used for '{nameAndParametersParsed.name}' is not an InputBindingComposite");
  586. // Set parameters.
  587. ApplyParameters(nameAndParametersParsed.parameters, instance, actionMap, ref binding, nameAndParametersParsed.name,
  588. binding.effectivePath);
  589. return instance;
  590. }
  591. private static void ApplyParameters(ReadOnlyArray<NamedValue> parameters, object instance, InputActionMap actionMap, ref InputBinding binding, string objectRegistrationName, string namesAndParameters)
  592. {
  593. foreach (var parameter in parameters)
  594. {
  595. // Find field.
  596. var field = instance.GetType().GetField(parameter.name,
  597. BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  598. if (field == null)
  599. {
  600. Debug.LogError(
  601. $"Type '{instance.GetType().Name}' registered as '{objectRegistrationName}' (mentioned in '{namesAndParameters}') has no public field called '{parameter.name}'");
  602. continue;
  603. }
  604. var fieldTypeCode = Type.GetTypeCode(field.FieldType);
  605. // See if we have a parameter override.
  606. var parameterOverride =
  607. InputActionRebindingExtensions.ParameterOverride.Find(actionMap, ref binding, parameter.name, objectRegistrationName);
  608. var value = parameterOverride != null
  609. ? parameterOverride.Value.value
  610. : parameter.value;
  611. field.SetValue(instance, value.ConvertTo(fieldTypeCode).ToObject());
  612. }
  613. }
  614. private static int AssignCompositePartIndex(object composite, string name, ref int currentCompositePartCount)
  615. {
  616. var type = composite.GetType();
  617. ////REVIEW: check for [InputControl] attribute?
  618. ////TODO: allow this to be a property instead
  619. // Look up field.
  620. var field = type.GetField(name,
  621. BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  622. if (field == null)
  623. throw new InvalidOperationException(
  624. $"Cannot find public field '{name}' used as parameter of binding composite '{composite}' of type '{type}'");
  625. ////REVIEW: should we wrap part numbers in a struct instead of using int?
  626. // Type-check.
  627. var fieldType = field.FieldType;
  628. if (fieldType != typeof(int))
  629. throw new InvalidOperationException(
  630. $"Field '{name}' used as a parameter of binding composite '{composite}' must be of type 'int' but is of type '{type.Name}' instead");
  631. ////REVIEW: this creates garbage; need a better solution to get to zero garbage during re-resolving
  632. // See if we've already assigned a part index. This can happen if there are multiple bindings
  633. // for the same named slot on the composite (e.g. multiple "Negative" bindings on an axis composite).
  634. var partIndex = (int)field.GetValue(composite);
  635. if (partIndex == 0)
  636. {
  637. // No, not assigned yet. Create new part index.
  638. partIndex = ++currentCompositePartCount;
  639. field.SetValue(composite, partIndex);
  640. }
  641. return partIndex;
  642. }
  643. }
  644. }