summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/assert.cpp7
-rw-r--r--src/common/assert.h55
-rw-r--r--src/common/bounded_threadsafe_queue.h159
-rw-r--r--src/common/detached_tasks.cpp4
-rw-r--r--src/common/page_table.h3
-rw-r--r--src/common/param_package.cpp6
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/thread.cpp12
-rw-r--r--src/common/thread.h1
-rw-r--r--src/common/uint128.h5
-rw-r--r--src/common/x64/native_clock.cpp5
-rw-r--r--src/common/x64/native_clock.h6
12 files changed, 132 insertions, 133 deletions
diff --git a/src/common/assert.cpp b/src/common/assert.cpp
index b44570528..6026b7dc2 100644
--- a/src/common/assert.cpp
+++ b/src/common/assert.cpp
@@ -6,8 +6,13 @@
6 6
7#include "common/settings.h" 7#include "common/settings.h"
8 8
9void assert_handle_failure() { 9void assert_fail_impl() {
10 if (Settings::values.use_debug_asserts) { 10 if (Settings::values.use_debug_asserts) {
11 Crash(); 11 Crash();
12 } 12 }
13} 13}
14
15[[noreturn]] void unreachable_impl() {
16 Crash();
17 throw std::runtime_error("Unreachable code");
18}
diff --git a/src/common/assert.h b/src/common/assert.h
index dbfd8abaf..8c927fcc0 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -9,44 +9,43 @@
9// Sometimes we want to try to continue even after hitting an assert. 9// Sometimes we want to try to continue even after hitting an assert.
10// However touching this file yields a global recompilation as this header is included almost 10// However touching this file yields a global recompilation as this header is included almost
11// everywhere. So let's just move the handling of the failed assert to a single cpp file. 11// everywhere. So let's just move the handling of the failed assert to a single cpp file.
12void assert_handle_failure();
13 12
14// For asserts we'd like to keep all the junk executed when an assert happens away from the 13void assert_fail_impl();
15// important code in the function. One way of doing this is to put all the relevant code inside a 14[[noreturn]] void unreachable_impl();
16// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to 15
17// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper 16#ifdef _MSC_VER
18// template that calls the lambda. This seems to generate an extra instruction at the call-site 17#define YUZU_NO_INLINE __declspec(noinline)
19// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good 18#else
20// enough for our purposes. 19#define YUZU_NO_INLINE __attribute__((noinline))
21template <typename Fn>
22#if defined(_MSC_VER)
23[[msvc::noinline]]
24#elif defined(__GNUC__)
25[[gnu::cold, gnu::noinline]]
26#endif 20#endif
27static void
28assert_noinline_call(const Fn& fn) {
29 fn();
30 assert_handle_failure();
31}
32 21
33#define ASSERT(_a_) \ 22#define ASSERT(_a_) \
34 do \ 23 ([&]() YUZU_NO_INLINE { \
35 if (!(_a_)) { \ 24 if (!(_a_)) [[unlikely]] { \
36 assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \ 25 LOG_CRITICAL(Debug, "Assertion Failed!"); \
26 assert_fail_impl(); \
37 } \ 27 } \
38 while (0) 28 }())
39 29
40#define ASSERT_MSG(_a_, ...) \ 30#define ASSERT_MSG(_a_, ...) \
41 do \ 31 ([&]() YUZU_NO_INLINE { \
42 if (!(_a_)) { \ 32 if (!(_a_)) [[unlikely]] { \
43 assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \ 33 LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
34 assert_fail_impl(); \
44 } \ 35 } \
45 while (0) 36 }())
37
38#define UNREACHABLE() \
39 do { \
40 LOG_CRITICAL(Debug, "Unreachable code!"); \
41 unreachable_impl(); \
42 } while (0)
46 43
47#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
48#define UNREACHABLE_MSG(...) \ 44#define UNREACHABLE_MSG(...) \
49 assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); }) 45 do { \
46 LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); \
47 unreachable_impl(); \
48 } while (0)
50 49
51#ifdef _DEBUG 50#ifdef _DEBUG
52#define DEBUG_ASSERT(_a_) ASSERT(_a_) 51#define DEBUG_ASSERT(_a_) ASSERT(_a_)
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h
index e83064c7f..7e465549b 100644
--- a/src/common/bounded_threadsafe_queue.h
+++ b/src/common/bounded_threadsafe_queue.h
@@ -1,10 +1,7 @@
1// SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se> 1// SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
2// SPDX-License-Identifier: MIT 2// SPDX-License-Identifier: MIT
3
3#pragma once 4#pragma once
4#ifdef _MSC_VER
5#pragma warning(push)
6#pragma warning(disable : 4324)
7#endif
8 5
9#include <atomic> 6#include <atomic>
10#include <bit> 7#include <bit>
@@ -12,105 +9,63 @@
12#include <memory> 9#include <memory>
13#include <mutex> 10#include <mutex>
14#include <new> 11#include <new>
15#include <stdexcept>
16#include <stop_token> 12#include <stop_token>
17#include <type_traits> 13#include <type_traits>
18#include <utility> 14#include <utility>
19 15
20namespace Common { 16namespace Common {
21namespace mpsc { 17
22#if defined(__cpp_lib_hardware_interference_size) 18#if defined(__cpp_lib_hardware_interference_size)
23constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size; 19constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size;
24#else 20#else
25constexpr size_t hardware_interference_size = 64; 21constexpr size_t hardware_interference_size = 64;
26#endif 22#endif
27 23
28template <typename T> 24#ifdef _MSC_VER
29using AlignedAllocator = std::allocator<T>; 25#pragma warning(push)
30 26#pragma warning(disable : 4324)
31template <typename T> 27#endif
32struct Slot {
33 ~Slot() noexcept {
34 if (turn.test()) {
35 destroy();
36 }
37 }
38
39 template <typename... Args>
40 void construct(Args&&... args) noexcept {
41 static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
42 "T must be nothrow constructible with Args&&...");
43 std::construct_at(reinterpret_cast<T*>(&storage), std::forward<Args>(args)...);
44 }
45
46 void destroy() noexcept {
47 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
48 std::destroy_at(reinterpret_cast<T*>(&storage));
49 }
50
51 T&& move() noexcept {
52 return reinterpret_cast<T&&>(storage);
53 }
54
55 // Align to avoid false sharing between adjacent slots
56 alignas(hardware_interference_size) std::atomic_flag turn{};
57 struct aligned_store {
58 struct type {
59 alignas(T) unsigned char data[sizeof(T)];
60 };
61 };
62 typename aligned_store::type storage;
63};
64 28
65template <typename T, typename Allocator = AlignedAllocator<Slot<T>>> 29template <typename T, size_t capacity = 0x400>
66class Queue { 30class MPSCQueue {
67public: 31public:
68 explicit Queue(const size_t capacity, const Allocator& allocator = Allocator()) 32 explicit MPSCQueue() : allocator{std::allocator<Slot<T>>()} {
69 : allocator_(allocator) {
70 if (capacity < 1) {
71 throw std::invalid_argument("capacity < 1");
72 }
73 // Ensure that the queue length is an integer power of 2
74 // This is so that idx(i) can be a simple i & mask_ insted of i % capacity
75 // https://github.com/rigtorp/MPMCQueue/pull/36
76 if (!std::has_single_bit(capacity)) {
77 throw std::invalid_argument("capacity must be an integer power of 2");
78 }
79
80 mask_ = capacity - 1;
81
82 // Allocate one extra slot to prevent false sharing on the last slot 33 // Allocate one extra slot to prevent false sharing on the last slot
83 slots_ = allocator_.allocate(mask_ + 2); 34 slots = allocator.allocate(capacity + 1);
84 // Allocators are not required to honor alignment for over-aligned types 35 // Allocators are not required to honor alignment for over-aligned types
85 // (see http://eel.is/c++draft/allocator.requirements#10) so we verify 36 // (see http://eel.is/c++draft/allocator.requirements#10) so we verify
86 // alignment here 37 // alignment here
87 if (reinterpret_cast<uintptr_t>(slots_) % alignof(Slot<T>) != 0) { 38 if (reinterpret_cast<uintptr_t>(slots) % alignof(Slot<T>) != 0) {
88 allocator_.deallocate(slots_, mask_ + 2); 39 allocator.deallocate(slots, capacity + 1);
89 throw std::bad_alloc(); 40 throw std::bad_alloc();
90 } 41 }
91 for (size_t i = 0; i < mask_ + 1; ++i) { 42 for (size_t i = 0; i < capacity; ++i) {
92 std::construct_at(&slots_[i]); 43 std::construct_at(&slots[i]);
93 } 44 }
45 static_assert(std::has_single_bit(capacity), "capacity must be an integer power of 2");
94 static_assert(alignof(Slot<T>) == hardware_interference_size, 46 static_assert(alignof(Slot<T>) == hardware_interference_size,
95 "Slot must be aligned to cache line boundary to prevent false sharing"); 47 "Slot must be aligned to cache line boundary to prevent false sharing");
96 static_assert(sizeof(Slot<T>) % hardware_interference_size == 0, 48 static_assert(sizeof(Slot<T>) % hardware_interference_size == 0,
97 "Slot size must be a multiple of cache line size to prevent " 49 "Slot size must be a multiple of cache line size to prevent "
98 "false sharing between adjacent slots"); 50 "false sharing between adjacent slots");
99 static_assert(sizeof(Queue) % hardware_interference_size == 0, 51 static_assert(sizeof(MPSCQueue) % hardware_interference_size == 0,
100 "Queue size must be a multiple of cache line size to " 52 "Queue size must be a multiple of cache line size to "
101 "prevent false sharing between adjacent queues"); 53 "prevent false sharing between adjacent queues");
102 } 54 }
103 55
104 ~Queue() noexcept { 56 ~MPSCQueue() noexcept {
105 for (size_t i = 0; i < mask_ + 1; ++i) { 57 for (size_t i = 0; i < capacity; ++i) {
106 slots_[i].~Slot(); 58 std::destroy_at(&slots[i]);
107 } 59 }
108 allocator_.deallocate(slots_, mask_ + 2); 60 allocator.deallocate(slots, capacity + 1);
109 } 61 }
110 62
111 // non-copyable and non-movable 63 // The queue must be both non-copyable and non-movable
112 Queue(const Queue&) = delete; 64 MPSCQueue(const MPSCQueue&) = delete;
113 Queue& operator=(const Queue&) = delete; 65 MPSCQueue& operator=(const MPSCQueue&) = delete;
66
67 MPSCQueue(MPSCQueue&&) = delete;
68 MPSCQueue& operator=(MPSCQueue&&) = delete;
114 69
115 void Push(const T& v) noexcept { 70 void Push(const T& v) noexcept {
116 static_assert(std::is_nothrow_copy_constructible_v<T>, 71 static_assert(std::is_nothrow_copy_constructible_v<T>,
@@ -125,8 +80,8 @@ public:
125 80
126 void Pop(T& v, std::stop_token stop) noexcept { 81 void Pop(T& v, std::stop_token stop) noexcept {
127 auto const tail = tail_.fetch_add(1); 82 auto const tail = tail_.fetch_add(1);
128 auto& slot = slots_[idx(tail)]; 83 auto& slot = slots[idx(tail)];
129 if (false == slot.turn.test()) { 84 if (!slot.turn.test()) {
130 std::unique_lock lock{cv_mutex}; 85 std::unique_lock lock{cv_mutex};
131 cv.wait(lock, stop, [&slot] { return slot.turn.test(); }); 86 cv.wait(lock, stop, [&slot] { return slot.turn.test(); });
132 } 87 }
@@ -137,12 +92,46 @@ public:
137 } 92 }
138 93
139private: 94private:
95 template <typename U = T>
96 struct Slot {
97 ~Slot() noexcept {
98 if (turn.test()) {
99 destroy();
100 }
101 }
102
103 template <typename... Args>
104 void construct(Args&&... args) noexcept {
105 static_assert(std::is_nothrow_constructible_v<U, Args&&...>,
106 "T must be nothrow constructible with Args&&...");
107 std::construct_at(reinterpret_cast<U*>(&storage), std::forward<Args>(args)...);
108 }
109
110 void destroy() noexcept {
111 static_assert(std::is_nothrow_destructible_v<U>, "T must be nothrow destructible");
112 std::destroy_at(reinterpret_cast<U*>(&storage));
113 }
114
115 U&& move() noexcept {
116 return reinterpret_cast<U&&>(storage);
117 }
118
119 // Align to avoid false sharing between adjacent slots
120 alignas(hardware_interference_size) std::atomic_flag turn{};
121 struct aligned_store {
122 struct type {
123 alignas(U) unsigned char data[sizeof(U)];
124 };
125 };
126 typename aligned_store::type storage;
127 };
128
140 template <typename... Args> 129 template <typename... Args>
141 void emplace(Args&&... args) noexcept { 130 void emplace(Args&&... args) noexcept {
142 static_assert(std::is_nothrow_constructible_v<T, Args&&...>, 131 static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
143 "T must be nothrow constructible with Args&&..."); 132 "T must be nothrow constructible with Args&&...");
144 auto const head = head_.fetch_add(1); 133 auto const head = head_.fetch_add(1);
145 auto& slot = slots_[idx(head)]; 134 auto& slot = slots[idx(head)];
146 slot.turn.wait(true); 135 slot.turn.wait(true);
147 slot.construct(std::forward<Args>(args)...); 136 slot.construct(std::forward<Args>(args)...);
148 slot.turn.test_and_set(); 137 slot.turn.test_and_set();
@@ -150,31 +139,29 @@ private:
150 } 139 }
151 140
152 constexpr size_t idx(size_t i) const noexcept { 141 constexpr size_t idx(size_t i) const noexcept {
153 return i & mask_; 142 return i & mask;
154 } 143 }
155 144
156 std::conditional_t<true, std::condition_variable_any, std::condition_variable> cv; 145 static constexpr size_t mask = capacity - 1;
157 std::mutex cv_mutex;
158 size_t mask_;
159 Slot<T>* slots_;
160 [[no_unique_address]] Allocator allocator_;
161 146
162 // Align to avoid false sharing between head_ and tail_ 147 // Align to avoid false sharing between head_ and tail_
163 alignas(hardware_interference_size) std::atomic<size_t> head_{0}; 148 alignas(hardware_interference_size) std::atomic<size_t> head_{0};
164 alignas(hardware_interference_size) std::atomic<size_t> tail_{0}; 149 alignas(hardware_interference_size) std::atomic<size_t> tail_{0};
165 150
151 std::mutex cv_mutex;
152 std::condition_variable_any cv;
153
154 Slot<T>* slots;
155 [[no_unique_address]] std::allocator<Slot<T>> allocator;
156
166 static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>, 157 static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>,
167 "T must be nothrow copy or move assignable"); 158 "T must be nothrow copy or move assignable");
168 159
169 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); 160 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
170}; 161};
171} // namespace mpsc
172
173template <typename T, typename Allocator = mpsc::AlignedAllocator<mpsc::Slot<T>>>
174using MPSCQueue = mpsc::Queue<T, Allocator>;
175
176} // namespace Common
177 162
178#ifdef _MSC_VER 163#ifdef _MSC_VER
179#pragma warning(pop) 164#pragma warning(pop)
180#endif 165#endif
166
167} // namespace Common
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index c1362631e..ec31d0b88 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -33,9 +33,9 @@ void DetachedTasks::AddTask(std::function<void()> task) {
33 ++instance->count; 33 ++instance->count;
34 std::thread([task{std::move(task)}]() { 34 std::thread([task{std::move(task)}]() {
35 task(); 35 task();
36 std::unique_lock lock{instance->mutex}; 36 std::unique_lock thread_lock{instance->mutex};
37 --instance->count; 37 --instance->count;
38 std::notify_all_at_thread_exit(instance->cv, std::move(lock)); 38 std::notify_all_at_thread_exit(instance->cv, std::move(thread_lock));
39 }).detach(); 39 }).detach();
40} 40}
41 41
diff --git a/src/common/page_table.h b/src/common/page_table.h
index fcbd12a43..1ad3a9f8b 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -15,6 +15,9 @@ enum class PageType : u8 {
15 Unmapped, 15 Unmapped,
16 /// Page is mapped to regular memory. This is the only type you can get pointers to. 16 /// Page is mapped to regular memory. This is the only type you can get pointers to.
17 Memory, 17 Memory,
18 /// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
19 /// the callbacks.
20 DebugMemory,
18 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and 21 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
19 /// invalidation 22 /// invalidation
20 RasterizerCachedMemory, 23 RasterizerCachedMemory,
diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp
index bbf20f5eb..462502e34 100644
--- a/src/common/param_package.cpp
+++ b/src/common/param_package.cpp
@@ -76,7 +76,7 @@ std::string ParamPackage::Serialize() const {
76std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const { 76std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const {
77 auto pair = data.find(key); 77 auto pair = data.find(key);
78 if (pair == data.end()) { 78 if (pair == data.end()) {
79 LOG_DEBUG(Common, "key '{}' not found", key); 79 LOG_TRACE(Common, "key '{}' not found", key);
80 return default_value; 80 return default_value;
81 } 81 }
82 82
@@ -86,7 +86,7 @@ std::string ParamPackage::Get(const std::string& key, const std::string& default
86int ParamPackage::Get(const std::string& key, int default_value) const { 86int ParamPackage::Get(const std::string& key, int default_value) const {
87 auto pair = data.find(key); 87 auto pair = data.find(key);
88 if (pair == data.end()) { 88 if (pair == data.end()) {
89 LOG_DEBUG(Common, "key '{}' not found", key); 89 LOG_TRACE(Common, "key '{}' not found", key);
90 return default_value; 90 return default_value;
91 } 91 }
92 92
@@ -101,7 +101,7 @@ int ParamPackage::Get(const std::string& key, int default_value) const {
101float ParamPackage::Get(const std::string& key, float default_value) const { 101float ParamPackage::Get(const std::string& key, float default_value) const {
102 auto pair = data.find(key); 102 auto pair = data.find(key);
103 if (pair == data.end()) { 103 if (pair == data.end()) {
104 LOG_DEBUG(Common, "key {} not found", key); 104 LOG_TRACE(Common, "key {} not found", key);
105 return default_value; 105 return default_value;
106 } 106 }
107 107
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 6ffab63af..751549583 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -147,7 +147,7 @@ void UpdateRescalingInfo() {
147 info.down_shift = 0; 147 info.down_shift = 0;
148 break; 148 break;
149 default: 149 default:
150 UNREACHABLE(); 150 ASSERT(false);
151 info.up_scale = 1; 151 info.up_scale = 1;
152 info.down_shift = 0; 152 info.down_shift = 0;
153 } 153 }
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index f932a7290..919e33af9 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -47,6 +47,9 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
47 case ThreadPriority::VeryHigh: 47 case ThreadPriority::VeryHigh:
48 windows_priority = THREAD_PRIORITY_HIGHEST; 48 windows_priority = THREAD_PRIORITY_HIGHEST;
49 break; 49 break;
50 case ThreadPriority::Critical:
51 windows_priority = THREAD_PRIORITY_TIME_CRITICAL;
52 break;
50 default: 53 default:
51 windows_priority = THREAD_PRIORITY_NORMAL; 54 windows_priority = THREAD_PRIORITY_NORMAL;
52 break; 55 break;
@@ -59,9 +62,10 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
59void SetCurrentThreadPriority(ThreadPriority new_priority) { 62void SetCurrentThreadPriority(ThreadPriority new_priority) {
60 pthread_t this_thread = pthread_self(); 63 pthread_t this_thread = pthread_self();
61 64
62 s32 max_prio = sched_get_priority_max(SCHED_OTHER); 65 const auto scheduling_type = SCHED_OTHER;
63 s32 min_prio = sched_get_priority_min(SCHED_OTHER); 66 s32 max_prio = sched_get_priority_max(scheduling_type);
64 u32 level = static_cast<u32>(new_priority) + 1; 67 s32 min_prio = sched_get_priority_min(scheduling_type);
68 u32 level = std::max(static_cast<u32>(new_priority) + 1, 4U);
65 69
66 struct sched_param params; 70 struct sched_param params;
67 if (max_prio > min_prio) { 71 if (max_prio > min_prio) {
@@ -70,7 +74,7 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
70 params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; 74 params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4;
71 } 75 }
72 76
73 pthread_setschedparam(this_thread, SCHED_OTHER, &params); 77 pthread_setschedparam(this_thread, scheduling_type, &params);
74} 78}
75 79
76#endif 80#endif
diff --git a/src/common/thread.h b/src/common/thread.h
index a63122516..1552f58e0 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -92,6 +92,7 @@ enum class ThreadPriority : u32 {
92 Normal = 1, 92 Normal = 1,
93 High = 2, 93 High = 2,
94 VeryHigh = 3, 94 VeryHigh = 3,
95 Critical = 4,
95}; 96};
96 97
97void SetCurrentThreadPriority(ThreadPriority new_priority); 98void SetCurrentThreadPriority(ThreadPriority new_priority);
diff --git a/src/common/uint128.h b/src/common/uint128.h
index f890ffec2..199d0f55e 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -31,12 +31,17 @@ namespace Common {
31 return _udiv128(r[1], r[0], d, &remainder); 31 return _udiv128(r[1], r[0], d, &remainder);
32#endif 32#endif
33#else 33#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
34 const u64 diva = a / d; 38 const u64 diva = a / d;
35 const u64 moda = a % d; 39 const u64 moda = a % d;
36 const u64 divb = b / d; 40 const u64 divb = b / d;
37 const u64 modb = b % d; 41 const u64 modb = b % d;
38 return diva * b + moda * divb + moda * modb / d; 42 return diva * b + moda * divb + moda * modb / d;
39#endif 43#endif
44#endif
40} 45}
41 46
42// This function multiplies 2 u64 values and produces a u128 value; 47// This function multiplies 2 u64 values and produces a u128 value;
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 1b7194503..6aaa8cdf9 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 new_time_point{};
79 TimePoint current_time_point{}; 78 TimePoint current_time_point{};
79 TimePoint new_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 {
@@ -89,8 +89,7 @@ u64 NativeClock::GetRTSC() {
89 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; 89 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
90 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, 90 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
91 current_time_point.pack, current_time_point.pack)); 91 current_time_point.pack, current_time_point.pack));
92 /// The clock cannot be more precise than the guest timer, remove the lower bits 92 return new_time_point.inner.accumulated_ticks;
93 return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
94} 93}
95 94
96void NativeClock::Pause(bool is_paused) { 95void NativeClock::Pause(bool is_paused) {
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index 30d2ba2e9..38ae7a462 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -37,12 +37,8 @@ private:
37 } inner; 37 } inner;
38 }; 38 };
39 39
40 /// value used to reduce the native clocks accuracy as some apss rely on
41 /// undefined behavior where the level of accuracy in the clock shouldn't
42 /// be higher.
43 static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
44
45 TimePoint time_point; 40 TimePoint time_point;
41
46 // factors 42 // factors
47 u64 clock_rtsc_factor{}; 43 u64 clock_rtsc_factor{};
48 u64 cpu_rtsc_factor{}; 44 u64 cpu_rtsc_factor{};