InputProcessor.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. [Preserve]
  26. public abstract class InputProcessor
  27. {
  28. /// <summary>
  29. /// Process an input value, given as an object, and return the processed value as an object.
  30. /// </summary>
  31. /// <param name="value">A value of type <see cref="valueType"/>.</param>
  32. /// <param name="control">Optional control that the value originated from. Must have the same value type
  33. /// that the processor has (<see cref="valueType"/>).</param>
  34. /// <returns>A processed value based on <paramref name="value"/>.</returns>
  35. /// <remarks>
  36. /// This method allocates GC heap memory. To process values without allocating GC memory, it is necessary to either know
  37. /// the value type of a processor at compile time and call <see cref="InputProcessor{TValue}.Process(TValue,UnityEngine.InputSystem.InputControl)"/>
  38. /// directly or to use <see cref="Process"/> instead and process values in raw memory buffers.
  39. /// </remarks>
  40. public abstract object ProcessAsObject(object value, InputControl control);
  41. public abstract unsafe void Process(void* buffer, int bufferSize, InputControl control);
  42. internal static TypeTable s_Processors;
  43. /// <summary>
  44. /// Get the value type of a processor without having to instantiate it and use <see cref="valueType"/>.
  45. /// </summary>
  46. /// <param name="processorType"></param>
  47. /// <returns>Value type of the given processor or null if it could not be determined statically.</returns>
  48. /// <exception cref="ArgumentNullException"><paramref name="processorType"/> is null.</exception>
  49. /// <remarks>
  50. /// This method is reliant on the processor being based on <see cref="InputProcessor{TValue}"/>. It will return
  51. /// the <c>TValue</c> argument used with the class. If the processor is not based on <see cref="InputProcessor{TValue}"/>,
  52. /// this method returns null.
  53. /// </remarks>
  54. internal static Type GetValueTypeFromType(Type processorType)
  55. {
  56. if (processorType == null)
  57. throw new ArgumentNullException(nameof(processorType));
  58. return TypeHelpers.GetGenericTypeArgumentFromHierarchy(processorType, typeof(InputProcessor<>), 0);
  59. }
  60. }
  61. /// <summary>
  62. /// A processor that conditions/transforms input values.
  63. /// </summary>
  64. /// <typeparam name="TValue">Type of value to be processed. Only InputControls that use the
  65. /// same value type will be compatible with the processor.</typeparam>
  66. /// <remarks>
  67. /// Each <see cref="InputControl"/> can have a stack of processors assigned to it.
  68. ///
  69. /// Note that processors CANNOT be stateful. If you need processing that requires keeping
  70. /// mutating state over time, use InputActions. All mutable state needs to be
  71. /// kept in the central state buffers.
  72. ///
  73. /// However, processors can have configurable parameters. Every public field on a processor
  74. /// object can be set using "parameters" in JSON or by supplying parameters through the
  75. /// <see cref="InputControlAttribute.processors"/> field.
  76. ///
  77. /// <example>
  78. /// <code>
  79. /// // To register the processor, call
  80. /// //
  81. /// // InputSystem.RegisterProcessor&lt;ScalingProcessor&gt;("scale");
  82. /// //
  83. /// public class ScalingProcessor : InputProcessor&lt;float&gt;
  84. /// {
  85. /// // This field can be set as a parameter. See examples below.
  86. /// // If not explicitly configured, will have its default value.
  87. /// public float factor = 2.0f;
  88. ///
  89. /// public float Process(float value, InputControl control)
  90. /// {
  91. /// return value * factor;
  92. /// }
  93. /// }
  94. ///
  95. /// // Use processor in JSON:
  96. /// const string json = @"
  97. /// {
  98. /// ""name"" : ""MyDevice"",
  99. /// ""controls"" : [
  100. /// { ""name"" : ""axis"", ""layout"" : ""Axis"", ""processors"" : ""scale(factor=4)"" }
  101. /// ]
  102. /// }
  103. /// ";
  104. ///
  105. /// // Use processor on C# state struct:
  106. /// public struct MyDeviceState : IInputStateTypeInfo
  107. /// {
  108. /// [InputControl(layout = "Axis", processors = "scale(factor=4)"]
  109. /// public float axis;
  110. /// }
  111. /// </code>
  112. /// </example>
  113. ///
  114. /// See <see cref="Editor.InputParameterEditor{T}"/> for how to define custom parameter
  115. /// editing UIs for processors.
  116. /// </remarks>
  117. /// <seealso cref="InputSystem.RegisterProcessor"/>
  118. [Scripting.Preserve]
  119. public abstract class InputProcessor<TValue> : InputProcessor
  120. where TValue : struct
  121. {
  122. /// <summary>
  123. /// Process the given value and return the result.
  124. /// </summary>
  125. /// <remarks>
  126. /// The implementation of this method must not be stateful.
  127. /// </remarks>
  128. /// <param name="value">Input value to process.</param>
  129. /// <param name="control">Control that the value originally came from. This can be null if the value did
  130. /// not originate from a control. This can be the case, for example, if the processor sits on a composite
  131. /// binding (<see cref="InputBindingComposite"/>) as composites are not directly associated with controls
  132. /// but rather source their values through their child bindings.</param>
  133. /// <returns>Processed input value.</returns>
  134. public abstract TValue Process(TValue value, InputControl control);
  135. public override object ProcessAsObject(object value, InputControl control)
  136. {
  137. if (value == null)
  138. throw new ArgumentNullException(nameof(value));
  139. if (!(value is TValue))
  140. throw new ArgumentException(
  141. $"Expecting value of type '{typeof(TValue).Name}' but got value '{value}' of type '{value.GetType().Name}'",
  142. nameof(value));
  143. var valueOfType = (TValue)value;
  144. return Process(valueOfType, control);
  145. }
  146. public override unsafe void Process(void* buffer, int bufferSize, InputControl control)
  147. {
  148. if (buffer == null)
  149. throw new ArgumentNullException(nameof(buffer));
  150. var valueSize = UnsafeUtility.SizeOf<TValue>();
  151. if (bufferSize < valueSize)
  152. throw new ArgumentException(
  153. $"Expected buffer of at least {valueSize} bytes but got buffer with just {bufferSize} bytes",
  154. nameof(bufferSize));
  155. var value = default(TValue);
  156. var valuePtr = UnsafeUtility.AddressOf(ref value);
  157. UnsafeUtility.MemCpy(valuePtr, buffer, valueSize);
  158. value = Process(value, control);
  159. valuePtr = UnsafeUtility.AddressOf(ref value);
  160. UnsafeUtility.MemCpy(buffer, valuePtr, valueSize);
  161. }
  162. }
  163. }