Nenhuma descrição
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

InputStateBlock.cs 40KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using Unity.Collections.LowLevel.Unsafe;
  4. using UnityEngine.InputSystem.Utilities;
  5. ////TODO: the Debug.Asserts here should be also be made as checks ahead of time (on the layout)
  6. ////TODO: the read/write methods need a proper pass for consistency
  7. ////FIXME: some architectures have strict memory alignment requirements; we should honor them when
  8. //// we read/write primitive values or support stitching values together from bytes manually
  9. //// where needed
  10. ////TODO: allow bitOffset to be non-zero for byte-aligned control as long as result is byte-aligned
  11. ////REVIEW: The combination of byte and bit offset instead of just a single bit offset has turned out
  12. //// to be plenty awkward to use in practice; should be replace it?
  13. ////REVIEW: AutomaticOffset is a very awkward mechanism; it's primary use really is for "parking" unused
  14. //// controls for which a more elegant and robust mechanism can surely be devised
  15. namespace UnityEngine.InputSystem.LowLevel
  16. {
  17. /// <summary>
  18. /// Information about a memory region storing input state.
  19. /// </summary>
  20. /// <remarks>
  21. /// Input state is kept in raw memory blocks. All state is centrally managed by the input system;
  22. /// controls cannot keep their own independent state.
  23. ///
  24. /// Each state block is tagged with a format code indicating the storage format used for the
  25. /// memory block. This can either be one out of a set of primitive formats (such as "INT") or a custom
  26. /// format code indicating a more complex format.
  27. ///
  28. /// Memory using primitive formats can be converted to and from primitive values directly by this struct.
  29. ///
  30. /// State memory is bit-addressable, meaning that it can be offset from a byte address in bits (<see cref="bitOffset"/>)
  31. /// and is sized in bits instead of bytes (<see cref="sizeInBits"/>). However, in practice, bit-addressing
  32. /// memory reads and writes are only supported on the <see cref="FormatBit">bitfield primitive format</see>.
  33. ///
  34. /// Input state memory is restricted to a maximum of 4GB in size. Offsets are recorded in 32 bits.
  35. /// </remarks>
  36. /// <seealso cref="InputControl.stateBlock"/>
  37. public unsafe struct InputStateBlock
  38. {
  39. public const uint InvalidOffset = 0xffffffff;
  40. public const uint AutomaticOffset = 0xfffffffe;
  41. /// <summary>
  42. /// Format code for invalid value type
  43. /// </summary>
  44. /// <seealso cref="format"/>
  45. public static readonly FourCC FormatInvalid = new FourCC(0);
  46. internal const int kFormatInvalid = 0;
  47. /// <summary>
  48. /// Format code for a variable-width bitfield representing an unsigned value,
  49. /// i.e. all bits including the highest one represent the magnitude of the value.
  50. /// </summary>
  51. /// <seealso cref="format"/>
  52. public static readonly FourCC FormatBit = new FourCC('B', 'I', 'T');
  53. internal const int kFormatBit = 'B' << 24 | 'I' << 16 | 'T' << 8 | ' ';
  54. /// <summary>
  55. /// Format code for a variable-width bitfield representing a signed value, i.e. the
  56. /// highest bit is used as a sign bit (0=unsigned, 1=signed) and the remaining bits represent
  57. /// the magnitude of the value.
  58. /// </summary>
  59. /// <seealso cref="format"/>
  60. public static readonly FourCC FormatSBit = new FourCC('S', 'B', 'I', 'T');
  61. internal const int kFormatSBit = 'S' << 24 | 'B' << 16 | 'I' << 8 | 'T';
  62. /// <summary>
  63. /// Format code for a 32-bit signed integer value.
  64. /// </summary>
  65. /// <seealso cref="format"/>
  66. public static readonly FourCC FormatInt = new FourCC('I', 'N', 'T');
  67. internal const int kFormatInt = 'I' << 24 | 'N' << 16 | 'T' << 8 | ' ';
  68. /// <summary>
  69. /// Format code for a 32-bit unsigned integer value.
  70. /// </summary>
  71. /// <seealso cref="format"/>
  72. public static readonly FourCC FormatUInt = new FourCC('U', 'I', 'N', 'T');
  73. internal const int kFormatUInt = 'U' << 24 | 'I' << 16 | 'N' << 8 | 'T';
  74. /// <summary>
  75. /// Format code for a 16-bit signed integer value.
  76. /// </summary>
  77. /// <seealso cref="format"/>
  78. public static readonly FourCC FormatShort = new FourCC('S', 'H', 'R', 'T');
  79. internal const int kFormatShort = 'S' << 24 | 'H' << 16 | 'R' << 8 | 'T';
  80. /// <summary>
  81. /// Format code for a 16-bit unsigned integer value.
  82. /// </summary>
  83. /// <seealso cref="format"/>
  84. public static readonly FourCC FormatUShort = new FourCC('U', 'S', 'H', 'T');
  85. internal const int kFormatUShort = 'U' << 24 | 'S' << 16 | 'H' << 8 | 'T';
  86. /// <summary>
  87. /// Format code for an 8-bit unsigned integer value.
  88. /// </summary>
  89. /// <seealso cref="format"/>
  90. public static readonly FourCC FormatByte = new FourCC('B', 'Y', 'T', 'E');
  91. internal const int kFormatByte = 'B' << 24 | 'Y' << 16 | 'T' << 8 | 'E';
  92. /// <summary>
  93. /// Format code for an 8-bit signed integer value.
  94. /// </summary>
  95. /// <seealso cref="format"/>
  96. public static readonly FourCC FormatSByte = new FourCC('S', 'B', 'Y', 'T');
  97. internal const int kFormatSByte = 'S' << 24 | 'B' << 16 | 'Y' << 8 | 'T';
  98. /// <summary>
  99. /// Format code for a 64-bit signed integer value.
  100. /// </summary>
  101. /// <seealso cref="format"/>
  102. public static readonly FourCC FormatLong = new FourCC('L', 'N', 'G');
  103. internal const int kFormatLong = 'L' << 24 | 'N' << 16 | 'G' << 8 | ' ';
  104. /// <summary>
  105. /// Format code for a 64-bit unsigned integer value.
  106. /// </summary>
  107. /// <seealso cref="format"/>
  108. public static readonly FourCC FormatULong = new FourCC('U', 'L', 'N', 'G');
  109. internal const int kFormatULong = 'U' << 24 | 'L' << 16 | 'N' << 8 | 'G';
  110. /// <summary>
  111. /// Format code for a 32-bit floating-point value.
  112. /// </summary>
  113. /// <seealso cref="format"/>
  114. public static readonly FourCC FormatFloat = new FourCC('F', 'L', 'T');
  115. internal const int kFormatFloat = 'F' << 24 | 'L' << 16 | 'T' << 8 | ' ';
  116. /// <summary>
  117. /// Format code for a 64-bit floating-point value.
  118. /// </summary>
  119. /// <seealso cref="format"/>
  120. public static readonly FourCC FormatDouble = new FourCC('D', 'B', 'L');
  121. internal const int kFormatDouble = 'D' << 24 | 'B' << 16 | 'L' << 8 | ' ';
  122. ////REVIEW: are these really useful?
  123. public static readonly FourCC FormatVector2 = new FourCC('V', 'E', 'C', '2');
  124. internal const int kFormatVector2 = 'V' << 24 | 'E' << 16 | 'C' << 8 | '2';
  125. public static readonly FourCC FormatVector3 = new FourCC('V', 'E', 'C', '3');
  126. internal const int kFormatVector3 = 'V' << 24 | 'E' << 16 | 'C' << 8 | '3';
  127. public static readonly FourCC FormatQuaternion = new FourCC('Q', 'U', 'A', 'T');
  128. internal const int kFormatQuaternion = 'Q' << 24 | 'U' << 16 | 'A' << 8 | 'T';
  129. public static readonly FourCC FormatVector2Short = new FourCC('V', 'C', '2', 'S');
  130. public static readonly FourCC FormatVector3Short = new FourCC('V', 'C', '3', 'S');
  131. public static readonly FourCC FormatVector2Byte = new FourCC('V', 'C', '2', 'B');
  132. public static readonly FourCC FormatVector3Byte = new FourCC('V', 'C', '3', 'B');
  133. public static readonly FourCC FormatPose = new FourCC('P', 'o', 's', 'e');
  134. internal const int kFormatPose = 'P' << 24 | 'o' << 16 | 's' << 8 | 'e';
  135. public static int GetSizeOfPrimitiveFormatInBits(FourCC type)
  136. {
  137. if (type == FormatBit || type == FormatSBit)
  138. return 1;
  139. if (type == FormatInt || type == FormatUInt)
  140. return 4 * 8;
  141. if (type == FormatShort || type == FormatUShort)
  142. return 2 * 8;
  143. if (type == FormatByte || type == FormatSByte)
  144. return 1 * 8;
  145. if (type == FormatLong || type == FormatULong)
  146. return 8 * 8;
  147. if (type == FormatFloat)
  148. return 4 * 8;
  149. if (type == FormatDouble)
  150. return 8 * 8;
  151. if (type == FormatVector2)
  152. return 2 * 4 * 8;
  153. if (type == FormatVector3)
  154. return 3 * 4 * 8;
  155. if (type == FormatQuaternion)
  156. return 4 * 4 * 8;
  157. if (type == FormatVector2Short)
  158. return 2 * 2 * 8;
  159. if (type == FormatVector3Short)
  160. return 3 * 2 * 8;
  161. if (type == FormatVector2Byte)
  162. return 2 * 1 * 8;
  163. if (type == FormatVector3Byte)
  164. return 3 * 1 * 8;
  165. return -1;
  166. }
  167. public static FourCC GetPrimitiveFormatFromType(Type type)
  168. {
  169. if (ReferenceEquals(type, typeof(int)))
  170. return FormatInt;
  171. if (ReferenceEquals(type, typeof(uint)))
  172. return FormatUInt;
  173. if (ReferenceEquals(type, typeof(short)))
  174. return FormatShort;
  175. if (ReferenceEquals(type, typeof(ushort)))
  176. return FormatUShort;
  177. if (ReferenceEquals(type, typeof(byte)))
  178. return FormatByte;
  179. if (ReferenceEquals(type, typeof(sbyte)))
  180. return FormatSByte;
  181. if (ReferenceEquals(type, typeof(long)))
  182. return FormatLong;
  183. if (ReferenceEquals(type, typeof(ulong)))
  184. return FormatULong;
  185. if (ReferenceEquals(type, typeof(float)))
  186. return FormatFloat;
  187. if (ReferenceEquals(type, typeof(double)))
  188. return FormatDouble;
  189. if (ReferenceEquals(type, typeof(Vector2)))
  190. return FormatVector2;
  191. if (ReferenceEquals(type, typeof(Vector3)))
  192. return FormatVector3;
  193. if (ReferenceEquals(type, typeof(Quaternion)))
  194. return FormatQuaternion;
  195. return new FourCC();
  196. }
  197. /// <summary>
  198. /// Type identifier for the memory layout used by the state.
  199. /// </summary>
  200. /// <remarks>
  201. /// Used for safety checks to make sure that when the system copies state memory, it
  202. /// copies between compatible layouts. If set to a primitive state format, also used to
  203. /// determine the size of the state block.
  204. /// </remarks>
  205. public FourCC format { get; set; }
  206. ////TODO: collapse byteOffset and bitOffset into a single 'offset' field
  207. // Offset into state buffer. After a device is added to the system, this is relative
  208. // to the global buffers; otherwise it is relative to the device root.
  209. // During setup, this can be InvalidOffset to indicate a control that should be placed
  210. // at an offset automatically; otherwise it denotes a fixed offset relative to the
  211. // parent control.
  212. public uint byteOffset
  213. {
  214. get => m_ByteOffset;
  215. set
  216. {
  217. m_ByteOffset = value;
  218. }
  219. }
  220. // Needed for fast access to avoid a call to getter in some places
  221. internal uint m_ByteOffset;
  222. // Bit offset from the given byte offset. Also zero-based (i.e. first bit is at bit
  223. // offset #0).
  224. public uint bitOffset { get; set; }
  225. // Size of the state in bits. If this % 8 is not 0, the control is considered a
  226. // bitfield control.
  227. // During setup, if this field is 0 it means the size of the control should be automatically
  228. // computed from either its children (if it has any) or its set format. If it has neither,
  229. // setup will throw.
  230. public uint sizeInBits { get; set; }
  231. internal uint alignedSizeInBytes => (sizeInBits + 7) >> 3;
  232. internal uint effectiveByteOffset => byteOffset + (bitOffset >> 3);
  233. internal uint effectiveBitOffset => byteOffset * 8 + bitOffset;
  234. public int ReadInt(void* statePtr)
  235. {
  236. Debug.Assert(sizeInBits != 0);
  237. var valuePtr = (byte*)statePtr + (int)byteOffset;
  238. var fmt = (int)format;
  239. switch (fmt)
  240. {
  241. case kFormatBit:
  242. if (sizeInBits == 1)
  243. return MemoryHelpers.ReadSingleBit(valuePtr, bitOffset) ? 1 : 0;
  244. return (int)MemoryHelpers.ReadMultipleBitsAsUInt(valuePtr, bitOffset, sizeInBits);
  245. case kFormatSBit:
  246. if (sizeInBits == 1)
  247. return MemoryHelpers.ReadSingleBit(valuePtr, bitOffset) ? 1 : -1;
  248. return MemoryHelpers.ReadExcessKMultipleBitsAsInt(valuePtr, bitOffset, sizeInBits);
  249. case kFormatInt:
  250. case kFormatUInt:
  251. Debug.Assert(sizeInBits == 32, "INT and UINT state must have sizeInBits=32");
  252. Debug.Assert(bitOffset == 0, "INT and UINT state must be byte-aligned");
  253. if (fmt == kFormatUInt)
  254. Debug.Assert(*(uint*)valuePtr <= int.MaxValue, "UINT must fit in the int");
  255. return *(int*)valuePtr;
  256. case kFormatShort:
  257. Debug.Assert(sizeInBits == 16, "SHRT state must have sizeInBits=16");
  258. Debug.Assert(bitOffset == 0, "SHRT state must be byte-aligned");
  259. return *(short*)valuePtr;
  260. case kFormatUShort:
  261. Debug.Assert(sizeInBits == 16, "USHT state must have sizeInBits=16");
  262. Debug.Assert(bitOffset == 0, "USHT state must be byte-aligned");
  263. return *(ushort*)valuePtr;
  264. case kFormatByte:
  265. Debug.Assert(sizeInBits == 8, "BYTE state must have sizeInBits=8");
  266. Debug.Assert(bitOffset == 0, "BYTE state must be byte-aligned");
  267. return *valuePtr;
  268. case kFormatSByte:
  269. Debug.Assert(sizeInBits == 8, "SBYT state must have sizeInBits=8");
  270. Debug.Assert(bitOffset == 0, "SBYT state must be byte-aligned");
  271. return *(sbyte*)valuePtr;
  272. // Not supported:
  273. // - kFormatLong
  274. // - kFormatULong
  275. // - kFormatFloat
  276. // - kFormatDouble
  277. default:
  278. throw new InvalidOperationException($"State format '{format}' is not supported as integer format");
  279. }
  280. }
  281. public void WriteInt(void* statePtr, int value)
  282. {
  283. Debug.Assert(sizeInBits != 0);
  284. var valuePtr = (byte*)statePtr + (int)byteOffset;
  285. var fmt = (int)format;
  286. switch (fmt)
  287. {
  288. case kFormatBit:
  289. if (sizeInBits == 1)
  290. MemoryHelpers.WriteSingleBit(valuePtr, bitOffset, value != 0);
  291. else
  292. MemoryHelpers.WriteUIntAsMultipleBits(valuePtr, bitOffset, sizeInBits, (uint)value);
  293. break;
  294. case kFormatSBit:
  295. if (sizeInBits == 1)
  296. MemoryHelpers.WriteSingleBit(valuePtr, bitOffset, value > 0);
  297. else
  298. MemoryHelpers.WriteIntAsExcessKMultipleBits(valuePtr, bitOffset, sizeInBits, value);
  299. break;
  300. case kFormatInt:
  301. case kFormatUInt:
  302. Debug.Assert(sizeInBits == 32, "INT and UINT state must have sizeInBits=32");
  303. Debug.Assert(bitOffset == 0, "INT and UINT state must be byte-aligned");
  304. *(int*)valuePtr = value;
  305. break;
  306. case kFormatShort:
  307. Debug.Assert(sizeInBits == 16, "SHRT state must have sizeInBits=16");
  308. Debug.Assert(bitOffset == 0, "SHRT state must be byte-aligned");
  309. *(short*)valuePtr = (short)value;
  310. break;
  311. case kFormatUShort:
  312. Debug.Assert(sizeInBits == 16, "USHT state must have sizeInBits=16");
  313. Debug.Assert(bitOffset == 0, "USHT state must be byte-aligned");
  314. *(ushort*)valuePtr = (ushort)value;
  315. break;
  316. case kFormatByte:
  317. Debug.Assert(sizeInBits == 8, "BYTE state must have sizeInBits=8");
  318. Debug.Assert(bitOffset == 0, "BYTE state must be byte-aligned");
  319. *valuePtr = (byte)value;
  320. break;
  321. case kFormatSByte:
  322. Debug.Assert(sizeInBits == 8, "SBYT state must have sizeInBits=8");
  323. Debug.Assert(bitOffset == 0, "SBYT state must be byte-aligned");
  324. *(sbyte*)valuePtr = (sbyte)value;
  325. break;
  326. // Not supported:
  327. // - kFormatLong
  328. // - kFormatULong
  329. // - kFormatFloat
  330. // - kFormatDouble
  331. default:
  332. throw new Exception($"State format '{format}' is not supported as integer format");
  333. }
  334. }
  335. public float ReadFloat(void* statePtr)
  336. {
  337. Debug.Assert(sizeInBits != 0);
  338. var valuePtr = (byte*)statePtr + (int)byteOffset;
  339. var fmt = (int)format;
  340. switch (fmt)
  341. {
  342. // If a control with an integer-based representation does not use the full range
  343. // of its integer size (e.g. only goes from [0..128]), processors or the parameters
  344. // above have to be used to re-process the resulting float values.
  345. case kFormatBit:
  346. if (sizeInBits == 1)
  347. return MemoryHelpers.ReadSingleBit(valuePtr, bitOffset) ? 1.0f : 0.0f;
  348. return MemoryHelpers.ReadMultipleBitsAsNormalizedUInt(valuePtr, bitOffset, sizeInBits);
  349. case kFormatSBit:
  350. if (sizeInBits == 1)
  351. return MemoryHelpers.ReadSingleBit(valuePtr, bitOffset) ? 1.0f : -1.0f;
  352. return MemoryHelpers.ReadMultipleBitsAsNormalizedUInt(valuePtr, bitOffset, sizeInBits) * 2.0f - 1.0f;
  353. case kFormatInt:
  354. Debug.Assert(sizeInBits == 32, "INT state must have sizeInBits=32");
  355. Debug.Assert(bitOffset == 0, "INT state must be byte-aligned");
  356. return NumberHelpers.IntToNormalizedFloat(*(int*)valuePtr, int.MinValue, int.MaxValue) * 2.0f - 1.0f;
  357. case kFormatUInt:
  358. Debug.Assert(sizeInBits == 32, "UINT state must have sizeInBits=32");
  359. Debug.Assert(bitOffset == 0, "UINT state must be byte-aligned");
  360. return NumberHelpers.UIntToNormalizedFloat(*(uint*)valuePtr, uint.MinValue, uint.MaxValue);
  361. case kFormatShort:
  362. Debug.Assert(sizeInBits == 16, "SHRT state must have sizeInBits=16");
  363. Debug.Assert(bitOffset == 0, "SHRT state must be byte-aligned");
  364. return NumberHelpers.IntToNormalizedFloat(*(short*)valuePtr, short.MinValue, short.MaxValue) * 2.0f - 1.0f;
  365. case kFormatUShort:
  366. Debug.Assert(sizeInBits == 16, "USHT state must have sizeInBits=16");
  367. Debug.Assert(bitOffset == 0, "USHT state must be byte-aligned");
  368. return NumberHelpers.UIntToNormalizedFloat(*(ushort*)valuePtr, ushort.MinValue, ushort.MaxValue);
  369. case kFormatByte:
  370. Debug.Assert(sizeInBits == 8, "BYTE state must have sizeInBits=8");
  371. Debug.Assert(bitOffset == 0, "BYTE state must be byte-aligned");
  372. return NumberHelpers.UIntToNormalizedFloat(*valuePtr, byte.MinValue, byte.MaxValue);
  373. case kFormatSByte:
  374. Debug.Assert(sizeInBits == 8, "SBYT state must have sizeInBits=8");
  375. Debug.Assert(bitOffset == 0, "SBYT state must be byte-aligned");
  376. return NumberHelpers.IntToNormalizedFloat(*(sbyte*)valuePtr, sbyte.MinValue, sbyte.MaxValue) * 2.0f - 1.0f;
  377. case kFormatFloat:
  378. Debug.Assert(sizeInBits == 32, "FLT state must have sizeInBits=32");
  379. Debug.Assert(bitOffset == 0, "FLT state must be byte-aligned");
  380. return *(float*)valuePtr;
  381. case kFormatDouble:
  382. Debug.Assert(sizeInBits == 64, "DBL state must have sizeInBits=64");
  383. Debug.Assert(bitOffset == 0, "DBL state must be byte-aligned");
  384. return (float)*(double*)valuePtr;
  385. // Not supported:
  386. // - kFormatLong
  387. // - kFormatULong
  388. default:
  389. throw new InvalidOperationException($"State format '{format}' is not supported as floating-point format");
  390. }
  391. }
  392. public void WriteFloat(void* statePtr, float value)
  393. {
  394. var valuePtr = (byte*)statePtr + (int)byteOffset;
  395. var fmt = (int)format;
  396. switch (fmt)
  397. {
  398. case kFormatBit:
  399. if (sizeInBits == 1)
  400. MemoryHelpers.WriteSingleBit(valuePtr, bitOffset, value >= 0.5f);////REVIEW: Shouldn't this be the global button press point?
  401. else
  402. MemoryHelpers.WriteNormalizedUIntAsMultipleBits(valuePtr, bitOffset, sizeInBits, value);
  403. break;
  404. case kFormatSBit:
  405. if (sizeInBits == 1)
  406. MemoryHelpers.WriteSingleBit(valuePtr, bitOffset, value >= 0.0f);
  407. else
  408. MemoryHelpers.WriteNormalizedUIntAsMultipleBits(valuePtr, bitOffset, sizeInBits, value * 0.5f + 0.5f);
  409. break;
  410. case kFormatInt:
  411. Debug.Assert(sizeInBits == 32, "INT state must have sizeInBits=32");
  412. Debug.Assert(bitOffset == 0, "INT state must be byte-aligned");
  413. *(int*)valuePtr = (int)NumberHelpers.NormalizedFloatToInt(value * 0.5f + 0.5f, int.MinValue, int.MaxValue);
  414. break;
  415. case kFormatUInt:
  416. Debug.Assert(sizeInBits == 32, "UINT state must have sizeInBits=32");
  417. Debug.Assert(bitOffset == 0, "UINT state must be byte-aligned");
  418. *(uint*)valuePtr = NumberHelpers.NormalizedFloatToUInt(value, uint.MinValue, uint.MaxValue);
  419. break;
  420. case kFormatShort:
  421. Debug.Assert(sizeInBits == 16, "SHRT state must have sizeInBits=16");
  422. Debug.Assert(bitOffset == 0, "SHRT state must be byte-aligned");
  423. *(short*)valuePtr = (short)NumberHelpers.NormalizedFloatToInt(value * 0.5f + 0.5f, short.MinValue, short.MaxValue);
  424. break;
  425. case kFormatUShort:
  426. Debug.Assert(sizeInBits == 16, "USHT state must have sizeInBits=16");
  427. Debug.Assert(bitOffset == 0, "USHT state must be byte-aligned");
  428. *(ushort*)valuePtr = (ushort)NumberHelpers.NormalizedFloatToUInt(value, ushort.MinValue, ushort.MaxValue);
  429. break;
  430. case kFormatByte:
  431. Debug.Assert(sizeInBits == 8, "BYTE state must have sizeInBits=8");
  432. Debug.Assert(bitOffset == 0, "BYTE state must be byte-aligned");
  433. *valuePtr = (byte)NumberHelpers.NormalizedFloatToUInt(value, byte.MinValue, byte.MaxValue);
  434. break;
  435. case kFormatSByte:
  436. Debug.Assert(sizeInBits == 8, "SBYT state must have sizeInBits=8");
  437. Debug.Assert(bitOffset == 0, "SBYT state must be byte-aligned");
  438. *(sbyte*)valuePtr = (sbyte)NumberHelpers.NormalizedFloatToInt(value * 0.5f + 0.5f, sbyte.MinValue, sbyte.MaxValue);
  439. break;
  440. case kFormatFloat:
  441. Debug.Assert(sizeInBits == 32, "FLT state must have sizeInBits=32");
  442. Debug.Assert(bitOffset == 0, "FLT state must be byte-aligned");
  443. *(float*)valuePtr = value;
  444. break;
  445. case kFormatDouble:
  446. Debug.Assert(sizeInBits == 64, "DBL state must have sizeInBits=64");
  447. Debug.Assert(bitOffset == 0, "DBL state must be byte-aligned");
  448. *(double*)valuePtr = value;
  449. break;
  450. // Not supported:
  451. // - kFormatLong
  452. // - kFormatULong
  453. default:
  454. throw new Exception($"State format '{format}' is not supported as floating-point format");
  455. }
  456. }
  457. internal PrimitiveValue FloatToPrimitiveValue(float value)
  458. {
  459. var fmt = (int)format;
  460. switch (fmt)
  461. {
  462. case kFormatBit:
  463. if (sizeInBits == 1)
  464. return value >= 0.5f;
  465. ////FIXME: is this supposed to be int or uint?
  466. return (int)NumberHelpers.NormalizedFloatToUInt(value, 0, (uint)((1UL << (int)sizeInBits) - 1));
  467. case kFormatSBit:
  468. {
  469. if (sizeInBits == 1)
  470. return value >= 0.0f;
  471. var minValue = (int)-(long)(1UL << ((int)sizeInBits - 1));
  472. var maxValue = (int)((1UL << ((int)sizeInBits - 1)) - 1);
  473. return NumberHelpers.NormalizedFloatToInt(value, minValue, maxValue);
  474. }
  475. case kFormatInt:
  476. Debug.Assert(sizeInBits == 32, "INT state must have sizeInBits=32");
  477. Debug.Assert(bitOffset == 0, "INT state must be byte-aligned");
  478. return NumberHelpers.NormalizedFloatToInt(value * 0.5f + 0.5f, int.MinValue, int.MaxValue);
  479. case kFormatUInt:
  480. Debug.Assert(sizeInBits == 32, "UINT state must have sizeInBits=32");
  481. Debug.Assert(bitOffset == 0, "UINT state must be byte-aligned");
  482. return NumberHelpers.NormalizedFloatToUInt(value, uint.MinValue, uint.MaxValue);
  483. case kFormatShort:
  484. Debug.Assert(sizeInBits == 16, "SHRT state must have sizeInBits=16");
  485. Debug.Assert(bitOffset == 0, "SHRT state must be byte-aligned");
  486. return (short)NumberHelpers.NormalizedFloatToInt(value * 0.5f + 0.5f, short.MinValue, short.MaxValue);
  487. case kFormatUShort:
  488. Debug.Assert(sizeInBits == 16, "USHT state must have sizeInBits=16");
  489. Debug.Assert(bitOffset == 0, "USHT state must be byte-aligned");
  490. return (ushort)NumberHelpers.NormalizedFloatToUInt(value, ushort.MinValue, ushort.MaxValue);
  491. case kFormatByte:
  492. Debug.Assert(sizeInBits == 8, "BYTE state must have sizeInBits=8");
  493. Debug.Assert(bitOffset == 0, "BYTE state must be byte-aligned");
  494. return (byte)NumberHelpers.NormalizedFloatToUInt(value, byte.MinValue, byte.MaxValue);
  495. case kFormatSByte:
  496. Debug.Assert(sizeInBits == 8, "SBYT state must have sizeInBits=8");
  497. Debug.Assert(bitOffset == 0, "SBYT state must be byte-aligned");
  498. return (sbyte)NumberHelpers.NormalizedFloatToInt(value * 0.5f + 0.5f, sbyte.MinValue, sbyte.MaxValue);
  499. case kFormatFloat:
  500. Debug.Assert(sizeInBits == 32, "FLT state must have sizeInBits=32");
  501. Debug.Assert(bitOffset == 0, "FLT state must be byte-aligned");
  502. return value;
  503. case kFormatDouble:
  504. Debug.Assert(sizeInBits == 64, "DBL state must have sizeInBits=64");
  505. Debug.Assert(bitOffset == 0, "DBL state must be byte-aligned");
  506. return value;
  507. // Not supported:
  508. // - kFormatLong
  509. // - kFormatULong
  510. default:
  511. throw new Exception($"State format '{format}' is not supported as floating-point format");
  512. }
  513. }
  514. ////REVIEW: This is some bad code duplication here between Read/WriteFloat&Double but given that there's no
  515. //// way to use a type argument here, not sure how to get rid of it.
  516. public double ReadDouble(void* statePtr)
  517. {
  518. Debug.Assert(sizeInBits != 0);
  519. var valuePtr = (byte*)statePtr + (int)byteOffset;
  520. var fmt = (int)format;
  521. switch (fmt)
  522. {
  523. // If a control with an integer-based representation does not use the full range
  524. // of its integer size (e.g. only goes from [0..128]), processors or the parameters
  525. // above have to be used to re-process the resulting float values.
  526. case kFormatBit:
  527. if (sizeInBits == 1)
  528. return MemoryHelpers.ReadSingleBit(valuePtr, bitOffset) ? 1.0f : 0.0f;
  529. return MemoryHelpers.ReadMultipleBitsAsNormalizedUInt(valuePtr, bitOffset, sizeInBits);
  530. case kFormatSBit:
  531. if (sizeInBits == 1)
  532. return MemoryHelpers.ReadSingleBit(valuePtr, bitOffset) ? 1.0f : -1.0f;
  533. return MemoryHelpers.ReadMultipleBitsAsNormalizedUInt(valuePtr, bitOffset, sizeInBits) * 2.0f - 1.0f;
  534. case kFormatInt:
  535. Debug.Assert(sizeInBits == 32, "INT state must have sizeInBits=32");
  536. Debug.Assert(bitOffset == 0, "INT state must be byte-aligned");
  537. return NumberHelpers.IntToNormalizedFloat(*(int*)valuePtr, int.MinValue, int.MaxValue) * 2.0f - 1.0f;
  538. case kFormatUInt:
  539. Debug.Assert(sizeInBits == 32, "UINT state must have sizeInBits=32");
  540. Debug.Assert(bitOffset == 0, "UINT state must be byte-aligned");
  541. return NumberHelpers.UIntToNormalizedFloat(*(uint*)valuePtr, uint.MinValue, uint.MaxValue);
  542. case kFormatShort:
  543. Debug.Assert(sizeInBits == 16, "SHRT state must have sizeInBits=16");
  544. Debug.Assert(bitOffset == 0, "SHRT state must be byte-aligned");
  545. return NumberHelpers.IntToNormalizedFloat(*(short*)valuePtr, short.MinValue, short.MaxValue) * 2.0f - 1.0f;
  546. case kFormatUShort:
  547. Debug.Assert(sizeInBits == 16, "USHT state must have sizeInBits=16");
  548. Debug.Assert(bitOffset == 0, "USHT state must be byte-aligned");
  549. return NumberHelpers.UIntToNormalizedFloat(*(ushort*)valuePtr, ushort.MinValue, ushort.MaxValue);
  550. case kFormatByte:
  551. Debug.Assert(sizeInBits == 8, "BYTE state must have sizeInBits=8");
  552. Debug.Assert(bitOffset == 0, "BYTE state must be byte-aligned");
  553. return NumberHelpers.UIntToNormalizedFloat(*valuePtr, byte.MinValue, byte.MaxValue);
  554. case kFormatSByte:
  555. Debug.Assert(sizeInBits == 8, "SBYT state must have sizeInBits=8");
  556. Debug.Assert(bitOffset == 0, "SBYT state must be byte-aligned");
  557. return NumberHelpers.IntToNormalizedFloat(*(sbyte*)valuePtr, sbyte.MinValue, sbyte.MaxValue) * 2.0f - 1.0f;
  558. case kFormatFloat:
  559. Debug.Assert(sizeInBits == 32, "FLT state must have sizeInBits=32");
  560. Debug.Assert(bitOffset == 0, "FLT state must be byte-aligned");
  561. return *(float*)valuePtr;
  562. case kFormatDouble:
  563. Debug.Assert(sizeInBits == 64, "DBL state must have sizeInBits=64");
  564. Debug.Assert(bitOffset == 0, "DBL state must be byte-aligned");
  565. return *(double*)valuePtr;
  566. // Not supported:
  567. // - kFormatLong
  568. // - kFormatULong
  569. // - kFormatFloat
  570. // - kFormatDouble
  571. default:
  572. throw new Exception($"State format '{format}' is not supported as floating-point format");
  573. }
  574. }
  575. public void WriteDouble(void* statePtr, double value)
  576. {
  577. var valuePtr = (byte*)statePtr + (int)byteOffset;
  578. var fmt = (int)format;
  579. switch (fmt)
  580. {
  581. case kFormatBit:
  582. if (sizeInBits == 1)
  583. MemoryHelpers.WriteSingleBit(valuePtr, bitOffset, value >= 0.5f);
  584. else
  585. MemoryHelpers.WriteNormalizedUIntAsMultipleBits(valuePtr, bitOffset, sizeInBits, (float)value);
  586. break;
  587. case kFormatSBit:
  588. if (sizeInBits == 1)
  589. MemoryHelpers.WriteSingleBit(valuePtr, bitOffset, value >= 0.0f);
  590. else
  591. MemoryHelpers.WriteNormalizedUIntAsMultipleBits(valuePtr, bitOffset, sizeInBits, (float)value * 0.5f + 0.5f);
  592. break;
  593. case kFormatInt:
  594. Debug.Assert(sizeInBits == 32, "INT state must have sizeInBits=16");
  595. Debug.Assert(bitOffset == 0, "INT state must be byte-aligned");
  596. *(int*)valuePtr = NumberHelpers.NormalizedFloatToInt((float)value * 0.5f + 0.5f, int.MinValue, int.MaxValue);
  597. break;
  598. case kFormatUInt:
  599. Debug.Assert(sizeInBits == 32, "UINT state must have sizeInBits=16");
  600. Debug.Assert(bitOffset == 0, "UINT state must be byte-aligned");
  601. *(uint*)valuePtr = NumberHelpers.NormalizedFloatToUInt((float)value, uint.MinValue, uint.MaxValue);
  602. break;
  603. case kFormatShort:
  604. Debug.Assert(sizeInBits == 16, "SHRT state must have sizeInBits=16");
  605. Debug.Assert(bitOffset == 0, "SHRT state must be byte-aligned");
  606. *(short*)valuePtr = (short)NumberHelpers.NormalizedFloatToInt((float)value * 0.5f + 0.5f, short.MinValue, short.MaxValue);
  607. break;
  608. case kFormatUShort:
  609. Debug.Assert(sizeInBits == 16, "USHT state must have sizeInBits=16");
  610. Debug.Assert(bitOffset == 0, "USHT state must be byte-aligned");
  611. *(ushort*)valuePtr = (ushort)NumberHelpers.NormalizedFloatToUInt((float)value, ushort.MinValue, ushort.MaxValue);
  612. break;
  613. case kFormatByte:
  614. Debug.Assert(sizeInBits == 8, "BYTE state must have sizeInBits=8");
  615. Debug.Assert(bitOffset == 0, "BYTE state must be byte-aligned");
  616. *valuePtr = (byte)NumberHelpers.NormalizedFloatToUInt((float)value, byte.MinValue, byte.MaxValue);
  617. break;
  618. case kFormatSByte:
  619. Debug.Assert(sizeInBits == 8, "SBYT state must have sizeInBits=8");
  620. Debug.Assert(bitOffset == 0, "SBYT state must be byte-aligned");
  621. *(sbyte*)valuePtr = (sbyte)NumberHelpers.NormalizedFloatToInt((float)value * 0.5f + 0.5f, sbyte.MinValue, sbyte.MaxValue);
  622. break;
  623. case kFormatFloat:
  624. Debug.Assert(sizeInBits == 32, "FLT state must have sizeInBits=32");
  625. Debug.Assert(bitOffset == 0, "FLT state must be byte-aligned");
  626. *(float*)valuePtr = (float)value;
  627. break;
  628. case kFormatDouble:
  629. Debug.Assert(sizeInBits == 64, "DBL state must have sizeInBits=64");
  630. Debug.Assert(bitOffset == 0, "DBL state must be byte-aligned");
  631. *(double*)valuePtr = value;
  632. break;
  633. // Not supported:
  634. // - kFormatLong
  635. // - kFormatULong
  636. // - kFormatFloat
  637. // - kFormatDouble
  638. default:
  639. throw new InvalidOperationException($"State format '{format}' is not supported as floating-point format");
  640. }
  641. }
  642. public void Write(void* statePtr, PrimitiveValue value)
  643. {
  644. var valuePtr = (byte*)statePtr + (int)byteOffset;
  645. var fmt = (int)format;
  646. switch (fmt)
  647. {
  648. case kFormatBit:
  649. if (sizeInBits == 1)
  650. MemoryHelpers.WriteSingleBit(valuePtr, bitOffset, value.ToBoolean());
  651. else
  652. MemoryHelpers.WriteUIntAsMultipleBits(valuePtr, bitOffset, sizeInBits, value.ToUInt32());
  653. break;
  654. case kFormatSBit:
  655. if (sizeInBits == 1)
  656. MemoryHelpers.WriteSingleBit(valuePtr, bitOffset, value.ToBoolean());
  657. else
  658. ////REVIEW: previous implementation was writing int32 as two's complement here
  659. MemoryHelpers.WriteIntAsExcessKMultipleBits(valuePtr, bitOffset, sizeInBits, value.ToInt32());
  660. break;
  661. case kFormatInt:
  662. Debug.Assert(sizeInBits == 32, "INT state must have sizeInBits=32");
  663. Debug.Assert(bitOffset == 0, "INT state must be byte-aligned");
  664. *(int*)valuePtr = value.ToInt32();
  665. break;
  666. case kFormatUInt:
  667. Debug.Assert(sizeInBits == 32, "UINT state must have sizeInBits=32");
  668. Debug.Assert(bitOffset == 0, "UINT state must be byte-aligned");
  669. *(uint*)valuePtr = value.ToUInt32();
  670. break;
  671. case kFormatShort:
  672. Debug.Assert(sizeInBits == 16, "SHRT state must have sizeInBits=16");
  673. Debug.Assert(bitOffset == 0, "SHRT state must be byte-aligned");
  674. *(short*)valuePtr = value.ToInt16();
  675. break;
  676. case kFormatUShort:
  677. Debug.Assert(sizeInBits == 16, "USHT state must have sizeInBits=16");
  678. Debug.Assert(bitOffset == 0, "USHT state must be byte-aligned");
  679. *(ushort*)valuePtr = value.ToUInt16();
  680. break;
  681. case kFormatByte:
  682. Debug.Assert(sizeInBits == 8, "BYTE state must have sizeInBits=8");
  683. Debug.Assert(bitOffset == 0, "BYTE state must be byte-aligned");
  684. *valuePtr = value.ToByte();
  685. break;
  686. case kFormatSByte:
  687. Debug.Assert(sizeInBits == 8, "SBYT state must have sizeInBits=8");
  688. Debug.Assert(bitOffset == 0, "SBYT state must be byte-aligned");
  689. *(sbyte*)valuePtr = value.ToSByte();
  690. break;
  691. case kFormatFloat:
  692. Debug.Assert(sizeInBits == 32, "FLT state must have sizeInBits=32");
  693. Debug.Assert(bitOffset == 0, "FLT state must be byte-aligned");
  694. *(float*)valuePtr = value.ToSingle();
  695. break;
  696. // Not supported:
  697. // - kFormatLong
  698. // - kFormatULong
  699. // - kFormatDouble
  700. default:
  701. throw new NotImplementedException(
  702. $"Writing primitive value of type '{value.type}' into state block with format '{format}'");
  703. }
  704. }
  705. public void CopyToFrom(void* toStatePtr, void* fromStatePtr)
  706. {
  707. if (bitOffset != 0 || sizeInBits % 8 != 0)
  708. throw new NotImplementedException("Copying bitfields");
  709. var from = (byte*)fromStatePtr + byteOffset;
  710. var to = (byte*)toStatePtr + byteOffset;
  711. UnsafeUtility.MemCpy(to, from, alignedSizeInBytes);
  712. }
  713. }
  714. }