summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/alignment.h5
-rw-r--r--src/common/cityhash.h1
-rw-r--r--src/common/common_funcs.h6
-rw-r--r--src/common/fiber.cpp23
-rw-r--r--src/common/fiber.h2
-rw-r--r--src/common/misc.cpp44
-rw-r--r--src/common/tiny_mt.h250
8 files changed, 305 insertions, 27 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index b657506b1..788516ded 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -167,6 +167,7 @@ add_library(common STATIC
167 threadsafe_queue.h 167 threadsafe_queue.h
168 time_zone.cpp 168 time_zone.cpp
169 time_zone.h 169 time_zone.h
170 tiny_mt.h
170 tree.h 171 tree.h
171 uint128.h 172 uint128.h
172 uuid.cpp 173 uuid.cpp
diff --git a/src/common/alignment.h b/src/common/alignment.h
index fb81f10d8..32d796ffa 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -42,6 +42,11 @@ requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, si
42 return (value & mask) == 0; 42 return (value & mask) == 0;
43} 43}
44 44
45template <typename T, typename U>
46requires std::is_integral_v<T>[[nodiscard]] constexpr T DivideUp(T x, U y) {
47 return (x + (y - 1)) / y;
48}
49
45template <typename T, size_t Align = 16> 50template <typename T, size_t Align = 16>
46class AlignmentAllocator { 51class AlignmentAllocator {
47public: 52public:
diff --git a/src/common/cityhash.h b/src/common/cityhash.h
index 022d0f7cb..d74fc7639 100644
--- a/src/common/cityhash.h
+++ b/src/common/cityhash.h
@@ -61,6 +61,7 @@
61 61
62#pragma once 62#pragma once
63 63
64#include <cstddef>
64#include "common/common_types.h" 65#include "common/common_types.h"
65 66
66namespace Common { 67namespace Common {
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 71b64e32a..4ace2cd33 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -52,9 +52,13 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
52// Generic function to get last error message. 52// Generic function to get last error message.
53// Call directly after the command or use the error num. 53// Call directly after the command or use the error num.
54// This function might change the error code. 54// This function might change the error code.
55// Defined in Misc.cpp. 55// Defined in misc.cpp.
56[[nodiscard]] std::string GetLastErrorMsg(); 56[[nodiscard]] std::string GetLastErrorMsg();
57 57
58// Like GetLastErrorMsg(), but passing an explicit error code.
59// Defined in misc.cpp.
60[[nodiscard]] std::string NativeErrorToString(int e);
61
58#define DECLARE_ENUM_FLAG_OPERATORS(type) \ 62#define DECLARE_ENUM_FLAG_OPERATORS(type) \
59 [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \ 63 [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
60 using T = std::underlying_type_t<type>; \ 64 using T = std::underlying_type_t<type>; \
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index 3c1eefcb7..39532ff58 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -116,16 +116,19 @@ void Fiber::Rewind() {
116 boost::context::detail::jump_fcontext(impl->rewind_context, this); 116 boost::context::detail::jump_fcontext(impl->rewind_context, this);
117} 117}
118 118
119void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) { 119void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
120 ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); 120 to.impl->guard.lock();
121 ASSERT_MSG(to != nullptr, "Next fiber is null!"); 121 to.impl->previous_fiber = weak_from.lock();
122 to->impl->guard.lock(); 122
123 to->impl->previous_fiber = from; 123 auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to);
124 auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); 124
125 ASSERT(from->impl->previous_fiber != nullptr); 125 // "from" might no longer be valid if the thread was killed
126 from->impl->previous_fiber->impl->context = transfer.fctx; 126 if (auto from = weak_from.lock()) {
127 from->impl->previous_fiber->impl->guard.unlock(); 127 ASSERT(from->impl->previous_fiber != nullptr);
128 from->impl->previous_fiber.reset(); 128 from->impl->previous_fiber->impl->context = transfer.fctx;
129 from->impl->previous_fiber->impl->guard.unlock();
130 from->impl->previous_fiber.reset();
131 }
129} 132}
130 133
131std::shared_ptr<Fiber> Fiber::ThreadToFiber() { 134std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
diff --git a/src/common/fiber.h b/src/common/fiber.h
index f7f587f8c..f2a8ff29a 100644
--- a/src/common/fiber.h
+++ b/src/common/fiber.h
@@ -41,7 +41,7 @@ public:
41 41
42 /// Yields control from Fiber 'from' to Fiber 'to' 42 /// Yields control from Fiber 'from' to Fiber 'to'
43 /// Fiber 'from' must be the currently running fiber. 43 /// Fiber 'from' must be the currently running fiber.
44 static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to); 44 static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
45 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); 45 [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
46 46
47 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param); 47 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
diff --git a/src/common/misc.cpp b/src/common/misc.cpp
index 1d5393597..495385b9e 100644
--- a/src/common/misc.cpp
+++ b/src/common/misc.cpp
@@ -12,27 +12,41 @@
12 12
13#include "common/common_funcs.h" 13#include "common/common_funcs.h"
14 14
15// Generic function to get last error message. 15std::string NativeErrorToString(int e) {
16// Call directly after the command or use the error num.
17// This function might change the error code.
18std::string GetLastErrorMsg() {
19 static constexpr std::size_t buff_size = 255;
20 char err_str[buff_size];
21
22#ifdef _WIN32 16#ifdef _WIN32
23 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 17 LPSTR err_str;
24 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr); 18
25 return std::string(err_str, buff_size); 19 DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
26#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)) 20 FORMAT_MESSAGE_IGNORE_INSERTS,
21 nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
22 reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
23 if (!res) {
24 return "(FormatMessageA failed to format error)";
25 }
26 std::string ret(err_str);
27 LocalFree(err_str);
28 return ret;
29#else
30 char err_str[255];
31#if defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
27 // Thread safe (GNU-specific) 32 // Thread safe (GNU-specific)
28 const char* str = strerror_r(errno, err_str, buff_size); 33 const char* str = strerror_r(e, err_str, sizeof(err_str));
29 return std::string(str); 34 return std::string(str);
30#else 35#else
31 // Thread safe (XSI-compliant) 36 // Thread safe (XSI-compliant)
32 const int success = strerror_r(errno, err_str, buff_size); 37 int second_err = strerror_r(e, err_str, sizeof(err_str));
33 if (success != 0) { 38 if (second_err != 0) {
34 return {}; 39 return "(strerror_r failed to format error)";
35 } 40 }
36 return std::string(err_str); 41 return std::string(err_str);
42#endif // GLIBC etc.
43#endif // _WIN32
44}
45
46std::string GetLastErrorMsg() {
47#ifdef _WIN32
48 return NativeErrorToString(GetLastError());
49#else
50 return NativeErrorToString(errno);
37#endif 51#endif
38} 52}
diff --git a/src/common/tiny_mt.h b/src/common/tiny_mt.h
new file mode 100644
index 000000000..19ae5b7d6
--- /dev/null
+++ b/src/common/tiny_mt.h
@@ -0,0 +1,250 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/alignment.h"
10#include "common/common_types.h"
11
12namespace Common {
13
14// Implementation of TinyMT (mersenne twister RNG).
15// Like Nintendo, we will use the sample parameters.
16class TinyMT {
17public:
18 static constexpr std::size_t NumStateWords = 4;
19
20 struct State {
21 std::array<u32, NumStateWords> data{};
22 };
23
24private:
25 static constexpr u32 ParamMat1 = 0x8F7011EE;
26 static constexpr u32 ParamMat2 = 0xFC78FF1F;
27 static constexpr u32 ParamTmat = 0x3793FDFF;
28
29 static constexpr u32 ParamMult = 0x6C078965;
30 static constexpr u32 ParamPlus = 0x0019660D;
31 static constexpr u32 ParamXor = 0x5D588B65;
32
33 static constexpr u32 TopBitmask = 0x7FFFFFFF;
34
35 static constexpr int MinimumInitIterations = 8;
36 static constexpr int NumDiscardedInitOutputs = 8;
37
38 static constexpr u32 XorByShifted27(u32 value) {
39 return value ^ (value >> 27);
40 }
41
42 static constexpr u32 XorByShifted30(u32 value) {
43 return value ^ (value >> 30);
44 }
45
46private:
47 State state{};
48
49private:
50 // Internal API.
51 void FinalizeInitialization() {
52 const u32 state0 = this->state.data[0] & TopBitmask;
53 const u32 state1 = this->state.data[1];
54 const u32 state2 = this->state.data[2];
55 const u32 state3 = this->state.data[3];
56
57 if (state0 == 0 && state1 == 0 && state2 == 0 && state3 == 0) {
58 this->state.data[0] = 'T';
59 this->state.data[1] = 'I';
60 this->state.data[2] = 'N';
61 this->state.data[3] = 'Y';
62 }
63
64 for (int i = 0; i < NumDiscardedInitOutputs; i++) {
65 this->GenerateRandomU32();
66 }
67 }
68
69 u32 GenerateRandomU24() {
70 return (this->GenerateRandomU32() >> 8);
71 }
72
73 static void GenerateInitialValuePlus(TinyMT::State* state, int index, u32 value) {
74 u32& state0 = state->data[(index + 0) % NumStateWords];
75 u32& state1 = state->data[(index + 1) % NumStateWords];
76 u32& state2 = state->data[(index + 2) % NumStateWords];
77 u32& state3 = state->data[(index + 3) % NumStateWords];
78
79 const u32 x = XorByShifted27(state0 ^ state1 ^ state3) * ParamPlus;
80 const u32 y = x + index + value;
81
82 state0 = y;
83 state1 += x;
84 state2 += y;
85 }
86
87 static void GenerateInitialValueXor(TinyMT::State* state, int index) {
88 u32& state0 = state->data[(index + 0) % NumStateWords];
89 u32& state1 = state->data[(index + 1) % NumStateWords];
90 u32& state2 = state->data[(index + 2) % NumStateWords];
91 u32& state3 = state->data[(index + 3) % NumStateWords];
92
93 const u32 x = XorByShifted27(state0 + state1 + state3) * ParamXor;
94 const u32 y = x - index;
95
96 state0 = y;
97 state1 ^= x;
98 state2 ^= y;
99 }
100
101public:
102 constexpr TinyMT() = default;
103
104 // Public API.
105
106 // Initialization.
107 void Initialize(u32 seed) {
108 this->state.data[0] = seed;
109 this->state.data[1] = ParamMat1;
110 this->state.data[2] = ParamMat2;
111 this->state.data[3] = ParamTmat;
112
113 for (int i = 1; i < MinimumInitIterations; i++) {
114 const u32 mixed = XorByShifted30(this->state.data[(i - 1) % NumStateWords]);
115 this->state.data[i % NumStateWords] ^= mixed * ParamMult + i;
116 }
117
118 this->FinalizeInitialization();
119 }
120
121 void Initialize(const u32* seed, int seed_count) {
122 this->state.data[0] = 0;
123 this->state.data[1] = ParamMat1;
124 this->state.data[2] = ParamMat2;
125 this->state.data[3] = ParamTmat;
126
127 {
128 const int num_init_iterations = std::max(seed_count + 1, MinimumInitIterations) - 1;
129
130 GenerateInitialValuePlus(&this->state, 0, seed_count);
131
132 for (int i = 0; i < num_init_iterations; i++) {
133 GenerateInitialValuePlus(&this->state, (i + 1) % NumStateWords,
134 (i < seed_count) ? seed[i] : 0);
135 }
136
137 for (int i = 0; i < static_cast<int>(NumStateWords); i++) {
138 GenerateInitialValueXor(&this->state,
139 (i + 1 + num_init_iterations) % NumStateWords);
140 }
141 }
142
143 this->FinalizeInitialization();
144 }
145
146 // State management.
147 void GetState(TinyMT::State& out) const {
148 out.data = this->state.data;
149 }
150
151 void SetState(const TinyMT::State& state_) {
152 this->state.data = state_.data;
153 }
154
155 // Random generation.
156 void GenerateRandomBytes(void* dst, std::size_t size) {
157 const uintptr_t start = reinterpret_cast<uintptr_t>(dst);
158 const uintptr_t end = start + size;
159 const uintptr_t aligned_start = Common::AlignUp(start, 4);
160 const uintptr_t aligned_end = Common::AlignDown(end, 4);
161
162 // Make sure we're aligned.
163 if (start < aligned_start) {
164 const u32 rnd = this->GenerateRandomU32();
165 std::memcpy(dst, &rnd, aligned_start - start);
166 }
167
168 // Write as many aligned u32s as we can.
169 {
170 u32* cur_dst = reinterpret_cast<u32*>(aligned_start);
171 u32* const end_dst = reinterpret_cast<u32*>(aligned_end);
172
173 while (cur_dst < end_dst) {
174 *(cur_dst++) = this->GenerateRandomU32();
175 }
176 }
177
178 // Handle any leftover unaligned data.
179 if (aligned_end < end) {
180 const u32 rnd = this->GenerateRandomU32();
181 std::memcpy(reinterpret_cast<void*>(aligned_end), &rnd, end - aligned_end);
182 }
183 }
184
185 u32 GenerateRandomU32() {
186 // Advance state.
187 const u32 x0 =
188 (this->state.data[0] & TopBitmask) ^ this->state.data[1] ^ this->state.data[2];
189 const u32 y0 = this->state.data[3];
190 const u32 x1 = x0 ^ (x0 << 1);
191 const u32 y1 = y0 ^ (y0 >> 1) ^ x1;
192
193 const u32 state0 = this->state.data[1];
194 u32 state1 = this->state.data[2];
195 u32 state2 = x1 ^ (y1 << 10);
196 const u32 state3 = y1;
197
198 if ((y1 & 1) != 0) {
199 state1 ^= ParamMat1;
200 state2 ^= ParamMat2;
201 }
202
203 this->state.data[0] = state0;
204 this->state.data[1] = state1;
205 this->state.data[2] = state2;
206 this->state.data[3] = state3;
207
208 // Temper.
209 const u32 t1 = state0 + (state2 >> 8);
210 u32 t0 = state3 ^ t1;
211
212 if ((t1 & 1) != 0) {
213 t0 ^= ParamTmat;
214 }
215
216 return t0;
217 }
218
219 u64 GenerateRandomU64() {
220 const u32 lo = this->GenerateRandomU32();
221 const u32 hi = this->GenerateRandomU32();
222 return (u64{hi} << 32) | u64{lo};
223 }
224
225 float GenerateRandomF32() {
226 // Floats have 24 bits of mantissa.
227 constexpr u32 MantissaBits = 24;
228 return static_cast<float>(GenerateRandomU24()) * (1.0f / (1U << MantissaBits));
229 }
230
231 double GenerateRandomF64() {
232 // Doubles have 53 bits of mantissa.
233 // The smart way to generate 53 bits of random would be to use 32 bits
234 // from the first rnd32() call, and then 21 from the second.
235 // Nintendo does not. They use (32 - 5) = 27 bits from the first rnd32()
236 // call, and (32 - 6) bits from the second. We'll do what they do, but
237 // There's not a clear reason why.
238 constexpr u32 MantissaBits = 53;
239 constexpr u32 Shift1st = (64 - MantissaBits) / 2;
240 constexpr u32 Shift2nd = (64 - MantissaBits) - Shift1st;
241
242 const u32 first = (this->GenerateRandomU32() >> Shift1st);
243 const u32 second = (this->GenerateRandomU32() >> Shift2nd);
244
245 return (1.0 * first * (u64{1} << (32 - Shift2nd)) + second) *
246 (1.0 / (u64{1} << MantissaBits));
247 }
248};
249
250} // namespace Common