summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt13
-rw-r--r--src/common/alignment.h3
-rw-r--r--src/common/announce_multiplayer_room.h139
-rw-r--r--src/common/atomic_helpers.h775
-rw-r--r--src/common/bit_field.h9
-rw-r--r--src/common/common_funcs.h10
-rw-r--r--src/common/detached_tasks.cpp5
-rw-r--r--src/common/detached_tasks.h5
-rw-r--r--src/common/error.cpp6
-rw-r--r--src/common/error.h6
-rw-r--r--src/common/fixed_point.h706
-rw-r--r--src/common/hash.h5
-rw-r--r--src/common/input.h34
-rw-r--r--src/common/logging/backend.cpp5
-rw-r--r--src/common/logging/backend.h5
-rw-r--r--src/common/logging/filter.cpp7
-rw-r--r--src/common/logging/filter.h5
-rw-r--r--src/common/logging/log.h5
-rw-r--r--src/common/logging/text_formatter.cpp5
-rw-r--r--src/common/logging/text_formatter.h5
-rw-r--r--src/common/logging/types.h2
-rw-r--r--src/common/microprofile.cpp5
-rw-r--r--src/common/microprofile.h14
-rw-r--r--src/common/microprofileui.h5
-rw-r--r--src/common/param_package.cpp5
-rw-r--r--src/common/param_package.h5
-rw-r--r--src/common/quaternion.h5
-rw-r--r--src/common/reader_writer_queue.h940
-rw-r--r--src/common/scm_rev.cpp.in5
-rw-r--r--src/common/scm_rev.h5
-rw-r--r--src/common/scope_exit.h5
-rw-r--r--src/common/settings.cpp6
-rw-r--r--src/common/settings.h74
-rw-r--r--src/common/socket_types.h51
-rw-r--r--src/common/telemetry.cpp5
-rw-r--r--src/common/telemetry.h5
-rw-r--r--src/common/threadsafe_queue.h2
-rw-r--r--src/common/uint128.h6
-rw-r--r--src/common/wall_clock.cpp2
-rw-r--r--src/common/x64/cpu_detect.cpp16
-rw-r--r--src/common/x64/cpu_detect.h5
-rw-r--r--src/common/x64/native_clock.cpp2
-rw-r--r--src/common/x64/xbyak_abi.h5
-rw-r--r--src/common/x64/xbyak_util.h5
44 files changed, 2788 insertions, 145 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 73bf626d4..635fb85c8 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -1,3 +1,6 @@
1# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
1if (DEFINED ENV{AZURECIREPO}) 4if (DEFINED ENV{AZURECIREPO})
2 set(BUILD_REPOSITORY $ENV{AZURECIREPO}) 5 set(BUILD_REPOSITORY $ENV{AZURECIREPO})
3endif() 6endif()
@@ -41,8 +44,10 @@ add_custom_command(OUTPUT scm_rev.cpp
41add_library(common STATIC 44add_library(common STATIC
42 algorithm.h 45 algorithm.h
43 alignment.h 46 alignment.h
47 announce_multiplayer_room.h
44 assert.cpp 48 assert.cpp
45 assert.h 49 assert.h
50 atomic_helpers.h
46 atomic_ops.h 51 atomic_ops.h
47 detached_tasks.cpp 52 detached_tasks.cpp
48 detached_tasks.h 53 detached_tasks.h
@@ -64,6 +69,7 @@ add_library(common STATIC
64 expected.h 69 expected.h
65 fiber.cpp 70 fiber.cpp
66 fiber.h 71 fiber.h
72 fixed_point.h
67 fs/file.cpp 73 fs/file.cpp
68 fs/file.h 74 fs/file.h
69 fs/fs.cpp 75 fs/fs.cpp
@@ -109,6 +115,7 @@ add_library(common STATIC
109 parent_of_member.h 115 parent_of_member.h
110 point.h 116 point.h
111 quaternion.h 117 quaternion.h
118 reader_writer_queue.h
112 ring_buffer.h 119 ring_buffer.h
113 scm_rev.cpp 120 scm_rev.cpp
114 scm_rev.h 121 scm_rev.h
@@ -117,6 +124,7 @@ add_library(common STATIC
117 settings.h 124 settings.h
118 settings_input.cpp 125 settings_input.cpp
119 settings_input.h 126 settings_input.h
127 socket_types.h
120 spin_lock.cpp 128 spin_lock.cpp
121 spin_lock.h 129 spin_lock.h
122 stream.cpp 130 stream.cpp
@@ -182,8 +190,9 @@ create_target_directory_groups(common)
182 190
183target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) 191target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
184target_link_libraries(common PRIVATE lz4::lz4 xbyak) 192target_link_libraries(common PRIVATE lz4::lz4 xbyak)
185if (MSVC) 193if (TARGET zstd::zstd)
186 target_link_libraries(common PRIVATE zstd::zstd) 194 target_link_libraries(common PRIVATE zstd::zstd)
187else() 195else()
188 target_link_libraries(common PRIVATE zstd) 196 target_link_libraries(common PRIVATE
197 $<IF:$<TARGET_EXISTS:zstd::libzstd_shared>,zstd::libzstd_shared,zstd::libzstd_static>)
189endif() 198endif()
diff --git a/src/common/alignment.h b/src/common/alignment.h
index 8570c7d3c..7e897334b 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -1,4 +1,5 @@
1// This file is under the public domain. 1// SPDX-FileCopyrightText: 2014 Jannik Vogel <email@jannikvogel.de>
2// SPDX-License-Identifier: CC0-1.0
2 3
3#pragma once 4#pragma once
4 5
diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h
new file mode 100644
index 000000000..cb004e0eb
--- /dev/null
+++ b/src/common/announce_multiplayer_room.h
@@ -0,0 +1,139 @@
1// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <functional>
8#include <string>
9#include <vector>
10#include "common/common_types.h"
11#include "common/socket_types.h"
12#include "web_service/web_result.h"
13
14namespace AnnounceMultiplayerRoom {
15
16struct GameInfo {
17 std::string name{""};
18 u64 id{0};
19};
20
21struct Member {
22 std::string username;
23 std::string nickname;
24 std::string display_name;
25 std::string avatar_url;
26 Network::IPv4Address fake_ip;
27 GameInfo game;
28};
29
30struct RoomInformation {
31 std::string name; ///< Name of the server
32 std::string description; ///< Server description
33 u32 member_slots; ///< Maximum number of members in this room
34 u16 port; ///< The port of this room
35 GameInfo preferred_game; ///< Game to advertise that you want to play
36 std::string host_username; ///< Forum username of the host
37 bool enable_yuzu_mods; ///< Allow yuzu Moderators to moderate on this room
38};
39
40struct Room {
41 RoomInformation information;
42
43 std::string id;
44 std::string verify_uid; ///< UID used for verification
45 std::string ip;
46 u32 net_version;
47 bool has_password;
48
49 std::vector<Member> members;
50};
51using RoomList = std::vector<Room>;
52
53/**
54 * A AnnounceMultiplayerRoom interface class. A backend to submit/get to/from a web service should
55 * implement this interface.
56 */
57class Backend {
58public:
59 virtual ~Backend() = default;
60
61 /**
62 * Sets the Information that gets used for the announce
63 * @param uid The Id of the room
64 * @param name The name of the room
65 * @param description The room description
66 * @param port The port of the room
67 * @param net_version The version of the libNetwork that gets used
68 * @param has_password True if the room is passowrd protected
69 * @param preferred_game The preferred game of the room
70 * @param preferred_game_id The title id of the preferred game
71 */
72 virtual void SetRoomInformation(const std::string& name, const std::string& description,
73 const u16 port, const u32 max_player, const u32 net_version,
74 const bool has_password, const GameInfo& preferred_game) = 0;
75 /**
76 * Adds a player information to the data that gets announced
77 * @param member The player to add
78 */
79 virtual void AddPlayer(const Member& member) = 0;
80
81 /**
82 * Updates the data in the announce service. Re-register the room when required.
83 * @result The result of the update attempt
84 */
85 virtual WebService::WebResult Update() = 0;
86
87 /**
88 * Registers the data in the announce service
89 * @result The result of the register attempt. When the result code is Success, A global Guid of
90 * the room which may be used for verification will be in the result's returned_data.
91 */
92 virtual WebService::WebResult Register() = 0;
93
94 /**
95 * Empties the stored players
96 */
97 virtual void ClearPlayers() = 0;
98
99 /**
100 * Get the room information from the announce service
101 * @result A list of all rooms the announce service has
102 */
103 virtual RoomList GetRoomList() = 0;
104
105 /**
106 * Sends a delete message to the announce service
107 */
108 virtual void Delete() = 0;
109};
110
111/**
112 * Empty implementation of AnnounceMultiplayerRoom interface that drops all data. Used when a
113 * functional backend implementation is not available.
114 */
115class NullBackend : public Backend {
116public:
117 ~NullBackend() = default;
118 void SetRoomInformation(const std::string& /*name*/, const std::string& /*description*/,
119 const u16 /*port*/, const u32 /*max_player*/, const u32 /*net_version*/,
120 const bool /*has_password*/,
121 const GameInfo& /*preferred_game*/) override {}
122 void AddPlayer(const Member& /*member*/) override {}
123 WebService::WebResult Update() override {
124 return WebService::WebResult{WebService::WebResult::Code::NoWebservice,
125 "WebService is missing", ""};
126 }
127 WebService::WebResult Register() override {
128 return WebService::WebResult{WebService::WebResult::Code::NoWebservice,
129 "WebService is missing", ""};
130 }
131 void ClearPlayers() override {}
132 RoomList GetRoomList() override {
133 return RoomList{};
134 }
135
136 void Delete() override {}
137};
138
139} // namespace AnnounceMultiplayerRoom
diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h
new file mode 100644
index 000000000..bef5015c1
--- /dev/null
+++ b/src/common/atomic_helpers.h
@@ -0,0 +1,775 @@
1// SPDX-FileCopyrightText: 2013-2016 Cameron Desrochers
2// SPDX-FileCopyrightText: 2015 Jeff Preshing
3// SPDX-License-Identifier: BSD-2-Clause AND Zlib
4
5// Distributed under the simplified BSD license (see the license file that
6// should have come with this header).
7// Uses Jeff Preshing's semaphore implementation (under the terms of its
8// separate zlib license, embedded below).
9
10#pragma once
11
12// Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant)
13// implementation of low-level memory barriers, plus a few semi-portable utility macros (for
14// inlining and alignment). Also has a basic atomic type (limited to hardware-supported atomics with
15// no memory ordering guarantees). Uses the AE_* prefix for macros (historical reasons), and the
16// "moodycamel" namespace for symbols.
17
18#include <cassert>
19#include <cerrno>
20#include <cstdint>
21#include <ctime>
22#include <type_traits>
23
24// Platform detection
25#if defined(__INTEL_COMPILER)
26#define AE_ICC
27#elif defined(_MSC_VER)
28#define AE_VCPP
29#elif defined(__GNUC__)
30#define AE_GCC
31#endif
32
33#if defined(_M_IA64) || defined(__ia64__)
34#define AE_ARCH_IA64
35#elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__)
36#define AE_ARCH_X64
37#elif defined(_M_IX86) || defined(__i386__)
38#define AE_ARCH_X86
39#elif defined(_M_PPC) || defined(__powerpc__)
40#define AE_ARCH_PPC
41#else
42#define AE_ARCH_UNKNOWN
43#endif
44
45// AE_UNUSED
46#define AE_UNUSED(x) ((void)x)
47
48// AE_NO_TSAN/AE_TSAN_ANNOTATE_*
49#if defined(__has_feature)
50#if __has_feature(thread_sanitizer)
51#if __cplusplus >= 201703L // inline variables require C++17
52namespace Common {
53inline int ae_tsan_global;
54}
55#define AE_TSAN_ANNOTATE_RELEASE() \
56 AnnotateHappensBefore(__FILE__, __LINE__, (void*)(&::moodycamel::ae_tsan_global))
57#define AE_TSAN_ANNOTATE_ACQUIRE() \
58 AnnotateHappensAfter(__FILE__, __LINE__, (void*)(&::moodycamel::ae_tsan_global))
59extern "C" void AnnotateHappensBefore(const char*, int, void*);
60extern "C" void AnnotateHappensAfter(const char*, int, void*);
61#else // when we can't work with tsan, attempt to disable its warnings
62#define AE_NO_TSAN __attribute__((no_sanitize("thread")))
63#endif
64#endif
65#endif
66#ifndef AE_NO_TSAN
67#define AE_NO_TSAN
68#endif
69#ifndef AE_TSAN_ANNOTATE_RELEASE
70#define AE_TSAN_ANNOTATE_RELEASE()
71#define AE_TSAN_ANNOTATE_ACQUIRE()
72#endif
73
74// AE_FORCEINLINE
75#if defined(AE_VCPP) || defined(AE_ICC)
76#define AE_FORCEINLINE __forceinline
77#elif defined(AE_GCC)
78//#define AE_FORCEINLINE __attribute__((always_inline))
79#define AE_FORCEINLINE inline
80#else
81#define AE_FORCEINLINE inline
82#endif
83
84// AE_ALIGN
85#if defined(AE_VCPP) || defined(AE_ICC)
86#define AE_ALIGN(x) __declspec(align(x))
87#elif defined(AE_GCC)
88#define AE_ALIGN(x) __attribute__((aligned(x)))
89#else
90// Assume GCC compliant syntax...
91#define AE_ALIGN(x) __attribute__((aligned(x)))
92#endif
93
94// Portable atomic fences implemented below:
95
96namespace Common {
97
98enum memory_order {
99 memory_order_relaxed,
100 memory_order_acquire,
101 memory_order_release,
102 memory_order_acq_rel,
103 memory_order_seq_cst,
104
105 // memory_order_sync: Forces a full sync:
106 // #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad
107 memory_order_sync = memory_order_seq_cst
108};
109
110} // namespace Common
111
112#if (defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))) || \
113 (defined(AE_ICC) && __INTEL_COMPILER < 1600)
114// VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences
115
116#include <intrin.h>
117
118#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
119#define AeFullSync _mm_mfence
120#define AeLiteSync _mm_mfence
121#elif defined(AE_ARCH_IA64)
122#define AeFullSync __mf
123#define AeLiteSync __mf
124#elif defined(AE_ARCH_PPC)
125#include <ppcintrinsics.h>
126#define AeFullSync __sync
127#define AeLiteSync __lwsync
128#endif
129
130#ifdef AE_VCPP
131#pragma warning(push)
132#pragma warning(disable : 4365) // Disable erroneous 'conversion from long to unsigned int,
133 // signed/unsigned mismatch' error when using `assert`
134#ifdef __cplusplus_cli
135#pragma managed(push, off)
136#endif
137#endif
138
139namespace Common {
140
141AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
142 switch (order) {
143 case memory_order_relaxed:
144 break;
145 case memory_order_acquire:
146 _ReadBarrier();
147 break;
148 case memory_order_release:
149 _WriteBarrier();
150 break;
151 case memory_order_acq_rel:
152 _ReadWriteBarrier();
153 break;
154 case memory_order_seq_cst:
155 _ReadWriteBarrier();
156 break;
157 default:
158 assert(false);
159 }
160}
161
162// x86/x64 have a strong memory model -- all loads and stores have
163// acquire and release semantics automatically (so only need compiler
164// barriers for those).
165#if defined(AE_ARCH_X86) || defined(AE_ARCH_X64)
166AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN {
167 switch (order) {
168 case memory_order_relaxed:
169 break;
170 case memory_order_acquire:
171 _ReadBarrier();
172 break;
173 case memory_order_release:
174 _WriteBarrier();
175 break;
176 case memory_order_acq_rel:
177 _ReadWriteBarrier();
178 break;
179 case memory_order_seq_cst:
180 _ReadWriteBarrier();
181 AeFullSync();
182 _ReadWriteBarrier();
183 break;
184 default:
185 assert(false);
186 }
187}
188#else
189AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN {
190 // Non-specialized arch, use heavier memory barriers everywhere just in case :-(
191 switch (order) {
192 case memory_order_relaxed:
193 break;
194 case memory_order_acquire:
195 _ReadBarrier();
196 AeLiteSync();
197 _ReadBarrier();
198 break;
199 case memory_order_release:
200 _WriteBarrier();
201 AeLiteSync();
202 _WriteBarrier();
203 break;
204 case memory_order_acq_rel:
205 _ReadWriteBarrier();
206 AeLiteSync();
207 _ReadWriteBarrier();
208 break;
209 case memory_order_seq_cst:
210 _ReadWriteBarrier();
211 AeFullSync();
212 _ReadWriteBarrier();
213 break;
214 default:
215 assert(false);
216 }
217}
218#endif
219} // namespace Common
220#else
221// Use standard library of atomics
222#include <atomic>
223
224namespace Common {
225
226AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
227 switch (order) {
228 case memory_order_relaxed:
229 break;
230 case memory_order_acquire:
231 std::atomic_signal_fence(std::memory_order_acquire);
232 break;
233 case memory_order_release:
234 std::atomic_signal_fence(std::memory_order_release);
235 break;
236 case memory_order_acq_rel:
237 std::atomic_signal_fence(std::memory_order_acq_rel);
238 break;
239 case memory_order_seq_cst:
240 std::atomic_signal_fence(std::memory_order_seq_cst);
241 break;
242 default:
243 assert(false);
244 }
245}
246
247AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN {
248 switch (order) {
249 case memory_order_relaxed:
250 break;
251 case memory_order_acquire:
252 AE_TSAN_ANNOTATE_ACQUIRE();
253 std::atomic_thread_fence(std::memory_order_acquire);
254 break;
255 case memory_order_release:
256 AE_TSAN_ANNOTATE_RELEASE();
257 std::atomic_thread_fence(std::memory_order_release);
258 break;
259 case memory_order_acq_rel:
260 AE_TSAN_ANNOTATE_ACQUIRE();
261 AE_TSAN_ANNOTATE_RELEASE();
262 std::atomic_thread_fence(std::memory_order_acq_rel);
263 break;
264 case memory_order_seq_cst:
265 AE_TSAN_ANNOTATE_ACQUIRE();
266 AE_TSAN_ANNOTATE_RELEASE();
267 std::atomic_thread_fence(std::memory_order_seq_cst);
268 break;
269 default:
270 assert(false);
271 }
272}
273
274} // namespace Common
275
276#endif
277
278#if !defined(AE_VCPP) || (_MSC_VER >= 1700 && !defined(__cplusplus_cli))
279#define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
280#endif
281
282#ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
283#include <atomic>
284#endif
285#include <utility>
286
287// WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY:
288// Provides basic support for atomic variables -- no memory ordering guarantees are provided.
289// The guarantee of atomicity is only made for types that already have atomic load and store
290// guarantees at the hardware level -- on most platforms this generally means aligned pointers and
291// integers (only).
292namespace Common {
293template <typename T>
294class weak_atomic {
295public:
296 AE_NO_TSAN weak_atomic() : value() {}
297#ifdef AE_VCPP
298#pragma warning(push)
299#pragma warning(disable : 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning
300#endif
301 template <typename U>
302 AE_NO_TSAN weak_atomic(U&& x) : value(std::forward<U>(x)) {}
303#ifdef __cplusplus_cli
304 // Work around bug with universal reference/nullptr combination that only appears when /clr is
305 // on
306 AE_NO_TSAN weak_atomic(nullptr_t) : value(nullptr) {}
307#endif
308 AE_NO_TSAN weak_atomic(weak_atomic const& other) : value(other.load()) {}
309 AE_NO_TSAN weak_atomic(weak_atomic&& other) : value(std::move(other.load())) {}
310#ifdef AE_VCPP
311#pragma warning(pop)
312#endif
313
314 AE_FORCEINLINE operator T() const AE_NO_TSAN {
315 return load();
316 }
317
318#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
319 template <typename U>
320 AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN {
321 value = std::forward<U>(x);
322 return *this;
323 }
324 AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN {
325 value = other.value;
326 return *this;
327 }
328
329 AE_FORCEINLINE T load() const AE_NO_TSAN {
330 return value;
331 }
332
333 AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN {
334#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
335 if (sizeof(T) == 4)
336 return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
337#if defined(_M_AMD64)
338 else if (sizeof(T) == 8)
339 return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
340#endif
341#else
342#error Unsupported platform
343#endif
344 assert(false && "T must be either a 32 or 64 bit type");
345 return value;
346 }
347
348 AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN {
349#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
350 if (sizeof(T) == 4)
351 return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
352#if defined(_M_AMD64)
353 else if (sizeof(T) == 8)
354 return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
355#endif
356#else
357#error Unsupported platform
358#endif
359 assert(false && "T must be either a 32 or 64 bit type");
360 return value;
361 }
362#else
363 template <typename U>
364 AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN {
365 value.store(std::forward<U>(x), std::memory_order_relaxed);
366 return *this;
367 }
368
369 AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN {
370 value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed);
371 return *this;
372 }
373
374 AE_FORCEINLINE T load() const AE_NO_TSAN {
375 return value.load(std::memory_order_relaxed);
376 }
377
378 AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN {
379 return value.fetch_add(increment, std::memory_order_acquire);
380 }
381
382 AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN {
383 return value.fetch_add(increment, std::memory_order_release);
384 }
385#endif
386
387private:
388#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
389 // No std::atomic support, but still need to circumvent compiler optimizations.
390 // `volatile` will make memory access slow, but is guaranteed to be reliable.
391 volatile T value;
392#else
393 std::atomic<T> value;
394#endif
395};
396
397} // namespace Common
398
399// Portable single-producer, single-consumer semaphore below:
400
401#if defined(_WIN32)
402// Avoid including windows.h in a header; we only need a handful of
403// items, so we'll redeclare them here (this is relatively safe since
404// the API generally has to remain stable between Windows versions).
405// I know this is an ugly hack but it still beats polluting the global
406// namespace with thousands of generic names or adding a .cpp for nothing.
407extern "C" {
408struct _SECURITY_ATTRIBUTES;
409__declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes,
410 long lInitialCount, long lMaximumCount,
411 const wchar_t* lpName);
412__declspec(dllimport) int __stdcall CloseHandle(void* hObject);
413__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle,
414 unsigned long dwMilliseconds);
415__declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount,
416 long* lpPreviousCount);
417}
418#elif defined(__MACH__)
419#include <mach/mach.h>
420#elif defined(__unix__)
421#include <semaphore.h>
422#elif defined(FREERTOS)
423#include <FreeRTOS.h>
424#include <semphr.h>
425#include <task.h>
426#endif
427
428namespace Common {
429// Code in the spsc_sema namespace below is an adaptation of Jeff Preshing's
430// portable + lightweight semaphore implementations, originally from
431// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
432// LICENSE:
433// Copyright (c) 2015 Jeff Preshing
434//
435// This software is provided 'as-is', without any express or implied
436// warranty. In no event will the authors be held liable for any damages
437// arising from the use of this software.
438//
439// Permission is granted to anyone to use this software for any purpose,
440// including commercial applications, and to alter it and redistribute it
441// freely, subject to the following restrictions:
442//
443// 1. The origin of this software must not be misrepresented; you must not
444// claim that you wrote the original software. If you use this software
445// in a product, an acknowledgement in the product documentation would be
446// appreciated but is not required.
447// 2. Altered source versions must be plainly marked as such, and must not be
448// misrepresented as being the original software.
449// 3. This notice may not be removed or altered from any source distribution.
450namespace spsc_sema {
451#if defined(_WIN32)
452class Semaphore {
453private:
454 void* m_hSema;
455
456 Semaphore(const Semaphore& other);
457 Semaphore& operator=(const Semaphore& other);
458
459public:
460 AE_NO_TSAN Semaphore(int initialCount = 0) : m_hSema() {
461 assert(initialCount >= 0);
462 const long maxLong = 0x7fffffff;
463 m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
464 assert(m_hSema);
465 }
466
467 AE_NO_TSAN ~Semaphore() {
468 CloseHandle(m_hSema);
469 }
470
471 bool wait() AE_NO_TSAN {
472 const unsigned long infinite = 0xffffffff;
473 return WaitForSingleObject(m_hSema, infinite) == 0;
474 }
475
476 bool try_wait() AE_NO_TSAN {
477 return WaitForSingleObject(m_hSema, 0) == 0;
478 }
479
480 bool timed_wait(std::uint64_t usecs) AE_NO_TSAN {
481 return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0;
482 }
483
484 void signal(int count = 1) AE_NO_TSAN {
485 while (!ReleaseSemaphore(m_hSema, count, nullptr))
486 ;
487 }
488};
489#elif defined(__MACH__)
490//---------------------------------------------------------
491// Semaphore (Apple iOS and OSX)
492// Can't use POSIX semaphores due to
493// http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
494//---------------------------------------------------------
495class Semaphore {
496private:
497 semaphore_t m_sema;
498
499 Semaphore(const Semaphore& other);
500 Semaphore& operator=(const Semaphore& other);
501
502public:
503 AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() {
504 assert(initialCount >= 0);
505 kern_return_t rc =
506 semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
507 assert(rc == KERN_SUCCESS);
508 AE_UNUSED(rc);
509 }
510
511 AE_NO_TSAN ~Semaphore() {
512 semaphore_destroy(mach_task_self(), m_sema);
513 }
514
515 bool wait() AE_NO_TSAN {
516 return semaphore_wait(m_sema) == KERN_SUCCESS;
517 }
518
519 bool try_wait() AE_NO_TSAN {
520 return timed_wait(0);
521 }
522
523 bool timed_wait(std::uint64_t timeout_usecs) AE_NO_TSAN {
524 mach_timespec_t ts;
525 ts.tv_sec = static_cast<unsigned int>(timeout_usecs / 1000000);
526 ts.tv_nsec = static_cast<int>((timeout_usecs % 1000000) * 1000);
527
528 // added in OSX 10.10:
529 // https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html
530 kern_return_t rc = semaphore_timedwait(m_sema, ts);
531 return rc == KERN_SUCCESS;
532 }
533
534 void signal() AE_NO_TSAN {
535 while (semaphore_signal(m_sema) != KERN_SUCCESS)
536 ;
537 }
538
539 void signal(int count) AE_NO_TSAN {
540 while (count-- > 0) {
541 while (semaphore_signal(m_sema) != KERN_SUCCESS)
542 ;
543 }
544 }
545};
546#elif defined(__unix__)
547//---------------------------------------------------------
548// Semaphore (POSIX, Linux)
549//---------------------------------------------------------
550class Semaphore {
551private:
552 sem_t m_sema;
553
554 Semaphore(const Semaphore& other);
555 Semaphore& operator=(const Semaphore& other);
556
557public:
558 AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() {
559 assert(initialCount >= 0);
560 int rc = sem_init(&m_sema, 0, static_cast<unsigned int>(initialCount));
561 assert(rc == 0);
562 AE_UNUSED(rc);
563 }
564
565 AE_NO_TSAN ~Semaphore() {
566 sem_destroy(&m_sema);
567 }
568
569 bool wait() AE_NO_TSAN {
570 // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
571 int rc;
572 do {
573 rc = sem_wait(&m_sema);
574 } while (rc == -1 && errno == EINTR);
575 return rc == 0;
576 }
577
578 bool try_wait() AE_NO_TSAN {
579 int rc;
580 do {
581 rc = sem_trywait(&m_sema);
582 } while (rc == -1 && errno == EINTR);
583 return rc == 0;
584 }
585
586 bool timed_wait(std::uint64_t usecs) AE_NO_TSAN {
587 struct timespec ts;
588 const int usecs_in_1_sec = 1000000;
589 const int nsecs_in_1_sec = 1000000000;
590 clock_gettime(CLOCK_REALTIME, &ts);
591 ts.tv_sec += static_cast<time_t>(usecs / usecs_in_1_sec);
592 ts.tv_nsec += static_cast<long>(usecs % usecs_in_1_sec) * 1000;
593 // sem_timedwait bombs if you have more than 1e9 in tv_nsec
594 // so we have to clean things up before passing it in
595 if (ts.tv_nsec >= nsecs_in_1_sec) {
596 ts.tv_nsec -= nsecs_in_1_sec;
597 ++ts.tv_sec;
598 }
599
600 int rc;
601 do {
602 rc = sem_timedwait(&m_sema, &ts);
603 } while (rc == -1 && errno == EINTR);
604 return rc == 0;
605 }
606
607 void signal() AE_NO_TSAN {
608 while (sem_post(&m_sema) == -1)
609 ;
610 }
611
612 void signal(int count) AE_NO_TSAN {
613 while (count-- > 0) {
614 while (sem_post(&m_sema) == -1)
615 ;
616 }
617 }
618};
619#elif defined(FREERTOS)
620//---------------------------------------------------------
621// Semaphore (FreeRTOS)
622//---------------------------------------------------------
623class Semaphore {
624private:
625 SemaphoreHandle_t m_sema;
626
627 Semaphore(const Semaphore& other);
628 Semaphore& operator=(const Semaphore& other);
629
630public:
631 AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() {
632 assert(initialCount >= 0);
633 m_sema = xSemaphoreCreateCounting(static_cast<UBaseType_t>(~0ull),
634 static_cast<UBaseType_t>(initialCount));
635 assert(m_sema);
636 }
637
638 AE_NO_TSAN ~Semaphore() {
639 vSemaphoreDelete(m_sema);
640 }
641
642 bool wait() AE_NO_TSAN {
643 return xSemaphoreTake(m_sema, portMAX_DELAY) == pdTRUE;
644 }
645
646 bool try_wait() AE_NO_TSAN {
647 // Note: In an ISR context, if this causes a task to unblock,
648 // the caller won't know about it
649 if (xPortIsInsideInterrupt())
650 return xSemaphoreTakeFromISR(m_sema, NULL) == pdTRUE;
651 return xSemaphoreTake(m_sema, 0) == pdTRUE;
652 }
653
654 bool timed_wait(std::uint64_t usecs) AE_NO_TSAN {
655 std::uint64_t msecs = usecs / 1000;
656 TickType_t ticks = static_cast<TickType_t>(msecs / portTICK_PERIOD_MS);
657 if (ticks == 0)
658 return try_wait();
659 return xSemaphoreTake(m_sema, ticks) == pdTRUE;
660 }
661
662 void signal() AE_NO_TSAN {
663 // Note: In an ISR context, if this causes a task to unblock,
664 // the caller won't know about it
665 BaseType_t rc;
666 if (xPortIsInsideInterrupt())
667 rc = xSemaphoreGiveFromISR(m_sema, NULL);
668 else
669 rc = xSemaphoreGive(m_sema);
670 assert(rc == pdTRUE);
671 AE_UNUSED(rc);
672 }
673
674 void signal(int count) AE_NO_TSAN {
675 while (count-- > 0)
676 signal();
677 }
678};
679#else
680#error Unsupported platform! (No semaphore wrapper available)
681#endif
682
683//---------------------------------------------------------
684// LightweightSemaphore
685//---------------------------------------------------------
686class LightweightSemaphore {
687public:
688 typedef std::make_signed<std::size_t>::type ssize_t;
689
690private:
691 weak_atomic<ssize_t> m_count;
692 Semaphore m_sema;
693
694 bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1) AE_NO_TSAN {
695 ssize_t oldCount;
696 // Is there a better way to set the initial spin count?
697 // If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC,
698 // as threads start hitting the kernel semaphore.
699 int spin = 1024;
700 while (--spin >= 0) {
701 if (m_count.load() > 0) {
702 m_count.fetch_add_acquire(-1);
703 return true;
704 }
705 compiler_fence(memory_order_acquire); // Prevent the compiler from collapsing the loop.
706 }
707 oldCount = m_count.fetch_add_acquire(-1);
708 if (oldCount > 0)
709 return true;
710 if (timeout_usecs < 0) {
711 if (m_sema.wait())
712 return true;
713 }
714 if (timeout_usecs > 0 && m_sema.timed_wait(static_cast<uint64_t>(timeout_usecs)))
715 return true;
716 // At this point, we've timed out waiting for the semaphore, but the
717 // count is still decremented indicating we may still be waiting on
718 // it. So we have to re-adjust the count, but only if the semaphore
719 // wasn't signaled enough times for us too since then. If it was, we
720 // need to release the semaphore too.
721 while (true) {
722 oldCount = m_count.fetch_add_release(1);
723 if (oldCount < 0)
724 return false; // successfully restored things to the way they were
725 // Oh, the producer thread just signaled the semaphore after all. Try again:
726 oldCount = m_count.fetch_add_acquire(-1);
727 if (oldCount > 0 && m_sema.try_wait())
728 return true;
729 }
730 }
731
732public:
733 AE_NO_TSAN LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount), m_sema() {
734 assert(initialCount >= 0);
735 }
736
737 bool tryWait() AE_NO_TSAN {
738 if (m_count.load() > 0) {
739 m_count.fetch_add_acquire(-1);
740 return true;
741 }
742 return false;
743 }
744
745 bool wait() AE_NO_TSAN {
746 return tryWait() || waitWithPartialSpinning();
747 }
748
749 bool wait(std::int64_t timeout_usecs) AE_NO_TSAN {
750 return tryWait() || waitWithPartialSpinning(timeout_usecs);
751 }
752
753 void signal(ssize_t count = 1) AE_NO_TSAN {
754 assert(count >= 0);
755 ssize_t oldCount = m_count.fetch_add_release(count);
756 assert(oldCount >= -1);
757 if (oldCount < 0) {
758 m_sema.signal(1);
759 }
760 }
761
762 std::size_t availableApprox() const AE_NO_TSAN {
763 ssize_t count = m_count.load();
764 return count > 0 ? static_cast<std::size_t>(count) : 0;
765 }
766};
767} // namespace spsc_sema
768} // namespace Common
769
770#if defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))
771#pragma warning(pop)
772#ifdef __cplusplus_cli
773#pragma managed(pop)
774#endif
775#endif
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 16d805694..7e1df62b1 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -146,7 +146,16 @@ public:
146 } 146 }
147 147
148 constexpr void Assign(const T& value) { 148 constexpr void Assign(const T& value) {
149#ifdef _MSC_VER
149 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); 150 storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
151#else
152 // Explicitly reload with memcpy to avoid compiler aliasing quirks
153 // regarding optimization: GCC/Clang clobber chained stores to
154 // different bitfields in the same struct with the last value.
155 StorageTypeWithEndian storage_;
156 std::memcpy(&storage_, &storage, sizeof(storage_));
157 storage = static_cast<StorageType>((storage_ & ~mask) | FormatValue(value));
158#endif
150 } 159 }
151 160
152 [[nodiscard]] constexpr T Value() const { 161 [[nodiscard]] constexpr T Value() const {
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index adc31c758..e1e2a90fc 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -18,14 +18,16 @@
18/// Helper macros to insert unused bytes or words to properly align structs. These values will be 18/// Helper macros to insert unused bytes or words to properly align structs. These values will be
19/// zero-initialized. 19/// zero-initialized.
20#define INSERT_PADDING_BYTES(num_bytes) \ 20#define INSERT_PADDING_BYTES(num_bytes) \
21 std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {} 21 [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {}
22#define INSERT_PADDING_WORDS(num_words) \ 22#define INSERT_PADDING_WORDS(num_words) \
23 std::array<u32, num_words> CONCAT2(pad, __LINE__) {} 23 [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__) {}
24 24
25/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents. 25/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents.
26/// This keeps the structure trivial to construct. 26/// This keeps the structure trivial to construct.
27#define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__) 27#define INSERT_PADDING_BYTES_NOINIT(num_bytes) \
28#define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__) 28 [[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
29#define INSERT_PADDING_WORDS_NOINIT(num_words) \
30 [[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__)
29 31
30#ifndef _MSC_VER 32#ifndef _MSC_VER
31 33
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index ec31d0b88..da64848da 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -1,6 +1,5 @@
1// Copyright 2018 Citra Emulator Project 1// SPDX-FileCopyrightText: 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <thread> 4#include <thread>
6#include "common/assert.h" 5#include "common/assert.h"
diff --git a/src/common/detached_tasks.h b/src/common/detached_tasks.h
index 5dd8fc27b..416a2d7f3 100644
--- a/src/common/detached_tasks.h
+++ b/src/common/detached_tasks.h
@@ -1,6 +1,5 @@
1// Copyright 2018 Citra Emulator Project 1// SPDX-FileCopyrightText: 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/error.cpp b/src/common/error.cpp
index d4455e310..ddb03bd45 100644
--- a/src/common/error.cpp
+++ b/src/common/error.cpp
@@ -1,6 +1,6 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#include <cstddef> 5#include <cstddef>
6#ifdef _WIN32 6#ifdef _WIN32
diff --git a/src/common/error.h b/src/common/error.h
index e084d4b0f..62a3bd835 100644
--- a/src/common/error.h
+++ b/src/common/error.h
@@ -1,6 +1,6 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-FileCopyrightText: 2014 Citra Emulator Project
3// Refer to the license.txt file included. 3// SPDX-License-Identifier: GPL-2.0-or-later
4 4
5#pragma once 5#pragma once
6 6
diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h
new file mode 100644
index 000000000..4a0f72cc9
--- /dev/null
+++ b/src/common/fixed_point.h
@@ -0,0 +1,706 @@
1// SPDX-FileCopyrightText: 2015 Evan Teran
2// SPDX-License-Identifier: MIT
3
4// From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h
5// See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math
6
7#ifndef FIXED_H_
8#define FIXED_H_
9
10#if __cplusplus >= 201402L
11#define CONSTEXPR14 constexpr
12#else
13#define CONSTEXPR14
14#endif
15
16#include <cstddef> // for size_t
17#include <cstdint>
18#include <exception>
19#include <ostream>
20#include <type_traits>
21
22namespace Common {
23
24template <size_t I, size_t F>
25class FixedPoint;
26
27namespace detail {
28
29// helper templates to make magic with types :)
30// these allow us to determine resonable types from
31// a desired size, they also let us infer the next largest type
32// from a type which is nice for the division op
33template <size_t T>
34struct type_from_size {
35 using value_type = void;
36 using unsigned_type = void;
37 using signed_type = void;
38 static constexpr bool is_specialized = false;
39};
40
41#if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__)
42template <>
43struct type_from_size<128> {
44 static constexpr bool is_specialized = true;
45 static constexpr size_t size = 128;
46
47 using value_type = __int128;
48 using unsigned_type = unsigned __int128;
49 using signed_type = __int128;
50 using next_size = type_from_size<256>;
51};
52#endif
53
54template <>
55struct type_from_size<64> {
56 static constexpr bool is_specialized = true;
57 static constexpr size_t size = 64;
58
59 using value_type = int64_t;
60 using unsigned_type = std::make_unsigned<value_type>::type;
61 using signed_type = std::make_signed<value_type>::type;
62 using next_size = type_from_size<128>;
63};
64
65template <>
66struct type_from_size<32> {
67 static constexpr bool is_specialized = true;
68 static constexpr size_t size = 32;
69
70 using value_type = int32_t;
71 using unsigned_type = std::make_unsigned<value_type>::type;
72 using signed_type = std::make_signed<value_type>::type;
73 using next_size = type_from_size<64>;
74};
75
76template <>
77struct type_from_size<16> {
78 static constexpr bool is_specialized = true;
79 static constexpr size_t size = 16;
80
81 using value_type = int16_t;
82 using unsigned_type = std::make_unsigned<value_type>::type;
83 using signed_type = std::make_signed<value_type>::type;
84 using next_size = type_from_size<32>;
85};
86
87template <>
88struct type_from_size<8> {
89 static constexpr bool is_specialized = true;
90 static constexpr size_t size = 8;
91
92 using value_type = int8_t;
93 using unsigned_type = std::make_unsigned<value_type>::type;
94 using signed_type = std::make_signed<value_type>::type;
95 using next_size = type_from_size<16>;
96};
97
98// this is to assist in adding support for non-native base
99// types (for adding big-int support), this should be fine
100// unless your bit-int class doesn't nicely support casting
101template <class B, class N>
102constexpr B next_to_base(N rhs) {
103 return static_cast<B>(rhs);
104}
105
106struct divide_by_zero : std::exception {};
107
108template <size_t I, size_t F>
109CONSTEXPR14 FixedPoint<I, F> divide(
110 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
111 typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
112
113 using next_type = typename FixedPoint<I, F>::next_type;
114 using base_type = typename FixedPoint<I, F>::base_type;
115 constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
116
117 next_type t(numerator.to_raw());
118 t <<= fractional_bits;
119
120 FixedPoint<I, F> quotient;
121
122 quotient = FixedPoint<I, F>::from_base(next_to_base<base_type>(t / denominator.to_raw()));
123 remainder = FixedPoint<I, F>::from_base(next_to_base<base_type>(t % denominator.to_raw()));
124
125 return quotient;
126}
127
128template <size_t I, size_t F>
129CONSTEXPR14 FixedPoint<I, F> divide(
130 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
131 typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
132
133 using unsigned_type = typename FixedPoint<I, F>::unsigned_type;
134
135 constexpr int bits = FixedPoint<I, F>::total_bits;
136
137 if (denominator == 0) {
138 throw divide_by_zero();
139 } else {
140
141 int sign = 0;
142
143 FixedPoint<I, F> quotient;
144
145 if (numerator < 0) {
146 sign ^= 1;
147 numerator = -numerator;
148 }
149
150 if (denominator < 0) {
151 sign ^= 1;
152 denominator = -denominator;
153 }
154
155 unsigned_type n = numerator.to_raw();
156 unsigned_type d = denominator.to_raw();
157 unsigned_type x = 1;
158 unsigned_type answer = 0;
159
160 // egyptian division algorithm
161 while ((n >= d) && (((d >> (bits - 1)) & 1) == 0)) {
162 x <<= 1;
163 d <<= 1;
164 }
165
166 while (x != 0) {
167 if (n >= d) {
168 n -= d;
169 answer += x;
170 }
171
172 x >>= 1;
173 d >>= 1;
174 }
175
176 unsigned_type l1 = n;
177 unsigned_type l2 = denominator.to_raw();
178
179 // calculate the lower bits (needs to be unsigned)
180 while (l1 >> (bits - F) > 0) {
181 l1 >>= 1;
182 l2 >>= 1;
183 }
184 const unsigned_type lo = (l1 << F) / l2;
185
186 quotient = FixedPoint<I, F>::from_base((answer << F) | lo);
187 remainder = n;
188
189 if (sign) {
190 quotient = -quotient;
191 }
192
193 return quotient;
194 }
195}
196
197// this is the usual implementation of multiplication
198template <size_t I, size_t F>
199CONSTEXPR14 FixedPoint<I, F> multiply(
200 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
201 typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
202
203 using next_type = typename FixedPoint<I, F>::next_type;
204 using base_type = typename FixedPoint<I, F>::base_type;
205
206 constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
207
208 next_type t(static_cast<next_type>(lhs.to_raw()) * static_cast<next_type>(rhs.to_raw()));
209 t >>= fractional_bits;
210
211 return FixedPoint<I, F>::from_base(next_to_base<base_type>(t));
212}
213
214// this is the fall back version we use when we don't have a next size
215// it is slightly slower, but is more robust since it doesn't
216// require and upgraded type
217template <size_t I, size_t F>
218CONSTEXPR14 FixedPoint<I, F> multiply(
219 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
220 typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
221
222 using base_type = typename FixedPoint<I, F>::base_type;
223
224 constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
225 constexpr base_type integer_mask = FixedPoint<I, F>::integer_mask;
226 constexpr base_type fractional_mask = FixedPoint<I, F>::fractional_mask;
227
228 // more costly but doesn't need a larger type
229 const base_type a_hi = (lhs.to_raw() & integer_mask) >> fractional_bits;
230 const base_type b_hi = (rhs.to_raw() & integer_mask) >> fractional_bits;
231 const base_type a_lo = (lhs.to_raw() & fractional_mask);
232 const base_type b_lo = (rhs.to_raw() & fractional_mask);
233
234 const base_type x1 = a_hi * b_hi;
235 const base_type x2 = a_hi * b_lo;
236 const base_type x3 = a_lo * b_hi;
237 const base_type x4 = a_lo * b_lo;
238
239 return FixedPoint<I, F>::from_base((x1 << fractional_bits) + (x3 + x2) +
240 (x4 >> fractional_bits));
241}
242} // namespace detail
243
244template <size_t I, size_t F>
245class FixedPoint {
246 static_assert(detail::type_from_size<I + F>::is_specialized, "invalid combination of sizes");
247
248public:
249 static constexpr size_t fractional_bits = F;
250 static constexpr size_t integer_bits = I;
251 static constexpr size_t total_bits = I + F;
252
253 using base_type_info = detail::type_from_size<total_bits>;
254
255 using base_type = typename base_type_info::value_type;
256 using next_type = typename base_type_info::next_size::value_type;
257 using unsigned_type = typename base_type_info::unsigned_type;
258
259public:
260#ifdef __GNUC__
261#pragma GCC diagnostic push
262#pragma GCC diagnostic ignored "-Woverflow"
263#endif
264 static constexpr base_type fractional_mask =
265 ~(static_cast<unsigned_type>(~base_type(0)) << fractional_bits);
266 static constexpr base_type integer_mask = ~fractional_mask;
267#ifdef __GNUC__
268#pragma GCC diagnostic pop
269#endif
270
271public:
272 static constexpr base_type one = base_type(1) << fractional_bits;
273
274public: // constructors
275 FixedPoint() = default;
276 FixedPoint(const FixedPoint&) = default;
277 FixedPoint(FixedPoint&&) = default;
278 FixedPoint& operator=(const FixedPoint&) = default;
279
280 template <class Number>
281 constexpr FixedPoint(
282 Number n, typename std::enable_if<std::is_arithmetic<Number>::value>::type* = nullptr)
283 : data_(static_cast<base_type>(n * one)) {}
284
285public: // conversion
286 template <size_t I2, size_t F2>
287 CONSTEXPR14 explicit FixedPoint(FixedPoint<I2, F2> other) {
288 static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types");
289 using T = FixedPoint<I2, F2>;
290
291 const base_type fractional = (other.data_ & T::fractional_mask);
292 const base_type integer = (other.data_ & T::integer_mask) >> T::fractional_bits;
293 data_ =
294 (integer << fractional_bits) | (fractional << (fractional_bits - T::fractional_bits));
295 }
296
297private:
298 // this makes it simpler to create a FixedPoint point object from
299 // a native type without scaling
300 // use "FixedPoint::from_base" in order to perform this.
301 struct NoScale {};
302
303 constexpr FixedPoint(base_type n, const NoScale&) : data_(n) {}
304
305public:
306 static constexpr FixedPoint from_base(base_type n) {
307 return FixedPoint(n, NoScale());
308 }
309
310public: // comparison operators
311 constexpr bool operator==(FixedPoint rhs) const {
312 return data_ == rhs.data_;
313 }
314
315 constexpr bool operator!=(FixedPoint rhs) const {
316 return data_ != rhs.data_;
317 }
318
319 constexpr bool operator<(FixedPoint rhs) const {
320 return data_ < rhs.data_;
321 }
322
323 constexpr bool operator>(FixedPoint rhs) const {
324 return data_ > rhs.data_;
325 }
326
327 constexpr bool operator<=(FixedPoint rhs) const {
328 return data_ <= rhs.data_;
329 }
330
331 constexpr bool operator>=(FixedPoint rhs) const {
332 return data_ >= rhs.data_;
333 }
334
335public: // unary operators
336 constexpr bool operator!() const {
337 return !data_;
338 }
339
340 constexpr FixedPoint operator~() const {
341 // NOTE(eteran): this will often appear to "just negate" the value
342 // that is not an error, it is because -x == (~x+1)
343 // and that "+1" is adding an infinitesimally small fraction to the
344 // complimented value
345 return FixedPoint::from_base(~data_);
346 }
347
348 constexpr FixedPoint operator-() const {
349 return FixedPoint::from_base(-data_);
350 }
351
352 constexpr FixedPoint operator+() const {
353 return FixedPoint::from_base(+data_);
354 }
355
356 CONSTEXPR14 FixedPoint& operator++() {
357 data_ += one;
358 return *this;
359 }
360
361 CONSTEXPR14 FixedPoint& operator--() {
362 data_ -= one;
363 return *this;
364 }
365
366 CONSTEXPR14 FixedPoint operator++(int) {
367 FixedPoint tmp(*this);
368 data_ += one;
369 return tmp;
370 }
371
372 CONSTEXPR14 FixedPoint operator--(int) {
373 FixedPoint tmp(*this);
374 data_ -= one;
375 return tmp;
376 }
377
378public: // basic math operators
379 CONSTEXPR14 FixedPoint& operator+=(FixedPoint n) {
380 data_ += n.data_;
381 return *this;
382 }
383
384 CONSTEXPR14 FixedPoint& operator-=(FixedPoint n) {
385 data_ -= n.data_;
386 return *this;
387 }
388
389 CONSTEXPR14 FixedPoint& operator*=(FixedPoint n) {
390 return assign(detail::multiply(*this, n));
391 }
392
393 CONSTEXPR14 FixedPoint& operator/=(FixedPoint n) {
394 FixedPoint temp;
395 return assign(detail::divide(*this, n, temp));
396 }
397
398private:
399 CONSTEXPR14 FixedPoint& assign(FixedPoint rhs) {
400 data_ = rhs.data_;
401 return *this;
402 }
403
404public: // binary math operators, effects underlying bit pattern since these
405 // don't really typically make sense for non-integer values
406 CONSTEXPR14 FixedPoint& operator&=(FixedPoint n) {
407 data_ &= n.data_;
408 return *this;
409 }
410
411 CONSTEXPR14 FixedPoint& operator|=(FixedPoint n) {
412 data_ |= n.data_;
413 return *this;
414 }
415
416 CONSTEXPR14 FixedPoint& operator^=(FixedPoint n) {
417 data_ ^= n.data_;
418 return *this;
419 }
420
421 template <class Integer,
422 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
423 CONSTEXPR14 FixedPoint& operator>>=(Integer n) {
424 data_ >>= n;
425 return *this;
426 }
427
428 template <class Integer,
429 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
430 CONSTEXPR14 FixedPoint& operator<<=(Integer n) {
431 data_ <<= n;
432 return *this;
433 }
434
435public: // conversion to basic types
436 constexpr void round_up() {
437 data_ += (data_ & fractional_mask) >> 1;
438 }
439
440 constexpr int to_int() {
441 round_up();
442 return static_cast<int>((data_ & integer_mask) >> fractional_bits);
443 }
444
445 constexpr unsigned int to_uint() const {
446 round_up();
447 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
448 }
449
450 constexpr int64_t to_long() {
451 round_up();
452 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
453 }
454
455 constexpr int to_int_floor() const {
456 return static_cast<int>((data_ & integer_mask) >> fractional_bits);
457 }
458
459 constexpr int64_t to_long_floor() {
460 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
461 }
462
463 constexpr unsigned int to_uint_floor() const {
464 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
465 }
466
467 constexpr float to_float() const {
468 return static_cast<float>(data_) / FixedPoint::one;
469 }
470
471 constexpr double to_double() const {
472 return static_cast<double>(data_) / FixedPoint::one;
473 }
474
475 constexpr base_type to_raw() const {
476 return data_;
477 }
478
479 constexpr void clear_int() {
480 data_ &= fractional_mask;
481 }
482
483 constexpr base_type get_frac() const {
484 return data_ & fractional_mask;
485 }
486
487public:
488 CONSTEXPR14 void swap(FixedPoint& rhs) {
489 using std::swap;
490 swap(data_, rhs.data_);
491 }
492
493public:
494 base_type data_;
495};
496
497// if we have the same fractional portion, but differing integer portions, we trivially upgrade the
498// smaller type
499template <size_t I1, size_t I2, size_t F>
500CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
501operator+(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
502
503 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
504
505 const T l = T::from_base(lhs.to_raw());
506 const T r = T::from_base(rhs.to_raw());
507 return l + r;
508}
509
510template <size_t I1, size_t I2, size_t F>
511CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
512operator-(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
513
514 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
515
516 const T l = T::from_base(lhs.to_raw());
517 const T r = T::from_base(rhs.to_raw());
518 return l - r;
519}
520
521template <size_t I1, size_t I2, size_t F>
522CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
523operator*(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
524
525 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
526
527 const T l = T::from_base(lhs.to_raw());
528 const T r = T::from_base(rhs.to_raw());
529 return l * r;
530}
531
532template <size_t I1, size_t I2, size_t F>
533CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
534operator/(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
535
536 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
537
538 const T l = T::from_base(lhs.to_raw());
539 const T r = T::from_base(rhs.to_raw());
540 return l / r;
541}
542
543template <size_t I, size_t F>
544std::ostream& operator<<(std::ostream& os, FixedPoint<I, F> f) {
545 os << f.to_double();
546 return os;
547}
548
549// basic math operators
550template <size_t I, size_t F>
551CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
552 lhs += rhs;
553 return lhs;
554}
555template <size_t I, size_t F>
556CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
557 lhs -= rhs;
558 return lhs;
559}
560template <size_t I, size_t F>
561CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
562 lhs *= rhs;
563 return lhs;
564}
565template <size_t I, size_t F>
566CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
567 lhs /= rhs;
568 return lhs;
569}
570
571template <size_t I, size_t F, class Number,
572 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
573CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
574 lhs += FixedPoint<I, F>(rhs);
575 return lhs;
576}
577template <size_t I, size_t F, class Number,
578 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
579CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
580 lhs -= FixedPoint<I, F>(rhs);
581 return lhs;
582}
583template <size_t I, size_t F, class Number,
584 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
585CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
586 lhs *= FixedPoint<I, F>(rhs);
587 return lhs;
588}
589template <size_t I, size_t F, class Number,
590 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
591CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
592 lhs /= FixedPoint<I, F>(rhs);
593 return lhs;
594}
595
596template <size_t I, size_t F, class Number,
597 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
598CONSTEXPR14 FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
599 FixedPoint<I, F> tmp(lhs);
600 tmp += rhs;
601 return tmp;
602}
603template <size_t I, size_t F, class Number,
604 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
605CONSTEXPR14 FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
606 FixedPoint<I, F> tmp(lhs);
607 tmp -= rhs;
608 return tmp;
609}
610template <size_t I, size_t F, class Number,
611 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
612CONSTEXPR14 FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
613 FixedPoint<I, F> tmp(lhs);
614 tmp *= rhs;
615 return tmp;
616}
617template <size_t I, size_t F, class Number,
618 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
619CONSTEXPR14 FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
620 FixedPoint<I, F> tmp(lhs);
621 tmp /= rhs;
622 return tmp;
623}
624
625// shift operators
626template <size_t I, size_t F, class Integer,
627 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
628CONSTEXPR14 FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
629 lhs <<= rhs;
630 return lhs;
631}
632template <size_t I, size_t F, class Integer,
633 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
634CONSTEXPR14 FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
635 lhs >>= rhs;
636 return lhs;
637}
638
639// comparison operators
640template <size_t I, size_t F, class Number,
641 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
642constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) {
643 return lhs > FixedPoint<I, F>(rhs);
644}
645template <size_t I, size_t F, class Number,
646 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
647constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) {
648 return lhs < FixedPoint<I, F>(rhs);
649}
650template <size_t I, size_t F, class Number,
651 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
652constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) {
653 return lhs >= FixedPoint<I, F>(rhs);
654}
655template <size_t I, size_t F, class Number,
656 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
657constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) {
658 return lhs <= FixedPoint<I, F>(rhs);
659}
660template <size_t I, size_t F, class Number,
661 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
662constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) {
663 return lhs == FixedPoint<I, F>(rhs);
664}
665template <size_t I, size_t F, class Number,
666 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
667constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) {
668 return lhs != FixedPoint<I, F>(rhs);
669}
670
671template <size_t I, size_t F, class Number,
672 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
673constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) {
674 return FixedPoint<I, F>(lhs) > rhs;
675}
676template <size_t I, size_t F, class Number,
677 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
678constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) {
679 return FixedPoint<I, F>(lhs) < rhs;
680}
681template <size_t I, size_t F, class Number,
682 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
683constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) {
684 return FixedPoint<I, F>(lhs) >= rhs;
685}
686template <size_t I, size_t F, class Number,
687 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
688constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) {
689 return FixedPoint<I, F>(lhs) <= rhs;
690}
691template <size_t I, size_t F, class Number,
692 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
693constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) {
694 return FixedPoint<I, F>(lhs) == rhs;
695}
696template <size_t I, size_t F, class Number,
697 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
698constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) {
699 return FixedPoint<I, F>(lhs) != rhs;
700}
701
702} // namespace Common
703
704#undef CONSTEXPR14
705
706#endif
diff --git a/src/common/hash.h b/src/common/hash.h
index 298930702..b6f3e6d6f 100644
--- a/src/common/hash.h
+++ b/src/common/hash.h
@@ -1,6 +1,5 @@
1// Copyright 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/input.h b/src/common/input.h
index bb42aaacc..213aa2384 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -1,6 +1,5 @@
1// Copyright 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -28,7 +27,7 @@ enum class InputType {
28 Color, 27 Color,
29 Vibration, 28 Vibration,
30 Nfc, 29 Nfc,
31 Ir, 30 IrSensor,
32}; 31};
33 32
34// Internal battery charge level 33// Internal battery charge level
@@ -53,6 +52,15 @@ enum class PollingMode {
53 IR, 52 IR,
54}; 53};
55 54
55enum class CameraFormat {
56 Size320x240,
57 Size160x120,
58 Size80x60,
59 Size40x30,
60 Size20x15,
61 None,
62};
63
56// Vibration reply from the controller 64// Vibration reply from the controller
57enum class VibrationError { 65enum class VibrationError {
58 None, 66 None,
@@ -68,6 +76,13 @@ enum class PollingError {
68 Unknown, 76 Unknown,
69}; 77};
70 78
79// Ir camera reply from the controller
80enum class CameraError {
81 None,
82 NotSupported,
83 Unknown,
84};
85
71// Hint for amplification curve to be used 86// Hint for amplification curve to be used
72enum class VibrationAmplificationType { 87enum class VibrationAmplificationType {
73 Linear, 88 Linear,
@@ -176,6 +191,12 @@ struct LedStatus {
176 bool led_4{}; 191 bool led_4{};
177}; 192};
178 193
194// Raw data fom camera
195struct CameraStatus {
196 CameraFormat format{CameraFormat::None};
197 std::vector<u8> data{};
198};
199
179// List of buttons to be passed to Qt that can be translated 200// List of buttons to be passed to Qt that can be translated
180enum class ButtonNames { 201enum class ButtonNames {
181 Undefined, 202 Undefined,
@@ -233,6 +254,7 @@ struct CallbackStatus {
233 BodyColorStatus color_status{}; 254 BodyColorStatus color_status{};
234 BatteryStatus battery_status{}; 255 BatteryStatus battery_status{};
235 VibrationStatus vibration_status{}; 256 VibrationStatus vibration_status{};
257 CameraStatus camera_status{};
236}; 258};
237 259
238// Triggered once every input change 260// Triggered once every input change
@@ -281,6 +303,10 @@ public:
281 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { 303 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
282 return PollingError::NotSupported; 304 return PollingError::NotSupported;
283 } 305 }
306
307 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
308 return CameraError::NotSupported;
309 }
284}; 310};
285 311
286/// An abstract class template for a factory that can create input devices. 312/// An abstract class template for a factory that can create input devices.
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index b3793106d..8ce1c2fd1 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <atomic> 4#include <atomic>
6#include <chrono> 5#include <chrono>
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index a0e80fe3c..12e5e2498 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 4acbff649..a959acb74 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6#include "common/logging/filter.h" 5#include "common/logging/filter.h"
@@ -128,7 +127,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
128 SUB(Service, PM) \ 127 SUB(Service, PM) \
129 SUB(Service, PREPO) \ 128 SUB(Service, PREPO) \
130 SUB(Service, PSC) \ 129 SUB(Service, PSC) \
131 SUB(Service, PSM) \ 130 SUB(Service, PTM) \
132 SUB(Service, SET) \ 131 SUB(Service, SET) \
133 SUB(Service, SM) \ 132 SUB(Service, SM) \
134 SUB(Service, SPL) \ 133 SUB(Service, SPL) \
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h
index 29419f051..54d172cc0 100644
--- a/src/common/logging/filter.h
+++ b/src/common/logging/filter.h
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 0c80d01ee..c00c01a9e 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp
index b2cad58d8..09398ea64 100644
--- a/src/common/logging/text_formatter.cpp
+++ b/src/common/logging/text_formatter.cpp
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <array> 4#include <array>
6#include <cstdio> 5#include <cstdio>
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h
index 92c0bf0c5..0d0ec4370 100644
--- a/src/common/logging/text_formatter.h
+++ b/src/common/logging/text_formatter.h
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index cabb4db8e..595c15ada 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -95,7 +95,7 @@ enum class Class : u8 {
95 Service_PM, ///< The PM service 95 Service_PM, ///< The PM service
96 Service_PREPO, ///< The PREPO (Play report) service 96 Service_PREPO, ///< The PREPO (Play report) service
97 Service_PSC, ///< The PSC service 97 Service_PSC, ///< The PSC service
98 Service_PSM, ///< The PSM service 98 Service_PTM, ///< The PTM service
99 Service_SET, ///< The SET (Settings) service 99 Service_SET, ///< The SET (Settings) service
100 Service_SM, ///< The SM (Service manager) service 100 Service_SM, ///< The SM (Service manager) service
101 Service_SPL, ///< The SPL service 101 Service_SPL, ///< The SPL service
diff --git a/src/common/microprofile.cpp b/src/common/microprofile.cpp
index ee25dd37f..e6657c82f 100644
--- a/src/common/microprofile.cpp
+++ b/src/common/microprofile.cpp
@@ -1,6 +1,5 @@
1// Copyright 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5// Includes the MicroProfile implementation in this file for compilation 4// Includes the MicroProfile implementation in this file for compilation
6#define MICROPROFILE_IMPL 1 5#define MICROPROFILE_IMPL 1
diff --git a/src/common/microprofile.h b/src/common/microprofile.h
index 54e7f3cc4..56ef0a2dc 100644
--- a/src/common/microprofile.h
+++ b/src/common/microprofile.h
@@ -1,6 +1,5 @@
1// Copyright 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -23,12 +22,3 @@ typedef void* HANDLE;
23#include <microprofile.h> 22#include <microprofile.h>
24 23
25#define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0) 24#define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0)
26
27// On OS X, some Mach header included by MicroProfile defines these as macros, conflicting with
28// identifiers we use.
29#ifdef PAGE_SIZE
30#undef PAGE_SIZE
31#endif
32#ifdef PAGE_MASK
33#undef PAGE_MASK
34#endif
diff --git a/src/common/microprofileui.h b/src/common/microprofileui.h
index 41abe6b75..39ed18ffa 100644
--- a/src/common/microprofileui.h
+++ b/src/common/microprofileui.h
@@ -1,6 +1,5 @@
1// Copyright 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp
index 462502e34..629babb81 100644
--- a/src/common/param_package.cpp
+++ b/src/common/param_package.cpp
@@ -1,6 +1,5 @@
1// Copyright 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <array> 4#include <array>
6#include <stdexcept> 5#include <stdexcept>
diff --git a/src/common/param_package.h b/src/common/param_package.h
index c13e45479..d7c13cb1f 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -1,6 +1,5 @@
1// Copyright 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/quaternion.h b/src/common/quaternion.h
index 4d0871eb4..5bb5f2af0 100644
--- a/src/common/quaternion.h
+++ b/src/common/quaternion.h
@@ -1,6 +1,5 @@
1// Copyright 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/reader_writer_queue.h b/src/common/reader_writer_queue.h
new file mode 100644
index 000000000..60c41a8cb
--- /dev/null
+++ b/src/common/reader_writer_queue.h
@@ -0,0 +1,940 @@
1// SPDX-FileCopyrightText: 2013-2020 Cameron Desrochers
2// SPDX-License-Identifier: BSD-2-Clause
3
4#pragma once
5
6#include <cassert>
7#include <cstdint>
8#include <cstdlib> // For malloc/free/abort & size_t
9#include <memory>
10#include <new>
11#include <stdexcept>
12#include <type_traits>
13#include <utility>
14
15#include "common/atomic_helpers.h"
16
17#if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012
18#include <chrono>
19#endif
20
21// A lock-free queue for a single-consumer, single-producer architecture.
22// The queue is also wait-free in the common path (except if more memory
23// needs to be allocated, in which case malloc is called).
24// Allocates memory sparingly, and only once if the original maximum size
25// estimate is never exceeded.
26// Tested on x86/x64 processors, but semantics should be correct for all
27// architectures (given the right implementations in atomicops.h), provided
28// that aligned integer and pointer accesses are naturally atomic.
29// Note that there should only be one consumer thread and producer thread;
30// Switching roles of the threads, or using multiple consecutive threads for
31// one role, is not safe unless properly synchronized.
32// Using the queue exclusively from one thread is fine, though a bit silly.
33
34#ifndef MOODYCAMEL_CACHE_LINE_SIZE
35#define MOODYCAMEL_CACHE_LINE_SIZE 64
36#endif
37
38#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
39#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || \
40 (!defined(_MSC_VER) && !defined(__GNUC__))
41#define MOODYCAMEL_EXCEPTIONS_ENABLED
42#endif
43#endif
44
45#ifndef MOODYCAMEL_HAS_EMPLACE
46#if !defined(_MSC_VER) || \
47 _MSC_VER >= 1800 // variadic templates: either a non-MS compiler or VS >= 2013
48#define MOODYCAMEL_HAS_EMPLACE 1
49#endif
50#endif
51
52#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
53#if defined(__APPLE__) && defined(__MACH__) && __cplusplus >= 201703L
54// This is required to find out what deployment target we are using
55#include <CoreFoundation/CoreFoundation.h>
56#if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || \
57 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14
58// C++17 new(size_t, align_val_t) is not backwards-compatible with older versions of macOS, so we
59// can't support over-alignment in this case
60#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
61#endif
62#endif
63#endif
64
65#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
66#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE AE_ALIGN(MOODYCAMEL_CACHE_LINE_SIZE)
67#endif
68
69#ifdef AE_VCPP
70#pragma warning(push)
71#pragma warning(disable : 4324) // structure was padded due to __declspec(align())
72#pragma warning(disable : 4820) // padding was added
73#pragma warning(disable : 4127) // conditional expression is constant
74#endif
75
76namespace Common {
77
78template <typename T, size_t MAX_BLOCK_SIZE = 512>
79class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue {
80 // Design: Based on a queue-of-queues. The low-level queues are just
81 // circular buffers with front and tail indices indicating where the
82 // next element to dequeue is and where the next element can be enqueued,
83 // respectively. Each low-level queue is called a "block". Each block
84 // wastes exactly one element's worth of space to keep the design simple
85 // (if front == tail then the queue is empty, and can't be full).
86 // The high-level queue is a circular linked list of blocks; again there
87 // is a front and tail, but this time they are pointers to the blocks.
88 // The front block is where the next element to be dequeued is, provided
89 // the block is not empty. The back block is where elements are to be
90 // enqueued, provided the block is not full.
91 // The producer thread owns all the tail indices/pointers. The consumer
92 // thread owns all the front indices/pointers. Both threads read each
93 // other's variables, but only the owning thread updates them. E.g. After
94 // the consumer reads the producer's tail, the tail may change before the
95 // consumer is done dequeuing an object, but the consumer knows the tail
96 // will never go backwards, only forwards.
97 // If there is no room to enqueue an object, an additional block (of
98 // equal size to the last block) is added. Blocks are never removed.
99
100public:
101 typedef T value_type;
102
103 // Constructs a queue that can hold at least `size` elements without further
104 // allocations. If more than MAX_BLOCK_SIZE elements are requested,
105 // then several blocks of MAX_BLOCK_SIZE each are reserved (including
106 // at least one extra buffer block).
107 AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15)
108#ifndef NDEBUG
109 : enqueuing(false), dequeuing(false)
110#endif
111 {
112 assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) &&
113 "MAX_BLOCK_SIZE must be a power of 2");
114 assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2");
115
116 Block* firstBlock = nullptr;
117
118 largestBlockSize =
119 ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block
120 if (largestBlockSize > MAX_BLOCK_SIZE * 2) {
121 // We need a spare block in case the producer is writing to a different block the
122 // consumer is reading from, and wants to enqueue the maximum number of elements. We
123 // also need a spare element in each block to avoid the ambiguity between front == tail
124 // meaning "empty" and "full". So the effective number of slots that are guaranteed to
125 // be usable at any time is the block size - 1 times the number of blocks - 1. Solving
126 // for size and applying a ceiling to the division gives us (after simplifying):
127 size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1);
128 largestBlockSize = MAX_BLOCK_SIZE;
129 Block* lastBlock = nullptr;
130 for (size_t i = 0; i != initialBlockCount; ++i) {
131 auto block = make_block(largestBlockSize);
132 if (block == nullptr) {
133#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
134 throw std::bad_alloc();
135#else
136 abort();
137#endif
138 }
139 if (firstBlock == nullptr) {
140 firstBlock = block;
141 } else {
142 lastBlock->next = block;
143 }
144 lastBlock = block;
145 block->next = firstBlock;
146 }
147 } else {
148 firstBlock = make_block(largestBlockSize);
149 if (firstBlock == nullptr) {
150#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
151 throw std::bad_alloc();
152#else
153 abort();
154#endif
155 }
156 firstBlock->next = firstBlock;
157 }
158 frontBlock = firstBlock;
159 tailBlock = firstBlock;
160
161 // Make sure the reader/writer threads will have the initialized memory setup above:
162 fence(memory_order_sync);
163 }
164
165 // Note: The queue should not be accessed concurrently while it's
166 // being moved. It's up to the user to synchronize this.
167 AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue&& other)
168 : frontBlock(other.frontBlock.load()), tailBlock(other.tailBlock.load()),
169 largestBlockSize(other.largestBlockSize)
170#ifndef NDEBUG
171 ,
172 enqueuing(false), dequeuing(false)
173#endif
174 {
175 other.largestBlockSize = 32;
176 Block* b = other.make_block(other.largestBlockSize);
177 if (b == nullptr) {
178#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
179 throw std::bad_alloc();
180#else
181 abort();
182#endif
183 }
184 b->next = b;
185 other.frontBlock = b;
186 other.tailBlock = b;
187 }
188
189 // Note: The queue should not be accessed concurrently while it's
190 // being moved. It's up to the user to synchronize this.
191 ReaderWriterQueue& operator=(ReaderWriterQueue&& other) AE_NO_TSAN {
192 Block* b = frontBlock.load();
193 frontBlock = other.frontBlock.load();
194 other.frontBlock = b;
195 b = tailBlock.load();
196 tailBlock = other.tailBlock.load();
197 other.tailBlock = b;
198 std::swap(largestBlockSize, other.largestBlockSize);
199 return *this;
200 }
201
202 // Note: The queue should not be accessed concurrently while it's
203 // being deleted. It's up to the user to synchronize this.
204 AE_NO_TSAN ~ReaderWriterQueue() {
205 // Make sure we get the latest version of all variables from other CPUs:
206 fence(memory_order_sync);
207
208 // Destroy any remaining objects in queue and free memory
209 Block* frontBlock_ = frontBlock;
210 Block* block = frontBlock_;
211 do {
212 Block* nextBlock = block->next;
213 size_t blockFront = block->front;
214 size_t blockTail = block->tail;
215
216 for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) {
217 auto element = reinterpret_cast<T*>(block->data + i * sizeof(T));
218 element->~T();
219 (void)element;
220 }
221
222 auto rawBlock = block->rawThis;
223 block->~Block();
224 std::free(rawBlock);
225 block = nextBlock;
226 } while (block != frontBlock_);
227 }
228
229 // Enqueues a copy of element if there is room in the queue.
230 // Returns true if the element was enqueued, false otherwise.
231 // Does not allocate memory.
232 AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN {
233 return inner_enqueue<CannotAlloc>(element);
234 }
235
236 // Enqueues a moved copy of element if there is room in the queue.
237 // Returns true if the element was enqueued, false otherwise.
238 // Does not allocate memory.
239 AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN {
240 return inner_enqueue<CannotAlloc>(std::forward<T>(element));
241 }
242
243#if MOODYCAMEL_HAS_EMPLACE
244 // Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
245 template <typename... Args>
246 AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN {
247 return inner_enqueue<CannotAlloc>(std::forward<Args>(args)...);
248 }
249#endif
250
251 // Enqueues a copy of element on the queue.
252 // Allocates an additional block of memory if needed.
253 // Only fails (returns false) if memory allocation fails.
254 AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN {
255 return inner_enqueue<CanAlloc>(element);
256 }
257
258 // Enqueues a moved copy of element on the queue.
259 // Allocates an additional block of memory if needed.
260 // Only fails (returns false) if memory allocation fails.
261 AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN {
262 return inner_enqueue<CanAlloc>(std::forward<T>(element));
263 }
264
265#if MOODYCAMEL_HAS_EMPLACE
266 // Like enqueue() but with emplace semantics (i.e. construct-in-place).
267 template <typename... Args>
268 AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN {
269 return inner_enqueue<CanAlloc>(std::forward<Args>(args)...);
270 }
271#endif
272
273 // Attempts to dequeue an element; if the queue is empty,
274 // returns false instead. If the queue has at least one element,
275 // moves front to result using operator=, then returns true.
276 template <typename U>
277 bool try_dequeue(U& result) AE_NO_TSAN {
278#ifndef NDEBUG
279 ReentrantGuard guard(this->dequeuing);
280#endif
281
282 // High-level pseudocode:
283 // Remember where the tail block is
284 // If the front block has an element in it, dequeue it
285 // Else
286 // If front block was the tail block when we entered the function, return false
287 // Else advance to next block and dequeue the item there
288
289 // Note that we have to use the value of the tail block from before we check if the front
290 // block is full or not, in case the front block is empty and then, before we check if the
291 // tail block is at the front block or not, the producer fills up the front block *and
292 // moves on*, which would make us skip a filled block. Seems unlikely, but was consistently
293 // reproducible in practice.
294 // In order to avoid overhead in the common case, though, we do a double-checked pattern
295 // where we have the fast path if the front block is not empty, then read the tail block,
296 // then re-read the front block and check if it's not empty again, then check if the tail
297 // block has advanced.
298
299 Block* frontBlock_ = frontBlock.load();
300 size_t blockTail = frontBlock_->localTail;
301 size_t blockFront = frontBlock_->front.load();
302
303 if (blockFront != blockTail ||
304 blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
305 fence(memory_order_acquire);
306
307 non_empty_front_block:
308 // Front block not empty, dequeue from here
309 auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
310 result = std::move(*element);
311 element->~T();
312
313 blockFront = (blockFront + 1) & frontBlock_->sizeMask;
314
315 fence(memory_order_release);
316 frontBlock_->front = blockFront;
317 } else if (frontBlock_ != tailBlock.load()) {
318 fence(memory_order_acquire);
319
320 frontBlock_ = frontBlock.load();
321 blockTail = frontBlock_->localTail = frontBlock_->tail.load();
322 blockFront = frontBlock_->front.load();
323 fence(memory_order_acquire);
324
325 if (blockFront != blockTail) {
326 // Oh look, the front block isn't empty after all
327 goto non_empty_front_block;
328 }
329
330 // Front block is empty but there's another block ahead, advance to it
331 Block* nextBlock = frontBlock_->next;
332 // Don't need an acquire fence here since next can only ever be set on the tailBlock,
333 // and we're not the tailBlock, and we did an acquire earlier after reading tailBlock
334 // which ensures next is up-to-date on this CPU in case we recently were at tailBlock.
335
336 size_t nextBlockFront = nextBlock->front.load();
337 size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
338 fence(memory_order_acquire);
339
340 // Since the tailBlock is only ever advanced after being written to,
341 // we know there's for sure an element to dequeue on it
342 assert(nextBlockFront != nextBlockTail);
343 AE_UNUSED(nextBlockTail);
344
345 // We're done with this block, let the producer use it if it needs
346 fence(memory_order_release); // Expose possibly pending changes to frontBlock->front
347 // from last dequeue
348 frontBlock = frontBlock_ = nextBlock;
349
350 compiler_fence(memory_order_release); // Not strictly needed
351
352 auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
353
354 result = std::move(*element);
355 element->~T();
356
357 nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
358
359 fence(memory_order_release);
360 frontBlock_->front = nextBlockFront;
361 } else {
362 // No elements in current block and no other block to advance to
363 return false;
364 }
365
366 return true;
367 }
368
369 // Returns a pointer to the front element in the queue (the one that
370 // would be removed next by a call to `try_dequeue` or `pop`). If the
371 // queue appears empty at the time the method is called, nullptr is
372 // returned instead.
373 // Must be called only from the consumer thread.
374 T* peek() const AE_NO_TSAN {
375#ifndef NDEBUG
376 ReentrantGuard guard(this->dequeuing);
377#endif
378 // See try_dequeue() for reasoning
379
380 Block* frontBlock_ = frontBlock.load();
381 size_t blockTail = frontBlock_->localTail;
382 size_t blockFront = frontBlock_->front.load();
383
384 if (blockFront != blockTail ||
385 blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
386 fence(memory_order_acquire);
387 non_empty_front_block:
388 return reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
389 } else if (frontBlock_ != tailBlock.load()) {
390 fence(memory_order_acquire);
391 frontBlock_ = frontBlock.load();
392 blockTail = frontBlock_->localTail = frontBlock_->tail.load();
393 blockFront = frontBlock_->front.load();
394 fence(memory_order_acquire);
395
396 if (blockFront != blockTail) {
397 goto non_empty_front_block;
398 }
399
400 Block* nextBlock = frontBlock_->next;
401
402 size_t nextBlockFront = nextBlock->front.load();
403 fence(memory_order_acquire);
404
405 assert(nextBlockFront != nextBlock->tail.load());
406 return reinterpret_cast<T*>(nextBlock->data + nextBlockFront * sizeof(T));
407 }
408
409 return nullptr;
410 }
411
412 // Removes the front element from the queue, if any, without returning it.
413 // Returns true on success, or false if the queue appeared empty at the time
414 // `pop` was called.
415 bool pop() AE_NO_TSAN {
416#ifndef NDEBUG
417 ReentrantGuard guard(this->dequeuing);
418#endif
419 // See try_dequeue() for reasoning
420
421 Block* frontBlock_ = frontBlock.load();
422 size_t blockTail = frontBlock_->localTail;
423 size_t blockFront = frontBlock_->front.load();
424
425 if (blockFront != blockTail ||
426 blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
427 fence(memory_order_acquire);
428
429 non_empty_front_block:
430 auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
431 element->~T();
432
433 blockFront = (blockFront + 1) & frontBlock_->sizeMask;
434
435 fence(memory_order_release);
436 frontBlock_->front = blockFront;
437 } else if (frontBlock_ != tailBlock.load()) {
438 fence(memory_order_acquire);
439 frontBlock_ = frontBlock.load();
440 blockTail = frontBlock_->localTail = frontBlock_->tail.load();
441 blockFront = frontBlock_->front.load();
442 fence(memory_order_acquire);
443
444 if (blockFront != blockTail) {
445 goto non_empty_front_block;
446 }
447
448 // Front block is empty but there's another block ahead, advance to it
449 Block* nextBlock = frontBlock_->next;
450
451 size_t nextBlockFront = nextBlock->front.load();
452 size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
453 fence(memory_order_acquire);
454
455 assert(nextBlockFront != nextBlockTail);
456 AE_UNUSED(nextBlockTail);
457
458 fence(memory_order_release);
459 frontBlock = frontBlock_ = nextBlock;
460
461 compiler_fence(memory_order_release);
462
463 auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
464 element->~T();
465
466 nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
467
468 fence(memory_order_release);
469 frontBlock_->front = nextBlockFront;
470 } else {
471 // No elements in current block and no other block to advance to
472 return false;
473 }
474
475 return true;
476 }
477
478 // Returns the approximate number of items currently in the queue.
479 // Safe to call from both the producer and consumer threads.
480 inline size_t size_approx() const AE_NO_TSAN {
481 size_t result = 0;
482 Block* frontBlock_ = frontBlock.load();
483 Block* block = frontBlock_;
484 do {
485 fence(memory_order_acquire);
486 size_t blockFront = block->front.load();
487 size_t blockTail = block->tail.load();
488 result += (blockTail - blockFront) & block->sizeMask;
489 block = block->next.load();
490 } while (block != frontBlock_);
491 return result;
492 }
493
494 // Returns the total number of items that could be enqueued without incurring
495 // an allocation when this queue is empty.
496 // Safe to call from both the producer and consumer threads.
497 //
498 // NOTE: The actual capacity during usage may be different depending on the consumer.
499 // If the consumer is removing elements concurrently, the producer cannot add to
500 // the block the consumer is removing from until it's completely empty, except in
501 // the case where the producer was writing to the same block the consumer was
502 // reading from the whole time.
503 inline size_t max_capacity() const {
504 size_t result = 0;
505 Block* frontBlock_ = frontBlock.load();
506 Block* block = frontBlock_;
507 do {
508 fence(memory_order_acquire);
509 result += block->sizeMask;
510 block = block->next.load();
511 } while (block != frontBlock_);
512 return result;
513 }
514
515private:
516 enum AllocationMode { CanAlloc, CannotAlloc };
517
518#if MOODYCAMEL_HAS_EMPLACE
519 template <AllocationMode canAlloc, typename... Args>
520 bool inner_enqueue(Args&&... args) AE_NO_TSAN
521#else
522 template <AllocationMode canAlloc, typename U>
523 bool inner_enqueue(U&& element) AE_NO_TSAN
524#endif
525 {
526#ifndef NDEBUG
527 ReentrantGuard guard(this->enqueuing);
528#endif
529
530 // High-level pseudocode (assuming we're allowed to alloc a new block):
531 // If room in tail block, add to tail
532 // Else check next block
533 // If next block is not the head block, enqueue on next block
534 // Else create a new block and enqueue there
535 // Advance tail to the block we just enqueued to
536
537 Block* tailBlock_ = tailBlock.load();
538 size_t blockFront = tailBlock_->localFront;
539 size_t blockTail = tailBlock_->tail.load();
540
541 size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask;
542 if (nextBlockTail != blockFront ||
543 nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) {
544 fence(memory_order_acquire);
545 // This block has room for at least one more element
546 char* location = tailBlock_->data + blockTail * sizeof(T);
547#if MOODYCAMEL_HAS_EMPLACE
548 new (location) T(std::forward<Args>(args)...);
549#else
550 new (location) T(std::forward<U>(element));
551#endif
552
553 fence(memory_order_release);
554 tailBlock_->tail = nextBlockTail;
555 } else {
556 fence(memory_order_acquire);
557 if (tailBlock_->next.load() != frontBlock) {
558 // Note that the reason we can't advance to the frontBlock and start adding new
559 // entries there is because if we did, then dequeue would stay in that block,
560 // eventually reading the new values, instead of advancing to the next full block
561 // (whose values were enqueued first and so should be consumed first).
562
563 fence(memory_order_acquire); // Ensure we get latest writes if we got the latest
564 // frontBlock
565
566 // tailBlock is full, but there's a free block ahead, use it
567 Block* tailBlockNext = tailBlock_->next.load();
568 size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load();
569 nextBlockTail = tailBlockNext->tail.load();
570 fence(memory_order_acquire);
571
572 // This block must be empty since it's not the head block and we
573 // go through the blocks in a circle
574 assert(nextBlockFront == nextBlockTail);
575 tailBlockNext->localFront = nextBlockFront;
576
577 char* location = tailBlockNext->data + nextBlockTail * sizeof(T);
578#if MOODYCAMEL_HAS_EMPLACE
579 new (location) T(std::forward<Args>(args)...);
580#else
581 new (location) T(std::forward<U>(element));
582#endif
583
584 tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask;
585
586 fence(memory_order_release);
587 tailBlock = tailBlockNext;
588 } else if (canAlloc == CanAlloc) {
589 // tailBlock is full and there's no free block ahead; create a new block
590 auto newBlockSize =
591 largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2;
592 auto newBlock = make_block(newBlockSize);
593 if (newBlock == nullptr) {
594 // Could not allocate a block!
595 return false;
596 }
597 largestBlockSize = newBlockSize;
598
599#if MOODYCAMEL_HAS_EMPLACE
600 new (newBlock->data) T(std::forward<Args>(args)...);
601#else
602 new (newBlock->data) T(std::forward<U>(element));
603#endif
604 assert(newBlock->front == 0);
605 newBlock->tail = newBlock->localTail = 1;
606
607 newBlock->next = tailBlock_->next.load();
608 tailBlock_->next = newBlock;
609
610 // Might be possible for the dequeue thread to see the new tailBlock->next
611 // *without* seeing the new tailBlock value, but this is OK since it can't
612 // advance to the next block until tailBlock is set anyway (because the only
613 // case where it could try to read the next is if it's already at the tailBlock,
614 // and it won't advance past tailBlock in any circumstance).
615
616 fence(memory_order_release);
617 tailBlock = newBlock;
618 } else if (canAlloc == CannotAlloc) {
619 // Would have had to allocate a new block to enqueue, but not allowed
620 return false;
621 } else {
622 assert(false && "Should be unreachable code");
623 return false;
624 }
625 }
626
627 return true;
628 }
629
630 // Disable copying
631 ReaderWriterQueue(ReaderWriterQueue const&) {}
632
633 // Disable assignment
634 ReaderWriterQueue& operator=(ReaderWriterQueue const&) {}
635
636 AE_FORCEINLINE static size_t ceilToPow2(size_t x) {
637 // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
638 --x;
639 x |= x >> 1;
640 x |= x >> 2;
641 x |= x >> 4;
642 for (size_t i = 1; i < sizeof(size_t); i <<= 1) {
643 x |= x >> (i << 3);
644 }
645 ++x;
646 return x;
647 }
648
649 template <typename U>
650 static AE_FORCEINLINE char* align_for(char* ptr) AE_NO_TSAN {
651 const std::size_t alignment = std::alignment_of<U>::value;
652 return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
653 }
654
655private:
656#ifndef NDEBUG
657 struct ReentrantGuard {
658 AE_NO_TSAN ReentrantGuard(weak_atomic<bool>& _inSection) : inSection(_inSection) {
659 assert(!inSection &&
660 "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one "
661 "thread at a time may hold the producer or consumer role)");
662 inSection = true;
663 }
664
665 AE_NO_TSAN ~ReentrantGuard() {
666 inSection = false;
667 }
668
669 private:
670 ReentrantGuard& operator=(ReentrantGuard const&);
671
672 private:
673 weak_atomic<bool>& inSection;
674 };
675#endif
676
677 struct Block {
678 // Avoid false-sharing by putting highly contended variables on their own cache lines
679 weak_atomic<size_t> front; // (Atomic) Elements are read from here
680 size_t localTail; // An uncontended shadow copy of tail, owned by the consumer
681
682 char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) -
683 sizeof(size_t)];
684 weak_atomic<size_t> tail; // (Atomic) Elements are enqueued here
685 size_t localFront;
686
687 char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) -
688 sizeof(size_t)]; // next isn't very contended, but we don't want it on
689 // the same cache line as tail (which is)
690 weak_atomic<Block*> next; // (Atomic)
691
692 char* data; // Contents (on heap) are aligned to T's alignment
693
694 const size_t sizeMask;
695
696 // size must be a power of two (and greater than 0)
697 AE_NO_TSAN Block(size_t const& _size, char* _rawThis, char* _data)
698 : front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data),
699 sizeMask(_size - 1), rawThis(_rawThis) {}
700
701 private:
702 // C4512 - Assignment operator could not be generated
703 Block& operator=(Block const&);
704
705 public:
706 char* rawThis;
707 };
708
709 static Block* make_block(size_t capacity) AE_NO_TSAN {
710 // Allocate enough memory for the block itself, as well as all the elements it will contain
711 auto size = sizeof(Block) + std::alignment_of<Block>::value - 1;
712 size += sizeof(T) * capacity + std::alignment_of<T>::value - 1;
713 auto newBlockRaw = static_cast<char*>(std::malloc(size));
714 if (newBlockRaw == nullptr) {
715 return nullptr;
716 }
717
718 auto newBlockAligned = align_for<Block>(newBlockRaw);
719 auto newBlockData = align_for<T>(newBlockAligned + sizeof(Block));
720 return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData);
721 }
722
723private:
724 weak_atomic<Block*> frontBlock; // (Atomic) Elements are dequeued from this block
725
726 char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<Block*>)];
727 weak_atomic<Block*> tailBlock; // (Atomic) Elements are enqueued to this block
728
729 size_t largestBlockSize;
730
731#ifndef NDEBUG
732 weak_atomic<bool> enqueuing;
733 mutable weak_atomic<bool> dequeuing;
734#endif
735};
736
737// Like ReaderWriterQueue, but also providees blocking operations
738template <typename T, size_t MAX_BLOCK_SIZE = 512>
739class BlockingReaderWriterQueue {
740private:
741 typedef ::Common::ReaderWriterQueue<T, MAX_BLOCK_SIZE> ReaderWriterQueue;
742
743public:
744 explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN
745 : inner(size),
746 sema(new spsc_sema::LightweightSemaphore()) {}
747
748 BlockingReaderWriterQueue(BlockingReaderWriterQueue&& other) AE_NO_TSAN
749 : inner(std::move(other.inner)),
750 sema(std::move(other.sema)) {}
751
752 BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue&& other) AE_NO_TSAN {
753 std::swap(sema, other.sema);
754 std::swap(inner, other.inner);
755 return *this;
756 }
757
758 // Enqueues a copy of element if there is room in the queue.
759 // Returns true if the element was enqueued, false otherwise.
760 // Does not allocate memory.
761 AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN {
762 if (inner.try_enqueue(element)) {
763 sema->signal();
764 return true;
765 }
766 return false;
767 }
768
769 // Enqueues a moved copy of element if there is room in the queue.
770 // Returns true if the element was enqueued, false otherwise.
771 // Does not allocate memory.
772 AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN {
773 if (inner.try_enqueue(std::forward<T>(element))) {
774 sema->signal();
775 return true;
776 }
777 return false;
778 }
779
780#if MOODYCAMEL_HAS_EMPLACE
781 // Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
782 template <typename... Args>
783 AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN {
784 if (inner.try_emplace(std::forward<Args>(args)...)) {
785 sema->signal();
786 return true;
787 }
788 return false;
789 }
790#endif
791
792 // Enqueues a copy of element on the queue.
793 // Allocates an additional block of memory if needed.
794 // Only fails (returns false) if memory allocation fails.
795 AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN {
796 if (inner.enqueue(element)) {
797 sema->signal();
798 return true;
799 }
800 return false;
801 }
802
803 // Enqueues a moved copy of element on the queue.
804 // Allocates an additional block of memory if needed.
805 // Only fails (returns false) if memory allocation fails.
806 AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN {
807 if (inner.enqueue(std::forward<T>(element))) {
808 sema->signal();
809 return true;
810 }
811 return false;
812 }
813
814#if MOODYCAMEL_HAS_EMPLACE
815 // Like enqueue() but with emplace semantics (i.e. construct-in-place).
816 template <typename... Args>
817 AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN {
818 if (inner.emplace(std::forward<Args>(args)...)) {
819 sema->signal();
820 return true;
821 }
822 return false;
823 }
824#endif
825
826 // Attempts to dequeue an element; if the queue is empty,
827 // returns false instead. If the queue has at least one element,
828 // moves front to result using operator=, then returns true.
829 template <typename U>
830 bool try_dequeue(U& result) AE_NO_TSAN {
831 if (sema->tryWait()) {
832 bool success = inner.try_dequeue(result);
833 assert(success);
834 AE_UNUSED(success);
835 return true;
836 }
837 return false;
838 }
839
840 // Attempts to dequeue an element; if the queue is empty,
841 // waits until an element is available, then dequeues it.
842 template <typename U>
843 void wait_dequeue(U& result) AE_NO_TSAN {
844 while (!sema->wait())
845 ;
846 bool success = inner.try_dequeue(result);
847 AE_UNUSED(result);
848 assert(success);
849 AE_UNUSED(success);
850 }
851
852 // Attempts to dequeue an element; if the queue is empty,
853 // waits until an element is available up to the specified timeout,
854 // then dequeues it and returns true, or returns false if the timeout
855 // expires before an element can be dequeued.
856 // Using a negative timeout indicates an indefinite timeout,
857 // and is thus functionally equivalent to calling wait_dequeue.
858 template <typename U>
859 bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs) AE_NO_TSAN {
860 if (!sema->wait(timeout_usecs)) {
861 return false;
862 }
863 bool success = inner.try_dequeue(result);
864 AE_UNUSED(result);
865 assert(success);
866 AE_UNUSED(success);
867 return true;
868 }
869
870#if __cplusplus > 199711L || _MSC_VER >= 1700
871 // Attempts to dequeue an element; if the queue is empty,
872 // waits until an element is available up to the specified timeout,
873 // then dequeues it and returns true, or returns false if the timeout
874 // expires before an element can be dequeued.
875 // Using a negative timeout indicates an indefinite timeout,
876 // and is thus functionally equivalent to calling wait_dequeue.
877 template <typename U, typename Rep, typename Period>
878 inline bool wait_dequeue_timed(U& result,
879 std::chrono::duration<Rep, Period> const& timeout) AE_NO_TSAN {
880 return wait_dequeue_timed(
881 result, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
882 }
883#endif
884
885 // Returns a pointer to the front element in the queue (the one that
886 // would be removed next by a call to `try_dequeue` or `pop`). If the
887 // queue appears empty at the time the method is called, nullptr is
888 // returned instead.
889 // Must be called only from the consumer thread.
890 AE_FORCEINLINE T* peek() const AE_NO_TSAN {
891 return inner.peek();
892 }
893
894 // Removes the front element from the queue, if any, without returning it.
895 // Returns true on success, or false if the queue appeared empty at the time
896 // `pop` was called.
897 AE_FORCEINLINE bool pop() AE_NO_TSAN {
898 if (sema->tryWait()) {
899 bool result = inner.pop();
900 assert(result);
901 AE_UNUSED(result);
902 return true;
903 }
904 return false;
905 }
906
907 // Returns the approximate number of items currently in the queue.
908 // Safe to call from both the producer and consumer threads.
909 AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN {
910 return sema->availableApprox();
911 }
912
913 // Returns the total number of items that could be enqueued without incurring
914 // an allocation when this queue is empty.
915 // Safe to call from both the producer and consumer threads.
916 //
917 // NOTE: The actual capacity during usage may be different depending on the consumer.
918 // If the consumer is removing elements concurrently, the producer cannot add to
919 // the block the consumer is removing from until it's completely empty, except in
920 // the case where the producer was writing to the same block the consumer was
921 // reading from the whole time.
922 AE_FORCEINLINE size_t max_capacity() const {
923 return inner.max_capacity();
924 }
925
926private:
927 // Disable copying & assignment
928 BlockingReaderWriterQueue(BlockingReaderWriterQueue const&) {}
929 BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue const&) {}
930
931private:
932 ReaderWriterQueue inner;
933 std::unique_ptr<spsc_sema::LightweightSemaphore> sema;
934};
935
936} // namespace Common
937
938#ifdef AE_VCPP
939#pragma warning(pop)
940#endif
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in
index cc88994c6..f0c124d69 100644
--- a/src/common/scm_rev.cpp.in
+++ b/src/common/scm_rev.cpp.in
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include "common/scm_rev.h" 4#include "common/scm_rev.h"
6 5
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h
index 563015ec9..88404316a 100644
--- a/src/common/scm_rev.h
+++ b/src/common/scm_rev.h
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
index 35dac3a8f..e9c789c88 100644
--- a/src/common/scope_exit.h
+++ b/src/common/scope_exit.h
@@ -1,6 +1,5 @@
1// Copyright 2014 Citra Emulator Project 1// SPDX-FileCopyrightText: 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 751549583..7282a45d3 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -62,7 +62,8 @@ void LogSettings() {
62 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); 62 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
63 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); 63 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
64 log_setting("Audio_OutputEngine", values.sink_id.GetValue()); 64 log_setting("Audio_OutputEngine", values.sink_id.GetValue());
65 log_setting("Audio_OutputDevice", values.audio_device_id.GetValue()); 65 log_setting("Audio_OutputDevice", values.audio_output_device_id.GetValue());
66 log_setting("Audio_InputDevice", values.audio_input_device_id.GetValue());
66 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue()); 67 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue());
67 log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir)); 68 log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir));
68 log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir)); 69 log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir));
@@ -104,7 +105,7 @@ float Volume() {
104 if (values.audio_muted) { 105 if (values.audio_muted) {
105 return 0.0f; 106 return 0.0f;
106 } 107 }
107 return values.volume.GetValue() / 100.0f; 108 return values.volume.GetValue() / static_cast<f32>(values.volume.GetDefault());
108} 109}
109 110
110void UpdateRescalingInfo() { 111void UpdateRescalingInfo() {
@@ -185,7 +186,6 @@ void RestoreGlobalState(bool is_powered_on) {
185 values.max_anisotropy.SetGlobal(true); 186 values.max_anisotropy.SetGlobal(true);
186 values.use_speed_limit.SetGlobal(true); 187 values.use_speed_limit.SetGlobal(true);
187 values.speed_limit.SetGlobal(true); 188 values.speed_limit.SetGlobal(true);
188 values.fps_cap.SetGlobal(true);
189 values.use_disk_shader_cache.SetGlobal(true); 189 values.use_disk_shader_cache.SetGlobal(true);
190 values.gpu_accuracy.SetGlobal(true); 190 values.gpu_accuracy.SetGlobal(true);
191 values.use_asynchronous_gpu_emulation.SetGlobal(true); 191 values.use_asynchronous_gpu_emulation.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 3583a2e70..14ed9b237 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -106,7 +106,7 @@ struct ResolutionScalingInfo {
106 * configurations. Specifying a default value and label is required. A minimum and maximum range can 106 * configurations. Specifying a default value and label is required. A minimum and maximum range can
107 * be specified for sanitization. 107 * be specified for sanitization.
108 */ 108 */
109template <typename Type> 109template <typename Type, bool ranged = false>
110class Setting { 110class Setting {
111protected: 111protected:
112 Setting() = default; 112 Setting() = default;
@@ -126,8 +126,8 @@ public:
126 * @param default_val Intial value of the setting, and default value of the setting 126 * @param default_val Intial value of the setting, and default value of the setting
127 * @param name Label for the setting 127 * @param name Label for the setting
128 */ 128 */
129 explicit Setting(const Type& default_val, const std::string& name) 129 explicit Setting(const Type& default_val, const std::string& name) requires(!ranged)
130 : value{default_val}, default_value{default_val}, ranged{false}, label{name} {} 130 : value{default_val}, default_value{default_val}, label{name} {}
131 virtual ~Setting() = default; 131 virtual ~Setting() = default;
132 132
133 /** 133 /**
@@ -139,9 +139,9 @@ public:
139 * @param name Label for the setting 139 * @param name Label for the setting
140 */ 140 */
141 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, 141 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
142 const std::string& name) 142 const std::string& name) requires(ranged)
143 : value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val}, 143 : value{default_val},
144 ranged{true}, label{name} {} 144 default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
145 145
146 /** 146 /**
147 * Returns a reference to the setting's value. 147 * Returns a reference to the setting's value.
@@ -158,7 +158,7 @@ public:
158 * @param val The desired value 158 * @param val The desired value
159 */ 159 */
160 virtual void SetValue(const Type& val) { 160 virtual void SetValue(const Type& val) {
161 Type temp{(ranged) ? std::clamp(val, minimum, maximum) : val}; 161 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
162 std::swap(value, temp); 162 std::swap(value, temp);
163 } 163 }
164 164
@@ -188,7 +188,7 @@ public:
188 * @returns A reference to the setting 188 * @returns A reference to the setting
189 */ 189 */
190 virtual const Type& operator=(const Type& val) { 190 virtual const Type& operator=(const Type& val) {
191 Type temp{(ranged) ? std::clamp(val, minimum, maximum) : val}; 191 Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
192 std::swap(value, temp); 192 std::swap(value, temp);
193 return value; 193 return value;
194 } 194 }
@@ -207,7 +207,6 @@ protected:
207 const Type default_value{}; ///< The default value 207 const Type default_value{}; ///< The default value
208 const Type maximum{}; ///< Maximum allowed value of the setting 208 const Type maximum{}; ///< Maximum allowed value of the setting
209 const Type minimum{}; ///< Minimum allowed value of the setting 209 const Type minimum{}; ///< Minimum allowed value of the setting
210 const bool ranged; ///< The setting has sanitization ranges
211 const std::string label{}; ///< The setting's label 210 const std::string label{}; ///< The setting's label
212}; 211};
213 212
@@ -219,8 +218,8 @@ protected:
219 * 218 *
220 * By default, the global setting is used. 219 * By default, the global setting is used.
221 */ 220 */
222template <typename Type> 221template <typename Type, bool ranged = false>
223class SwitchableSetting : virtual public Setting<Type> { 222class SwitchableSetting : virtual public Setting<Type, ranged> {
224public: 223public:
225 /** 224 /**
226 * Sets a default value, label, and setting value. 225 * Sets a default value, label, and setting value.
@@ -228,7 +227,7 @@ public:
228 * @param default_val Intial value of the setting, and default value of the setting 227 * @param default_val Intial value of the setting, and default value of the setting
229 * @param name Label for the setting 228 * @param name Label for the setting
230 */ 229 */
231 explicit SwitchableSetting(const Type& default_val, const std::string& name) 230 explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged)
232 : Setting<Type>{default_val, name} {} 231 : Setting<Type>{default_val, name} {}
233 virtual ~SwitchableSetting() = default; 232 virtual ~SwitchableSetting() = default;
234 233
@@ -241,8 +240,8 @@ public:
241 * @param name Label for the setting 240 * @param name Label for the setting
242 */ 241 */
243 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val, 242 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
244 const std::string& name) 243 const std::string& name) requires(ranged)
245 : Setting<Type>{default_val, min_val, max_val, name} {} 244 : Setting<Type, true>{default_val, min_val, max_val, name} {}
246 245
247 /** 246 /**
248 * Tells this setting to represent either the global or custom setting when other member 247 * Tells this setting to represent either the global or custom setting when other member
@@ -290,7 +289,7 @@ public:
290 * @param val The new value 289 * @param val The new value
291 */ 290 */
292 void SetValue(const Type& val) override { 291 void SetValue(const Type& val) override {
293 Type temp{(this->ranged) ? std::clamp(val, this->minimum, this->maximum) : val}; 292 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
294 if (use_global) { 293 if (use_global) {
295 std::swap(this->value, temp); 294 std::swap(this->value, temp);
296 } else { 295 } else {
@@ -306,7 +305,7 @@ public:
306 * @returns A reference to the current setting value 305 * @returns A reference to the current setting value
307 */ 306 */
308 const Type& operator=(const Type& val) override { 307 const Type& operator=(const Type& val) override {
309 Type temp{(this->ranged) ? std::clamp(val, this->minimum, this->maximum) : val}; 308 Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
310 if (use_global) { 309 if (use_global) {
311 std::swap(this->value, temp); 310 std::swap(this->value, temp);
312 return this->value; 311 return this->value;
@@ -371,18 +370,20 @@ struct TouchFromButtonMap {
371 370
372struct Values { 371struct Values {
373 // Audio 372 // Audio
374 Setting<std::string> audio_device_id{"auto", "output_device"};
375 Setting<std::string> sink_id{"auto", "output_engine"}; 373 Setting<std::string> sink_id{"auto", "output_engine"};
374 Setting<std::string> audio_output_device_id{"auto", "output_device"};
375 Setting<std::string> audio_input_device_id{"auto", "input_device"};
376 Setting<bool> audio_muted{false, "audio_muted"}; 376 Setting<bool> audio_muted{false, "audio_muted"};
377 SwitchableSetting<u8> volume{100, 0, 100, "volume"}; 377 SwitchableSetting<u8, true> volume{100, 0, 200, "volume"};
378 Setting<bool> dump_audio_commands{false, "dump_audio_commands"};
378 379
379 // Core 380 // Core
380 SwitchableSetting<bool> use_multi_core{true, "use_multi_core"}; 381 SwitchableSetting<bool> use_multi_core{true, "use_multi_core"};
381 SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"}; 382 SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
382 383
383 // Cpu 384 // Cpu
384 SwitchableSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, 385 SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
385 CPUAccuracy::Paranoid, "cpu_accuracy"}; 386 CPUAccuracy::Paranoid, "cpu_accuracy"};
386 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 387 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
387 Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; 388 Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
388 Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; 389 Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
@@ -409,7 +410,7 @@ struct Values {
409 true, "cpuopt_unsafe_ignore_global_monitor"}; 410 true, "cpuopt_unsafe_ignore_global_monitor"};
410 411
411 // Renderer 412 // Renderer
412 SwitchableSetting<RendererBackend> renderer_backend{ 413 SwitchableSetting<RendererBackend, true> renderer_backend{
413 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; 414 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
414 Setting<bool> renderer_debug{false, "debug"}; 415 Setting<bool> renderer_debug{false, "debug"};
415 Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; 416 Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
@@ -423,28 +424,26 @@ struct Values {
423 SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"}; 424 SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
424 // *nix platforms may have issues with the borderless windowed fullscreen mode. 425 // *nix platforms may have issues with the borderless windowed fullscreen mode.
425 // Default to exclusive fullscreen on these platforms for now. 426 // Default to exclusive fullscreen on these platforms for now.
426 SwitchableSetting<FullscreenMode> fullscreen_mode{ 427 SwitchableSetting<FullscreenMode, true> fullscreen_mode{
427#ifdef _WIN32 428#ifdef _WIN32
428 FullscreenMode::Borderless, 429 FullscreenMode::Borderless,
429#else 430#else
430 FullscreenMode::Exclusive, 431 FullscreenMode::Exclusive,
431#endif 432#endif
432 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"}; 433 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
433 SwitchableSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"}; 434 SwitchableSetting<int, true> aspect_ratio{0, 0, 3, "aspect_ratio"};
434 SwitchableSetting<int> max_anisotropy{0, 0, 5, "max_anisotropy"}; 435 SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"};
435 SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"}; 436 SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"};
436 SwitchableSetting<u16> speed_limit{100, 0, 9999, "speed_limit"}; 437 SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"};
437 SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; 438 SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
438 SwitchableSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, 439 SwitchableSetting<GPUAccuracy, true> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
439 GPUAccuracy::Extreme, "gpu_accuracy"}; 440 GPUAccuracy::Extreme, "gpu_accuracy"};
440 SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; 441 SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
441 SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; 442 SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
442 SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"}; 443 SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
443 SwitchableSetting<bool> use_vsync{true, "use_vsync"}; 444 SwitchableSetting<bool> use_vsync{true, "use_vsync"};
444 SwitchableSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; 445 SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
445 Setting<bool> disable_fps_limit{false, "disable_fps_limit"}; 446 ShaderBackend::SPIRV, "shader_backend"};
446 SwitchableSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
447 ShaderBackend::SPIRV, "shader_backend"};
448 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 447 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
449 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 448 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
450 449
@@ -460,10 +459,10 @@ struct Values {
460 s64 custom_rtc_differential; 459 s64 custom_rtc_differential;
461 460
462 Setting<s32> current_user{0, "current_user"}; 461 Setting<s32> current_user{0, "current_user"};
463 SwitchableSetting<s32> language_index{1, 0, 17, "language_index"}; 462 SwitchableSetting<s32, true> language_index{1, 0, 17, "language_index"};
464 SwitchableSetting<s32> region_index{1, 0, 6, "region_index"}; 463 SwitchableSetting<s32, true> region_index{1, 0, 6, "region_index"};
465 SwitchableSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; 464 SwitchableSetting<s32, true> time_zone_index{0, 0, 45, "time_zone_index"};
466 SwitchableSetting<s32> sound_index{1, 0, 2, "sound_index"}; 465 SwitchableSetting<s32, true> sound_index{1, 0, 2, "sound_index"};
467 466
468 // Controls 467 // Controls
469 InputSetting<std::array<PlayerInput, 10>> players; 468 InputSetting<std::array<PlayerInput, 10>> players;
@@ -485,7 +484,7 @@ struct Values {
485 Setting<bool> tas_loop{false, "tas_loop"}; 484 Setting<bool> tas_loop{false, "tas_loop"};
486 485
487 Setting<bool> mouse_panning{false, "mouse_panning"}; 486 Setting<bool> mouse_panning{false, "mouse_panning"};
488 Setting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; 487 Setting<u8, true> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
489 Setting<bool> mouse_enabled{false, "mouse_enabled"}; 488 Setting<bool> mouse_enabled{false, "mouse_enabled"};
490 489
491 Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; 490 Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
@@ -504,6 +503,9 @@ struct Values {
504 Setting<bool> enable_ring_controller{true, "enable_ring_controller"}; 503 Setting<bool> enable_ring_controller{true, "enable_ring_controller"};
505 RingconRaw ringcon_analogs; 504 RingconRaw ringcon_analogs;
506 505
506 Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"};
507 Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"};
508
507 // Data Storage 509 // Data Storage
508 Setting<bool> use_virtual_sd{true, "use_virtual_sd"}; 510 Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
509 Setting<bool> gamecard_inserted{false, "gamecard_inserted"}; 511 Setting<bool> gamecard_inserted{false, "gamecard_inserted"};
diff --git a/src/common/socket_types.h b/src/common/socket_types.h
new file mode 100644
index 000000000..0a801a443
--- /dev/null
+++ b/src/common/socket_types.h
@@ -0,0 +1,51 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Network {
9
10/// Address families
11enum class Domain : u8 {
12 INET, ///< Address family for IPv4
13};
14
15/// Socket types
16enum class Type {
17 STREAM,
18 DGRAM,
19 RAW,
20 SEQPACKET,
21};
22
23/// Protocol values for sockets
24enum class Protocol : u8 {
25 ICMP,
26 TCP,
27 UDP,
28};
29
30/// Shutdown mode
31enum class ShutdownHow {
32 RD,
33 WR,
34 RDWR,
35};
36
37/// Array of IPv4 address
38using IPv4Address = std::array<u8, 4>;
39
40/// Cross-platform sockaddr structure
41struct SockAddrIn {
42 Domain family;
43 IPv4Address ip;
44 u16 portno;
45};
46
47constexpr u32 FLAG_MSG_PEEK = 0x2;
48constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
49constexpr u32 FLAG_O_NONBLOCK = 0x800;
50
51} // namespace Network
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 67261c55b..d26394359 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -1,6 +1,5 @@
1// Copyright 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6#include <cstring> 5#include <cstring>
diff --git a/src/common/telemetry.h b/src/common/telemetry.h
index f9a824a7d..ba633d5a5 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -1,6 +1,5 @@
1// Copyright 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index f7ae9d8c2..053798e79 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -39,7 +39,7 @@ public:
39 template <typename Arg> 39 template <typename Arg>
40 void Push(Arg&& t) { 40 void Push(Arg&& t) {
41 // create the element, add it to the queue 41 // create the element, add it to the queue
42 write_ptr->current = std::forward<Arg>(t); 42 write_ptr->current = std::move(t);
43 // set the next pointer to a new element ptr 43 // set the next pointer to a new element ptr
44 // then advance the write pointer 44 // then advance the write pointer
45 ElementPtr* new_ptr = new ElementPtr(); 45 ElementPtr* new_ptr = new ElementPtr();
diff --git a/src/common/uint128.h b/src/common/uint128.h
index 199d0f55e..f450a6db9 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -12,7 +12,6 @@
12#pragma intrinsic(_udiv128) 12#pragma intrinsic(_udiv128)
13#else 13#else
14#include <cstring> 14#include <cstring>
15#include <x86intrin.h>
16#endif 15#endif
17 16
18#include "common/common_types.h" 17#include "common/common_types.h"
@@ -31,17 +30,12 @@ namespace Common {
31 return _udiv128(r[1], r[0], d, &remainder); 30 return _udiv128(r[1], r[0], d, &remainder);
32#endif 31#endif
33#else 32#else
34#ifdef __SIZEOF_INT128__
35 const auto product = static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b);
36 return static_cast<u64>(product / d);
37#else
38 const u64 diva = a / d; 33 const u64 diva = a / d;
39 const u64 moda = a % d; 34 const u64 moda = a % d;
40 const u64 divb = b / d; 35 const u64 divb = b / d;
41 const u64 modb = b % d; 36 const u64 modb = b % d;
42 return diva * b + moda * divb + moda * modb / d; 37 return diva * b + moda * divb + moda * modb / d;
43#endif 38#endif
44#endif
45} 39}
46 40
47// This function multiplies 2 u64 values and produces a u128 value; 41// This function multiplies 2 u64 values and produces a u128 value;
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index b4fb3a59f..ae07f2811 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -67,7 +67,7 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
67 const auto& caps = GetCPUCaps(); 67 const auto& caps = GetCPUCaps();
68 u64 rtsc_frequency = 0; 68 u64 rtsc_frequency = 0;
69 if (caps.invariant_tsc) { 69 if (caps.invariant_tsc) {
70 rtsc_frequency = EstimateRDTSCFrequency(); 70 rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency();
71 } 71 }
72 72
73 // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: 73 // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than:
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index 322aa1f08..1a27532d4 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -161,6 +161,22 @@ static CPUCaps Detect() {
161 caps.invariant_tsc = Common::Bit<8>(cpu_id[3]); 161 caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
162 } 162 }
163 163
164 if (max_std_fn >= 0x15) {
165 __cpuid(cpu_id, 0x15);
166 caps.tsc_crystal_ratio_denominator = cpu_id[0];
167 caps.tsc_crystal_ratio_numerator = cpu_id[1];
168 caps.crystal_frequency = cpu_id[2];
169 // Some CPU models might not return a crystal frequency.
170 // The CPU model can be detected to use the values from turbostat
171 // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569
172 // but it's easier to just estimate the TSC tick rate for these cases.
173 if (caps.tsc_crystal_ratio_denominator) {
174 caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
175 caps.tsc_crystal_ratio_numerator /
176 caps.tsc_crystal_ratio_denominator;
177 }
178 }
179
164 if (max_std_fn >= 0x16) { 180 if (max_std_fn >= 0x16) {
165 __cpuid(cpu_id, 0x16); 181 __cpuid(cpu_id, 0x16);
166 caps.base_frequency = cpu_id[0]; 182 caps.base_frequency = cpu_id[0];
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index 9bdc9dbfa..6830f3795 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -30,6 +30,11 @@ struct CPUCaps {
30 u32 max_frequency; 30 u32 max_frequency;
31 u32 bus_frequency; 31 u32 bus_frequency;
32 32
33 u32 tsc_crystal_ratio_denominator;
34 u32 tsc_crystal_ratio_numerator;
35 u32 crystal_frequency;
36 u64 tsc_frequency; // Derived from the above three values
37
33 bool sse : 1; 38 bool sse : 1;
34 bool sse2 : 1; 39 bool sse2 : 1;
35 bool sse3 : 1; 40 bool sse3 : 1;
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 6aaa8cdf9..8b08332ab 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -75,8 +75,8 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
75} 75}
76 76
77u64 NativeClock::GetRTSC() { 77u64 NativeClock::GetRTSC() {
78 TimePoint current_time_point{};
79 TimePoint new_time_point{}; 78 TimePoint new_time_point{};
79 TimePoint current_time_point{};
80 80
81 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); 81 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
82 do { 82 do {
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
index 87b3d63a4..67e6e63c8 100644
--- a/src/common/x64/xbyak_abi.h
+++ b/src/common/x64/xbyak_abi.h
@@ -1,6 +1,5 @@
1// Copyright 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
index 44d2558f1..250e5cddb 100644
--- a/src/common/x64/xbyak_util.h
+++ b/src/common/x64/xbyak_util.h
@@ -1,6 +1,5 @@
1// Copyright 2016 Citra Emulator Project 1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// SPDX-License-Identifier: GPL-2.0-or-later
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5