Brak opisu
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.

InputProcessor.cs 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. using System;
  2. using Unity.Collections.LowLevel.Unsafe;
  3. using UnityEngine.InputSystem.Layouts;
  4. using UnityEngine.InputSystem.Utilities;
  5. using UnityEngine.Scripting;
  6. ////TODO: come up with a mechanism to allow (certain) processors to be stateful
  7. ////TODO: cache processors globally; there's no need to instantiate the same processor with the same parameters multiple times
  8. //// (except if they do end up being stateful)
  9. namespace UnityEngine.InputSystem
  10. {
  11. /// <summary>
  12. /// A processor that conditions/transforms input values.
  13. /// </summary>
  14. /// <remarks>
  15. /// To define a custom processor, it is usable best to derive from <see cref="InputProcessor{TValue}"/>
  16. /// instead of from this class. Doing so will avoid having to deal with things such as the raw memory
  17. /// buffers of <see cref="Process"/>.
  18. ///
  19. /// Note, however, that if you do want to define a processor that can process more than one type of
  20. /// value, you can derive directly from this class.
  21. /// </remarks>
  22. /// <seealso cref="InputBinding.processors"/>
  23. /// <seealso cref="InputControlLayout.ControlItem.processors"/>
  24. /// <seealso cref="InputSystem.RegisterProcessor{T}"/>
  25. /// <seealso cref="InputActionRebindingExtensions.GetParameterValue(InputAction,string,InputBinding)"/>
  26. /// <seealso cref="InputActionRebindingExtensions.ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/>
  27. public abstract class InputProcessor
  28. {
  29. /// <summary>
  30. /// Process an input value, given as an object, and return the processed value as an object.
  31. /// </summary>
  32. /// <param name="value">A value matching the processor's value type.</param>
  33. /// <param name="control">Optional control that the value originated from. Must have the same value type
  34. /// that the processor has.</param>
  35. /// <returns>A processed value based on <paramref name="value"/>.</returns>
  36. /// <remarks>
  37. /// This method allocates GC heap memory. To process values without allocating GC memory, it is necessary to either know
  38. /// the value type of a processor at compile time and call <see cref="InputProcessor{TValue}.Process(TValue,UnityEngine.InputSystem.InputControl)"/>
  39. /// directly or to use <see cref="Process"/> instead and process values in raw memory buffers.
  40. /// </remarks>
  41. public abstract object ProcessAsObject(object value, InputControl control);
  42. /// <summary>
  43. /// Process an input value stored in the given memory buffer.
  44. /// </summary>
  45. /// <param name="buffer">Memory buffer containing the input value. Must be at least large enough
  46. /// to hold one full value as indicated by <paramref name="bufferSize"/>.</param>
  47. /// <param name="bufferSize">Size (in bytes) of the value inside <paramref name="buffer"/>.</param>
  48. /// <param name="control">Optional control that the value originated from. Must have the same value type
  49. /// that the processor has.</param>
  50. /// <remarks>
  51. /// This method allows processing values of arbitrary size without allocating memory on the GC heap.
  52. /// </remarks>
  53. public abstract unsafe void Process(void* buffer, int bufferSize, InputControl control);
  54. internal static TypeTable s_Processors;
  55. /// <summary>
  56. /// Get the value type of a processor without having to instantiate it and use <see cref="valueType"/>.
  57. /// </summary>
  58. /// <param name="processorType"></param>
  59. /// <returns>Value type of the given processor or null if it could not be determined statically.</returns>
  60. /// <exception cref="ArgumentNullException"><paramref name="processorType"/> is null.</exception>
  61. /// <remarks>
  62. /// This method is reliant on the processor being based on <see cref="InputProcessor{TValue}"/>. It will return
  63. /// the <c>TValue</c> argument used with the class. If the processor is not based on <see cref="InputProcessor{TValue}"/>,
  64. /// this method returns null.
  65. /// </remarks>
  66. internal static Type GetValueTypeFromType(Type processorType)
  67. {
  68. if (processorType == null)
  69. throw new ArgumentNullException(nameof(processorType));
  70. return TypeHelpers.GetGenericTypeArgumentFromHierarchy(processorType, typeof(InputProcessor<>), 0);
  71. }
  72. /// <summary>
  73. /// Caching policy regarding usage of return value from processors.
  74. /// </summary>
  75. public enum CachingPolicy
  76. {
  77. /// <summary>
  78. /// Cache result value if unprocessed value has not been changed.
  79. /// </summary>
  80. CacheResult = 0,
  81. /// <summary>
  82. /// Process value every call to <see cref="InputControl{TValue}.ReadValue()"/> even if unprocessed value has not been changed.
  83. /// </summary>
  84. EvaluateOnEveryRead = 1
  85. }
  86. /// <summary>
  87. /// Caching policy of the processor. Override this property to provide a different value.
  88. /// </summary>
  89. public virtual CachingPolicy cachingPolicy => CachingPolicy.CacheResult;
  90. }
  91. /// <summary>
  92. /// A processor that conditions/transforms input values.
  93. /// </summary>
  94. /// <typeparam name="TValue">Type of value to be processed. Only InputControls that use the
  95. /// same value type will be compatible with the processor.</typeparam>
  96. /// <remarks>
  97. /// Each <see cref="InputControl"/> can have a stack of processors assigned to it.
  98. ///
  99. /// Note that processors CANNOT be stateful. If you need processing that requires keeping
  100. /// mutating state over time, use InputActions. All mutable state needs to be
  101. /// kept in the central state buffers.
  102. ///
  103. /// However, processors can have configurable parameters. Every public field on a processor
  104. /// object can be set using "parameters" in JSON or by supplying parameters through the
  105. /// <see cref="InputControlAttribute.processors"/> field.
  106. ///
  107. /// <example>
  108. /// <code>
  109. /// // To register the processor, call
  110. /// //
  111. /// // InputSystem.RegisterProcessor&lt;ScalingProcessor&gt;("scale");
  112. /// //
  113. /// public class ScalingProcessor : InputProcessor&lt;float&gt;
  114. /// {
  115. /// // This field can be set as a parameter. See examples below.
  116. /// // If not explicitly configured, will have its default value.
  117. /// public float factor = 2.0f;
  118. ///
  119. /// public float Process(float value, InputControl control)
  120. /// {
  121. /// return value * factor;
  122. /// }
  123. /// }
  124. ///
  125. /// // Use processor in JSON:
  126. /// const string json = @"
  127. /// {
  128. /// ""name"" : ""MyDevice"",
  129. /// ""controls"" : [
  130. /// { ""name"" : ""axis"", ""layout"" : ""Axis"", ""processors"" : ""scale(factor=4)"" }
  131. /// ]
  132. /// }
  133. /// ";
  134. ///
  135. /// // Use processor on C# state struct:
  136. /// public struct MyDeviceState : IInputStateTypeInfo
  137. /// {
  138. /// [InputControl(layout = "Axis", processors = "scale(factor=4)"]
  139. /// public float axis;
  140. /// }
  141. /// </code>
  142. /// </example>
  143. ///
  144. /// See <see cref="Editor.InputParameterEditor{T}"/> for how to define custom parameter
  145. /// editing UIs for processors.
  146. /// </remarks>
  147. /// <seealso cref="InputSystem.RegisterProcessor"/>
  148. public abstract class InputProcessor<TValue> : InputProcessor
  149. where TValue : struct
  150. {
  151. /// <summary>
  152. /// Process the given value and return the result.
  153. /// </summary>
  154. /// <remarks>
  155. /// The implementation of this method must not be stateful.
  156. /// </remarks>
  157. /// <param name="value">Input value to process.</param>
  158. /// <param name="control">Control that the value originally came from. This can be null if the value did
  159. /// not originate from a control. This can be the case, for example, if the processor sits on a composite
  160. /// binding (<see cref="InputBindingComposite"/>) as composites are not directly associated with controls
  161. /// but rather source their values through their child bindings.</param>
  162. /// <returns>Processed input value.</returns>
  163. public abstract TValue Process(TValue value, InputControl control);
  164. public override object ProcessAsObject(object value, InputControl control)
  165. {
  166. if (value == null)
  167. throw new ArgumentNullException(nameof(value));
  168. if (!(value is TValue))
  169. throw new ArgumentException(
  170. $"Expecting value of type '{typeof(TValue).Name}' but got value '{value}' of type '{value.GetType().Name}'",
  171. nameof(value));
  172. var valueOfType = (TValue)value;
  173. return Process(valueOfType, control);
  174. }
  175. public override unsafe void Process(void* buffer, int bufferSize, InputControl control)
  176. {
  177. if (buffer == null)
  178. throw new ArgumentNullException(nameof(buffer));
  179. var valueSize = UnsafeUtility.SizeOf<TValue>();
  180. if (bufferSize < valueSize)
  181. throw new ArgumentException(
  182. $"Expected buffer of at least {valueSize} bytes but got buffer with just {bufferSize} bytes",
  183. nameof(bufferSize));
  184. var value = default(TValue);
  185. var valuePtr = UnsafeUtility.AddressOf(ref value);
  186. UnsafeUtility.MemCpy(valuePtr, buffer, valueSize);
  187. value = Process(value, control);
  188. valuePtr = UnsafeUtility.AddressOf(ref value);
  189. UnsafeUtility.MemCpy(buffer, valuePtr, valueSize);
  190. }
  191. }
  192. }