diff options
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/common/atomic_ops.cpp | 70 | ||||
| -rw-r--r-- | src/common/atomic_ops.h | 17 | ||||
| -rw-r--r-- | src/common/fiber.cpp | 10 | ||||
| -rw-r--r-- | src/common/spin_lock.cpp | 6 | ||||
| -rw-r--r-- | src/common/spin_lock.h | 5 | ||||
| -rw-r--r-- | src/common/thread.cpp | 52 | ||||
| -rw-r--r-- | src/common/thread.h | 9 | ||||
| -rw-r--r-- | src/common/wall_clock.cpp | 11 | ||||
| -rw-r--r-- | src/common/wall_clock.h | 2 | ||||
| -rw-r--r-- | src/common/x64/native_clock.cpp | 14 | ||||
| -rw-r--r-- | src/common/x64/native_clock.h | 7 |
12 files changed, 186 insertions, 19 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 3cc17d0e9..d120c8d3d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -98,6 +98,8 @@ add_library(common STATIC | |||
| 98 | algorithm.h | 98 | algorithm.h |
| 99 | alignment.h | 99 | alignment.h |
| 100 | assert.h | 100 | assert.h |
| 101 | atomic_ops.cpp | ||
| 102 | atomic_ops.h | ||
| 101 | detached_tasks.cpp | 103 | detached_tasks.cpp |
| 102 | detached_tasks.h | 104 | detached_tasks.h |
| 103 | bit_field.h | 105 | bit_field.h |
diff --git a/src/common/atomic_ops.cpp b/src/common/atomic_ops.cpp new file mode 100644 index 000000000..1098e21ff --- /dev/null +++ b/src/common/atomic_ops.cpp | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | |||
| 7 | #include "common/atomic_ops.h" | ||
| 8 | |||
| 9 | #if _MSC_VER | ||
| 10 | #include <intrin.h> | ||
| 11 | #endif | ||
| 12 | |||
| 13 | namespace Common { | ||
| 14 | |||
| 15 | #if _MSC_VER | ||
| 16 | |||
| 17 | bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { | ||
| 18 | u8 result = _InterlockedCompareExchange8((char*)pointer, value, expected); | ||
| 19 | return result == expected; | ||
| 20 | } | ||
| 21 | |||
| 22 | bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { | ||
| 23 | u16 result = _InterlockedCompareExchange16((short*)pointer, value, expected); | ||
| 24 | return result == expected; | ||
| 25 | } | ||
| 26 | |||
| 27 | bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { | ||
| 28 | u32 result = _InterlockedCompareExchange((long*)pointer, value, expected); | ||
| 29 | return result == expected; | ||
| 30 | } | ||
| 31 | |||
| 32 | bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { | ||
| 33 | u64 result = _InterlockedCompareExchange64((__int64*)pointer, value, expected); | ||
| 34 | return result == expected; | ||
| 35 | } | ||
| 36 | |||
| 37 | bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { | ||
| 38 | return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0], | ||
| 39 | (__int64*)expected.data()) != 0; | ||
| 40 | } | ||
| 41 | |||
| 42 | #else | ||
| 43 | |||
| 44 | bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { | ||
| 45 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 46 | } | ||
| 47 | |||
| 48 | bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { | ||
| 49 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 50 | } | ||
| 51 | |||
| 52 | bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { | ||
| 53 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 54 | } | ||
| 55 | |||
| 56 | bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { | ||
| 57 | return __sync_bool_compare_and_swap(pointer, expected, value); | ||
| 58 | } | ||
| 59 | |||
| 60 | bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { | ||
| 61 | unsigned __int128 value_a; | ||
| 62 | unsigned __int128 expected_a; | ||
| 63 | std::memcpy(&value_a, value.data(), sizeof(u128)); | ||
| 64 | std::memcpy(&expected_a, expected.data(), sizeof(u128)); | ||
| 65 | return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); | ||
| 66 | } | ||
| 67 | |||
| 68 | #endif | ||
| 69 | |||
| 70 | } // namespace Common | ||
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h new file mode 100644 index 000000000..e6181d521 --- /dev/null +++ b/src/common/atomic_ops.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected); | ||
| 12 | bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected); | ||
| 13 | bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected); | ||
| 14 | bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected); | ||
| 15 | bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected); | ||
| 16 | |||
| 17 | } // namespace Common | ||
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index f97ad433b..1c1d09ccb 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp | |||
| @@ -54,9 +54,7 @@ Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_paramete | |||
| 54 | impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); | 54 | impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | Fiber::Fiber() { | 57 | Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} |
| 58 | impl = std::make_unique<FiberImpl>(); | ||
| 59 | } | ||
| 60 | 58 | ||
| 61 | Fiber::~Fiber() { | 59 | Fiber::~Fiber() { |
| 62 | if (released) { | 60 | if (released) { |
| @@ -116,8 +114,8 @@ std::shared_ptr<Fiber> Fiber::ThreadToFiber() { | |||
| 116 | 114 | ||
| 117 | struct Fiber::FiberImpl { | 115 | struct Fiber::FiberImpl { |
| 118 | alignas(64) std::array<u8, default_stack_size> stack; | 116 | alignas(64) std::array<u8, default_stack_size> stack; |
| 119 | u8* stack_limit; | ||
| 120 | alignas(64) std::array<u8, default_stack_size> rewind_stack; | 117 | alignas(64) std::array<u8, default_stack_size> rewind_stack; |
| 118 | u8* stack_limit; | ||
| 121 | u8* rewind_stack_limit; | 119 | u8* rewind_stack_limit; |
| 122 | boost::context::detail::fcontext_t context; | 120 | boost::context::detail::fcontext_t context; |
| 123 | boost::context::detail::fcontext_t rewind_context; | 121 | boost::context::detail::fcontext_t rewind_context; |
| @@ -168,9 +166,7 @@ void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start | |||
| 168 | rewind_parameter = start_parameter; | 166 | rewind_parameter = start_parameter; |
| 169 | } | 167 | } |
| 170 | 168 | ||
| 171 | Fiber::Fiber() { | 169 | Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} |
| 172 | impl = std::make_unique<FiberImpl>(); | ||
| 173 | } | ||
| 174 | 170 | ||
| 175 | Fiber::~Fiber() { | 171 | Fiber::~Fiber() { |
| 176 | if (released) { | 172 | if (released) { |
diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp index c7b46aac6..c1524220f 100644 --- a/src/common/spin_lock.cpp +++ b/src/common/spin_lock.cpp | |||
| @@ -20,7 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | namespace { | 21 | namespace { |
| 22 | 22 | ||
| 23 | void thread_pause() { | 23 | void ThreadPause() { |
| 24 | #if __x86_64__ | 24 | #if __x86_64__ |
| 25 | _mm_pause(); | 25 | _mm_pause(); |
| 26 | #elif __aarch64__ && _MSC_VER | 26 | #elif __aarch64__ && _MSC_VER |
| @@ -30,13 +30,13 @@ void thread_pause() { | |||
| 30 | #endif | 30 | #endif |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | } // namespace | 33 | } // Anonymous namespace |
| 34 | 34 | ||
| 35 | namespace Common { | 35 | namespace Common { |
| 36 | 36 | ||
| 37 | void SpinLock::lock() { | 37 | void SpinLock::lock() { |
| 38 | while (lck.test_and_set(std::memory_order_acquire)) { | 38 | while (lck.test_and_set(std::memory_order_acquire)) { |
| 39 | thread_pause(); | 39 | ThreadPause(); |
| 40 | } | 40 | } |
| 41 | } | 41 | } |
| 42 | 42 | ||
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h index 70282a961..1df5528c4 100644 --- a/src/common/spin_lock.h +++ b/src/common/spin_lock.h | |||
| @@ -8,6 +8,11 @@ | |||
| 8 | 8 | ||
| 9 | namespace Common { | 9 | namespace Common { |
| 10 | 10 | ||
| 11 | /** | ||
| 12 | * SpinLock class | ||
| 13 | * a lock similar to mutex that forces a thread to spin wait instead calling the | ||
| 14 | * supervisor. Should be used on short sequences of code. | ||
| 15 | */ | ||
| 11 | class SpinLock { | 16 | class SpinLock { |
| 12 | public: | 17 | public: |
| 13 | void lock(); | 18 | void lock(); |
diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 0cd2d10bf..8e5935e6a 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp | |||
| @@ -25,6 +25,52 @@ | |||
| 25 | 25 | ||
| 26 | namespace Common { | 26 | namespace Common { |
| 27 | 27 | ||
| 28 | #ifdef _WIN32 | ||
| 29 | |||
| 30 | void SetCurrentThreadPriority(ThreadPriority new_priority) { | ||
| 31 | auto handle = GetCurrentThread(); | ||
| 32 | int windows_priority = 0; | ||
| 33 | switch (new_priority) { | ||
| 34 | case ThreadPriority::Low: | ||
| 35 | windows_priority = THREAD_PRIORITY_BELOW_NORMAL; | ||
| 36 | break; | ||
| 37 | case ThreadPriority::Normal: | ||
| 38 | windows_priority = THREAD_PRIORITY_NORMAL; | ||
| 39 | break; | ||
| 40 | case ThreadPriority::High: | ||
| 41 | windows_priority = THREAD_PRIORITY_ABOVE_NORMAL; | ||
| 42 | break; | ||
| 43 | case ThreadPriority::VeryHigh: | ||
| 44 | windows_priority = THREAD_PRIORITY_HIGHEST; | ||
| 45 | break; | ||
| 46 | default: | ||
| 47 | windows_priority = THREAD_PRIORITY_NORMAL; | ||
| 48 | break; | ||
| 49 | } | ||
| 50 | SetThreadPriority(handle, windows_priority); | ||
| 51 | } | ||
| 52 | |||
| 53 | #else | ||
| 54 | |||
| 55 | void SetCurrentThreadPriority(ThreadPriority new_priority) { | ||
| 56 | pthread_t this_thread = pthread_self(); | ||
| 57 | |||
| 58 | s32 max_prio = sched_get_priority_max(SCHED_OTHER); | ||
| 59 | s32 min_prio = sched_get_priority_min(SCHED_OTHER); | ||
| 60 | u32 level = static_cast<u32>(new_priority) + 1; | ||
| 61 | |||
| 62 | struct sched_param params; | ||
| 63 | if (max_prio > min_prio) { | ||
| 64 | params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4; | ||
| 65 | } else { | ||
| 66 | params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; | ||
| 67 | } | ||
| 68 | |||
| 69 | pthread_setschedparam(this_thread, SCHED_OTHER, ¶ms); | ||
| 70 | } | ||
| 71 | |||
| 72 | #endif | ||
| 73 | |||
| 28 | #ifdef _MSC_VER | 74 | #ifdef _MSC_VER |
| 29 | 75 | ||
| 30 | // Sets the debugger-visible name of the current thread. | 76 | // Sets the debugger-visible name of the current thread. |
| @@ -70,6 +116,12 @@ void SetCurrentThreadName(const char* name) { | |||
| 70 | } | 116 | } |
| 71 | #endif | 117 | #endif |
| 72 | 118 | ||
| 119 | #if defined(_WIN32) | ||
| 120 | void SetCurrentThreadName(const char* name) { | ||
| 121 | // Do Nothing on MingW | ||
| 122 | } | ||
| 123 | #endif | ||
| 124 | |||
| 73 | #endif | 125 | #endif |
| 74 | 126 | ||
| 75 | } // namespace Common | 127 | } // namespace Common |
diff --git a/src/common/thread.h b/src/common/thread.h index 127cc7e23..52b359413 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -86,6 +86,15 @@ private: | |||
| 86 | std::size_t generation = 0; // Incremented once each time the barrier is used | 86 | std::size_t generation = 0; // Incremented once each time the barrier is used |
| 87 | }; | 87 | }; |
| 88 | 88 | ||
| 89 | enum class ThreadPriority : u32 { | ||
| 90 | Low = 0, | ||
| 91 | Normal = 1, | ||
| 92 | High = 2, | ||
| 93 | VeryHigh = 3, | ||
| 94 | }; | ||
| 95 | |||
| 96 | void SetCurrentThreadPriority(ThreadPriority new_priority); | ||
| 97 | |||
| 89 | void SetCurrentThreadName(const char* name); | 98 | void SetCurrentThreadName(const char* name); |
| 90 | 99 | ||
| 91 | } // namespace Common | 100 | } // namespace Common |
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index d4d35f4e7..3afbdb898 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp | |||
| @@ -53,6 +53,10 @@ public: | |||
| 53 | return Common::Divide128On32(temporary, 1000000000).first; | 53 | return Common::Divide128On32(temporary, 1000000000).first; |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | void Pause(bool is_paused) override { | ||
| 57 | // Do nothing in this clock type. | ||
| 58 | } | ||
| 59 | |||
| 56 | private: | 60 | private: |
| 57 | base_time_point start_time; | 61 | base_time_point start_time; |
| 58 | }; | 62 | }; |
| @@ -64,12 +68,7 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, | |||
| 64 | const auto& caps = GetCPUCaps(); | 68 | const auto& caps = GetCPUCaps(); |
| 65 | u64 rtsc_frequency = 0; | 69 | u64 rtsc_frequency = 0; |
| 66 | if (caps.invariant_tsc) { | 70 | if (caps.invariant_tsc) { |
| 67 | if (caps.base_frequency != 0) { | 71 | rtsc_frequency = EstimateRDTSCFrequency(); |
| 68 | rtsc_frequency = static_cast<u64>(caps.base_frequency) * 1000000U; | ||
| 69 | } | ||
| 70 | if (rtsc_frequency == 0) { | ||
| 71 | rtsc_frequency = EstimateRDTSCFrequency(); | ||
| 72 | } | ||
| 73 | } | 72 | } |
| 74 | if (rtsc_frequency == 0) { | 73 | if (rtsc_frequency == 0) { |
| 75 | return std::make_unique<StandardWallClock>(emulated_cpu_frequency, | 74 | return std::make_unique<StandardWallClock>(emulated_cpu_frequency, |
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index ed284cf50..367d72134 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h | |||
| @@ -28,6 +28,8 @@ public: | |||
| 28 | /// Returns current wall time in emulated cpu cycles | 28 | /// Returns current wall time in emulated cpu cycles |
| 29 | virtual u64 GetCPUCycles() = 0; | 29 | virtual u64 GetCPUCycles() = 0; |
| 30 | 30 | ||
| 31 | virtual void Pause(bool is_paused) = 0; | ||
| 32 | |||
| 31 | /// Tells if the wall clock, uses the host CPU's hardware clock | 33 | /// Tells if the wall clock, uses the host CPU's hardware clock |
| 32 | bool IsNative() const { | 34 | bool IsNative() const { |
| 33 | return is_native; | 35 | return is_native; |
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 26d4d0ba6..424b39b1f 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <chrono> | 5 | #include <chrono> |
| 6 | #include <mutex> | ||
| 6 | #include <thread> | 7 | #include <thread> |
| 7 | 8 | ||
| 8 | #ifdef _MSC_VER | 9 | #ifdef _MSC_VER |
| @@ -52,7 +53,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequenc | |||
| 52 | } | 53 | } |
| 53 | 54 | ||
| 54 | u64 NativeClock::GetRTSC() { | 55 | u64 NativeClock::GetRTSC() { |
| 55 | rtsc_serialize.lock(); | 56 | std::scoped_lock scope{rtsc_serialize}; |
| 56 | _mm_mfence(); | 57 | _mm_mfence(); |
| 57 | const u64 current_measure = __rdtsc(); | 58 | const u64 current_measure = __rdtsc(); |
| 58 | u64 diff = current_measure - last_measure; | 59 | u64 diff = current_measure - last_measure; |
| @@ -61,8 +62,15 @@ u64 NativeClock::GetRTSC() { | |||
| 61 | last_measure = current_measure; | 62 | last_measure = current_measure; |
| 62 | } | 63 | } |
| 63 | accumulated_ticks += diff; | 64 | accumulated_ticks += diff; |
| 64 | rtsc_serialize.unlock(); | 65 | /// The clock cannot be more precise than the guest timer, remove the lower bits |
| 65 | return accumulated_ticks; | 66 | return accumulated_ticks & inaccuracy_mask; |
| 67 | } | ||
| 68 | |||
| 69 | void NativeClock::Pause(bool is_paused) { | ||
| 70 | if (!is_paused) { | ||
| 71 | _mm_mfence(); | ||
| 72 | last_measure = __rdtsc(); | ||
| 73 | } | ||
| 66 | } | 74 | } |
| 67 | 75 | ||
| 68 | std::chrono::nanoseconds NativeClock::GetTimeNS() { | 76 | std::chrono::nanoseconds NativeClock::GetTimeNS() { |
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index b58cf9f5a..891a3bbfd 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h | |||
| @@ -26,9 +26,16 @@ public: | |||
| 26 | 26 | ||
| 27 | u64 GetCPUCycles() override; | 27 | u64 GetCPUCycles() override; |
| 28 | 28 | ||
| 29 | void Pause(bool is_paused) override; | ||
| 30 | |||
| 29 | private: | 31 | private: |
| 30 | u64 GetRTSC(); | 32 | u64 GetRTSC(); |
| 31 | 33 | ||
| 34 | /// value used to reduce the native clocks accuracy as some apss rely on | ||
| 35 | /// undefined behavior where the level of accuracy in the clock shouldn't | ||
| 36 | /// be higher. | ||
| 37 | static constexpr u64 inaccuracy_mask = ~(0x400 - 1); | ||
| 38 | |||
| 32 | SpinLock rtsc_serialize{}; | 39 | SpinLock rtsc_serialize{}; |
| 33 | u64 last_measure{}; | 40 | u64 last_measure{}; |
| 34 | u64 accumulated_ticks{}; | 41 | u64 accumulated_ticks{}; |