tiny_mt.h 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include <array>
  5. #include "common/alignment.h"
  6. #include "common/common_types.h"
  7. namespace Common {
  8. // Implementation of TinyMT (mersenne twister RNG).
  9. // Like Nintendo, we will use the sample parameters.
  10. class TinyMT {
  11. public:
  12. static constexpr std::size_t NumStateWords = 4;
  13. struct State {
  14. std::array<u32, NumStateWords> data{};
  15. };
  16. private:
  17. static constexpr u32 ParamMat1 = 0x8F7011EE;
  18. static constexpr u32 ParamMat2 = 0xFC78FF1F;
  19. static constexpr u32 ParamTmat = 0x3793FDFF;
  20. static constexpr u32 ParamMult = 0x6C078965;
  21. static constexpr u32 ParamPlus = 0x0019660D;
  22. static constexpr u32 ParamXor = 0x5D588B65;
  23. static constexpr u32 TopBitmask = 0x7FFFFFFF;
  24. static constexpr int MinimumInitIterations = 8;
  25. static constexpr int NumDiscardedInitOutputs = 8;
  26. static constexpr u32 XorByShifted27(u32 value) {
  27. return value ^ (value >> 27);
  28. }
  29. static constexpr u32 XorByShifted30(u32 value) {
  30. return value ^ (value >> 30);
  31. }
  32. private:
  33. State state{};
  34. private:
  35. // Internal API.
  36. void FinalizeInitialization() {
  37. const u32 state0 = this->state.data[0] & TopBitmask;
  38. const u32 state1 = this->state.data[1];
  39. const u32 state2 = this->state.data[2];
  40. const u32 state3 = this->state.data[3];
  41. if (state0 == 0 && state1 == 0 && state2 == 0 && state3 == 0) {
  42. this->state.data[0] = 'T';
  43. this->state.data[1] = 'I';
  44. this->state.data[2] = 'N';
  45. this->state.data[3] = 'Y';
  46. }
  47. for (int i = 0; i < NumDiscardedInitOutputs; i++) {
  48. this->GenerateRandomU32();
  49. }
  50. }
  51. u32 GenerateRandomU24() {
  52. return (this->GenerateRandomU32() >> 8);
  53. }
  54. static void GenerateInitialValuePlus(TinyMT::State* state, int index, u32 value) {
  55. u32& state0 = state->data[(index + 0) % NumStateWords];
  56. u32& state1 = state->data[(index + 1) % NumStateWords];
  57. u32& state2 = state->data[(index + 2) % NumStateWords];
  58. u32& state3 = state->data[(index + 3) % NumStateWords];
  59. const u32 x = XorByShifted27(state0 ^ state1 ^ state3) * ParamPlus;
  60. const u32 y = x + index + value;
  61. state0 = y;
  62. state1 += x;
  63. state2 += y;
  64. }
  65. static void GenerateInitialValueXor(TinyMT::State* state, int index) {
  66. u32& state0 = state->data[(index + 0) % NumStateWords];
  67. u32& state1 = state->data[(index + 1) % NumStateWords];
  68. u32& state2 = state->data[(index + 2) % NumStateWords];
  69. u32& state3 = state->data[(index + 3) % NumStateWords];
  70. const u32 x = XorByShifted27(state0 + state1 + state3) * ParamXor;
  71. const u32 y = x - index;
  72. state0 = y;
  73. state1 ^= x;
  74. state2 ^= y;
  75. }
  76. public:
  77. constexpr TinyMT() = default;
  78. // Public API.
  79. // Initialization.
  80. void Initialize(u32 seed) {
  81. this->state.data[0] = seed;
  82. this->state.data[1] = ParamMat1;
  83. this->state.data[2] = ParamMat2;
  84. this->state.data[3] = ParamTmat;
  85. for (int i = 1; i < MinimumInitIterations; i++) {
  86. const u32 mixed = XorByShifted30(this->state.data[(i - 1) % NumStateWords]);
  87. this->state.data[i % NumStateWords] ^= mixed * ParamMult + i;
  88. }
  89. this->FinalizeInitialization();
  90. }
  91. void Initialize(const u32* seed, int seed_count) {
  92. this->state.data[0] = 0;
  93. this->state.data[1] = ParamMat1;
  94. this->state.data[2] = ParamMat2;
  95. this->state.data[3] = ParamTmat;
  96. {
  97. const int num_init_iterations = std::max(seed_count + 1, MinimumInitIterations) - 1;
  98. GenerateInitialValuePlus(&this->state, 0, seed_count);
  99. for (int i = 0; i < num_init_iterations; i++) {
  100. GenerateInitialValuePlus(&this->state, (i + 1) % NumStateWords,
  101. (i < seed_count) ? seed[i] : 0);
  102. }
  103. for (int i = 0; i < static_cast<int>(NumStateWords); i++) {
  104. GenerateInitialValueXor(&this->state,
  105. (i + 1 + num_init_iterations) % NumStateWords);
  106. }
  107. }
  108. this->FinalizeInitialization();
  109. }
  110. // State management.
  111. void GetState(TinyMT::State& out) const {
  112. out.data = this->state.data;
  113. }
  114. void SetState(const TinyMT::State& state_) {
  115. this->state.data = state_.data;
  116. }
  117. // Random generation.
  118. void GenerateRandomBytes(void* dst, std::size_t size) {
  119. const uintptr_t start = reinterpret_cast<uintptr_t>(dst);
  120. const uintptr_t end = start + size;
  121. const uintptr_t aligned_start = Common::AlignUp(start, 4);
  122. const uintptr_t aligned_end = Common::AlignDown(end, 4);
  123. // Make sure we're aligned.
  124. if (start < aligned_start) {
  125. const u32 rnd = this->GenerateRandomU32();
  126. std::memcpy(dst, &rnd, aligned_start - start);
  127. }
  128. // Write as many aligned u32s as we can.
  129. {
  130. u32* cur_dst = reinterpret_cast<u32*>(aligned_start);
  131. u32* const end_dst = reinterpret_cast<u32*>(aligned_end);
  132. while (cur_dst < end_dst) {
  133. *(cur_dst++) = this->GenerateRandomU32();
  134. }
  135. }
  136. // Handle any leftover unaligned data.
  137. if (aligned_end < end) {
  138. const u32 rnd = this->GenerateRandomU32();
  139. std::memcpy(reinterpret_cast<void*>(aligned_end), &rnd, end - aligned_end);
  140. }
  141. }
  142. u32 GenerateRandomU32() {
  143. // Advance state.
  144. const u32 x0 =
  145. (this->state.data[0] & TopBitmask) ^ this->state.data[1] ^ this->state.data[2];
  146. const u32 y0 = this->state.data[3];
  147. const u32 x1 = x0 ^ (x0 << 1);
  148. const u32 y1 = y0 ^ (y0 >> 1) ^ x1;
  149. const u32 state0 = this->state.data[1];
  150. u32 state1 = this->state.data[2];
  151. u32 state2 = x1 ^ (y1 << 10);
  152. const u32 state3 = y1;
  153. if ((y1 & 1) != 0) {
  154. state1 ^= ParamMat1;
  155. state2 ^= ParamMat2;
  156. }
  157. this->state.data[0] = state0;
  158. this->state.data[1] = state1;
  159. this->state.data[2] = state2;
  160. this->state.data[3] = state3;
  161. // Temper.
  162. const u32 t1 = state0 + (state2 >> 8);
  163. u32 t0 = state3 ^ t1;
  164. if ((t1 & 1) != 0) {
  165. t0 ^= ParamTmat;
  166. }
  167. return t0;
  168. }
  169. u64 GenerateRandomU64() {
  170. const u32 lo = this->GenerateRandomU32();
  171. const u32 hi = this->GenerateRandomU32();
  172. return (u64{hi} << 32) | u64{lo};
  173. }
  174. float GenerateRandomF32() {
  175. // Floats have 24 bits of mantissa.
  176. constexpr u32 MantissaBits = 24;
  177. return static_cast<float>(GenerateRandomU24()) * (1.0f / (1U << MantissaBits));
  178. }
  179. double GenerateRandomF64() {
  180. // Doubles have 53 bits of mantissa.
  181. // The smart way to generate 53 bits of random would be to use 32 bits
  182. // from the first rnd32() call, and then 21 from the second.
  183. // Nintendo does not. They use (32 - 5) = 27 bits from the first rnd32()
  184. // call, and (32 - 6) bits from the second. We'll do what they do, but
  185. // There's not a clear reason why.
  186. constexpr u32 MantissaBits = 53;
  187. constexpr u32 Shift1st = (64 - MantissaBits) / 2;
  188. constexpr u32 Shift2nd = (64 - MantissaBits) - Shift1st;
  189. const u32 first = (this->GenerateRandomU32() >> Shift1st);
  190. const u32 second = (this->GenerateRandomU32() >> Shift2nd);
  191. return (1.0 * first * (u64{1} << (32 - Shift2nd)) + second) *
  192. (1.0 / (u64{1} << MantissaBits));
  193. }
  194. };
  195. } // namespace Common