diff options
204 files changed, 6922 insertions, 2516 deletions
diff --git a/dist/yuzu.manifest b/dist/yuzu.manifest index fd30b656f..038edff23 100644 --- a/dist/yuzu.manifest +++ b/dist/yuzu.manifest | |||
| @@ -1,24 +1,58 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | 1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> |
| 2 | <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | 2 | <assembly manifestVersion="1.0" |
| 3 | <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> | 3 | xmlns="urn:schemas-microsoft-com:asm.v1" |
| 4 | <security> | 4 | xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> |
| 5 | <requestedPrivileges> | 5 | <asmv3:application> |
| 6 | <requestedExecutionLevel level="asInvoker" uiAccess="false"/> | 6 | <asmv3:windowsSettings> |
| 7 | </requestedPrivileges> | 7 | <!-- Windows 7/8/8.1/10 --> |
| 8 | </security> | 8 | <dpiAware |
| 9 | </trustInfo> | 9 | xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> |
| 10 | <application xmlns="urn:schemas-microsoft-com:asm.v3"> | 10 | true/pm |
| 11 | <windowsSettings> | 11 | </dpiAware> |
| 12 | <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware> | 12 | <!-- Windows 10, version 1607 or later --> |
| 13 | <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware> | 13 | <dpiAwareness |
| 14 | </windowsSettings> | 14 | xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> |
| 15 | </application> | 15 | PerMonitorV2 |
| 16 | <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> | 16 | </dpiAwareness> |
| 17 | <application> | 17 | <!-- Windows 10, version 1703 or later --> |
| 18 | <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> | 18 | <gdiScaling |
| 19 | <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> | 19 | xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings"> |
| 20 | <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> | 20 | true |
| 21 | <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> | 21 | </gdiScaling> |
| 22 | </application> | 22 | <ws2:longPathAware |
| 23 | </compatibility> | 23 | xmlns:ws3="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> |
| 24 | </assembly> \ No newline at end of file | 24 | true |
| 25 | </ws2:longPathAware> | ||
| 26 | </asmv3:windowsSettings> | ||
| 27 | </asmv3:application> | ||
| 28 | <compatibility | ||
| 29 | xmlns="urn:schemas-microsoft-com:compatibility.v1"> | ||
| 30 | <application> | ||
| 31 | <!-- Windows 10 --> | ||
| 32 | <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> | ||
| 33 | <!-- Windows 8.1 --> | ||
| 34 | <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> | ||
| 35 | <!-- Windows 8 --> | ||
| 36 | <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> | ||
| 37 | <!-- Windows 7 --> | ||
| 38 | <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> | ||
| 39 | </application> | ||
| 40 | </compatibility> | ||
| 41 | <trustInfo | ||
| 42 | xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
| 43 | <security> | ||
| 44 | <requestedPrivileges> | ||
| 45 | <!-- | ||
| 46 | UAC settings: | ||
| 47 | - app should run at same integrity level as calling process | ||
| 48 | - app does not need to manipulate windows belonging to | ||
| 49 | higher-integrity-level processes | ||
| 50 | --> | ||
| 51 | <requestedExecutionLevel | ||
| 52 | level="asInvoker" | ||
| 53 | uiAccess="false" | ||
| 54 | /> | ||
| 55 | </requestedPrivileges> | ||
| 56 | </security> | ||
| 57 | </trustInfo> | ||
| 58 | </assembly> | ||
diff --git a/externals/dynarmic b/externals/dynarmic | |||
| Subproject e7166e8ba74d7b9c85e87afc0aaf667e7e84cfe | Subproject 4f967387c07365b7ea35d2fa3e19b7df8872a09 | ||
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3a57356ab..1e977e8a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -62,6 +62,10 @@ else() | |||
| 62 | -Wno-unused-parameter | 62 | -Wno-unused-parameter |
| 63 | ) | 63 | ) |
| 64 | 64 | ||
| 65 | if (ARCHITECTURE_x86_64) | ||
| 66 | add_compile_options("-mcx16") | ||
| 67 | endif() | ||
| 68 | |||
| 65 | if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) | 69 | if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) |
| 66 | add_compile_options("-stdlib=libc++") | 70 | add_compile_options("-stdlib=libc++") |
| 67 | endif() | 71 | endif() |
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 4ca98f8ea..dfc4805d9 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp | |||
| @@ -59,15 +59,24 @@ Stream::State Stream::GetState() const { | |||
| 59 | return state; | 59 | return state; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { | 62 | s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const { |
| 63 | const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; | 63 | const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; |
| 64 | const auto us = | 64 | const auto ns = |
| 65 | std::chrono::microseconds((static_cast<u64>(num_samples) * 1000000) / sample_rate); | 65 | std::chrono::nanoseconds((static_cast<u64>(num_samples) * 1000000000ULL) / sample_rate); |
| 66 | return Core::Timing::usToCycles(us); | 66 | return ns.count(); |
| 67 | } | ||
| 68 | |||
| 69 | s64 Stream::GetBufferReleaseNSHostTiming(const Buffer& buffer) const { | ||
| 70 | const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; | ||
| 71 | /// DSP signals before playing the last sample, in HLE we emulate this in this way | ||
| 72 | s64 base_samples = std::max<s64>(static_cast<s64>(num_samples) - 1, 0); | ||
| 73 | const auto ns = | ||
| 74 | std::chrono::nanoseconds((static_cast<u64>(base_samples) * 1000000000ULL) / sample_rate); | ||
| 75 | return ns.count(); | ||
| 67 | } | 76 | } |
| 68 | 77 | ||
| 69 | static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) { | 78 | static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) { |
| 70 | const float volume{std::clamp(Settings::values.volume - (1.0f - game_volume), 0.0f, 1.0f)}; | 79 | const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)}; |
| 71 | 80 | ||
| 72 | if (volume == 1.0f) { | 81 | if (volume == 1.0f) { |
| 73 | return; | 82 | return; |
| @@ -105,7 +114,11 @@ void Stream::PlayNextBuffer() { | |||
| 105 | 114 | ||
| 106 | sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); | 115 | sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); |
| 107 | 116 | ||
| 108 | core_timing.ScheduleEvent(GetBufferReleaseCycles(*active_buffer), release_event, {}); | 117 | if (core_timing.IsHostTiming()) { |
| 118 | core_timing.ScheduleEvent(GetBufferReleaseNSHostTiming(*active_buffer), release_event, {}); | ||
| 119 | } else { | ||
| 120 | core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer), release_event, {}); | ||
| 121 | } | ||
| 109 | } | 122 | } |
| 110 | 123 | ||
| 111 | void Stream::ReleaseActiveBuffer() { | 124 | void Stream::ReleaseActiveBuffer() { |
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 1708a4d98..e309d60fe 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h | |||
| @@ -96,7 +96,10 @@ private: | |||
| 96 | void ReleaseActiveBuffer(); | 96 | void ReleaseActiveBuffer(); |
| 97 | 97 | ||
| 98 | /// Gets the number of core cycles when the specified buffer will be released | 98 | /// Gets the number of core cycles when the specified buffer will be released |
| 99 | s64 GetBufferReleaseCycles(const Buffer& buffer) const; | 99 | s64 GetBufferReleaseNS(const Buffer& buffer) const; |
| 100 | |||
| 101 | /// Gets the number of core cycles when the specified buffer will be released | ||
| 102 | s64 GetBufferReleaseNSHostTiming(const Buffer& buffer) const; | ||
| 100 | 103 | ||
| 101 | u32 sample_rate; ///< Sample rate of the stream | 104 | u32 sample_rate; ///< Sample rate of the stream |
| 102 | Format format; ///< Format of the stream | 105 | Format format; ///< Format of the stream |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 0a3e2f4d1..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 |
| @@ -110,6 +112,8 @@ add_library(common STATIC | |||
| 110 | common_types.h | 112 | common_types.h |
| 111 | dynamic_library.cpp | 113 | dynamic_library.cpp |
| 112 | dynamic_library.h | 114 | dynamic_library.h |
| 115 | fiber.cpp | ||
| 116 | fiber.h | ||
| 113 | file_util.cpp | 117 | file_util.cpp |
| 114 | file_util.h | 118 | file_util.h |
| 115 | hash.h | 119 | hash.h |
| @@ -143,6 +147,8 @@ add_library(common STATIC | |||
| 143 | scm_rev.cpp | 147 | scm_rev.cpp |
| 144 | scm_rev.h | 148 | scm_rev.h |
| 145 | scope_exit.h | 149 | scope_exit.h |
| 150 | spin_lock.cpp | ||
| 151 | spin_lock.h | ||
| 146 | string_util.cpp | 152 | string_util.cpp |
| 147 | string_util.h | 153 | string_util.h |
| 148 | swap.h | 154 | swap.h |
| @@ -163,6 +169,8 @@ add_library(common STATIC | |||
| 163 | vector_math.h | 169 | vector_math.h |
| 164 | virtual_buffer.cpp | 170 | virtual_buffer.cpp |
| 165 | virtual_buffer.h | 171 | virtual_buffer.h |
| 172 | wall_clock.cpp | ||
| 173 | wall_clock.h | ||
| 166 | web_result.h | 174 | web_result.h |
| 167 | zstd_compression.cpp | 175 | zstd_compression.cpp |
| 168 | zstd_compression.h | 176 | zstd_compression.h |
| @@ -173,12 +181,15 @@ if(ARCHITECTURE_x86_64) | |||
| 173 | PRIVATE | 181 | PRIVATE |
| 174 | x64/cpu_detect.cpp | 182 | x64/cpu_detect.cpp |
| 175 | x64/cpu_detect.h | 183 | x64/cpu_detect.h |
| 184 | x64/native_clock.cpp | ||
| 185 | x64/native_clock.h | ||
| 176 | x64/xbyak_abi.h | 186 | x64/xbyak_abi.h |
| 177 | x64/xbyak_util.h | 187 | x64/xbyak_util.h |
| 178 | ) | 188 | ) |
| 179 | endif() | 189 | endif() |
| 180 | 190 | ||
| 181 | create_target_directory_groups(common) | 191 | create_target_directory_groups(common) |
| 192 | find_package(Boost 1.71 COMPONENTS context headers REQUIRED) | ||
| 182 | 193 | ||
| 183 | target_link_libraries(common PUBLIC Boost::boost fmt::fmt microprofile) | 194 | target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile) |
| 184 | target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak) | 195 | target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak) |
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 new file mode 100644 index 000000000..1c1d09ccb --- /dev/null +++ b/src/common/fiber.cpp | |||
| @@ -0,0 +1,222 @@ | |||
| 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 "common/assert.h" | ||
| 6 | #include "common/fiber.h" | ||
| 7 | #if defined(_WIN32) || defined(WIN32) | ||
| 8 | #include <windows.h> | ||
| 9 | #else | ||
| 10 | #include <boost/context/detail/fcontext.hpp> | ||
| 11 | #endif | ||
| 12 | |||
| 13 | namespace Common { | ||
| 14 | |||
| 15 | constexpr std::size_t default_stack_size = 256 * 1024; // 256kb | ||
| 16 | |||
| 17 | #if defined(_WIN32) || defined(WIN32) | ||
| 18 | |||
| 19 | struct Fiber::FiberImpl { | ||
| 20 | LPVOID handle = nullptr; | ||
| 21 | LPVOID rewind_handle = nullptr; | ||
| 22 | }; | ||
| 23 | |||
| 24 | void Fiber::Start() { | ||
| 25 | ASSERT(previous_fiber != nullptr); | ||
| 26 | previous_fiber->guard.unlock(); | ||
| 27 | previous_fiber.reset(); | ||
| 28 | entry_point(start_parameter); | ||
| 29 | UNREACHABLE(); | ||
| 30 | } | ||
| 31 | |||
| 32 | void Fiber::OnRewind() { | ||
| 33 | ASSERT(impl->handle != nullptr); | ||
| 34 | DeleteFiber(impl->handle); | ||
| 35 | impl->handle = impl->rewind_handle; | ||
| 36 | impl->rewind_handle = nullptr; | ||
| 37 | rewind_point(rewind_parameter); | ||
| 38 | UNREACHABLE(); | ||
| 39 | } | ||
| 40 | |||
| 41 | void Fiber::FiberStartFunc(void* fiber_parameter) { | ||
| 42 | auto fiber = static_cast<Fiber*>(fiber_parameter); | ||
| 43 | fiber->Start(); | ||
| 44 | } | ||
| 45 | |||
| 46 | void Fiber::RewindStartFunc(void* fiber_parameter) { | ||
| 47 | auto fiber = static_cast<Fiber*>(fiber_parameter); | ||
| 48 | fiber->OnRewind(); | ||
| 49 | } | ||
| 50 | |||
| 51 | Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter) | ||
| 52 | : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { | ||
| 53 | impl = std::make_unique<FiberImpl>(); | ||
| 54 | impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); | ||
| 55 | } | ||
| 56 | |||
| 57 | Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} | ||
| 58 | |||
| 59 | Fiber::~Fiber() { | ||
| 60 | if (released) { | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | // Make sure the Fiber is not being used | ||
| 64 | const bool locked = guard.try_lock(); | ||
| 65 | ASSERT_MSG(locked, "Destroying a fiber that's still running"); | ||
| 66 | if (locked) { | ||
| 67 | guard.unlock(); | ||
| 68 | } | ||
| 69 | DeleteFiber(impl->handle); | ||
| 70 | } | ||
| 71 | |||
| 72 | void Fiber::Exit() { | ||
| 73 | ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); | ||
| 74 | if (!is_thread_fiber) { | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | ConvertFiberToThread(); | ||
| 78 | guard.unlock(); | ||
| 79 | released = true; | ||
| 80 | } | ||
| 81 | |||
| 82 | void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) { | ||
| 83 | rewind_point = std::move(rewind_func); | ||
| 84 | rewind_parameter = start_parameter; | ||
| 85 | } | ||
| 86 | |||
| 87 | void Fiber::Rewind() { | ||
| 88 | ASSERT(rewind_point); | ||
| 89 | ASSERT(impl->rewind_handle == nullptr); | ||
| 90 | impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this); | ||
| 91 | SwitchToFiber(impl->rewind_handle); | ||
| 92 | } | ||
| 93 | |||
| 94 | void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) { | ||
| 95 | ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); | ||
| 96 | ASSERT_MSG(to != nullptr, "Next fiber is null!"); | ||
| 97 | to->guard.lock(); | ||
| 98 | to->previous_fiber = from; | ||
| 99 | SwitchToFiber(to->impl->handle); | ||
| 100 | ASSERT(from->previous_fiber != nullptr); | ||
| 101 | from->previous_fiber->guard.unlock(); | ||
| 102 | from->previous_fiber.reset(); | ||
| 103 | } | ||
| 104 | |||
| 105 | std::shared_ptr<Fiber> Fiber::ThreadToFiber() { | ||
| 106 | std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()}; | ||
| 107 | fiber->guard.lock(); | ||
| 108 | fiber->impl->handle = ConvertThreadToFiber(nullptr); | ||
| 109 | fiber->is_thread_fiber = true; | ||
| 110 | return fiber; | ||
| 111 | } | ||
| 112 | |||
| 113 | #else | ||
| 114 | |||
| 115 | struct Fiber::FiberImpl { | ||
| 116 | alignas(64) std::array<u8, default_stack_size> stack; | ||
| 117 | alignas(64) std::array<u8, default_stack_size> rewind_stack; | ||
| 118 | u8* stack_limit; | ||
| 119 | u8* rewind_stack_limit; | ||
| 120 | boost::context::detail::fcontext_t context; | ||
| 121 | boost::context::detail::fcontext_t rewind_context; | ||
| 122 | }; | ||
| 123 | |||
| 124 | void Fiber::Start(boost::context::detail::transfer_t& transfer) { | ||
| 125 | ASSERT(previous_fiber != nullptr); | ||
| 126 | previous_fiber->impl->context = transfer.fctx; | ||
| 127 | previous_fiber->guard.unlock(); | ||
| 128 | previous_fiber.reset(); | ||
| 129 | entry_point(start_parameter); | ||
| 130 | UNREACHABLE(); | ||
| 131 | } | ||
| 132 | |||
| 133 | void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transfer) { | ||
| 134 | ASSERT(impl->context != nullptr); | ||
| 135 | impl->context = impl->rewind_context; | ||
| 136 | impl->rewind_context = nullptr; | ||
| 137 | u8* tmp = impl->stack_limit; | ||
| 138 | impl->stack_limit = impl->rewind_stack_limit; | ||
| 139 | impl->rewind_stack_limit = tmp; | ||
| 140 | rewind_point(rewind_parameter); | ||
| 141 | UNREACHABLE(); | ||
| 142 | } | ||
| 143 | |||
| 144 | void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { | ||
| 145 | auto fiber = static_cast<Fiber*>(transfer.data); | ||
| 146 | fiber->Start(transfer); | ||
| 147 | } | ||
| 148 | |||
| 149 | void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { | ||
| 150 | auto fiber = static_cast<Fiber*>(transfer.data); | ||
| 151 | fiber->OnRewind(transfer); | ||
| 152 | } | ||
| 153 | |||
| 154 | Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter) | ||
| 155 | : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { | ||
| 156 | impl = std::make_unique<FiberImpl>(); | ||
| 157 | impl->stack_limit = impl->stack.data(); | ||
| 158 | impl->rewind_stack_limit = impl->rewind_stack.data(); | ||
| 159 | u8* stack_base = impl->stack_limit + default_stack_size; | ||
| 160 | impl->context = | ||
| 161 | boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc); | ||
| 162 | } | ||
| 163 | |||
| 164 | void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) { | ||
| 165 | rewind_point = std::move(rewind_func); | ||
| 166 | rewind_parameter = start_parameter; | ||
| 167 | } | ||
| 168 | |||
| 169 | Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {} | ||
| 170 | |||
| 171 | Fiber::~Fiber() { | ||
| 172 | if (released) { | ||
| 173 | return; | ||
| 174 | } | ||
| 175 | // Make sure the Fiber is not being used | ||
| 176 | const bool locked = guard.try_lock(); | ||
| 177 | ASSERT_MSG(locked, "Destroying a fiber that's still running"); | ||
| 178 | if (locked) { | ||
| 179 | guard.unlock(); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | void Fiber::Exit() { | ||
| 184 | |||
| 185 | ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); | ||
| 186 | if (!is_thread_fiber) { | ||
| 187 | return; | ||
| 188 | } | ||
| 189 | guard.unlock(); | ||
| 190 | released = true; | ||
| 191 | } | ||
| 192 | |||
| 193 | void Fiber::Rewind() { | ||
| 194 | ASSERT(rewind_point); | ||
| 195 | ASSERT(impl->rewind_context == nullptr); | ||
| 196 | u8* stack_base = impl->rewind_stack_limit + default_stack_size; | ||
| 197 | impl->rewind_context = | ||
| 198 | boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc); | ||
| 199 | boost::context::detail::jump_fcontext(impl->rewind_context, this); | ||
| 200 | } | ||
| 201 | |||
| 202 | void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) { | ||
| 203 | ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); | ||
| 204 | ASSERT_MSG(to != nullptr, "Next fiber is null!"); | ||
| 205 | to->guard.lock(); | ||
| 206 | to->previous_fiber = from; | ||
| 207 | auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); | ||
| 208 | ASSERT(from->previous_fiber != nullptr); | ||
| 209 | from->previous_fiber->impl->context = transfer.fctx; | ||
| 210 | from->previous_fiber->guard.unlock(); | ||
| 211 | from->previous_fiber.reset(); | ||
| 212 | } | ||
| 213 | |||
| 214 | std::shared_ptr<Fiber> Fiber::ThreadToFiber() { | ||
| 215 | std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()}; | ||
| 216 | fiber->guard.lock(); | ||
| 217 | fiber->is_thread_fiber = true; | ||
| 218 | return fiber; | ||
| 219 | } | ||
| 220 | |||
| 221 | #endif | ||
| 222 | } // namespace Common | ||
diff --git a/src/common/fiber.h b/src/common/fiber.h new file mode 100644 index 000000000..dafc1100e --- /dev/null +++ b/src/common/fiber.h | |||
| @@ -0,0 +1,92 @@ | |||
| 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 <functional> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "common/spin_lock.h" | ||
| 12 | |||
| 13 | #if !defined(_WIN32) && !defined(WIN32) | ||
| 14 | namespace boost::context::detail { | ||
| 15 | struct transfer_t; | ||
| 16 | } | ||
| 17 | #endif | ||
| 18 | |||
| 19 | namespace Common { | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Fiber class | ||
| 23 | * a fiber is a userspace thread with it's own context. They can be used to | ||
| 24 | * implement coroutines, emulated threading systems and certain asynchronous | ||
| 25 | * patterns. | ||
| 26 | * | ||
| 27 | * This class implements fibers at a low level, thus allowing greater freedom | ||
| 28 | * to implement such patterns. This fiber class is 'threadsafe' only one fiber | ||
| 29 | * can be running at a time and threads will be locked while trying to yield to | ||
| 30 | * a running fiber until it yields. WARNING exchanging two running fibers between | ||
| 31 | * threads will cause a deadlock. In order to prevent a deadlock, each thread should | ||
| 32 | * have an intermediary fiber, you switch to the intermediary fiber of the current | ||
| 33 | * thread and then from it switch to the expected fiber. This way you can exchange | ||
| 34 | * 2 fibers within 2 different threads. | ||
| 35 | */ | ||
| 36 | class Fiber { | ||
| 37 | public: | ||
| 38 | Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter); | ||
| 39 | ~Fiber(); | ||
| 40 | |||
| 41 | Fiber(const Fiber&) = delete; | ||
| 42 | Fiber& operator=(const Fiber&) = delete; | ||
| 43 | |||
| 44 | Fiber(Fiber&&) = default; | ||
| 45 | Fiber& operator=(Fiber&&) = default; | ||
| 46 | |||
| 47 | /// Yields control from Fiber 'from' to Fiber 'to' | ||
| 48 | /// Fiber 'from' must be the currently running fiber. | ||
| 49 | static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to); | ||
| 50 | static std::shared_ptr<Fiber> ThreadToFiber(); | ||
| 51 | |||
| 52 | void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter); | ||
| 53 | |||
| 54 | void Rewind(); | ||
| 55 | |||
| 56 | /// Only call from main thread's fiber | ||
| 57 | void Exit(); | ||
| 58 | |||
| 59 | /// Changes the start parameter of the fiber. Has no effect if the fiber already started | ||
| 60 | void SetStartParameter(void* new_parameter) { | ||
| 61 | start_parameter = new_parameter; | ||
| 62 | } | ||
| 63 | |||
| 64 | private: | ||
| 65 | Fiber(); | ||
| 66 | |||
| 67 | #if defined(_WIN32) || defined(WIN32) | ||
| 68 | void OnRewind(); | ||
| 69 | void Start(); | ||
| 70 | static void FiberStartFunc(void* fiber_parameter); | ||
| 71 | static void RewindStartFunc(void* fiber_parameter); | ||
| 72 | #else | ||
| 73 | void OnRewind(boost::context::detail::transfer_t& transfer); | ||
| 74 | void Start(boost::context::detail::transfer_t& transfer); | ||
| 75 | static void FiberStartFunc(boost::context::detail::transfer_t transfer); | ||
| 76 | static void RewindStartFunc(boost::context::detail::transfer_t transfer); | ||
| 77 | #endif | ||
| 78 | |||
| 79 | struct FiberImpl; | ||
| 80 | |||
| 81 | SpinLock guard{}; | ||
| 82 | std::function<void(void*)> entry_point; | ||
| 83 | std::function<void(void*)> rewind_point; | ||
| 84 | void* rewind_parameter{}; | ||
| 85 | void* start_parameter{}; | ||
| 86 | std::shared_ptr<Fiber> previous_fiber; | ||
| 87 | std::unique_ptr<FiberImpl> impl; | ||
| 88 | bool is_thread_fiber{}; | ||
| 89 | bool released{}; | ||
| 90 | }; | ||
| 91 | |||
| 92 | } // namespace Common | ||
diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp new file mode 100644 index 000000000..c1524220f --- /dev/null +++ b/src/common/spin_lock.cpp | |||
| @@ -0,0 +1,54 @@ | |||
| 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 "common/spin_lock.h" | ||
| 6 | |||
| 7 | #if _MSC_VER | ||
| 8 | #include <intrin.h> | ||
| 9 | #if _M_AMD64 | ||
| 10 | #define __x86_64__ 1 | ||
| 11 | #endif | ||
| 12 | #if _M_ARM64 | ||
| 13 | #define __aarch64__ 1 | ||
| 14 | #endif | ||
| 15 | #else | ||
| 16 | #if __x86_64__ | ||
| 17 | #include <xmmintrin.h> | ||
| 18 | #endif | ||
| 19 | #endif | ||
| 20 | |||
| 21 | namespace { | ||
| 22 | |||
| 23 | void ThreadPause() { | ||
| 24 | #if __x86_64__ | ||
| 25 | _mm_pause(); | ||
| 26 | #elif __aarch64__ && _MSC_VER | ||
| 27 | __yield(); | ||
| 28 | #elif __aarch64__ | ||
| 29 | asm("yield"); | ||
| 30 | #endif | ||
| 31 | } | ||
| 32 | |||
| 33 | } // Anonymous namespace | ||
| 34 | |||
| 35 | namespace Common { | ||
| 36 | |||
| 37 | void SpinLock::lock() { | ||
| 38 | while (lck.test_and_set(std::memory_order_acquire)) { | ||
| 39 | ThreadPause(); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | void SpinLock::unlock() { | ||
| 44 | lck.clear(std::memory_order_release); | ||
| 45 | } | ||
| 46 | |||
| 47 | bool SpinLock::try_lock() { | ||
| 48 | if (lck.test_and_set(std::memory_order_acquire)) { | ||
| 49 | return false; | ||
| 50 | } | ||
| 51 | return true; | ||
| 52 | } | ||
| 53 | |||
| 54 | } // namespace Common | ||
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h new file mode 100644 index 000000000..1df5528c4 --- /dev/null +++ b/src/common/spin_lock.h | |||
| @@ -0,0 +1,26 @@ | |||
| 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 <atomic> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 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 | */ | ||
| 16 | class SpinLock { | ||
| 17 | public: | ||
| 18 | void lock(); | ||
| 19 | void unlock(); | ||
| 20 | bool try_lock(); | ||
| 21 | |||
| 22 | private: | ||
| 23 | std::atomic_flag lck = ATOMIC_FLAG_INIT; | ||
| 24 | }; | ||
| 25 | |||
| 26 | } // namespace Common | ||
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp index 200c6489a..16d42facd 100644 --- a/src/common/telemetry.cpp +++ b/src/common/telemetry.cpp | |||
| @@ -60,6 +60,7 @@ void AppendCPUInfo(FieldCollection& fc) { | |||
| 60 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); | 60 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); |
| 61 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); | 61 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); |
| 62 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); | 62 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); |
| 63 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512); | ||
| 63 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); | 64 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); |
| 64 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); | 65 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); |
| 65 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); | 66 | fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); |
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 2fc071685..52b359413 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <cstddef> | 9 | #include <cstddef> |
| 10 | #include <mutex> | 10 | #include <mutex> |
| 11 | #include <thread> | 11 | #include <thread> |
| 12 | #include "common/common_types.h" | ||
| 12 | 13 | ||
| 13 | namespace Common { | 14 | namespace Common { |
| 14 | 15 | ||
| @@ -28,8 +29,7 @@ public: | |||
| 28 | is_set = false; | 29 | is_set = false; |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | template <class Duration> | 32 | bool WaitFor(const std::chrono::nanoseconds& time) { |
| 32 | bool WaitFor(const std::chrono::duration<Duration>& time) { | ||
| 33 | std::unique_lock lk{mutex}; | 33 | std::unique_lock lk{mutex}; |
| 34 | if (!condvar.wait_for(lk, time, [this] { return is_set; })) | 34 | if (!condvar.wait_for(lk, time, [this] { return is_set; })) |
| 35 | return false; | 35 | return false; |
| @@ -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/uint128.cpp b/src/common/uint128.cpp index 32bf56730..16bf7c828 100644 --- a/src/common/uint128.cpp +++ b/src/common/uint128.cpp | |||
| @@ -6,12 +6,38 @@ | |||
| 6 | #include <intrin.h> | 6 | #include <intrin.h> |
| 7 | 7 | ||
| 8 | #pragma intrinsic(_umul128) | 8 | #pragma intrinsic(_umul128) |
| 9 | #pragma intrinsic(_udiv128) | ||
| 9 | #endif | 10 | #endif |
| 10 | #include <cstring> | 11 | #include <cstring> |
| 11 | #include "common/uint128.h" | 12 | #include "common/uint128.h" |
| 12 | 13 | ||
| 13 | namespace Common { | 14 | namespace Common { |
| 14 | 15 | ||
| 16 | #ifdef _MSC_VER | ||
| 17 | |||
| 18 | u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) { | ||
| 19 | u128 r{}; | ||
| 20 | r[0] = _umul128(a, b, &r[1]); | ||
| 21 | u64 remainder; | ||
| 22 | #if _MSC_VER < 1923 | ||
| 23 | return udiv128(r[1], r[0], d, &remainder); | ||
| 24 | #else | ||
| 25 | return _udiv128(r[1], r[0], d, &remainder); | ||
| 26 | #endif | ||
| 27 | } | ||
| 28 | |||
| 29 | #else | ||
| 30 | |||
| 31 | u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) { | ||
| 32 | const u64 diva = a / d; | ||
| 33 | const u64 moda = a % d; | ||
| 34 | const u64 divb = b / d; | ||
| 35 | const u64 modb = b % d; | ||
| 36 | return diva * b + moda * divb + moda * modb / d; | ||
| 37 | } | ||
| 38 | |||
| 39 | #endif | ||
| 40 | |||
| 15 | u128 Multiply64Into128(u64 a, u64 b) { | 41 | u128 Multiply64Into128(u64 a, u64 b) { |
| 16 | u128 result; | 42 | u128 result; |
| 17 | #ifdef _MSC_VER | 43 | #ifdef _MSC_VER |
diff --git a/src/common/uint128.h b/src/common/uint128.h index a3be2a2cb..503cd2d0c 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h | |||
| @@ -9,6 +9,9 @@ | |||
| 9 | 9 | ||
| 10 | namespace Common { | 10 | namespace Common { |
| 11 | 11 | ||
| 12 | // This function multiplies 2 u64 values and divides it by a u64 value. | ||
| 13 | u64 MultiplyAndDivide64(u64 a, u64 b, u64 d); | ||
| 14 | |||
| 12 | // This function multiplies 2 u64 values and produces a u128 value; | 15 | // This function multiplies 2 u64 values and produces a u128 value; |
| 13 | u128 Multiply64Into128(u64 a, u64 b); | 16 | u128 Multiply64Into128(u64 a, u64 b); |
| 14 | 17 | ||
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp new file mode 100644 index 000000000..3afbdb898 --- /dev/null +++ b/src/common/wall_clock.cpp | |||
| @@ -0,0 +1,91 @@ | |||
| 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 "common/uint128.h" | ||
| 6 | #include "common/wall_clock.h" | ||
| 7 | |||
| 8 | #ifdef ARCHITECTURE_x86_64 | ||
| 9 | #include "common/x64/cpu_detect.h" | ||
| 10 | #include "common/x64/native_clock.h" | ||
| 11 | #endif | ||
| 12 | |||
| 13 | namespace Common { | ||
| 14 | |||
| 15 | using base_timer = std::chrono::steady_clock; | ||
| 16 | using base_time_point = std::chrono::time_point<base_timer>; | ||
| 17 | |||
| 18 | class StandardWallClock : public WallClock { | ||
| 19 | public: | ||
| 20 | StandardWallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency) | ||
| 21 | : WallClock(emulated_cpu_frequency, emulated_clock_frequency, false) { | ||
| 22 | start_time = base_timer::now(); | ||
| 23 | } | ||
| 24 | |||
| 25 | std::chrono::nanoseconds GetTimeNS() override { | ||
| 26 | base_time_point current = base_timer::now(); | ||
| 27 | auto elapsed = current - start_time; | ||
| 28 | return std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed); | ||
| 29 | } | ||
| 30 | |||
| 31 | std::chrono::microseconds GetTimeUS() override { | ||
| 32 | base_time_point current = base_timer::now(); | ||
| 33 | auto elapsed = current - start_time; | ||
| 34 | return std::chrono::duration_cast<std::chrono::microseconds>(elapsed); | ||
| 35 | } | ||
| 36 | |||
| 37 | std::chrono::milliseconds GetTimeMS() override { | ||
| 38 | base_time_point current = base_timer::now(); | ||
| 39 | auto elapsed = current - start_time; | ||
| 40 | return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed); | ||
| 41 | } | ||
| 42 | |||
| 43 | u64 GetClockCycles() override { | ||
| 44 | std::chrono::nanoseconds time_now = GetTimeNS(); | ||
| 45 | const u128 temporary = | ||
| 46 | Common::Multiply64Into128(time_now.count(), emulated_clock_frequency); | ||
| 47 | return Common::Divide128On32(temporary, 1000000000).first; | ||
| 48 | } | ||
| 49 | |||
| 50 | u64 GetCPUCycles() override { | ||
| 51 | std::chrono::nanoseconds time_now = GetTimeNS(); | ||
| 52 | const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency); | ||
| 53 | return Common::Divide128On32(temporary, 1000000000).first; | ||
| 54 | } | ||
| 55 | |||
| 56 | void Pause(bool is_paused) override { | ||
| 57 | // Do nothing in this clock type. | ||
| 58 | } | ||
| 59 | |||
| 60 | private: | ||
| 61 | base_time_point start_time; | ||
| 62 | }; | ||
| 63 | |||
| 64 | #ifdef ARCHITECTURE_x86_64 | ||
| 65 | |||
| 66 | std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, | ||
| 67 | u32 emulated_clock_frequency) { | ||
| 68 | const auto& caps = GetCPUCaps(); | ||
| 69 | u64 rtsc_frequency = 0; | ||
| 70 | if (caps.invariant_tsc) { | ||
| 71 | rtsc_frequency = EstimateRDTSCFrequency(); | ||
| 72 | } | ||
| 73 | if (rtsc_frequency == 0) { | ||
| 74 | return std::make_unique<StandardWallClock>(emulated_cpu_frequency, | ||
| 75 | emulated_clock_frequency); | ||
| 76 | } else { | ||
| 77 | return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency, | ||
| 78 | rtsc_frequency); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | #else | ||
| 83 | |||
| 84 | std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, | ||
| 85 | u32 emulated_clock_frequency) { | ||
| 86 | return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency); | ||
| 87 | } | ||
| 88 | |||
| 89 | #endif | ||
| 90 | |||
| 91 | } // namespace Common | ||
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h new file mode 100644 index 000000000..367d72134 --- /dev/null +++ b/src/common/wall_clock.h | |||
| @@ -0,0 +1,53 @@ | |||
| 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 <chrono> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | class WallClock { | ||
| 15 | public: | ||
| 16 | /// Returns current wall time in nanoseconds | ||
| 17 | virtual std::chrono::nanoseconds GetTimeNS() = 0; | ||
| 18 | |||
| 19 | /// Returns current wall time in microseconds | ||
| 20 | virtual std::chrono::microseconds GetTimeUS() = 0; | ||
| 21 | |||
| 22 | /// Returns current wall time in milliseconds | ||
| 23 | virtual std::chrono::milliseconds GetTimeMS() = 0; | ||
| 24 | |||
| 25 | /// Returns current wall time in emulated clock cycles | ||
| 26 | virtual u64 GetClockCycles() = 0; | ||
| 27 | |||
| 28 | /// Returns current wall time in emulated cpu cycles | ||
| 29 | virtual u64 GetCPUCycles() = 0; | ||
| 30 | |||
| 31 | virtual void Pause(bool is_paused) = 0; | ||
| 32 | |||
| 33 | /// Tells if the wall clock, uses the host CPU's hardware clock | ||
| 34 | bool IsNative() const { | ||
| 35 | return is_native; | ||
| 36 | } | ||
| 37 | |||
| 38 | protected: | ||
| 39 | WallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, bool is_native) | ||
| 40 | : emulated_cpu_frequency{emulated_cpu_frequency}, | ||
| 41 | emulated_clock_frequency{emulated_clock_frequency}, is_native{is_native} {} | ||
| 42 | |||
| 43 | u64 emulated_cpu_frequency; | ||
| 44 | u64 emulated_clock_frequency; | ||
| 45 | |||
| 46 | private: | ||
| 47 | bool is_native; | ||
| 48 | }; | ||
| 49 | |||
| 50 | std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, | ||
| 51 | u32 emulated_clock_frequency); | ||
| 52 | |||
| 53 | } // namespace Common | ||
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index c9349a6b4..fccd2eee5 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp | |||
| @@ -62,6 +62,17 @@ static CPUCaps Detect() { | |||
| 62 | std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); | 62 | std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); |
| 63 | std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); | 63 | std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); |
| 64 | std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); | 64 | std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); |
| 65 | if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69) | ||
| 66 | caps.manufacturer = Manufacturer::Intel; | ||
| 67 | else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65) | ||
| 68 | caps.manufacturer = Manufacturer::AMD; | ||
| 69 | else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e) | ||
| 70 | caps.manufacturer = Manufacturer::Hygon; | ||
| 71 | else | ||
| 72 | caps.manufacturer = Manufacturer::Unknown; | ||
| 73 | |||
| 74 | u32 family = {}; | ||
| 75 | u32 model = {}; | ||
| 65 | 76 | ||
| 66 | __cpuid(cpu_id, 0x80000000); | 77 | __cpuid(cpu_id, 0x80000000); |
| 67 | 78 | ||
| @@ -73,6 +84,14 @@ static CPUCaps Detect() { | |||
| 73 | // Detect family and other miscellaneous features | 84 | // Detect family and other miscellaneous features |
| 74 | if (max_std_fn >= 1) { | 85 | if (max_std_fn >= 1) { |
| 75 | __cpuid(cpu_id, 0x00000001); | 86 | __cpuid(cpu_id, 0x00000001); |
| 87 | family = (cpu_id[0] >> 8) & 0xf; | ||
| 88 | model = (cpu_id[0] >> 4) & 0xf; | ||
| 89 | if (family == 0xf) { | ||
| 90 | family += (cpu_id[0] >> 20) & 0xff; | ||
| 91 | } | ||
| 92 | if (family >= 6) { | ||
| 93 | model += ((cpu_id[0] >> 16) & 0xf) << 4; | ||
| 94 | } | ||
| 76 | 95 | ||
| 77 | if ((cpu_id[3] >> 25) & 1) | 96 | if ((cpu_id[3] >> 25) & 1) |
| 78 | caps.sse = true; | 97 | caps.sse = true; |
| @@ -110,6 +129,11 @@ static CPUCaps Detect() { | |||
| 110 | caps.bmi1 = true; | 129 | caps.bmi1 = true; |
| 111 | if ((cpu_id[1] >> 8) & 1) | 130 | if ((cpu_id[1] >> 8) & 1) |
| 112 | caps.bmi2 = true; | 131 | caps.bmi2 = true; |
| 132 | // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP) | ||
| 133 | if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 && | ||
| 134 | (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) { | ||
| 135 | caps.avx512 = caps.avx2; | ||
| 136 | } | ||
| 113 | } | 137 | } |
| 114 | } | 138 | } |
| 115 | 139 | ||
| @@ -130,6 +154,20 @@ static CPUCaps Detect() { | |||
| 130 | caps.fma4 = true; | 154 | caps.fma4 = true; |
| 131 | } | 155 | } |
| 132 | 156 | ||
| 157 | if (max_ex_fn >= 0x80000007) { | ||
| 158 | __cpuid(cpu_id, 0x80000007); | ||
| 159 | if (cpu_id[3] & (1 << 8)) { | ||
| 160 | caps.invariant_tsc = true; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | if (max_std_fn >= 0x16) { | ||
| 165 | __cpuid(cpu_id, 0x16); | ||
| 166 | caps.base_frequency = cpu_id[0]; | ||
| 167 | caps.max_frequency = cpu_id[1]; | ||
| 168 | caps.bus_frequency = cpu_id[2]; | ||
| 169 | } | ||
| 170 | |||
| 133 | return caps; | 171 | return caps; |
| 134 | } | 172 | } |
| 135 | 173 | ||
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 20f2ba234..e3b63302e 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h | |||
| @@ -6,8 +6,16 @@ | |||
| 6 | 6 | ||
| 7 | namespace Common { | 7 | namespace Common { |
| 8 | 8 | ||
| 9 | enum class Manufacturer : u32 { | ||
| 10 | Intel = 0, | ||
| 11 | AMD = 1, | ||
| 12 | Hygon = 2, | ||
| 13 | Unknown = 3, | ||
| 14 | }; | ||
| 15 | |||
| 9 | /// x86/x64 CPU capabilities that may be detected by this module | 16 | /// x86/x64 CPU capabilities that may be detected by this module |
| 10 | struct CPUCaps { | 17 | struct CPUCaps { |
| 18 | Manufacturer manufacturer; | ||
| 11 | char cpu_string[0x21]; | 19 | char cpu_string[0x21]; |
| 12 | char brand_string[0x41]; | 20 | char brand_string[0x41]; |
| 13 | bool sse; | 21 | bool sse; |
| @@ -19,11 +27,16 @@ struct CPUCaps { | |||
| 19 | bool lzcnt; | 27 | bool lzcnt; |
| 20 | bool avx; | 28 | bool avx; |
| 21 | bool avx2; | 29 | bool avx2; |
| 30 | bool avx512; | ||
| 22 | bool bmi1; | 31 | bool bmi1; |
| 23 | bool bmi2; | 32 | bool bmi2; |
| 24 | bool fma; | 33 | bool fma; |
| 25 | bool fma4; | 34 | bool fma4; |
| 26 | bool aes; | 35 | bool aes; |
| 36 | bool invariant_tsc; | ||
| 37 | u32 base_frequency; | ||
| 38 | u32 max_frequency; | ||
| 39 | u32 bus_frequency; | ||
| 27 | }; | 40 | }; |
| 28 | 41 | ||
| 29 | /** | 42 | /** |
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp new file mode 100644 index 000000000..424b39b1f --- /dev/null +++ b/src/common/x64/native_clock.cpp | |||
| @@ -0,0 +1,103 @@ | |||
| 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 <chrono> | ||
| 6 | #include <mutex> | ||
| 7 | #include <thread> | ||
| 8 | |||
| 9 | #ifdef _MSC_VER | ||
| 10 | #include <intrin.h> | ||
| 11 | #else | ||
| 12 | #include <x86intrin.h> | ||
| 13 | #endif | ||
| 14 | |||
| 15 | #include "common/uint128.h" | ||
| 16 | #include "common/x64/native_clock.h" | ||
| 17 | |||
| 18 | namespace Common { | ||
| 19 | |||
| 20 | u64 EstimateRDTSCFrequency() { | ||
| 21 | const auto milli_10 = std::chrono::milliseconds{10}; | ||
| 22 | // get current time | ||
| 23 | _mm_mfence(); | ||
| 24 | const u64 tscStart = __rdtsc(); | ||
| 25 | const auto startTime = std::chrono::high_resolution_clock::now(); | ||
| 26 | // wait roughly 3 seconds | ||
| 27 | while (true) { | ||
| 28 | auto milli = std::chrono::duration_cast<std::chrono::milliseconds>( | ||
| 29 | std::chrono::high_resolution_clock::now() - startTime); | ||
| 30 | if (milli.count() >= 3000) | ||
| 31 | break; | ||
| 32 | std::this_thread::sleep_for(milli_10); | ||
| 33 | } | ||
| 34 | const auto endTime = std::chrono::high_resolution_clock::now(); | ||
| 35 | _mm_mfence(); | ||
| 36 | const u64 tscEnd = __rdtsc(); | ||
| 37 | // calculate difference | ||
| 38 | const u64 timer_diff = | ||
| 39 | std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count(); | ||
| 40 | const u64 tsc_diff = tscEnd - tscStart; | ||
| 41 | const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); | ||
| 42 | return tsc_freq; | ||
| 43 | } | ||
| 44 | |||
| 45 | namespace X64 { | ||
| 46 | NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, | ||
| 47 | u64 rtsc_frequency) | ||
| 48 | : WallClock(emulated_cpu_frequency, emulated_clock_frequency, true), rtsc_frequency{ | ||
| 49 | rtsc_frequency} { | ||
| 50 | _mm_mfence(); | ||
| 51 | last_measure = __rdtsc(); | ||
| 52 | accumulated_ticks = 0U; | ||
| 53 | } | ||
| 54 | |||
| 55 | u64 NativeClock::GetRTSC() { | ||
| 56 | std::scoped_lock scope{rtsc_serialize}; | ||
| 57 | _mm_mfence(); | ||
| 58 | const u64 current_measure = __rdtsc(); | ||
| 59 | u64 diff = current_measure - last_measure; | ||
| 60 | diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) | ||
| 61 | if (current_measure > last_measure) { | ||
| 62 | last_measure = current_measure; | ||
| 63 | } | ||
| 64 | accumulated_ticks += diff; | ||
| 65 | /// The clock cannot be more precise than the guest timer, remove the lower bits | ||
| 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 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | std::chrono::nanoseconds NativeClock::GetTimeNS() { | ||
| 77 | const u64 rtsc_value = GetRTSC(); | ||
| 78 | return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)}; | ||
| 79 | } | ||
| 80 | |||
| 81 | std::chrono::microseconds NativeClock::GetTimeUS() { | ||
| 82 | const u64 rtsc_value = GetRTSC(); | ||
| 83 | return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)}; | ||
| 84 | } | ||
| 85 | |||
| 86 | std::chrono::milliseconds NativeClock::GetTimeMS() { | ||
| 87 | const u64 rtsc_value = GetRTSC(); | ||
| 88 | return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)}; | ||
| 89 | } | ||
| 90 | |||
| 91 | u64 NativeClock::GetClockCycles() { | ||
| 92 | const u64 rtsc_value = GetRTSC(); | ||
| 93 | return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency); | ||
| 94 | } | ||
| 95 | |||
| 96 | u64 NativeClock::GetCPUCycles() { | ||
| 97 | const u64 rtsc_value = GetRTSC(); | ||
| 98 | return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); | ||
| 99 | } | ||
| 100 | |||
| 101 | } // namespace X64 | ||
| 102 | |||
| 103 | } // namespace Common | ||
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h new file mode 100644 index 000000000..891a3bbfd --- /dev/null +++ b/src/common/x64/native_clock.h | |||
| @@ -0,0 +1,48 @@ | |||
| 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 <optional> | ||
| 8 | |||
| 9 | #include "common/spin_lock.h" | ||
| 10 | #include "common/wall_clock.h" | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | namespace X64 { | ||
| 15 | class NativeClock : public WallClock { | ||
| 16 | public: | ||
| 17 | NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, u64 rtsc_frequency); | ||
| 18 | |||
| 19 | std::chrono::nanoseconds GetTimeNS() override; | ||
| 20 | |||
| 21 | std::chrono::microseconds GetTimeUS() override; | ||
| 22 | |||
| 23 | std::chrono::milliseconds GetTimeMS() override; | ||
| 24 | |||
| 25 | u64 GetClockCycles() override; | ||
| 26 | |||
| 27 | u64 GetCPUCycles() override; | ||
| 28 | |||
| 29 | void Pause(bool is_paused) override; | ||
| 30 | |||
| 31 | private: | ||
| 32 | u64 GetRTSC(); | ||
| 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 | |||
| 39 | SpinLock rtsc_serialize{}; | ||
| 40 | u64 last_measure{}; | ||
| 41 | u64 accumulated_ticks{}; | ||
| 42 | u64 rtsc_frequency; | ||
| 43 | }; | ||
| 44 | } // namespace X64 | ||
| 45 | |||
| 46 | u64 EstimateRDTSCFrequency(); | ||
| 47 | |||
| 48 | } // namespace Common | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index cb9ced5c9..f87d67db5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -7,6 +7,16 @@ endif() | |||
| 7 | add_library(core STATIC | 7 | add_library(core STATIC |
| 8 | arm/arm_interface.h | 8 | arm/arm_interface.h |
| 9 | arm/arm_interface.cpp | 9 | arm/arm_interface.cpp |
| 10 | arm/cpu_interrupt_handler.cpp | ||
| 11 | arm/cpu_interrupt_handler.h | ||
| 12 | arm/dynarmic/arm_dynarmic_32.cpp | ||
| 13 | arm/dynarmic/arm_dynarmic_32.h | ||
| 14 | arm/dynarmic/arm_dynarmic_64.cpp | ||
| 15 | arm/dynarmic/arm_dynarmic_64.h | ||
| 16 | arm/dynarmic/arm_dynarmic_cp15.cpp | ||
| 17 | arm/dynarmic/arm_dynarmic_cp15.h | ||
| 18 | arm/dynarmic/arm_exclusive_monitor.cpp | ||
| 19 | arm/dynarmic/arm_exclusive_monitor.h | ||
| 10 | arm/exclusive_monitor.cpp | 20 | arm/exclusive_monitor.cpp |
| 11 | arm/exclusive_monitor.h | 21 | arm/exclusive_monitor.h |
| 12 | arm/unicorn/arm_unicorn.cpp | 22 | arm/unicorn/arm_unicorn.cpp |
| @@ -15,8 +25,6 @@ add_library(core STATIC | |||
| 15 | constants.h | 25 | constants.h |
| 16 | core.cpp | 26 | core.cpp |
| 17 | core.h | 27 | core.h |
| 18 | core_manager.cpp | ||
| 19 | core_manager.h | ||
| 20 | core_timing.cpp | 28 | core_timing.cpp |
| 21 | core_timing.h | 29 | core_timing.h |
| 22 | core_timing_util.cpp | 30 | core_timing_util.cpp |
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index d079a1bc8..d2295ed90 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp | |||
| @@ -139,6 +139,63 @@ std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_addr | |||
| 139 | 139 | ||
| 140 | constexpr u64 SEGMENT_BASE = 0x7100000000ull; | 140 | constexpr u64 SEGMENT_BASE = 0x7100000000ull; |
| 141 | 141 | ||
| 142 | std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( | ||
| 143 | System& system, const ThreadContext64& ctx) { | ||
| 144 | std::vector<BacktraceEntry> out; | ||
| 145 | auto& memory = system.Memory(); | ||
| 146 | |||
| 147 | auto fp = ctx.cpu_registers[29]; | ||
| 148 | auto lr = ctx.cpu_registers[30]; | ||
| 149 | while (true) { | ||
| 150 | out.push_back({"", 0, lr, 0}); | ||
| 151 | if (!fp) { | ||
| 152 | break; | ||
| 153 | } | ||
| 154 | lr = memory.Read64(fp + 8) - 4; | ||
| 155 | fp = memory.Read64(fp); | ||
| 156 | } | ||
| 157 | |||
| 158 | std::map<VAddr, std::string> modules; | ||
| 159 | auto& loader{system.GetAppLoader()}; | ||
| 160 | if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { | ||
| 161 | return {}; | ||
| 162 | } | ||
| 163 | |||
| 164 | std::map<std::string, Symbols> symbols; | ||
| 165 | for (const auto& module : modules) { | ||
| 166 | symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); | ||
| 167 | } | ||
| 168 | |||
| 169 | for (auto& entry : out) { | ||
| 170 | VAddr base = 0; | ||
| 171 | for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) { | ||
| 172 | const auto& module{*iter}; | ||
| 173 | if (entry.original_address >= module.first) { | ||
| 174 | entry.module = module.second; | ||
| 175 | base = module.first; | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | entry.offset = entry.original_address - base; | ||
| 181 | entry.address = SEGMENT_BASE + entry.offset; | ||
| 182 | |||
| 183 | if (entry.module.empty()) | ||
| 184 | entry.module = "unknown"; | ||
| 185 | |||
| 186 | const auto symbol_set = symbols.find(entry.module); | ||
| 187 | if (symbol_set != symbols.end()) { | ||
| 188 | const auto symbol = GetSymbolName(symbol_set->second, entry.offset); | ||
| 189 | if (symbol.has_value()) { | ||
| 190 | // TODO(DarkLordZach): Add demangling of symbol names. | ||
| 191 | entry.name = *symbol; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | return out; | ||
| 197 | } | ||
| 198 | |||
| 142 | std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { | 199 | std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { |
| 143 | std::vector<BacktraceEntry> out; | 200 | std::vector<BacktraceEntry> out; |
| 144 | auto& memory = system.Memory(); | 201 | auto& memory = system.Memory(); |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index cb2e640e2..1f24051e4 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/hardware_properties.h" | ||
| 10 | 11 | ||
| 11 | namespace Common { | 12 | namespace Common { |
| 12 | struct PageTable; | 13 | struct PageTable; |
| @@ -18,25 +19,29 @@ enum class VMAPermission : u8; | |||
| 18 | 19 | ||
| 19 | namespace Core { | 20 | namespace Core { |
| 20 | class System; | 21 | class System; |
| 22 | class CPUInterruptHandler; | ||
| 23 | |||
| 24 | using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>; | ||
| 21 | 25 | ||
| 22 | /// Generic ARMv8 CPU interface | 26 | /// Generic ARMv8 CPU interface |
| 23 | class ARM_Interface : NonCopyable { | 27 | class ARM_Interface : NonCopyable { |
| 24 | public: | 28 | public: |
| 25 | explicit ARM_Interface(System& system_) : system{system_} {} | 29 | explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers, bool uses_wall_clock) |
| 30 | : system{system_}, interrupt_handlers{interrupt_handlers}, uses_wall_clock{ | ||
| 31 | uses_wall_clock} {} | ||
| 26 | virtual ~ARM_Interface() = default; | 32 | virtual ~ARM_Interface() = default; |
| 27 | 33 | ||
| 28 | struct ThreadContext32 { | 34 | struct ThreadContext32 { |
| 29 | std::array<u32, 16> cpu_registers{}; | 35 | std::array<u32, 16> cpu_registers{}; |
| 36 | std::array<u32, 64> extension_registers{}; | ||
| 30 | u32 cpsr{}; | 37 | u32 cpsr{}; |
| 31 | std::array<u8, 4> padding{}; | ||
| 32 | std::array<u64, 32> fprs{}; | ||
| 33 | u32 fpscr{}; | 38 | u32 fpscr{}; |
| 34 | u32 fpexc{}; | 39 | u32 fpexc{}; |
| 35 | u32 tpidr{}; | 40 | u32 tpidr{}; |
| 36 | }; | 41 | }; |
| 37 | // Internally within the kernel, it expects the AArch32 version of the | 42 | // Internally within the kernel, it expects the AArch32 version of the |
| 38 | // thread context to be 344 bytes in size. | 43 | // thread context to be 344 bytes in size. |
| 39 | static_assert(sizeof(ThreadContext32) == 0x158); | 44 | static_assert(sizeof(ThreadContext32) == 0x150); |
| 40 | 45 | ||
| 41 | struct ThreadContext64 { | 46 | struct ThreadContext64 { |
| 42 | std::array<u64, 31> cpu_registers{}; | 47 | std::array<u64, 31> cpu_registers{}; |
| @@ -143,6 +148,8 @@ public: | |||
| 143 | */ | 148 | */ |
| 144 | virtual void SetTPIDR_EL0(u64 value) = 0; | 149 | virtual void SetTPIDR_EL0(u64 value) = 0; |
| 145 | 150 | ||
| 151 | virtual void ChangeProcessorID(std::size_t new_core_id) = 0; | ||
| 152 | |||
| 146 | virtual void SaveContext(ThreadContext32& ctx) = 0; | 153 | virtual void SaveContext(ThreadContext32& ctx) = 0; |
| 147 | virtual void SaveContext(ThreadContext64& ctx) = 0; | 154 | virtual void SaveContext(ThreadContext64& ctx) = 0; |
| 148 | virtual void LoadContext(const ThreadContext32& ctx) = 0; | 155 | virtual void LoadContext(const ThreadContext32& ctx) = 0; |
| @@ -162,6 +169,9 @@ public: | |||
| 162 | std::string name; | 169 | std::string name; |
| 163 | }; | 170 | }; |
| 164 | 171 | ||
| 172 | static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, | ||
| 173 | const ThreadContext64& ctx); | ||
| 174 | |||
| 165 | std::vector<BacktraceEntry> GetBacktrace() const; | 175 | std::vector<BacktraceEntry> GetBacktrace() const; |
| 166 | 176 | ||
| 167 | /// fp (= r29) points to the last frame record. | 177 | /// fp (= r29) points to the last frame record. |
| @@ -175,6 +185,8 @@ public: | |||
| 175 | protected: | 185 | protected: |
| 176 | /// System context that this ARM interface is running under. | 186 | /// System context that this ARM interface is running under. |
| 177 | System& system; | 187 | System& system; |
| 188 | CPUInterrupts& interrupt_handlers; | ||
| 189 | bool uses_wall_clock; | ||
| 178 | }; | 190 | }; |
| 179 | 191 | ||
| 180 | } // namespace Core | 192 | } // namespace Core |
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp new file mode 100644 index 000000000..2f1a1a269 --- /dev/null +++ b/src/core/arm/cpu_interrupt_handler.cpp | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/thread.h" | ||
| 8 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 9 | |||
| 10 | namespace Core { | ||
| 11 | |||
| 12 | CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} { | ||
| 13 | interrupt_event = std::make_unique<Common::Event>(); | ||
| 14 | } | ||
| 15 | |||
| 16 | CPUInterruptHandler::~CPUInterruptHandler() = default; | ||
| 17 | |||
| 18 | void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) { | ||
| 19 | if (is_interrupted_) { | ||
| 20 | interrupt_event->Set(); | ||
| 21 | } | ||
| 22 | this->is_interrupted = is_interrupted_; | ||
| 23 | } | ||
| 24 | |||
| 25 | void CPUInterruptHandler::AwaitInterrupt() { | ||
| 26 | interrupt_event->Wait(); | ||
| 27 | } | ||
| 28 | |||
| 29 | } // namespace Core | ||
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h new file mode 100644 index 000000000..3d062d326 --- /dev/null +++ b/src/core/arm/cpu_interrupt_handler.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | class Event; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Core { | ||
| 14 | |||
| 15 | class CPUInterruptHandler { | ||
| 16 | public: | ||
| 17 | CPUInterruptHandler(); | ||
| 18 | ~CPUInterruptHandler(); | ||
| 19 | |||
| 20 | CPUInterruptHandler(const CPUInterruptHandler&) = delete; | ||
| 21 | CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete; | ||
| 22 | |||
| 23 | CPUInterruptHandler(CPUInterruptHandler&&) = default; | ||
| 24 | CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default; | ||
| 25 | |||
| 26 | bool IsInterrupted() const { | ||
| 27 | return is_interrupted; | ||
| 28 | } | ||
| 29 | |||
| 30 | void SetInterrupt(bool is_interrupted); | ||
| 31 | |||
| 32 | void AwaitInterrupt(); | ||
| 33 | |||
| 34 | private: | ||
| 35 | bool is_interrupted{}; | ||
| 36 | std::unique_ptr<Common::Event> interrupt_event; | ||
| 37 | }; | ||
| 38 | |||
| 39 | } // namespace Core | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 19d798dc7..0d4ab95b7 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -7,15 +7,17 @@ | |||
| 7 | #include <dynarmic/A32/a32.h> | 7 | #include <dynarmic/A32/a32.h> |
| 8 | #include <dynarmic/A32/config.h> | 8 | #include <dynarmic/A32/config.h> |
| 9 | #include <dynarmic/A32/context.h> | 9 | #include <dynarmic/A32/context.h> |
| 10 | #include "common/microprofile.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/page_table.h" | ||
| 12 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 11 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | 13 | #include "core/arm/dynarmic/arm_dynarmic_32.h" |
| 12 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | ||
| 13 | #include "core/arm/dynarmic/arm_dynarmic_cp15.h" | 14 | #include "core/arm/dynarmic/arm_dynarmic_cp15.h" |
| 15 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" | ||
| 14 | #include "core/core.h" | 16 | #include "core/core.h" |
| 15 | #include "core/core_manager.h" | ||
| 16 | #include "core/core_timing.h" | 17 | #include "core/core_timing.h" |
| 17 | #include "core/hle/kernel/svc.h" | 18 | #include "core/hle/kernel/svc.h" |
| 18 | #include "core/memory.h" | 19 | #include "core/memory.h" |
| 20 | #include "core/settings.h" | ||
| 19 | 21 | ||
| 20 | namespace Core { | 22 | namespace Core { |
| 21 | 23 | ||
| @@ -49,6 +51,19 @@ public: | |||
| 49 | parent.system.Memory().Write64(vaddr, value); | 51 | parent.system.Memory().Write64(vaddr, value); |
| 50 | } | 52 | } |
| 51 | 53 | ||
| 54 | bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override { | ||
| 55 | return parent.system.Memory().WriteExclusive8(vaddr, value, expected); | ||
| 56 | } | ||
| 57 | bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override { | ||
| 58 | return parent.system.Memory().WriteExclusive16(vaddr, value, expected); | ||
| 59 | } | ||
| 60 | bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override { | ||
| 61 | return parent.system.Memory().WriteExclusive32(vaddr, value, expected); | ||
| 62 | } | ||
| 63 | bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override { | ||
| 64 | return parent.system.Memory().WriteExclusive64(vaddr, value, expected); | ||
| 65 | } | ||
| 66 | |||
| 52 | void InterpreterFallback(u32 pc, std::size_t num_instructions) override { | 67 | void InterpreterFallback(u32 pc, std::size_t num_instructions) override { |
| 53 | UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, | 68 | UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, |
| 54 | MemoryReadCode(pc)); | 69 | MemoryReadCode(pc)); |
| @@ -62,7 +77,7 @@ public: | |||
| 62 | case Dynarmic::A32::Exception::Breakpoint: | 77 | case Dynarmic::A32::Exception::Breakpoint: |
| 63 | break; | 78 | break; |
| 64 | } | 79 | } |
| 65 | LOG_CRITICAL(HW_GPU, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", | 80 | LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", |
| 66 | static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); | 81 | static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); |
| 67 | UNIMPLEMENTED(); | 82 | UNIMPLEMENTED(); |
| 68 | } | 83 | } |
| @@ -72,24 +87,36 @@ public: | |||
| 72 | } | 87 | } |
| 73 | 88 | ||
| 74 | void AddTicks(u64 ticks) override { | 89 | void AddTicks(u64 ticks) override { |
| 90 | if (parent.uses_wall_clock) { | ||
| 91 | return; | ||
| 92 | } | ||
| 75 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a | 93 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a |
| 76 | // rough approximation of the amount of executed ticks in the system, it may be thrown off | 94 | // rough approximation of the amount of executed ticks in the system, it may be thrown off |
| 77 | // if not all cores are doing a similar amount of work. Instead of doing this, we should | 95 | // if not all cores are doing a similar amount of work. Instead of doing this, we should |
| 78 | // device a way so that timing is consistent across all cores without increasing the ticks 4 | 96 | // device a way so that timing is consistent across all cores without increasing the ticks 4 |
| 79 | // times. | 97 | // times. |
| 80 | u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; | 98 | u64 amortized_ticks = |
| 99 | (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES; | ||
| 81 | // Always execute at least one tick. | 100 | // Always execute at least one tick. |
| 82 | amortized_ticks = std::max<u64>(amortized_ticks, 1); | 101 | amortized_ticks = std::max<u64>(amortized_ticks, 1); |
| 83 | 102 | ||
| 84 | parent.system.CoreTiming().AddTicks(amortized_ticks); | 103 | parent.system.CoreTiming().AddTicks(amortized_ticks); |
| 85 | num_interpreted_instructions = 0; | 104 | num_interpreted_instructions = 0; |
| 86 | } | 105 | } |
| 106 | |||
| 87 | u64 GetTicksRemaining() override { | 107 | u64 GetTicksRemaining() override { |
| 88 | return std::max(parent.system.CoreTiming().GetDowncount(), {}); | 108 | if (parent.uses_wall_clock) { |
| 109 | if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { | ||
| 110 | return minimum_run_cycles; | ||
| 111 | } | ||
| 112 | return 0U; | ||
| 113 | } | ||
| 114 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); | ||
| 89 | } | 115 | } |
| 90 | 116 | ||
| 91 | ARM_Dynarmic_32& parent; | 117 | ARM_Dynarmic_32& parent; |
| 92 | std::size_t num_interpreted_instructions{}; | 118 | std::size_t num_interpreted_instructions{}; |
| 119 | static constexpr u64 minimum_run_cycles = 1000U; | ||
| 93 | }; | 120 | }; |
| 94 | 121 | ||
| 95 | std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, | 122 | std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, |
| @@ -100,13 +127,31 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& | |||
| 100 | // config.page_table = &page_table.pointers; | 127 | // config.page_table = &page_table.pointers; |
| 101 | config.coprocessors[15] = cp15; | 128 | config.coprocessors[15] = cp15; |
| 102 | config.define_unpredictable_behaviour = true; | 129 | config.define_unpredictable_behaviour = true; |
| 130 | static constexpr std::size_t PAGE_BITS = 12; | ||
| 131 | static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); | ||
| 132 | config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( | ||
| 133 | page_table.pointers.data()); | ||
| 134 | config.absolute_offset_page_table = true; | ||
| 135 | config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; | ||
| 136 | config.only_detect_misalignment_via_page_table_on_page_boundary = true; | ||
| 137 | |||
| 138 | // Multi-process state | ||
| 139 | config.processor_id = core_index; | ||
| 140 | config.global_monitor = &exclusive_monitor.monitor; | ||
| 141 | |||
| 142 | // Timing | ||
| 143 | config.wall_clock_cntpct = uses_wall_clock; | ||
| 144 | |||
| 145 | // Optimizations | ||
| 146 | if (Settings::values.disable_cpu_opt) { | ||
| 147 | config.enable_optimizations = false; | ||
| 148 | config.enable_fast_dispatch = false; | ||
| 149 | } | ||
| 150 | |||
| 103 | return std::make_unique<Dynarmic::A32::Jit>(config); | 151 | return std::make_unique<Dynarmic::A32::Jit>(config); |
| 104 | } | 152 | } |
| 105 | 153 | ||
| 106 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_32, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); | ||
| 107 | |||
| 108 | void ARM_Dynarmic_32::Run() { | 154 | void ARM_Dynarmic_32::Run() { |
| 109 | MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_32); | ||
| 110 | jit->Run(); | 155 | jit->Run(); |
| 111 | } | 156 | } |
| 112 | 157 | ||
| @@ -114,9 +159,11 @@ void ARM_Dynarmic_32::Step() { | |||
| 114 | jit->Step(); | 159 | jit->Step(); |
| 115 | } | 160 | } |
| 116 | 161 | ||
| 117 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, | 162 | ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, |
| 163 | bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, | ||
| 118 | std::size_t core_index) | 164 | std::size_t core_index) |
| 119 | : ARM_Interface{system}, cb(std::make_unique<DynarmicCallbacks32>(*this)), | 165 | : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, |
| 166 | cb(std::make_unique<DynarmicCallbacks32>(*this)), | ||
| 120 | cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index}, | 167 | cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index}, |
| 121 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} | 168 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} |
| 122 | 169 | ||
| @@ -168,17 +215,25 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { | |||
| 168 | cp15->uprw = static_cast<u32>(value); | 215 | cp15->uprw = static_cast<u32>(value); |
| 169 | } | 216 | } |
| 170 | 217 | ||
| 218 | void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) { | ||
| 219 | jit->ChangeProcessorID(new_core_id); | ||
| 220 | } | ||
| 221 | |||
| 171 | void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { | 222 | void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { |
| 172 | Dynarmic::A32::Context context; | 223 | Dynarmic::A32::Context context; |
| 173 | jit->SaveContext(context); | 224 | jit->SaveContext(context); |
| 174 | ctx.cpu_registers = context.Regs(); | 225 | ctx.cpu_registers = context.Regs(); |
| 226 | ctx.extension_registers = context.ExtRegs(); | ||
| 175 | ctx.cpsr = context.Cpsr(); | 227 | ctx.cpsr = context.Cpsr(); |
| 228 | ctx.fpscr = context.Fpscr(); | ||
| 176 | } | 229 | } |
| 177 | 230 | ||
| 178 | void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { | 231 | void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { |
| 179 | Dynarmic::A32::Context context; | 232 | Dynarmic::A32::Context context; |
| 180 | context.Regs() = ctx.cpu_registers; | 233 | context.Regs() = ctx.cpu_registers; |
| 234 | context.ExtRegs() = ctx.extension_registers; | ||
| 181 | context.SetCpsr(ctx.cpsr); | 235 | context.SetCpsr(ctx.cpsr); |
| 236 | context.SetFpscr(ctx.fpscr); | ||
| 182 | jit->LoadContext(context); | 237 | jit->LoadContext(context); |
| 183 | } | 238 | } |
| 184 | 239 | ||
| @@ -187,10 +242,15 @@ void ARM_Dynarmic_32::PrepareReschedule() { | |||
| 187 | } | 242 | } |
| 188 | 243 | ||
| 189 | void ARM_Dynarmic_32::ClearInstructionCache() { | 244 | void ARM_Dynarmic_32::ClearInstructionCache() { |
| 245 | if (!jit) { | ||
| 246 | return; | ||
| 247 | } | ||
| 190 | jit->ClearCache(); | 248 | jit->ClearCache(); |
| 191 | } | 249 | } |
| 192 | 250 | ||
| 193 | void ARM_Dynarmic_32::ClearExclusiveState() {} | 251 | void ARM_Dynarmic_32::ClearExclusiveState() { |
| 252 | jit->ClearExclusiveState(); | ||
| 253 | } | ||
| 194 | 254 | ||
| 195 | void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, | 255 | void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, |
| 196 | std::size_t new_address_space_size_in_bits) { | 256 | std::size_t new_address_space_size_in_bits) { |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index e5b92d7bb..2bab31b92 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include <dynarmic/A32/a32.h> | 10 | #include <dynarmic/A32/a32.h> |
| 11 | #include <dynarmic/A64/a64.h> | 11 | #include <dynarmic/A64/a64.h> |
| 12 | #include <dynarmic/A64/exclusive_monitor.h> | 12 | #include <dynarmic/exclusive_monitor.h> |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/hash.h" | 14 | #include "common/hash.h" |
| 15 | #include "core/arm/arm_interface.h" | 15 | #include "core/arm/arm_interface.h" |
| @@ -21,6 +21,7 @@ class Memory; | |||
| 21 | 21 | ||
| 22 | namespace Core { | 22 | namespace Core { |
| 23 | 23 | ||
| 24 | class CPUInterruptHandler; | ||
| 24 | class DynarmicCallbacks32; | 25 | class DynarmicCallbacks32; |
| 25 | class DynarmicCP15; | 26 | class DynarmicCP15; |
| 26 | class DynarmicExclusiveMonitor; | 27 | class DynarmicExclusiveMonitor; |
| @@ -28,7 +29,8 @@ class System; | |||
| 28 | 29 | ||
| 29 | class ARM_Dynarmic_32 final : public ARM_Interface { | 30 | class ARM_Dynarmic_32 final : public ARM_Interface { |
| 30 | public: | 31 | public: |
| 31 | ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | 32 | ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, |
| 33 | ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | ||
| 32 | ~ARM_Dynarmic_32() override; | 34 | ~ARM_Dynarmic_32() override; |
| 33 | 35 | ||
| 34 | void SetPC(u64 pc) override; | 36 | void SetPC(u64 pc) override; |
| @@ -45,6 +47,7 @@ public: | |||
| 45 | void SetTlsAddress(VAddr address) override; | 47 | void SetTlsAddress(VAddr address) override; |
| 46 | void SetTPIDR_EL0(u64 value) override; | 48 | void SetTPIDR_EL0(u64 value) override; |
| 47 | u64 GetTPIDR_EL0() const override; | 49 | u64 GetTPIDR_EL0() const override; |
| 50 | void ChangeProcessorID(std::size_t new_core_id) override; | ||
| 48 | 51 | ||
| 49 | void SaveContext(ThreadContext32& ctx) override; | 52 | void SaveContext(ThreadContext32& ctx) override; |
| 50 | void SaveContext(ThreadContext64& ctx) override {} | 53 | void SaveContext(ThreadContext64& ctx) override {} |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 337b97be9..790981034 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -7,11 +7,11 @@ | |||
| 7 | #include <dynarmic/A64/a64.h> | 7 | #include <dynarmic/A64/a64.h> |
| 8 | #include <dynarmic/A64/config.h> | 8 | #include <dynarmic/A64/config.h> |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/microprofile.h" | ||
| 11 | #include "common/page_table.h" | 10 | #include "common/page_table.h" |
| 11 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 12 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 12 | #include "core/arm/dynarmic/arm_dynarmic_64.h" |
| 13 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" | ||
| 13 | #include "core/core.h" | 14 | #include "core/core.h" |
| 14 | #include "core/core_manager.h" | ||
| 15 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 16 | #include "core/core_timing_util.h" | 16 | #include "core/core_timing_util.h" |
| 17 | #include "core/gdbstub/gdbstub.h" | 17 | #include "core/gdbstub/gdbstub.h" |
| @@ -65,6 +65,22 @@ public: | |||
| 65 | memory.Write64(vaddr + 8, value[1]); | 65 | memory.Write64(vaddr + 8, value[1]); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override { | ||
| 69 | return parent.system.Memory().WriteExclusive8(vaddr, value, expected); | ||
| 70 | } | ||
| 71 | bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override { | ||
| 72 | return parent.system.Memory().WriteExclusive16(vaddr, value, expected); | ||
| 73 | } | ||
| 74 | bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override { | ||
| 75 | return parent.system.Memory().WriteExclusive32(vaddr, value, expected); | ||
| 76 | } | ||
| 77 | bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override { | ||
| 78 | return parent.system.Memory().WriteExclusive64(vaddr, value, expected); | ||
| 79 | } | ||
| 80 | bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override { | ||
| 81 | return parent.system.Memory().WriteExclusive128(vaddr, value, expected); | ||
| 82 | } | ||
| 83 | |||
| 68 | void InterpreterFallback(u64 pc, std::size_t num_instructions) override { | 84 | void InterpreterFallback(u64 pc, std::size_t num_instructions) override { |
| 69 | LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, | 85 | LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, |
| 70 | num_instructions, MemoryReadCode(pc)); | 86 | num_instructions, MemoryReadCode(pc)); |
| @@ -98,8 +114,8 @@ public: | |||
| 98 | } | 114 | } |
| 99 | [[fallthrough]]; | 115 | [[fallthrough]]; |
| 100 | default: | 116 | default: |
| 101 | ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})", | 117 | ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", |
| 102 | static_cast<std::size_t>(exception), pc); | 118 | static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); |
| 103 | } | 119 | } |
| 104 | } | 120 | } |
| 105 | 121 | ||
| @@ -108,29 +124,42 @@ public: | |||
| 108 | } | 124 | } |
| 109 | 125 | ||
| 110 | void AddTicks(u64 ticks) override { | 126 | void AddTicks(u64 ticks) override { |
| 127 | if (parent.uses_wall_clock) { | ||
| 128 | return; | ||
| 129 | } | ||
| 111 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a | 130 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a |
| 112 | // rough approximation of the amount of executed ticks in the system, it may be thrown off | 131 | // rough approximation of the amount of executed ticks in the system, it may be thrown off |
| 113 | // if not all cores are doing a similar amount of work. Instead of doing this, we should | 132 | // if not all cores are doing a similar amount of work. Instead of doing this, we should |
| 114 | // device a way so that timing is consistent across all cores without increasing the ticks 4 | 133 | // device a way so that timing is consistent across all cores without increasing the ticks 4 |
| 115 | // times. | 134 | // times. |
| 116 | u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; | 135 | u64 amortized_ticks = |
| 136 | (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES; | ||
| 117 | // Always execute at least one tick. | 137 | // Always execute at least one tick. |
| 118 | amortized_ticks = std::max<u64>(amortized_ticks, 1); | 138 | amortized_ticks = std::max<u64>(amortized_ticks, 1); |
| 119 | 139 | ||
| 120 | parent.system.CoreTiming().AddTicks(amortized_ticks); | 140 | parent.system.CoreTiming().AddTicks(amortized_ticks); |
| 121 | num_interpreted_instructions = 0; | 141 | num_interpreted_instructions = 0; |
| 122 | } | 142 | } |
| 143 | |||
| 123 | u64 GetTicksRemaining() override { | 144 | u64 GetTicksRemaining() override { |
| 124 | return std::max(parent.system.CoreTiming().GetDowncount(), s64{0}); | 145 | if (parent.uses_wall_clock) { |
| 146 | if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { | ||
| 147 | return minimum_run_cycles; | ||
| 148 | } | ||
| 149 | return 0U; | ||
| 150 | } | ||
| 151 | return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); | ||
| 125 | } | 152 | } |
| 153 | |||
| 126 | u64 GetCNTPCT() override { | 154 | u64 GetCNTPCT() override { |
| 127 | return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); | 155 | return parent.system.CoreTiming().GetClockTicks(); |
| 128 | } | 156 | } |
| 129 | 157 | ||
| 130 | ARM_Dynarmic_64& parent; | 158 | ARM_Dynarmic_64& parent; |
| 131 | std::size_t num_interpreted_instructions = 0; | 159 | std::size_t num_interpreted_instructions = 0; |
| 132 | u64 tpidrro_el0 = 0; | 160 | u64 tpidrro_el0 = 0; |
| 133 | u64 tpidr_el0 = 0; | 161 | u64 tpidr_el0 = 0; |
| 162 | static constexpr u64 minimum_run_cycles = 1000U; | ||
| 134 | }; | 163 | }; |
| 135 | 164 | ||
| 136 | std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, | 165 | std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, |
| @@ -168,14 +197,13 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& | |||
| 168 | config.enable_fast_dispatch = false; | 197 | config.enable_fast_dispatch = false; |
| 169 | } | 198 | } |
| 170 | 199 | ||
| 200 | // Timing | ||
| 201 | config.wall_clock_cntpct = uses_wall_clock; | ||
| 202 | |||
| 171 | return std::make_shared<Dynarmic::A64::Jit>(config); | 203 | return std::make_shared<Dynarmic::A64::Jit>(config); |
| 172 | } | 204 | } |
| 173 | 205 | ||
| 174 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_64, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); | ||
| 175 | |||
| 176 | void ARM_Dynarmic_64::Run() { | 206 | void ARM_Dynarmic_64::Run() { |
| 177 | MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_64); | ||
| 178 | |||
| 179 | jit->Run(); | 207 | jit->Run(); |
| 180 | } | 208 | } |
| 181 | 209 | ||
| @@ -183,11 +211,16 @@ void ARM_Dynarmic_64::Step() { | |||
| 183 | cb->InterpreterFallback(jit->GetPC(), 1); | 211 | cb->InterpreterFallback(jit->GetPC(), 1); |
| 184 | } | 212 | } |
| 185 | 213 | ||
| 186 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, | 214 | ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, |
| 215 | bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor, | ||
| 187 | std::size_t core_index) | 216 | std::size_t core_index) |
| 188 | : ARM_Interface{system}, cb(std::make_unique<DynarmicCallbacks64>(*this)), | 217 | : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, |
| 189 | inner_unicorn{system, ARM_Unicorn::Arch::AArch64}, core_index{core_index}, | 218 | cb(std::make_unique<DynarmicCallbacks64>(*this)), inner_unicorn{system, interrupt_handlers, |
| 190 | exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} | 219 | uses_wall_clock, |
| 220 | ARM_Unicorn::Arch::AArch64, | ||
| 221 | core_index}, | ||
| 222 | core_index{core_index}, exclusive_monitor{ | ||
| 223 | dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} | ||
| 191 | 224 | ||
| 192 | ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; | 225 | ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; |
| 193 | 226 | ||
| @@ -239,6 +272,10 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { | |||
| 239 | cb->tpidr_el0 = value; | 272 | cb->tpidr_el0 = value; |
| 240 | } | 273 | } |
| 241 | 274 | ||
| 275 | void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) { | ||
| 276 | jit->ChangeProcessorID(new_core_id); | ||
| 277 | } | ||
| 278 | |||
| 242 | void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { | 279 | void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { |
| 243 | ctx.cpu_registers = jit->GetRegisters(); | 280 | ctx.cpu_registers = jit->GetRegisters(); |
| 244 | ctx.sp = jit->GetSP(); | 281 | ctx.sp = jit->GetSP(); |
| @@ -266,6 +303,9 @@ void ARM_Dynarmic_64::PrepareReschedule() { | |||
| 266 | } | 303 | } |
| 267 | 304 | ||
| 268 | void ARM_Dynarmic_64::ClearInstructionCache() { | 305 | void ARM_Dynarmic_64::ClearInstructionCache() { |
| 306 | if (!jit) { | ||
| 307 | return; | ||
| 308 | } | ||
| 269 | jit->ClearCache(); | 309 | jit->ClearCache(); |
| 270 | } | 310 | } |
| 271 | 311 | ||
| @@ -285,44 +325,4 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, | |||
| 285 | jit_cache.emplace(key, jit); | 325 | jit_cache.emplace(key, jit); |
| 286 | } | 326 | } |
| 287 | 327 | ||
| 288 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count) | ||
| 289 | : monitor(core_count), memory{memory} {} | ||
| 290 | |||
| 291 | DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; | ||
| 292 | |||
| 293 | void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) { | ||
| 294 | // Size doesn't actually matter. | ||
| 295 | monitor.Mark(core_index, addr, 16); | ||
| 296 | } | ||
| 297 | |||
| 298 | void DynarmicExclusiveMonitor::ClearExclusive() { | ||
| 299 | monitor.Clear(); | ||
| 300 | } | ||
| 301 | |||
| 302 | bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { | ||
| 303 | return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); }); | ||
| 304 | } | ||
| 305 | |||
| 306 | bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { | ||
| 307 | return monitor.DoExclusiveOperation(core_index, vaddr, 2, | ||
| 308 | [&] { memory.Write16(vaddr, value); }); | ||
| 309 | } | ||
| 310 | |||
| 311 | bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { | ||
| 312 | return monitor.DoExclusiveOperation(core_index, vaddr, 4, | ||
| 313 | [&] { memory.Write32(vaddr, value); }); | ||
| 314 | } | ||
| 315 | |||
| 316 | bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { | ||
| 317 | return monitor.DoExclusiveOperation(core_index, vaddr, 8, | ||
| 318 | [&] { memory.Write64(vaddr, value); }); | ||
| 319 | } | ||
| 320 | |||
| 321 | bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { | ||
| 322 | return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { | ||
| 323 | memory.Write64(vaddr + 0, value[0]); | ||
| 324 | memory.Write64(vaddr + 8, value[1]); | ||
| 325 | }); | ||
| 326 | } | ||
| 327 | |||
| 328 | } // namespace Core | 328 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 647cecaf0..403c55961 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include <unordered_map> | 8 | #include <unordered_map> |
| 9 | 9 | ||
| 10 | #include <dynarmic/A64/a64.h> | 10 | #include <dynarmic/A64/a64.h> |
| 11 | #include <dynarmic/A64/exclusive_monitor.h> | ||
| 12 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 13 | #include "common/hash.h" | 12 | #include "common/hash.h" |
| 14 | #include "core/arm/arm_interface.h" | 13 | #include "core/arm/arm_interface.h" |
| @@ -22,12 +21,14 @@ class Memory; | |||
| 22 | namespace Core { | 21 | namespace Core { |
| 23 | 22 | ||
| 24 | class DynarmicCallbacks64; | 23 | class DynarmicCallbacks64; |
| 24 | class CPUInterruptHandler; | ||
| 25 | class DynarmicExclusiveMonitor; | 25 | class DynarmicExclusiveMonitor; |
| 26 | class System; | 26 | class System; |
| 27 | 27 | ||
| 28 | class ARM_Dynarmic_64 final : public ARM_Interface { | 28 | class ARM_Dynarmic_64 final : public ARM_Interface { |
| 29 | public: | 29 | public: |
| 30 | ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | 30 | ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, |
| 31 | ExclusiveMonitor& exclusive_monitor, std::size_t core_index); | ||
| 31 | ~ARM_Dynarmic_64() override; | 32 | ~ARM_Dynarmic_64() override; |
| 32 | 33 | ||
| 33 | void SetPC(u64 pc) override; | 34 | void SetPC(u64 pc) override; |
| @@ -44,6 +45,7 @@ public: | |||
| 44 | void SetTlsAddress(VAddr address) override; | 45 | void SetTlsAddress(VAddr address) override; |
| 45 | void SetTPIDR_EL0(u64 value) override; | 46 | void SetTPIDR_EL0(u64 value) override; |
| 46 | u64 GetTPIDR_EL0() const override; | 47 | u64 GetTPIDR_EL0() const override; |
| 48 | void ChangeProcessorID(std::size_t new_core_id) override; | ||
| 47 | 49 | ||
| 48 | void SaveContext(ThreadContext32& ctx) override {} | 50 | void SaveContext(ThreadContext32& ctx) override {} |
| 49 | void SaveContext(ThreadContext64& ctx) override; | 51 | void SaveContext(ThreadContext64& ctx) override; |
| @@ -75,24 +77,4 @@ private: | |||
| 75 | DynarmicExclusiveMonitor& exclusive_monitor; | 77 | DynarmicExclusiveMonitor& exclusive_monitor; |
| 76 | }; | 78 | }; |
| 77 | 79 | ||
| 78 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { | ||
| 79 | public: | ||
| 80 | explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count); | ||
| 81 | ~DynarmicExclusiveMonitor() override; | ||
| 82 | |||
| 83 | void SetExclusive(std::size_t core_index, VAddr addr) override; | ||
| 84 | void ClearExclusive() override; | ||
| 85 | |||
| 86 | bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; | ||
| 87 | bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; | ||
| 88 | bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override; | ||
| 89 | bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override; | ||
| 90 | bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; | ||
| 91 | |||
| 92 | private: | ||
| 93 | friend class ARM_Dynarmic_64; | ||
| 94 | Dynarmic::A64::ExclusiveMonitor monitor; | ||
| 95 | Core::Memory::Memory& memory; | ||
| 96 | }; | ||
| 97 | |||
| 98 | } // namespace Core | 80 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index d43e4dd70..54556e0f9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp | |||
| @@ -97,7 +97,7 @@ CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc | |||
| 97 | const auto callback = static_cast<u64 (*)(Dynarmic::A32::Jit*, void*, u32, u32)>( | 97 | const auto callback = static_cast<u64 (*)(Dynarmic::A32::Jit*, void*, u32, u32)>( |
| 98 | [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 { | 98 | [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 { |
| 99 | ARM_Dynarmic_32& parent = *(ARM_Dynarmic_32*)arg; | 99 | ARM_Dynarmic_32& parent = *(ARM_Dynarmic_32*)arg; |
| 100 | return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); | 100 | return parent.system.CoreTiming().GetClockTicks(); |
| 101 | }); | 101 | }); |
| 102 | return Dynarmic::A32::Coprocessor::Callback{callback, (void*)&parent}; | 102 | return Dynarmic::A32::Coprocessor::Callback{callback, (void*)&parent}; |
| 103 | } | 103 | } |
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp new file mode 100644 index 000000000..4e209f6a5 --- /dev/null +++ b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cinttypes> | ||
| 6 | #include <memory> | ||
| 7 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" | ||
| 8 | #include "core/memory.h" | ||
| 9 | |||
| 10 | namespace Core { | ||
| 11 | |||
| 12 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count) | ||
| 13 | : monitor(core_count), memory{memory} {} | ||
| 14 | |||
| 15 | DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; | ||
| 16 | |||
| 17 | u8 DynarmicExclusiveMonitor::ExclusiveRead8(std::size_t core_index, VAddr addr) { | ||
| 18 | return monitor.ReadAndMark<u8>(core_index, addr, [&]() -> u8 { return memory.Read8(addr); }); | ||
| 19 | } | ||
| 20 | |||
| 21 | u16 DynarmicExclusiveMonitor::ExclusiveRead16(std::size_t core_index, VAddr addr) { | ||
| 22 | return monitor.ReadAndMark<u16>(core_index, addr, [&]() -> u16 { return memory.Read16(addr); }); | ||
| 23 | } | ||
| 24 | |||
| 25 | u32 DynarmicExclusiveMonitor::ExclusiveRead32(std::size_t core_index, VAddr addr) { | ||
| 26 | return monitor.ReadAndMark<u32>(core_index, addr, [&]() -> u32 { return memory.Read32(addr); }); | ||
| 27 | } | ||
| 28 | |||
| 29 | u64 DynarmicExclusiveMonitor::ExclusiveRead64(std::size_t core_index, VAddr addr) { | ||
| 30 | return monitor.ReadAndMark<u64>(core_index, addr, [&]() -> u64 { return memory.Read64(addr); }); | ||
| 31 | } | ||
| 32 | |||
| 33 | u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr addr) { | ||
| 34 | return monitor.ReadAndMark<u128>(core_index, addr, [&]() -> u128 { | ||
| 35 | u128 result; | ||
| 36 | result[0] = memory.Read64(addr); | ||
| 37 | result[1] = memory.Read64(addr + 8); | ||
| 38 | return result; | ||
| 39 | }); | ||
| 40 | } | ||
| 41 | |||
| 42 | void DynarmicExclusiveMonitor::ClearExclusive() { | ||
| 43 | monitor.Clear(); | ||
| 44 | } | ||
| 45 | |||
| 46 | bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { | ||
| 47 | return monitor.DoExclusiveOperation<u8>(core_index, vaddr, [&](u8 expected) -> bool { | ||
| 48 | return memory.WriteExclusive8(vaddr, value, expected); | ||
| 49 | }); | ||
| 50 | } | ||
| 51 | |||
| 52 | bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { | ||
| 53 | return monitor.DoExclusiveOperation<u16>(core_index, vaddr, [&](u16 expected) -> bool { | ||
| 54 | return memory.WriteExclusive16(vaddr, value, expected); | ||
| 55 | }); | ||
| 56 | } | ||
| 57 | |||
| 58 | bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { | ||
| 59 | return monitor.DoExclusiveOperation<u32>(core_index, vaddr, [&](u32 expected) -> bool { | ||
| 60 | return memory.WriteExclusive32(vaddr, value, expected); | ||
| 61 | }); | ||
| 62 | } | ||
| 63 | |||
| 64 | bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { | ||
| 65 | return monitor.DoExclusiveOperation<u64>(core_index, vaddr, [&](u64 expected) -> bool { | ||
| 66 | return memory.WriteExclusive64(vaddr, value, expected); | ||
| 67 | }); | ||
| 68 | } | ||
| 69 | |||
| 70 | bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { | ||
| 71 | return monitor.DoExclusiveOperation<u128>(core_index, vaddr, [&](u128 expected) -> bool { | ||
| 72 | return memory.WriteExclusive128(vaddr, value, expected); | ||
| 73 | }); | ||
| 74 | } | ||
| 75 | |||
| 76 | } // namespace Core | ||
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h new file mode 100644 index 000000000..964f4a55d --- /dev/null +++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <unordered_map> | ||
| 9 | |||
| 10 | #include <dynarmic/exclusive_monitor.h> | ||
| 11 | |||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | ||
| 14 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | ||
| 15 | #include "core/arm/exclusive_monitor.h" | ||
| 16 | |||
| 17 | namespace Core::Memory { | ||
| 18 | class Memory; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace Core { | ||
| 22 | |||
| 23 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { | ||
| 24 | public: | ||
| 25 | explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count); | ||
| 26 | ~DynarmicExclusiveMonitor() override; | ||
| 27 | |||
| 28 | u8 ExclusiveRead8(std::size_t core_index, VAddr addr) override; | ||
| 29 | u16 ExclusiveRead16(std::size_t core_index, VAddr addr) override; | ||
| 30 | u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override; | ||
| 31 | u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override; | ||
| 32 | u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override; | ||
| 33 | void ClearExclusive() override; | ||
| 34 | |||
| 35 | bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; | ||
| 36 | bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; | ||
| 37 | bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override; | ||
| 38 | bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override; | ||
| 39 | bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; | ||
| 40 | |||
| 41 | private: | ||
| 42 | friend class ARM_Dynarmic_32; | ||
| 43 | friend class ARM_Dynarmic_64; | ||
| 44 | Dynarmic::ExclusiveMonitor monitor; | ||
| 45 | Core::Memory::Memory& memory; | ||
| 46 | }; | ||
| 47 | |||
| 48 | } // namespace Core | ||
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index b32401e0b..d8cba369d 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #ifdef ARCHITECTURE_x86_64 | 5 | #ifdef ARCHITECTURE_x86_64 |
| 6 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 6 | #include "core/arm/dynarmic/arm_exclusive_monitor.h" |
| 7 | #endif | 7 | #endif |
| 8 | #include "core/arm/exclusive_monitor.h" | 8 | #include "core/arm/exclusive_monitor.h" |
| 9 | #include "core/memory.h" | 9 | #include "core/memory.h" |
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index ccd73b80f..62f6e6023 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h | |||
| @@ -18,7 +18,11 @@ class ExclusiveMonitor { | |||
| 18 | public: | 18 | public: |
| 19 | virtual ~ExclusiveMonitor(); | 19 | virtual ~ExclusiveMonitor(); |
| 20 | 20 | ||
| 21 | virtual void SetExclusive(std::size_t core_index, VAddr addr) = 0; | 21 | virtual u8 ExclusiveRead8(std::size_t core_index, VAddr addr) = 0; |
| 22 | virtual u16 ExclusiveRead16(std::size_t core_index, VAddr addr) = 0; | ||
| 23 | virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0; | ||
| 24 | virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0; | ||
| 25 | virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0; | ||
| 22 | virtual void ClearExclusive() = 0; | 26 | virtual void ClearExclusive() = 0; |
| 23 | 27 | ||
| 24 | virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; | 28 | virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; |
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index e40e9626a..1df3f3ed1 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <unicorn/arm64.h> | 6 | #include <unicorn/arm64.h> |
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/microprofile.h" | 8 | #include "common/microprofile.h" |
| 9 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 9 | #include "core/arm/unicorn/arm_unicorn.h" | 10 | #include "core/arm/unicorn/arm_unicorn.h" |
| 10 | #include "core/core.h" | 11 | #include "core/core.h" |
| 11 | #include "core/core_timing.h" | 12 | #include "core/core_timing.h" |
| @@ -62,7 +63,9 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si | |||
| 62 | return false; | 63 | return false; |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | ARM_Unicorn::ARM_Unicorn(System& system, Arch architecture) : ARM_Interface{system} { | 66 | ARM_Unicorn::ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, |
| 67 | Arch architecture, std::size_t core_index) | ||
| 68 | : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, core_index{core_index} { | ||
| 66 | const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; | 69 | const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; |
| 67 | CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); | 70 | CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); |
| 68 | 71 | ||
| @@ -156,12 +159,20 @@ void ARM_Unicorn::SetTPIDR_EL0(u64 value) { | |||
| 156 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value)); | 159 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value)); |
| 157 | } | 160 | } |
| 158 | 161 | ||
| 162 | void ARM_Unicorn::ChangeProcessorID(std::size_t new_core_id) { | ||
| 163 | core_index = new_core_id; | ||
| 164 | } | ||
| 165 | |||
| 159 | void ARM_Unicorn::Run() { | 166 | void ARM_Unicorn::Run() { |
| 160 | if (GDBStub::IsServerEnabled()) { | 167 | if (GDBStub::IsServerEnabled()) { |
| 161 | ExecuteInstructions(std::max(4000000U, 0U)); | 168 | ExecuteInstructions(std::max(4000000U, 0U)); |
| 162 | } else { | 169 | } else { |
| 163 | ExecuteInstructions( | 170 | while (true) { |
| 164 | std::max(std::size_t(system.CoreTiming().GetDowncount()), std::size_t{0})); | 171 | if (interrupt_handlers[core_index].IsInterrupted()) { |
| 172 | return; | ||
| 173 | } | ||
| 174 | ExecuteInstructions(10); | ||
| 175 | } | ||
| 165 | } | 176 | } |
| 166 | } | 177 | } |
| 167 | 178 | ||
| @@ -183,8 +194,6 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) { | |||
| 183 | UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data())); | 194 | UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data())); |
| 184 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); | 195 | CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); |
| 185 | CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size())); | 196 | CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size())); |
| 186 | |||
| 187 | system.CoreTiming().AddTicks(num_instructions); | ||
| 188 | if (GDBStub::IsServerEnabled()) { | 197 | if (GDBStub::IsServerEnabled()) { |
| 189 | if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { | 198 | if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { |
| 190 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); | 199 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); |
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 725c65085..810aff311 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h | |||
| @@ -20,7 +20,8 @@ public: | |||
| 20 | AArch64, // 64-bit ARM | 20 | AArch64, // 64-bit ARM |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| 23 | explicit ARM_Unicorn(System& system, Arch architecture); | 23 | explicit ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock, |
| 24 | Arch architecture, std::size_t core_index); | ||
| 24 | ~ARM_Unicorn() override; | 25 | ~ARM_Unicorn() override; |
| 25 | 26 | ||
| 26 | void SetPC(u64 pc) override; | 27 | void SetPC(u64 pc) override; |
| @@ -35,6 +36,7 @@ public: | |||
| 35 | void SetTlsAddress(VAddr address) override; | 36 | void SetTlsAddress(VAddr address) override; |
| 36 | void SetTPIDR_EL0(u64 value) override; | 37 | void SetTPIDR_EL0(u64 value) override; |
| 37 | u64 GetTPIDR_EL0() const override; | 38 | u64 GetTPIDR_EL0() const override; |
| 39 | void ChangeProcessorID(std::size_t new_core_id) override; | ||
| 38 | void PrepareReschedule() override; | 40 | void PrepareReschedule() override; |
| 39 | void ClearExclusiveState() override; | 41 | void ClearExclusiveState() override; |
| 40 | void ExecuteInstructions(std::size_t num_instructions); | 42 | void ExecuteInstructions(std::size_t num_instructions); |
| @@ -55,6 +57,7 @@ private: | |||
| 55 | uc_engine* uc{}; | 57 | uc_engine* uc{}; |
| 56 | GDBStub::BreakpointAddress last_bkpt{}; | 58 | GDBStub::BreakpointAddress last_bkpt{}; |
| 57 | bool last_bkpt_hit = false; | 59 | bool last_bkpt_hit = false; |
| 60 | std::size_t core_index; | ||
| 58 | }; | 61 | }; |
| 59 | 62 | ||
| 60 | } // namespace Core | 63 | } // namespace Core |
diff --git a/src/core/core.cpp b/src/core/core.cpp index f9f8a3000..1a243c515 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -8,10 +8,10 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/microprofile.h" | ||
| 11 | #include "common/string_util.h" | 12 | #include "common/string_util.h" |
| 12 | #include "core/arm/exclusive_monitor.h" | 13 | #include "core/arm/exclusive_monitor.h" |
| 13 | #include "core/core.h" | 14 | #include "core/core.h" |
| 14 | #include "core/core_manager.h" | ||
| 15 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 16 | #include "core/cpu_manager.h" | 16 | #include "core/cpu_manager.h" |
| 17 | #include "core/device_memory.h" | 17 | #include "core/device_memory.h" |
| @@ -51,6 +51,11 @@ | |||
| 51 | #include "video_core/renderer_base.h" | 51 | #include "video_core/renderer_base.h" |
| 52 | #include "video_core/video_core.h" | 52 | #include "video_core/video_core.h" |
| 53 | 53 | ||
| 54 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU0, "ARM JIT", "Dynarmic CPU 0", MP_RGB(255, 64, 64)); | ||
| 55 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU1, "ARM JIT", "Dynarmic CPU 1", MP_RGB(255, 64, 64)); | ||
| 56 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU2, "ARM JIT", "Dynarmic CPU 2", MP_RGB(255, 64, 64)); | ||
| 57 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU3, "ARM JIT", "Dynarmic CPU 3", MP_RGB(255, 64, 64)); | ||
| 58 | |||
| 54 | namespace Core { | 59 | namespace Core { |
| 55 | 60 | ||
| 56 | namespace { | 61 | namespace { |
| @@ -117,23 +122,22 @@ struct System::Impl { | |||
| 117 | : kernel{system}, fs_controller{system}, memory{system}, | 122 | : kernel{system}, fs_controller{system}, memory{system}, |
| 118 | cpu_manager{system}, reporter{system}, applet_manager{system} {} | 123 | cpu_manager{system}, reporter{system}, applet_manager{system} {} |
| 119 | 124 | ||
| 120 | CoreManager& CurrentCoreManager() { | 125 | ResultStatus Run() { |
| 121 | return cpu_manager.GetCurrentCoreManager(); | 126 | status = ResultStatus::Success; |
| 122 | } | ||
| 123 | 127 | ||
| 124 | Kernel::PhysicalCore& CurrentPhysicalCore() { | 128 | kernel.Suspend(false); |
| 125 | const auto index = cpu_manager.GetActiveCoreIndex(); | 129 | core_timing.SyncPause(false); |
| 126 | return kernel.PhysicalCore(index); | 130 | cpu_manager.Pause(false); |
| 127 | } | ||
| 128 | 131 | ||
| 129 | Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) { | 132 | return status; |
| 130 | return kernel.PhysicalCore(index); | ||
| 131 | } | 133 | } |
| 132 | 134 | ||
| 133 | ResultStatus RunLoop(bool tight_loop) { | 135 | ResultStatus Pause() { |
| 134 | status = ResultStatus::Success; | 136 | status = ResultStatus::Success; |
| 135 | 137 | ||
| 136 | cpu_manager.RunLoop(tight_loop); | 138 | core_timing.SyncPause(true); |
| 139 | kernel.Suspend(true); | ||
| 140 | cpu_manager.Pause(true); | ||
| 137 | 141 | ||
| 138 | return status; | 142 | return status; |
| 139 | } | 143 | } |
| @@ -143,7 +147,15 @@ struct System::Impl { | |||
| 143 | 147 | ||
| 144 | device_memory = std::make_unique<Core::DeviceMemory>(system); | 148 | device_memory = std::make_unique<Core::DeviceMemory>(system); |
| 145 | 149 | ||
| 146 | core_timing.Initialize(); | 150 | is_multicore = Settings::values.use_multi_core; |
| 151 | is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation; | ||
| 152 | |||
| 153 | kernel.SetMulticore(is_multicore); | ||
| 154 | cpu_manager.SetMulticore(is_multicore); | ||
| 155 | cpu_manager.SetAsyncGpu(is_async_gpu); | ||
| 156 | core_timing.SetMulticore(is_multicore); | ||
| 157 | |||
| 158 | core_timing.Initialize([&system]() { system.RegisterHostThread(); }); | ||
| 147 | kernel.Initialize(); | 159 | kernel.Initialize(); |
| 148 | cpu_manager.Initialize(); | 160 | cpu_manager.Initialize(); |
| 149 | 161 | ||
| @@ -180,6 +192,11 @@ struct System::Impl { | |||
| 180 | is_powered_on = true; | 192 | is_powered_on = true; |
| 181 | exit_lock = false; | 193 | exit_lock = false; |
| 182 | 194 | ||
| 195 | microprofile_dynarmic[0] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU0); | ||
| 196 | microprofile_dynarmic[1] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU1); | ||
| 197 | microprofile_dynarmic[2] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU2); | ||
| 198 | microprofile_dynarmic[3] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU3); | ||
| 199 | |||
| 183 | LOG_DEBUG(Core, "Initialized OK"); | 200 | LOG_DEBUG(Core, "Initialized OK"); |
| 184 | 201 | ||
| 185 | return ResultStatus::Success; | 202 | return ResultStatus::Success; |
| @@ -277,8 +294,6 @@ struct System::Impl { | |||
| 277 | service_manager.reset(); | 294 | service_manager.reset(); |
| 278 | cheat_engine.reset(); | 295 | cheat_engine.reset(); |
| 279 | telemetry_session.reset(); | 296 | telemetry_session.reset(); |
| 280 | perf_stats.reset(); | ||
| 281 | gpu_core.reset(); | ||
| 282 | device_memory.reset(); | 297 | device_memory.reset(); |
| 283 | 298 | ||
| 284 | // Close all CPU/threading state | 299 | // Close all CPU/threading state |
| @@ -290,6 +305,8 @@ struct System::Impl { | |||
| 290 | 305 | ||
| 291 | // Close app loader | 306 | // Close app loader |
| 292 | app_loader.reset(); | 307 | app_loader.reset(); |
| 308 | gpu_core.reset(); | ||
| 309 | perf_stats.reset(); | ||
| 293 | 310 | ||
| 294 | // Clear all applets | 311 | // Clear all applets |
| 295 | applet_manager.ClearAll(); | 312 | applet_manager.ClearAll(); |
| @@ -382,25 +399,35 @@ struct System::Impl { | |||
| 382 | 399 | ||
| 383 | std::unique_ptr<Core::PerfStats> perf_stats; | 400 | std::unique_ptr<Core::PerfStats> perf_stats; |
| 384 | Core::FrameLimiter frame_limiter; | 401 | Core::FrameLimiter frame_limiter; |
| 402 | |||
| 403 | bool is_multicore{}; | ||
| 404 | bool is_async_gpu{}; | ||
| 405 | |||
| 406 | std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; | ||
| 407 | std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{}; | ||
| 385 | }; | 408 | }; |
| 386 | 409 | ||
| 387 | System::System() : impl{std::make_unique<Impl>(*this)} {} | 410 | System::System() : impl{std::make_unique<Impl>(*this)} {} |
| 388 | System::~System() = default; | 411 | System::~System() = default; |
| 389 | 412 | ||
| 390 | CoreManager& System::CurrentCoreManager() { | 413 | CpuManager& System::GetCpuManager() { |
| 391 | return impl->CurrentCoreManager(); | 414 | return impl->cpu_manager; |
| 415 | } | ||
| 416 | |||
| 417 | const CpuManager& System::GetCpuManager() const { | ||
| 418 | return impl->cpu_manager; | ||
| 392 | } | 419 | } |
| 393 | 420 | ||
| 394 | const CoreManager& System::CurrentCoreManager() const { | 421 | System::ResultStatus System::Run() { |
| 395 | return impl->CurrentCoreManager(); | 422 | return impl->Run(); |
| 396 | } | 423 | } |
| 397 | 424 | ||
| 398 | System::ResultStatus System::RunLoop(bool tight_loop) { | 425 | System::ResultStatus System::Pause() { |
| 399 | return impl->RunLoop(tight_loop); | 426 | return impl->Pause(); |
| 400 | } | 427 | } |
| 401 | 428 | ||
| 402 | System::ResultStatus System::SingleStep() { | 429 | System::ResultStatus System::SingleStep() { |
| 403 | return RunLoop(false); | 430 | return ResultStatus::Success; |
| 404 | } | 431 | } |
| 405 | 432 | ||
| 406 | void System::InvalidateCpuInstructionCaches() { | 433 | void System::InvalidateCpuInstructionCaches() { |
| @@ -416,7 +443,7 @@ bool System::IsPoweredOn() const { | |||
| 416 | } | 443 | } |
| 417 | 444 | ||
| 418 | void System::PrepareReschedule() { | 445 | void System::PrepareReschedule() { |
| 419 | impl->CurrentPhysicalCore().Stop(); | 446 | // Deprecated, does nothing, kept for backward compatibility. |
| 420 | } | 447 | } |
| 421 | 448 | ||
| 422 | void System::PrepareReschedule(const u32 core_index) { | 449 | void System::PrepareReschedule(const u32 core_index) { |
| @@ -436,31 +463,41 @@ const TelemetrySession& System::TelemetrySession() const { | |||
| 436 | } | 463 | } |
| 437 | 464 | ||
| 438 | ARM_Interface& System::CurrentArmInterface() { | 465 | ARM_Interface& System::CurrentArmInterface() { |
| 439 | return impl->CurrentPhysicalCore().ArmInterface(); | 466 | return impl->kernel.CurrentScheduler().GetCurrentThread()->ArmInterface(); |
| 440 | } | 467 | } |
| 441 | 468 | ||
| 442 | const ARM_Interface& System::CurrentArmInterface() const { | 469 | const ARM_Interface& System::CurrentArmInterface() const { |
| 443 | return impl->CurrentPhysicalCore().ArmInterface(); | 470 | return impl->kernel.CurrentScheduler().GetCurrentThread()->ArmInterface(); |
| 444 | } | 471 | } |
| 445 | 472 | ||
| 446 | std::size_t System::CurrentCoreIndex() const { | 473 | std::size_t System::CurrentCoreIndex() const { |
| 447 | return impl->cpu_manager.GetActiveCoreIndex(); | 474 | std::size_t core = impl->kernel.GetCurrentHostThreadID(); |
| 475 | ASSERT(core < Core::Hardware::NUM_CPU_CORES); | ||
| 476 | return core; | ||
| 448 | } | 477 | } |
| 449 | 478 | ||
| 450 | Kernel::Scheduler& System::CurrentScheduler() { | 479 | Kernel::Scheduler& System::CurrentScheduler() { |
| 451 | return impl->CurrentPhysicalCore().Scheduler(); | 480 | return impl->kernel.CurrentScheduler(); |
| 452 | } | 481 | } |
| 453 | 482 | ||
| 454 | const Kernel::Scheduler& System::CurrentScheduler() const { | 483 | const Kernel::Scheduler& System::CurrentScheduler() const { |
| 455 | return impl->CurrentPhysicalCore().Scheduler(); | 484 | return impl->kernel.CurrentScheduler(); |
| 485 | } | ||
| 486 | |||
| 487 | Kernel::PhysicalCore& System::CurrentPhysicalCore() { | ||
| 488 | return impl->kernel.CurrentPhysicalCore(); | ||
| 489 | } | ||
| 490 | |||
| 491 | const Kernel::PhysicalCore& System::CurrentPhysicalCore() const { | ||
| 492 | return impl->kernel.CurrentPhysicalCore(); | ||
| 456 | } | 493 | } |
| 457 | 494 | ||
| 458 | Kernel::Scheduler& System::Scheduler(std::size_t core_index) { | 495 | Kernel::Scheduler& System::Scheduler(std::size_t core_index) { |
| 459 | return impl->GetPhysicalCore(core_index).Scheduler(); | 496 | return impl->kernel.Scheduler(core_index); |
| 460 | } | 497 | } |
| 461 | 498 | ||
| 462 | const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { | 499 | const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { |
| 463 | return impl->GetPhysicalCore(core_index).Scheduler(); | 500 | return impl->kernel.Scheduler(core_index); |
| 464 | } | 501 | } |
| 465 | 502 | ||
| 466 | /// Gets the global scheduler | 503 | /// Gets the global scheduler |
| @@ -490,20 +527,15 @@ const Kernel::Process* System::CurrentProcess() const { | |||
| 490 | } | 527 | } |
| 491 | 528 | ||
| 492 | ARM_Interface& System::ArmInterface(std::size_t core_index) { | 529 | ARM_Interface& System::ArmInterface(std::size_t core_index) { |
| 493 | return impl->GetPhysicalCore(core_index).ArmInterface(); | 530 | auto* thread = impl->kernel.Scheduler(core_index).GetCurrentThread(); |
| 531 | ASSERT(thread && !thread->IsHLEThread()); | ||
| 532 | return thread->ArmInterface(); | ||
| 494 | } | 533 | } |
| 495 | 534 | ||
| 496 | const ARM_Interface& System::ArmInterface(std::size_t core_index) const { | 535 | const ARM_Interface& System::ArmInterface(std::size_t core_index) const { |
| 497 | return impl->GetPhysicalCore(core_index).ArmInterface(); | 536 | auto* thread = impl->kernel.Scheduler(core_index).GetCurrentThread(); |
| 498 | } | 537 | ASSERT(thread && !thread->IsHLEThread()); |
| 499 | 538 | return thread->ArmInterface(); | |
| 500 | CoreManager& System::GetCoreManager(std::size_t core_index) { | ||
| 501 | return impl->cpu_manager.GetCoreManager(core_index); | ||
| 502 | } | ||
| 503 | |||
| 504 | const CoreManager& System::GetCoreManager(std::size_t core_index) const { | ||
| 505 | ASSERT(core_index < NUM_CPU_CORES); | ||
| 506 | return impl->cpu_manager.GetCoreManager(core_index); | ||
| 507 | } | 539 | } |
| 508 | 540 | ||
| 509 | ExclusiveMonitor& System::Monitor() { | 541 | ExclusiveMonitor& System::Monitor() { |
| @@ -722,4 +754,18 @@ void System::RegisterHostThread() { | |||
| 722 | impl->kernel.RegisterHostThread(); | 754 | impl->kernel.RegisterHostThread(); |
| 723 | } | 755 | } |
| 724 | 756 | ||
| 757 | void System::EnterDynarmicProfile() { | ||
| 758 | std::size_t core = impl->kernel.GetCurrentHostThreadID(); | ||
| 759 | impl->dynarmic_ticks[core] = MicroProfileEnter(impl->microprofile_dynarmic[core]); | ||
| 760 | } | ||
| 761 | |||
| 762 | void System::ExitDynarmicProfile() { | ||
| 763 | std::size_t core = impl->kernel.GetCurrentHostThreadID(); | ||
| 764 | MicroProfileLeave(impl->microprofile_dynarmic[core], impl->dynarmic_ticks[core]); | ||
| 765 | } | ||
| 766 | |||
| 767 | bool System::IsMulticore() const { | ||
| 768 | return impl->is_multicore; | ||
| 769 | } | ||
| 770 | |||
| 725 | } // namespace Core | 771 | } // namespace Core |
diff --git a/src/core/core.h b/src/core/core.h index acc53d6a1..5c6cfbffe 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -27,6 +27,7 @@ class VfsFilesystem; | |||
| 27 | namespace Kernel { | 27 | namespace Kernel { |
| 28 | class GlobalScheduler; | 28 | class GlobalScheduler; |
| 29 | class KernelCore; | 29 | class KernelCore; |
| 30 | class PhysicalCore; | ||
| 30 | class Process; | 31 | class Process; |
| 31 | class Scheduler; | 32 | class Scheduler; |
| 32 | } // namespace Kernel | 33 | } // namespace Kernel |
| @@ -90,7 +91,7 @@ class InterruptManager; | |||
| 90 | namespace Core { | 91 | namespace Core { |
| 91 | 92 | ||
| 92 | class ARM_Interface; | 93 | class ARM_Interface; |
| 93 | class CoreManager; | 94 | class CpuManager; |
| 94 | class DeviceMemory; | 95 | class DeviceMemory; |
| 95 | class ExclusiveMonitor; | 96 | class ExclusiveMonitor; |
| 96 | class FrameLimiter; | 97 | class FrameLimiter; |
| @@ -136,16 +137,16 @@ public: | |||
| 136 | }; | 137 | }; |
| 137 | 138 | ||
| 138 | /** | 139 | /** |
| 139 | * Run the core CPU loop | 140 | * Run the OS and Application |
| 140 | * This function runs the core for the specified number of CPU instructions before trying to | 141 | * This function will start emulation and run the relevant devices |
| 141 | * update hardware. This is much faster than SingleStep (and should be equivalent), as the CPU | 142 | */ |
| 142 | * is not required to do a full dispatch with each instruction. NOTE: the number of instructions | 143 | ResultStatus Run(); |
| 143 | * requested is not guaranteed to run, as this will be interrupted preemptively if a hardware | 144 | |
| 144 | * update is requested (e.g. on a thread switch). | 145 | /** |
| 145 | * @param tight_loop If false, the CPU single-steps. | 146 | * Pause the OS and Application |
| 146 | * @return Result status, indicating whether or not the operation succeeded. | 147 | * This function will pause emulation and stop the relevant devices |
| 147 | */ | 148 | */ |
| 148 | ResultStatus RunLoop(bool tight_loop = true); | 149 | ResultStatus Pause(); |
| 149 | 150 | ||
| 150 | /** | 151 | /** |
| 151 | * Step the CPU one instruction | 152 | * Step the CPU one instruction |
| @@ -209,17 +210,21 @@ public: | |||
| 209 | /// Gets the scheduler for the CPU core that is currently running | 210 | /// Gets the scheduler for the CPU core that is currently running |
| 210 | const Kernel::Scheduler& CurrentScheduler() const; | 211 | const Kernel::Scheduler& CurrentScheduler() const; |
| 211 | 212 | ||
| 213 | /// Gets the physical core for the CPU core that is currently running | ||
| 214 | Kernel::PhysicalCore& CurrentPhysicalCore(); | ||
| 215 | |||
| 216 | /// Gets the physical core for the CPU core that is currently running | ||
| 217 | const Kernel::PhysicalCore& CurrentPhysicalCore() const; | ||
| 218 | |||
| 212 | /// Gets a reference to an ARM interface for the CPU core with the specified index | 219 | /// Gets a reference to an ARM interface for the CPU core with the specified index |
| 213 | ARM_Interface& ArmInterface(std::size_t core_index); | 220 | ARM_Interface& ArmInterface(std::size_t core_index); |
| 214 | 221 | ||
| 215 | /// Gets a const reference to an ARM interface from the CPU core with the specified index | 222 | /// Gets a const reference to an ARM interface from the CPU core with the specified index |
| 216 | const ARM_Interface& ArmInterface(std::size_t core_index) const; | 223 | const ARM_Interface& ArmInterface(std::size_t core_index) const; |
| 217 | 224 | ||
| 218 | /// Gets a CPU interface to the CPU core with the specified index | 225 | CpuManager& GetCpuManager(); |
| 219 | CoreManager& GetCoreManager(std::size_t core_index); | ||
| 220 | 226 | ||
| 221 | /// Gets a CPU interface to the CPU core with the specified index | 227 | const CpuManager& GetCpuManager() const; |
| 222 | const CoreManager& GetCoreManager(std::size_t core_index) const; | ||
| 223 | 228 | ||
| 224 | /// Gets a reference to the exclusive monitor | 229 | /// Gets a reference to the exclusive monitor |
| 225 | ExclusiveMonitor& Monitor(); | 230 | ExclusiveMonitor& Monitor(); |
| @@ -370,14 +375,17 @@ public: | |||
| 370 | /// Register a host thread as an auxiliary thread. | 375 | /// Register a host thread as an auxiliary thread. |
| 371 | void RegisterHostThread(); | 376 | void RegisterHostThread(); |
| 372 | 377 | ||
| 373 | private: | 378 | /// Enter Dynarmic Microprofile |
| 374 | System(); | 379 | void EnterDynarmicProfile(); |
| 380 | |||
| 381 | /// Exit Dynarmic Microprofile | ||
| 382 | void ExitDynarmicProfile(); | ||
| 375 | 383 | ||
| 376 | /// Returns the currently running CPU core | 384 | /// Tells if system is running on multicore. |
| 377 | CoreManager& CurrentCoreManager(); | 385 | bool IsMulticore() const; |
| 378 | 386 | ||
| 379 | /// Returns the currently running CPU core | 387 | private: |
| 380 | const CoreManager& CurrentCoreManager() const; | 388 | System(); |
| 381 | 389 | ||
| 382 | /** | 390 | /** |
| 383 | * Initialize the emulated system. | 391 | * Initialize the emulated system. |
diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp deleted file mode 100644 index b6b797c80..000000000 --- a/src/core/core_manager.cpp +++ /dev/null | |||
| @@ -1,67 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <condition_variable> | ||
| 6 | #include <mutex> | ||
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/arm/exclusive_monitor.h" | ||
| 10 | #include "core/arm/unicorn/arm_unicorn.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/core_manager.h" | ||
| 13 | #include "core/core_timing.h" | ||
| 14 | #include "core/hle/kernel/kernel.h" | ||
| 15 | #include "core/hle/kernel/physical_core.h" | ||
| 16 | #include "core/hle/kernel/scheduler.h" | ||
| 17 | #include "core/hle/kernel/thread.h" | ||
| 18 | #include "core/hle/lock.h" | ||
| 19 | #include "core/settings.h" | ||
| 20 | |||
| 21 | namespace Core { | ||
| 22 | |||
| 23 | CoreManager::CoreManager(System& system, std::size_t core_index) | ||
| 24 | : global_scheduler{system.GlobalScheduler()}, physical_core{system.Kernel().PhysicalCore( | ||
| 25 | core_index)}, | ||
| 26 | core_timing{system.CoreTiming()}, core_index{core_index} {} | ||
| 27 | |||
| 28 | CoreManager::~CoreManager() = default; | ||
| 29 | |||
| 30 | void CoreManager::RunLoop(bool tight_loop) { | ||
| 31 | Reschedule(); | ||
| 32 | |||
| 33 | // If we don't have a currently active thread then don't execute instructions, | ||
| 34 | // instead advance to the next event and try to yield to the next thread | ||
| 35 | if (Kernel::GetCurrentThread() == nullptr) { | ||
| 36 | LOG_TRACE(Core, "Core-{} idling", core_index); | ||
| 37 | core_timing.Idle(); | ||
| 38 | } else { | ||
| 39 | if (tight_loop) { | ||
| 40 | physical_core.Run(); | ||
| 41 | } else { | ||
| 42 | physical_core.Step(); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | core_timing.Advance(); | ||
| 46 | |||
| 47 | Reschedule(); | ||
| 48 | } | ||
| 49 | |||
| 50 | void CoreManager::SingleStep() { | ||
| 51 | return RunLoop(false); | ||
| 52 | } | ||
| 53 | |||
| 54 | void CoreManager::PrepareReschedule() { | ||
| 55 | physical_core.Stop(); | ||
| 56 | } | ||
| 57 | |||
| 58 | void CoreManager::Reschedule() { | ||
| 59 | // Lock the global kernel mutex when we manipulate the HLE state | ||
| 60 | std::lock_guard lock(HLE::g_hle_lock); | ||
| 61 | |||
| 62 | global_scheduler.SelectThread(core_index); | ||
| 63 | |||
| 64 | physical_core.Scheduler().TryDoContextSwitch(); | ||
| 65 | } | ||
| 66 | |||
| 67 | } // namespace Core | ||
diff --git a/src/core/core_manager.h b/src/core/core_manager.h deleted file mode 100644 index d525de00a..000000000 --- a/src/core/core_manager.h +++ /dev/null | |||
| @@ -1,63 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <cstddef> | ||
| 9 | #include <memory> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | class GlobalScheduler; | ||
| 14 | class PhysicalCore; | ||
| 15 | } // namespace Kernel | ||
| 16 | |||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace Core::Timing { | ||
| 22 | class CoreTiming; | ||
| 23 | } | ||
| 24 | |||
| 25 | namespace Core::Memory { | ||
| 26 | class Memory; | ||
| 27 | } | ||
| 28 | |||
| 29 | namespace Core { | ||
| 30 | |||
| 31 | constexpr unsigned NUM_CPU_CORES{4}; | ||
| 32 | |||
| 33 | class CoreManager { | ||
| 34 | public: | ||
| 35 | CoreManager(System& system, std::size_t core_index); | ||
| 36 | ~CoreManager(); | ||
| 37 | |||
| 38 | void RunLoop(bool tight_loop = true); | ||
| 39 | |||
| 40 | void SingleStep(); | ||
| 41 | |||
| 42 | void PrepareReschedule(); | ||
| 43 | |||
| 44 | bool IsMainCore() const { | ||
| 45 | return core_index == 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | std::size_t CoreIndex() const { | ||
| 49 | return core_index; | ||
| 50 | } | ||
| 51 | |||
| 52 | private: | ||
| 53 | void Reschedule(); | ||
| 54 | |||
| 55 | Kernel::GlobalScheduler& global_scheduler; | ||
| 56 | Kernel::PhysicalCore& physical_core; | ||
| 57 | Timing::CoreTiming& core_timing; | ||
| 58 | |||
| 59 | std::atomic<bool> reschedule_pending = false; | ||
| 60 | std::size_t core_index; | ||
| 61 | }; | ||
| 62 | |||
| 63 | } // namespace Core | ||
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 46d4178c4..5c83c41a4 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -1,29 +1,27 @@ | |||
| 1 | // Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | |||
| 7 | #include <algorithm> | 5 | #include <algorithm> |
| 8 | #include <mutex> | 6 | #include <mutex> |
| 9 | #include <string> | 7 | #include <string> |
| 10 | #include <tuple> | 8 | #include <tuple> |
| 11 | 9 | ||
| 12 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 13 | #include "common/thread.h" | 11 | #include "common/microprofile.h" |
| 12 | #include "core/core_timing.h" | ||
| 14 | #include "core/core_timing_util.h" | 13 | #include "core/core_timing_util.h" |
| 15 | #include "core/hardware_properties.h" | ||
| 16 | 14 | ||
| 17 | namespace Core::Timing { | 15 | namespace Core::Timing { |
| 18 | 16 | ||
| 19 | constexpr int MAX_SLICE_LENGTH = 10000; | 17 | constexpr u64 MAX_SLICE_LENGTH = 4000; |
| 20 | 18 | ||
| 21 | std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { | 19 | std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { |
| 22 | return std::make_shared<EventType>(std::move(callback), std::move(name)); | 20 | return std::make_shared<EventType>(std::move(callback), std::move(name)); |
| 23 | } | 21 | } |
| 24 | 22 | ||
| 25 | struct CoreTiming::Event { | 23 | struct CoreTiming::Event { |
| 26 | s64 time; | 24 | u64 time; |
| 27 | u64 fifo_order; | 25 | u64 fifo_order; |
| 28 | u64 userdata; | 26 | u64 userdata; |
| 29 | std::weak_ptr<EventType> type; | 27 | std::weak_ptr<EventType> type; |
| @@ -39,51 +37,90 @@ struct CoreTiming::Event { | |||
| 39 | } | 37 | } |
| 40 | }; | 38 | }; |
| 41 | 39 | ||
| 42 | CoreTiming::CoreTiming() = default; | 40 | CoreTiming::CoreTiming() { |
| 43 | CoreTiming::~CoreTiming() = default; | 41 | clock = |
| 42 | Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ); | ||
| 43 | } | ||
| 44 | 44 | ||
| 45 | void CoreTiming::Initialize() { | 45 | CoreTiming::~CoreTiming() = default; |
| 46 | downcounts.fill(MAX_SLICE_LENGTH); | ||
| 47 | time_slice.fill(MAX_SLICE_LENGTH); | ||
| 48 | slice_length = MAX_SLICE_LENGTH; | ||
| 49 | global_timer = 0; | ||
| 50 | idled_cycles = 0; | ||
| 51 | current_context = 0; | ||
| 52 | 46 | ||
| 53 | // The time between CoreTiming being initialized and the first call to Advance() is considered | 47 | void CoreTiming::ThreadEntry(CoreTiming& instance) { |
| 54 | // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before | 48 | constexpr char name[] = "yuzu:HostTiming"; |
| 55 | // executing the first cycle of each slice to prepare the slice length and downcount for | 49 | MicroProfileOnThreadCreate(name); |
| 56 | // that slice. | 50 | Common::SetCurrentThreadName(name); |
| 57 | is_global_timer_sane = true; | 51 | Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); |
| 52 | instance.on_thread_init(); | ||
| 53 | instance.ThreadLoop(); | ||
| 54 | } | ||
| 58 | 55 | ||
| 56 | void CoreTiming::Initialize(std::function<void(void)>&& on_thread_init_) { | ||
| 57 | on_thread_init = std::move(on_thread_init_); | ||
| 59 | event_fifo_id = 0; | 58 | event_fifo_id = 0; |
| 60 | 59 | shutting_down = false; | |
| 60 | ticks = 0; | ||
| 61 | const auto empty_timed_callback = [](u64, s64) {}; | 61 | const auto empty_timed_callback = [](u64, s64) {}; |
| 62 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); | 62 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); |
| 63 | if (is_multicore) { | ||
| 64 | timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); | ||
| 65 | } | ||
| 63 | } | 66 | } |
| 64 | 67 | ||
| 65 | void CoreTiming::Shutdown() { | 68 | void CoreTiming::Shutdown() { |
| 69 | paused = true; | ||
| 70 | shutting_down = true; | ||
| 71 | pause_event.Set(); | ||
| 72 | event.Set(); | ||
| 73 | if (timer_thread) { | ||
| 74 | timer_thread->join(); | ||
| 75 | } | ||
| 66 | ClearPendingEvents(); | 76 | ClearPendingEvents(); |
| 77 | timer_thread.reset(); | ||
| 78 | has_started = false; | ||
| 67 | } | 79 | } |
| 68 | 80 | ||
| 69 | void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, | 81 | void CoreTiming::Pause(bool is_paused) { |
| 70 | u64 userdata) { | 82 | paused = is_paused; |
| 71 | std::lock_guard guard{inner_mutex}; | 83 | pause_event.Set(); |
| 72 | const s64 timeout = GetTicks() + cycles_into_future; | 84 | } |
| 73 | 85 | ||
| 74 | // If this event needs to be scheduled before the next advance(), force one early | 86 | void CoreTiming::SyncPause(bool is_paused) { |
| 75 | if (!is_global_timer_sane) { | 87 | if (is_paused == paused && paused_set == paused) { |
| 76 | ForceExceptionCheck(cycles_into_future); | 88 | return; |
| 89 | } | ||
| 90 | Pause(is_paused); | ||
| 91 | if (timer_thread) { | ||
| 92 | if (!is_paused) { | ||
| 93 | pause_event.Set(); | ||
| 94 | } | ||
| 95 | event.Set(); | ||
| 96 | while (paused_set != is_paused) | ||
| 97 | ; | ||
| 77 | } | 98 | } |
| 99 | } | ||
| 78 | 100 | ||
| 79 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); | 101 | bool CoreTiming::IsRunning() const { |
| 102 | return !paused_set; | ||
| 103 | } | ||
| 80 | 104 | ||
| 81 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 105 | bool CoreTiming::HasPendingEvents() const { |
| 106 | return !(wait_set && event_queue.empty()); | ||
| 82 | } | 107 | } |
| 83 | 108 | ||
| 84 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { | 109 | void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, |
| 85 | std::lock_guard guard{inner_mutex}; | 110 | u64 userdata) { |
| 111 | { | ||
| 112 | std::scoped_lock scope{basic_lock}; | ||
| 113 | const u64 timeout = static_cast<u64>(GetGlobalTimeNs().count() + ns_into_future); | ||
| 114 | |||
| 115 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); | ||
| 86 | 116 | ||
| 117 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||
| 118 | } | ||
| 119 | event.Set(); | ||
| 120 | } | ||
| 121 | |||
| 122 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { | ||
| 123 | std::scoped_lock scope{basic_lock}; | ||
| 87 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 124 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { |
| 88 | return e.type.lock().get() == event_type.get() && e.userdata == userdata; | 125 | return e.type.lock().get() == event_type.get() && e.userdata == userdata; |
| 89 | }); | 126 | }); |
| @@ -95,21 +132,39 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u | |||
| 95 | } | 132 | } |
| 96 | } | 133 | } |
| 97 | 134 | ||
| 98 | u64 CoreTiming::GetTicks() const { | 135 | void CoreTiming::AddTicks(u64 ticks) { |
| 99 | u64 ticks = static_cast<u64>(global_timer); | 136 | this->ticks += ticks; |
| 100 | if (!is_global_timer_sane) { | 137 | downcount -= ticks; |
| 101 | ticks += accumulated_ticks; | 138 | } |
| 139 | |||
| 140 | void CoreTiming::Idle() { | ||
| 141 | if (!event_queue.empty()) { | ||
| 142 | const u64 next_event_time = event_queue.front().time; | ||
| 143 | const u64 next_ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U; | ||
| 144 | if (next_ticks > ticks) { | ||
| 145 | ticks = next_ticks; | ||
| 146 | } | ||
| 147 | return; | ||
| 102 | } | 148 | } |
| 103 | return ticks; | 149 | ticks += 1000U; |
| 104 | } | 150 | } |
| 105 | 151 | ||
| 106 | u64 CoreTiming::GetIdleTicks() const { | 152 | void CoreTiming::ResetTicks() { |
| 107 | return static_cast<u64>(idled_cycles); | 153 | downcount = MAX_SLICE_LENGTH; |
| 108 | } | 154 | } |
| 109 | 155 | ||
| 110 | void CoreTiming::AddTicks(u64 ticks) { | 156 | u64 CoreTiming::GetCPUTicks() const { |
| 111 | accumulated_ticks += ticks; | 157 | if (is_multicore) { |
| 112 | downcounts[current_context] -= static_cast<s64>(ticks); | 158 | return clock->GetCPUCycles(); |
| 159 | } | ||
| 160 | return ticks; | ||
| 161 | } | ||
| 162 | |||
| 163 | u64 CoreTiming::GetClockTicks() const { | ||
| 164 | if (is_multicore) { | ||
| 165 | return clock->GetClockCycles(); | ||
| 166 | } | ||
| 167 | return CpuCyclesToClockCycles(ticks); | ||
| 113 | } | 168 | } |
| 114 | 169 | ||
| 115 | void CoreTiming::ClearPendingEvents() { | 170 | void CoreTiming::ClearPendingEvents() { |
| @@ -117,7 +172,7 @@ void CoreTiming::ClearPendingEvents() { | |||
| 117 | } | 172 | } |
| 118 | 173 | ||
| 119 | void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { | 174 | void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { |
| 120 | std::lock_guard guard{inner_mutex}; | 175 | basic_lock.lock(); |
| 121 | 176 | ||
| 122 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 177 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { |
| 123 | return e.type.lock().get() == event_type.get(); | 178 | return e.type.lock().get() == event_type.get(); |
| @@ -128,99 +183,72 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { | |||
| 128 | event_queue.erase(itr, event_queue.end()); | 183 | event_queue.erase(itr, event_queue.end()); |
| 129 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 184 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 130 | } | 185 | } |
| 186 | basic_lock.unlock(); | ||
| 131 | } | 187 | } |
| 132 | 188 | ||
| 133 | void CoreTiming::ForceExceptionCheck(s64 cycles) { | 189 | std::optional<s64> CoreTiming::Advance() { |
| 134 | cycles = std::max<s64>(0, cycles); | 190 | std::scoped_lock advance_scope{advance_lock}; |
| 135 | if (downcounts[current_context] <= cycles) { | 191 | std::scoped_lock basic_scope{basic_lock}; |
| 136 | return; | 192 | global_timer = GetGlobalTimeNs().count(); |
| 137 | } | ||
| 138 | |||
| 139 | // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int | ||
| 140 | // here. Account for cycles already executed by adjusting the g.slice_length | ||
| 141 | downcounts[current_context] = static_cast<int>(cycles); | ||
| 142 | } | ||
| 143 | |||
| 144 | std::optional<u64> CoreTiming::NextAvailableCore(const s64 needed_ticks) const { | ||
| 145 | const u64 original_context = current_context; | ||
| 146 | u64 next_context = (original_context + 1) % num_cpu_cores; | ||
| 147 | while (next_context != original_context) { | ||
| 148 | if (time_slice[next_context] >= needed_ticks) { | ||
| 149 | return {next_context}; | ||
| 150 | } else if (time_slice[next_context] >= 0) { | ||
| 151 | return std::nullopt; | ||
| 152 | } | ||
| 153 | next_context = (next_context + 1) % num_cpu_cores; | ||
| 154 | } | ||
| 155 | return std::nullopt; | ||
| 156 | } | ||
| 157 | |||
| 158 | void CoreTiming::Advance() { | ||
| 159 | std::unique_lock<std::mutex> guard(inner_mutex); | ||
| 160 | |||
| 161 | const u64 cycles_executed = accumulated_ticks; | ||
| 162 | time_slice[current_context] = std::max<s64>(0, time_slice[current_context] - accumulated_ticks); | ||
| 163 | global_timer += cycles_executed; | ||
| 164 | |||
| 165 | is_global_timer_sane = true; | ||
| 166 | 193 | ||
| 167 | while (!event_queue.empty() && event_queue.front().time <= global_timer) { | 194 | while (!event_queue.empty() && event_queue.front().time <= global_timer) { |
| 168 | Event evt = std::move(event_queue.front()); | 195 | Event evt = std::move(event_queue.front()); |
| 169 | std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 196 | std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 170 | event_queue.pop_back(); | 197 | event_queue.pop_back(); |
| 171 | inner_mutex.unlock(); | 198 | basic_lock.unlock(); |
| 172 | 199 | ||
| 173 | if (auto event_type{evt.type.lock()}) { | 200 | if (auto event_type{evt.type.lock()}) { |
| 174 | event_type->callback(evt.userdata, global_timer - evt.time); | 201 | event_type->callback(evt.userdata, global_timer - evt.time); |
| 175 | } | 202 | } |
| 176 | 203 | ||
| 177 | inner_mutex.lock(); | 204 | basic_lock.lock(); |
| 205 | global_timer = GetGlobalTimeNs().count(); | ||
| 178 | } | 206 | } |
| 179 | 207 | ||
| 180 | is_global_timer_sane = false; | ||
| 181 | |||
| 182 | // Still events left (scheduled in the future) | ||
| 183 | if (!event_queue.empty()) { | 208 | if (!event_queue.empty()) { |
| 184 | const s64 needed_ticks = | 209 | const s64 next_time = event_queue.front().time - global_timer; |
| 185 | std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); | 210 | return next_time; |
| 186 | const auto next_core = NextAvailableCore(needed_ticks); | 211 | } else { |
| 187 | if (next_core) { | 212 | return std::nullopt; |
| 188 | downcounts[*next_core] = needed_ticks; | ||
| 189 | } | ||
| 190 | } | 213 | } |
| 191 | |||
| 192 | accumulated_ticks = 0; | ||
| 193 | |||
| 194 | downcounts[current_context] = time_slice[current_context]; | ||
| 195 | } | 214 | } |
| 196 | 215 | ||
| 197 | void CoreTiming::ResetRun() { | 216 | void CoreTiming::ThreadLoop() { |
| 198 | downcounts.fill(MAX_SLICE_LENGTH); | 217 | has_started = true; |
| 199 | time_slice.fill(MAX_SLICE_LENGTH); | 218 | while (!shutting_down) { |
| 200 | current_context = 0; | 219 | while (!paused) { |
| 201 | // Still events left (scheduled in the future) | 220 | paused_set = false; |
| 202 | if (!event_queue.empty()) { | 221 | const auto next_time = Advance(); |
| 203 | const s64 needed_ticks = | 222 | if (next_time) { |
| 204 | std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); | 223 | if (*next_time > 0) { |
| 205 | downcounts[current_context] = needed_ticks; | 224 | std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); |
| 225 | event.WaitFor(next_time_ns); | ||
| 226 | } | ||
| 227 | } else { | ||
| 228 | wait_set = true; | ||
| 229 | event.Wait(); | ||
| 230 | } | ||
| 231 | wait_set = false; | ||
| 232 | } | ||
| 233 | paused_set = true; | ||
| 234 | clock->Pause(true); | ||
| 235 | pause_event.Wait(); | ||
| 236 | clock->Pause(false); | ||
| 206 | } | 237 | } |
| 207 | |||
| 208 | is_global_timer_sane = false; | ||
| 209 | accumulated_ticks = 0; | ||
| 210 | } | 238 | } |
| 211 | 239 | ||
| 212 | void CoreTiming::Idle() { | 240 | std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { |
| 213 | accumulated_ticks += downcounts[current_context]; | 241 | if (is_multicore) { |
| 214 | idled_cycles += downcounts[current_context]; | 242 | return clock->GetTimeNS(); |
| 215 | downcounts[current_context] = 0; | 243 | } |
| 244 | return CyclesToNs(ticks); | ||
| 216 | } | 245 | } |
| 217 | 246 | ||
| 218 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { | 247 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { |
| 219 | return std::chrono::microseconds{GetTicks() * 1000000 / Hardware::BASE_CLOCK_RATE}; | 248 | if (is_multicore) { |
| 220 | } | 249 | return clock->GetTimeUS(); |
| 221 | 250 | } | |
| 222 | s64 CoreTiming::GetDowncount() const { | 251 | return CyclesToUs(ticks); |
| 223 | return downcounts[current_context]; | ||
| 224 | } | 252 | } |
| 225 | 253 | ||
| 226 | } // namespace Core::Timing | 254 | } // namespace Core::Timing |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index d50f4eb8a..72faaab64 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -1,19 +1,25 @@ | |||
| 1 | // Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2+ | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include <chrono> | 8 | #include <chrono> |
| 8 | #include <functional> | 9 | #include <functional> |
| 9 | #include <memory> | 10 | #include <memory> |
| 10 | #include <mutex> | 11 | #include <mutex> |
| 11 | #include <optional> | 12 | #include <optional> |
| 12 | #include <string> | 13 | #include <string> |
| 14 | #include <thread> | ||
| 13 | #include <vector> | 15 | #include <vector> |
| 14 | 16 | ||
| 15 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 18 | #include "common/spin_lock.h" | ||
| 19 | #include "common/thread.h" | ||
| 16 | #include "common/threadsafe_queue.h" | 20 | #include "common/threadsafe_queue.h" |
| 21 | #include "common/wall_clock.h" | ||
| 22 | #include "core/hardware_properties.h" | ||
| 17 | 23 | ||
| 18 | namespace Core::Timing { | 24 | namespace Core::Timing { |
| 19 | 25 | ||
| @@ -56,16 +62,40 @@ public: | |||
| 56 | 62 | ||
| 57 | /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is | 63 | /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is |
| 58 | /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. | 64 | /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. |
| 59 | void Initialize(); | 65 | void Initialize(std::function<void(void)>&& on_thread_init_); |
| 60 | 66 | ||
| 61 | /// Tears down all timing related functionality. | 67 | /// Tears down all timing related functionality. |
| 62 | void Shutdown(); | 68 | void Shutdown(); |
| 63 | 69 | ||
| 64 | /// After the first Advance, the slice lengths and the downcount will be reduced whenever an | 70 | /// Sets if emulation is multicore or single core, must be set before Initialize |
| 65 | /// event is scheduled earlier than the current values. | 71 | void SetMulticore(bool is_multicore) { |
| 66 | /// | 72 | this->is_multicore = is_multicore; |
| 67 | /// Scheduling from a callback will not update the downcount until the Advance() completes. | 73 | } |
| 68 | void ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, | 74 | |
| 75 | /// Check if it's using host timing. | ||
| 76 | bool IsHostTiming() const { | ||
| 77 | return is_multicore; | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Pauses/Unpauses the execution of the timer thread. | ||
| 81 | void Pause(bool is_paused); | ||
| 82 | |||
| 83 | /// Pauses/Unpauses the execution of the timer thread and waits until paused. | ||
| 84 | void SyncPause(bool is_paused); | ||
| 85 | |||
| 86 | /// Checks if core timing is running. | ||
| 87 | bool IsRunning() const; | ||
| 88 | |||
| 89 | /// Checks if the timer thread has started. | ||
| 90 | bool HasStarted() const { | ||
| 91 | return has_started; | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Checks if there are any pending time events. | ||
| 95 | bool HasPendingEvents() const; | ||
| 96 | |||
| 97 | /// Schedules an event in core timing | ||
| 98 | void ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, | ||
| 69 | u64 userdata = 0); | 99 | u64 userdata = 0); |
| 70 | 100 | ||
| 71 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); | 101 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); |
| @@ -73,41 +103,30 @@ public: | |||
| 73 | /// We only permit one event of each type in the queue at a time. | 103 | /// We only permit one event of each type in the queue at a time. |
| 74 | void RemoveEvent(const std::shared_ptr<EventType>& event_type); | 104 | void RemoveEvent(const std::shared_ptr<EventType>& event_type); |
| 75 | 105 | ||
| 76 | void ForceExceptionCheck(s64 cycles); | ||
| 77 | |||
| 78 | /// This should only be called from the emu thread, if you are calling it any other thread, | ||
| 79 | /// you are doing something evil | ||
| 80 | u64 GetTicks() const; | ||
| 81 | |||
| 82 | u64 GetIdleTicks() const; | ||
| 83 | |||
| 84 | void AddTicks(u64 ticks); | 106 | void AddTicks(u64 ticks); |
| 85 | 107 | ||
| 86 | /// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends | 108 | void ResetTicks(); |
| 87 | /// the previous timing slice and begins the next one, you must Advance from the previous | ||
| 88 | /// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an | ||
| 89 | /// Advance() is required to initialize the slice length before the first cycle of emulated | ||
| 90 | /// instructions is executed. | ||
| 91 | void Advance(); | ||
| 92 | 109 | ||
| 93 | /// Pretend that the main CPU has executed enough cycles to reach the next event. | ||
| 94 | void Idle(); | 110 | void Idle(); |
| 95 | 111 | ||
| 96 | std::chrono::microseconds GetGlobalTimeUs() const; | 112 | s64 GetDowncount() const { |
| 113 | return downcount; | ||
| 114 | } | ||
| 97 | 115 | ||
| 98 | void ResetRun(); | 116 | /// Returns current time in emulated CPU cycles |
| 117 | u64 GetCPUTicks() const; | ||
| 99 | 118 | ||
| 100 | s64 GetDowncount() const; | 119 | /// Returns current time in emulated in Clock cycles |
| 120 | u64 GetClockTicks() const; | ||
| 101 | 121 | ||
| 102 | void SwitchContext(u64 new_context) { | 122 | /// Returns current time in microseconds. |
| 103 | current_context = new_context; | 123 | std::chrono::microseconds GetGlobalTimeUs() const; |
| 104 | } | ||
| 105 | 124 | ||
| 106 | bool CanCurrentContextRun() const { | 125 | /// Returns current time in nanoseconds. |
| 107 | return time_slice[current_context] > 0; | 126 | std::chrono::nanoseconds GetGlobalTimeNs() const; |
| 108 | } | ||
| 109 | 127 | ||
| 110 | std::optional<u64> NextAvailableCore(const s64 needed_ticks) const; | 128 | /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. |
| 129 | std::optional<s64> Advance(); | ||
| 111 | 130 | ||
| 112 | private: | 131 | private: |
| 113 | struct Event; | 132 | struct Event; |
| @@ -115,21 +134,14 @@ private: | |||
| 115 | /// Clear all pending events. This should ONLY be done on exit. | 134 | /// Clear all pending events. This should ONLY be done on exit. |
| 116 | void ClearPendingEvents(); | 135 | void ClearPendingEvents(); |
| 117 | 136 | ||
| 118 | static constexpr u64 num_cpu_cores = 4; | 137 | static void ThreadEntry(CoreTiming& instance); |
| 138 | void ThreadLoop(); | ||
| 119 | 139 | ||
| 120 | s64 global_timer = 0; | 140 | std::unique_ptr<Common::WallClock> clock; |
| 121 | s64 idled_cycles = 0; | ||
| 122 | s64 slice_length = 0; | ||
| 123 | u64 accumulated_ticks = 0; | ||
| 124 | std::array<s64, num_cpu_cores> downcounts{}; | ||
| 125 | // Slice of time assigned to each core per run. | ||
| 126 | std::array<s64, num_cpu_cores> time_slice{}; | ||
| 127 | u64 current_context = 0; | ||
| 128 | 141 | ||
| 129 | // Are we in a function that has been called from Advance() | 142 | u64 global_timer = 0; |
| 130 | // If events are scheduled from a function that gets called from Advance(), | 143 | |
| 131 | // don't change slice_length and downcount. | 144 | std::chrono::nanoseconds start_point; |
| 132 | bool is_global_timer_sane = false; | ||
| 133 | 145 | ||
| 134 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. | 146 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. |
| 135 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and | 147 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and |
| @@ -139,8 +151,23 @@ private: | |||
| 139 | u64 event_fifo_id = 0; | 151 | u64 event_fifo_id = 0; |
| 140 | 152 | ||
| 141 | std::shared_ptr<EventType> ev_lost; | 153 | std::shared_ptr<EventType> ev_lost; |
| 142 | 154 | Common::Event event{}; | |
| 143 | std::mutex inner_mutex; | 155 | Common::Event pause_event{}; |
| 156 | Common::SpinLock basic_lock{}; | ||
| 157 | Common::SpinLock advance_lock{}; | ||
| 158 | std::unique_ptr<std::thread> timer_thread; | ||
| 159 | std::atomic<bool> paused{}; | ||
| 160 | std::atomic<bool> paused_set{}; | ||
| 161 | std::atomic<bool> wait_set{}; | ||
| 162 | std::atomic<bool> shutting_down{}; | ||
| 163 | std::atomic<bool> has_started{}; | ||
| 164 | std::function<void(void)> on_thread_init{}; | ||
| 165 | |||
| 166 | bool is_multicore{}; | ||
| 167 | |||
| 168 | /// Cycle timing | ||
| 169 | u64 ticks{}; | ||
| 170 | s64 downcount{}; | ||
| 144 | }; | 171 | }; |
| 145 | 172 | ||
| 146 | /// Creates a core timing event with the given name and callback. | 173 | /// Creates a core timing event with the given name and callback. |
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index de50d3b14..aefc63663 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp | |||
| @@ -38,15 +38,23 @@ s64 usToCycles(std::chrono::microseconds us) { | |||
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | s64 nsToCycles(std::chrono::nanoseconds ns) { | 40 | s64 nsToCycles(std::chrono::nanoseconds ns) { |
| 41 | if (static_cast<u64>(ns.count() / 1000000000) > MAX_VALUE_TO_MULTIPLY) { | 41 | const u128 temporal = Common::Multiply64Into128(ns.count(), Hardware::BASE_CLOCK_RATE); |
| 42 | LOG_ERROR(Core_Timing, "Integer overflow, use max value"); | 42 | return Common::Divide128On32(temporal, static_cast<u32>(1000000000)).first; |
| 43 | return std::numeric_limits<s64>::max(); | 43 | } |
| 44 | } | 44 | |
| 45 | if (static_cast<u64>(ns.count()) > MAX_VALUE_TO_MULTIPLY) { | 45 | u64 msToClockCycles(std::chrono::milliseconds ns) { |
| 46 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); | 46 | const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); |
| 47 | return Hardware::BASE_CLOCK_RATE * (ns.count() / 1000000000); | 47 | return Common::Divide128On32(temp, 1000).first; |
| 48 | } | 48 | } |
| 49 | return (Hardware::BASE_CLOCK_RATE * ns.count()) / 1000000000; | 49 | |
| 50 | u64 usToClockCycles(std::chrono::microseconds ns) { | ||
| 51 | const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); | ||
| 52 | return Common::Divide128On32(temp, 1000000).first; | ||
| 53 | } | ||
| 54 | |||
| 55 | u64 nsToClockCycles(std::chrono::nanoseconds ns) { | ||
| 56 | const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); | ||
| 57 | return Common::Divide128On32(temp, 1000000000).first; | ||
| 50 | } | 58 | } |
| 51 | 59 | ||
| 52 | u64 CpuCyclesToClockCycles(u64 ticks) { | 60 | u64 CpuCyclesToClockCycles(u64 ticks) { |
| @@ -54,4 +62,22 @@ u64 CpuCyclesToClockCycles(u64 ticks) { | |||
| 54 | return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; | 62 | return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; |
| 55 | } | 63 | } |
| 56 | 64 | ||
| 65 | std::chrono::milliseconds CyclesToMs(s64 cycles) { | ||
| 66 | const u128 temporal = Common::Multiply64Into128(cycles, 1000); | ||
| 67 | u64 ms = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; | ||
| 68 | return std::chrono::milliseconds(ms); | ||
| 69 | } | ||
| 70 | |||
| 71 | std::chrono::nanoseconds CyclesToNs(s64 cycles) { | ||
| 72 | const u128 temporal = Common::Multiply64Into128(cycles, 1000000000); | ||
| 73 | u64 ns = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; | ||
| 74 | return std::chrono::nanoseconds(ns); | ||
| 75 | } | ||
| 76 | |||
| 77 | std::chrono::microseconds CyclesToUs(s64 cycles) { | ||
| 78 | const u128 temporal = Common::Multiply64Into128(cycles, 1000000); | ||
| 79 | u64 us = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; | ||
| 80 | return std::chrono::microseconds(us); | ||
| 81 | } | ||
| 82 | |||
| 57 | } // namespace Core::Timing | 83 | } // namespace Core::Timing |
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index addc72b19..2ed979e14 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h | |||
| @@ -13,18 +13,12 @@ namespace Core::Timing { | |||
| 13 | s64 msToCycles(std::chrono::milliseconds ms); | 13 | s64 msToCycles(std::chrono::milliseconds ms); |
| 14 | s64 usToCycles(std::chrono::microseconds us); | 14 | s64 usToCycles(std::chrono::microseconds us); |
| 15 | s64 nsToCycles(std::chrono::nanoseconds ns); | 15 | s64 nsToCycles(std::chrono::nanoseconds ns); |
| 16 | 16 | u64 msToClockCycles(std::chrono::milliseconds ns); | |
| 17 | inline std::chrono::milliseconds CyclesToMs(s64 cycles) { | 17 | u64 usToClockCycles(std::chrono::microseconds ns); |
| 18 | return std::chrono::milliseconds(cycles * 1000 / Hardware::BASE_CLOCK_RATE); | 18 | u64 nsToClockCycles(std::chrono::nanoseconds ns); |
| 19 | } | 19 | std::chrono::milliseconds CyclesToMs(s64 cycles); |
| 20 | 20 | std::chrono::nanoseconds CyclesToNs(s64 cycles); | |
| 21 | inline std::chrono::nanoseconds CyclesToNs(s64 cycles) { | 21 | std::chrono::microseconds CyclesToUs(s64 cycles); |
| 22 | return std::chrono::nanoseconds(cycles * 1000000000 / Hardware::BASE_CLOCK_RATE); | ||
| 23 | } | ||
| 24 | |||
| 25 | inline std::chrono::microseconds CyclesToUs(s64 cycles) { | ||
| 26 | return std::chrono::microseconds(cycles * 1000000 / Hardware::BASE_CLOCK_RATE); | ||
| 27 | } | ||
| 28 | 22 | ||
| 29 | u64 CpuCyclesToClockCycles(u64 ticks); | 23 | u64 CpuCyclesToClockCycles(u64 ticks); |
| 30 | 24 | ||
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 70ddbdcca..32afcf3ae 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -2,80 +2,372 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/fiber.h" | ||
| 6 | #include "common/microprofile.h" | ||
| 7 | #include "common/thread.h" | ||
| 5 | #include "core/arm/exclusive_monitor.h" | 8 | #include "core/arm/exclusive_monitor.h" |
| 6 | #include "core/core.h" | 9 | #include "core/core.h" |
| 7 | #include "core/core_manager.h" | ||
| 8 | #include "core/core_timing.h" | 10 | #include "core/core_timing.h" |
| 9 | #include "core/cpu_manager.h" | 11 | #include "core/cpu_manager.h" |
| 10 | #include "core/gdbstub/gdbstub.h" | 12 | #include "core/gdbstub/gdbstub.h" |
| 13 | #include "core/hle/kernel/kernel.h" | ||
| 14 | #include "core/hle/kernel/physical_core.h" | ||
| 15 | #include "core/hle/kernel/scheduler.h" | ||
| 16 | #include "core/hle/kernel/thread.h" | ||
| 17 | #include "video_core/gpu.h" | ||
| 11 | 18 | ||
| 12 | namespace Core { | 19 | namespace Core { |
| 13 | 20 | ||
| 14 | CpuManager::CpuManager(System& system) : system{system} {} | 21 | CpuManager::CpuManager(System& system) : system{system} {} |
| 15 | CpuManager::~CpuManager() = default; | 22 | CpuManager::~CpuManager() = default; |
| 16 | 23 | ||
| 24 | void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { | ||
| 25 | cpu_manager.RunThread(core); | ||
| 26 | } | ||
| 27 | |||
| 17 | void CpuManager::Initialize() { | 28 | void CpuManager::Initialize() { |
| 18 | for (std::size_t index = 0; index < core_managers.size(); ++index) { | 29 | running_mode = true; |
| 19 | core_managers[index] = std::make_unique<CoreManager>(system, index); | 30 | if (is_multicore) { |
| 31 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 32 | core_data[core].host_thread = | ||
| 33 | std::make_unique<std::thread>(ThreadStart, std::ref(*this), core); | ||
| 34 | } | ||
| 35 | } else { | ||
| 36 | core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0); | ||
| 20 | } | 37 | } |
| 21 | } | 38 | } |
| 22 | 39 | ||
| 23 | void CpuManager::Shutdown() { | 40 | void CpuManager::Shutdown() { |
| 24 | for (auto& cpu_core : core_managers) { | 41 | running_mode = false; |
| 25 | cpu_core.reset(); | 42 | Pause(false); |
| 43 | if (is_multicore) { | ||
| 44 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 45 | core_data[core].host_thread->join(); | ||
| 46 | core_data[core].host_thread.reset(); | ||
| 47 | } | ||
| 48 | } else { | ||
| 49 | core_data[0].host_thread->join(); | ||
| 50 | core_data[0].host_thread.reset(); | ||
| 26 | } | 51 | } |
| 27 | } | 52 | } |
| 28 | 53 | ||
| 29 | CoreManager& CpuManager::GetCoreManager(std::size_t index) { | 54 | std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { |
| 30 | return *core_managers.at(index); | 55 | return std::function<void(void*)>(GuestThreadFunction); |
| 31 | } | 56 | } |
| 32 | 57 | ||
| 33 | const CoreManager& CpuManager::GetCoreManager(std::size_t index) const { | 58 | std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { |
| 34 | return *core_managers.at(index); | 59 | return std::function<void(void*)>(IdleThreadFunction); |
| 35 | } | 60 | } |
| 36 | 61 | ||
| 37 | CoreManager& CpuManager::GetCurrentCoreManager() { | 62 | std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { |
| 38 | // Otherwise, use single-threaded mode active_core variable | 63 | return std::function<void(void*)>(SuspendThreadFunction); |
| 39 | return *core_managers[active_core]; | ||
| 40 | } | 64 | } |
| 41 | 65 | ||
| 42 | const CoreManager& CpuManager::GetCurrentCoreManager() const { | 66 | void CpuManager::GuestThreadFunction(void* cpu_manager_) { |
| 43 | // Otherwise, use single-threaded mode active_core variable | 67 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 44 | return *core_managers[active_core]; | 68 | if (cpu_manager->is_multicore) { |
| 69 | cpu_manager->MultiCoreRunGuestThread(); | ||
| 70 | } else { | ||
| 71 | cpu_manager->SingleCoreRunGuestThread(); | ||
| 72 | } | ||
| 45 | } | 73 | } |
| 46 | 74 | ||
| 47 | void CpuManager::RunLoop(bool tight_loop) { | 75 | void CpuManager::GuestRewindFunction(void* cpu_manager_) { |
| 48 | if (GDBStub::IsServerEnabled()) { | 76 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 49 | GDBStub::HandlePacket(); | 77 | if (cpu_manager->is_multicore) { |
| 78 | cpu_manager->MultiCoreRunGuestLoop(); | ||
| 79 | } else { | ||
| 80 | cpu_manager->SingleCoreRunGuestLoop(); | ||
| 81 | } | ||
| 82 | } | ||
| 50 | 83 | ||
| 51 | // If the loop is halted and we want to step, use a tiny (1) number of instructions to | 84 | void CpuManager::IdleThreadFunction(void* cpu_manager_) { |
| 52 | // execute. Otherwise, get out of the loop function. | 85 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); |
| 53 | if (GDBStub::GetCpuHaltFlag()) { | 86 | if (cpu_manager->is_multicore) { |
| 54 | if (GDBStub::GetCpuStepFlag()) { | 87 | cpu_manager->MultiCoreRunIdleThread(); |
| 55 | tight_loop = false; | 88 | } else { |
| 56 | } else { | 89 | cpu_manager->SingleCoreRunIdleThread(); |
| 57 | return; | 90 | } |
| 91 | } | ||
| 92 | |||
| 93 | void CpuManager::SuspendThreadFunction(void* cpu_manager_) { | ||
| 94 | CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); | ||
| 95 | if (cpu_manager->is_multicore) { | ||
| 96 | cpu_manager->MultiCoreRunSuspendThread(); | ||
| 97 | } else { | ||
| 98 | cpu_manager->SingleCoreRunSuspendThread(); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | void* CpuManager::GetStartFuncParamater() { | ||
| 103 | return static_cast<void*>(this); | ||
| 104 | } | ||
| 105 | |||
| 106 | /////////////////////////////////////////////////////////////////////////////// | ||
| 107 | /// MultiCore /// | ||
| 108 | /////////////////////////////////////////////////////////////////////////////// | ||
| 109 | |||
| 110 | void CpuManager::MultiCoreRunGuestThread() { | ||
| 111 | auto& kernel = system.Kernel(); | ||
| 112 | { | ||
| 113 | auto& sched = kernel.CurrentScheduler(); | ||
| 114 | sched.OnThreadStart(); | ||
| 115 | } | ||
| 116 | MultiCoreRunGuestLoop(); | ||
| 117 | } | ||
| 118 | |||
| 119 | void CpuManager::MultiCoreRunGuestLoop() { | ||
| 120 | auto& kernel = system.Kernel(); | ||
| 121 | auto* thread = kernel.CurrentScheduler().GetCurrentThread(); | ||
| 122 | while (true) { | ||
| 123 | auto* physical_core = &kernel.CurrentPhysicalCore(); | ||
| 124 | auto& arm_interface = thread->ArmInterface(); | ||
| 125 | system.EnterDynarmicProfile(); | ||
| 126 | while (!physical_core->IsInterrupted()) { | ||
| 127 | arm_interface.Run(); | ||
| 128 | physical_core = &kernel.CurrentPhysicalCore(); | ||
| 129 | } | ||
| 130 | system.ExitDynarmicProfile(); | ||
| 131 | arm_interface.ClearExclusiveState(); | ||
| 132 | auto& scheduler = kernel.CurrentScheduler(); | ||
| 133 | scheduler.TryDoContextSwitch(); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | void CpuManager::MultiCoreRunIdleThread() { | ||
| 138 | auto& kernel = system.Kernel(); | ||
| 139 | while (true) { | ||
| 140 | auto& physical_core = kernel.CurrentPhysicalCore(); | ||
| 141 | physical_core.Idle(); | ||
| 142 | auto& scheduler = kernel.CurrentScheduler(); | ||
| 143 | scheduler.TryDoContextSwitch(); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | void CpuManager::MultiCoreRunSuspendThread() { | ||
| 148 | auto& kernel = system.Kernel(); | ||
| 149 | { | ||
| 150 | auto& sched = kernel.CurrentScheduler(); | ||
| 151 | sched.OnThreadStart(); | ||
| 152 | } | ||
| 153 | while (true) { | ||
| 154 | auto core = kernel.GetCurrentHostThreadID(); | ||
| 155 | auto& scheduler = kernel.CurrentScheduler(); | ||
| 156 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 157 | Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context); | ||
| 158 | ASSERT(scheduler.ContextSwitchPending()); | ||
| 159 | ASSERT(core == kernel.GetCurrentHostThreadID()); | ||
| 160 | scheduler.TryDoContextSwitch(); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | void CpuManager::MultiCorePause(bool paused) { | ||
| 165 | if (!paused) { | ||
| 166 | bool all_not_barrier = false; | ||
| 167 | while (!all_not_barrier) { | ||
| 168 | all_not_barrier = true; | ||
| 169 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 170 | all_not_barrier &= | ||
| 171 | !core_data[core].is_running.load() && core_data[core].initialized.load(); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 175 | core_data[core].enter_barrier->Set(); | ||
| 176 | } | ||
| 177 | if (paused_state.load()) { | ||
| 178 | bool all_barrier = false; | ||
| 179 | while (!all_barrier) { | ||
| 180 | all_barrier = true; | ||
| 181 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 182 | all_barrier &= | ||
| 183 | core_data[core].is_paused.load() && core_data[core].initialized.load(); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 187 | core_data[core].exit_barrier->Set(); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | } else { | ||
| 191 | /// Wait until all cores are paused. | ||
| 192 | bool all_barrier = false; | ||
| 193 | while (!all_barrier) { | ||
| 194 | all_barrier = true; | ||
| 195 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 196 | all_barrier &= | ||
| 197 | core_data[core].is_paused.load() && core_data[core].initialized.load(); | ||
| 58 | } | 198 | } |
| 59 | } | 199 | } |
| 200 | /// Don't release the barrier | ||
| 60 | } | 201 | } |
| 202 | paused_state = paused; | ||
| 203 | } | ||
| 204 | |||
| 205 | /////////////////////////////////////////////////////////////////////////////// | ||
| 206 | /// SingleCore /// | ||
| 207 | /////////////////////////////////////////////////////////////////////////////// | ||
| 61 | 208 | ||
| 62 | auto& core_timing = system.CoreTiming(); | 209 | void CpuManager::SingleCoreRunGuestThread() { |
| 63 | core_timing.ResetRun(); | 210 | auto& kernel = system.Kernel(); |
| 64 | bool keep_running{}; | 211 | { |
| 65 | do { | 212 | auto& sched = kernel.CurrentScheduler(); |
| 66 | keep_running = false; | 213 | sched.OnThreadStart(); |
| 67 | for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { | 214 | } |
| 68 | core_timing.SwitchContext(active_core); | 215 | SingleCoreRunGuestLoop(); |
| 69 | if (core_timing.CanCurrentContextRun()) { | 216 | } |
| 70 | core_managers[active_core]->RunLoop(tight_loop); | 217 | |
| 218 | void CpuManager::SingleCoreRunGuestLoop() { | ||
| 219 | auto& kernel = system.Kernel(); | ||
| 220 | auto* thread = kernel.CurrentScheduler().GetCurrentThread(); | ||
| 221 | while (true) { | ||
| 222 | auto* physical_core = &kernel.CurrentPhysicalCore(); | ||
| 223 | auto& arm_interface = thread->ArmInterface(); | ||
| 224 | system.EnterDynarmicProfile(); | ||
| 225 | if (!physical_core->IsInterrupted()) { | ||
| 226 | arm_interface.Run(); | ||
| 227 | physical_core = &kernel.CurrentPhysicalCore(); | ||
| 228 | } | ||
| 229 | system.ExitDynarmicProfile(); | ||
| 230 | thread->SetPhantomMode(true); | ||
| 231 | system.CoreTiming().Advance(); | ||
| 232 | thread->SetPhantomMode(false); | ||
| 233 | arm_interface.ClearExclusiveState(); | ||
| 234 | PreemptSingleCore(); | ||
| 235 | auto& scheduler = kernel.Scheduler(current_core); | ||
| 236 | scheduler.TryDoContextSwitch(); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | void CpuManager::SingleCoreRunIdleThread() { | ||
| 241 | auto& kernel = system.Kernel(); | ||
| 242 | while (true) { | ||
| 243 | auto& physical_core = kernel.CurrentPhysicalCore(); | ||
| 244 | PreemptSingleCore(false); | ||
| 245 | system.CoreTiming().AddTicks(1000U); | ||
| 246 | idle_count++; | ||
| 247 | auto& scheduler = physical_core.Scheduler(); | ||
| 248 | scheduler.TryDoContextSwitch(); | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | void CpuManager::SingleCoreRunSuspendThread() { | ||
| 253 | auto& kernel = system.Kernel(); | ||
| 254 | { | ||
| 255 | auto& sched = kernel.CurrentScheduler(); | ||
| 256 | sched.OnThreadStart(); | ||
| 257 | } | ||
| 258 | while (true) { | ||
| 259 | auto core = kernel.GetCurrentHostThreadID(); | ||
| 260 | auto& scheduler = kernel.CurrentScheduler(); | ||
| 261 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 262 | Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context); | ||
| 263 | ASSERT(scheduler.ContextSwitchPending()); | ||
| 264 | ASSERT(core == kernel.GetCurrentHostThreadID()); | ||
| 265 | scheduler.TryDoContextSwitch(); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | void CpuManager::PreemptSingleCore(bool from_running_enviroment) { | ||
| 270 | std::size_t old_core = current_core; | ||
| 271 | auto& scheduler = system.Kernel().Scheduler(old_core); | ||
| 272 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 273 | if (idle_count >= 4 || from_running_enviroment) { | ||
| 274 | if (!from_running_enviroment) { | ||
| 275 | system.CoreTiming().Idle(); | ||
| 276 | idle_count = 0; | ||
| 277 | } | ||
| 278 | current_thread->SetPhantomMode(true); | ||
| 279 | system.CoreTiming().Advance(); | ||
| 280 | current_thread->SetPhantomMode(false); | ||
| 281 | } | ||
| 282 | current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); | ||
| 283 | system.CoreTiming().ResetTicks(); | ||
| 284 | scheduler.Unload(); | ||
| 285 | auto& next_scheduler = system.Kernel().Scheduler(current_core); | ||
| 286 | Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); | ||
| 287 | /// May have changed scheduler | ||
| 288 | auto& current_scheduler = system.Kernel().Scheduler(current_core); | ||
| 289 | current_scheduler.Reload(); | ||
| 290 | auto* currrent_thread2 = current_scheduler.GetCurrentThread(); | ||
| 291 | if (!currrent_thread2->IsIdleThread()) { | ||
| 292 | idle_count = 0; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | void CpuManager::SingleCorePause(bool paused) { | ||
| 297 | if (!paused) { | ||
| 298 | bool all_not_barrier = false; | ||
| 299 | while (!all_not_barrier) { | ||
| 300 | all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load(); | ||
| 301 | } | ||
| 302 | core_data[0].enter_barrier->Set(); | ||
| 303 | if (paused_state.load()) { | ||
| 304 | bool all_barrier = false; | ||
| 305 | while (!all_barrier) { | ||
| 306 | all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); | ||
| 71 | } | 307 | } |
| 72 | keep_running |= core_timing.CanCurrentContextRun(); | 308 | core_data[0].exit_barrier->Set(); |
| 73 | } | 309 | } |
| 74 | } while (keep_running); | 310 | } else { |
| 311 | /// Wait until all cores are paused. | ||
| 312 | bool all_barrier = false; | ||
| 313 | while (!all_barrier) { | ||
| 314 | all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); | ||
| 315 | } | ||
| 316 | /// Don't release the barrier | ||
| 317 | } | ||
| 318 | paused_state = paused; | ||
| 319 | } | ||
| 320 | |||
| 321 | void CpuManager::Pause(bool paused) { | ||
| 322 | if (is_multicore) { | ||
| 323 | MultiCorePause(paused); | ||
| 324 | } else { | ||
| 325 | SingleCorePause(paused); | ||
| 326 | } | ||
| 327 | } | ||
| 75 | 328 | ||
| 76 | if (GDBStub::IsServerEnabled()) { | 329 | void CpuManager::RunThread(std::size_t core) { |
| 77 | GDBStub::SetCpuStepFlag(false); | 330 | /// Initialization |
| 331 | system.RegisterCoreThread(core); | ||
| 332 | std::string name; | ||
| 333 | if (is_multicore) { | ||
| 334 | name = "yuzu:CoreCPUThread_" + std::to_string(core); | ||
| 335 | } else { | ||
| 336 | name = "yuzu:CPUThread"; | ||
| 337 | } | ||
| 338 | MicroProfileOnThreadCreate(name.c_str()); | ||
| 339 | Common::SetCurrentThreadName(name.c_str()); | ||
| 340 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||
| 341 | auto& data = core_data[core]; | ||
| 342 | data.enter_barrier = std::make_unique<Common::Event>(); | ||
| 343 | data.exit_barrier = std::make_unique<Common::Event>(); | ||
| 344 | data.host_context = Common::Fiber::ThreadToFiber(); | ||
| 345 | data.is_running = false; | ||
| 346 | data.initialized = true; | ||
| 347 | const bool sc_sync = !is_async_gpu && !is_multicore; | ||
| 348 | bool sc_sync_first_use = sc_sync; | ||
| 349 | /// Running | ||
| 350 | while (running_mode) { | ||
| 351 | data.is_running = false; | ||
| 352 | data.enter_barrier->Wait(); | ||
| 353 | if (sc_sync_first_use) { | ||
| 354 | system.GPU().ObtainContext(); | ||
| 355 | sc_sync_first_use = false; | ||
| 356 | } | ||
| 357 | auto& scheduler = system.Kernel().CurrentScheduler(); | ||
| 358 | Kernel::Thread* current_thread = scheduler.GetCurrentThread(); | ||
| 359 | data.is_running = true; | ||
| 360 | Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext()); | ||
| 361 | data.is_running = false; | ||
| 362 | data.is_paused = true; | ||
| 363 | data.exit_barrier->Wait(); | ||
| 364 | data.is_paused = false; | ||
| 78 | } | 365 | } |
| 366 | /// Time to cleanup | ||
| 367 | data.host_context->Exit(); | ||
| 368 | data.enter_barrier.reset(); | ||
| 369 | data.exit_barrier.reset(); | ||
| 370 | data.initialized = false; | ||
| 79 | } | 371 | } |
| 80 | 372 | ||
| 81 | } // namespace Core | 373 | } // namespace Core |
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 97554d1bb..35929ed94 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h | |||
| @@ -5,12 +5,19 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <atomic> | ||
| 9 | #include <functional> | ||
| 8 | #include <memory> | 10 | #include <memory> |
| 11 | #include <thread> | ||
| 9 | #include "core/hardware_properties.h" | 12 | #include "core/hardware_properties.h" |
| 10 | 13 | ||
| 14 | namespace Common { | ||
| 15 | class Event; | ||
| 16 | class Fiber; | ||
| 17 | } // namespace Common | ||
| 18 | |||
| 11 | namespace Core { | 19 | namespace Core { |
| 12 | 20 | ||
| 13 | class CoreManager; | ||
| 14 | class System; | 21 | class System; |
| 15 | 22 | ||
| 16 | class CpuManager { | 23 | class CpuManager { |
| @@ -24,24 +31,75 @@ public: | |||
| 24 | CpuManager& operator=(const CpuManager&) = delete; | 31 | CpuManager& operator=(const CpuManager&) = delete; |
| 25 | CpuManager& operator=(CpuManager&&) = delete; | 32 | CpuManager& operator=(CpuManager&&) = delete; |
| 26 | 33 | ||
| 34 | /// Sets if emulation is multicore or single core, must be set before Initialize | ||
| 35 | void SetMulticore(bool is_multicore) { | ||
| 36 | this->is_multicore = is_multicore; | ||
| 37 | } | ||
| 38 | |||
| 39 | /// Sets if emulation is using an asynchronous GPU. | ||
| 40 | void SetAsyncGpu(bool is_async_gpu) { | ||
| 41 | this->is_async_gpu = is_async_gpu; | ||
| 42 | } | ||
| 43 | |||
| 27 | void Initialize(); | 44 | void Initialize(); |
| 28 | void Shutdown(); | 45 | void Shutdown(); |
| 29 | 46 | ||
| 30 | CoreManager& GetCoreManager(std::size_t index); | 47 | void Pause(bool paused); |
| 31 | const CoreManager& GetCoreManager(std::size_t index) const; | ||
| 32 | 48 | ||
| 33 | CoreManager& GetCurrentCoreManager(); | 49 | std::function<void(void*)> GetGuestThreadStartFunc(); |
| 34 | const CoreManager& GetCurrentCoreManager() const; | 50 | std::function<void(void*)> GetIdleThreadStartFunc(); |
| 51 | std::function<void(void*)> GetSuspendThreadStartFunc(); | ||
| 52 | void* GetStartFuncParamater(); | ||
| 35 | 53 | ||
| 36 | std::size_t GetActiveCoreIndex() const { | 54 | void PreemptSingleCore(bool from_running_enviroment = true); |
| 37 | return active_core; | ||
| 38 | } | ||
| 39 | 55 | ||
| 40 | void RunLoop(bool tight_loop); | 56 | std::size_t CurrentCore() const { |
| 57 | return current_core.load(); | ||
| 58 | } | ||
| 41 | 59 | ||
| 42 | private: | 60 | private: |
| 43 | std::array<std::unique_ptr<CoreManager>, Hardware::NUM_CPU_CORES> core_managers; | 61 | static void GuestThreadFunction(void* cpu_manager); |
| 44 | std::size_t active_core{}; ///< Active core, only used in single thread mode | 62 | static void GuestRewindFunction(void* cpu_manager); |
| 63 | static void IdleThreadFunction(void* cpu_manager); | ||
| 64 | static void SuspendThreadFunction(void* cpu_manager); | ||
| 65 | |||
| 66 | void MultiCoreRunGuestThread(); | ||
| 67 | void MultiCoreRunGuestLoop(); | ||
| 68 | void MultiCoreRunIdleThread(); | ||
| 69 | void MultiCoreRunSuspendThread(); | ||
| 70 | void MultiCorePause(bool paused); | ||
| 71 | |||
| 72 | void SingleCoreRunGuestThread(); | ||
| 73 | void SingleCoreRunGuestLoop(); | ||
| 74 | void SingleCoreRunIdleThread(); | ||
| 75 | void SingleCoreRunSuspendThread(); | ||
| 76 | void SingleCorePause(bool paused); | ||
| 77 | |||
| 78 | static void ThreadStart(CpuManager& cpu_manager, std::size_t core); | ||
| 79 | |||
| 80 | void RunThread(std::size_t core); | ||
| 81 | |||
| 82 | struct CoreData { | ||
| 83 | std::shared_ptr<Common::Fiber> host_context; | ||
| 84 | std::unique_ptr<Common::Event> enter_barrier; | ||
| 85 | std::unique_ptr<Common::Event> exit_barrier; | ||
| 86 | std::atomic<bool> is_running; | ||
| 87 | std::atomic<bool> is_paused; | ||
| 88 | std::atomic<bool> initialized; | ||
| 89 | std::unique_ptr<std::thread> host_thread; | ||
| 90 | }; | ||
| 91 | |||
| 92 | std::atomic<bool> running_mode{}; | ||
| 93 | std::atomic<bool> paused_state{}; | ||
| 94 | |||
| 95 | std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{}; | ||
| 96 | |||
| 97 | bool is_async_gpu{}; | ||
| 98 | bool is_multicore{}; | ||
| 99 | std::atomic<std::size_t> current_core{}; | ||
| 100 | std::size_t preemption_count{}; | ||
| 101 | std::size_t idle_count{}; | ||
| 102 | static constexpr std::size_t max_cycle_runs = 5; | ||
| 45 | 103 | ||
| 46 | System& system; | 104 | System& system; |
| 47 | }; | 105 | }; |
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 70c0f8b80..79f22a403 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -35,7 +35,6 @@ | |||
| 35 | #include "common/swap.h" | 35 | #include "common/swap.h" |
| 36 | #include "core/arm/arm_interface.h" | 36 | #include "core/arm/arm_interface.h" |
| 37 | #include "core/core.h" | 37 | #include "core/core.h" |
| 38 | #include "core/core_manager.h" | ||
| 39 | #include "core/gdbstub/gdbstub.h" | 38 | #include "core/gdbstub/gdbstub.h" |
| 40 | #include "core/hle/kernel/memory/page_table.h" | 39 | #include "core/hle/kernel/memory/page_table.h" |
| 41 | #include "core/hle/kernel/process.h" | 40 | #include "core/hle/kernel/process.h" |
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h index b04e046ed..456b41e1b 100644 --- a/src/core/hardware_properties.h +++ b/src/core/hardware_properties.h | |||
| @@ -42,6 +42,10 @@ struct EmuThreadHandle { | |||
| 42 | constexpr u32 invalid_handle = 0xFFFFFFFF; | 42 | constexpr u32 invalid_handle = 0xFFFFFFFF; |
| 43 | return {invalid_handle, invalid_handle}; | 43 | return {invalid_handle, invalid_handle}; |
| 44 | } | 44 | } |
| 45 | |||
| 46 | bool IsInvalid() const { | ||
| 47 | return (*this) == InvalidHandle(); | ||
| 48 | } | ||
| 45 | }; | 49 | }; |
| 46 | 50 | ||
| 47 | } // namespace Core | 51 | } // namespace Core |
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 8475b698c..4d2a9b35d 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -7,11 +7,15 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/arm/exclusive_monitor.h" | ||
| 10 | #include "core/core.h" | 11 | #include "core/core.h" |
| 11 | #include "core/hle/kernel/address_arbiter.h" | 12 | #include "core/hle/kernel/address_arbiter.h" |
| 12 | #include "core/hle/kernel/errors.h" | 13 | #include "core/hle/kernel/errors.h" |
| 14 | #include "core/hle/kernel/handle_table.h" | ||
| 15 | #include "core/hle/kernel/kernel.h" | ||
| 13 | #include "core/hle/kernel/scheduler.h" | 16 | #include "core/hle/kernel/scheduler.h" |
| 14 | #include "core/hle/kernel/thread.h" | 17 | #include "core/hle/kernel/thread.h" |
| 18 | #include "core/hle/kernel/time_manager.h" | ||
| 15 | #include "core/hle/result.h" | 19 | #include "core/hle/result.h" |
| 16 | #include "core/memory.h" | 20 | #include "core/memory.h" |
| 17 | 21 | ||
| @@ -20,6 +24,7 @@ namespace Kernel { | |||
| 20 | // Wake up num_to_wake (or all) threads in a vector. | 24 | // Wake up num_to_wake (or all) threads in a vector. |
| 21 | void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, | 25 | void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, |
| 22 | s32 num_to_wake) { | 26 | s32 num_to_wake) { |
| 27 | auto& time_manager = system.Kernel().TimeManager(); | ||
| 23 | // Only process up to 'target' threads, unless 'target' is <= 0, in which case process | 28 | // Only process up to 'target' threads, unless 'target' is <= 0, in which case process |
| 24 | // them all. | 29 | // them all. |
| 25 | std::size_t last = waiting_threads.size(); | 30 | std::size_t last = waiting_threads.size(); |
| @@ -29,12 +34,10 @@ void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& wai | |||
| 29 | 34 | ||
| 30 | // Signal the waiting threads. | 35 | // Signal the waiting threads. |
| 31 | for (std::size_t i = 0; i < last; i++) { | 36 | for (std::size_t i = 0; i < last; i++) { |
| 32 | ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb); | 37 | waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS); |
| 33 | waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 34 | RemoveThread(waiting_threads[i]); | 38 | RemoveThread(waiting_threads[i]); |
| 35 | waiting_threads[i]->SetArbiterWaitAddress(0); | 39 | waiting_threads[i]->WaitForArbitration(false); |
| 36 | waiting_threads[i]->ResumeFromWait(); | 40 | waiting_threads[i]->ResumeFromWait(); |
| 37 | system.PrepareReschedule(waiting_threads[i]->GetProcessorID()); | ||
| 38 | } | 41 | } |
| 39 | } | 42 | } |
| 40 | 43 | ||
| @@ -56,6 +59,7 @@ ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 v | |||
| 56 | } | 59 | } |
| 57 | 60 | ||
| 58 | ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { | 61 | ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { |
| 62 | SchedulerLock lock(system.Kernel()); | ||
| 59 | const std::vector<std::shared_ptr<Thread>> waiting_threads = | 63 | const std::vector<std::shared_ptr<Thread>> waiting_threads = |
| 60 | GetThreadsWaitingOnAddress(address); | 64 | GetThreadsWaitingOnAddress(address); |
| 61 | WakeThreads(waiting_threads, num_to_wake); | 65 | WakeThreads(waiting_threads, num_to_wake); |
| @@ -64,6 +68,7 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { | |||
| 64 | 68 | ||
| 65 | ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, | 69 | ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, |
| 66 | s32 num_to_wake) { | 70 | s32 num_to_wake) { |
| 71 | SchedulerLock lock(system.Kernel()); | ||
| 67 | auto& memory = system.Memory(); | 72 | auto& memory = system.Memory(); |
| 68 | 73 | ||
| 69 | // Ensure that we can write to the address. | 74 | // Ensure that we can write to the address. |
| @@ -71,16 +76,24 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 | |||
| 71 | return ERR_INVALID_ADDRESS_STATE; | 76 | return ERR_INVALID_ADDRESS_STATE; |
| 72 | } | 77 | } |
| 73 | 78 | ||
| 74 | if (static_cast<s32>(memory.Read32(address)) != value) { | 79 | const std::size_t current_core = system.CurrentCoreIndex(); |
| 75 | return ERR_INVALID_STATE; | 80 | auto& monitor = system.Monitor(); |
| 76 | } | 81 | u32 current_value; |
| 82 | do { | ||
| 83 | current_value = monitor.ExclusiveRead32(current_core, address); | ||
| 84 | |||
| 85 | if (current_value != value) { | ||
| 86 | return ERR_INVALID_STATE; | ||
| 87 | } | ||
| 88 | current_value++; | ||
| 89 | } while (!monitor.ExclusiveWrite32(current_core, address, current_value)); | ||
| 77 | 90 | ||
| 78 | memory.Write32(address, static_cast<u32>(value + 1)); | ||
| 79 | return SignalToAddressOnly(address, num_to_wake); | 91 | return SignalToAddressOnly(address, num_to_wake); |
| 80 | } | 92 | } |
| 81 | 93 | ||
| 82 | ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, | 94 | ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, |
| 83 | s32 num_to_wake) { | 95 | s32 num_to_wake) { |
| 96 | SchedulerLock lock(system.Kernel()); | ||
| 84 | auto& memory = system.Memory(); | 97 | auto& memory = system.Memory(); |
| 85 | 98 | ||
| 86 | // Ensure that we can write to the address. | 99 | // Ensure that we can write to the address. |
| @@ -92,29 +105,33 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a | |||
| 92 | const std::vector<std::shared_ptr<Thread>> waiting_threads = | 105 | const std::vector<std::shared_ptr<Thread>> waiting_threads = |
| 93 | GetThreadsWaitingOnAddress(address); | 106 | GetThreadsWaitingOnAddress(address); |
| 94 | 107 | ||
| 95 | // Determine the modified value depending on the waiting count. | 108 | const std::size_t current_core = system.CurrentCoreIndex(); |
| 109 | auto& monitor = system.Monitor(); | ||
| 96 | s32 updated_value; | 110 | s32 updated_value; |
| 97 | if (num_to_wake <= 0) { | 111 | do { |
| 98 | if (waiting_threads.empty()) { | 112 | updated_value = monitor.ExclusiveRead32(current_core, address); |
| 99 | updated_value = value + 1; | 113 | |
| 100 | } else { | 114 | if (updated_value != value) { |
| 101 | updated_value = value - 1; | 115 | return ERR_INVALID_STATE; |
| 102 | } | 116 | } |
| 103 | } else { | 117 | // Determine the modified value depending on the waiting count. |
| 104 | if (waiting_threads.empty()) { | 118 | if (num_to_wake <= 0) { |
| 105 | updated_value = value + 1; | 119 | if (waiting_threads.empty()) { |
| 106 | } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) { | 120 | updated_value = value + 1; |
| 107 | updated_value = value - 1; | 121 | } else { |
| 122 | updated_value = value - 1; | ||
| 123 | } | ||
| 108 | } else { | 124 | } else { |
| 109 | updated_value = value; | 125 | if (waiting_threads.empty()) { |
| 126 | updated_value = value + 1; | ||
| 127 | } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) { | ||
| 128 | updated_value = value - 1; | ||
| 129 | } else { | ||
| 130 | updated_value = value; | ||
| 131 | } | ||
| 110 | } | 132 | } |
| 111 | } | 133 | } while (!monitor.ExclusiveWrite32(current_core, address, updated_value)); |
| 112 | 134 | ||
| 113 | if (static_cast<s32>(memory.Read32(address)) != value) { | ||
| 114 | return ERR_INVALID_STATE; | ||
| 115 | } | ||
| 116 | |||
| 117 | memory.Write32(address, static_cast<u32>(updated_value)); | ||
| 118 | WakeThreads(waiting_threads, num_to_wake); | 135 | WakeThreads(waiting_threads, num_to_wake); |
| 119 | return RESULT_SUCCESS; | 136 | return RESULT_SUCCESS; |
| 120 | } | 137 | } |
| @@ -136,60 +153,127 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s | |||
| 136 | ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, | 153 | ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, |
| 137 | bool should_decrement) { | 154 | bool should_decrement) { |
| 138 | auto& memory = system.Memory(); | 155 | auto& memory = system.Memory(); |
| 156 | auto& kernel = system.Kernel(); | ||
| 157 | Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); | ||
| 139 | 158 | ||
| 140 | // Ensure that we can read the address. | 159 | Handle event_handle = InvalidHandle; |
| 141 | if (!memory.IsValidVirtualAddress(address)) { | 160 | { |
| 142 | return ERR_INVALID_ADDRESS_STATE; | 161 | SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); |
| 143 | } | 162 | |
| 163 | if (current_thread->IsPendingTermination()) { | ||
| 164 | lock.CancelSleep(); | ||
| 165 | return ERR_THREAD_TERMINATING; | ||
| 166 | } | ||
| 167 | |||
| 168 | // Ensure that we can read the address. | ||
| 169 | if (!memory.IsValidVirtualAddress(address)) { | ||
| 170 | lock.CancelSleep(); | ||
| 171 | return ERR_INVALID_ADDRESS_STATE; | ||
| 172 | } | ||
| 173 | |||
| 174 | s32 current_value = static_cast<s32>(memory.Read32(address)); | ||
| 175 | if (current_value >= value) { | ||
| 176 | lock.CancelSleep(); | ||
| 177 | return ERR_INVALID_STATE; | ||
| 178 | } | ||
| 179 | |||
| 180 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 181 | |||
| 182 | s32 decrement_value; | ||
| 183 | |||
| 184 | const std::size_t current_core = system.CurrentCoreIndex(); | ||
| 185 | auto& monitor = system.Monitor(); | ||
| 186 | do { | ||
| 187 | current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address)); | ||
| 188 | if (should_decrement) { | ||
| 189 | decrement_value = current_value - 1; | ||
| 190 | } else { | ||
| 191 | decrement_value = current_value; | ||
| 192 | } | ||
| 193 | } while ( | ||
| 194 | !monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))); | ||
| 195 | |||
| 196 | // Short-circuit without rescheduling, if timeout is zero. | ||
| 197 | if (timeout == 0) { | ||
| 198 | lock.CancelSleep(); | ||
| 199 | return RESULT_TIMEOUT; | ||
| 200 | } | ||
| 144 | 201 | ||
| 145 | const s32 cur_value = static_cast<s32>(memory.Read32(address)); | 202 | current_thread->SetArbiterWaitAddress(address); |
| 146 | if (cur_value >= value) { | 203 | InsertThread(SharedFrom(current_thread)); |
| 147 | return ERR_INVALID_STATE; | 204 | current_thread->SetStatus(ThreadStatus::WaitArb); |
| 205 | current_thread->WaitForArbitration(true); | ||
| 148 | } | 206 | } |
| 149 | 207 | ||
| 150 | if (should_decrement) { | 208 | if (event_handle != InvalidHandle) { |
| 151 | memory.Write32(address, static_cast<u32>(cur_value - 1)); | 209 | auto& time_manager = kernel.TimeManager(); |
| 210 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 152 | } | 211 | } |
| 153 | 212 | ||
| 154 | // Short-circuit without rescheduling, if timeout is zero. | 213 | { |
| 155 | if (timeout == 0) { | 214 | SchedulerLock lock(kernel); |
| 156 | return RESULT_TIMEOUT; | 215 | if (current_thread->IsWaitingForArbitration()) { |
| 216 | RemoveThread(SharedFrom(current_thread)); | ||
| 217 | current_thread->WaitForArbitration(false); | ||
| 218 | } | ||
| 157 | } | 219 | } |
| 158 | 220 | ||
| 159 | return WaitForAddressImpl(address, timeout); | 221 | return current_thread->GetSignalingResult(); |
| 160 | } | 222 | } |
| 161 | 223 | ||
| 162 | ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { | 224 | ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { |
| 163 | auto& memory = system.Memory(); | 225 | auto& memory = system.Memory(); |
| 226 | auto& kernel = system.Kernel(); | ||
| 227 | Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); | ||
| 164 | 228 | ||
| 165 | // Ensure that we can read the address. | 229 | Handle event_handle = InvalidHandle; |
| 166 | if (!memory.IsValidVirtualAddress(address)) { | 230 | { |
| 167 | return ERR_INVALID_ADDRESS_STATE; | 231 | SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); |
| 168 | } | 232 | |
| 233 | if (current_thread->IsPendingTermination()) { | ||
| 234 | lock.CancelSleep(); | ||
| 235 | return ERR_THREAD_TERMINATING; | ||
| 236 | } | ||
| 237 | |||
| 238 | // Ensure that we can read the address. | ||
| 239 | if (!memory.IsValidVirtualAddress(address)) { | ||
| 240 | lock.CancelSleep(); | ||
| 241 | return ERR_INVALID_ADDRESS_STATE; | ||
| 242 | } | ||
| 169 | 243 | ||
| 170 | // Only wait for the address if equal. | 244 | s32 current_value = static_cast<s32>(memory.Read32(address)); |
| 171 | if (static_cast<s32>(memory.Read32(address)) != value) { | 245 | if (current_value != value) { |
| 172 | return ERR_INVALID_STATE; | 246 | lock.CancelSleep(); |
| 247 | return ERR_INVALID_STATE; | ||
| 248 | } | ||
| 249 | |||
| 250 | // Short-circuit without rescheduling, if timeout is zero. | ||
| 251 | if (timeout == 0) { | ||
| 252 | lock.CancelSleep(); | ||
| 253 | return RESULT_TIMEOUT; | ||
| 254 | } | ||
| 255 | |||
| 256 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 257 | current_thread->SetArbiterWaitAddress(address); | ||
| 258 | InsertThread(SharedFrom(current_thread)); | ||
| 259 | current_thread->SetStatus(ThreadStatus::WaitArb); | ||
| 260 | current_thread->WaitForArbitration(true); | ||
| 173 | } | 261 | } |
| 174 | 262 | ||
| 175 | // Short-circuit without rescheduling if timeout is zero. | 263 | if (event_handle != InvalidHandle) { |
| 176 | if (timeout == 0) { | 264 | auto& time_manager = kernel.TimeManager(); |
| 177 | return RESULT_TIMEOUT; | 265 | time_manager.UnscheduleTimeEvent(event_handle); |
| 178 | } | 266 | } |
| 179 | 267 | ||
| 180 | return WaitForAddressImpl(address, timeout); | 268 | { |
| 181 | } | 269 | SchedulerLock lock(kernel); |
| 270 | if (current_thread->IsWaitingForArbitration()) { | ||
| 271 | RemoveThread(SharedFrom(current_thread)); | ||
| 272 | current_thread->WaitForArbitration(false); | ||
| 273 | } | ||
| 274 | } | ||
| 182 | 275 | ||
| 183 | ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) { | 276 | return current_thread->GetSignalingResult(); |
| 184 | Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); | ||
| 185 | current_thread->SetArbiterWaitAddress(address); | ||
| 186 | InsertThread(SharedFrom(current_thread)); | ||
| 187 | current_thread->SetStatus(ThreadStatus::WaitArb); | ||
| 188 | current_thread->InvalidateWakeupCallback(); | ||
| 189 | current_thread->WakeAfterDelay(timeout); | ||
| 190 | |||
| 191 | system.PrepareReschedule(current_thread->GetProcessorID()); | ||
| 192 | return RESULT_TIMEOUT; | ||
| 193 | } | 277 | } |
| 194 | 278 | ||
| 195 | void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) { | 279 | void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) { |
| @@ -221,9 +305,9 @@ void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) { | |||
| 221 | const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(), | 305 | const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(), |
| 222 | [&thread](const auto& entry) { return thread == entry; }); | 306 | [&thread](const auto& entry) { return thread == entry; }); |
| 223 | 307 | ||
| 224 | ASSERT(iter != thread_list.cend()); | 308 | if (iter != thread_list.cend()) { |
| 225 | 309 | thread_list.erase(iter); | |
| 226 | thread_list.erase(iter); | 310 | } |
| 227 | } | 311 | } |
| 228 | 312 | ||
| 229 | std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress( | 313 | std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress( |
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index f958eee5a..0b05d533c 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h | |||
| @@ -73,9 +73,6 @@ private: | |||
| 73 | /// Waits on an address if the value passed is equal to the argument value. | 73 | /// Waits on an address if the value passed is equal to the argument value. |
| 74 | ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); | 74 | ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); |
| 75 | 75 | ||
| 76 | // Waits on the given address with a timeout in nanoseconds | ||
| 77 | ResultCode WaitForAddressImpl(VAddr address, s64 timeout); | ||
| 78 | |||
| 79 | /// Wake up num_to_wake (or all) threads in a vector. | 76 | /// Wake up num_to_wake (or all) threads in a vector. |
| 80 | void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake); | 77 | void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake); |
| 81 | 78 | ||
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index 5498fd313..8aff2227a 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp | |||
| @@ -34,7 +34,7 @@ ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() { | |||
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | // Wake the threads waiting on the ServerPort | 36 | // Wake the threads waiting on the ServerPort |
| 37 | server_port->WakeupAllWaitingThreads(); | 37 | server_port->Signal(); |
| 38 | 38 | ||
| 39 | return MakeResult(std::move(client)); | 39 | return MakeResult(std::move(client)); |
| 40 | } | 40 | } |
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 29bfa3621..d4e5d88cf 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h | |||
| @@ -12,6 +12,7 @@ namespace Kernel { | |||
| 12 | 12 | ||
| 13 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; | 13 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; |
| 14 | constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; | 14 | constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; |
| 15 | constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59}; | ||
| 15 | constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; | 16 | constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; |
| 16 | constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; | 17 | constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; |
| 17 | constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; | 18 | constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index ba0eac4c2..9277b5d08 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -14,14 +14,17 @@ | |||
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 16 | #include "core/hle/ipc_helpers.h" | 16 | #include "core/hle/ipc_helpers.h" |
| 17 | #include "core/hle/kernel/errors.h" | ||
| 17 | #include "core/hle/kernel/handle_table.h" | 18 | #include "core/hle/kernel/handle_table.h" |
| 18 | #include "core/hle/kernel/hle_ipc.h" | 19 | #include "core/hle/kernel/hle_ipc.h" |
| 19 | #include "core/hle/kernel/kernel.h" | 20 | #include "core/hle/kernel/kernel.h" |
| 20 | #include "core/hle/kernel/object.h" | 21 | #include "core/hle/kernel/object.h" |
| 21 | #include "core/hle/kernel/process.h" | 22 | #include "core/hle/kernel/process.h" |
| 22 | #include "core/hle/kernel/readable_event.h" | 23 | #include "core/hle/kernel/readable_event.h" |
| 24 | #include "core/hle/kernel/scheduler.h" | ||
| 23 | #include "core/hle/kernel/server_session.h" | 25 | #include "core/hle/kernel/server_session.h" |
| 24 | #include "core/hle/kernel/thread.h" | 26 | #include "core/hle/kernel/thread.h" |
| 27 | #include "core/hle/kernel/time_manager.h" | ||
| 25 | #include "core/hle/kernel/writable_event.h" | 28 | #include "core/hle/kernel/writable_event.h" |
| 26 | #include "core/memory.h" | 29 | #include "core/memory.h" |
| 27 | 30 | ||
| @@ -46,15 +49,6 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread( | |||
| 46 | const std::string& reason, u64 timeout, WakeupCallback&& callback, | 49 | const std::string& reason, u64 timeout, WakeupCallback&& callback, |
| 47 | std::shared_ptr<WritableEvent> writable_event) { | 50 | std::shared_ptr<WritableEvent> writable_event) { |
| 48 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. | 51 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. |
| 49 | thread->SetWakeupCallback( | ||
| 50 | [context = *this, callback](ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | ||
| 51 | std::shared_ptr<SynchronizationObject> object, | ||
| 52 | std::size_t index) mutable -> bool { | ||
| 53 | ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent); | ||
| 54 | callback(thread, context, reason); | ||
| 55 | context.WriteToOutgoingCommandBuffer(*thread); | ||
| 56 | return true; | ||
| 57 | }); | ||
| 58 | 52 | ||
| 59 | if (!writable_event) { | 53 | if (!writable_event) { |
| 60 | // Create event if not provided | 54 | // Create event if not provided |
| @@ -62,14 +56,26 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread( | |||
| 62 | writable_event = pair.writable; | 56 | writable_event = pair.writable; |
| 63 | } | 57 | } |
| 64 | 58 | ||
| 65 | const auto readable_event{writable_event->GetReadableEvent()}; | 59 | { |
| 66 | writable_event->Clear(); | 60 | Handle event_handle = InvalidHandle; |
| 67 | thread->SetStatus(ThreadStatus::WaitHLEEvent); | 61 | SchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout); |
| 68 | thread->SetSynchronizationObjects({readable_event}); | 62 | thread->SetHLECallback( |
| 69 | readable_event->AddWaitingThread(thread); | 63 | [context = *this, callback](std::shared_ptr<Thread> thread) mutable -> bool { |
| 70 | 64 | ThreadWakeupReason reason = thread->GetSignalingResult() == RESULT_TIMEOUT | |
| 71 | if (timeout > 0) { | 65 | ? ThreadWakeupReason::Timeout |
| 72 | thread->WakeAfterDelay(timeout); | 66 | : ThreadWakeupReason::Signal; |
| 67 | callback(thread, context, reason); | ||
| 68 | context.WriteToOutgoingCommandBuffer(*thread); | ||
| 69 | return true; | ||
| 70 | }); | ||
| 71 | const auto readable_event{writable_event->GetReadableEvent()}; | ||
| 72 | writable_event->Clear(); | ||
| 73 | thread->SetHLESyncObject(readable_event.get()); | ||
| 74 | thread->SetStatus(ThreadStatus::WaitHLEEvent); | ||
| 75 | thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 76 | readable_event->AddWaitingThread(thread); | ||
| 77 | lock.Release(); | ||
| 78 | thread->SetHLETimeEvent(event_handle); | ||
| 73 | } | 79 | } |
| 74 | 80 | ||
| 75 | is_thread_waiting = true; | 81 | is_thread_waiting = true; |
| @@ -282,18 +288,18 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { | |||
| 282 | } | 288 | } |
| 283 | 289 | ||
| 284 | std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { | 290 | std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { |
| 285 | std::vector<u8> buffer; | 291 | std::vector<u8> buffer{}; |
| 286 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && | 292 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && |
| 287 | BufferDescriptorA()[buffer_index].Size()}; | 293 | BufferDescriptorA()[buffer_index].Size()}; |
| 288 | 294 | ||
| 289 | if (is_buffer_a) { | 295 | if (is_buffer_a) { |
| 290 | ASSERT_MSG(BufferDescriptorA().size() > buffer_index, | 296 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return buffer; }, |
| 291 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | 297 | "BufferDescriptorA invalid buffer_index {}", buffer_index); |
| 292 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); | 298 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); |
| 293 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); | 299 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 294 | } else { | 300 | } else { |
| 295 | ASSERT_MSG(BufferDescriptorX().size() > buffer_index, | 301 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return buffer; }, |
| 296 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | 302 | "BufferDescriptorX invalid buffer_index {}", buffer_index); |
| 297 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); | 303 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); |
| 298 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); | 304 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 299 | } | 305 | } |
| @@ -318,16 +324,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, | |||
| 318 | } | 324 | } |
| 319 | 325 | ||
| 320 | if (is_buffer_b) { | 326 | if (is_buffer_b) { |
| 321 | ASSERT_MSG(BufferDescriptorB().size() > buffer_index, | 327 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index && |
| 322 | "BufferDescriptorB invalid buffer_index {}", buffer_index); | 328 | BufferDescriptorB()[buffer_index].Size() >= size, |
| 323 | ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size, | 329 | { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", |
| 324 | "BufferDescriptorB buffer_index {} is not large enough", buffer_index); | 330 | buffer_index, size); |
| 325 | memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); | 331 | memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); |
| 326 | } else { | 332 | } else { |
| 327 | ASSERT_MSG(BufferDescriptorC().size() > buffer_index, | 333 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index && |
| 328 | "BufferDescriptorC invalid buffer_index {}", buffer_index); | 334 | BufferDescriptorC()[buffer_index].Size() >= size, |
| 329 | ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size, | 335 | { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", |
| 330 | "BufferDescriptorC buffer_index {} is not large enough", buffer_index); | 336 | buffer_index, size); |
| 331 | memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); | 337 | memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); |
| 332 | } | 338 | } |
| 333 | 339 | ||
| @@ -338,16 +344,12 @@ std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const | |||
| 338 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && | 344 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && |
| 339 | BufferDescriptorA()[buffer_index].Size()}; | 345 | BufferDescriptorA()[buffer_index].Size()}; |
| 340 | if (is_buffer_a) { | 346 | if (is_buffer_a) { |
| 341 | ASSERT_MSG(BufferDescriptorA().size() > buffer_index, | 347 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return 0; }, |
| 342 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | 348 | "BufferDescriptorA invalid buffer_index {}", buffer_index); |
| 343 | ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0, | ||
| 344 | "BufferDescriptorA buffer_index {} is empty", buffer_index); | ||
| 345 | return BufferDescriptorA()[buffer_index].Size(); | 349 | return BufferDescriptorA()[buffer_index].Size(); |
| 346 | } else { | 350 | } else { |
| 347 | ASSERT_MSG(BufferDescriptorX().size() > buffer_index, | 351 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return 0; }, |
| 348 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | 352 | "BufferDescriptorX invalid buffer_index {}", buffer_index); |
| 349 | ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0, | ||
| 350 | "BufferDescriptorX buffer_index {} is empty", buffer_index); | ||
| 351 | return BufferDescriptorX()[buffer_index].Size(); | 353 | return BufferDescriptorX()[buffer_index].Size(); |
| 352 | } | 354 | } |
| 353 | } | 355 | } |
| @@ -356,14 +358,15 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons | |||
| 356 | const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && | 358 | const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && |
| 357 | BufferDescriptorB()[buffer_index].Size()}; | 359 | BufferDescriptorB()[buffer_index].Size()}; |
| 358 | if (is_buffer_b) { | 360 | if (is_buffer_b) { |
| 359 | ASSERT_MSG(BufferDescriptorB().size() > buffer_index, | 361 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index, { return 0; }, |
| 360 | "BufferDescriptorB invalid buffer_index {}", buffer_index); | 362 | "BufferDescriptorB invalid buffer_index {}", buffer_index); |
| 361 | return BufferDescriptorB()[buffer_index].Size(); | 363 | return BufferDescriptorB()[buffer_index].Size(); |
| 362 | } else { | 364 | } else { |
| 363 | ASSERT_MSG(BufferDescriptorC().size() > buffer_index, | 365 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index, { return 0; }, |
| 364 | "BufferDescriptorC invalid buffer_index {}", buffer_index); | 366 | "BufferDescriptorC invalid buffer_index {}", buffer_index); |
| 365 | return BufferDescriptorC()[buffer_index].Size(); | 367 | return BufferDescriptorC()[buffer_index].Size(); |
| 366 | } | 368 | } |
| 369 | return 0; | ||
| 367 | } | 370 | } |
| 368 | 371 | ||
| 369 | std::string HLERequestContext::Description() const { | 372 | std::string HLERequestContext::Description() const { |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 7655382fa..1f2af7a1b 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | ||
| 5 | #include <atomic> | 6 | #include <atomic> |
| 6 | #include <bitset> | 7 | #include <bitset> |
| 7 | #include <functional> | 8 | #include <functional> |
| @@ -13,11 +14,15 @@ | |||
| 13 | 14 | ||
| 14 | #include "common/assert.h" | 15 | #include "common/assert.h" |
| 15 | #include "common/logging/log.h" | 16 | #include "common/logging/log.h" |
| 17 | #include "common/microprofile.h" | ||
| 18 | #include "common/thread.h" | ||
| 16 | #include "core/arm/arm_interface.h" | 19 | #include "core/arm/arm_interface.h" |
| 20 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 17 | #include "core/arm/exclusive_monitor.h" | 21 | #include "core/arm/exclusive_monitor.h" |
| 18 | #include "core/core.h" | 22 | #include "core/core.h" |
| 19 | #include "core/core_timing.h" | 23 | #include "core/core_timing.h" |
| 20 | #include "core/core_timing_util.h" | 24 | #include "core/core_timing_util.h" |
| 25 | #include "core/cpu_manager.h" | ||
| 21 | #include "core/device_memory.h" | 26 | #include "core/device_memory.h" |
| 22 | #include "core/hardware_properties.h" | 27 | #include "core/hardware_properties.h" |
| 23 | #include "core/hle/kernel/client_port.h" | 28 | #include "core/hle/kernel/client_port.h" |
| @@ -39,85 +44,28 @@ | |||
| 39 | #include "core/hle/result.h" | 44 | #include "core/hle/result.h" |
| 40 | #include "core/memory.h" | 45 | #include "core/memory.h" |
| 41 | 46 | ||
| 42 | namespace Kernel { | 47 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); |
| 43 | |||
| 44 | /** | ||
| 45 | * Callback that will wake up the thread it was scheduled for | ||
| 46 | * @param thread_handle The handle of the thread that's been awoken | ||
| 47 | * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time | ||
| 48 | */ | ||
| 49 | static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_late) { | ||
| 50 | const auto proper_handle = static_cast<Handle>(thread_handle); | ||
| 51 | const auto& system = Core::System::GetInstance(); | ||
| 52 | |||
| 53 | // Lock the global kernel mutex when we enter the kernel HLE. | ||
| 54 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 55 | |||
| 56 | std::shared_ptr<Thread> thread = | ||
| 57 | system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); | ||
| 58 | if (thread == nullptr) { | ||
| 59 | LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | |||
| 63 | bool resume = true; | ||
| 64 | |||
| 65 | if (thread->GetStatus() == ThreadStatus::WaitSynch || | ||
| 66 | thread->GetStatus() == ThreadStatus::WaitHLEEvent) { | ||
| 67 | // Remove the thread from each of its waiting objects' waitlists | ||
| 68 | for (const auto& object : thread->GetSynchronizationObjects()) { | ||
| 69 | object->RemoveWaitingThread(thread); | ||
| 70 | } | ||
| 71 | thread->ClearSynchronizationObjects(); | ||
| 72 | |||
| 73 | // Invoke the wakeup callback before clearing the wait objects | ||
| 74 | if (thread->HasWakeupCallback()) { | ||
| 75 | resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0); | ||
| 76 | } | ||
| 77 | } else if (thread->GetStatus() == ThreadStatus::WaitMutex || | ||
| 78 | thread->GetStatus() == ThreadStatus::WaitCondVar) { | ||
| 79 | thread->SetMutexWaitAddress(0); | ||
| 80 | thread->SetWaitHandle(0); | ||
| 81 | if (thread->GetStatus() == ThreadStatus::WaitCondVar) { | ||
| 82 | thread->GetOwnerProcess()->RemoveConditionVariableThread(thread); | ||
| 83 | thread->SetCondVarWaitAddress(0); | ||
| 84 | } | ||
| 85 | |||
| 86 | auto* const lock_owner = thread->GetLockOwner(); | ||
| 87 | // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance | ||
| 88 | // and don't have a lock owner unless SignalProcessWideKey was called first and the thread | ||
| 89 | // wasn't awakened due to the mutex already being acquired. | ||
| 90 | if (lock_owner != nullptr) { | ||
| 91 | lock_owner->RemoveMutexWaiter(thread); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | 48 | ||
| 95 | if (thread->GetStatus() == ThreadStatus::WaitArb) { | 49 | namespace Kernel { |
| 96 | auto& address_arbiter = thread->GetOwnerProcess()->GetAddressArbiter(); | ||
| 97 | address_arbiter.HandleWakeupThread(thread); | ||
| 98 | } | ||
| 99 | |||
| 100 | if (resume) { | ||
| 101 | if (thread->GetStatus() == ThreadStatus::WaitCondVar || | ||
| 102 | thread->GetStatus() == ThreadStatus::WaitArb) { | ||
| 103 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 104 | } | ||
| 105 | thread->ResumeFromWait(); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | 50 | ||
| 109 | struct KernelCore::Impl { | 51 | struct KernelCore::Impl { |
| 110 | explicit Impl(Core::System& system, KernelCore& kernel) | 52 | explicit Impl(Core::System& system, KernelCore& kernel) |
| 111 | : global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {} | 53 | : global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {} |
| 112 | 54 | ||
| 55 | void SetMulticore(bool is_multicore) { | ||
| 56 | this->is_multicore = is_multicore; | ||
| 57 | } | ||
| 58 | |||
| 113 | void Initialize(KernelCore& kernel) { | 59 | void Initialize(KernelCore& kernel) { |
| 114 | Shutdown(); | 60 | Shutdown(); |
| 61 | RegisterHostThread(); | ||
| 115 | 62 | ||
| 116 | InitializePhysicalCores(); | 63 | InitializePhysicalCores(); |
| 117 | InitializeSystemResourceLimit(kernel); | 64 | InitializeSystemResourceLimit(kernel); |
| 118 | InitializeMemoryLayout(); | 65 | InitializeMemoryLayout(); |
| 119 | InitializeThreads(); | 66 | InitializePreemption(kernel); |
| 120 | InitializePreemption(); | 67 | InitializeSchedulers(); |
| 68 | InitializeSuspendThreads(); | ||
| 121 | } | 69 | } |
| 122 | 70 | ||
| 123 | void Shutdown() { | 71 | void Shutdown() { |
| @@ -126,13 +74,26 @@ struct KernelCore::Impl { | |||
| 126 | next_user_process_id = Process::ProcessIDMin; | 74 | next_user_process_id = Process::ProcessIDMin; |
| 127 | next_thread_id = 1; | 75 | next_thread_id = 1; |
| 128 | 76 | ||
| 77 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 78 | if (suspend_threads[i]) { | ||
| 79 | suspend_threads[i].reset(); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | for (std::size_t i = 0; i < cores.size(); i++) { | ||
| 84 | cores[i].Shutdown(); | ||
| 85 | schedulers[i].reset(); | ||
| 86 | } | ||
| 87 | cores.clear(); | ||
| 88 | |||
| 89 | registered_core_threads.reset(); | ||
| 90 | |||
| 129 | process_list.clear(); | 91 | process_list.clear(); |
| 130 | current_process = nullptr; | 92 | current_process = nullptr; |
| 131 | 93 | ||
| 132 | system_resource_limit = nullptr; | 94 | system_resource_limit = nullptr; |
| 133 | 95 | ||
| 134 | global_handle_table.Clear(); | 96 | global_handle_table.Clear(); |
| 135 | thread_wakeup_event_type = nullptr; | ||
| 136 | preemption_event = nullptr; | 97 | preemption_event = nullptr; |
| 137 | 98 | ||
| 138 | global_scheduler.Shutdown(); | 99 | global_scheduler.Shutdown(); |
| @@ -145,13 +106,21 @@ struct KernelCore::Impl { | |||
| 145 | cores.clear(); | 106 | cores.clear(); |
| 146 | 107 | ||
| 147 | exclusive_monitor.reset(); | 108 | exclusive_monitor.reset(); |
| 109 | host_thread_ids.clear(); | ||
| 148 | } | 110 | } |
| 149 | 111 | ||
| 150 | void InitializePhysicalCores() { | 112 | void InitializePhysicalCores() { |
| 151 | exclusive_monitor = | 113 | exclusive_monitor = |
| 152 | Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); | 114 | Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); |
| 153 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | 115 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { |
| 154 | cores.emplace_back(system, i, *exclusive_monitor); | 116 | schedulers[i] = std::make_unique<Kernel::Scheduler>(system, i); |
| 117 | cores.emplace_back(system, i, *schedulers[i], interrupts[i]); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | void InitializeSchedulers() { | ||
| 122 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 123 | cores[i].Scheduler().Initialize(); | ||
| 155 | } | 124 | } |
| 156 | } | 125 | } |
| 157 | 126 | ||
| @@ -173,15 +142,13 @@ struct KernelCore::Impl { | |||
| 173 | } | 142 | } |
| 174 | } | 143 | } |
| 175 | 144 | ||
| 176 | void InitializeThreads() { | 145 | void InitializePreemption(KernelCore& kernel) { |
| 177 | thread_wakeup_event_type = | 146 | preemption_event = Core::Timing::CreateEvent( |
| 178 | Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback); | 147 | "PreemptionCallback", [this, &kernel](u64 userdata, s64 cycles_late) { |
| 179 | } | 148 | { |
| 180 | 149 | SchedulerLock lock(kernel); | |
| 181 | void InitializePreemption() { | 150 | global_scheduler.PreemptThreads(); |
| 182 | preemption_event = | 151 | } |
| 183 | Core::Timing::CreateEvent("PreemptionCallback", [this](u64 userdata, s64 cycles_late) { | ||
| 184 | global_scheduler.PreemptThreads(); | ||
| 185 | s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); | 152 | s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); |
| 186 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 153 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 187 | }); | 154 | }); |
| @@ -190,6 +157,20 @@ struct KernelCore::Impl { | |||
| 190 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 157 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 191 | } | 158 | } |
| 192 | 159 | ||
| 160 | void InitializeSuspendThreads() { | ||
| 161 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 162 | std::string name = "Suspend Thread Id:" + std::to_string(i); | ||
| 163 | std::function<void(void*)> init_func = | ||
| 164 | system.GetCpuManager().GetSuspendThreadStartFunc(); | ||
| 165 | void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); | ||
| 166 | ThreadType type = | ||
| 167 | static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND); | ||
| 168 | auto thread_res = Thread::Create(system, type, name, 0, 0, 0, static_cast<u32>(i), 0, | ||
| 169 | nullptr, std::move(init_func), init_func_parameter); | ||
| 170 | suspend_threads[i] = std::move(thread_res).Unwrap(); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 193 | void MakeCurrentProcess(Process* process) { | 174 | void MakeCurrentProcess(Process* process) { |
| 194 | current_process = process; | 175 | current_process = process; |
| 195 | 176 | ||
| @@ -197,15 +178,17 @@ struct KernelCore::Impl { | |||
| 197 | return; | 178 | return; |
| 198 | } | 179 | } |
| 199 | 180 | ||
| 200 | for (auto& core : cores) { | 181 | u32 core_id = GetCurrentHostThreadID(); |
| 201 | core.SetIs64Bit(process->Is64BitProcess()); | 182 | if (core_id < Core::Hardware::NUM_CPU_CORES) { |
| 183 | system.Memory().SetCurrentPageTable(*process, core_id); | ||
| 202 | } | 184 | } |
| 203 | |||
| 204 | system.Memory().SetCurrentPageTable(*process); | ||
| 205 | } | 185 | } |
| 206 | 186 | ||
| 207 | void RegisterCoreThread(std::size_t core_id) { | 187 | void RegisterCoreThread(std::size_t core_id) { |
| 208 | std::unique_lock lock{register_thread_mutex}; | 188 | std::unique_lock lock{register_thread_mutex}; |
| 189 | if (!is_multicore) { | ||
| 190 | single_core_thread_id = std::this_thread::get_id(); | ||
| 191 | } | ||
| 209 | const std::thread::id this_id = std::this_thread::get_id(); | 192 | const std::thread::id this_id = std::this_thread::get_id(); |
| 210 | const auto it = host_thread_ids.find(this_id); | 193 | const auto it = host_thread_ids.find(this_id); |
| 211 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | 194 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); |
| @@ -219,12 +202,19 @@ struct KernelCore::Impl { | |||
| 219 | std::unique_lock lock{register_thread_mutex}; | 202 | std::unique_lock lock{register_thread_mutex}; |
| 220 | const std::thread::id this_id = std::this_thread::get_id(); | 203 | const std::thread::id this_id = std::this_thread::get_id(); |
| 221 | const auto it = host_thread_ids.find(this_id); | 204 | const auto it = host_thread_ids.find(this_id); |
| 222 | ASSERT(it == host_thread_ids.end()); | 205 | if (it != host_thread_ids.end()) { |
| 206 | return; | ||
| 207 | } | ||
| 223 | host_thread_ids[this_id] = registered_thread_ids++; | 208 | host_thread_ids[this_id] = registered_thread_ids++; |
| 224 | } | 209 | } |
| 225 | 210 | ||
| 226 | u32 GetCurrentHostThreadID() const { | 211 | u32 GetCurrentHostThreadID() const { |
| 227 | const std::thread::id this_id = std::this_thread::get_id(); | 212 | const std::thread::id this_id = std::this_thread::get_id(); |
| 213 | if (!is_multicore) { | ||
| 214 | if (single_core_thread_id == this_id) { | ||
| 215 | return static_cast<u32>(system.GetCpuManager().CurrentCore()); | ||
| 216 | } | ||
| 217 | } | ||
| 228 | const auto it = host_thread_ids.find(this_id); | 218 | const auto it = host_thread_ids.find(this_id); |
| 229 | if (it == host_thread_ids.end()) { | 219 | if (it == host_thread_ids.end()) { |
| 230 | return Core::INVALID_HOST_THREAD_ID; | 220 | return Core::INVALID_HOST_THREAD_ID; |
| @@ -240,7 +230,7 @@ struct KernelCore::Impl { | |||
| 240 | } | 230 | } |
| 241 | const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler(); | 231 | const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler(); |
| 242 | const Kernel::Thread* current = sched.GetCurrentThread(); | 232 | const Kernel::Thread* current = sched.GetCurrentThread(); |
| 243 | if (current != nullptr) { | 233 | if (current != nullptr && !current->IsPhantomMode()) { |
| 244 | result.guest_handle = current->GetGlobalHandle(); | 234 | result.guest_handle = current->GetGlobalHandle(); |
| 245 | } else { | 235 | } else { |
| 246 | result.guest_handle = InvalidHandle; | 236 | result.guest_handle = InvalidHandle; |
| @@ -313,7 +303,6 @@ struct KernelCore::Impl { | |||
| 313 | 303 | ||
| 314 | std::shared_ptr<ResourceLimit> system_resource_limit; | 304 | std::shared_ptr<ResourceLimit> system_resource_limit; |
| 315 | 305 | ||
| 316 | std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type; | ||
| 317 | std::shared_ptr<Core::Timing::EventType> preemption_event; | 306 | std::shared_ptr<Core::Timing::EventType> preemption_event; |
| 318 | 307 | ||
| 319 | // This is the kernel's handle table or supervisor handle table which | 308 | // This is the kernel's handle table or supervisor handle table which |
| @@ -343,6 +332,15 @@ struct KernelCore::Impl { | |||
| 343 | std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; | 332 | std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; |
| 344 | std::shared_ptr<Kernel::SharedMemory> time_shared_mem; | 333 | std::shared_ptr<Kernel::SharedMemory> time_shared_mem; |
| 345 | 334 | ||
| 335 | std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; | ||
| 336 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; | ||
| 337 | std::array<std::unique_ptr<Kernel::Scheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; | ||
| 338 | |||
| 339 | bool is_multicore{}; | ||
| 340 | std::thread::id single_core_thread_id{}; | ||
| 341 | |||
| 342 | std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; | ||
| 343 | |||
| 346 | // System context | 344 | // System context |
| 347 | Core::System& system; | 345 | Core::System& system; |
| 348 | }; | 346 | }; |
| @@ -352,6 +350,10 @@ KernelCore::~KernelCore() { | |||
| 352 | Shutdown(); | 350 | Shutdown(); |
| 353 | } | 351 | } |
| 354 | 352 | ||
| 353 | void KernelCore::SetMulticore(bool is_multicore) { | ||
| 354 | impl->SetMulticore(is_multicore); | ||
| 355 | } | ||
| 356 | |||
| 355 | void KernelCore::Initialize() { | 357 | void KernelCore::Initialize() { |
| 356 | impl->Initialize(*this); | 358 | impl->Initialize(*this); |
| 357 | } | 359 | } |
| @@ -397,11 +399,11 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const { | |||
| 397 | } | 399 | } |
| 398 | 400 | ||
| 399 | Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) { | 401 | Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) { |
| 400 | return impl->cores[id].Scheduler(); | 402 | return *impl->schedulers[id]; |
| 401 | } | 403 | } |
| 402 | 404 | ||
| 403 | const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const { | 405 | const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const { |
| 404 | return impl->cores[id].Scheduler(); | 406 | return *impl->schedulers[id]; |
| 405 | } | 407 | } |
| 406 | 408 | ||
| 407 | Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { | 409 | Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { |
| @@ -412,6 +414,39 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { | |||
| 412 | return impl->cores[id]; | 414 | return impl->cores[id]; |
| 413 | } | 415 | } |
| 414 | 416 | ||
| 417 | Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { | ||
| 418 | u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 419 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 420 | return impl->cores[core_id]; | ||
| 421 | } | ||
| 422 | |||
| 423 | const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { | ||
| 424 | u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 425 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 426 | return impl->cores[core_id]; | ||
| 427 | } | ||
| 428 | |||
| 429 | Kernel::Scheduler& KernelCore::CurrentScheduler() { | ||
| 430 | u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 431 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 432 | return *impl->schedulers[core_id]; | ||
| 433 | } | ||
| 434 | |||
| 435 | const Kernel::Scheduler& KernelCore::CurrentScheduler() const { | ||
| 436 | u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 437 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 438 | return *impl->schedulers[core_id]; | ||
| 439 | } | ||
| 440 | |||
| 441 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() { | ||
| 442 | return impl->interrupts; | ||
| 443 | } | ||
| 444 | |||
| 445 | const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() | ||
| 446 | const { | ||
| 447 | return impl->interrupts; | ||
| 448 | } | ||
| 449 | |||
| 415 | Kernel::Synchronization& KernelCore::Synchronization() { | 450 | Kernel::Synchronization& KernelCore::Synchronization() { |
| 416 | return impl->synchronization; | 451 | return impl->synchronization; |
| 417 | } | 452 | } |
| @@ -437,15 +472,17 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { | |||
| 437 | } | 472 | } |
| 438 | 473 | ||
| 439 | void KernelCore::InvalidateAllInstructionCaches() { | 474 | void KernelCore::InvalidateAllInstructionCaches() { |
| 440 | for (std::size_t i = 0; i < impl->global_scheduler.CpuCoresCount(); i++) { | 475 | auto& threads = GlobalScheduler().GetThreadList(); |
| 441 | PhysicalCore(i).ArmInterface().ClearInstructionCache(); | 476 | for (auto& thread : threads) { |
| 477 | if (!thread->IsHLEThread()) { | ||
| 478 | auto& arm_interface = thread->ArmInterface(); | ||
| 479 | arm_interface.ClearInstructionCache(); | ||
| 480 | } | ||
| 442 | } | 481 | } |
| 443 | } | 482 | } |
| 444 | 483 | ||
| 445 | void KernelCore::PrepareReschedule(std::size_t id) { | 484 | void KernelCore::PrepareReschedule(std::size_t id) { |
| 446 | if (id < impl->global_scheduler.CpuCoresCount()) { | 485 | // TODO: Reimplement, this |
| 447 | impl->cores[id].Stop(); | ||
| 448 | } | ||
| 449 | } | 486 | } |
| 450 | 487 | ||
| 451 | void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { | 488 | void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { |
| @@ -481,10 +518,6 @@ u64 KernelCore::CreateNewUserProcessID() { | |||
| 481 | return impl->next_user_process_id++; | 518 | return impl->next_user_process_id++; |
| 482 | } | 519 | } |
| 483 | 520 | ||
| 484 | const std::shared_ptr<Core::Timing::EventType>& KernelCore::ThreadWakeupCallbackEventType() const { | ||
| 485 | return impl->thread_wakeup_event_type; | ||
| 486 | } | ||
| 487 | |||
| 488 | Kernel::HandleTable& KernelCore::GlobalHandleTable() { | 521 | Kernel::HandleTable& KernelCore::GlobalHandleTable() { |
| 489 | return impl->global_handle_table; | 522 | return impl->global_handle_table; |
| 490 | } | 523 | } |
| @@ -557,4 +590,34 @@ const Kernel::SharedMemory& KernelCore::GetTimeSharedMem() const { | |||
| 557 | return *impl->time_shared_mem; | 590 | return *impl->time_shared_mem; |
| 558 | } | 591 | } |
| 559 | 592 | ||
| 593 | void KernelCore::Suspend(bool in_suspention) { | ||
| 594 | const bool should_suspend = exception_exited || in_suspention; | ||
| 595 | { | ||
| 596 | SchedulerLock lock(*this); | ||
| 597 | ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep; | ||
| 598 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 599 | impl->suspend_threads[i]->SetStatus(status); | ||
| 600 | } | ||
| 601 | } | ||
| 602 | } | ||
| 603 | |||
| 604 | bool KernelCore::IsMulticore() const { | ||
| 605 | return impl->is_multicore; | ||
| 606 | } | ||
| 607 | |||
| 608 | void KernelCore::ExceptionalExit() { | ||
| 609 | exception_exited = true; | ||
| 610 | Suspend(true); | ||
| 611 | } | ||
| 612 | |||
| 613 | void KernelCore::EnterSVCProfile() { | ||
| 614 | std::size_t core = impl->GetCurrentHostThreadID(); | ||
| 615 | impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC)); | ||
| 616 | } | ||
| 617 | |||
| 618 | void KernelCore::ExitSVCProfile() { | ||
| 619 | std::size_t core = impl->GetCurrentHostThreadID(); | ||
| 620 | MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]); | ||
| 621 | } | ||
| 622 | |||
| 560 | } // namespace Kernel | 623 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 83de1f542..49bd47e89 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -4,15 +4,17 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <unordered_map> | 10 | #include <unordered_map> |
| 10 | #include <vector> | 11 | #include <vector> |
| 12 | #include "core/hardware_properties.h" | ||
| 11 | #include "core/hle/kernel/memory/memory_types.h" | 13 | #include "core/hle/kernel/memory/memory_types.h" |
| 12 | #include "core/hle/kernel/object.h" | 14 | #include "core/hle/kernel/object.h" |
| 13 | 15 | ||
| 14 | namespace Core { | 16 | namespace Core { |
| 15 | struct EmuThreadHandle; | 17 | class CPUInterruptHandler; |
| 16 | class ExclusiveMonitor; | 18 | class ExclusiveMonitor; |
| 17 | class System; | 19 | class System; |
| 18 | } // namespace Core | 20 | } // namespace Core |
| @@ -65,6 +67,9 @@ public: | |||
| 65 | KernelCore(KernelCore&&) = delete; | 67 | KernelCore(KernelCore&&) = delete; |
| 66 | KernelCore& operator=(KernelCore&&) = delete; | 68 | KernelCore& operator=(KernelCore&&) = delete; |
| 67 | 69 | ||
| 70 | /// Sets if emulation is multicore or single core, must be set before Initialize | ||
| 71 | void SetMulticore(bool is_multicore); | ||
| 72 | |||
| 68 | /// Resets the kernel to a clean slate for use. | 73 | /// Resets the kernel to a clean slate for use. |
| 69 | void Initialize(); | 74 | void Initialize(); |
| 70 | 75 | ||
| @@ -110,6 +115,18 @@ public: | |||
| 110 | /// Gets the an instance of the respective physical CPU core. | 115 | /// Gets the an instance of the respective physical CPU core. |
| 111 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; | 116 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; |
| 112 | 117 | ||
| 118 | /// Gets the sole instance of the Scheduler at the current running core. | ||
| 119 | Kernel::Scheduler& CurrentScheduler(); | ||
| 120 | |||
| 121 | /// Gets the sole instance of the Scheduler at the current running core. | ||
| 122 | const Kernel::Scheduler& CurrentScheduler() const; | ||
| 123 | |||
| 124 | /// Gets the an instance of the current physical CPU core. | ||
| 125 | Kernel::PhysicalCore& CurrentPhysicalCore(); | ||
| 126 | |||
| 127 | /// Gets the an instance of the current physical CPU core. | ||
| 128 | const Kernel::PhysicalCore& CurrentPhysicalCore() const; | ||
| 129 | |||
| 113 | /// Gets the an instance of the Synchronization Interface. | 130 | /// Gets the an instance of the Synchronization Interface. |
| 114 | Kernel::Synchronization& Synchronization(); | 131 | Kernel::Synchronization& Synchronization(); |
| 115 | 132 | ||
| @@ -129,6 +146,10 @@ public: | |||
| 129 | 146 | ||
| 130 | const Core::ExclusiveMonitor& GetExclusiveMonitor() const; | 147 | const Core::ExclusiveMonitor& GetExclusiveMonitor() const; |
| 131 | 148 | ||
| 149 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts(); | ||
| 150 | |||
| 151 | const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const; | ||
| 152 | |||
| 132 | void InvalidateAllInstructionCaches(); | 153 | void InvalidateAllInstructionCaches(); |
| 133 | 154 | ||
| 134 | /// Adds a port to the named port table | 155 | /// Adds a port to the named port table |
| @@ -191,6 +212,18 @@ public: | |||
| 191 | /// Gets the shared memory object for Time services. | 212 | /// Gets the shared memory object for Time services. |
| 192 | const Kernel::SharedMemory& GetTimeSharedMem() const; | 213 | const Kernel::SharedMemory& GetTimeSharedMem() const; |
| 193 | 214 | ||
| 215 | /// Suspend/unsuspend the OS. | ||
| 216 | void Suspend(bool in_suspention); | ||
| 217 | |||
| 218 | /// Exceptional exit the OS. | ||
| 219 | void ExceptionalExit(); | ||
| 220 | |||
| 221 | bool IsMulticore() const; | ||
| 222 | |||
| 223 | void EnterSVCProfile(); | ||
| 224 | |||
| 225 | void ExitSVCProfile(); | ||
| 226 | |||
| 194 | private: | 227 | private: |
| 195 | friend class Object; | 228 | friend class Object; |
| 196 | friend class Process; | 229 | friend class Process; |
| @@ -208,9 +241,6 @@ private: | |||
| 208 | /// Creates a new thread ID, incrementing the internal thread ID counter. | 241 | /// Creates a new thread ID, incrementing the internal thread ID counter. |
| 209 | u64 CreateNewThreadID(); | 242 | u64 CreateNewThreadID(); |
| 210 | 243 | ||
| 211 | /// Retrieves the event type used for thread wakeup callbacks. | ||
| 212 | const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const; | ||
| 213 | |||
| 214 | /// Provides a reference to the global handle table. | 244 | /// Provides a reference to the global handle table. |
| 215 | Kernel::HandleTable& GlobalHandleTable(); | 245 | Kernel::HandleTable& GlobalHandleTable(); |
| 216 | 246 | ||
| @@ -219,6 +249,7 @@ private: | |||
| 219 | 249 | ||
| 220 | struct Impl; | 250 | struct Impl; |
| 221 | std::unique_ptr<Impl> impl; | 251 | std::unique_ptr<Impl> impl; |
| 252 | bool exception_exited{}; | ||
| 222 | }; | 253 | }; |
| 223 | 254 | ||
| 224 | } // namespace Kernel | 255 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/memory/memory_manager.cpp b/src/core/hle/kernel/memory/memory_manager.cpp index 616148190..acf13585c 100644 --- a/src/core/hle/kernel/memory/memory_manager.cpp +++ b/src/core/hle/kernel/memory/memory_manager.cpp | |||
| @@ -139,7 +139,6 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa | |||
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | // Only succeed if we allocated as many pages as we wanted | 141 | // Only succeed if we allocated as many pages as we wanted |
| 142 | ASSERT(num_pages >= 0); | ||
| 143 | if (num_pages) { | 142 | if (num_pages) { |
| 144 | return ERR_OUT_OF_MEMORY; | 143 | return ERR_OUT_OF_MEMORY; |
| 145 | } | 144 | } |
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 7869eb32b..8f6c944d1 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -34,8 +34,6 @@ static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThr | |||
| 34 | if (thread->GetMutexWaitAddress() != mutex_addr) | 34 | if (thread->GetMutexWaitAddress() != mutex_addr) |
| 35 | continue; | 35 | continue; |
| 36 | 36 | ||
| 37 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); | ||
| 38 | |||
| 39 | ++num_waiters; | 37 | ++num_waiters; |
| 40 | if (highest_priority_thread == nullptr || | 38 | if (highest_priority_thread == nullptr || |
| 41 | thread->GetPriority() < highest_priority_thread->GetPriority()) { | 39 | thread->GetPriority() < highest_priority_thread->GetPriority()) { |
| @@ -49,6 +47,7 @@ static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThr | |||
| 49 | /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. | 47 | /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. |
| 50 | static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread, | 48 | static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread, |
| 51 | std::shared_ptr<Thread> new_owner) { | 49 | std::shared_ptr<Thread> new_owner) { |
| 50 | current_thread->RemoveMutexWaiter(new_owner); | ||
| 52 | const auto threads = current_thread->GetMutexWaitingThreads(); | 51 | const auto threads = current_thread->GetMutexWaitingThreads(); |
| 53 | for (const auto& thread : threads) { | 52 | for (const auto& thread : threads) { |
| 54 | if (thread->GetMutexWaitAddress() != mutex_addr) | 53 | if (thread->GetMutexWaitAddress() != mutex_addr) |
| @@ -72,85 +71,100 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, | |||
| 72 | return ERR_INVALID_ADDRESS; | 71 | return ERR_INVALID_ADDRESS; |
| 73 | } | 72 | } |
| 74 | 73 | ||
| 75 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 74 | auto& kernel = system.Kernel(); |
| 76 | std::shared_ptr<Thread> current_thread = | 75 | std::shared_ptr<Thread> current_thread = |
| 77 | SharedFrom(system.CurrentScheduler().GetCurrentThread()); | 76 | SharedFrom(kernel.CurrentScheduler().GetCurrentThread()); |
| 78 | std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); | 77 | { |
| 79 | std::shared_ptr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle); | 78 | SchedulerLock lock(kernel); |
| 79 | // The mutex address must be 4-byte aligned | ||
| 80 | if ((address % sizeof(u32)) != 0) { | ||
| 81 | return ERR_INVALID_ADDRESS; | ||
| 82 | } | ||
| 80 | 83 | ||
| 81 | // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another | 84 | const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
| 82 | // thread. | 85 | std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); |
| 83 | ASSERT(requesting_thread == current_thread); | 86 | std::shared_ptr<Thread> requesting_thread = |
| 87 | handle_table.Get<Thread>(requesting_thread_handle); | ||
| 84 | 88 | ||
| 85 | const u32 addr_value = system.Memory().Read32(address); | 89 | // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of |
| 90 | // another thread. | ||
| 91 | ASSERT(requesting_thread == current_thread); | ||
| 86 | 92 | ||
| 87 | // If the mutex isn't being held, just return success. | 93 | current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); |
| 88 | if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { | ||
| 89 | return RESULT_SUCCESS; | ||
| 90 | } | ||
| 91 | 94 | ||
| 92 | if (holding_thread == nullptr) { | 95 | const u32 addr_value = system.Memory().Read32(address); |
| 93 | LOG_ERROR(Kernel, "Holding thread does not exist! thread_handle={:08X}", | 96 | |
| 94 | holding_thread_handle); | 97 | // If the mutex isn't being held, just return success. |
| 95 | return ERR_INVALID_HANDLE; | 98 | if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { |
| 96 | } | 99 | return RESULT_SUCCESS; |
| 100 | } | ||
| 97 | 101 | ||
| 98 | // Wait until the mutex is released | 102 | if (holding_thread == nullptr) { |
| 99 | current_thread->SetMutexWaitAddress(address); | 103 | return ERR_INVALID_HANDLE; |
| 100 | current_thread->SetWaitHandle(requesting_thread_handle); | 104 | } |
| 101 | 105 | ||
| 102 | current_thread->SetStatus(ThreadStatus::WaitMutex); | 106 | // Wait until the mutex is released |
| 103 | current_thread->InvalidateWakeupCallback(); | 107 | current_thread->SetMutexWaitAddress(address); |
| 108 | current_thread->SetWaitHandle(requesting_thread_handle); | ||
| 104 | 109 | ||
| 105 | // Update the lock holder thread's priority to prevent priority inversion. | 110 | current_thread->SetStatus(ThreadStatus::WaitMutex); |
| 106 | holding_thread->AddMutexWaiter(current_thread); | ||
| 107 | 111 | ||
| 108 | system.PrepareReschedule(); | 112 | // Update the lock holder thread's priority to prevent priority inversion. |
| 113 | holding_thread->AddMutexWaiter(current_thread); | ||
| 114 | } | ||
| 109 | 115 | ||
| 110 | return RESULT_SUCCESS; | 116 | { |
| 117 | SchedulerLock lock(kernel); | ||
| 118 | auto* owner = current_thread->GetLockOwner(); | ||
| 119 | if (owner != nullptr) { | ||
| 120 | owner->RemoveMutexWaiter(current_thread); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | return current_thread->GetSignalingResult(); | ||
| 111 | } | 124 | } |
| 112 | 125 | ||
| 113 | ResultCode Mutex::Release(VAddr address) { | 126 | std::pair<ResultCode, std::shared_ptr<Thread>> Mutex::Unlock(std::shared_ptr<Thread> owner, |
| 127 | VAddr address) { | ||
| 114 | // The mutex address must be 4-byte aligned | 128 | // The mutex address must be 4-byte aligned |
| 115 | if ((address % sizeof(u32)) != 0) { | 129 | if ((address % sizeof(u32)) != 0) { |
| 116 | LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); | 130 | LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); |
| 117 | return ERR_INVALID_ADDRESS; | 131 | return {ERR_INVALID_ADDRESS, nullptr}; |
| 118 | } | 132 | } |
| 119 | 133 | ||
| 120 | std::shared_ptr<Thread> current_thread = | 134 | auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address); |
| 121 | SharedFrom(system.CurrentScheduler().GetCurrentThread()); | 135 | if (new_owner == nullptr) { |
| 122 | auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address); | ||
| 123 | |||
| 124 | // There are no more threads waiting for the mutex, release it completely. | ||
| 125 | if (thread == nullptr) { | ||
| 126 | system.Memory().Write32(address, 0); | 136 | system.Memory().Write32(address, 0); |
| 127 | return RESULT_SUCCESS; | 137 | return {RESULT_SUCCESS, nullptr}; |
| 128 | } | 138 | } |
| 129 | |||
| 130 | // Transfer the ownership of the mutex from the previous owner to the new one. | 139 | // Transfer the ownership of the mutex from the previous owner to the new one. |
| 131 | TransferMutexOwnership(address, current_thread, thread); | 140 | TransferMutexOwnership(address, owner, new_owner); |
| 132 | 141 | u32 mutex_value = new_owner->GetWaitHandle(); | |
| 133 | u32 mutex_value = thread->GetWaitHandle(); | ||
| 134 | |||
| 135 | if (num_waiters >= 2) { | 142 | if (num_waiters >= 2) { |
| 136 | // Notify the guest that there are still some threads waiting for the mutex | 143 | // Notify the guest that there are still some threads waiting for the mutex |
| 137 | mutex_value |= Mutex::MutexHasWaitersFlag; | 144 | mutex_value |= Mutex::MutexHasWaitersFlag; |
| 138 | } | 145 | } |
| 146 | new_owner->SetSynchronizationResults(nullptr, RESULT_SUCCESS); | ||
| 147 | new_owner->SetLockOwner(nullptr); | ||
| 148 | new_owner->ResumeFromWait(); | ||
| 139 | 149 | ||
| 140 | // Grant the mutex to the next waiting thread and resume it. | ||
| 141 | system.Memory().Write32(address, mutex_value); | 150 | system.Memory().Write32(address, mutex_value); |
| 151 | return {RESULT_SUCCESS, new_owner}; | ||
| 152 | } | ||
| 142 | 153 | ||
| 143 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); | 154 | ResultCode Mutex::Release(VAddr address) { |
| 144 | thread->ResumeFromWait(); | 155 | auto& kernel = system.Kernel(); |
| 156 | SchedulerLock lock(kernel); | ||
| 145 | 157 | ||
| 146 | thread->SetLockOwner(nullptr); | 158 | std::shared_ptr<Thread> current_thread = |
| 147 | thread->SetCondVarWaitAddress(0); | 159 | SharedFrom(kernel.CurrentScheduler().GetCurrentThread()); |
| 148 | thread->SetMutexWaitAddress(0); | ||
| 149 | thread->SetWaitHandle(0); | ||
| 150 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 151 | 160 | ||
| 152 | system.PrepareReschedule(); | 161 | auto [result, new_owner] = Unlock(current_thread, address); |
| 153 | 162 | ||
| 154 | return RESULT_SUCCESS; | 163 | if (result != RESULT_SUCCESS && new_owner != nullptr) { |
| 164 | new_owner->SetSynchronizationResults(nullptr, result); | ||
| 165 | } | ||
| 166 | |||
| 167 | return result; | ||
| 155 | } | 168 | } |
| 169 | |||
| 156 | } // namespace Kernel | 170 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index b904de2e8..3b81dc3df 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -28,6 +28,10 @@ public: | |||
| 28 | ResultCode TryAcquire(VAddr address, Handle holding_thread_handle, | 28 | ResultCode TryAcquire(VAddr address, Handle holding_thread_handle, |
| 29 | Handle requesting_thread_handle); | 29 | Handle requesting_thread_handle); |
| 30 | 30 | ||
| 31 | /// Unlocks a mutex for owner at address | ||
| 32 | std::pair<ResultCode, std::shared_ptr<Thread>> Unlock(std::shared_ptr<Thread> owner, | ||
| 33 | VAddr address); | ||
| 34 | |||
| 31 | /// Releases the mutex at the specified address. | 35 | /// Releases the mutex at the specified address. |
| 32 | ResultCode Release(VAddr address); | 36 | ResultCode Release(VAddr address); |
| 33 | 37 | ||
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index a15011076..c6bbdb080 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp | |||
| @@ -2,12 +2,15 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/assert.h" | ||
| 5 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "common/spin_lock.h" | ||
| 6 | #include "core/arm/arm_interface.h" | 8 | #include "core/arm/arm_interface.h" |
| 7 | #ifdef ARCHITECTURE_x86_64 | 9 | #ifdef ARCHITECTURE_x86_64 |
| 8 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | 10 | #include "core/arm/dynarmic/arm_dynarmic_32.h" |
| 9 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | 11 | #include "core/arm/dynarmic/arm_dynarmic_64.h" |
| 10 | #endif | 12 | #endif |
| 13 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 11 | #include "core/arm/exclusive_monitor.h" | 14 | #include "core/arm/exclusive_monitor.h" |
| 12 | #include "core/arm/unicorn/arm_unicorn.h" | 15 | #include "core/arm/unicorn/arm_unicorn.h" |
| 13 | #include "core/core.h" | 16 | #include "core/core.h" |
| @@ -17,50 +20,37 @@ | |||
| 17 | 20 | ||
| 18 | namespace Kernel { | 21 | namespace Kernel { |
| 19 | 22 | ||
| 20 | PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, | 23 | PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler, |
| 21 | Core::ExclusiveMonitor& exclusive_monitor) | 24 | Core::CPUInterruptHandler& interrupt_handler) |
| 22 | : core_index{id} { | 25 | : interrupt_handler{interrupt_handler}, core_index{id}, scheduler{scheduler} { |
| 23 | #ifdef ARCHITECTURE_x86_64 | ||
| 24 | arm_interface_32 = | ||
| 25 | std::make_unique<Core::ARM_Dynarmic_32>(system, exclusive_monitor, core_index); | ||
| 26 | arm_interface_64 = | ||
| 27 | std::make_unique<Core::ARM_Dynarmic_64>(system, exclusive_monitor, core_index); | ||
| 28 | |||
| 29 | #else | ||
| 30 | using Core::ARM_Unicorn; | ||
| 31 | arm_interface_32 = std::make_unique<ARM_Unicorn>(system, ARM_Unicorn::Arch::AArch32); | ||
| 32 | arm_interface_64 = std::make_unique<ARM_Unicorn>(system, ARM_Unicorn::Arch::AArch64); | ||
| 33 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | ||
| 34 | #endif | ||
| 35 | 26 | ||
| 36 | scheduler = std::make_unique<Kernel::Scheduler>(system, core_index); | 27 | guard = std::make_unique<Common::SpinLock>(); |
| 37 | } | 28 | } |
| 38 | 29 | ||
| 39 | PhysicalCore::~PhysicalCore() = default; | 30 | PhysicalCore::~PhysicalCore() = default; |
| 40 | 31 | ||
| 41 | void PhysicalCore::Run() { | 32 | void PhysicalCore::Idle() { |
| 42 | arm_interface->Run(); | 33 | interrupt_handler.AwaitInterrupt(); |
| 43 | arm_interface->ClearExclusiveState(); | ||
| 44 | } | 34 | } |
| 45 | 35 | ||
| 46 | void PhysicalCore::Step() { | 36 | void PhysicalCore::Shutdown() { |
| 47 | arm_interface->Step(); | 37 | scheduler.Shutdown(); |
| 48 | } | 38 | } |
| 49 | 39 | ||
| 50 | void PhysicalCore::Stop() { | 40 | bool PhysicalCore::IsInterrupted() const { |
| 51 | arm_interface->PrepareReschedule(); | 41 | return interrupt_handler.IsInterrupted(); |
| 52 | } | 42 | } |
| 53 | 43 | ||
| 54 | void PhysicalCore::Shutdown() { | 44 | void PhysicalCore::Interrupt() { |
| 55 | scheduler->Shutdown(); | 45 | guard->lock(); |
| 46 | interrupt_handler.SetInterrupt(true); | ||
| 47 | guard->unlock(); | ||
| 56 | } | 48 | } |
| 57 | 49 | ||
| 58 | void PhysicalCore::SetIs64Bit(bool is_64_bit) { | 50 | void PhysicalCore::ClearInterrupt() { |
| 59 | if (is_64_bit) { | 51 | guard->lock(); |
| 60 | arm_interface = arm_interface_64.get(); | 52 | interrupt_handler.SetInterrupt(false); |
| 61 | } else { | 53 | guard->unlock(); |
| 62 | arm_interface = arm_interface_32.get(); | ||
| 63 | } | ||
| 64 | } | 54 | } |
| 65 | 55 | ||
| 66 | } // namespace Kernel | 56 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 3269166be..d7a7a951c 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h | |||
| @@ -7,12 +7,17 @@ | |||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | 9 | ||
| 10 | namespace Common { | ||
| 11 | class SpinLock; | ||
| 12 | } | ||
| 13 | |||
| 10 | namespace Kernel { | 14 | namespace Kernel { |
| 11 | class Scheduler; | 15 | class Scheduler; |
| 12 | } // namespace Kernel | 16 | } // namespace Kernel |
| 13 | 17 | ||
| 14 | namespace Core { | 18 | namespace Core { |
| 15 | class ARM_Interface; | 19 | class ARM_Interface; |
| 20 | class CPUInterruptHandler; | ||
| 16 | class ExclusiveMonitor; | 21 | class ExclusiveMonitor; |
| 17 | class System; | 22 | class System; |
| 18 | } // namespace Core | 23 | } // namespace Core |
| @@ -21,7 +26,8 @@ namespace Kernel { | |||
| 21 | 26 | ||
| 22 | class PhysicalCore { | 27 | class PhysicalCore { |
| 23 | public: | 28 | public: |
| 24 | PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor); | 29 | PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler, |
| 30 | Core::CPUInterruptHandler& interrupt_handler); | ||
| 25 | ~PhysicalCore(); | 31 | ~PhysicalCore(); |
| 26 | 32 | ||
| 27 | PhysicalCore(const PhysicalCore&) = delete; | 33 | PhysicalCore(const PhysicalCore&) = delete; |
| @@ -30,23 +36,18 @@ public: | |||
| 30 | PhysicalCore(PhysicalCore&&) = default; | 36 | PhysicalCore(PhysicalCore&&) = default; |
| 31 | PhysicalCore& operator=(PhysicalCore&&) = default; | 37 | PhysicalCore& operator=(PhysicalCore&&) = default; |
| 32 | 38 | ||
| 33 | /// Execute current jit state | 39 | void Idle(); |
| 34 | void Run(); | 40 | /// Interrupt this physical core. |
| 35 | /// Execute a single instruction in current jit. | 41 | void Interrupt(); |
| 36 | void Step(); | ||
| 37 | /// Stop JIT execution/exit | ||
| 38 | void Stop(); | ||
| 39 | 42 | ||
| 40 | // Shutdown this physical core. | 43 | /// Clear this core's interrupt |
| 41 | void Shutdown(); | 44 | void ClearInterrupt(); |
| 42 | 45 | ||
| 43 | Core::ARM_Interface& ArmInterface() { | 46 | /// Check if this core is interrupted |
| 44 | return *arm_interface; | 47 | bool IsInterrupted() const; |
| 45 | } | ||
| 46 | 48 | ||
| 47 | const Core::ARM_Interface& ArmInterface() const { | 49 | // Shutdown this physical core. |
| 48 | return *arm_interface; | 50 | void Shutdown(); |
| 49 | } | ||
| 50 | 51 | ||
| 51 | bool IsMainCore() const { | 52 | bool IsMainCore() const { |
| 52 | return core_index == 0; | 53 | return core_index == 0; |
| @@ -61,21 +62,18 @@ public: | |||
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | Kernel::Scheduler& Scheduler() { | 64 | Kernel::Scheduler& Scheduler() { |
| 64 | return *scheduler; | 65 | return scheduler; |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | const Kernel::Scheduler& Scheduler() const { | 68 | const Kernel::Scheduler& Scheduler() const { |
| 68 | return *scheduler; | 69 | return scheduler; |
| 69 | } | 70 | } |
| 70 | 71 | ||
| 71 | void SetIs64Bit(bool is_64_bit); | ||
| 72 | |||
| 73 | private: | 72 | private: |
| 73 | Core::CPUInterruptHandler& interrupt_handler; | ||
| 74 | std::size_t core_index; | 74 | std::size_t core_index; |
| 75 | std::unique_ptr<Core::ARM_Interface> arm_interface_32; | 75 | Kernel::Scheduler& scheduler; |
| 76 | std::unique_ptr<Core::ARM_Interface> arm_interface_64; | 76 | std::unique_ptr<Common::SpinLock> guard; |
| 77 | std::unique_ptr<Kernel::Scheduler> scheduler; | ||
| 78 | Core::ARM_Interface* arm_interface{}; | ||
| 79 | }; | 77 | }; |
| 80 | 78 | ||
| 81 | } // namespace Kernel | 79 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index c4c5199b1..f9d7c024d 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "core/hle/kernel/resource_limit.h" | 22 | #include "core/hle/kernel/resource_limit.h" |
| 23 | #include "core/hle/kernel/scheduler.h" | 23 | #include "core/hle/kernel/scheduler.h" |
| 24 | #include "core/hle/kernel/thread.h" | 24 | #include "core/hle/kernel/thread.h" |
| 25 | #include "core/hle/lock.h" | ||
| 25 | #include "core/memory.h" | 26 | #include "core/memory.h" |
| 26 | #include "core/settings.h" | 27 | #include "core/settings.h" |
| 27 | 28 | ||
| @@ -30,14 +31,15 @@ namespace { | |||
| 30 | /** | 31 | /** |
| 31 | * Sets up the primary application thread | 32 | * Sets up the primary application thread |
| 32 | * | 33 | * |
| 34 | * @param system The system instance to create the main thread under. | ||
| 33 | * @param owner_process The parent process for the main thread | 35 | * @param owner_process The parent process for the main thread |
| 34 | * @param kernel The kernel instance to create the main thread under. | ||
| 35 | * @param priority The priority to give the main thread | 36 | * @param priority The priority to give the main thread |
| 36 | */ | 37 | */ |
| 37 | void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, VAddr stack_top) { | 38 | void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) { |
| 38 | const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); | 39 | const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); |
| 39 | auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, | 40 | ThreadType type = THREADTYPE_USER; |
| 40 | owner_process.GetIdealCore(), stack_top, owner_process); | 41 | auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0, |
| 42 | owner_process.GetIdealCore(), stack_top, &owner_process); | ||
| 41 | 43 | ||
| 42 | std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap(); | 44 | std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap(); |
| 43 | 45 | ||
| @@ -48,8 +50,12 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, V | |||
| 48 | thread->GetContext32().cpu_registers[1] = thread_handle; | 50 | thread->GetContext32().cpu_registers[1] = thread_handle; |
| 49 | thread->GetContext64().cpu_registers[1] = thread_handle; | 51 | thread->GetContext64().cpu_registers[1] = thread_handle; |
| 50 | 52 | ||
| 53 | auto& kernel = system.Kernel(); | ||
| 51 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires | 54 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires |
| 52 | thread->ResumeFromWait(); | 55 | { |
| 56 | SchedulerLock lock{kernel}; | ||
| 57 | thread->SetStatus(ThreadStatus::Ready); | ||
| 58 | } | ||
| 53 | } | 59 | } |
| 54 | } // Anonymous namespace | 60 | } // Anonymous namespace |
| 55 | 61 | ||
| @@ -182,7 +188,6 @@ void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) { | |||
| 182 | } | 188 | } |
| 183 | ++it; | 189 | ++it; |
| 184 | } | 190 | } |
| 185 | UNREACHABLE(); | ||
| 186 | } | 191 | } |
| 187 | 192 | ||
| 188 | std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads( | 193 | std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads( |
| @@ -207,6 +212,7 @@ void Process::UnregisterThread(const Thread* thread) { | |||
| 207 | } | 212 | } |
| 208 | 213 | ||
| 209 | ResultCode Process::ClearSignalState() { | 214 | ResultCode Process::ClearSignalState() { |
| 215 | SchedulerLock lock(system.Kernel()); | ||
| 210 | if (status == ProcessStatus::Exited) { | 216 | if (status == ProcessStatus::Exited) { |
| 211 | LOG_ERROR(Kernel, "called on a terminated process instance."); | 217 | LOG_ERROR(Kernel, "called on a terminated process instance."); |
| 212 | return ERR_INVALID_STATE; | 218 | return ERR_INVALID_STATE; |
| @@ -294,7 +300,7 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) { | |||
| 294 | 300 | ||
| 295 | ChangeStatus(ProcessStatus::Running); | 301 | ChangeStatus(ProcessStatus::Running); |
| 296 | 302 | ||
| 297 | SetupMainThread(*this, kernel, main_thread_priority, main_thread_stack_top); | 303 | SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top); |
| 298 | resource_limit->Reserve(ResourceType::Threads, 1); | 304 | resource_limit->Reserve(ResourceType::Threads, 1); |
| 299 | resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size); | 305 | resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size); |
| 300 | } | 306 | } |
| @@ -340,6 +346,7 @@ static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) { | |||
| 340 | } | 346 | } |
| 341 | 347 | ||
| 342 | VAddr Process::CreateTLSRegion() { | 348 | VAddr Process::CreateTLSRegion() { |
| 349 | SchedulerLock lock(system.Kernel()); | ||
| 343 | if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; | 350 | if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; |
| 344 | tls_page_iter != tls_pages.cend()) { | 351 | tls_page_iter != tls_pages.cend()) { |
| 345 | return *tls_page_iter->ReserveSlot(); | 352 | return *tls_page_iter->ReserveSlot(); |
| @@ -370,6 +377,7 @@ VAddr Process::CreateTLSRegion() { | |||
| 370 | } | 377 | } |
| 371 | 378 | ||
| 372 | void Process::FreeTLSRegion(VAddr tls_address) { | 379 | void Process::FreeTLSRegion(VAddr tls_address) { |
| 380 | SchedulerLock lock(system.Kernel()); | ||
| 373 | const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); | 381 | const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); |
| 374 | auto iter = | 382 | auto iter = |
| 375 | std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { | 383 | std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { |
| @@ -384,6 +392,7 @@ void Process::FreeTLSRegion(VAddr tls_address) { | |||
| 384 | } | 392 | } |
| 385 | 393 | ||
| 386 | void Process::LoadModule(CodeSet code_set, VAddr base_addr) { | 394 | void Process::LoadModule(CodeSet code_set, VAddr base_addr) { |
| 395 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 387 | const auto ReprotectSegment = [&](const CodeSet::Segment& segment, | 396 | const auto ReprotectSegment = [&](const CodeSet::Segment& segment, |
| 388 | Memory::MemoryPermission permission) { | 397 | Memory::MemoryPermission permission) { |
| 389 | page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission); | 398 | page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission); |
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp index ef5e19e63..6e286419e 100644 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp | |||
| @@ -6,8 +6,10 @@ | |||
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/hle/kernel/errors.h" | 8 | #include "core/hle/kernel/errors.h" |
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 9 | #include "core/hle/kernel/object.h" | 10 | #include "core/hle/kernel/object.h" |
| 10 | #include "core/hle/kernel/readable_event.h" | 11 | #include "core/hle/kernel/readable_event.h" |
| 12 | #include "core/hle/kernel/scheduler.h" | ||
| 11 | #include "core/hle/kernel/thread.h" | 13 | #include "core/hle/kernel/thread.h" |
| 12 | 14 | ||
| 13 | namespace Kernel { | 15 | namespace Kernel { |
| @@ -37,6 +39,7 @@ void ReadableEvent::Clear() { | |||
| 37 | } | 39 | } |
| 38 | 40 | ||
| 39 | ResultCode ReadableEvent::Reset() { | 41 | ResultCode ReadableEvent::Reset() { |
| 42 | SchedulerLock lock(kernel); | ||
| 40 | if (!is_signaled) { | 43 | if (!is_signaled) { |
| 41 | LOG_TRACE(Kernel, "Handle is not signaled! object_id={}, object_type={}, object_name={}", | 44 | LOG_TRACE(Kernel, "Handle is not signaled! object_id={}, object_type={}, object_name={}", |
| 42 | GetObjectId(), GetTypeName(), GetName()); | 45 | GetObjectId(), GetTypeName(), GetName()); |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 1140c72a3..2b12c0dbf 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -11,11 +11,15 @@ | |||
| 11 | #include <utility> | 11 | #include <utility> |
| 12 | 12 | ||
| 13 | #include "common/assert.h" | 13 | #include "common/assert.h" |
| 14 | #include "common/bit_util.h" | ||
| 15 | #include "common/fiber.h" | ||
| 14 | #include "common/logging/log.h" | 16 | #include "common/logging/log.h" |
| 15 | #include "core/arm/arm_interface.h" | 17 | #include "core/arm/arm_interface.h" |
| 16 | #include "core/core.h" | 18 | #include "core/core.h" |
| 17 | #include "core/core_timing.h" | 19 | #include "core/core_timing.h" |
| 20 | #include "core/cpu_manager.h" | ||
| 18 | #include "core/hle/kernel/kernel.h" | 21 | #include "core/hle/kernel/kernel.h" |
| 22 | #include "core/hle/kernel/physical_core.h" | ||
| 19 | #include "core/hle/kernel/process.h" | 23 | #include "core/hle/kernel/process.h" |
| 20 | #include "core/hle/kernel/scheduler.h" | 24 | #include "core/hle/kernel/scheduler.h" |
| 21 | #include "core/hle/kernel/time_manager.h" | 25 | #include "core/hle/kernel/time_manager.h" |
| @@ -27,103 +31,151 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {} | |||
| 27 | GlobalScheduler::~GlobalScheduler() = default; | 31 | GlobalScheduler::~GlobalScheduler() = default; |
| 28 | 32 | ||
| 29 | void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) { | 33 | void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) { |
| 34 | global_list_guard.lock(); | ||
| 30 | thread_list.push_back(std::move(thread)); | 35 | thread_list.push_back(std::move(thread)); |
| 36 | global_list_guard.unlock(); | ||
| 31 | } | 37 | } |
| 32 | 38 | ||
| 33 | void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) { | 39 | void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) { |
| 40 | global_list_guard.lock(); | ||
| 34 | thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), | 41 | thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), |
| 35 | thread_list.end()); | 42 | thread_list.end()); |
| 43 | global_list_guard.unlock(); | ||
| 36 | } | 44 | } |
| 37 | 45 | ||
| 38 | void GlobalScheduler::UnloadThread(std::size_t core) { | 46 | u32 GlobalScheduler::SelectThreads() { |
| 39 | Scheduler& sched = kernel.Scheduler(core); | 47 | ASSERT(is_locked); |
| 40 | sched.UnloadThread(); | ||
| 41 | } | ||
| 42 | |||
| 43 | void GlobalScheduler::SelectThread(std::size_t core) { | ||
| 44 | const auto update_thread = [](Thread* thread, Scheduler& sched) { | 48 | const auto update_thread = [](Thread* thread, Scheduler& sched) { |
| 45 | if (thread != sched.selected_thread.get()) { | 49 | sched.guard.lock(); |
| 50 | if (thread != sched.selected_thread_set.get()) { | ||
| 46 | if (thread == nullptr) { | 51 | if (thread == nullptr) { |
| 47 | ++sched.idle_selection_count; | 52 | ++sched.idle_selection_count; |
| 48 | } | 53 | } |
| 49 | sched.selected_thread = SharedFrom(thread); | 54 | sched.selected_thread_set = SharedFrom(thread); |
| 50 | } | 55 | } |
| 51 | sched.is_context_switch_pending = sched.selected_thread != sched.current_thread; | 56 | const bool reschedule_pending = |
| 57 | sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread); | ||
| 58 | sched.is_context_switch_pending = reschedule_pending; | ||
| 52 | std::atomic_thread_fence(std::memory_order_seq_cst); | 59 | std::atomic_thread_fence(std::memory_order_seq_cst); |
| 60 | sched.guard.unlock(); | ||
| 61 | return reschedule_pending; | ||
| 53 | }; | 62 | }; |
| 54 | Scheduler& sched = kernel.Scheduler(core); | 63 | if (!is_reselection_pending.load()) { |
| 55 | Thread* current_thread = nullptr; | 64 | return 0; |
| 56 | // Step 1: Get top thread in schedule queue. | ||
| 57 | current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); | ||
| 58 | if (current_thread) { | ||
| 59 | update_thread(current_thread, sched); | ||
| 60 | return; | ||
| 61 | } | 65 | } |
| 62 | // Step 2: Try selecting a suggested thread. | 66 | std::array<Thread*, Core::Hardware::NUM_CPU_CORES> top_threads{}; |
| 63 | Thread* winner = nullptr; | 67 | |
| 64 | std::set<s32> sug_cores; | 68 | u32 idle_cores{}; |
| 65 | for (auto thread : suggested_queue[core]) { | 69 | |
| 66 | s32 this_core = thread->GetProcessorID(); | 70 | // Step 1: Get top thread in schedule queue. |
| 67 | Thread* thread_on_core = nullptr; | 71 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 68 | if (this_core >= 0) { | 72 | Thread* top_thread = |
| 69 | thread_on_core = scheduled_queue[this_core].front(); | 73 | scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); |
| 70 | } | 74 | if (top_thread != nullptr) { |
| 71 | if (this_core < 0 || thread != thread_on_core) { | 75 | // TODO(Blinkhawk): Implement Thread Pinning |
| 72 | winner = thread; | 76 | } else { |
| 73 | break; | 77 | idle_cores |= (1ul << core); |
| 74 | } | 78 | } |
| 75 | sug_cores.insert(this_core); | 79 | top_threads[core] = top_thread; |
| 76 | } | 80 | } |
| 77 | // if we got a suggested thread, select it, else do a second pass. | 81 | |
| 78 | if (winner && winner->GetPriority() > 2) { | 82 | while (idle_cores != 0) { |
| 79 | if (winner->IsRunning()) { | 83 | u32 core_id = Common::CountTrailingZeroes32(idle_cores); |
| 80 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | 84 | |
| 85 | if (!suggested_queue[core_id].empty()) { | ||
| 86 | std::array<s32, Core::Hardware::NUM_CPU_CORES> migration_candidates{}; | ||
| 87 | std::size_t num_candidates = 0; | ||
| 88 | auto iter = suggested_queue[core_id].begin(); | ||
| 89 | Thread* suggested = nullptr; | ||
| 90 | // Step 2: Try selecting a suggested thread. | ||
| 91 | while (iter != suggested_queue[core_id].end()) { | ||
| 92 | suggested = *iter; | ||
| 93 | iter++; | ||
| 94 | s32 suggested_core_id = suggested->GetProcessorID(); | ||
| 95 | Thread* top_thread = | ||
| 96 | suggested_core_id >= 0 ? top_threads[suggested_core_id] : nullptr; | ||
| 97 | if (top_thread != suggested) { | ||
| 98 | if (top_thread != nullptr && | ||
| 99 | top_thread->GetPriority() < THREADPRIO_MAX_CORE_MIGRATION) { | ||
| 100 | suggested = nullptr; | ||
| 101 | break; | ||
| 102 | // There's a too high thread to do core migration, cancel | ||
| 103 | } | ||
| 104 | TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id), suggested); | ||
| 105 | break; | ||
| 106 | } | ||
| 107 | suggested = nullptr; | ||
| 108 | migration_candidates[num_candidates++] = suggested_core_id; | ||
| 109 | } | ||
| 110 | // Step 3: Select a suggested thread from another core | ||
| 111 | if (suggested == nullptr) { | ||
| 112 | for (std::size_t i = 0; i < num_candidates; i++) { | ||
| 113 | s32 candidate_core = migration_candidates[i]; | ||
| 114 | suggested = top_threads[candidate_core]; | ||
| 115 | auto it = scheduled_queue[candidate_core].begin(); | ||
| 116 | it++; | ||
| 117 | Thread* next = it != scheduled_queue[candidate_core].end() ? *it : nullptr; | ||
| 118 | if (next != nullptr) { | ||
| 119 | TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id), | ||
| 120 | suggested); | ||
| 121 | top_threads[candidate_core] = next; | ||
| 122 | break; | ||
| 123 | } else { | ||
| 124 | suggested = nullptr; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | top_threads[core_id] = suggested; | ||
| 81 | } | 129 | } |
| 82 | TransferToCore(winner->GetPriority(), static_cast<s32>(core), winner); | 130 | |
| 83 | update_thread(winner, sched); | 131 | idle_cores &= ~(1ul << core_id); |
| 84 | return; | ||
| 85 | } | 132 | } |
| 86 | // Step 3: Select a suggested thread from another core | 133 | u32 cores_needing_context_switch{}; |
| 87 | for (auto& src_core : sug_cores) { | 134 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 88 | auto it = scheduled_queue[src_core].begin(); | 135 | Scheduler& sched = kernel.Scheduler(core); |
| 89 | it++; | 136 | ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core); |
| 90 | if (it != scheduled_queue[src_core].end()) { | 137 | if (update_thread(top_threads[core], sched)) { |
| 91 | Thread* thread_on_core = scheduled_queue[src_core].front(); | 138 | cores_needing_context_switch |= (1ul << core); |
| 92 | Thread* to_change = *it; | ||
| 93 | if (thread_on_core->IsRunning() || to_change->IsRunning()) { | ||
| 94 | UnloadThread(static_cast<u32>(src_core)); | ||
| 95 | } | ||
| 96 | TransferToCore(thread_on_core->GetPriority(), static_cast<s32>(core), thread_on_core); | ||
| 97 | current_thread = thread_on_core; | ||
| 98 | break; | ||
| 99 | } | 139 | } |
| 100 | } | 140 | } |
| 101 | update_thread(current_thread, sched); | 141 | return cores_needing_context_switch; |
| 102 | } | 142 | } |
| 103 | 143 | ||
| 104 | bool GlobalScheduler::YieldThread(Thread* yielding_thread) { | 144 | bool GlobalScheduler::YieldThread(Thread* yielding_thread) { |
| 145 | ASSERT(is_locked); | ||
| 105 | // Note: caller should use critical section, etc. | 146 | // Note: caller should use critical section, etc. |
| 147 | if (!yielding_thread->IsRunnable()) { | ||
| 148 | // Normally this case shouldn't happen except for SetThreadActivity. | ||
| 149 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 150 | return false; | ||
| 151 | } | ||
| 106 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); | 152 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); |
| 107 | const u32 priority = yielding_thread->GetPriority(); | 153 | const u32 priority = yielding_thread->GetPriority(); |
| 108 | 154 | ||
| 109 | // Yield the thread | 155 | // Yield the thread |
| 110 | const Thread* const winner = scheduled_queue[core_id].front(priority); | 156 | Reschedule(priority, core_id, yielding_thread); |
| 111 | ASSERT_MSG(yielding_thread == winner, "Thread yielding without being in front"); | 157 | const Thread* const winner = scheduled_queue[core_id].front(); |
| 112 | scheduled_queue[core_id].yield(priority); | 158 | if (kernel.GetCurrentHostThreadID() != core_id) { |
| 159 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 160 | } | ||
| 113 | 161 | ||
| 114 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); | 162 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); |
| 115 | } | 163 | } |
| 116 | 164 | ||
| 117 | bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { | 165 | bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { |
| 166 | ASSERT(is_locked); | ||
| 118 | // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, | 167 | // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, |
| 119 | // etc. | 168 | // etc. |
| 169 | if (!yielding_thread->IsRunnable()) { | ||
| 170 | // Normally this case shouldn't happen except for SetThreadActivity. | ||
| 171 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 172 | return false; | ||
| 173 | } | ||
| 120 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); | 174 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); |
| 121 | const u32 priority = yielding_thread->GetPriority(); | 175 | const u32 priority = yielding_thread->GetPriority(); |
| 122 | 176 | ||
| 123 | // Yield the thread | 177 | // Yield the thread |
| 124 | ASSERT_MSG(yielding_thread == scheduled_queue[core_id].front(priority), | 178 | Reschedule(priority, core_id, yielding_thread); |
| 125 | "Thread yielding without being in front"); | ||
| 126 | scheduled_queue[core_id].yield(priority); | ||
| 127 | 179 | ||
| 128 | std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads; | 180 | std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads; |
| 129 | for (std::size_t i = 0; i < current_threads.size(); i++) { | 181 | for (std::size_t i = 0; i < current_threads.size(); i++) { |
| @@ -153,21 +205,28 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { | |||
| 153 | 205 | ||
| 154 | if (winner != nullptr) { | 206 | if (winner != nullptr) { |
| 155 | if (winner != yielding_thread) { | 207 | if (winner != yielding_thread) { |
| 156 | if (winner->IsRunning()) { | ||
| 157 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 158 | } | ||
| 159 | TransferToCore(winner->GetPriority(), s32(core_id), winner); | 208 | TransferToCore(winner->GetPriority(), s32(core_id), winner); |
| 160 | } | 209 | } |
| 161 | } else { | 210 | } else { |
| 162 | winner = next_thread; | 211 | winner = next_thread; |
| 163 | } | 212 | } |
| 164 | 213 | ||
| 214 | if (kernel.GetCurrentHostThreadID() != core_id) { | ||
| 215 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 216 | } | ||
| 217 | |||
| 165 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); | 218 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); |
| 166 | } | 219 | } |
| 167 | 220 | ||
| 168 | bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) { | 221 | bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) { |
| 222 | ASSERT(is_locked); | ||
| 169 | // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, | 223 | // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, |
| 170 | // etc. | 224 | // etc. |
| 225 | if (!yielding_thread->IsRunnable()) { | ||
| 226 | // Normally this case shouldn't happen except for SetThreadActivity. | ||
| 227 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 228 | return false; | ||
| 229 | } | ||
| 171 | Thread* winner = nullptr; | 230 | Thread* winner = nullptr; |
| 172 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); | 231 | const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); |
| 173 | 232 | ||
| @@ -195,25 +254,31 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread | |||
| 195 | } | 254 | } |
| 196 | if (winner != nullptr) { | 255 | if (winner != nullptr) { |
| 197 | if (winner != yielding_thread) { | 256 | if (winner != yielding_thread) { |
| 198 | if (winner->IsRunning()) { | ||
| 199 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 200 | } | ||
| 201 | TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner); | 257 | TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner); |
| 202 | } | 258 | } |
| 203 | } else { | 259 | } else { |
| 204 | winner = yielding_thread; | 260 | winner = yielding_thread; |
| 205 | } | 261 | } |
| 262 | } else { | ||
| 263 | winner = scheduled_queue[core_id].front(); | ||
| 264 | } | ||
| 265 | |||
| 266 | if (kernel.GetCurrentHostThreadID() != core_id) { | ||
| 267 | is_reselection_pending.store(true, std::memory_order_release); | ||
| 206 | } | 268 | } |
| 207 | 269 | ||
| 208 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); | 270 | return AskForReselectionOrMarkRedundant(yielding_thread, winner); |
| 209 | } | 271 | } |
| 210 | 272 | ||
| 211 | void GlobalScheduler::PreemptThreads() { | 273 | void GlobalScheduler::PreemptThreads() { |
| 274 | ASSERT(is_locked); | ||
| 212 | for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { | 275 | for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { |
| 213 | const u32 priority = preemption_priorities[core_id]; | 276 | const u32 priority = preemption_priorities[core_id]; |
| 214 | 277 | ||
| 215 | if (scheduled_queue[core_id].size(priority) > 0) { | 278 | if (scheduled_queue[core_id].size(priority) > 0) { |
| 216 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); | 279 | if (scheduled_queue[core_id].size(priority) > 1) { |
| 280 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); | ||
| 281 | } | ||
| 217 | scheduled_queue[core_id].yield(priority); | 282 | scheduled_queue[core_id].yield(priority); |
| 218 | if (scheduled_queue[core_id].size(priority) > 1) { | 283 | if (scheduled_queue[core_id].size(priority) > 1) { |
| 219 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); | 284 | scheduled_queue[core_id].front(priority)->IncrementYieldCount(); |
| @@ -247,9 +312,6 @@ void GlobalScheduler::PreemptThreads() { | |||
| 247 | } | 312 | } |
| 248 | 313 | ||
| 249 | if (winner != nullptr) { | 314 | if (winner != nullptr) { |
| 250 | if (winner->IsRunning()) { | ||
| 251 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 252 | } | ||
| 253 | TransferToCore(winner->GetPriority(), s32(core_id), winner); | 315 | TransferToCore(winner->GetPriority(), s32(core_id), winner); |
| 254 | current_thread = | 316 | current_thread = |
| 255 | winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread; | 317 | winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread; |
| @@ -280,9 +342,6 @@ void GlobalScheduler::PreemptThreads() { | |||
| 280 | } | 342 | } |
| 281 | 343 | ||
| 282 | if (winner != nullptr) { | 344 | if (winner != nullptr) { |
| 283 | if (winner->IsRunning()) { | ||
| 284 | UnloadThread(static_cast<u32>(winner->GetProcessorID())); | ||
| 285 | } | ||
| 286 | TransferToCore(winner->GetPriority(), s32(core_id), winner); | 345 | TransferToCore(winner->GetPriority(), s32(core_id), winner); |
| 287 | current_thread = winner; | 346 | current_thread = winner; |
| 288 | } | 347 | } |
| @@ -292,34 +351,65 @@ void GlobalScheduler::PreemptThreads() { | |||
| 292 | } | 351 | } |
| 293 | } | 352 | } |
| 294 | 353 | ||
| 354 | void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule, | ||
| 355 | Core::EmuThreadHandle global_thread) { | ||
| 356 | u32 current_core = global_thread.host_handle; | ||
| 357 | bool must_context_switch = global_thread.guest_handle != InvalidHandle && | ||
| 358 | (current_core < Core::Hardware::NUM_CPU_CORES); | ||
| 359 | while (cores_pending_reschedule != 0) { | ||
| 360 | u32 core = Common::CountTrailingZeroes32(cores_pending_reschedule); | ||
| 361 | ASSERT(core < Core::Hardware::NUM_CPU_CORES); | ||
| 362 | if (!must_context_switch || core != current_core) { | ||
| 363 | auto& phys_core = kernel.PhysicalCore(core); | ||
| 364 | phys_core.Interrupt(); | ||
| 365 | } else { | ||
| 366 | must_context_switch = true; | ||
| 367 | } | ||
| 368 | cores_pending_reschedule &= ~(1ul << core); | ||
| 369 | } | ||
| 370 | if (must_context_switch) { | ||
| 371 | auto& core_scheduler = kernel.CurrentScheduler(); | ||
| 372 | kernel.ExitSVCProfile(); | ||
| 373 | core_scheduler.TryDoContextSwitch(); | ||
| 374 | kernel.EnterSVCProfile(); | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 295 | void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) { | 378 | void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) { |
| 379 | ASSERT(is_locked); | ||
| 296 | suggested_queue[core].add(thread, priority); | 380 | suggested_queue[core].add(thread, priority); |
| 297 | } | 381 | } |
| 298 | 382 | ||
| 299 | void GlobalScheduler::Unsuggest(u32 priority, std::size_t core, Thread* thread) { | 383 | void GlobalScheduler::Unsuggest(u32 priority, std::size_t core, Thread* thread) { |
| 384 | ASSERT(is_locked); | ||
| 300 | suggested_queue[core].remove(thread, priority); | 385 | suggested_queue[core].remove(thread, priority); |
| 301 | } | 386 | } |
| 302 | 387 | ||
| 303 | void GlobalScheduler::Schedule(u32 priority, std::size_t core, Thread* thread) { | 388 | void GlobalScheduler::Schedule(u32 priority, std::size_t core, Thread* thread) { |
| 389 | ASSERT(is_locked); | ||
| 304 | ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); | 390 | ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); |
| 305 | scheduled_queue[core].add(thread, priority); | 391 | scheduled_queue[core].add(thread, priority); |
| 306 | } | 392 | } |
| 307 | 393 | ||
| 308 | void GlobalScheduler::SchedulePrepend(u32 priority, std::size_t core, Thread* thread) { | 394 | void GlobalScheduler::SchedulePrepend(u32 priority, std::size_t core, Thread* thread) { |
| 395 | ASSERT(is_locked); | ||
| 309 | ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); | 396 | ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); |
| 310 | scheduled_queue[core].add(thread, priority, false); | 397 | scheduled_queue[core].add(thread, priority, false); |
| 311 | } | 398 | } |
| 312 | 399 | ||
| 313 | void GlobalScheduler::Reschedule(u32 priority, std::size_t core, Thread* thread) { | 400 | void GlobalScheduler::Reschedule(u32 priority, std::size_t core, Thread* thread) { |
| 401 | ASSERT(is_locked); | ||
| 314 | scheduled_queue[core].remove(thread, priority); | 402 | scheduled_queue[core].remove(thread, priority); |
| 315 | scheduled_queue[core].add(thread, priority); | 403 | scheduled_queue[core].add(thread, priority); |
| 316 | } | 404 | } |
| 317 | 405 | ||
| 318 | void GlobalScheduler::Unschedule(u32 priority, std::size_t core, Thread* thread) { | 406 | void GlobalScheduler::Unschedule(u32 priority, std::size_t core, Thread* thread) { |
| 407 | ASSERT(is_locked); | ||
| 319 | scheduled_queue[core].remove(thread, priority); | 408 | scheduled_queue[core].remove(thread, priority); |
| 320 | } | 409 | } |
| 321 | 410 | ||
| 322 | void GlobalScheduler::TransferToCore(u32 priority, s32 destination_core, Thread* thread) { | 411 | void GlobalScheduler::TransferToCore(u32 priority, s32 destination_core, Thread* thread) { |
| 412 | ASSERT(is_locked); | ||
| 323 | const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT; | 413 | const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT; |
| 324 | const s32 source_core = thread->GetProcessorID(); | 414 | const s32 source_core = thread->GetProcessorID(); |
| 325 | if (source_core == destination_core || !schedulable) { | 415 | if (source_core == destination_core || !schedulable) { |
| @@ -349,6 +439,108 @@ bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread, | |||
| 349 | } | 439 | } |
| 350 | } | 440 | } |
| 351 | 441 | ||
| 442 | void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) { | ||
| 443 | if (old_flags == thread->scheduling_state) { | ||
| 444 | return; | ||
| 445 | } | ||
| 446 | ASSERT(is_locked); | ||
| 447 | |||
| 448 | if (old_flags == static_cast<u32>(ThreadSchedStatus::Runnable)) { | ||
| 449 | // In this case the thread was running, now it's pausing/exitting | ||
| 450 | if (thread->processor_id >= 0) { | ||
| 451 | Unschedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 452 | } | ||
| 453 | |||
| 454 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 455 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 456 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 457 | Unsuggest(thread->current_priority, core, thread); | ||
| 458 | } | ||
| 459 | } | ||
| 460 | } else if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { | ||
| 461 | // The thread is now set to running from being stopped | ||
| 462 | if (thread->processor_id >= 0) { | ||
| 463 | Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 464 | } | ||
| 465 | |||
| 466 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 467 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 468 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 469 | Suggest(thread->current_priority, core, thread); | ||
| 470 | } | ||
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 474 | SetReselectionPending(); | ||
| 475 | } | ||
| 476 | |||
| 477 | void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priority) { | ||
| 478 | if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable)) { | ||
| 479 | return; | ||
| 480 | } | ||
| 481 | ASSERT(is_locked); | ||
| 482 | if (thread->processor_id >= 0) { | ||
| 483 | Unschedule(old_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 484 | } | ||
| 485 | |||
| 486 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 487 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 488 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 489 | Unsuggest(old_priority, core, thread); | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | if (thread->processor_id >= 0) { | ||
| 494 | if (thread == kernel.CurrentScheduler().GetCurrentThread()) { | ||
| 495 | SchedulePrepend(thread->current_priority, static_cast<u32>(thread->processor_id), | ||
| 496 | thread); | ||
| 497 | } else { | ||
| 498 | Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread); | ||
| 499 | } | ||
| 500 | } | ||
| 501 | |||
| 502 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 503 | if (core != static_cast<u32>(thread->processor_id) && | ||
| 504 | ((thread->affinity_mask >> core) & 1) != 0) { | ||
| 505 | Suggest(thread->current_priority, core, thread); | ||
| 506 | } | ||
| 507 | } | ||
| 508 | thread->IncrementYieldCount(); | ||
| 509 | SetReselectionPending(); | ||
| 510 | } | ||
| 511 | |||
| 512 | void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, | ||
| 513 | s32 old_core) { | ||
| 514 | if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable) || | ||
| 515 | thread->current_priority >= THREADPRIO_COUNT) { | ||
| 516 | return; | ||
| 517 | } | ||
| 518 | ASSERT(is_locked); | ||
| 519 | |||
| 520 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 521 | if (((old_affinity_mask >> core) & 1) != 0) { | ||
| 522 | if (core == static_cast<u32>(old_core)) { | ||
| 523 | Unschedule(thread->current_priority, core, thread); | ||
| 524 | } else { | ||
| 525 | Unsuggest(thread->current_priority, core, thread); | ||
| 526 | } | ||
| 527 | } | ||
| 528 | } | ||
| 529 | |||
| 530 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 531 | if (((thread->affinity_mask >> core) & 1) != 0) { | ||
| 532 | if (core == static_cast<u32>(thread->processor_id)) { | ||
| 533 | Schedule(thread->current_priority, core, thread); | ||
| 534 | } else { | ||
| 535 | Suggest(thread->current_priority, core, thread); | ||
| 536 | } | ||
| 537 | } | ||
| 538 | } | ||
| 539 | |||
| 540 | thread->IncrementYieldCount(); | ||
| 541 | SetReselectionPending(); | ||
| 542 | } | ||
| 543 | |||
| 352 | void GlobalScheduler::Shutdown() { | 544 | void GlobalScheduler::Shutdown() { |
| 353 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 545 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 354 | scheduled_queue[core].clear(); | 546 | scheduled_queue[core].clear(); |
| @@ -359,10 +551,12 @@ void GlobalScheduler::Shutdown() { | |||
| 359 | 551 | ||
| 360 | void GlobalScheduler::Lock() { | 552 | void GlobalScheduler::Lock() { |
| 361 | Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID(); | 553 | Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID(); |
| 554 | ASSERT(!current_thread.IsInvalid()); | ||
| 362 | if (current_thread == current_owner) { | 555 | if (current_thread == current_owner) { |
| 363 | ++scope_lock; | 556 | ++scope_lock; |
| 364 | } else { | 557 | } else { |
| 365 | inner_lock.lock(); | 558 | inner_lock.lock(); |
| 559 | is_locked = true; | ||
| 366 | current_owner = current_thread; | 560 | current_owner = current_thread; |
| 367 | ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle()); | 561 | ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle()); |
| 368 | scope_lock = 1; | 562 | scope_lock = 1; |
| @@ -374,17 +568,18 @@ void GlobalScheduler::Unlock() { | |||
| 374 | ASSERT(scope_lock > 0); | 568 | ASSERT(scope_lock > 0); |
| 375 | return; | 569 | return; |
| 376 | } | 570 | } |
| 377 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | 571 | u32 cores_pending_reschedule = SelectThreads(); |
| 378 | SelectThread(i); | 572 | Core::EmuThreadHandle leaving_thread = current_owner; |
| 379 | } | ||
| 380 | current_owner = Core::EmuThreadHandle::InvalidHandle(); | 573 | current_owner = Core::EmuThreadHandle::InvalidHandle(); |
| 381 | scope_lock = 1; | 574 | scope_lock = 1; |
| 575 | is_locked = false; | ||
| 382 | inner_lock.unlock(); | 576 | inner_lock.unlock(); |
| 383 | // TODO(Blinkhawk): Setup the interrupts and change context on current core. | 577 | EnableInterruptAndSchedule(cores_pending_reschedule, leaving_thread); |
| 384 | } | 578 | } |
| 385 | 579 | ||
| 386 | Scheduler::Scheduler(Core::System& system, std::size_t core_id) | 580 | Scheduler::Scheduler(Core::System& system, std::size_t core_id) : system(system), core_id(core_id) { |
| 387 | : system{system}, core_id{core_id} {} | 581 | switch_fiber = std::make_shared<Common::Fiber>(std::function<void(void*)>(OnSwitch), this); |
| 582 | } | ||
| 388 | 583 | ||
| 389 | Scheduler::~Scheduler() = default; | 584 | Scheduler::~Scheduler() = default; |
| 390 | 585 | ||
| @@ -393,56 +588,128 @@ bool Scheduler::HaveReadyThreads() const { | |||
| 393 | } | 588 | } |
| 394 | 589 | ||
| 395 | Thread* Scheduler::GetCurrentThread() const { | 590 | Thread* Scheduler::GetCurrentThread() const { |
| 396 | return current_thread.get(); | 591 | if (current_thread) { |
| 592 | return current_thread.get(); | ||
| 593 | } | ||
| 594 | return idle_thread.get(); | ||
| 397 | } | 595 | } |
| 398 | 596 | ||
| 399 | Thread* Scheduler::GetSelectedThread() const { | 597 | Thread* Scheduler::GetSelectedThread() const { |
| 400 | return selected_thread.get(); | 598 | return selected_thread.get(); |
| 401 | } | 599 | } |
| 402 | 600 | ||
| 403 | void Scheduler::SelectThreads() { | ||
| 404 | system.GlobalScheduler().SelectThread(core_id); | ||
| 405 | } | ||
| 406 | |||
| 407 | u64 Scheduler::GetLastContextSwitchTicks() const { | 601 | u64 Scheduler::GetLastContextSwitchTicks() const { |
| 408 | return last_context_switch_time; | 602 | return last_context_switch_time; |
| 409 | } | 603 | } |
| 410 | 604 | ||
| 411 | void Scheduler::TryDoContextSwitch() { | 605 | void Scheduler::TryDoContextSwitch() { |
| 606 | auto& phys_core = system.Kernel().CurrentPhysicalCore(); | ||
| 607 | if (phys_core.IsInterrupted()) { | ||
| 608 | phys_core.ClearInterrupt(); | ||
| 609 | } | ||
| 610 | guard.lock(); | ||
| 412 | if (is_context_switch_pending) { | 611 | if (is_context_switch_pending) { |
| 413 | SwitchContext(); | 612 | SwitchContext(); |
| 613 | } else { | ||
| 614 | guard.unlock(); | ||
| 414 | } | 615 | } |
| 415 | } | 616 | } |
| 416 | 617 | ||
| 417 | void Scheduler::UnloadThread() { | 618 | void Scheduler::OnThreadStart() { |
| 418 | Thread* const previous_thread = GetCurrentThread(); | 619 | SwitchContextStep2(); |
| 419 | Process* const previous_process = system.Kernel().CurrentProcess(); | 620 | } |
| 420 | 621 | ||
| 421 | UpdateLastContextSwitchTime(previous_thread, previous_process); | 622 | void Scheduler::Unload() { |
| 623 | Thread* thread = current_thread.get(); | ||
| 624 | if (thread) { | ||
| 625 | thread->SetContinuousOnSVC(false); | ||
| 626 | thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); | ||
| 627 | thread->SetIsRunning(false); | ||
| 628 | if (!thread->IsHLEThread() && !thread->HasExited()) { | ||
| 629 | Core::ARM_Interface& cpu_core = thread->ArmInterface(); | ||
| 630 | cpu_core.SaveContext(thread->GetContext32()); | ||
| 631 | cpu_core.SaveContext(thread->GetContext64()); | ||
| 632 | // Save the TPIDR_EL0 system register in case it was modified. | ||
| 633 | thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); | ||
| 634 | cpu_core.ClearExclusiveState(); | ||
| 635 | } | ||
| 636 | thread->context_guard.unlock(); | ||
| 637 | } | ||
| 638 | } | ||
| 422 | 639 | ||
| 423 | // Save context for previous thread | 640 | void Scheduler::Reload() { |
| 424 | if (previous_thread) { | 641 | Thread* thread = current_thread.get(); |
| 425 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); | 642 | if (thread) { |
| 426 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64()); | 643 | ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, |
| 427 | // Save the TPIDR_EL0 system register in case it was modified. | 644 | "Thread must be runnable."); |
| 428 | previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0()); | ||
| 429 | 645 | ||
| 430 | if (previous_thread->GetStatus() == ThreadStatus::Running) { | 646 | // Cancel any outstanding wakeup events for this thread |
| 431 | // This is only the case when a reschedule is triggered without the current thread | 647 | thread->SetIsRunning(true); |
| 432 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) | 648 | thread->SetWasRunning(false); |
| 433 | previous_thread->SetStatus(ThreadStatus::Ready); | 649 | thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); |
| 650 | |||
| 651 | auto* const thread_owner_process = thread->GetOwnerProcess(); | ||
| 652 | if (thread_owner_process != nullptr) { | ||
| 653 | system.Kernel().MakeCurrentProcess(thread_owner_process); | ||
| 654 | } | ||
| 655 | if (!thread->IsHLEThread()) { | ||
| 656 | Core::ARM_Interface& cpu_core = thread->ArmInterface(); | ||
| 657 | cpu_core.LoadContext(thread->GetContext32()); | ||
| 658 | cpu_core.LoadContext(thread->GetContext64()); | ||
| 659 | cpu_core.SetTlsAddress(thread->GetTLSAddress()); | ||
| 660 | cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); | ||
| 661 | cpu_core.ChangeProcessorID(this->core_id); | ||
| 662 | cpu_core.ClearExclusiveState(); | ||
| 434 | } | 663 | } |
| 435 | previous_thread->SetIsRunning(false); | ||
| 436 | } | 664 | } |
| 437 | current_thread = nullptr; | 665 | } |
| 666 | |||
| 667 | void Scheduler::SwitchContextStep2() { | ||
| 668 | Thread* previous_thread = current_thread_prev.get(); | ||
| 669 | Thread* new_thread = selected_thread.get(); | ||
| 670 | |||
| 671 | // Load context of new thread | ||
| 672 | Process* const previous_process = | ||
| 673 | previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; | ||
| 674 | |||
| 675 | if (new_thread) { | ||
| 676 | ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, | ||
| 677 | "Thread must be runnable."); | ||
| 678 | |||
| 679 | // Cancel any outstanding wakeup events for this thread | ||
| 680 | new_thread->SetIsRunning(true); | ||
| 681 | new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); | ||
| 682 | new_thread->SetWasRunning(false); | ||
| 683 | |||
| 684 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); | ||
| 685 | if (thread_owner_process != nullptr) { | ||
| 686 | system.Kernel().MakeCurrentProcess(thread_owner_process); | ||
| 687 | } | ||
| 688 | if (!new_thread->IsHLEThread()) { | ||
| 689 | Core::ARM_Interface& cpu_core = new_thread->ArmInterface(); | ||
| 690 | cpu_core.LoadContext(new_thread->GetContext32()); | ||
| 691 | cpu_core.LoadContext(new_thread->GetContext64()); | ||
| 692 | cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); | ||
| 693 | cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); | ||
| 694 | cpu_core.ChangeProcessorID(this->core_id); | ||
| 695 | cpu_core.ClearExclusiveState(); | ||
| 696 | } | ||
| 697 | } | ||
| 698 | |||
| 699 | TryDoContextSwitch(); | ||
| 438 | } | 700 | } |
| 439 | 701 | ||
| 440 | void Scheduler::SwitchContext() { | 702 | void Scheduler::SwitchContext() { |
| 441 | Thread* const previous_thread = GetCurrentThread(); | 703 | current_thread_prev = current_thread; |
| 442 | Thread* const new_thread = GetSelectedThread(); | 704 | selected_thread = selected_thread_set; |
| 705 | Thread* previous_thread = current_thread_prev.get(); | ||
| 706 | Thread* new_thread = selected_thread.get(); | ||
| 707 | current_thread = selected_thread; | ||
| 443 | 708 | ||
| 444 | is_context_switch_pending = false; | 709 | is_context_switch_pending = false; |
| 710 | |||
| 445 | if (new_thread == previous_thread) { | 711 | if (new_thread == previous_thread) { |
| 712 | guard.unlock(); | ||
| 446 | return; | 713 | return; |
| 447 | } | 714 | } |
| 448 | 715 | ||
| @@ -452,51 +719,75 @@ void Scheduler::SwitchContext() { | |||
| 452 | 719 | ||
| 453 | // Save context for previous thread | 720 | // Save context for previous thread |
| 454 | if (previous_thread) { | 721 | if (previous_thread) { |
| 455 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); | 722 | if (new_thread != nullptr && new_thread->IsSuspendThread()) { |
| 456 | system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64()); | 723 | previous_thread->SetWasRunning(true); |
| 457 | // Save the TPIDR_EL0 system register in case it was modified. | ||
| 458 | previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0()); | ||
| 459 | |||
| 460 | if (previous_thread->GetStatus() == ThreadStatus::Running) { | ||
| 461 | // This is only the case when a reschedule is triggered without the current thread | ||
| 462 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) | ||
| 463 | previous_thread->SetStatus(ThreadStatus::Ready); | ||
| 464 | } | 724 | } |
| 725 | previous_thread->SetContinuousOnSVC(false); | ||
| 726 | previous_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); | ||
| 465 | previous_thread->SetIsRunning(false); | 727 | previous_thread->SetIsRunning(false); |
| 466 | } | 728 | if (!previous_thread->IsHLEThread() && !previous_thread->HasExited()) { |
| 467 | 729 | Core::ARM_Interface& cpu_core = previous_thread->ArmInterface(); | |
| 468 | // Load context of new thread | 730 | cpu_core.SaveContext(previous_thread->GetContext32()); |
| 469 | if (new_thread) { | 731 | cpu_core.SaveContext(previous_thread->GetContext64()); |
| 470 | ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id), | 732 | // Save the TPIDR_EL0 system register in case it was modified. |
| 471 | "Thread must be assigned to this core."); | 733 | previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); |
| 472 | ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, | 734 | cpu_core.ClearExclusiveState(); |
| 473 | "Thread must be ready to become running."); | ||
| 474 | |||
| 475 | // Cancel any outstanding wakeup events for this thread | ||
| 476 | new_thread->CancelWakeupTimer(); | ||
| 477 | current_thread = SharedFrom(new_thread); | ||
| 478 | new_thread->SetStatus(ThreadStatus::Running); | ||
| 479 | new_thread->SetIsRunning(true); | ||
| 480 | |||
| 481 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); | ||
| 482 | if (previous_process != thread_owner_process) { | ||
| 483 | system.Kernel().MakeCurrentProcess(thread_owner_process); | ||
| 484 | } | 735 | } |
| 736 | previous_thread->context_guard.unlock(); | ||
| 737 | } | ||
| 485 | 738 | ||
| 486 | system.ArmInterface(core_id).LoadContext(new_thread->GetContext32()); | 739 | std::shared_ptr<Common::Fiber>* old_context; |
| 487 | system.ArmInterface(core_id).LoadContext(new_thread->GetContext64()); | 740 | if (previous_thread != nullptr) { |
| 488 | system.ArmInterface(core_id).SetTlsAddress(new_thread->GetTLSAddress()); | 741 | old_context = &previous_thread->GetHostContext(); |
| 489 | system.ArmInterface(core_id).SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); | ||
| 490 | } else { | 742 | } else { |
| 491 | current_thread = nullptr; | 743 | old_context = &idle_thread->GetHostContext(); |
| 492 | // Note: We do not reset the current process and current page table when idling because | 744 | } |
| 493 | // technically we haven't changed processes, our threads are just paused. | 745 | guard.unlock(); |
| 746 | |||
| 747 | Common::Fiber::YieldTo(*old_context, switch_fiber); | ||
| 748 | /// When a thread wakes up, the scheduler may have changed to other in another core. | ||
| 749 | auto& next_scheduler = system.Kernel().CurrentScheduler(); | ||
| 750 | next_scheduler.SwitchContextStep2(); | ||
| 751 | } | ||
| 752 | |||
| 753 | void Scheduler::OnSwitch(void* this_scheduler) { | ||
| 754 | Scheduler* sched = static_cast<Scheduler*>(this_scheduler); | ||
| 755 | sched->SwitchToCurrent(); | ||
| 756 | } | ||
| 757 | |||
| 758 | void Scheduler::SwitchToCurrent() { | ||
| 759 | while (true) { | ||
| 760 | guard.lock(); | ||
| 761 | selected_thread = selected_thread_set; | ||
| 762 | current_thread = selected_thread; | ||
| 763 | is_context_switch_pending = false; | ||
| 764 | guard.unlock(); | ||
| 765 | while (!is_context_switch_pending) { | ||
| 766 | if (current_thread != nullptr && !current_thread->IsHLEThread()) { | ||
| 767 | current_thread->context_guard.lock(); | ||
| 768 | if (!current_thread->IsRunnable()) { | ||
| 769 | current_thread->context_guard.unlock(); | ||
| 770 | break; | ||
| 771 | } | ||
| 772 | if (current_thread->GetProcessorID() != core_id) { | ||
| 773 | current_thread->context_guard.unlock(); | ||
| 774 | break; | ||
| 775 | } | ||
| 776 | } | ||
| 777 | std::shared_ptr<Common::Fiber>* next_context; | ||
| 778 | if (current_thread != nullptr) { | ||
| 779 | next_context = ¤t_thread->GetHostContext(); | ||
| 780 | } else { | ||
| 781 | next_context = &idle_thread->GetHostContext(); | ||
| 782 | } | ||
| 783 | Common::Fiber::YieldTo(switch_fiber, *next_context); | ||
| 784 | } | ||
| 494 | } | 785 | } |
| 495 | } | 786 | } |
| 496 | 787 | ||
| 497 | void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { | 788 | void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { |
| 498 | const u64 prev_switch_ticks = last_context_switch_time; | 789 | const u64 prev_switch_ticks = last_context_switch_time; |
| 499 | const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks(); | 790 | const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks(); |
| 500 | const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; | 791 | const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; |
| 501 | 792 | ||
| 502 | if (thread != nullptr) { | 793 | if (thread != nullptr) { |
| @@ -510,6 +801,16 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { | |||
| 510 | last_context_switch_time = most_recent_switch_ticks; | 801 | last_context_switch_time = most_recent_switch_ticks; |
| 511 | } | 802 | } |
| 512 | 803 | ||
| 804 | void Scheduler::Initialize() { | ||
| 805 | std::string name = "Idle Thread Id:" + std::to_string(core_id); | ||
| 806 | std::function<void(void*)> init_func = system.GetCpuManager().GetIdleThreadStartFunc(); | ||
| 807 | void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); | ||
| 808 | ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE); | ||
| 809 | auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0, | ||
| 810 | nullptr, std::move(init_func), init_func_parameter); | ||
| 811 | idle_thread = std::move(thread_res).Unwrap(); | ||
| 812 | } | ||
| 813 | |||
| 513 | void Scheduler::Shutdown() { | 814 | void Scheduler::Shutdown() { |
| 514 | current_thread = nullptr; | 815 | current_thread = nullptr; |
| 515 | selected_thread = nullptr; | 816 | selected_thread = nullptr; |
| @@ -538,4 +839,13 @@ SchedulerLockAndSleep::~SchedulerLockAndSleep() { | |||
| 538 | time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds); | 839 | time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds); |
| 539 | } | 840 | } |
| 540 | 841 | ||
| 842 | void SchedulerLockAndSleep::Release() { | ||
| 843 | if (sleep_cancelled) { | ||
| 844 | return; | ||
| 845 | } | ||
| 846 | auto& time_manager = kernel.TimeManager(); | ||
| 847 | time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds); | ||
| 848 | sleep_cancelled = true; | ||
| 849 | } | ||
| 850 | |||
| 541 | } // namespace Kernel | 851 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 07df33f9c..b3b4b5169 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -11,9 +11,14 @@ | |||
| 11 | 11 | ||
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/multi_level_queue.h" | 13 | #include "common/multi_level_queue.h" |
| 14 | #include "common/spin_lock.h" | ||
| 14 | #include "core/hardware_properties.h" | 15 | #include "core/hardware_properties.h" |
| 15 | #include "core/hle/kernel/thread.h" | 16 | #include "core/hle/kernel/thread.h" |
| 16 | 17 | ||
| 18 | namespace Common { | ||
| 19 | class Fiber; | ||
| 20 | } | ||
| 21 | |||
| 17 | namespace Core { | 22 | namespace Core { |
| 18 | class ARM_Interface; | 23 | class ARM_Interface; |
| 19 | class System; | 24 | class System; |
| @@ -41,41 +46,17 @@ public: | |||
| 41 | return thread_list; | 46 | return thread_list; |
| 42 | } | 47 | } |
| 43 | 48 | ||
| 44 | /** | 49 | /// Notify the scheduler a thread's status has changed. |
| 45 | * Add a thread to the suggested queue of a cpu core. Suggested threads may be | 50 | void AdjustSchedulingOnStatus(Thread* thread, u32 old_flags); |
| 46 | * picked if no thread is scheduled to run on the core. | ||
| 47 | */ | ||
| 48 | void Suggest(u32 priority, std::size_t core, Thread* thread); | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Remove a thread to the suggested queue of a cpu core. Suggested threads may be | ||
| 52 | * picked if no thread is scheduled to run on the core. | ||
| 53 | */ | ||
| 54 | void Unsuggest(u32 priority, std::size_t core, Thread* thread); | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | ||
| 58 | * back the queue in its priority level. | ||
| 59 | */ | ||
| 60 | void Schedule(u32 priority, std::size_t core, Thread* thread); | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | ||
| 64 | * front the queue in its priority level. | ||
| 65 | */ | ||
| 66 | void SchedulePrepend(u32 priority, std::size_t core, Thread* thread); | ||
| 67 | 51 | ||
| 68 | /// Reschedule an already scheduled thread based on a new priority | 52 | /// Notify the scheduler a thread's priority has changed. |
| 69 | void Reschedule(u32 priority, std::size_t core, Thread* thread); | 53 | void AdjustSchedulingOnPriority(Thread* thread, u32 old_priority); |
| 70 | |||
| 71 | /// Unschedules a thread. | ||
| 72 | void Unschedule(u32 priority, std::size_t core, Thread* thread); | ||
| 73 | 54 | ||
| 74 | /// Selects a core and forces it to unload its current thread's context | 55 | /// Notify the scheduler a thread's core and/or affinity mask has changed. |
| 75 | void UnloadThread(std::size_t core); | 56 | void AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, s32 old_core); |
| 76 | 57 | ||
| 77 | /** | 58 | /** |
| 78 | * Takes care of selecting the new scheduled thread in three steps: | 59 | * Takes care of selecting the new scheduled threads in three steps: |
| 79 | * | 60 | * |
| 80 | * 1. First a thread is selected from the top of the priority queue. If no thread | 61 | * 1. First a thread is selected from the top of the priority queue. If no thread |
| 81 | * is obtained then we move to step two, else we are done. | 62 | * is obtained then we move to step two, else we are done. |
| @@ -85,8 +66,10 @@ public: | |||
| 85 | * | 66 | * |
| 86 | * 3. Third is no suggested thread is found, we do a second pass and pick a running | 67 | * 3. Third is no suggested thread is found, we do a second pass and pick a running |
| 87 | * thread in another core and swap it with its current thread. | 68 | * thread in another core and swap it with its current thread. |
| 69 | * | ||
| 70 | * returns the cores needing scheduling. | ||
| 88 | */ | 71 | */ |
| 89 | void SelectThread(std::size_t core); | 72 | u32 SelectThreads(); |
| 90 | 73 | ||
| 91 | bool HaveReadyThreads(std::size_t core_id) const { | 74 | bool HaveReadyThreads(std::size_t core_id) const { |
| 92 | return !scheduled_queue[core_id].empty(); | 75 | return !scheduled_queue[core_id].empty(); |
| @@ -149,6 +132,40 @@ private: | |||
| 149 | /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling | 132 | /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling |
| 150 | /// and reschedules current core if needed. | 133 | /// and reschedules current core if needed. |
| 151 | void Unlock(); | 134 | void Unlock(); |
| 135 | |||
| 136 | void EnableInterruptAndSchedule(u32 cores_pending_reschedule, | ||
| 137 | Core::EmuThreadHandle global_thread); | ||
| 138 | |||
| 139 | /** | ||
| 140 | * Add a thread to the suggested queue of a cpu core. Suggested threads may be | ||
| 141 | * picked if no thread is scheduled to run on the core. | ||
| 142 | */ | ||
| 143 | void Suggest(u32 priority, std::size_t core, Thread* thread); | ||
| 144 | |||
| 145 | /** | ||
| 146 | * Remove a thread to the suggested queue of a cpu core. Suggested threads may be | ||
| 147 | * picked if no thread is scheduled to run on the core. | ||
| 148 | */ | ||
| 149 | void Unsuggest(u32 priority, std::size_t core, Thread* thread); | ||
| 150 | |||
| 151 | /** | ||
| 152 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | ||
| 153 | * back the queue in its priority level. | ||
| 154 | */ | ||
| 155 | void Schedule(u32 priority, std::size_t core, Thread* thread); | ||
| 156 | |||
| 157 | /** | ||
| 158 | * Add a thread to the scheduling queue of a cpu core. The thread is added at the | ||
| 159 | * front the queue in its priority level. | ||
| 160 | */ | ||
| 161 | void SchedulePrepend(u32 priority, std::size_t core, Thread* thread); | ||
| 162 | |||
| 163 | /// Reschedule an already scheduled thread based on a new priority | ||
| 164 | void Reschedule(u32 priority, std::size_t core, Thread* thread); | ||
| 165 | |||
| 166 | /// Unschedules a thread. | ||
| 167 | void Unschedule(u32 priority, std::size_t core, Thread* thread); | ||
| 168 | |||
| 152 | /** | 169 | /** |
| 153 | * Transfers a thread into an specific core. If the destination_core is -1 | 170 | * Transfers a thread into an specific core. If the destination_core is -1 |
| 154 | * it will be unscheduled from its source code and added into its suggested | 171 | * it will be unscheduled from its source code and added into its suggested |
| @@ -170,10 +187,13 @@ private: | |||
| 170 | std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; | 187 | std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; |
| 171 | 188 | ||
| 172 | /// Scheduler lock mechanisms. | 189 | /// Scheduler lock mechanisms. |
| 173 | std::mutex inner_lock{}; // TODO(Blinkhawk): Replace for a SpinLock | 190 | bool is_locked{}; |
| 191 | Common::SpinLock inner_lock{}; | ||
| 174 | std::atomic<s64> scope_lock{}; | 192 | std::atomic<s64> scope_lock{}; |
| 175 | Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; | 193 | Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; |
| 176 | 194 | ||
| 195 | Common::SpinLock global_list_guard{}; | ||
| 196 | |||
| 177 | /// Lists all thread ids that aren't deleted/etc. | 197 | /// Lists all thread ids that aren't deleted/etc. |
| 178 | std::vector<std::shared_ptr<Thread>> thread_list; | 198 | std::vector<std::shared_ptr<Thread>> thread_list; |
| 179 | KernelCore& kernel; | 199 | KernelCore& kernel; |
| @@ -190,11 +210,11 @@ public: | |||
| 190 | /// Reschedules to the next available thread (call after current thread is suspended) | 210 | /// Reschedules to the next available thread (call after current thread is suspended) |
| 191 | void TryDoContextSwitch(); | 211 | void TryDoContextSwitch(); |
| 192 | 212 | ||
| 193 | /// Unloads currently running thread | 213 | /// The next two are for SingleCore Only. |
| 194 | void UnloadThread(); | 214 | /// Unload current thread before preempting core. |
| 195 | 215 | void Unload(); | |
| 196 | /// Select the threads in top of the scheduling multilist. | 216 | /// Reload current thread after core preemption. |
| 197 | void SelectThreads(); | 217 | void Reload(); |
| 198 | 218 | ||
| 199 | /// Gets the current running thread | 219 | /// Gets the current running thread |
| 200 | Thread* GetCurrentThread() const; | 220 | Thread* GetCurrentThread() const; |
| @@ -209,15 +229,30 @@ public: | |||
| 209 | return is_context_switch_pending; | 229 | return is_context_switch_pending; |
| 210 | } | 230 | } |
| 211 | 231 | ||
| 232 | void Initialize(); | ||
| 233 | |||
| 212 | /// Shutdowns the scheduler. | 234 | /// Shutdowns the scheduler. |
| 213 | void Shutdown(); | 235 | void Shutdown(); |
| 214 | 236 | ||
| 237 | void OnThreadStart(); | ||
| 238 | |||
| 239 | std::shared_ptr<Common::Fiber>& ControlContext() { | ||
| 240 | return switch_fiber; | ||
| 241 | } | ||
| 242 | |||
| 243 | const std::shared_ptr<Common::Fiber>& ControlContext() const { | ||
| 244 | return switch_fiber; | ||
| 245 | } | ||
| 246 | |||
| 215 | private: | 247 | private: |
| 216 | friend class GlobalScheduler; | 248 | friend class GlobalScheduler; |
| 217 | 249 | ||
| 218 | /// Switches the CPU's active thread context to that of the specified thread | 250 | /// Switches the CPU's active thread context to that of the specified thread |
| 219 | void SwitchContext(); | 251 | void SwitchContext(); |
| 220 | 252 | ||
| 253 | /// When a thread wakes up, it must run this through it's new scheduler | ||
| 254 | void SwitchContextStep2(); | ||
| 255 | |||
| 221 | /** | 256 | /** |
| 222 | * Called on every context switch to update the internal timestamp | 257 | * Called on every context switch to update the internal timestamp |
| 223 | * This also updates the running time ticks for the given thread and | 258 | * This also updates the running time ticks for the given thread and |
| @@ -231,14 +266,24 @@ private: | |||
| 231 | */ | 266 | */ |
| 232 | void UpdateLastContextSwitchTime(Thread* thread, Process* process); | 267 | void UpdateLastContextSwitchTime(Thread* thread, Process* process); |
| 233 | 268 | ||
| 269 | static void OnSwitch(void* this_scheduler); | ||
| 270 | void SwitchToCurrent(); | ||
| 271 | |||
| 234 | std::shared_ptr<Thread> current_thread = nullptr; | 272 | std::shared_ptr<Thread> current_thread = nullptr; |
| 235 | std::shared_ptr<Thread> selected_thread = nullptr; | 273 | std::shared_ptr<Thread> selected_thread = nullptr; |
| 274 | std::shared_ptr<Thread> current_thread_prev = nullptr; | ||
| 275 | std::shared_ptr<Thread> selected_thread_set = nullptr; | ||
| 276 | std::shared_ptr<Thread> idle_thread = nullptr; | ||
| 277 | |||
| 278 | std::shared_ptr<Common::Fiber> switch_fiber = nullptr; | ||
| 236 | 279 | ||
| 237 | Core::System& system; | 280 | Core::System& system; |
| 238 | u64 last_context_switch_time = 0; | 281 | u64 last_context_switch_time = 0; |
| 239 | u64 idle_selection_count = 0; | 282 | u64 idle_selection_count = 0; |
| 240 | const std::size_t core_id; | 283 | const std::size_t core_id; |
| 241 | 284 | ||
| 285 | Common::SpinLock guard{}; | ||
| 286 | |||
| 242 | bool is_context_switch_pending = false; | 287 | bool is_context_switch_pending = false; |
| 243 | }; | 288 | }; |
| 244 | 289 | ||
| @@ -261,6 +306,8 @@ public: | |||
| 261 | sleep_cancelled = true; | 306 | sleep_cancelled = true; |
| 262 | } | 307 | } |
| 263 | 308 | ||
| 309 | void Release(); | ||
| 310 | |||
| 264 | private: | 311 | private: |
| 265 | Handle& event_handle; | 312 | Handle& event_handle; |
| 266 | Thread* time_task; | 313 | Thread* time_task; |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 25438b86b..7b23a6889 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "core/hle/kernel/hle_ipc.h" | 17 | #include "core/hle/kernel/hle_ipc.h" |
| 18 | #include "core/hle/kernel/kernel.h" | 18 | #include "core/hle/kernel/kernel.h" |
| 19 | #include "core/hle/kernel/process.h" | 19 | #include "core/hle/kernel/process.h" |
| 20 | #include "core/hle/kernel/scheduler.h" | ||
| 20 | #include "core/hle/kernel/server_session.h" | 21 | #include "core/hle/kernel/server_session.h" |
| 21 | #include "core/hle/kernel/session.h" | 22 | #include "core/hle/kernel/session.h" |
| 22 | #include "core/hle/kernel/thread.h" | 23 | #include "core/hle/kernel/thread.h" |
| @@ -168,9 +169,12 @@ ResultCode ServerSession::CompleteSyncRequest() { | |||
| 168 | } | 169 | } |
| 169 | 170 | ||
| 170 | // Some service requests require the thread to block | 171 | // Some service requests require the thread to block |
| 171 | if (!context.IsThreadWaiting()) { | 172 | { |
| 172 | context.GetThread().ResumeFromWait(); | 173 | SchedulerLock lock(kernel); |
| 173 | context.GetThread().SetWaitSynchronizationResult(result); | 174 | if (!context.IsThreadWaiting()) { |
| 175 | context.GetThread().ResumeFromWait(); | ||
| 176 | context.GetThread().SetSynchronizationResults(nullptr, result); | ||
| 177 | } | ||
| 174 | } | 178 | } |
| 175 | 179 | ||
| 176 | request_queue.Pop(); | 180 | request_queue.Pop(); |
| @@ -180,8 +184,10 @@ ResultCode ServerSession::CompleteSyncRequest() { | |||
| 180 | 184 | ||
| 181 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, | 185 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, |
| 182 | Core::Memory::Memory& memory) { | 186 | Core::Memory::Memory& memory) { |
| 183 | Core::System::GetInstance().CoreTiming().ScheduleEvent(20000, request_event, {}); | 187 | ResultCode result = QueueSyncRequest(std::move(thread), memory); |
| 184 | return QueueSyncRequest(std::move(thread), memory); | 188 | const u64 delay = kernel.IsMulticore() ? 0U : 20000U; |
| 189 | Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {}); | ||
| 190 | return result; | ||
| 185 | } | 191 | } |
| 186 | 192 | ||
| 187 | } // namespace Kernel | 193 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4ae4529f5..5db19dcf3 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -10,14 +10,15 @@ | |||
| 10 | 10 | ||
| 11 | #include "common/alignment.h" | 11 | #include "common/alignment.h" |
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/fiber.h" | ||
| 13 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 14 | #include "common/microprofile.h" | 15 | #include "common/microprofile.h" |
| 15 | #include "common/string_util.h" | 16 | #include "common/string_util.h" |
| 16 | #include "core/arm/exclusive_monitor.h" | 17 | #include "core/arm/exclusive_monitor.h" |
| 17 | #include "core/core.h" | 18 | #include "core/core.h" |
| 18 | #include "core/core_manager.h" | ||
| 19 | #include "core/core_timing.h" | 19 | #include "core/core_timing.h" |
| 20 | #include "core/core_timing_util.h" | 20 | #include "core/core_timing_util.h" |
| 21 | #include "core/cpu_manager.h" | ||
| 21 | #include "core/hle/kernel/address_arbiter.h" | 22 | #include "core/hle/kernel/address_arbiter.h" |
| 22 | #include "core/hle/kernel/client_port.h" | 23 | #include "core/hle/kernel/client_port.h" |
| 23 | #include "core/hle/kernel/client_session.h" | 24 | #include "core/hle/kernel/client_session.h" |
| @@ -27,6 +28,7 @@ | |||
| 27 | #include "core/hle/kernel/memory/memory_block.h" | 28 | #include "core/hle/kernel/memory/memory_block.h" |
| 28 | #include "core/hle/kernel/memory/page_table.h" | 29 | #include "core/hle/kernel/memory/page_table.h" |
| 29 | #include "core/hle/kernel/mutex.h" | 30 | #include "core/hle/kernel/mutex.h" |
| 31 | #include "core/hle/kernel/physical_core.h" | ||
| 30 | #include "core/hle/kernel/process.h" | 32 | #include "core/hle/kernel/process.h" |
| 31 | #include "core/hle/kernel/readable_event.h" | 33 | #include "core/hle/kernel/readable_event.h" |
| 32 | #include "core/hle/kernel/resource_limit.h" | 34 | #include "core/hle/kernel/resource_limit.h" |
| @@ -37,6 +39,7 @@ | |||
| 37 | #include "core/hle/kernel/svc_wrap.h" | 39 | #include "core/hle/kernel/svc_wrap.h" |
| 38 | #include "core/hle/kernel/synchronization.h" | 40 | #include "core/hle/kernel/synchronization.h" |
| 39 | #include "core/hle/kernel/thread.h" | 41 | #include "core/hle/kernel/thread.h" |
| 42 | #include "core/hle/kernel/time_manager.h" | ||
| 40 | #include "core/hle/kernel/transfer_memory.h" | 43 | #include "core/hle/kernel/transfer_memory.h" |
| 41 | #include "core/hle/kernel/writable_event.h" | 44 | #include "core/hle/kernel/writable_event.h" |
| 42 | #include "core/hle/lock.h" | 45 | #include "core/hle/lock.h" |
| @@ -133,6 +136,7 @@ enum class ResourceLimitValueType { | |||
| 133 | 136 | ||
| 134 | ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, | 137 | ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, |
| 135 | u32 resource_type, ResourceLimitValueType value_type) { | 138 | u32 resource_type, ResourceLimitValueType value_type) { |
| 139 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 136 | const auto type = static_cast<ResourceType>(resource_type); | 140 | const auto type = static_cast<ResourceType>(resource_type); |
| 137 | if (!IsValidResourceType(type)) { | 141 | if (!IsValidResourceType(type)) { |
| 138 | LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); | 142 | LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); |
| @@ -160,6 +164,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_ | |||
| 160 | 164 | ||
| 161 | /// Set the process heap to a given Size. It can both extend and shrink the heap. | 165 | /// Set the process heap to a given Size. It can both extend and shrink the heap. |
| 162 | static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) { | 166 | static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) { |
| 167 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 163 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); | 168 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); |
| 164 | 169 | ||
| 165 | // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. | 170 | // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. |
| @@ -190,6 +195,7 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s | |||
| 190 | 195 | ||
| 191 | static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, | 196 | static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, |
| 192 | u32 attribute) { | 197 | u32 attribute) { |
| 198 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 193 | LOG_DEBUG(Kernel_SVC, | 199 | LOG_DEBUG(Kernel_SVC, |
| 194 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, | 200 | "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, |
| 195 | size, mask, attribute); | 201 | size, mask, attribute); |
| @@ -226,8 +232,15 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si | |||
| 226 | static_cast<Memory::MemoryAttribute>(attribute)); | 232 | static_cast<Memory::MemoryAttribute>(attribute)); |
| 227 | } | 233 | } |
| 228 | 234 | ||
| 235 | static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, | ||
| 236 | u32 attribute) { | ||
| 237 | return SetMemoryAttribute(system, static_cast<VAddr>(address), static_cast<std::size_t>(size), | ||
| 238 | mask, attribute); | ||
| 239 | } | ||
| 240 | |||
| 229 | /// Maps a memory range into a different range. | 241 | /// Maps a memory range into a different range. |
| 230 | static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { | 242 | static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { |
| 243 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 231 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 244 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 232 | src_addr, size); | 245 | src_addr, size); |
| 233 | 246 | ||
| @@ -241,8 +254,14 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr | |||
| 241 | return page_table.Map(dst_addr, src_addr, size); | 254 | return page_table.Map(dst_addr, src_addr, size); |
| 242 | } | 255 | } |
| 243 | 256 | ||
| 257 | static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { | ||
| 258 | return MapMemory(system, static_cast<VAddr>(dst_addr), static_cast<VAddr>(src_addr), | ||
| 259 | static_cast<std::size_t>(size)); | ||
| 260 | } | ||
| 261 | |||
| 244 | /// Unmaps a region that was previously mapped with svcMapMemory | 262 | /// Unmaps a region that was previously mapped with svcMapMemory |
| 245 | static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { | 263 | static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { |
| 264 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 246 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 265 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 247 | src_addr, size); | 266 | src_addr, size); |
| 248 | 267 | ||
| @@ -256,9 +275,15 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad | |||
| 256 | return page_table.Unmap(dst_addr, src_addr, size); | 275 | return page_table.Unmap(dst_addr, src_addr, size); |
| 257 | } | 276 | } |
| 258 | 277 | ||
| 278 | static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { | ||
| 279 | return UnmapMemory(system, static_cast<VAddr>(dst_addr), static_cast<VAddr>(src_addr), | ||
| 280 | static_cast<std::size_t>(size)); | ||
| 281 | } | ||
| 282 | |||
| 259 | /// Connect to an OS service given the port name, returns the handle to the port to out | 283 | /// Connect to an OS service given the port name, returns the handle to the port to out |
| 260 | static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, | 284 | static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, |
| 261 | VAddr port_name_address) { | 285 | VAddr port_name_address) { |
| 286 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 262 | auto& memory = system.Memory(); | 287 | auto& memory = system.Memory(); |
| 263 | 288 | ||
| 264 | if (!memory.IsValidVirtualAddress(port_name_address)) { | 289 | if (!memory.IsValidVirtualAddress(port_name_address)) { |
| @@ -317,11 +342,30 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { | |||
| 317 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); | 342 | LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); |
| 318 | 343 | ||
| 319 | auto thread = system.CurrentScheduler().GetCurrentThread(); | 344 | auto thread = system.CurrentScheduler().GetCurrentThread(); |
| 320 | thread->InvalidateWakeupCallback(); | 345 | { |
| 321 | thread->SetStatus(ThreadStatus::WaitIPC); | 346 | SchedulerLock lock(system.Kernel()); |
| 322 | system.PrepareReschedule(thread->GetProcessorID()); | 347 | thread->InvalidateHLECallback(); |
| 348 | thread->SetStatus(ThreadStatus::WaitIPC); | ||
| 349 | session->SendSyncRequest(SharedFrom(thread), system.Memory()); | ||
| 350 | } | ||
| 351 | |||
| 352 | if (thread->HasHLECallback()) { | ||
| 353 | Handle event_handle = thread->GetHLETimeEvent(); | ||
| 354 | if (event_handle != InvalidHandle) { | ||
| 355 | auto& time_manager = system.Kernel().TimeManager(); | ||
| 356 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 357 | } | ||
| 358 | |||
| 359 | { | ||
| 360 | SchedulerLock lock(system.Kernel()); | ||
| 361 | auto* sync_object = thread->GetHLESyncObject(); | ||
| 362 | sync_object->RemoveWaitingThread(SharedFrom(thread)); | ||
| 363 | } | ||
| 364 | |||
| 365 | thread->InvokeHLECallback(SharedFrom(thread)); | ||
| 366 | } | ||
| 323 | 367 | ||
| 324 | return session->SendSyncRequest(SharedFrom(thread), system.Memory()); | 368 | return thread->GetSignalingResult(); |
| 325 | } | 369 | } |
| 326 | 370 | ||
| 327 | static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { | 371 | static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { |
| @@ -383,6 +427,15 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han | |||
| 383 | return ERR_INVALID_HANDLE; | 427 | return ERR_INVALID_HANDLE; |
| 384 | } | 428 | } |
| 385 | 429 | ||
| 430 | static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* process_id_high, | ||
| 431 | Handle handle) { | ||
| 432 | u64 process_id{}; | ||
| 433 | const auto result = GetProcessId(system, &process_id, handle); | ||
| 434 | *process_id_low = static_cast<u32>(process_id); | ||
| 435 | *process_id_high = static_cast<u32>(process_id >> 32); | ||
| 436 | return result; | ||
| 437 | } | ||
| 438 | |||
| 386 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | 439 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 387 | static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, | 440 | static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, |
| 388 | u64 handle_count, s64 nano_seconds) { | 441 | u64 handle_count, s64 nano_seconds) { |
| @@ -447,10 +500,13 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand | |||
| 447 | } | 500 | } |
| 448 | 501 | ||
| 449 | thread->CancelWait(); | 502 | thread->CancelWait(); |
| 450 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 451 | return RESULT_SUCCESS; | 503 | return RESULT_SUCCESS; |
| 452 | } | 504 | } |
| 453 | 505 | ||
| 506 | static ResultCode CancelSynchronization32(Core::System& system, Handle thread_handle) { | ||
| 507 | return CancelSynchronization(system, thread_handle); | ||
| 508 | } | ||
| 509 | |||
| 454 | /// Attempts to locks a mutex, creating it if it does not already exist | 510 | /// Attempts to locks a mutex, creating it if it does not already exist |
| 455 | static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, | 511 | static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, |
| 456 | VAddr mutex_addr, Handle requesting_thread_handle) { | 512 | VAddr mutex_addr, Handle requesting_thread_handle) { |
| @@ -475,6 +531,12 @@ static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_hand | |||
| 475 | requesting_thread_handle); | 531 | requesting_thread_handle); |
| 476 | } | 532 | } |
| 477 | 533 | ||
| 534 | static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle, | ||
| 535 | u32 mutex_addr, Handle requesting_thread_handle) { | ||
| 536 | return ArbitrateLock(system, holding_thread_handle, static_cast<VAddr>(mutex_addr), | ||
| 537 | requesting_thread_handle); | ||
| 538 | } | ||
| 539 | |||
| 478 | /// Unlock a mutex | 540 | /// Unlock a mutex |
| 479 | static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { | 541 | static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { |
| 480 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); | 542 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); |
| @@ -494,6 +556,10 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { | |||
| 494 | return current_process->GetMutex().Release(mutex_addr); | 556 | return current_process->GetMutex().Release(mutex_addr); |
| 495 | } | 557 | } |
| 496 | 558 | ||
| 559 | static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) { | ||
| 560 | return ArbitrateUnlock(system, static_cast<VAddr>(mutex_addr)); | ||
| 561 | } | ||
| 562 | |||
| 497 | enum class BreakType : u32 { | 563 | enum class BreakType : u32 { |
| 498 | Panic = 0, | 564 | Panic = 0, |
| 499 | AssertionFailed = 1, | 565 | AssertionFailed = 1, |
| @@ -594,6 +660,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | |||
| 594 | info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); | 660 | info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); |
| 595 | 661 | ||
| 596 | if (!break_reason.signal_debugger) { | 662 | if (!break_reason.signal_debugger) { |
| 663 | SchedulerLock lock(system.Kernel()); | ||
| 597 | LOG_CRITICAL( | 664 | LOG_CRITICAL( |
| 598 | Debug_Emulated, | 665 | Debug_Emulated, |
| 599 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | 666 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", |
| @@ -605,14 +672,16 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | |||
| 605 | const auto thread_processor_id = current_thread->GetProcessorID(); | 672 | const auto thread_processor_id = current_thread->GetProcessorID(); |
| 606 | system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); | 673 | system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); |
| 607 | 674 | ||
| 608 | system.Kernel().CurrentProcess()->PrepareForTermination(); | ||
| 609 | |||
| 610 | // Kill the current thread | 675 | // Kill the current thread |
| 676 | system.Kernel().ExceptionalExit(); | ||
| 611 | current_thread->Stop(); | 677 | current_thread->Stop(); |
| 612 | system.PrepareReschedule(); | ||
| 613 | } | 678 | } |
| 614 | } | 679 | } |
| 615 | 680 | ||
| 681 | static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { | ||
| 682 | Break(system, reason, static_cast<u64>(info1), static_cast<u64>(info2)); | ||
| 683 | } | ||
| 684 | |||
| 616 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | 685 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit |
| 617 | static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { | 686 | static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { |
| 618 | if (len == 0) { | 687 | if (len == 0) { |
| @@ -627,6 +696,7 @@ static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr addre | |||
| 627 | /// Gets system/memory information for the current process | 696 | /// Gets system/memory information for the current process |
| 628 | static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle, | 697 | static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle, |
| 629 | u64 info_sub_id) { | 698 | u64 info_sub_id) { |
| 699 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 630 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, | 700 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, |
| 631 | info_sub_id, handle); | 701 | info_sub_id, handle); |
| 632 | 702 | ||
| @@ -863,9 +933,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha | |||
| 863 | if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { | 933 | if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { |
| 864 | const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); | 934 | const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); |
| 865 | 935 | ||
| 866 | out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks); | 936 | out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); |
| 867 | } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { | 937 | } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { |
| 868 | out_ticks = core_timing.GetTicks() - prev_ctx_ticks; | 938 | out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; |
| 869 | } | 939 | } |
| 870 | 940 | ||
| 871 | *result = out_ticks; | 941 | *result = out_ticks; |
| @@ -892,6 +962,7 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h | |||
| 892 | 962 | ||
| 893 | /// Maps memory at a desired address | 963 | /// Maps memory at a desired address |
| 894 | static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { | 964 | static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { |
| 965 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 895 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); | 966 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); |
| 896 | 967 | ||
| 897 | if (!Common::Is4KBAligned(addr)) { | 968 | if (!Common::Is4KBAligned(addr)) { |
| @@ -939,8 +1010,13 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) | |||
| 939 | return page_table.MapPhysicalMemory(addr, size); | 1010 | return page_table.MapPhysicalMemory(addr, size); |
| 940 | } | 1011 | } |
| 941 | 1012 | ||
| 1013 | static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { | ||
| 1014 | return MapPhysicalMemory(system, static_cast<VAddr>(addr), static_cast<std::size_t>(size)); | ||
| 1015 | } | ||
| 1016 | |||
| 942 | /// Unmaps memory previously mapped via MapPhysicalMemory | 1017 | /// Unmaps memory previously mapped via MapPhysicalMemory |
| 943 | static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { | 1018 | static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { |
| 1019 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 944 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); | 1020 | LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); |
| 945 | 1021 | ||
| 946 | if (!Common::Is4KBAligned(addr)) { | 1022 | if (!Common::Is4KBAligned(addr)) { |
| @@ -988,6 +1064,10 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size | |||
| 988 | return page_table.UnmapPhysicalMemory(addr, size); | 1064 | return page_table.UnmapPhysicalMemory(addr, size); |
| 989 | } | 1065 | } |
| 990 | 1066 | ||
| 1067 | static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { | ||
| 1068 | return UnmapPhysicalMemory(system, static_cast<VAddr>(addr), static_cast<std::size_t>(size)); | ||
| 1069 | } | ||
| 1070 | |||
| 991 | /// Sets the thread activity | 1071 | /// Sets the thread activity |
| 992 | static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { | 1072 | static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { |
| 993 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); | 1073 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); |
| @@ -1017,10 +1097,11 @@ static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 act | |||
| 1017 | return ERR_BUSY; | 1097 | return ERR_BUSY; |
| 1018 | } | 1098 | } |
| 1019 | 1099 | ||
| 1020 | thread->SetActivity(static_cast<ThreadActivity>(activity)); | 1100 | return thread->SetActivity(static_cast<ThreadActivity>(activity)); |
| 1101 | } | ||
| 1021 | 1102 | ||
| 1022 | system.PrepareReschedule(thread->GetProcessorID()); | 1103 | static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) { |
| 1023 | return RESULT_SUCCESS; | 1104 | return SetThreadActivity(system, handle, activity); |
| 1024 | } | 1105 | } |
| 1025 | 1106 | ||
| 1026 | /// Gets the thread context | 1107 | /// Gets the thread context |
| @@ -1064,6 +1145,10 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H | |||
| 1064 | return RESULT_SUCCESS; | 1145 | return RESULT_SUCCESS; |
| 1065 | } | 1146 | } |
| 1066 | 1147 | ||
| 1148 | static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) { | ||
| 1149 | return GetThreadContext(system, static_cast<VAddr>(thread_context), handle); | ||
| 1150 | } | ||
| 1151 | |||
| 1067 | /// Gets the priority for the specified thread | 1152 | /// Gets the priority for the specified thread |
| 1068 | static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) { | 1153 | static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) { |
| 1069 | LOG_TRACE(Kernel_SVC, "called"); | 1154 | LOG_TRACE(Kernel_SVC, "called"); |
| @@ -1071,6 +1156,7 @@ static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle | |||
| 1071 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 1156 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1072 | const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle); | 1157 | const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle); |
| 1073 | if (!thread) { | 1158 | if (!thread) { |
| 1159 | *priority = 0; | ||
| 1074 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); | 1160 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); |
| 1075 | return ERR_INVALID_HANDLE; | 1161 | return ERR_INVALID_HANDLE; |
| 1076 | } | 1162 | } |
| @@ -1105,18 +1191,26 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri | |||
| 1105 | 1191 | ||
| 1106 | thread->SetPriority(priority); | 1192 | thread->SetPriority(priority); |
| 1107 | 1193 | ||
| 1108 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1109 | return RESULT_SUCCESS; | 1194 | return RESULT_SUCCESS; |
| 1110 | } | 1195 | } |
| 1111 | 1196 | ||
| 1197 | static ResultCode SetThreadPriority32(Core::System& system, Handle handle, u32 priority) { | ||
| 1198 | return SetThreadPriority(system, handle, priority); | ||
| 1199 | } | ||
| 1200 | |||
| 1112 | /// Get which CPU core is executing the current thread | 1201 | /// Get which CPU core is executing the current thread |
| 1113 | static u32 GetCurrentProcessorNumber(Core::System& system) { | 1202 | static u32 GetCurrentProcessorNumber(Core::System& system) { |
| 1114 | LOG_TRACE(Kernel_SVC, "called"); | 1203 | LOG_TRACE(Kernel_SVC, "called"); |
| 1115 | return system.CurrentScheduler().GetCurrentThread()->GetProcessorID(); | 1204 | return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex()); |
| 1205 | } | ||
| 1206 | |||
| 1207 | static u32 GetCurrentProcessorNumber32(Core::System& system) { | ||
| 1208 | return GetCurrentProcessorNumber(system); | ||
| 1116 | } | 1209 | } |
| 1117 | 1210 | ||
| 1118 | static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, | 1211 | static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, |
| 1119 | u64 size, u32 permissions) { | 1212 | u64 size, u32 permissions) { |
| 1213 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 1120 | LOG_TRACE(Kernel_SVC, | 1214 | LOG_TRACE(Kernel_SVC, |
| 1121 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", | 1215 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", |
| 1122 | shared_memory_handle, addr, size, permissions); | 1216 | shared_memory_handle, addr, size, permissions); |
| @@ -1187,9 +1281,16 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han | |||
| 1187 | return shared_memory->Map(*current_process, addr, size, permission_type); | 1281 | return shared_memory->Map(*current_process, addr, size, permission_type); |
| 1188 | } | 1282 | } |
| 1189 | 1283 | ||
| 1284 | static ResultCode MapSharedMemory32(Core::System& system, Handle shared_memory_handle, u32 addr, | ||
| 1285 | u32 size, u32 permissions) { | ||
| 1286 | return MapSharedMemory(system, shared_memory_handle, static_cast<VAddr>(addr), | ||
| 1287 | static_cast<std::size_t>(size), permissions); | ||
| 1288 | } | ||
| 1289 | |||
| 1190 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, | 1290 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, |
| 1191 | VAddr page_info_address, Handle process_handle, | 1291 | VAddr page_info_address, Handle process_handle, |
| 1192 | VAddr address) { | 1292 | VAddr address) { |
| 1293 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 1193 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); | 1294 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); |
| 1194 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 1295 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1195 | std::shared_ptr<Process> process = handle_table.Get<Process>(process_handle); | 1296 | std::shared_ptr<Process> process = handle_table.Get<Process>(process_handle); |
| @@ -1372,6 +1473,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha | |||
| 1372 | /// Exits the current process | 1473 | /// Exits the current process |
| 1373 | static void ExitProcess(Core::System& system) { | 1474 | static void ExitProcess(Core::System& system) { |
| 1374 | auto* current_process = system.Kernel().CurrentProcess(); | 1475 | auto* current_process = system.Kernel().CurrentProcess(); |
| 1476 | UNIMPLEMENTED(); | ||
| 1375 | 1477 | ||
| 1376 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); | 1478 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); |
| 1377 | ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, | 1479 | ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, |
| @@ -1381,8 +1483,10 @@ static void ExitProcess(Core::System& system) { | |||
| 1381 | 1483 | ||
| 1382 | // Kill the current thread | 1484 | // Kill the current thread |
| 1383 | system.CurrentScheduler().GetCurrentThread()->Stop(); | 1485 | system.CurrentScheduler().GetCurrentThread()->Stop(); |
| 1486 | } | ||
| 1384 | 1487 | ||
| 1385 | system.PrepareReschedule(); | 1488 | static void ExitProcess32(Core::System& system) { |
| 1489 | ExitProcess(system); | ||
| 1386 | } | 1490 | } |
| 1387 | 1491 | ||
| 1388 | /// Creates a new thread | 1492 | /// Creates a new thread |
| @@ -1428,9 +1532,10 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e | |||
| 1428 | 1532 | ||
| 1429 | ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); | 1533 | ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); |
| 1430 | 1534 | ||
| 1535 | ThreadType type = THREADTYPE_USER; | ||
| 1431 | CASCADE_RESULT(std::shared_ptr<Thread> thread, | 1536 | CASCADE_RESULT(std::shared_ptr<Thread> thread, |
| 1432 | Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top, | 1537 | Thread::Create(system, type, "", entry_point, priority, arg, processor_id, |
| 1433 | *current_process)); | 1538 | stack_top, current_process)); |
| 1434 | 1539 | ||
| 1435 | const auto new_thread_handle = current_process->GetHandleTable().Create(thread); | 1540 | const auto new_thread_handle = current_process->GetHandleTable().Create(thread); |
| 1436 | if (new_thread_handle.Failed()) { | 1541 | if (new_thread_handle.Failed()) { |
| @@ -1444,11 +1549,15 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e | |||
| 1444 | thread->SetName( | 1549 | thread->SetName( |
| 1445 | fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle)); | 1550 | fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle)); |
| 1446 | 1551 | ||
| 1447 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1448 | |||
| 1449 | return RESULT_SUCCESS; | 1552 | return RESULT_SUCCESS; |
| 1450 | } | 1553 | } |
| 1451 | 1554 | ||
| 1555 | static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 priority, | ||
| 1556 | u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { | ||
| 1557 | return CreateThread(system, out_handle, static_cast<VAddr>(entry_point), static_cast<u64>(arg), | ||
| 1558 | static_cast<VAddr>(stack_top), priority, processor_id); | ||
| 1559 | } | ||
| 1560 | |||
| 1452 | /// Starts the thread for the provided handle | 1561 | /// Starts the thread for the provided handle |
| 1453 | static ResultCode StartThread(Core::System& system, Handle thread_handle) { | 1562 | static ResultCode StartThread(Core::System& system, Handle thread_handle) { |
| 1454 | LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | 1563 | LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); |
| @@ -1463,13 +1572,11 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) { | |||
| 1463 | 1572 | ||
| 1464 | ASSERT(thread->GetStatus() == ThreadStatus::Dormant); | 1573 | ASSERT(thread->GetStatus() == ThreadStatus::Dormant); |
| 1465 | 1574 | ||
| 1466 | thread->ResumeFromWait(); | 1575 | return thread->Start(); |
| 1467 | 1576 | } | |
| 1468 | if (thread->GetStatus() == ThreadStatus::Ready) { | ||
| 1469 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1470 | } | ||
| 1471 | 1577 | ||
| 1472 | return RESULT_SUCCESS; | 1578 | static ResultCode StartThread32(Core::System& system, Handle thread_handle) { |
| 1579 | return StartThread(system, thread_handle); | ||
| 1473 | } | 1580 | } |
| 1474 | 1581 | ||
| 1475 | /// Called when a thread exits | 1582 | /// Called when a thread exits |
| @@ -1477,9 +1584,12 @@ static void ExitThread(Core::System& system) { | |||
| 1477 | LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); | 1584 | LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); |
| 1478 | 1585 | ||
| 1479 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); | 1586 | auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); |
| 1480 | current_thread->Stop(); | ||
| 1481 | system.GlobalScheduler().RemoveThread(SharedFrom(current_thread)); | 1587 | system.GlobalScheduler().RemoveThread(SharedFrom(current_thread)); |
| 1482 | system.PrepareReschedule(); | 1588 | current_thread->Stop(); |
| 1589 | } | ||
| 1590 | |||
| 1591 | static void ExitThread32(Core::System& system) { | ||
| 1592 | ExitThread(system); | ||
| 1483 | } | 1593 | } |
| 1484 | 1594 | ||
| 1485 | /// Sleep the current thread | 1595 | /// Sleep the current thread |
| @@ -1498,15 +1608,21 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { | |||
| 1498 | 1608 | ||
| 1499 | if (nanoseconds <= 0) { | 1609 | if (nanoseconds <= 0) { |
| 1500 | switch (static_cast<SleepType>(nanoseconds)) { | 1610 | switch (static_cast<SleepType>(nanoseconds)) { |
| 1501 | case SleepType::YieldWithoutLoadBalancing: | 1611 | case SleepType::YieldWithoutLoadBalancing: { |
| 1502 | is_redundant = current_thread->YieldSimple(); | 1612 | auto pair = current_thread->YieldSimple(); |
| 1613 | is_redundant = pair.second; | ||
| 1503 | break; | 1614 | break; |
| 1504 | case SleepType::YieldWithLoadBalancing: | 1615 | } |
| 1505 | is_redundant = current_thread->YieldAndBalanceLoad(); | 1616 | case SleepType::YieldWithLoadBalancing: { |
| 1617 | auto pair = current_thread->YieldAndBalanceLoad(); | ||
| 1618 | is_redundant = pair.second; | ||
| 1506 | break; | 1619 | break; |
| 1507 | case SleepType::YieldAndWaitForLoadBalancing: | 1620 | } |
| 1508 | is_redundant = current_thread->YieldAndWaitForLoadBalancing(); | 1621 | case SleepType::YieldAndWaitForLoadBalancing: { |
| 1622 | auto pair = current_thread->YieldAndWaitForLoadBalancing(); | ||
| 1623 | is_redundant = pair.second; | ||
| 1509 | break; | 1624 | break; |
| 1625 | } | ||
| 1510 | default: | 1626 | default: |
| 1511 | UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); | 1627 | UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); |
| 1512 | } | 1628 | } |
| @@ -1514,13 +1630,18 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { | |||
| 1514 | current_thread->Sleep(nanoseconds); | 1630 | current_thread->Sleep(nanoseconds); |
| 1515 | } | 1631 | } |
| 1516 | 1632 | ||
| 1517 | if (is_redundant) { | 1633 | if (is_redundant && !system.Kernel().IsMulticore()) { |
| 1518 | // If it's redundant, the core is pretty much idle. Some games keep idling | 1634 | system.Kernel().ExitSVCProfile(); |
| 1519 | // a core while it's doing nothing, we advance timing to avoid costly continuous | 1635 | system.CoreTiming().AddTicks(1000U); |
| 1520 | // calls. | 1636 | system.GetCpuManager().PreemptSingleCore(); |
| 1521 | system.CoreTiming().AddTicks(2000); | 1637 | system.Kernel().EnterSVCProfile(); |
| 1522 | } | 1638 | } |
| 1523 | system.PrepareReschedule(current_thread->GetProcessorID()); | 1639 | } |
| 1640 | |||
| 1641 | static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) { | ||
| 1642 | const s64 nanoseconds = static_cast<s64>(static_cast<u64>(nanoseconds_low) | | ||
| 1643 | (static_cast<u64>(nanoseconds_high) << 32)); | ||
| 1644 | SleepThread(system, nanoseconds); | ||
| 1524 | } | 1645 | } |
| 1525 | 1646 | ||
| 1526 | /// Wait process wide key atomic | 1647 | /// Wait process wide key atomic |
| @@ -1547,31 +1668,69 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add | |||
| 1547 | } | 1668 | } |
| 1548 | 1669 | ||
| 1549 | ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); | 1670 | ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); |
| 1550 | 1671 | auto& kernel = system.Kernel(); | |
| 1672 | Handle event_handle; | ||
| 1673 | Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); | ||
| 1551 | auto* const current_process = system.Kernel().CurrentProcess(); | 1674 | auto* const current_process = system.Kernel().CurrentProcess(); |
| 1552 | const auto& handle_table = current_process->GetHandleTable(); | 1675 | { |
| 1553 | std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 1676 | SchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds); |
| 1554 | ASSERT(thread); | 1677 | const auto& handle_table = current_process->GetHandleTable(); |
| 1678 | std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); | ||
| 1679 | ASSERT(thread); | ||
| 1680 | |||
| 1681 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 1682 | |||
| 1683 | if (thread->IsPendingTermination()) { | ||
| 1684 | lock.CancelSleep(); | ||
| 1685 | return ERR_THREAD_TERMINATING; | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | const auto release_result = current_process->GetMutex().Release(mutex_addr); | ||
| 1689 | if (release_result.IsError()) { | ||
| 1690 | lock.CancelSleep(); | ||
| 1691 | return release_result; | ||
| 1692 | } | ||
| 1693 | |||
| 1694 | if (nano_seconds == 0) { | ||
| 1695 | lock.CancelSleep(); | ||
| 1696 | return RESULT_TIMEOUT; | ||
| 1697 | } | ||
| 1555 | 1698 | ||
| 1556 | const auto release_result = current_process->GetMutex().Release(mutex_addr); | 1699 | current_thread->SetCondVarWaitAddress(condition_variable_addr); |
| 1557 | if (release_result.IsError()) { | 1700 | current_thread->SetMutexWaitAddress(mutex_addr); |
| 1558 | return release_result; | 1701 | current_thread->SetWaitHandle(thread_handle); |
| 1702 | current_thread->SetStatus(ThreadStatus::WaitCondVar); | ||
| 1703 | current_process->InsertConditionVariableThread(SharedFrom(current_thread)); | ||
| 1559 | } | 1704 | } |
| 1560 | 1705 | ||
| 1561 | Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); | 1706 | if (event_handle != InvalidHandle) { |
| 1562 | current_thread->SetCondVarWaitAddress(condition_variable_addr); | 1707 | auto& time_manager = kernel.TimeManager(); |
| 1563 | current_thread->SetMutexWaitAddress(mutex_addr); | 1708 | time_manager.UnscheduleTimeEvent(event_handle); |
| 1564 | current_thread->SetWaitHandle(thread_handle); | 1709 | } |
| 1565 | current_thread->SetStatus(ThreadStatus::WaitCondVar); | 1710 | |
| 1566 | current_thread->InvalidateWakeupCallback(); | 1711 | { |
| 1567 | current_process->InsertConditionVariableThread(SharedFrom(current_thread)); | 1712 | SchedulerLock lock(kernel); |
| 1568 | 1713 | ||
| 1569 | current_thread->WakeAfterDelay(nano_seconds); | 1714 | auto* owner = current_thread->GetLockOwner(); |
| 1715 | if (owner != nullptr) { | ||
| 1716 | owner->RemoveMutexWaiter(SharedFrom(current_thread)); | ||
| 1717 | } | ||
| 1570 | 1718 | ||
| 1719 | current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); | ||
| 1720 | } | ||
| 1571 | // Note: Deliberately don't attempt to inherit the lock owner's priority. | 1721 | // Note: Deliberately don't attempt to inherit the lock owner's priority. |
| 1572 | 1722 | ||
| 1573 | system.PrepareReschedule(current_thread->GetProcessorID()); | 1723 | return current_thread->GetSignalingResult(); |
| 1574 | return RESULT_SUCCESS; | 1724 | } |
| 1725 | |||
| 1726 | static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr, | ||
| 1727 | u32 condition_variable_addr, Handle thread_handle, | ||
| 1728 | u32 nanoseconds_low, u32 nanoseconds_high) { | ||
| 1729 | const s64 nanoseconds = | ||
| 1730 | static_cast<s64>(nanoseconds_low | (static_cast<u64>(nanoseconds_high) << 32)); | ||
| 1731 | return WaitProcessWideKeyAtomic(system, static_cast<VAddr>(mutex_addr), | ||
| 1732 | static_cast<VAddr>(condition_variable_addr), thread_handle, | ||
| 1733 | nanoseconds); | ||
| 1575 | } | 1734 | } |
| 1576 | 1735 | ||
| 1577 | /// Signal process wide key | 1736 | /// Signal process wide key |
| @@ -1582,7 +1741,9 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1582 | ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); | 1741 | ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); |
| 1583 | 1742 | ||
| 1584 | // Retrieve a list of all threads that are waiting for this condition variable. | 1743 | // Retrieve a list of all threads that are waiting for this condition variable. |
| 1585 | auto* const current_process = system.Kernel().CurrentProcess(); | 1744 | auto& kernel = system.Kernel(); |
| 1745 | SchedulerLock lock(kernel); | ||
| 1746 | auto* const current_process = kernel.CurrentProcess(); | ||
| 1586 | std::vector<std::shared_ptr<Thread>> waiting_threads = | 1747 | std::vector<std::shared_ptr<Thread>> waiting_threads = |
| 1587 | current_process->GetConditionVariableThreads(condition_variable_addr); | 1748 | current_process->GetConditionVariableThreads(condition_variable_addr); |
| 1588 | 1749 | ||
| @@ -1591,7 +1752,7 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1591 | std::size_t last = waiting_threads.size(); | 1752 | std::size_t last = waiting_threads.size(); |
| 1592 | if (target > 0) | 1753 | if (target > 0) |
| 1593 | last = std::min(waiting_threads.size(), static_cast<std::size_t>(target)); | 1754 | last = std::min(waiting_threads.size(), static_cast<std::size_t>(target)); |
| 1594 | 1755 | auto& time_manager = kernel.TimeManager(); | |
| 1595 | for (std::size_t index = 0; index < last; ++index) { | 1756 | for (std::size_t index = 0; index < last; ++index) { |
| 1596 | auto& thread = waiting_threads[index]; | 1757 | auto& thread = waiting_threads[index]; |
| 1597 | 1758 | ||
| @@ -1599,7 +1760,6 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1599 | 1760 | ||
| 1600 | // liberate Cond Var Thread. | 1761 | // liberate Cond Var Thread. |
| 1601 | current_process->RemoveConditionVariableThread(thread); | 1762 | current_process->RemoveConditionVariableThread(thread); |
| 1602 | thread->SetCondVarWaitAddress(0); | ||
| 1603 | 1763 | ||
| 1604 | const std::size_t current_core = system.CurrentCoreIndex(); | 1764 | const std::size_t current_core = system.CurrentCoreIndex(); |
| 1605 | auto& monitor = system.Monitor(); | 1765 | auto& monitor = system.Monitor(); |
| @@ -1610,10 +1770,8 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1610 | u32 update_val = 0; | 1770 | u32 update_val = 0; |
| 1611 | const VAddr mutex_address = thread->GetMutexWaitAddress(); | 1771 | const VAddr mutex_address = thread->GetMutexWaitAddress(); |
| 1612 | do { | 1772 | do { |
| 1613 | monitor.SetExclusive(current_core, mutex_address); | ||
| 1614 | |||
| 1615 | // If the mutex is not yet acquired, acquire it. | 1773 | // If the mutex is not yet acquired, acquire it. |
| 1616 | mutex_val = memory.Read32(mutex_address); | 1774 | mutex_val = monitor.ExclusiveRead32(current_core, mutex_address); |
| 1617 | 1775 | ||
| 1618 | if (mutex_val != 0) { | 1776 | if (mutex_val != 0) { |
| 1619 | update_val = mutex_val | Mutex::MutexHasWaitersFlag; | 1777 | update_val = mutex_val | Mutex::MutexHasWaitersFlag; |
| @@ -1621,33 +1779,28 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1621 | update_val = thread->GetWaitHandle(); | 1779 | update_val = thread->GetWaitHandle(); |
| 1622 | } | 1780 | } |
| 1623 | } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val)); | 1781 | } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val)); |
| 1782 | monitor.ClearExclusive(); | ||
| 1624 | if (mutex_val == 0) { | 1783 | if (mutex_val == 0) { |
| 1625 | // We were able to acquire the mutex, resume this thread. | 1784 | // We were able to acquire the mutex, resume this thread. |
| 1626 | ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); | ||
| 1627 | thread->ResumeFromWait(); | ||
| 1628 | |||
| 1629 | auto* const lock_owner = thread->GetLockOwner(); | 1785 | auto* const lock_owner = thread->GetLockOwner(); |
| 1630 | if (lock_owner != nullptr) { | 1786 | if (lock_owner != nullptr) { |
| 1631 | lock_owner->RemoveMutexWaiter(thread); | 1787 | lock_owner->RemoveMutexWaiter(thread); |
| 1632 | } | 1788 | } |
| 1633 | 1789 | ||
| 1634 | thread->SetLockOwner(nullptr); | 1790 | thread->SetLockOwner(nullptr); |
| 1635 | thread->SetMutexWaitAddress(0); | 1791 | thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); |
| 1636 | thread->SetWaitHandle(0); | 1792 | thread->ResumeFromWait(); |
| 1637 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 1638 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1639 | } else { | 1793 | } else { |
| 1640 | // The mutex is already owned by some other thread, make this thread wait on it. | 1794 | // The mutex is already owned by some other thread, make this thread wait on it. |
| 1641 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | 1795 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); |
| 1642 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 1796 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1643 | auto owner = handle_table.Get<Thread>(owner_handle); | 1797 | auto owner = handle_table.Get<Thread>(owner_handle); |
| 1644 | ASSERT(owner); | 1798 | ASSERT(owner); |
| 1645 | ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); | 1799 | if (thread->GetStatus() == ThreadStatus::WaitCondVar) { |
| 1646 | thread->InvalidateWakeupCallback(); | 1800 | thread->SetStatus(ThreadStatus::WaitMutex); |
| 1647 | thread->SetStatus(ThreadStatus::WaitMutex); | 1801 | } |
| 1648 | 1802 | ||
| 1649 | owner->AddMutexWaiter(thread); | 1803 | owner->AddMutexWaiter(thread); |
| 1650 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1651 | } | 1804 | } |
| 1652 | } | 1805 | } |
| 1653 | } | 1806 | } |
| @@ -1678,12 +1831,15 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, | |||
| 1678 | auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); | 1831 | auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); |
| 1679 | const ResultCode result = | 1832 | const ResultCode result = |
| 1680 | address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); | 1833 | address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); |
| 1681 | if (result == RESULT_SUCCESS) { | ||
| 1682 | system.PrepareReschedule(); | ||
| 1683 | } | ||
| 1684 | return result; | 1834 | return result; |
| 1685 | } | 1835 | } |
| 1686 | 1836 | ||
| 1837 | static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value, | ||
| 1838 | u32 timeout_low, u32 timeout_high) { | ||
| 1839 | s64 timeout = static_cast<s64>(timeout_low | (static_cast<u64>(timeout_high) << 32)); | ||
| 1840 | return WaitForAddress(system, static_cast<VAddr>(address), type, value, timeout); | ||
| 1841 | } | ||
| 1842 | |||
| 1687 | // Signals to an address (via Address Arbiter) | 1843 | // Signals to an address (via Address Arbiter) |
| 1688 | static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, | 1844 | static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, |
| 1689 | s32 num_to_wake) { | 1845 | s32 num_to_wake) { |
| @@ -1707,6 +1863,11 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, | |||
| 1707 | return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); | 1863 | return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); |
| 1708 | } | 1864 | } |
| 1709 | 1865 | ||
| 1866 | static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value, | ||
| 1867 | s32 num_to_wake) { | ||
| 1868 | return SignalToAddress(system, static_cast<VAddr>(address), type, value, num_to_wake); | ||
| 1869 | } | ||
| 1870 | |||
| 1710 | static void KernelDebug([[maybe_unused]] Core::System& system, | 1871 | static void KernelDebug([[maybe_unused]] Core::System& system, |
| 1711 | [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1, | 1872 | [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1, |
| 1712 | [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) { | 1873 | [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) { |
| @@ -1725,14 +1886,21 @@ static u64 GetSystemTick(Core::System& system) { | |||
| 1725 | auto& core_timing = system.CoreTiming(); | 1886 | auto& core_timing = system.CoreTiming(); |
| 1726 | 1887 | ||
| 1727 | // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) | 1888 | // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) |
| 1728 | const u64 result{Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks())}; | 1889 | const u64 result{system.CoreTiming().GetClockTicks()}; |
| 1729 | 1890 | ||
| 1730 | // Advance time to defeat dumb games that busy-wait for the frame to end. | 1891 | if (!system.Kernel().IsMulticore()) { |
| 1731 | core_timing.AddTicks(400); | 1892 | core_timing.AddTicks(400U); |
| 1893 | } | ||
| 1732 | 1894 | ||
| 1733 | return result; | 1895 | return result; |
| 1734 | } | 1896 | } |
| 1735 | 1897 | ||
| 1898 | static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) { | ||
| 1899 | u64 time = GetSystemTick(system); | ||
| 1900 | *time_low = static_cast<u32>(time); | ||
| 1901 | *time_high = static_cast<u32>(time >> 32); | ||
| 1902 | } | ||
| 1903 | |||
| 1736 | /// Close a handle | 1904 | /// Close a handle |
| 1737 | static ResultCode CloseHandle(Core::System& system, Handle handle) { | 1905 | static ResultCode CloseHandle(Core::System& system, Handle handle) { |
| 1738 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); | 1906 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); |
| @@ -1765,9 +1933,14 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) { | |||
| 1765 | return ERR_INVALID_HANDLE; | 1933 | return ERR_INVALID_HANDLE; |
| 1766 | } | 1934 | } |
| 1767 | 1935 | ||
| 1936 | static ResultCode ResetSignal32(Core::System& system, Handle handle) { | ||
| 1937 | return ResetSignal(system, handle); | ||
| 1938 | } | ||
| 1939 | |||
| 1768 | /// Creates a TransferMemory object | 1940 | /// Creates a TransferMemory object |
| 1769 | static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size, | 1941 | static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size, |
| 1770 | u32 permissions) { | 1942 | u32 permissions) { |
| 1943 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 1771 | LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, | 1944 | LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, |
| 1772 | permissions); | 1945 | permissions); |
| 1773 | 1946 | ||
| @@ -1812,6 +1985,12 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd | |||
| 1812 | return RESULT_SUCCESS; | 1985 | return RESULT_SUCCESS; |
| 1813 | } | 1986 | } |
| 1814 | 1987 | ||
| 1988 | static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u32 addr, u32 size, | ||
| 1989 | u32 permissions) { | ||
| 1990 | return CreateTransferMemory(system, handle, static_cast<VAddr>(addr), | ||
| 1991 | static_cast<std::size_t>(size), permissions); | ||
| 1992 | } | ||
| 1993 | |||
| 1815 | static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, | 1994 | static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, |
| 1816 | u64* mask) { | 1995 | u64* mask) { |
| 1817 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); | 1996 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); |
| @@ -1821,6 +2000,8 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, | |||
| 1821 | if (!thread) { | 2000 | if (!thread) { |
| 1822 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | 2001 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| 1823 | thread_handle); | 2002 | thread_handle); |
| 2003 | *core = 0; | ||
| 2004 | *mask = 0; | ||
| 1824 | return ERR_INVALID_HANDLE; | 2005 | return ERR_INVALID_HANDLE; |
| 1825 | } | 2006 | } |
| 1826 | 2007 | ||
| @@ -1830,6 +2011,15 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, | |||
| 1830 | return RESULT_SUCCESS; | 2011 | return RESULT_SUCCESS; |
| 1831 | } | 2012 | } |
| 1832 | 2013 | ||
| 2014 | static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, u32* core, | ||
| 2015 | u32* mask_low, u32* mask_high) { | ||
| 2016 | u64 mask{}; | ||
| 2017 | const auto result = GetThreadCoreMask(system, thread_handle, core, &mask); | ||
| 2018 | *mask_high = static_cast<u32>(mask >> 32); | ||
| 2019 | *mask_low = static_cast<u32>(mask); | ||
| 2020 | return result; | ||
| 2021 | } | ||
| 2022 | |||
| 1833 | static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, | 2023 | static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, |
| 1834 | u64 affinity_mask) { | 2024 | u64 affinity_mask) { |
| 1835 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}", | 2025 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}", |
| @@ -1861,7 +2051,7 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, | |||
| 1861 | return ERR_INVALID_COMBINATION; | 2051 | return ERR_INVALID_COMBINATION; |
| 1862 | } | 2052 | } |
| 1863 | 2053 | ||
| 1864 | if (core < Core::NUM_CPU_CORES) { | 2054 | if (core < Core::Hardware::NUM_CPU_CORES) { |
| 1865 | if ((affinity_mask & (1ULL << core)) == 0) { | 2055 | if ((affinity_mask & (1ULL << core)) == 0) { |
| 1866 | LOG_ERROR(Kernel_SVC, | 2056 | LOG_ERROR(Kernel_SVC, |
| 1867 | "Core is not enabled for the current mask, core={}, mask={:016X}", core, | 2057 | "Core is not enabled for the current mask, core={}, mask={:016X}", core, |
| @@ -1883,11 +2073,14 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, | |||
| 1883 | return ERR_INVALID_HANDLE; | 2073 | return ERR_INVALID_HANDLE; |
| 1884 | } | 2074 | } |
| 1885 | 2075 | ||
| 1886 | system.PrepareReschedule(thread->GetProcessorID()); | 2076 | return thread->SetCoreAndAffinityMask(core, affinity_mask); |
| 1887 | thread->ChangeCore(core, affinity_mask); | 2077 | } |
| 1888 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 1889 | 2078 | ||
| 1890 | return RESULT_SUCCESS; | 2079 | static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core, |
| 2080 | u32 affinity_mask_low, u32 affinity_mask_high) { | ||
| 2081 | const u64 affinity_mask = | ||
| 2082 | static_cast<u64>(affinity_mask_low) | (static_cast<u64>(affinity_mask_high) << 32); | ||
| 2083 | return SetThreadCoreMask(system, thread_handle, core, affinity_mask); | ||
| 1891 | } | 2084 | } |
| 1892 | 2085 | ||
| 1893 | static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { | 2086 | static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { |
| @@ -1918,6 +2111,10 @@ static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle | |||
| 1918 | return RESULT_SUCCESS; | 2111 | return RESULT_SUCCESS; |
| 1919 | } | 2112 | } |
| 1920 | 2113 | ||
| 2114 | static ResultCode CreateEvent32(Core::System& system, Handle* write_handle, Handle* read_handle) { | ||
| 2115 | return CreateEvent(system, write_handle, read_handle); | ||
| 2116 | } | ||
| 2117 | |||
| 1921 | static ResultCode ClearEvent(Core::System& system, Handle handle) { | 2118 | static ResultCode ClearEvent(Core::System& system, Handle handle) { |
| 1922 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); | 2119 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); |
| 1923 | 2120 | ||
| @@ -1939,6 +2136,10 @@ static ResultCode ClearEvent(Core::System& system, Handle handle) { | |||
| 1939 | return ERR_INVALID_HANDLE; | 2136 | return ERR_INVALID_HANDLE; |
| 1940 | } | 2137 | } |
| 1941 | 2138 | ||
| 2139 | static ResultCode ClearEvent32(Core::System& system, Handle handle) { | ||
| 2140 | return ClearEvent(system, handle); | ||
| 2141 | } | ||
| 2142 | |||
| 1942 | static ResultCode SignalEvent(Core::System& system, Handle handle) { | 2143 | static ResultCode SignalEvent(Core::System& system, Handle handle) { |
| 1943 | LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); | 2144 | LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); |
| 1944 | 2145 | ||
| @@ -1951,10 +2152,13 @@ static ResultCode SignalEvent(Core::System& system, Handle handle) { | |||
| 1951 | } | 2152 | } |
| 1952 | 2153 | ||
| 1953 | writable_event->Signal(); | 2154 | writable_event->Signal(); |
| 1954 | system.PrepareReschedule(); | ||
| 1955 | return RESULT_SUCCESS; | 2155 | return RESULT_SUCCESS; |
| 1956 | } | 2156 | } |
| 1957 | 2157 | ||
| 2158 | static ResultCode SignalEvent32(Core::System& system, Handle handle) { | ||
| 2159 | return SignalEvent(system, handle); | ||
| 2160 | } | ||
| 2161 | |||
| 1958 | static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { | 2162 | static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { |
| 1959 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); | 2163 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); |
| 1960 | 2164 | ||
| @@ -1982,6 +2186,7 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_ | |||
| 1982 | } | 2186 | } |
| 1983 | 2187 | ||
| 1984 | static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) { | 2188 | static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) { |
| 2189 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 1985 | LOG_DEBUG(Kernel_SVC, "called"); | 2190 | LOG_DEBUG(Kernel_SVC, "called"); |
| 1986 | 2191 | ||
| 1987 | auto& kernel = system.Kernel(); | 2192 | auto& kernel = system.Kernel(); |
| @@ -2139,6 +2344,15 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd | |||
| 2139 | return RESULT_SUCCESS; | 2344 | return RESULT_SUCCESS; |
| 2140 | } | 2345 | } |
| 2141 | 2346 | ||
| 2347 | static ResultCode FlushProcessDataCache32(Core::System& system, Handle handle, u32 address, | ||
| 2348 | u32 size) { | ||
| 2349 | // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a nope | ||
| 2350 | // as all emulation is done in the same cache level in host architecture, thus data cache | ||
| 2351 | // does not need flushing. | ||
| 2352 | LOG_DEBUG(Kernel_SVC, "called"); | ||
| 2353 | return RESULT_SUCCESS; | ||
| 2354 | } | ||
| 2355 | |||
| 2142 | namespace { | 2356 | namespace { |
| 2143 | struct FunctionDef { | 2357 | struct FunctionDef { |
| 2144 | using Func = void(Core::System&); | 2358 | using Func = void(Core::System&); |
| @@ -2153,57 +2367,57 @@ static const FunctionDef SVC_Table_32[] = { | |||
| 2153 | {0x00, nullptr, "Unknown"}, | 2367 | {0x00, nullptr, "Unknown"}, |
| 2154 | {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"}, | 2368 | {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"}, |
| 2155 | {0x02, nullptr, "Unknown"}, | 2369 | {0x02, nullptr, "Unknown"}, |
| 2156 | {0x03, nullptr, "SetMemoryAttribute32"}, | 2370 | {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"}, |
| 2157 | {0x04, nullptr, "MapMemory32"}, | 2371 | {0x04, SvcWrap32<MapMemory32>, "MapMemory32"}, |
| 2158 | {0x05, nullptr, "UnmapMemory32"}, | 2372 | {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"}, |
| 2159 | {0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"}, | 2373 | {0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"}, |
| 2160 | {0x07, nullptr, "ExitProcess32"}, | 2374 | {0x07, SvcWrap32<ExitProcess32>, "ExitProcess32"}, |
| 2161 | {0x08, nullptr, "CreateThread32"}, | 2375 | {0x08, SvcWrap32<CreateThread32>, "CreateThread32"}, |
| 2162 | {0x09, nullptr, "StartThread32"}, | 2376 | {0x09, SvcWrap32<StartThread32>, "StartThread32"}, |
| 2163 | {0x0a, nullptr, "ExitThread32"}, | 2377 | {0x0a, SvcWrap32<ExitThread32>, "ExitThread32"}, |
| 2164 | {0x0b, nullptr, "SleepThread32"}, | 2378 | {0x0b, SvcWrap32<SleepThread32>, "SleepThread32"}, |
| 2165 | {0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"}, | 2379 | {0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"}, |
| 2166 | {0x0d, nullptr, "SetThreadPriority32"}, | 2380 | {0x0d, SvcWrap32<SetThreadPriority32>, "SetThreadPriority32"}, |
| 2167 | {0x0e, nullptr, "GetThreadCoreMask32"}, | 2381 | {0x0e, SvcWrap32<GetThreadCoreMask32>, "GetThreadCoreMask32"}, |
| 2168 | {0x0f, nullptr, "SetThreadCoreMask32"}, | 2382 | {0x0f, SvcWrap32<SetThreadCoreMask32>, "SetThreadCoreMask32"}, |
| 2169 | {0x10, nullptr, "GetCurrentProcessorNumber32"}, | 2383 | {0x10, SvcWrap32<GetCurrentProcessorNumber32>, "GetCurrentProcessorNumber32"}, |
| 2170 | {0x11, nullptr, "SignalEvent32"}, | 2384 | {0x11, SvcWrap32<SignalEvent32>, "SignalEvent32"}, |
| 2171 | {0x12, nullptr, "ClearEvent32"}, | 2385 | {0x12, SvcWrap32<ClearEvent32>, "ClearEvent32"}, |
| 2172 | {0x13, nullptr, "MapSharedMemory32"}, | 2386 | {0x13, SvcWrap32<MapSharedMemory32>, "MapSharedMemory32"}, |
| 2173 | {0x14, nullptr, "UnmapSharedMemory32"}, | 2387 | {0x14, nullptr, "UnmapSharedMemory32"}, |
| 2174 | {0x15, nullptr, "CreateTransferMemory32"}, | 2388 | {0x15, SvcWrap32<CreateTransferMemory32>, "CreateTransferMemory32"}, |
| 2175 | {0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"}, | 2389 | {0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"}, |
| 2176 | {0x17, nullptr, "ResetSignal32"}, | 2390 | {0x17, SvcWrap32<ResetSignal32>, "ResetSignal32"}, |
| 2177 | {0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"}, | 2391 | {0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"}, |
| 2178 | {0x19, nullptr, "CancelSynchronization32"}, | 2392 | {0x19, SvcWrap32<CancelSynchronization32>, "CancelSynchronization32"}, |
| 2179 | {0x1a, nullptr, "ArbitrateLock32"}, | 2393 | {0x1a, SvcWrap32<ArbitrateLock32>, "ArbitrateLock32"}, |
| 2180 | {0x1b, nullptr, "ArbitrateUnlock32"}, | 2394 | {0x1b, SvcWrap32<ArbitrateUnlock32>, "ArbitrateUnlock32"}, |
| 2181 | {0x1c, nullptr, "WaitProcessWideKeyAtomic32"}, | 2395 | {0x1c, SvcWrap32<WaitProcessWideKeyAtomic32>, "WaitProcessWideKeyAtomic32"}, |
| 2182 | {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"}, | 2396 | {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"}, |
| 2183 | {0x1e, nullptr, "GetSystemTick32"}, | 2397 | {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"}, |
| 2184 | {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"}, | 2398 | {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"}, |
| 2185 | {0x20, nullptr, "Unknown"}, | 2399 | {0x20, nullptr, "Unknown"}, |
| 2186 | {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"}, | 2400 | {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"}, |
| 2187 | {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, | 2401 | {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, |
| 2188 | {0x23, nullptr, "Unknown"}, | 2402 | {0x23, nullptr, "Unknown"}, |
| 2189 | {0x24, nullptr, "GetProcessId32"}, | 2403 | {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"}, |
| 2190 | {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"}, | 2404 | {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"}, |
| 2191 | {0x26, nullptr, "Break32"}, | 2405 | {0x26, SvcWrap32<Break32>, "Break32"}, |
| 2192 | {0x27, nullptr, "OutputDebugString32"}, | 2406 | {0x27, nullptr, "OutputDebugString32"}, |
| 2193 | {0x28, nullptr, "Unknown"}, | 2407 | {0x28, nullptr, "Unknown"}, |
| 2194 | {0x29, SvcWrap32<GetInfo32>, "GetInfo32"}, | 2408 | {0x29, SvcWrap32<GetInfo32>, "GetInfo32"}, |
| 2195 | {0x2a, nullptr, "Unknown"}, | 2409 | {0x2a, nullptr, "Unknown"}, |
| 2196 | {0x2b, nullptr, "Unknown"}, | 2410 | {0x2b, nullptr, "Unknown"}, |
| 2197 | {0x2c, nullptr, "MapPhysicalMemory32"}, | 2411 | {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"}, |
| 2198 | {0x2d, nullptr, "UnmapPhysicalMemory32"}, | 2412 | {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"}, |
| 2199 | {0x2e, nullptr, "Unknown"}, | 2413 | {0x2e, nullptr, "Unknown"}, |
| 2200 | {0x2f, nullptr, "Unknown"}, | 2414 | {0x2f, nullptr, "Unknown"}, |
| 2201 | {0x30, nullptr, "Unknown"}, | 2415 | {0x30, nullptr, "Unknown"}, |
| 2202 | {0x31, nullptr, "Unknown"}, | 2416 | {0x31, nullptr, "Unknown"}, |
| 2203 | {0x32, nullptr, "SetThreadActivity32"}, | 2417 | {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"}, |
| 2204 | {0x33, nullptr, "GetThreadContext32"}, | 2418 | {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"}, |
| 2205 | {0x34, nullptr, "WaitForAddress32"}, | 2419 | {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"}, |
| 2206 | {0x35, nullptr, "SignalToAddress32"}, | 2420 | {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"}, |
| 2207 | {0x36, nullptr, "Unknown"}, | 2421 | {0x36, nullptr, "Unknown"}, |
| 2208 | {0x37, nullptr, "Unknown"}, | 2422 | {0x37, nullptr, "Unknown"}, |
| 2209 | {0x38, nullptr, "Unknown"}, | 2423 | {0x38, nullptr, "Unknown"}, |
| @@ -2219,7 +2433,7 @@ static const FunctionDef SVC_Table_32[] = { | |||
| 2219 | {0x42, nullptr, "Unknown"}, | 2433 | {0x42, nullptr, "Unknown"}, |
| 2220 | {0x43, nullptr, "ReplyAndReceive32"}, | 2434 | {0x43, nullptr, "ReplyAndReceive32"}, |
| 2221 | {0x44, nullptr, "Unknown"}, | 2435 | {0x44, nullptr, "Unknown"}, |
| 2222 | {0x45, nullptr, "CreateEvent32"}, | 2436 | {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"}, |
| 2223 | {0x46, nullptr, "Unknown"}, | 2437 | {0x46, nullptr, "Unknown"}, |
| 2224 | {0x47, nullptr, "Unknown"}, | 2438 | {0x47, nullptr, "Unknown"}, |
| 2225 | {0x48, nullptr, "Unknown"}, | 2439 | {0x48, nullptr, "Unknown"}, |
| @@ -2245,7 +2459,7 @@ static const FunctionDef SVC_Table_32[] = { | |||
| 2245 | {0x5c, nullptr, "Unknown"}, | 2459 | {0x5c, nullptr, "Unknown"}, |
| 2246 | {0x5d, nullptr, "Unknown"}, | 2460 | {0x5d, nullptr, "Unknown"}, |
| 2247 | {0x5e, nullptr, "Unknown"}, | 2461 | {0x5e, nullptr, "Unknown"}, |
| 2248 | {0x5F, nullptr, "FlushProcessDataCache32"}, | 2462 | {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"}, |
| 2249 | {0x60, nullptr, "Unknown"}, | 2463 | {0x60, nullptr, "Unknown"}, |
| 2250 | {0x61, nullptr, "Unknown"}, | 2464 | {0x61, nullptr, "Unknown"}, |
| 2251 | {0x62, nullptr, "Unknown"}, | 2465 | {0x62, nullptr, "Unknown"}, |
| @@ -2423,13 +2637,10 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) { | |||
| 2423 | return &SVC_Table_64[func_num]; | 2637 | return &SVC_Table_64[func_num]; |
| 2424 | } | 2638 | } |
| 2425 | 2639 | ||
| 2426 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); | ||
| 2427 | |||
| 2428 | void Call(Core::System& system, u32 immediate) { | 2640 | void Call(Core::System& system, u32 immediate) { |
| 2429 | MICROPROFILE_SCOPE(Kernel_SVC); | 2641 | system.ExitDynarmicProfile(); |
| 2430 | 2642 | auto& kernel = system.Kernel(); | |
| 2431 | // Lock the global kernel mutex when we enter the kernel HLE. | 2643 | kernel.EnterSVCProfile(); |
| 2432 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 2433 | 2644 | ||
| 2434 | const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) | 2645 | const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) |
| 2435 | : GetSVCInfo32(immediate); | 2646 | : GetSVCInfo32(immediate); |
| @@ -2442,6 +2653,9 @@ void Call(Core::System& system, u32 immediate) { | |||
| 2442 | } else { | 2653 | } else { |
| 2443 | LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); | 2654 | LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); |
| 2444 | } | 2655 | } |
| 2656 | |||
| 2657 | kernel.ExitSVCProfile(); | ||
| 2658 | system.EnterDynarmicProfile(); | ||
| 2445 | } | 2659 | } |
| 2446 | 2660 | ||
| 2447 | } // namespace Kernel::Svc | 2661 | } // namespace Kernel::Svc |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 7d735e3fa..0b6dd9df0 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -350,13 +350,50 @@ void SvcWrap64(Core::System& system) { | |||
| 350 | func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)); | 350 | func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)); |
| 351 | } | 351 | } |
| 352 | 352 | ||
| 353 | // Used by QueryMemory32 | 353 | // Used by QueryMemory32, ArbitrateLock32 |
| 354 | template <ResultCode func(Core::System&, u32, u32, u32)> | 354 | template <ResultCode func(Core::System&, u32, u32, u32)> |
| 355 | void SvcWrap32(Core::System& system) { | 355 | void SvcWrap32(Core::System& system) { |
| 356 | FuncReturn32(system, | 356 | FuncReturn32(system, |
| 357 | func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw); | 357 | func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw); |
| 358 | } | 358 | } |
| 359 | 359 | ||
| 360 | // Used by Break32 | ||
| 361 | template <void func(Core::System&, u32, u32, u32)> | ||
| 362 | void SvcWrap32(Core::System& system) { | ||
| 363 | func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)); | ||
| 364 | } | ||
| 365 | |||
| 366 | // Used by ExitProcess32, ExitThread32 | ||
| 367 | template <void func(Core::System&)> | ||
| 368 | void SvcWrap32(Core::System& system) { | ||
| 369 | func(system); | ||
| 370 | } | ||
| 371 | |||
| 372 | // Used by GetCurrentProcessorNumber32 | ||
| 373 | template <u32 func(Core::System&)> | ||
| 374 | void SvcWrap32(Core::System& system) { | ||
| 375 | FuncReturn32(system, func(system)); | ||
| 376 | } | ||
| 377 | |||
| 378 | // Used by SleepThread32 | ||
| 379 | template <void func(Core::System&, u32, u32)> | ||
| 380 | void SvcWrap32(Core::System& system) { | ||
| 381 | func(system, Param32(system, 0), Param32(system, 1)); | ||
| 382 | } | ||
| 383 | |||
| 384 | // Used by CreateThread32 | ||
| 385 | template <ResultCode func(Core::System&, Handle*, u32, u32, u32, u32, s32)> | ||
| 386 | void SvcWrap32(Core::System& system) { | ||
| 387 | Handle param_1 = 0; | ||
| 388 | |||
| 389 | const u32 retval = func(system, ¶m_1, Param32(system, 0), Param32(system, 1), | ||
| 390 | Param32(system, 2), Param32(system, 3), Param32(system, 4)) | ||
| 391 | .raw; | ||
| 392 | |||
| 393 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 394 | FuncReturn(system, retval); | ||
| 395 | } | ||
| 396 | |||
| 360 | // Used by GetInfo32 | 397 | // Used by GetInfo32 |
| 361 | template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)> | 398 | template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)> |
| 362 | void SvcWrap32(Core::System& system) { | 399 | void SvcWrap32(Core::System& system) { |
| @@ -393,18 +430,114 @@ void SvcWrap32(Core::System& system) { | |||
| 393 | FuncReturn(system, retval); | 430 | FuncReturn(system, retval); |
| 394 | } | 431 | } |
| 395 | 432 | ||
| 433 | // Used by GetSystemTick32 | ||
| 434 | template <void func(Core::System&, u32*, u32*)> | ||
| 435 | void SvcWrap32(Core::System& system) { | ||
| 436 | u32 param_1 = 0; | ||
| 437 | u32 param_2 = 0; | ||
| 438 | |||
| 439 | func(system, ¶m_1, ¶m_2); | ||
| 440 | system.CurrentArmInterface().SetReg(0, param_1); | ||
| 441 | system.CurrentArmInterface().SetReg(1, param_2); | ||
| 442 | } | ||
| 443 | |||
| 444 | // Used by CreateEvent32 | ||
| 445 | template <ResultCode func(Core::System&, Handle*, Handle*)> | ||
| 446 | void SvcWrap32(Core::System& system) { | ||
| 447 | Handle param_1 = 0; | ||
| 448 | Handle param_2 = 0; | ||
| 449 | |||
| 450 | const u32 retval = func(system, ¶m_1, ¶m_2).raw; | ||
| 451 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 452 | system.CurrentArmInterface().SetReg(2, param_2); | ||
| 453 | FuncReturn(system, retval); | ||
| 454 | } | ||
| 455 | |||
| 456 | // Used by GetThreadId32 | ||
| 457 | template <ResultCode func(Core::System&, Handle, u32*, u32*, u32*)> | ||
| 458 | void SvcWrap32(Core::System& system) { | ||
| 459 | u32 param_1 = 0; | ||
| 460 | u32 param_2 = 0; | ||
| 461 | u32 param_3 = 0; | ||
| 462 | |||
| 463 | const u32 retval = func(system, Param32(system, 2), ¶m_1, ¶m_2, ¶m_3).raw; | ||
| 464 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 465 | system.CurrentArmInterface().SetReg(2, param_2); | ||
| 466 | system.CurrentArmInterface().SetReg(3, param_3); | ||
| 467 | FuncReturn(system, retval); | ||
| 468 | } | ||
| 469 | |||
| 396 | // Used by SignalProcessWideKey32 | 470 | // Used by SignalProcessWideKey32 |
| 397 | template <void func(Core::System&, u32, s32)> | 471 | template <void func(Core::System&, u32, s32)> |
| 398 | void SvcWrap32(Core::System& system) { | 472 | void SvcWrap32(Core::System& system) { |
| 399 | func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1))); | 473 | func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1))); |
| 400 | } | 474 | } |
| 401 | 475 | ||
| 402 | // Used by SendSyncRequest32 | 476 | // Used by SetThreadPriority32 |
| 477 | template <ResultCode func(Core::System&, Handle, u32)> | ||
| 478 | void SvcWrap32(Core::System& system) { | ||
| 479 | const u32 retval = | ||
| 480 | func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw; | ||
| 481 | FuncReturn(system, retval); | ||
| 482 | } | ||
| 483 | |||
| 484 | // Used by SetThreadCoreMask32 | ||
| 485 | template <ResultCode func(Core::System&, Handle, u32, u32, u32)> | ||
| 486 | void SvcWrap32(Core::System& system) { | ||
| 487 | const u32 retval = | ||
| 488 | func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)), | ||
| 489 | static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3))) | ||
| 490 | .raw; | ||
| 491 | FuncReturn(system, retval); | ||
| 492 | } | ||
| 493 | |||
| 494 | // Used by WaitProcessWideKeyAtomic32 | ||
| 495 | template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)> | ||
| 496 | void SvcWrap32(Core::System& system) { | ||
| 497 | const u32 retval = | ||
| 498 | func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)), | ||
| 499 | static_cast<Handle>(Param(system, 2)), static_cast<u32>(Param(system, 3)), | ||
| 500 | static_cast<u32>(Param(system, 4))) | ||
| 501 | .raw; | ||
| 502 | FuncReturn(system, retval); | ||
| 503 | } | ||
| 504 | |||
| 505 | // Used by WaitForAddress32 | ||
| 506 | template <ResultCode func(Core::System&, u32, u32, s32, u32, u32)> | ||
| 507 | void SvcWrap32(Core::System& system) { | ||
| 508 | const u32 retval = func(system, static_cast<u32>(Param(system, 0)), | ||
| 509 | static_cast<u32>(Param(system, 1)), static_cast<s32>(Param(system, 2)), | ||
| 510 | static_cast<u32>(Param(system, 3)), static_cast<u32>(Param(system, 4))) | ||
| 511 | .raw; | ||
| 512 | FuncReturn(system, retval); | ||
| 513 | } | ||
| 514 | |||
| 515 | // Used by SignalToAddress32 | ||
| 516 | template <ResultCode func(Core::System&, u32, u32, s32, s32)> | ||
| 517 | void SvcWrap32(Core::System& system) { | ||
| 518 | const u32 retval = | ||
| 519 | func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)), | ||
| 520 | static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) | ||
| 521 | .raw; | ||
| 522 | FuncReturn(system, retval); | ||
| 523 | } | ||
| 524 | |||
| 525 | // Used by SendSyncRequest32, ArbitrateUnlock32 | ||
| 403 | template <ResultCode func(Core::System&, u32)> | 526 | template <ResultCode func(Core::System&, u32)> |
| 404 | void SvcWrap32(Core::System& system) { | 527 | void SvcWrap32(Core::System& system) { |
| 405 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); | 528 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); |
| 406 | } | 529 | } |
| 407 | 530 | ||
| 531 | // Used by CreateTransferMemory32 | ||
| 532 | template <ResultCode func(Core::System&, Handle*, u32, u32, u32)> | ||
| 533 | void SvcWrap32(Core::System& system) { | ||
| 534 | Handle handle = 0; | ||
| 535 | const u32 retval = | ||
| 536 | func(system, &handle, Param32(system, 1), Param32(system, 2), Param32(system, 3)).raw; | ||
| 537 | system.CurrentArmInterface().SetReg(1, handle); | ||
| 538 | FuncReturn(system, retval); | ||
| 539 | } | ||
| 540 | |||
| 408 | // Used by WaitSynchronization32 | 541 | // Used by WaitSynchronization32 |
| 409 | template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)> | 542 | template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)> |
| 410 | void SvcWrap32(Core::System& system) { | 543 | void SvcWrap32(Core::System& system) { |
diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp index dc37fad1a..851b702a5 100644 --- a/src/core/hle/kernel/synchronization.cpp +++ b/src/core/hle/kernel/synchronization.cpp | |||
| @@ -10,78 +10,107 @@ | |||
| 10 | #include "core/hle/kernel/synchronization.h" | 10 | #include "core/hle/kernel/synchronization.h" |
| 11 | #include "core/hle/kernel/synchronization_object.h" | 11 | #include "core/hle/kernel/synchronization_object.h" |
| 12 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 13 | #include "core/hle/kernel/time_manager.h" | ||
| 13 | 14 | ||
| 14 | namespace Kernel { | 15 | namespace Kernel { |
| 15 | 16 | ||
| 16 | /// Default thread wakeup callback for WaitSynchronization | ||
| 17 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | ||
| 18 | std::shared_ptr<SynchronizationObject> object, | ||
| 19 | std::size_t index) { | ||
| 20 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); | ||
| 21 | |||
| 22 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 23 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 24 | return true; | ||
| 25 | } | ||
| 26 | |||
| 27 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 28 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 29 | thread->SetWaitSynchronizationOutput(static_cast<u32>(index)); | ||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | Synchronization::Synchronization(Core::System& system) : system{system} {} | 17 | Synchronization::Synchronization(Core::System& system) : system{system} {} |
| 34 | 18 | ||
| 35 | void Synchronization::SignalObject(SynchronizationObject& obj) const { | 19 | void Synchronization::SignalObject(SynchronizationObject& obj) const { |
| 20 | auto& kernel = system.Kernel(); | ||
| 21 | SchedulerLock lock(kernel); | ||
| 22 | auto& time_manager = kernel.TimeManager(); | ||
| 36 | if (obj.IsSignaled()) { | 23 | if (obj.IsSignaled()) { |
| 37 | obj.WakeupAllWaitingThreads(); | 24 | for (auto thread : obj.GetWaitingThreads()) { |
| 25 | if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { | ||
| 26 | if (thread->GetStatus() != ThreadStatus::WaitHLEEvent) { | ||
| 27 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); | ||
| 28 | ASSERT(thread->IsWaitingSync()); | ||
| 29 | } | ||
| 30 | thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); | ||
| 31 | thread->ResumeFromWait(); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | obj.ClearWaitingThreads(); | ||
| 38 | } | 35 | } |
| 39 | } | 36 | } |
| 40 | 37 | ||
| 41 | std::pair<ResultCode, Handle> Synchronization::WaitFor( | 38 | std::pair<ResultCode, Handle> Synchronization::WaitFor( |
| 42 | std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) { | 39 | std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) { |
| 40 | auto& kernel = system.Kernel(); | ||
| 43 | auto* const thread = system.CurrentScheduler().GetCurrentThread(); | 41 | auto* const thread = system.CurrentScheduler().GetCurrentThread(); |
| 44 | // Find the first object that is acquirable in the provided list of objects | 42 | Handle event_handle = InvalidHandle; |
| 45 | const auto itr = std::find_if(sync_objects.begin(), sync_objects.end(), | 43 | { |
| 46 | [thread](const std::shared_ptr<SynchronizationObject>& object) { | 44 | SchedulerLockAndSleep lock(kernel, event_handle, thread, nano_seconds); |
| 47 | return object->IsSignaled(); | 45 | const auto itr = |
| 48 | }); | 46 | std::find_if(sync_objects.begin(), sync_objects.end(), |
| 49 | 47 | [thread](const std::shared_ptr<SynchronizationObject>& object) { | |
| 50 | if (itr != sync_objects.end()) { | 48 | return object->IsSignaled(); |
| 51 | // We found a ready object, acquire it and set the result value | 49 | }); |
| 52 | SynchronizationObject* object = itr->get(); | 50 | |
| 53 | object->Acquire(thread); | 51 | if (itr != sync_objects.end()) { |
| 54 | const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); | 52 | // We found a ready object, acquire it and set the result value |
| 55 | return {RESULT_SUCCESS, index}; | 53 | SynchronizationObject* object = itr->get(); |
| 54 | object->Acquire(thread); | ||
| 55 | const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); | ||
| 56 | lock.CancelSleep(); | ||
| 57 | return {RESULT_SUCCESS, index}; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (nano_seconds == 0) { | ||
| 61 | lock.CancelSleep(); | ||
| 62 | return {RESULT_TIMEOUT, InvalidHandle}; | ||
| 63 | } | ||
| 64 | |||
| 65 | if (thread->IsPendingTermination()) { | ||
| 66 | lock.CancelSleep(); | ||
| 67 | return {ERR_THREAD_TERMINATING, InvalidHandle}; | ||
| 68 | } | ||
| 69 | |||
| 70 | if (thread->IsSyncCancelled()) { | ||
| 71 | thread->SetSyncCancelled(false); | ||
| 72 | lock.CancelSleep(); | ||
| 73 | return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; | ||
| 74 | } | ||
| 75 | |||
| 76 | for (auto& object : sync_objects) { | ||
| 77 | object->AddWaitingThread(SharedFrom(thread)); | ||
| 78 | } | ||
| 79 | |||
| 80 | thread->SetSynchronizationObjects(&sync_objects); | ||
| 81 | thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 82 | thread->SetStatus(ThreadStatus::WaitSynch); | ||
| 83 | thread->SetWaitingSync(true); | ||
| 56 | } | 84 | } |
| 85 | thread->SetWaitingSync(false); | ||
| 57 | 86 | ||
| 58 | // No objects were ready to be acquired, prepare to suspend the thread. | 87 | if (event_handle != InvalidHandle) { |
| 59 | 88 | auto& time_manager = kernel.TimeManager(); | |
| 60 | // If a timeout value of 0 was provided, just return the Timeout error code instead of | 89 | time_manager.UnscheduleTimeEvent(event_handle); |
| 61 | // suspending the thread. | ||
| 62 | if (nano_seconds == 0) { | ||
| 63 | return {RESULT_TIMEOUT, InvalidHandle}; | ||
| 64 | } | 90 | } |
| 65 | 91 | ||
| 66 | if (thread->IsSyncCancelled()) { | 92 | { |
| 67 | thread->SetSyncCancelled(false); | 93 | SchedulerLock lock(kernel); |
| 68 | return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; | 94 | ResultCode signaling_result = thread->GetSignalingResult(); |
| 95 | SynchronizationObject* signaling_object = thread->GetSignalingObject(); | ||
| 96 | thread->SetSynchronizationObjects(nullptr); | ||
| 97 | auto shared_thread = SharedFrom(thread); | ||
| 98 | for (auto& obj : sync_objects) { | ||
| 99 | obj->RemoveWaitingThread(shared_thread); | ||
| 100 | } | ||
| 101 | if (signaling_object != nullptr) { | ||
| 102 | const auto itr = std::find_if( | ||
| 103 | sync_objects.begin(), sync_objects.end(), | ||
| 104 | [signaling_object](const std::shared_ptr<SynchronizationObject>& object) { | ||
| 105 | return object.get() == signaling_object; | ||
| 106 | }); | ||
| 107 | ASSERT(itr != sync_objects.end()); | ||
| 108 | signaling_object->Acquire(thread); | ||
| 109 | const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); | ||
| 110 | return {signaling_result, index}; | ||
| 111 | } | ||
| 112 | return {signaling_result, -1}; | ||
| 69 | } | 113 | } |
| 70 | |||
| 71 | for (auto& object : sync_objects) { | ||
| 72 | object->AddWaitingThread(SharedFrom(thread)); | ||
| 73 | } | ||
| 74 | |||
| 75 | thread->SetSynchronizationObjects(std::move(sync_objects)); | ||
| 76 | thread->SetStatus(ThreadStatus::WaitSynch); | ||
| 77 | |||
| 78 | // Create an event to wake the thread up after the specified nanosecond delay has passed | ||
| 79 | thread->WakeAfterDelay(nano_seconds); | ||
| 80 | thread->SetWakeupCallback(DefaultThreadWakeupCallback); | ||
| 81 | |||
| 82 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 83 | |||
| 84 | return {RESULT_TIMEOUT, InvalidHandle}; | ||
| 85 | } | 114 | } |
| 86 | 115 | ||
| 87 | } // namespace Kernel | 116 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/synchronization_object.cpp b/src/core/hle/kernel/synchronization_object.cpp index 43f3eef18..ba4d39157 100644 --- a/src/core/hle/kernel/synchronization_object.cpp +++ b/src/core/hle/kernel/synchronization_object.cpp | |||
| @@ -38,68 +38,8 @@ void SynchronizationObject::RemoveWaitingThread(std::shared_ptr<Thread> thread) | |||
| 38 | waiting_threads.erase(itr); | 38 | waiting_threads.erase(itr); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | std::shared_ptr<Thread> SynchronizationObject::GetHighestPriorityReadyThread() const { | 41 | void SynchronizationObject::ClearWaitingThreads() { |
| 42 | Thread* candidate = nullptr; | 42 | waiting_threads.clear(); |
| 43 | u32 candidate_priority = THREADPRIO_LOWEST + 1; | ||
| 44 | |||
| 45 | for (const auto& thread : waiting_threads) { | ||
| 46 | const ThreadStatus thread_status = thread->GetStatus(); | ||
| 47 | |||
| 48 | // The list of waiting threads must not contain threads that are not waiting to be awakened. | ||
| 49 | ASSERT_MSG(thread_status == ThreadStatus::WaitSynch || | ||
| 50 | thread_status == ThreadStatus::WaitHLEEvent, | ||
| 51 | "Inconsistent thread statuses in waiting_threads"); | ||
| 52 | |||
| 53 | if (thread->GetPriority() >= candidate_priority) | ||
| 54 | continue; | ||
| 55 | |||
| 56 | if (ShouldWait(thread.get())) | ||
| 57 | continue; | ||
| 58 | |||
| 59 | candidate = thread.get(); | ||
| 60 | candidate_priority = thread->GetPriority(); | ||
| 61 | } | ||
| 62 | |||
| 63 | return SharedFrom(candidate); | ||
| 64 | } | ||
| 65 | |||
| 66 | void SynchronizationObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) { | ||
| 67 | ASSERT(!ShouldWait(thread.get())); | ||
| 68 | |||
| 69 | if (!thread) { | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | if (thread->IsSleepingOnWait()) { | ||
| 74 | for (const auto& object : thread->GetSynchronizationObjects()) { | ||
| 75 | ASSERT(!object->ShouldWait(thread.get())); | ||
| 76 | object->Acquire(thread.get()); | ||
| 77 | } | ||
| 78 | } else { | ||
| 79 | Acquire(thread.get()); | ||
| 80 | } | ||
| 81 | |||
| 82 | const std::size_t index = thread->GetSynchronizationObjectIndex(SharedFrom(this)); | ||
| 83 | |||
| 84 | thread->ClearSynchronizationObjects(); | ||
| 85 | |||
| 86 | thread->CancelWakeupTimer(); | ||
| 87 | |||
| 88 | bool resume = true; | ||
| 89 | if (thread->HasWakeupCallback()) { | ||
| 90 | resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, SharedFrom(this), | ||
| 91 | index); | ||
| 92 | } | ||
| 93 | if (resume) { | ||
| 94 | thread->ResumeFromWait(); | ||
| 95 | kernel.PrepareReschedule(thread->GetProcessorID()); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | void SynchronizationObject::WakeupAllWaitingThreads() { | ||
| 100 | while (auto thread = GetHighestPriorityReadyThread()) { | ||
| 101 | WakeupWaitingThread(thread); | ||
| 102 | } | ||
| 103 | } | 43 | } |
| 104 | 44 | ||
| 105 | const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const { | 45 | const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const { |
diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h index 741c31faf..f89b24204 100644 --- a/src/core/hle/kernel/synchronization_object.h +++ b/src/core/hle/kernel/synchronization_object.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | namespace Kernel { | 12 | namespace Kernel { |
| 13 | 13 | ||
| 14 | class KernelCore; | 14 | class KernelCore; |
| 15 | class Synchronization; | ||
| 15 | class Thread; | 16 | class Thread; |
| 16 | 17 | ||
| 17 | /// Class that represents a Kernel object that a thread can be waiting on | 18 | /// Class that represents a Kernel object that a thread can be waiting on |
| @@ -49,24 +50,11 @@ public: | |||
| 49 | */ | 50 | */ |
| 50 | void RemoveWaitingThread(std::shared_ptr<Thread> thread); | 51 | void RemoveWaitingThread(std::shared_ptr<Thread> thread); |
| 51 | 52 | ||
| 52 | /** | ||
| 53 | * Wake up all threads waiting on this object that can be awoken, in priority order, | ||
| 54 | * and set the synchronization result and output of the thread. | ||
| 55 | */ | ||
| 56 | void WakeupAllWaitingThreads(); | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Wakes up a single thread waiting on this object. | ||
| 60 | * @param thread Thread that is waiting on this object to wakeup. | ||
| 61 | */ | ||
| 62 | void WakeupWaitingThread(std::shared_ptr<Thread> thread); | ||
| 63 | |||
| 64 | /// Obtains the highest priority thread that is ready to run from this object's waiting list. | ||
| 65 | std::shared_ptr<Thread> GetHighestPriorityReadyThread() const; | ||
| 66 | |||
| 67 | /// Get a const reference to the waiting threads list for debug use | 53 | /// Get a const reference to the waiting threads list for debug use |
| 68 | const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; | 54 | const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; |
| 69 | 55 | ||
| 56 | void ClearWaitingThreads(); | ||
| 57 | |||
| 70 | protected: | 58 | protected: |
| 71 | bool is_signaled{}; // Tells if this sync object is signalled; | 59 | bool is_signaled{}; // Tells if this sync object is signalled; |
| 72 | 60 | ||
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index db7f379ac..2b1092697 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -9,12 +9,21 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/fiber.h" | ||
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 13 | #include "common/thread_queue_list.h" | 14 | #include "common/thread_queue_list.h" |
| 14 | #include "core/arm/arm_interface.h" | 15 | #include "core/arm/arm_interface.h" |
| 16 | #ifdef ARCHITECTURE_x86_64 | ||
| 17 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | ||
| 18 | #include "core/arm/dynarmic/arm_dynarmic_64.h" | ||
| 19 | #endif | ||
| 20 | #include "core/arm/cpu_interrupt_handler.h" | ||
| 21 | #include "core/arm/exclusive_monitor.h" | ||
| 22 | #include "core/arm/unicorn/arm_unicorn.h" | ||
| 15 | #include "core/core.h" | 23 | #include "core/core.h" |
| 16 | #include "core/core_timing.h" | 24 | #include "core/core_timing.h" |
| 17 | #include "core/core_timing_util.h" | 25 | #include "core/core_timing_util.h" |
| 26 | #include "core/cpu_manager.h" | ||
| 18 | #include "core/hardware_properties.h" | 27 | #include "core/hardware_properties.h" |
| 19 | #include "core/hle/kernel/errors.h" | 28 | #include "core/hle/kernel/errors.h" |
| 20 | #include "core/hle/kernel/handle_table.h" | 29 | #include "core/hle/kernel/handle_table.h" |
| @@ -23,6 +32,7 @@ | |||
| 23 | #include "core/hle/kernel/process.h" | 32 | #include "core/hle/kernel/process.h" |
| 24 | #include "core/hle/kernel/scheduler.h" | 33 | #include "core/hle/kernel/scheduler.h" |
| 25 | #include "core/hle/kernel/thread.h" | 34 | #include "core/hle/kernel/thread.h" |
| 35 | #include "core/hle/kernel/time_manager.h" | ||
| 26 | #include "core/hle/result.h" | 36 | #include "core/hle/result.h" |
| 27 | #include "core/memory.h" | 37 | #include "core/memory.h" |
| 28 | 38 | ||
| @@ -44,46 +54,26 @@ Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {} | |||
| 44 | Thread::~Thread() = default; | 54 | Thread::~Thread() = default; |
| 45 | 55 | ||
| 46 | void Thread::Stop() { | 56 | void Thread::Stop() { |
| 47 | // Cancel any outstanding wakeup events for this thread | 57 | { |
| 48 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), | 58 | SchedulerLock lock(kernel); |
| 49 | global_handle); | 59 | SetStatus(ThreadStatus::Dead); |
| 50 | kernel.GlobalHandleTable().Close(global_handle); | 60 | Signal(); |
| 51 | global_handle = 0; | 61 | kernel.GlobalHandleTable().Close(global_handle); |
| 52 | SetStatus(ThreadStatus::Dead); | ||
| 53 | Signal(); | ||
| 54 | |||
| 55 | // Clean up any dangling references in objects that this thread was waiting for | ||
| 56 | for (auto& wait_object : wait_objects) { | ||
| 57 | wait_object->RemoveWaitingThread(SharedFrom(this)); | ||
| 58 | } | ||
| 59 | wait_objects.clear(); | ||
| 60 | |||
| 61 | owner_process->UnregisterThread(this); | ||
| 62 | |||
| 63 | // Mark the TLS slot in the thread's page as free. | ||
| 64 | owner_process->FreeTLSRegion(tls_address); | ||
| 65 | } | ||
| 66 | |||
| 67 | void Thread::WakeAfterDelay(s64 nanoseconds) { | ||
| 68 | // Don't schedule a wakeup if the thread wants to wait forever | ||
| 69 | if (nanoseconds == -1) | ||
| 70 | return; | ||
| 71 | 62 | ||
| 72 | // This function might be called from any thread so we have to be cautious and use the | 63 | if (owner_process) { |
| 73 | // thread-safe version of ScheduleEvent. | 64 | owner_process->UnregisterThread(this); |
| 74 | const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); | ||
| 75 | Core::System::GetInstance().CoreTiming().ScheduleEvent( | ||
| 76 | cycles, kernel.ThreadWakeupCallbackEventType(), global_handle); | ||
| 77 | } | ||
| 78 | 65 | ||
| 79 | void Thread::CancelWakeupTimer() { | 66 | // Mark the TLS slot in the thread's page as free. |
| 80 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), | 67 | owner_process->FreeTLSRegion(tls_address); |
| 81 | global_handle); | 68 | } |
| 69 | arm_interface.reset(); | ||
| 70 | has_exited = true; | ||
| 71 | } | ||
| 72 | global_handle = 0; | ||
| 82 | } | 73 | } |
| 83 | 74 | ||
| 84 | void Thread::ResumeFromWait() { | 75 | void Thread::ResumeFromWait() { |
| 85 | ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); | 76 | SchedulerLock lock(kernel); |
| 86 | |||
| 87 | switch (status) { | 77 | switch (status) { |
| 88 | case ThreadStatus::Paused: | 78 | case ThreadStatus::Paused: |
| 89 | case ThreadStatus::WaitSynch: | 79 | case ThreadStatus::WaitSynch: |
| @@ -99,7 +89,7 @@ void Thread::ResumeFromWait() { | |||
| 99 | case ThreadStatus::Ready: | 89 | case ThreadStatus::Ready: |
| 100 | // The thread's wakeup callback must have already been cleared when the thread was first | 90 | // The thread's wakeup callback must have already been cleared when the thread was first |
| 101 | // awoken. | 91 | // awoken. |
| 102 | ASSERT(wakeup_callback == nullptr); | 92 | ASSERT(hle_callback == nullptr); |
| 103 | // If the thread is waiting on multiple wait objects, it might be awoken more than once | 93 | // If the thread is waiting on multiple wait objects, it might be awoken more than once |
| 104 | // before actually resuming. We can ignore subsequent wakeups if the thread status has | 94 | // before actually resuming. We can ignore subsequent wakeups if the thread status has |
| 105 | // already been set to ThreadStatus::Ready. | 95 | // already been set to ThreadStatus::Ready. |
| @@ -115,24 +105,31 @@ void Thread::ResumeFromWait() { | |||
| 115 | return; | 105 | return; |
| 116 | } | 106 | } |
| 117 | 107 | ||
| 118 | wakeup_callback = nullptr; | 108 | SetStatus(ThreadStatus::Ready); |
| 109 | } | ||
| 110 | |||
| 111 | void Thread::OnWakeUp() { | ||
| 112 | SchedulerLock lock(kernel); | ||
| 119 | 113 | ||
| 120 | if (activity == ThreadActivity::Paused) { | 114 | SetStatus(ThreadStatus::Ready); |
| 121 | SetStatus(ThreadStatus::Paused); | 115 | } |
| 122 | return; | ||
| 123 | } | ||
| 124 | 116 | ||
| 117 | ResultCode Thread::Start() { | ||
| 118 | SchedulerLock lock(kernel); | ||
| 125 | SetStatus(ThreadStatus::Ready); | 119 | SetStatus(ThreadStatus::Ready); |
| 120 | return RESULT_SUCCESS; | ||
| 126 | } | 121 | } |
| 127 | 122 | ||
| 128 | void Thread::CancelWait() { | 123 | void Thread::CancelWait() { |
| 129 | if (GetSchedulingStatus() != ThreadSchedStatus::Paused) { | 124 | SchedulerLock lock(kernel); |
| 125 | if (GetSchedulingStatus() != ThreadSchedStatus::Paused || !is_waiting_on_sync) { | ||
| 130 | is_sync_cancelled = true; | 126 | is_sync_cancelled = true; |
| 131 | return; | 127 | return; |
| 132 | } | 128 | } |
| 129 | // TODO(Blinkhawk): Implement cancel of server session | ||
| 133 | is_sync_cancelled = false; | 130 | is_sync_cancelled = false; |
| 134 | SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED); | 131 | SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); |
| 135 | ResumeFromWait(); | 132 | SetStatus(ThreadStatus::Ready); |
| 136 | } | 133 | } |
| 137 | 134 | ||
| 138 | static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, | 135 | static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, |
| @@ -153,12 +150,29 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, | |||
| 153 | context.fpcr = 0; | 150 | context.fpcr = 0; |
| 154 | } | 151 | } |
| 155 | 152 | ||
| 156 | ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name, | 153 | std::shared_ptr<Common::Fiber>& Thread::GetHostContext() { |
| 157 | VAddr entry_point, u32 priority, u64 arg, | 154 | return host_context; |
| 158 | s32 processor_id, VAddr stack_top, | 155 | } |
| 159 | Process& owner_process) { | 156 | |
| 157 | ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags, | ||
| 158 | std::string name, VAddr entry_point, u32 priority, | ||
| 159 | u64 arg, s32 processor_id, VAddr stack_top, | ||
| 160 | Process* owner_process) { | ||
| 161 | std::function<void(void*)> init_func = system.GetCpuManager().GetGuestThreadStartFunc(); | ||
| 162 | void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); | ||
| 163 | return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top, | ||
| 164 | owner_process, std::move(init_func), init_func_parameter); | ||
| 165 | } | ||
| 166 | |||
| 167 | ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags, | ||
| 168 | std::string name, VAddr entry_point, u32 priority, | ||
| 169 | u64 arg, s32 processor_id, VAddr stack_top, | ||
| 170 | Process* owner_process, | ||
| 171 | std::function<void(void*)>&& thread_start_func, | ||
| 172 | void* thread_start_parameter) { | ||
| 173 | auto& kernel = system.Kernel(); | ||
| 160 | // Check if priority is in ranged. Lowest priority -> highest priority id. | 174 | // Check if priority is in ranged. Lowest priority -> highest priority id. |
| 161 | if (priority > THREADPRIO_LOWEST) { | 175 | if (priority > THREADPRIO_LOWEST && ((type_flags & THREADTYPE_IDLE) == 0)) { |
| 162 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); | 176 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); |
| 163 | return ERR_INVALID_THREAD_PRIORITY; | 177 | return ERR_INVALID_THREAD_PRIORITY; |
| 164 | } | 178 | } |
| @@ -168,11 +182,12 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin | |||
| 168 | return ERR_INVALID_PROCESSOR_ID; | 182 | return ERR_INVALID_PROCESSOR_ID; |
| 169 | } | 183 | } |
| 170 | 184 | ||
| 171 | auto& system = Core::System::GetInstance(); | 185 | if (owner_process) { |
| 172 | if (!system.Memory().IsValidVirtualAddress(owner_process, entry_point)) { | 186 | if (!system.Memory().IsValidVirtualAddress(*owner_process, entry_point)) { |
| 173 | LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); | 187 | LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); |
| 174 | // TODO (bunnei): Find the correct error code to use here | 188 | // TODO (bunnei): Find the correct error code to use here |
| 175 | return RESULT_UNKNOWN; | 189 | return RESULT_UNKNOWN; |
| 190 | } | ||
| 176 | } | 191 | } |
| 177 | 192 | ||
| 178 | std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); | 193 | std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); |
| @@ -183,51 +198,82 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin | |||
| 183 | thread->stack_top = stack_top; | 198 | thread->stack_top = stack_top; |
| 184 | thread->tpidr_el0 = 0; | 199 | thread->tpidr_el0 = 0; |
| 185 | thread->nominal_priority = thread->current_priority = priority; | 200 | thread->nominal_priority = thread->current_priority = priority; |
| 186 | thread->last_running_ticks = system.CoreTiming().GetTicks(); | 201 | thread->last_running_ticks = 0; |
| 187 | thread->processor_id = processor_id; | 202 | thread->processor_id = processor_id; |
| 188 | thread->ideal_core = processor_id; | 203 | thread->ideal_core = processor_id; |
| 189 | thread->affinity_mask = 1ULL << processor_id; | 204 | thread->affinity_mask = 1ULL << processor_id; |
| 190 | thread->wait_objects.clear(); | 205 | thread->wait_objects = nullptr; |
| 191 | thread->mutex_wait_address = 0; | 206 | thread->mutex_wait_address = 0; |
| 192 | thread->condvar_wait_address = 0; | 207 | thread->condvar_wait_address = 0; |
| 193 | thread->wait_handle = 0; | 208 | thread->wait_handle = 0; |
| 194 | thread->name = std::move(name); | 209 | thread->name = std::move(name); |
| 195 | thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); | 210 | thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); |
| 196 | thread->owner_process = &owner_process; | 211 | thread->owner_process = owner_process; |
| 197 | auto& scheduler = kernel.GlobalScheduler(); | 212 | thread->type = type_flags; |
| 198 | scheduler.AddThread(thread); | 213 | if ((type_flags & THREADTYPE_IDLE) == 0) { |
| 199 | thread->tls_address = thread->owner_process->CreateTLSRegion(); | 214 | auto& scheduler = kernel.GlobalScheduler(); |
| 200 | 215 | scheduler.AddThread(thread); | |
| 201 | thread->owner_process->RegisterThread(thread.get()); | 216 | } |
| 217 | if (owner_process) { | ||
| 218 | thread->tls_address = thread->owner_process->CreateTLSRegion(); | ||
| 219 | thread->owner_process->RegisterThread(thread.get()); | ||
| 220 | } else { | ||
| 221 | thread->tls_address = 0; | ||
| 222 | } | ||
| 223 | // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used | ||
| 224 | // to initialize the context | ||
| 225 | thread->arm_interface.reset(); | ||
| 226 | if ((type_flags & THREADTYPE_HLE) == 0) { | ||
| 227 | #ifdef ARCHITECTURE_x86_64 | ||
| 228 | if (owner_process && !owner_process->Is64BitProcess()) { | ||
| 229 | thread->arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( | ||
| 230 | system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(), | ||
| 231 | processor_id); | ||
| 232 | } else { | ||
| 233 | thread->arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( | ||
| 234 | system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(), | ||
| 235 | processor_id); | ||
| 236 | } | ||
| 202 | 237 | ||
| 203 | ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top), | 238 | #else |
| 204 | static_cast<u32>(entry_point), static_cast<u32>(arg)); | 239 | if (owner_process && !owner_process->Is64BitProcess()) { |
| 205 | ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); | 240 | thread->arm_interface = std::make_shared<Core::ARM_Unicorn>( |
| 241 | system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch32, | ||
| 242 | processor_id); | ||
| 243 | } else { | ||
| 244 | thread->arm_interface = std::make_shared<Core::ARM_Unicorn>( | ||
| 245 | system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch64, | ||
| 246 | processor_id); | ||
| 247 | } | ||
| 248 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | ||
| 249 | #endif | ||
| 250 | ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top), | ||
| 251 | static_cast<u32>(entry_point), static_cast<u32>(arg)); | ||
| 252 | ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); | ||
| 253 | } | ||
| 254 | thread->host_context = | ||
| 255 | std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter); | ||
| 206 | 256 | ||
| 207 | return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); | 257 | return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); |
| 208 | } | 258 | } |
| 209 | 259 | ||
| 210 | void Thread::SetPriority(u32 priority) { | 260 | void Thread::SetPriority(u32 priority) { |
| 261 | SchedulerLock lock(kernel); | ||
| 211 | ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, | 262 | ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, |
| 212 | "Invalid priority value."); | 263 | "Invalid priority value."); |
| 213 | nominal_priority = priority; | 264 | nominal_priority = priority; |
| 214 | UpdatePriority(); | 265 | UpdatePriority(); |
| 215 | } | 266 | } |
| 216 | 267 | ||
| 217 | void Thread::SetWaitSynchronizationResult(ResultCode result) { | 268 | void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode result) { |
| 218 | context_32.cpu_registers[0] = result.raw; | 269 | signaling_object = object; |
| 219 | context_64.cpu_registers[0] = result.raw; | 270 | signaling_result = result; |
| 220 | } | ||
| 221 | |||
| 222 | void Thread::SetWaitSynchronizationOutput(s32 output) { | ||
| 223 | context_32.cpu_registers[1] = output; | ||
| 224 | context_64.cpu_registers[1] = output; | ||
| 225 | } | 271 | } |
| 226 | 272 | ||
| 227 | s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const { | 273 | s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const { |
| 228 | ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); | 274 | ASSERT_MSG(!wait_objects->empty(), "Thread is not waiting for anything"); |
| 229 | const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); | 275 | const auto match = std::find(wait_objects->rbegin(), wait_objects->rend(), object); |
| 230 | return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1); | 276 | return static_cast<s32>(std::distance(match, wait_objects->rend()) - 1); |
| 231 | } | 277 | } |
| 232 | 278 | ||
| 233 | VAddr Thread::GetCommandBufferAddress() const { | 279 | VAddr Thread::GetCommandBufferAddress() const { |
| @@ -236,6 +282,14 @@ VAddr Thread::GetCommandBufferAddress() const { | |||
| 236 | return GetTLSAddress() + command_header_offset; | 282 | return GetTLSAddress() + command_header_offset; |
| 237 | } | 283 | } |
| 238 | 284 | ||
| 285 | Core::ARM_Interface& Thread::ArmInterface() { | ||
| 286 | return *arm_interface; | ||
| 287 | } | ||
| 288 | |||
| 289 | const Core::ARM_Interface& Thread::ArmInterface() const { | ||
| 290 | return *arm_interface; | ||
| 291 | } | ||
| 292 | |||
| 239 | void Thread::SetStatus(ThreadStatus new_status) { | 293 | void Thread::SetStatus(ThreadStatus new_status) { |
| 240 | if (new_status == status) { | 294 | if (new_status == status) { |
| 241 | return; | 295 | return; |
| @@ -257,10 +311,6 @@ void Thread::SetStatus(ThreadStatus new_status) { | |||
| 257 | break; | 311 | break; |
| 258 | } | 312 | } |
| 259 | 313 | ||
| 260 | if (status == ThreadStatus::Running) { | ||
| 261 | last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks(); | ||
| 262 | } | ||
| 263 | |||
| 264 | status = new_status; | 314 | status = new_status; |
| 265 | } | 315 | } |
| 266 | 316 | ||
| @@ -341,75 +391,116 @@ void Thread::UpdatePriority() { | |||
| 341 | lock_owner->UpdatePriority(); | 391 | lock_owner->UpdatePriority(); |
| 342 | } | 392 | } |
| 343 | 393 | ||
| 344 | void Thread::ChangeCore(u32 core, u64 mask) { | ||
| 345 | SetCoreAndAffinityMask(core, mask); | ||
| 346 | } | ||
| 347 | |||
| 348 | bool Thread::AllSynchronizationObjectsReady() const { | 394 | bool Thread::AllSynchronizationObjectsReady() const { |
| 349 | return std::none_of(wait_objects.begin(), wait_objects.end(), | 395 | return std::none_of(wait_objects->begin(), wait_objects->end(), |
| 350 | [this](const std::shared_ptr<SynchronizationObject>& object) { | 396 | [this](const std::shared_ptr<SynchronizationObject>& object) { |
| 351 | return object->ShouldWait(this); | 397 | return object->ShouldWait(this); |
| 352 | }); | 398 | }); |
| 353 | } | 399 | } |
| 354 | 400 | ||
| 355 | bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | 401 | bool Thread::InvokeHLECallback(std::shared_ptr<Thread> thread) { |
| 356 | std::shared_ptr<SynchronizationObject> object, | 402 | ASSERT(hle_callback); |
| 357 | std::size_t index) { | 403 | return hle_callback(std::move(thread)); |
| 358 | ASSERT(wakeup_callback); | ||
| 359 | return wakeup_callback(reason, std::move(thread), std::move(object), index); | ||
| 360 | } | 404 | } |
| 361 | 405 | ||
| 362 | void Thread::SetActivity(ThreadActivity value) { | 406 | ResultCode Thread::SetActivity(ThreadActivity value) { |
| 363 | activity = value; | 407 | SchedulerLock lock(kernel); |
| 408 | |||
| 409 | auto sched_status = GetSchedulingStatus(); | ||
| 410 | |||
| 411 | if (sched_status != ThreadSchedStatus::Runnable && sched_status != ThreadSchedStatus::Paused) { | ||
| 412 | return ERR_INVALID_STATE; | ||
| 413 | } | ||
| 414 | |||
| 415 | if (IsPendingTermination()) { | ||
| 416 | return RESULT_SUCCESS; | ||
| 417 | } | ||
| 364 | 418 | ||
| 365 | if (value == ThreadActivity::Paused) { | 419 | if (value == ThreadActivity::Paused) { |
| 366 | // Set status if not waiting | 420 | if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) != 0) { |
| 367 | if (status == ThreadStatus::Ready || status == ThreadStatus::Running) { | 421 | return ERR_INVALID_STATE; |
| 368 | SetStatus(ThreadStatus::Paused); | 422 | } |
| 369 | kernel.PrepareReschedule(processor_id); | 423 | AddSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); |
| 424 | } else { | ||
| 425 | if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) == 0) { | ||
| 426 | return ERR_INVALID_STATE; | ||
| 370 | } | 427 | } |
| 371 | } else if (status == ThreadStatus::Paused) { | 428 | RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); |
| 372 | // Ready to reschedule | ||
| 373 | ResumeFromWait(); | ||
| 374 | } | 429 | } |
| 430 | return RESULT_SUCCESS; | ||
| 375 | } | 431 | } |
| 376 | 432 | ||
| 377 | void Thread::Sleep(s64 nanoseconds) { | 433 | ResultCode Thread::Sleep(s64 nanoseconds) { |
| 378 | // Sleep current thread and check for next thread to schedule | 434 | Handle event_handle{}; |
| 379 | SetStatus(ThreadStatus::WaitSleep); | 435 | { |
| 436 | SchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); | ||
| 437 | SetStatus(ThreadStatus::WaitSleep); | ||
| 438 | } | ||
| 380 | 439 | ||
| 381 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 440 | if (event_handle != InvalidHandle) { |
| 382 | WakeAfterDelay(nanoseconds); | 441 | auto& time_manager = kernel.TimeManager(); |
| 442 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 443 | } | ||
| 444 | return RESULT_SUCCESS; | ||
| 445 | } | ||
| 446 | |||
| 447 | std::pair<ResultCode, bool> Thread::YieldSimple() { | ||
| 448 | bool is_redundant = false; | ||
| 449 | { | ||
| 450 | SchedulerLock lock(kernel); | ||
| 451 | is_redundant = kernel.GlobalScheduler().YieldThread(this); | ||
| 452 | } | ||
| 453 | return {RESULT_SUCCESS, is_redundant}; | ||
| 454 | } | ||
| 455 | |||
| 456 | std::pair<ResultCode, bool> Thread::YieldAndBalanceLoad() { | ||
| 457 | bool is_redundant = false; | ||
| 458 | { | ||
| 459 | SchedulerLock lock(kernel); | ||
| 460 | is_redundant = kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this); | ||
| 461 | } | ||
| 462 | return {RESULT_SUCCESS, is_redundant}; | ||
| 383 | } | 463 | } |
| 384 | 464 | ||
| 385 | bool Thread::YieldSimple() { | 465 | std::pair<ResultCode, bool> Thread::YieldAndWaitForLoadBalancing() { |
| 386 | auto& scheduler = kernel.GlobalScheduler(); | 466 | bool is_redundant = false; |
| 387 | return scheduler.YieldThread(this); | 467 | { |
| 468 | SchedulerLock lock(kernel); | ||
| 469 | is_redundant = kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this); | ||
| 470 | } | ||
| 471 | return {RESULT_SUCCESS, is_redundant}; | ||
| 388 | } | 472 | } |
| 389 | 473 | ||
| 390 | bool Thread::YieldAndBalanceLoad() { | 474 | void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { |
| 391 | auto& scheduler = kernel.GlobalScheduler(); | 475 | const u32 old_state = scheduling_state; |
| 392 | return scheduler.YieldThreadAndBalanceLoad(this); | 476 | pausing_state |= static_cast<u32>(flag); |
| 477 | const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); | ||
| 478 | scheduling_state = base_scheduling | pausing_state; | ||
| 479 | kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state); | ||
| 393 | } | 480 | } |
| 394 | 481 | ||
| 395 | bool Thread::YieldAndWaitForLoadBalancing() { | 482 | void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) { |
| 396 | auto& scheduler = kernel.GlobalScheduler(); | 483 | const u32 old_state = scheduling_state; |
| 397 | return scheduler.YieldThreadAndWaitForLoadBalancing(this); | 484 | pausing_state &= ~static_cast<u32>(flag); |
| 485 | const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); | ||
| 486 | scheduling_state = base_scheduling | pausing_state; | ||
| 487 | kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state); | ||
| 398 | } | 488 | } |
| 399 | 489 | ||
| 400 | void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { | 490 | void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { |
| 401 | const u32 old_flags = scheduling_state; | 491 | const u32 old_state = scheduling_state; |
| 402 | scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) | | 492 | scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) | |
| 403 | static_cast<u32>(new_status); | 493 | static_cast<u32>(new_status); |
| 404 | AdjustSchedulingOnStatus(old_flags); | 494 | kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state); |
| 405 | } | 495 | } |
| 406 | 496 | ||
| 407 | void Thread::SetCurrentPriority(u32 new_priority) { | 497 | void Thread::SetCurrentPriority(u32 new_priority) { |
| 408 | const u32 old_priority = std::exchange(current_priority, new_priority); | 498 | const u32 old_priority = std::exchange(current_priority, new_priority); |
| 409 | AdjustSchedulingOnPriority(old_priority); | 499 | kernel.GlobalScheduler().AdjustSchedulingOnPriority(this, old_priority); |
| 410 | } | 500 | } |
| 411 | 501 | ||
| 412 | ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { | 502 | ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { |
| 503 | SchedulerLock lock(kernel); | ||
| 413 | const auto HighestSetCore = [](u64 mask, u32 max_cores) { | 504 | const auto HighestSetCore = [](u64 mask, u32 max_cores) { |
| 414 | for (s32 core = static_cast<s32>(max_cores - 1); core >= 0; core--) { | 505 | for (s32 core = static_cast<s32>(max_cores - 1); core >= 0; core--) { |
| 415 | if (((mask >> core) & 1) != 0) { | 506 | if (((mask >> core) & 1) != 0) { |
| @@ -443,111 +534,12 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { | |||
| 443 | processor_id = ideal_core; | 534 | processor_id = ideal_core; |
| 444 | } | 535 | } |
| 445 | } | 536 | } |
| 446 | AdjustSchedulingOnAffinity(old_affinity_mask, old_core); | 537 | kernel.GlobalScheduler().AdjustSchedulingOnAffinity(this, old_affinity_mask, old_core); |
| 447 | } | 538 | } |
| 448 | } | 539 | } |
| 449 | return RESULT_SUCCESS; | 540 | return RESULT_SUCCESS; |
| 450 | } | 541 | } |
| 451 | 542 | ||
| 452 | void Thread::AdjustSchedulingOnStatus(u32 old_flags) { | ||
| 453 | if (old_flags == scheduling_state) { | ||
| 454 | return; | ||
| 455 | } | ||
| 456 | |||
| 457 | auto& scheduler = kernel.GlobalScheduler(); | ||
| 458 | if (static_cast<ThreadSchedStatus>(old_flags & static_cast<u32>(ThreadSchedMasks::LowMask)) == | ||
| 459 | ThreadSchedStatus::Runnable) { | ||
| 460 | // In this case the thread was running, now it's pausing/exitting | ||
| 461 | if (processor_id >= 0) { | ||
| 462 | scheduler.Unschedule(current_priority, static_cast<u32>(processor_id), this); | ||
| 463 | } | ||
| 464 | |||
| 465 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 466 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 467 | scheduler.Unsuggest(current_priority, core, this); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } else if (GetSchedulingStatus() == ThreadSchedStatus::Runnable) { | ||
| 471 | // The thread is now set to running from being stopped | ||
| 472 | if (processor_id >= 0) { | ||
| 473 | scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this); | ||
| 474 | } | ||
| 475 | |||
| 476 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 477 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 478 | scheduler.Suggest(current_priority, core, this); | ||
| 479 | } | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | scheduler.SetReselectionPending(); | ||
| 484 | } | ||
| 485 | |||
| 486 | void Thread::AdjustSchedulingOnPriority(u32 old_priority) { | ||
| 487 | if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) { | ||
| 488 | return; | ||
| 489 | } | ||
| 490 | auto& scheduler = kernel.GlobalScheduler(); | ||
| 491 | if (processor_id >= 0) { | ||
| 492 | scheduler.Unschedule(old_priority, static_cast<u32>(processor_id), this); | ||
| 493 | } | ||
| 494 | |||
| 495 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 496 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 497 | scheduler.Unsuggest(old_priority, core, this); | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 501 | // Add thread to the new priority queues. | ||
| 502 | Thread* current_thread = GetCurrentThread(); | ||
| 503 | |||
| 504 | if (processor_id >= 0) { | ||
| 505 | if (current_thread == this) { | ||
| 506 | scheduler.SchedulePrepend(current_priority, static_cast<u32>(processor_id), this); | ||
| 507 | } else { | ||
| 508 | scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this); | ||
| 509 | } | ||
| 510 | } | ||
| 511 | |||
| 512 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 513 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | ||
| 514 | scheduler.Suggest(current_priority, core, this); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | scheduler.SetReselectionPending(); | ||
| 519 | } | ||
| 520 | |||
| 521 | void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) { | ||
| 522 | auto& scheduler = kernel.GlobalScheduler(); | ||
| 523 | if (GetSchedulingStatus() != ThreadSchedStatus::Runnable || | ||
| 524 | current_priority >= THREADPRIO_COUNT) { | ||
| 525 | return; | ||
| 526 | } | ||
| 527 | |||
| 528 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 529 | if (((old_affinity_mask >> core) & 1) != 0) { | ||
| 530 | if (core == static_cast<u32>(old_core)) { | ||
| 531 | scheduler.Unschedule(current_priority, core, this); | ||
| 532 | } else { | ||
| 533 | scheduler.Unsuggest(current_priority, core, this); | ||
| 534 | } | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | ||
| 539 | if (((affinity_mask >> core) & 1) != 0) { | ||
| 540 | if (core == static_cast<u32>(processor_id)) { | ||
| 541 | scheduler.Schedule(current_priority, core, this); | ||
| 542 | } else { | ||
| 543 | scheduler.Suggest(current_priority, core, this); | ||
| 544 | } | ||
| 545 | } | ||
| 546 | } | ||
| 547 | |||
| 548 | scheduler.SetReselectionPending(); | ||
| 549 | } | ||
| 550 | |||
| 551 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 543 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 552 | 544 | ||
| 553 | /** | 545 | /** |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 23fdef8a4..c0342c462 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -6,26 +6,47 @@ | |||
| 6 | 6 | ||
| 7 | #include <functional> | 7 | #include <functional> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <utility> | ||
| 9 | #include <vector> | 10 | #include <vector> |
| 10 | 11 | ||
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/spin_lock.h" | ||
| 12 | #include "core/arm/arm_interface.h" | 14 | #include "core/arm/arm_interface.h" |
| 13 | #include "core/hle/kernel/object.h" | 15 | #include "core/hle/kernel/object.h" |
| 14 | #include "core/hle/kernel/synchronization_object.h" | 16 | #include "core/hle/kernel/synchronization_object.h" |
| 15 | #include "core/hle/result.h" | 17 | #include "core/hle/result.h" |
| 16 | 18 | ||
| 19 | namespace Common { | ||
| 20 | class Fiber; | ||
| 21 | } | ||
| 22 | |||
| 23 | namespace Core { | ||
| 24 | class ARM_Interface; | ||
| 25 | class System; | ||
| 26 | } // namespace Core | ||
| 27 | |||
| 17 | namespace Kernel { | 28 | namespace Kernel { |
| 18 | 29 | ||
| 30 | class GlobalScheduler; | ||
| 19 | class KernelCore; | 31 | class KernelCore; |
| 20 | class Process; | 32 | class Process; |
| 21 | class Scheduler; | 33 | class Scheduler; |
| 22 | 34 | ||
| 23 | enum ThreadPriority : u32 { | 35 | enum ThreadPriority : u32 { |
| 24 | THREADPRIO_HIGHEST = 0, ///< Highest thread priority | 36 | THREADPRIO_HIGHEST = 0, ///< Highest thread priority |
| 25 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps | 37 | THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration |
| 26 | THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps | 38 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps |
| 27 | THREADPRIO_LOWEST = 63, ///< Lowest thread priority | 39 | THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps |
| 28 | THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. | 40 | THREADPRIO_LOWEST = 63, ///< Lowest thread priority |
| 41 | THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. | ||
| 42 | }; | ||
| 43 | |||
| 44 | enum ThreadType : u32 { | ||
| 45 | THREADTYPE_USER = 0x1, | ||
| 46 | THREADTYPE_KERNEL = 0x2, | ||
| 47 | THREADTYPE_HLE = 0x4, | ||
| 48 | THREADTYPE_IDLE = 0x8, | ||
| 49 | THREADTYPE_SUSPEND = 0x10, | ||
| 29 | }; | 50 | }; |
| 30 | 51 | ||
| 31 | enum ThreadProcessorId : s32 { | 52 | enum ThreadProcessorId : s32 { |
| @@ -107,26 +128,45 @@ public: | |||
| 107 | 128 | ||
| 108 | using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>; | 129 | using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>; |
| 109 | 130 | ||
| 110 | using WakeupCallback = | 131 | using HLECallback = std::function<bool(std::shared_ptr<Thread> thread)>; |
| 111 | std::function<bool(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | 132 | |
| 112 | std::shared_ptr<SynchronizationObject> object, std::size_t index)>; | 133 | /** |
| 134 | * Creates and returns a new thread. The new thread is immediately scheduled | ||
| 135 | * @param system The instance of the whole system | ||
| 136 | * @param name The friendly name desired for the thread | ||
| 137 | * @param entry_point The address at which the thread should start execution | ||
| 138 | * @param priority The thread's priority | ||
| 139 | * @param arg User data to pass to the thread | ||
| 140 | * @param processor_id The ID(s) of the processors on which the thread is desired to be run | ||
| 141 | * @param stack_top The address of the thread's stack top | ||
| 142 | * @param owner_process The parent process for the thread, if null, it's a kernel thread | ||
| 143 | * @return A shared pointer to the newly created thread | ||
| 144 | */ | ||
| 145 | static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags, | ||
| 146 | std::string name, VAddr entry_point, | ||
| 147 | u32 priority, u64 arg, s32 processor_id, | ||
| 148 | VAddr stack_top, Process* owner_process); | ||
| 113 | 149 | ||
| 114 | /** | 150 | /** |
| 115 | * Creates and returns a new thread. The new thread is immediately scheduled | 151 | * Creates and returns a new thread. The new thread is immediately scheduled |
| 116 | * @param kernel The kernel instance this thread will be created under. | 152 | * @param system The instance of the whole system |
| 117 | * @param name The friendly name desired for the thread | 153 | * @param name The friendly name desired for the thread |
| 118 | * @param entry_point The address at which the thread should start execution | 154 | * @param entry_point The address at which the thread should start execution |
| 119 | * @param priority The thread's priority | 155 | * @param priority The thread's priority |
| 120 | * @param arg User data to pass to the thread | 156 | * @param arg User data to pass to the thread |
| 121 | * @param processor_id The ID(s) of the processors on which the thread is desired to be run | 157 | * @param processor_id The ID(s) of the processors on which the thread is desired to be run |
| 122 | * @param stack_top The address of the thread's stack top | 158 | * @param stack_top The address of the thread's stack top |
| 123 | * @param owner_process The parent process for the thread | 159 | * @param owner_process The parent process for the thread, if null, it's a kernel thread |
| 160 | * @param thread_start_func The function where the host context will start. | ||
| 161 | * @param thread_start_parameter The parameter which will passed to host context on init | ||
| 124 | * @return A shared pointer to the newly created thread | 162 | * @return A shared pointer to the newly created thread |
| 125 | */ | 163 | */ |
| 126 | static ResultVal<std::shared_ptr<Thread>> Create(KernelCore& kernel, std::string name, | 164 | static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags, |
| 127 | VAddr entry_point, u32 priority, u64 arg, | 165 | std::string name, VAddr entry_point, |
| 128 | s32 processor_id, VAddr stack_top, | 166 | u32 priority, u64 arg, s32 processor_id, |
| 129 | Process& owner_process); | 167 | VAddr stack_top, Process* owner_process, |
| 168 | std::function<void(void*)>&& thread_start_func, | ||
| 169 | void* thread_start_parameter); | ||
| 130 | 170 | ||
| 131 | std::string GetName() const override { | 171 | std::string GetName() const override { |
| 132 | return name; | 172 | return name; |
| @@ -181,7 +221,7 @@ public: | |||
| 181 | void UpdatePriority(); | 221 | void UpdatePriority(); |
| 182 | 222 | ||
| 183 | /// Changes the core that the thread is running or scheduled to run on. | 223 | /// Changes the core that the thread is running or scheduled to run on. |
| 184 | void ChangeCore(u32 core, u64 mask); | 224 | ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); |
| 185 | 225 | ||
| 186 | /** | 226 | /** |
| 187 | * Gets the thread's thread ID | 227 | * Gets the thread's thread ID |
| @@ -194,6 +234,10 @@ public: | |||
| 194 | /// Resumes a thread from waiting | 234 | /// Resumes a thread from waiting |
| 195 | void ResumeFromWait(); | 235 | void ResumeFromWait(); |
| 196 | 236 | ||
| 237 | void OnWakeUp(); | ||
| 238 | |||
| 239 | ResultCode Start(); | ||
| 240 | |||
| 197 | /// Cancels a waiting operation that this thread may or may not be within. | 241 | /// Cancels a waiting operation that this thread may or may not be within. |
| 198 | /// | 242 | /// |
| 199 | /// When the thread is within a waiting state, this will set the thread's | 243 | /// When the thread is within a waiting state, this will set the thread's |
| @@ -202,26 +246,19 @@ public: | |||
| 202 | /// | 246 | /// |
| 203 | void CancelWait(); | 247 | void CancelWait(); |
| 204 | 248 | ||
| 205 | /** | 249 | void SetSynchronizationResults(SynchronizationObject* object, ResultCode result); |
| 206 | * Schedules an event to wake up the specified thread after the specified delay | ||
| 207 | * @param nanoseconds The time this thread will be allowed to sleep for | ||
| 208 | */ | ||
| 209 | void WakeAfterDelay(s64 nanoseconds); | ||
| 210 | 250 | ||
| 211 | /// Cancel any outstanding wakeup events for this thread | 251 | Core::ARM_Interface& ArmInterface(); |
| 212 | void CancelWakeupTimer(); | ||
| 213 | 252 | ||
| 214 | /** | 253 | const Core::ARM_Interface& ArmInterface() const; |
| 215 | * Sets the result after the thread awakens (from svcWaitSynchronization) | ||
| 216 | * @param result Value to set to the returned result | ||
| 217 | */ | ||
| 218 | void SetWaitSynchronizationResult(ResultCode result); | ||
| 219 | 254 | ||
| 220 | /** | 255 | SynchronizationObject* GetSignalingObject() const { |
| 221 | * Sets the output parameter value after the thread awakens (from svcWaitSynchronization) | 256 | return signaling_object; |
| 222 | * @param output Value to set to the output parameter | 257 | } |
| 223 | */ | 258 | |
| 224 | void SetWaitSynchronizationOutput(s32 output); | 259 | ResultCode GetSignalingResult() const { |
| 260 | return signaling_result; | ||
| 261 | } | ||
| 225 | 262 | ||
| 226 | /** | 263 | /** |
| 227 | * Retrieves the index that this particular object occupies in the list of objects | 264 | * Retrieves the index that this particular object occupies in the list of objects |
| @@ -269,11 +306,6 @@ public: | |||
| 269 | */ | 306 | */ |
| 270 | VAddr GetCommandBufferAddress() const; | 307 | VAddr GetCommandBufferAddress() const; |
| 271 | 308 | ||
| 272 | /// Returns whether this thread is waiting on objects from a WaitSynchronization call. | ||
| 273 | bool IsSleepingOnWait() const { | ||
| 274 | return status == ThreadStatus::WaitSynch; | ||
| 275 | } | ||
| 276 | |||
| 277 | ThreadContext32& GetContext32() { | 309 | ThreadContext32& GetContext32() { |
| 278 | return context_32; | 310 | return context_32; |
| 279 | } | 311 | } |
| @@ -290,6 +322,28 @@ public: | |||
| 290 | return context_64; | 322 | return context_64; |
| 291 | } | 323 | } |
| 292 | 324 | ||
| 325 | bool IsHLEThread() const { | ||
| 326 | return (type & THREADTYPE_HLE) != 0; | ||
| 327 | } | ||
| 328 | |||
| 329 | bool IsSuspendThread() const { | ||
| 330 | return (type & THREADTYPE_SUSPEND) != 0; | ||
| 331 | } | ||
| 332 | |||
| 333 | bool IsIdleThread() const { | ||
| 334 | return (type & THREADTYPE_IDLE) != 0; | ||
| 335 | } | ||
| 336 | |||
| 337 | bool WasRunning() const { | ||
| 338 | return was_running; | ||
| 339 | } | ||
| 340 | |||
| 341 | void SetWasRunning(bool value) { | ||
| 342 | was_running = value; | ||
| 343 | } | ||
| 344 | |||
| 345 | std::shared_ptr<Common::Fiber>& GetHostContext(); | ||
| 346 | |||
| 293 | ThreadStatus GetStatus() const { | 347 | ThreadStatus GetStatus() const { |
| 294 | return status; | 348 | return status; |
| 295 | } | 349 | } |
| @@ -325,18 +379,18 @@ public: | |||
| 325 | } | 379 | } |
| 326 | 380 | ||
| 327 | const ThreadSynchronizationObjects& GetSynchronizationObjects() const { | 381 | const ThreadSynchronizationObjects& GetSynchronizationObjects() const { |
| 328 | return wait_objects; | 382 | return *wait_objects; |
| 329 | } | 383 | } |
| 330 | 384 | ||
| 331 | void SetSynchronizationObjects(ThreadSynchronizationObjects objects) { | 385 | void SetSynchronizationObjects(ThreadSynchronizationObjects* objects) { |
| 332 | wait_objects = std::move(objects); | 386 | wait_objects = objects; |
| 333 | } | 387 | } |
| 334 | 388 | ||
| 335 | void ClearSynchronizationObjects() { | 389 | void ClearSynchronizationObjects() { |
| 336 | for (const auto& waiting_object : wait_objects) { | 390 | for (const auto& waiting_object : *wait_objects) { |
| 337 | waiting_object->RemoveWaitingThread(SharedFrom(this)); | 391 | waiting_object->RemoveWaitingThread(SharedFrom(this)); |
| 338 | } | 392 | } |
| 339 | wait_objects.clear(); | 393 | wait_objects->clear(); |
| 340 | } | 394 | } |
| 341 | 395 | ||
| 342 | /// Determines whether all the objects this thread is waiting on are ready. | 396 | /// Determines whether all the objects this thread is waiting on are ready. |
| @@ -386,26 +440,35 @@ public: | |||
| 386 | arb_wait_address = address; | 440 | arb_wait_address = address; |
| 387 | } | 441 | } |
| 388 | 442 | ||
| 389 | bool HasWakeupCallback() const { | 443 | bool HasHLECallback() const { |
| 390 | return wakeup_callback != nullptr; | 444 | return hle_callback != nullptr; |
| 391 | } | 445 | } |
| 392 | 446 | ||
| 393 | void SetWakeupCallback(WakeupCallback callback) { | 447 | void SetHLECallback(HLECallback callback) { |
| 394 | wakeup_callback = std::move(callback); | 448 | hle_callback = std::move(callback); |
| 395 | } | 449 | } |
| 396 | 450 | ||
| 397 | void InvalidateWakeupCallback() { | 451 | void SetHLETimeEvent(Handle time_event) { |
| 398 | SetWakeupCallback(nullptr); | 452 | hle_time_event = time_event; |
| 399 | } | 453 | } |
| 400 | 454 | ||
| 401 | /** | 455 | void SetHLESyncObject(SynchronizationObject* object) { |
| 402 | * Invokes the thread's wakeup callback. | 456 | hle_object = object; |
| 403 | * | 457 | } |
| 404 | * @pre A valid wakeup callback has been set. Violating this precondition | 458 | |
| 405 | * will cause an assertion to trigger. | 459 | Handle GetHLETimeEvent() const { |
| 406 | */ | 460 | return hle_time_event; |
| 407 | bool InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | 461 | } |
| 408 | std::shared_ptr<SynchronizationObject> object, std::size_t index); | 462 | |
| 463 | SynchronizationObject* GetHLESyncObject() const { | ||
| 464 | return hle_object; | ||
| 465 | } | ||
| 466 | |||
| 467 | void InvalidateHLECallback() { | ||
| 468 | SetHLECallback(nullptr); | ||
| 469 | } | ||
| 470 | |||
| 471 | bool InvokeHLECallback(std::shared_ptr<Thread> thread); | ||
| 409 | 472 | ||
| 410 | u32 GetIdealCore() const { | 473 | u32 GetIdealCore() const { |
| 411 | return ideal_core; | 474 | return ideal_core; |
| @@ -415,23 +478,19 @@ public: | |||
| 415 | return affinity_mask; | 478 | return affinity_mask; |
| 416 | } | 479 | } |
| 417 | 480 | ||
| 418 | ThreadActivity GetActivity() const { | 481 | ResultCode SetActivity(ThreadActivity value); |
| 419 | return activity; | ||
| 420 | } | ||
| 421 | |||
| 422 | void SetActivity(ThreadActivity value); | ||
| 423 | 482 | ||
| 424 | /// Sleeps this thread for the given amount of nanoseconds. | 483 | /// Sleeps this thread for the given amount of nanoseconds. |
| 425 | void Sleep(s64 nanoseconds); | 484 | ResultCode Sleep(s64 nanoseconds); |
| 426 | 485 | ||
| 427 | /// Yields this thread without rebalancing loads. | 486 | /// Yields this thread without rebalancing loads. |
| 428 | bool YieldSimple(); | 487 | std::pair<ResultCode, bool> YieldSimple(); |
| 429 | 488 | ||
| 430 | /// Yields this thread and does a load rebalancing. | 489 | /// Yields this thread and does a load rebalancing. |
| 431 | bool YieldAndBalanceLoad(); | 490 | std::pair<ResultCode, bool> YieldAndBalanceLoad(); |
| 432 | 491 | ||
| 433 | /// Yields this thread and if the core is left idle, loads are rebalanced | 492 | /// Yields this thread and if the core is left idle, loads are rebalanced |
| 434 | bool YieldAndWaitForLoadBalancing(); | 493 | std::pair<ResultCode, bool> YieldAndWaitForLoadBalancing(); |
| 435 | 494 | ||
| 436 | void IncrementYieldCount() { | 495 | void IncrementYieldCount() { |
| 437 | yield_count++; | 496 | yield_count++; |
| @@ -446,6 +505,10 @@ public: | |||
| 446 | static_cast<u32>(ThreadSchedMasks::LowMask)); | 505 | static_cast<u32>(ThreadSchedMasks::LowMask)); |
| 447 | } | 506 | } |
| 448 | 507 | ||
| 508 | bool IsRunnable() const { | ||
| 509 | return scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable); | ||
| 510 | } | ||
| 511 | |||
| 449 | bool IsRunning() const { | 512 | bool IsRunning() const { |
| 450 | return is_running; | 513 | return is_running; |
| 451 | } | 514 | } |
| @@ -466,17 +529,67 @@ public: | |||
| 466 | return global_handle; | 529 | return global_handle; |
| 467 | } | 530 | } |
| 468 | 531 | ||
| 532 | bool IsWaitingForArbitration() const { | ||
| 533 | return waiting_for_arbitration; | ||
| 534 | } | ||
| 535 | |||
| 536 | void WaitForArbitration(bool set) { | ||
| 537 | waiting_for_arbitration = set; | ||
| 538 | } | ||
| 539 | |||
| 540 | bool IsWaitingSync() const { | ||
| 541 | return is_waiting_on_sync; | ||
| 542 | } | ||
| 543 | |||
| 544 | void SetWaitingSync(bool is_waiting) { | ||
| 545 | is_waiting_on_sync = is_waiting; | ||
| 546 | } | ||
| 547 | |||
| 548 | bool IsPendingTermination() const { | ||
| 549 | return will_be_terminated || GetSchedulingStatus() == ThreadSchedStatus::Exited; | ||
| 550 | } | ||
| 551 | |||
| 552 | bool IsPaused() const { | ||
| 553 | return pausing_state != 0; | ||
| 554 | } | ||
| 555 | |||
| 556 | bool IsContinuousOnSVC() const { | ||
| 557 | return is_continuous_on_svc; | ||
| 558 | } | ||
| 559 | |||
| 560 | void SetContinuousOnSVC(bool is_continuous) { | ||
| 561 | is_continuous_on_svc = is_continuous; | ||
| 562 | } | ||
| 563 | |||
| 564 | bool IsPhantomMode() const { | ||
| 565 | return is_phantom_mode; | ||
| 566 | } | ||
| 567 | |||
| 568 | void SetPhantomMode(bool phantom) { | ||
| 569 | is_phantom_mode = phantom; | ||
| 570 | } | ||
| 571 | |||
| 572 | bool HasExited() const { | ||
| 573 | return has_exited; | ||
| 574 | } | ||
| 575 | |||
| 469 | private: | 576 | private: |
| 577 | friend class GlobalScheduler; | ||
| 578 | friend class Scheduler; | ||
| 579 | |||
| 470 | void SetSchedulingStatus(ThreadSchedStatus new_status); | 580 | void SetSchedulingStatus(ThreadSchedStatus new_status); |
| 581 | void AddSchedulingFlag(ThreadSchedFlags flag); | ||
| 582 | void RemoveSchedulingFlag(ThreadSchedFlags flag); | ||
| 583 | |||
| 471 | void SetCurrentPriority(u32 new_priority); | 584 | void SetCurrentPriority(u32 new_priority); |
| 472 | ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); | ||
| 473 | 585 | ||
| 474 | void AdjustSchedulingOnStatus(u32 old_flags); | ||
| 475 | void AdjustSchedulingOnPriority(u32 old_priority); | ||
| 476 | void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); | 586 | void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); |
| 477 | 587 | ||
| 588 | Common::SpinLock context_guard{}; | ||
| 478 | ThreadContext32 context_32{}; | 589 | ThreadContext32 context_32{}; |
| 479 | ThreadContext64 context_64{}; | 590 | ThreadContext64 context_64{}; |
| 591 | std::unique_ptr<Core::ARM_Interface> arm_interface{}; | ||
| 592 | std::shared_ptr<Common::Fiber> host_context{}; | ||
| 480 | 593 | ||
| 481 | u64 thread_id = 0; | 594 | u64 thread_id = 0; |
| 482 | 595 | ||
| @@ -485,6 +598,8 @@ private: | |||
| 485 | VAddr entry_point = 0; | 598 | VAddr entry_point = 0; |
| 486 | VAddr stack_top = 0; | 599 | VAddr stack_top = 0; |
| 487 | 600 | ||
| 601 | ThreadType type; | ||
| 602 | |||
| 488 | /// Nominal thread priority, as set by the emulated application. | 603 | /// Nominal thread priority, as set by the emulated application. |
| 489 | /// The nominal priority is the thread priority without priority | 604 | /// The nominal priority is the thread priority without priority |
| 490 | /// inheritance taken into account. | 605 | /// inheritance taken into account. |
| @@ -509,7 +624,10 @@ private: | |||
| 509 | 624 | ||
| 510 | /// Objects that the thread is waiting on, in the same order as they were | 625 | /// Objects that the thread is waiting on, in the same order as they were |
| 511 | /// passed to WaitSynchronization. | 626 | /// passed to WaitSynchronization. |
| 512 | ThreadSynchronizationObjects wait_objects; | 627 | ThreadSynchronizationObjects* wait_objects; |
| 628 | |||
| 629 | SynchronizationObject* signaling_object; | ||
| 630 | ResultCode signaling_result{RESULT_SUCCESS}; | ||
| 513 | 631 | ||
| 514 | /// List of threads that are waiting for a mutex that is held by this thread. | 632 | /// List of threads that are waiting for a mutex that is held by this thread. |
| 515 | MutexWaitingThreads wait_mutex_threads; | 633 | MutexWaitingThreads wait_mutex_threads; |
| @@ -526,30 +644,39 @@ private: | |||
| 526 | 644 | ||
| 527 | /// If waiting for an AddressArbiter, this is the address being waited on. | 645 | /// If waiting for an AddressArbiter, this is the address being waited on. |
| 528 | VAddr arb_wait_address{0}; | 646 | VAddr arb_wait_address{0}; |
| 647 | bool waiting_for_arbitration{}; | ||
| 529 | 648 | ||
| 530 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. | 649 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. |
| 531 | Handle global_handle = 0; | 650 | Handle global_handle = 0; |
| 532 | 651 | ||
| 533 | /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread | 652 | /// Callback for HLE Events |
| 534 | /// was waiting via WaitSynchronization then the object will be the last object that became | 653 | HLECallback hle_callback; |
| 535 | /// available. In case of a timeout, the object will be nullptr. | 654 | Handle hle_time_event; |
| 536 | WakeupCallback wakeup_callback; | 655 | SynchronizationObject* hle_object; |
| 537 | 656 | ||
| 538 | Scheduler* scheduler = nullptr; | 657 | Scheduler* scheduler = nullptr; |
| 539 | 658 | ||
| 540 | u32 ideal_core{0xFFFFFFFF}; | 659 | u32 ideal_core{0xFFFFFFFF}; |
| 541 | u64 affinity_mask{0x1}; | 660 | u64 affinity_mask{0x1}; |
| 542 | 661 | ||
| 543 | ThreadActivity activity = ThreadActivity::Normal; | ||
| 544 | |||
| 545 | s32 ideal_core_override = -1; | 662 | s32 ideal_core_override = -1; |
| 546 | u64 affinity_mask_override = 0x1; | 663 | u64 affinity_mask_override = 0x1; |
| 547 | u32 affinity_override_count = 0; | 664 | u32 affinity_override_count = 0; |
| 548 | 665 | ||
| 549 | u32 scheduling_state = 0; | 666 | u32 scheduling_state = 0; |
| 667 | u32 pausing_state = 0; | ||
| 550 | bool is_running = false; | 668 | bool is_running = false; |
| 669 | bool is_waiting_on_sync = false; | ||
| 551 | bool is_sync_cancelled = false; | 670 | bool is_sync_cancelled = false; |
| 552 | 671 | ||
| 672 | bool is_continuous_on_svc = false; | ||
| 673 | |||
| 674 | bool will_be_terminated = false; | ||
| 675 | bool is_phantom_mode = false; | ||
| 676 | bool has_exited = false; | ||
| 677 | |||
| 678 | bool was_running = false; | ||
| 679 | |||
| 553 | std::string name; | 680 | std::string name; |
| 554 | }; | 681 | }; |
| 555 | 682 | ||
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 21b290468..941305e8e 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp | |||
| @@ -8,30 +8,37 @@ | |||
| 8 | #include "core/core_timing_util.h" | 8 | #include "core/core_timing_util.h" |
| 9 | #include "core/hle/kernel/handle_table.h" | 9 | #include "core/hle/kernel/handle_table.h" |
| 10 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/kernel/scheduler.h" | ||
| 11 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 12 | #include "core/hle/kernel/time_manager.h" | 13 | #include "core/hle/kernel/time_manager.h" |
| 13 | 14 | ||
| 14 | namespace Kernel { | 15 | namespace Kernel { |
| 15 | 16 | ||
| 16 | TimeManager::TimeManager(Core::System& system) : system{system} { | 17 | TimeManager::TimeManager(Core::System& system_) : system{system_} { |
| 17 | time_manager_event_type = Core::Timing::CreateEvent( | 18 | time_manager_event_type = Core::Timing::CreateEvent( |
| 18 | "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { | 19 | "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { |
| 20 | SchedulerLock lock(system.Kernel()); | ||
| 19 | Handle proper_handle = static_cast<Handle>(thread_handle); | 21 | Handle proper_handle = static_cast<Handle>(thread_handle); |
| 22 | if (cancelled_events[proper_handle]) { | ||
| 23 | return; | ||
| 24 | } | ||
| 20 | std::shared_ptr<Thread> thread = | 25 | std::shared_ptr<Thread> thread = |
| 21 | this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); | 26 | this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); |
| 22 | thread->ResumeFromWait(); | 27 | thread->OnWakeUp(); |
| 23 | }); | 28 | }); |
| 24 | } | 29 | } |
| 25 | 30 | ||
| 26 | void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) { | 31 | void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) { |
| 32 | event_handle = timetask->GetGlobalHandle(); | ||
| 27 | if (nanoseconds > 0) { | 33 | if (nanoseconds > 0) { |
| 28 | ASSERT(timetask); | 34 | ASSERT(timetask); |
| 29 | event_handle = timetask->GetGlobalHandle(); | 35 | ASSERT(timetask->GetStatus() != ThreadStatus::Ready); |
| 30 | const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); | 36 | ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); |
| 31 | system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle); | 37 | system.CoreTiming().ScheduleEvent(nanoseconds, time_manager_event_type, event_handle); |
| 32 | } else { | 38 | } else { |
| 33 | event_handle = InvalidHandle; | 39 | event_handle = InvalidHandle; |
| 34 | } | 40 | } |
| 41 | cancelled_events[event_handle] = false; | ||
| 35 | } | 42 | } |
| 36 | 43 | ||
| 37 | void TimeManager::UnscheduleTimeEvent(Handle event_handle) { | 44 | void TimeManager::UnscheduleTimeEvent(Handle event_handle) { |
| @@ -39,6 +46,12 @@ void TimeManager::UnscheduleTimeEvent(Handle event_handle) { | |||
| 39 | return; | 46 | return; |
| 40 | } | 47 | } |
| 41 | system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle); | 48 | system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle); |
| 49 | cancelled_events[event_handle] = true; | ||
| 50 | } | ||
| 51 | |||
| 52 | void TimeManager::CancelTimeEvent(Thread* time_task) { | ||
| 53 | Handle event_handle = time_task->GetGlobalHandle(); | ||
| 54 | UnscheduleTimeEvent(event_handle); | ||
| 42 | } | 55 | } |
| 43 | 56 | ||
| 44 | } // namespace Kernel | 57 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h index eaec486d1..307a18765 100644 --- a/src/core/hle/kernel/time_manager.h +++ b/src/core/hle/kernel/time_manager.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <unordered_map> | ||
| 8 | 9 | ||
| 9 | #include "core/hle/kernel/object.h" | 10 | #include "core/hle/kernel/object.h" |
| 10 | 11 | ||
| @@ -35,9 +36,12 @@ public: | |||
| 35 | /// Unschedule an existing time event | 36 | /// Unschedule an existing time event |
| 36 | void UnscheduleTimeEvent(Handle event_handle); | 37 | void UnscheduleTimeEvent(Handle event_handle); |
| 37 | 38 | ||
| 39 | void CancelTimeEvent(Thread* time_task); | ||
| 40 | |||
| 38 | private: | 41 | private: |
| 39 | Core::System& system; | 42 | Core::System& system; |
| 40 | std::shared_ptr<Core::Timing::EventType> time_manager_event_type; | 43 | std::shared_ptr<Core::Timing::EventType> time_manager_event_type; |
| 44 | std::unordered_map<Handle, bool> cancelled_events; | ||
| 41 | }; | 45 | }; |
| 42 | 46 | ||
| 43 | } // namespace Kernel | 47 | } // namespace Kernel |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 630a8b048..94d8c1fc6 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -44,6 +44,218 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) { | |||
| 44 | return static_cast<u32>(std::min(size, max_jpeg_image_size)); | 44 | return static_cast<u32>(std::min(size, max_jpeg_image_size)); |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> { | ||
| 48 | public: | ||
| 49 | explicit IManagerForSystemService(Common::UUID user_id) | ||
| 50 | : ServiceFramework("IManagerForSystemService") { | ||
| 51 | // clang-format off | ||
| 52 | static const FunctionInfo functions[] = { | ||
| 53 | {0, nullptr, "CheckAvailability"}, | ||
| 54 | {1, nullptr, "GetAccountId"}, | ||
| 55 | {2, nullptr, "EnsureIdTokenCacheAsync"}, | ||
| 56 | {3, nullptr, "LoadIdTokenCache"}, | ||
| 57 | {100, nullptr, "SetSystemProgramIdentification"}, | ||
| 58 | {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+ | ||
| 59 | {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+ | ||
| 60 | {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+ | ||
| 61 | {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0 | ||
| 62 | {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+ | ||
| 63 | {120, nullptr, "GetNintendoAccountId"}, | ||
| 64 | {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+ | ||
| 65 | {130, nullptr, "GetNintendoAccountUserResourceCache"}, | ||
| 66 | {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"}, | ||
| 67 | {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"}, | ||
| 68 | {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+ | ||
| 69 | {134, nullptr, "RefreshNintendoAccountVerificationUrlCache"}, // 9.0.0+ | ||
| 70 | {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+ | ||
| 71 | {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+ | ||
| 72 | {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+ | ||
| 73 | {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+ | ||
| 74 | {150, nullptr, "CreateAuthorizationRequest"}, | ||
| 75 | }; | ||
| 76 | // clang-format on | ||
| 77 | |||
| 78 | RegisterHandlers(functions); | ||
| 79 | } | ||
| 80 | }; | ||
| 81 | |||
| 82 | // 3.0.0+ | ||
| 83 | class IFloatingRegistrationRequest final : public ServiceFramework<IFloatingRegistrationRequest> { | ||
| 84 | public: | ||
| 85 | explicit IFloatingRegistrationRequest(Common::UUID user_id) | ||
| 86 | : ServiceFramework("IFloatingRegistrationRequest") { | ||
| 87 | // clang-format off | ||
| 88 | static const FunctionInfo functions[] = { | ||
| 89 | {0, nullptr, "GetSessionId"}, | ||
| 90 | {12, nullptr, "GetAccountId"}, | ||
| 91 | {13, nullptr, "GetLinkedNintendoAccountId"}, | ||
| 92 | {14, nullptr, "GetNickname"}, | ||
| 93 | {15, nullptr, "GetProfileImage"}, | ||
| 94 | {21, nullptr, "LoadIdTokenCache"}, | ||
| 95 | {100, nullptr, "RegisterUser"}, // [1.0.0-3.0.2] RegisterAsync | ||
| 96 | {101, nullptr, "RegisterUserWithUid"}, // [1.0.0-3.0.2] RegisterWithUidAsync | ||
| 97 | {102, nullptr, "RegisterNetworkServiceAccountAsync"}, // 4.0.0+ | ||
| 98 | {103, nullptr, "RegisterNetworkServiceAccountWithUidAsync"}, // 4.0.0+ | ||
| 99 | {110, nullptr, "SetSystemProgramIdentification"}, | ||
| 100 | {111, nullptr, "EnsureIdTokenCacheAsync"}, | ||
| 101 | }; | ||
| 102 | // clang-format on | ||
| 103 | |||
| 104 | RegisterHandlers(functions); | ||
| 105 | } | ||
| 106 | }; | ||
| 107 | |||
| 108 | class IAdministrator final : public ServiceFramework<IAdministrator> { | ||
| 109 | public: | ||
| 110 | explicit IAdministrator(Common::UUID user_id) : ServiceFramework("IAdministrator") { | ||
| 111 | // clang-format off | ||
| 112 | static const FunctionInfo functions[] = { | ||
| 113 | {0, nullptr, "CheckAvailability"}, | ||
| 114 | {1, nullptr, "GetAccountId"}, | ||
| 115 | {2, nullptr, "EnsureIdTokenCacheAsync"}, | ||
| 116 | {3, nullptr, "LoadIdTokenCache"}, | ||
| 117 | {100, nullptr, "SetSystemProgramIdentification"}, | ||
| 118 | {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+ | ||
| 119 | {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+ | ||
| 120 | {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+ | ||
| 121 | {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0 | ||
| 122 | {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+ | ||
| 123 | {120, nullptr, "GetNintendoAccountId"}, | ||
| 124 | {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+ | ||
| 125 | {130, nullptr, "GetNintendoAccountUserResourceCache"}, | ||
| 126 | {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"}, | ||
| 127 | {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"}, | ||
| 128 | {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+ | ||
| 129 | {134, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsync"}, // 9.0.0+ | ||
| 130 | {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+ | ||
| 131 | {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+ | ||
| 132 | {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+ | ||
| 133 | {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+ | ||
| 134 | {150, nullptr, "CreateAuthorizationRequest"}, | ||
| 135 | {200, nullptr, "IsRegistered"}, | ||
| 136 | {201, nullptr, "RegisterAsync"}, | ||
| 137 | {202, nullptr, "UnregisterAsync"}, | ||
| 138 | {203, nullptr, "DeleteRegistrationInfoLocally"}, | ||
| 139 | {220, nullptr, "SynchronizeProfileAsync"}, | ||
| 140 | {221, nullptr, "UploadProfileAsync"}, | ||
| 141 | {222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"}, | ||
| 142 | {250, nullptr, "IsLinkedWithNintendoAccount"}, | ||
| 143 | {251, nullptr, "CreateProcedureToLinkWithNintendoAccount"}, | ||
| 144 | {252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"}, | ||
| 145 | {255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"}, | ||
| 146 | {256, nullptr, "ResumeProcedureToUpdateLinkageStateOfNintendoAccount"}, | ||
| 147 | {260, nullptr, "CreateProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+ | ||
| 148 | {261, nullptr, "ResumeProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+ | ||
| 149 | {280, nullptr, "ProxyProcedureToAcquireApplicationAuthorizationForNintendoAccount"}, | ||
| 150 | {290, nullptr, "GetRequestForNintendoAccountUserResourceView"}, // 8.0.0+ | ||
| 151 | {300, nullptr, "TryRecoverNintendoAccountUserStateAsync"}, // 6.0.0+ | ||
| 152 | {400, nullptr, "IsServiceEntryRequirementCacheRefreshRequiredForOnlinePlay"}, // 6.1.0+ | ||
| 153 | {401, nullptr, "RefreshServiceEntryRequirementCacheForOnlinePlayAsync"}, // 6.1.0+ | ||
| 154 | {900, nullptr, "GetAuthenticationInfoForWin"}, // 9.0.0+ | ||
| 155 | {901, nullptr, "ImportAsyncForWin"}, // 9.0.0+ | ||
| 156 | {997, nullptr, "DebugUnlinkNintendoAccountAsync"}, | ||
| 157 | {998, nullptr, "DebugSetAvailabilityErrorDetail"}, | ||
| 158 | }; | ||
| 159 | // clang-format on | ||
| 160 | |||
| 161 | RegisterHandlers(functions); | ||
| 162 | } | ||
| 163 | }; | ||
| 164 | |||
| 165 | class IAuthorizationRequest final : public ServiceFramework<IAuthorizationRequest> { | ||
| 166 | public: | ||
| 167 | explicit IAuthorizationRequest(Common::UUID user_id) | ||
| 168 | : ServiceFramework("IAuthorizationRequest") { | ||
| 169 | // clang-format off | ||
| 170 | static const FunctionInfo functions[] = { | ||
| 171 | {0, nullptr, "GetSessionId"}, | ||
| 172 | {10, nullptr, "InvokeWithoutInteractionAsync"}, | ||
| 173 | {19, nullptr, "IsAuthorized"}, | ||
| 174 | {20, nullptr, "GetAuthorizationCode"}, | ||
| 175 | {21, nullptr, "GetIdToken"}, | ||
| 176 | {22, nullptr, "GetState"}, | ||
| 177 | }; | ||
| 178 | // clang-format on | ||
| 179 | |||
| 180 | RegisterHandlers(functions); | ||
| 181 | } | ||
| 182 | }; | ||
| 183 | |||
| 184 | class IOAuthProcedure final : public ServiceFramework<IOAuthProcedure> { | ||
| 185 | public: | ||
| 186 | explicit IOAuthProcedure(Common::UUID user_id) : ServiceFramework("IOAuthProcedure") { | ||
| 187 | // clang-format off | ||
| 188 | static const FunctionInfo functions[] = { | ||
| 189 | {0, nullptr, "PrepareAsync"}, | ||
| 190 | {1, nullptr, "GetRequest"}, | ||
| 191 | {2, nullptr, "ApplyResponse"}, | ||
| 192 | {3, nullptr, "ApplyResponseAsync"}, | ||
| 193 | {10, nullptr, "Suspend"}, | ||
| 194 | }; | ||
| 195 | // clang-format on | ||
| 196 | |||
| 197 | RegisterHandlers(functions); | ||
| 198 | } | ||
| 199 | }; | ||
| 200 | |||
| 201 | // 3.0.0+ | ||
| 202 | class IOAuthProcedureForExternalNsa final : public ServiceFramework<IOAuthProcedureForExternalNsa> { | ||
| 203 | public: | ||
| 204 | explicit IOAuthProcedureForExternalNsa(Common::UUID user_id) | ||
| 205 | : ServiceFramework("IOAuthProcedureForExternalNsa") { | ||
| 206 | // clang-format off | ||
| 207 | static const FunctionInfo functions[] = { | ||
| 208 | {0, nullptr, "PrepareAsync"}, | ||
| 209 | {1, nullptr, "GetRequest"}, | ||
| 210 | {2, nullptr, "ApplyResponse"}, | ||
| 211 | {3, nullptr, "ApplyResponseAsync"}, | ||
| 212 | {10, nullptr, "Suspend"}, | ||
| 213 | {100, nullptr, "GetAccountId"}, | ||
| 214 | {101, nullptr, "GetLinkedNintendoAccountId"}, | ||
| 215 | {102, nullptr, "GetNickname"}, | ||
| 216 | {103, nullptr, "GetProfileImage"}, | ||
| 217 | }; | ||
| 218 | // clang-format on | ||
| 219 | |||
| 220 | RegisterHandlers(functions); | ||
| 221 | } | ||
| 222 | }; | ||
| 223 | |||
| 224 | class IOAuthProcedureForNintendoAccountLinkage final | ||
| 225 | : public ServiceFramework<IOAuthProcedureForNintendoAccountLinkage> { | ||
| 226 | public: | ||
| 227 | explicit IOAuthProcedureForNintendoAccountLinkage(Common::UUID user_id) | ||
| 228 | : ServiceFramework("IOAuthProcedureForNintendoAccountLinkage") { | ||
| 229 | // clang-format off | ||
| 230 | static const FunctionInfo functions[] = { | ||
| 231 | {0, nullptr, "PrepareAsync"}, | ||
| 232 | {1, nullptr, "GetRequest"}, | ||
| 233 | {2, nullptr, "ApplyResponse"}, | ||
| 234 | {3, nullptr, "ApplyResponseAsync"}, | ||
| 235 | {10, nullptr, "Suspend"}, | ||
| 236 | {100, nullptr, "GetRequestWithTheme"}, | ||
| 237 | {101, nullptr, "IsNetworkServiceAccountReplaced"}, | ||
| 238 | {199, nullptr, "GetUrlForIntroductionOfExtraMembership"}, // 2.0.0 - 5.1.0 | ||
| 239 | }; | ||
| 240 | // clang-format on | ||
| 241 | |||
| 242 | RegisterHandlers(functions); | ||
| 243 | } | ||
| 244 | }; | ||
| 245 | |||
| 246 | class INotifier final : public ServiceFramework<INotifier> { | ||
| 247 | public: | ||
| 248 | explicit INotifier(Common::UUID user_id) : ServiceFramework("INotifier") { | ||
| 249 | // clang-format off | ||
| 250 | static const FunctionInfo functions[] = { | ||
| 251 | {0, nullptr, "GetSystemEvent"}, | ||
| 252 | }; | ||
| 253 | // clang-format on | ||
| 254 | |||
| 255 | RegisterHandlers(functions); | ||
| 256 | } | ||
| 257 | }; | ||
| 258 | |||
| 47 | class IProfileCommon : public ServiceFramework<IProfileCommon> { | 259 | class IProfileCommon : public ServiceFramework<IProfileCommon> { |
| 48 | public: | 260 | public: |
| 49 | explicit IProfileCommon(const char* name, bool editor_commands, Common::UUID user_id, | 261 | explicit IProfileCommon(const char* name, bool editor_commands, Common::UUID user_id, |
| @@ -226,6 +438,54 @@ public: | |||
| 226 | : IProfileCommon("IProfileEditor", true, user_id, profile_manager) {} | 438 | : IProfileCommon("IProfileEditor", true, user_id, profile_manager) {} |
| 227 | }; | 439 | }; |
| 228 | 440 | ||
| 441 | class IAsyncContext final : public ServiceFramework<IAsyncContext> { | ||
| 442 | public: | ||
| 443 | explicit IAsyncContext(Common::UUID user_id) : ServiceFramework("IAsyncContext") { | ||
| 444 | // clang-format off | ||
| 445 | static const FunctionInfo functions[] = { | ||
| 446 | {0, nullptr, "GetSystemEvent"}, | ||
| 447 | {1, nullptr, "Cancel"}, | ||
| 448 | {2, nullptr, "HasDone"}, | ||
| 449 | {3, nullptr, "GetResult"}, | ||
| 450 | }; | ||
| 451 | // clang-format on | ||
| 452 | |||
| 453 | RegisterHandlers(functions); | ||
| 454 | } | ||
| 455 | }; | ||
| 456 | |||
| 457 | class ISessionObject final : public ServiceFramework<ISessionObject> { | ||
| 458 | public: | ||
| 459 | explicit ISessionObject(Common::UUID user_id) : ServiceFramework("ISessionObject") { | ||
| 460 | // clang-format off | ||
| 461 | static const FunctionInfo functions[] = { | ||
| 462 | {999, nullptr, "Dummy"}, | ||
| 463 | }; | ||
| 464 | // clang-format on | ||
| 465 | |||
| 466 | RegisterHandlers(functions); | ||
| 467 | } | ||
| 468 | }; | ||
| 469 | |||
| 470 | class IGuestLoginRequest final : public ServiceFramework<IGuestLoginRequest> { | ||
| 471 | public: | ||
| 472 | explicit IGuestLoginRequest(Common::UUID) : ServiceFramework("IGuestLoginRequest") { | ||
| 473 | // clang-format off | ||
| 474 | static const FunctionInfo functions[] = { | ||
| 475 | {0, nullptr, "GetSessionId"}, | ||
| 476 | {11, nullptr, "Unknown"}, // 1.0.0 - 2.3.0 (the name is blank on Switchbrew) | ||
| 477 | {12, nullptr, "GetAccountId"}, | ||
| 478 | {13, nullptr, "GetLinkedNintendoAccountId"}, | ||
| 479 | {14, nullptr, "GetNickname"}, | ||
| 480 | {15, nullptr, "GetProfileImage"}, | ||
| 481 | {21, nullptr, "LoadIdTokenCache"}, // 3.0.0+ | ||
| 482 | }; | ||
| 483 | // clang-format on | ||
| 484 | |||
| 485 | RegisterHandlers(functions); | ||
| 486 | } | ||
| 487 | }; | ||
| 488 | |||
| 229 | class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { | 489 | class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { |
| 230 | public: | 490 | public: |
| 231 | explicit IManagerForApplication(Common::UUID user_id) | 491 | explicit IManagerForApplication(Common::UUID user_id) |
| @@ -265,6 +525,87 @@ private: | |||
| 265 | Common::UUID user_id; | 525 | Common::UUID user_id; |
| 266 | }; | 526 | }; |
| 267 | 527 | ||
| 528 | // 6.0.0+ | ||
| 529 | class IAsyncNetworkServiceLicenseKindContext final | ||
| 530 | : public ServiceFramework<IAsyncNetworkServiceLicenseKindContext> { | ||
| 531 | public: | ||
| 532 | explicit IAsyncNetworkServiceLicenseKindContext(Common::UUID user_id) | ||
| 533 | : ServiceFramework("IAsyncNetworkServiceLicenseKindContext") { | ||
| 534 | // clang-format off | ||
| 535 | static const FunctionInfo functions[] = { | ||
| 536 | {0, nullptr, "GetSystemEvent"}, | ||
| 537 | {1, nullptr, "Cancel"}, | ||
| 538 | {2, nullptr, "HasDone"}, | ||
| 539 | {3, nullptr, "GetResult"}, | ||
| 540 | {4, nullptr, "GetNetworkServiceLicenseKind"}, | ||
| 541 | }; | ||
| 542 | // clang-format on | ||
| 543 | |||
| 544 | RegisterHandlers(functions); | ||
| 545 | } | ||
| 546 | }; | ||
| 547 | |||
| 548 | // 8.0.0+ | ||
| 549 | class IOAuthProcedureForUserRegistration final | ||
| 550 | : public ServiceFramework<IOAuthProcedureForUserRegistration> { | ||
| 551 | public: | ||
| 552 | explicit IOAuthProcedureForUserRegistration(Common::UUID user_id) | ||
| 553 | : ServiceFramework("IOAuthProcedureForUserRegistration") { | ||
| 554 | // clang-format off | ||
| 555 | static const FunctionInfo functions[] = { | ||
| 556 | {0, nullptr, "PrepareAsync"}, | ||
| 557 | {1, nullptr, "GetRequest"}, | ||
| 558 | {2, nullptr, "ApplyResponse"}, | ||
| 559 | {3, nullptr, "ApplyResponseAsync"}, | ||
| 560 | {10, nullptr, "Suspend"}, | ||
| 561 | {100, nullptr, "GetAccountId"}, | ||
| 562 | {101, nullptr, "GetLinkedNintendoAccountId"}, | ||
| 563 | {102, nullptr, "GetNickname"}, | ||
| 564 | {103, nullptr, "GetProfileImage"}, | ||
| 565 | {110, nullptr, "RegisterUserAsync"}, | ||
| 566 | {111, nullptr, "GetUid"}, | ||
| 567 | }; | ||
| 568 | // clang-format on | ||
| 569 | |||
| 570 | RegisterHandlers(functions); | ||
| 571 | } | ||
| 572 | }; | ||
| 573 | |||
| 574 | class DAUTH_O final : public ServiceFramework<DAUTH_O> { | ||
| 575 | public: | ||
| 576 | explicit DAUTH_O(Common::UUID) : ServiceFramework("dauth:o") { | ||
| 577 | // clang-format off | ||
| 578 | static const FunctionInfo functions[] = { | ||
| 579 | {0, nullptr, "EnsureAuthenticationTokenCacheAsync"}, // [5.0.0-5.1.0] GeneratePostData | ||
| 580 | {1, nullptr, "LoadAuthenticationTokenCache"}, // 6.0.0+ | ||
| 581 | {2, nullptr, "InvalidateAuthenticationTokenCache"}, // 6.0.0+ | ||
| 582 | {10, nullptr, "EnsureEdgeTokenCacheAsync"}, // 6.0.0+ | ||
| 583 | {11, nullptr, "LoadEdgeTokenCache"}, // 6.0.0+ | ||
| 584 | {12, nullptr, "InvalidateEdgeTokenCache"}, // 6.0.0+ | ||
| 585 | }; | ||
| 586 | // clang-format on | ||
| 587 | |||
| 588 | RegisterHandlers(functions); | ||
| 589 | } | ||
| 590 | }; | ||
| 591 | |||
| 592 | // 6.0.0+ | ||
| 593 | class IAsyncResult final : public ServiceFramework<IAsyncResult> { | ||
| 594 | public: | ||
| 595 | explicit IAsyncResult(Common::UUID user_id) : ServiceFramework("IAsyncResult") { | ||
| 596 | // clang-format off | ||
| 597 | static const FunctionInfo functions[] = { | ||
| 598 | {0, nullptr, "GetResult"}, | ||
| 599 | {1, nullptr, "Cancel"}, | ||
| 600 | {2, nullptr, "IsAvailable"}, | ||
| 601 | {3, nullptr, "GetSystemEvent"}, | ||
| 602 | }; | ||
| 603 | // clang-format on | ||
| 604 | |||
| 605 | RegisterHandlers(functions); | ||
| 606 | } | ||
| 607 | }; | ||
| 608 | |||
| 268 | void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { | 609 | void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { |
| 269 | LOG_DEBUG(Service_ACC, "called"); | 610 | LOG_DEBUG(Service_ACC, "called"); |
| 270 | IPC::ResponseBuilder rb{ctx, 3}; | 611 | IPC::ResponseBuilder rb{ctx, 3}; |
diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp index 3bac6bcd1..51f119b12 100644 --- a/src/core/hle/service/acc/acc_aa.cpp +++ b/src/core/hle/service/acc/acc_aa.cpp | |||
| @@ -13,8 +13,8 @@ ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 13 | {0, nullptr, "EnsureCacheAsync"}, | 13 | {0, nullptr, "EnsureCacheAsync"}, |
| 14 | {1, nullptr, "LoadCache"}, | 14 | {1, nullptr, "LoadCache"}, |
| 15 | {2, nullptr, "GetDeviceAccountId"}, | 15 | {2, nullptr, "GetDeviceAccountId"}, |
| 16 | {50, nullptr, "RegisterNotificationTokenAsync"}, | 16 | {50, nullptr, "RegisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0 |
| 17 | {51, nullptr, "UnregisterNotificationTokenAsync"}, | 17 | {51, nullptr, "UnregisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0 |
| 18 | }; | 18 | }; |
| 19 | RegisterHandlers(functions); | 19 | RegisterHandlers(functions); |
| 20 | } | 20 | } |
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 2eefc6df5..85620bde3 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp | |||
| @@ -17,28 +17,28 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 17 | {3, &ACC_SU::ListOpenUsers, "ListOpenUsers"}, | 17 | {3, &ACC_SU::ListOpenUsers, "ListOpenUsers"}, |
| 18 | {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"}, | 18 | {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"}, |
| 19 | {5, &ACC_SU::GetProfile, "GetProfile"}, | 19 | {5, &ACC_SU::GetProfile, "GetProfile"}, |
| 20 | {6, nullptr, "GetProfileDigest"}, | 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ |
| 21 | {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 21 | {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 22 | {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 22 | {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 23 | {60, nullptr, "ListOpenContextStoredUsers"}, | 23 | {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 |
| 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, | 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ |
| 25 | {100, nullptr, "GetUserRegistrationNotifier"}, | 25 | {100, nullptr, "GetUserRegistrationNotifier"}, |
| 26 | {101, nullptr, "GetUserStateChangeNotifier"}, | 26 | {101, nullptr, "GetUserStateChangeNotifier"}, |
| 27 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, | 27 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, |
| 28 | {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, | 28 | {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, |
| 29 | {104, nullptr, "GetProfileUpdateNotifier"}, | 29 | {104, nullptr, "GetProfileUpdateNotifier"}, |
| 30 | {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, | 30 | {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ |
| 31 | {106, nullptr, "GetProfileSyncNotifier"}, | 31 | {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ |
| 32 | {110, nullptr, "StoreSaveDataThumbnail"}, | 32 | {110, nullptr, "StoreSaveDataThumbnail"}, |
| 33 | {111, nullptr, "ClearSaveDataThumbnail"}, | 33 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 34 | {112, nullptr, "LoadSaveDataThumbnail"}, | 34 | {112, nullptr, "LoadSaveDataThumbnail"}, |
| 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, | 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ |
| 36 | {120, nullptr, "ListOpenUsersInApplication"}, | 36 | {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+ |
| 37 | {130, nullptr, "ActivateOpenContextRetention"}, | 37 | {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+ |
| 38 | {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, | 38 | {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ |
| 39 | {150, nullptr, "AuthenticateApplicationAsync"}, | 39 | {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+ |
| 40 | {190, nullptr, "GetUserLastOpenedApplication"}, | 40 | {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0 |
| 41 | {191, nullptr, "ActivateOpenContextHolder"}, | 41 | {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+ |
| 42 | {200, nullptr, "BeginUserRegistration"}, | 42 | {200, nullptr, "BeginUserRegistration"}, |
| 43 | {201, nullptr, "CompleteUserRegistration"}, | 43 | {201, nullptr, "CompleteUserRegistration"}, |
| 44 | {202, nullptr, "CancelUserRegistration"}, | 44 | {202, nullptr, "CancelUserRegistration"}, |
| @@ -46,15 +46,15 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 46 | {204, nullptr, "SetUserPosition"}, | 46 | {204, nullptr, "SetUserPosition"}, |
| 47 | {205, &ACC_SU::GetProfileEditor, "GetProfileEditor"}, | 47 | {205, &ACC_SU::GetProfileEditor, "GetProfileEditor"}, |
| 48 | {206, nullptr, "CompleteUserRegistrationForcibly"}, | 48 | {206, nullptr, "CompleteUserRegistrationForcibly"}, |
| 49 | {210, nullptr, "CreateFloatingRegistrationRequest"}, | 49 | {210, nullptr, "CreateFloatingRegistrationRequest"}, // 3.0.0+ |
| 50 | {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, | 50 | {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+ |
| 51 | {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, | 51 | {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+ |
| 52 | {230, nullptr, "AuthenticateServiceAsync"}, | 52 | {230, nullptr, "AuthenticateServiceAsync"}, |
| 53 | {250, nullptr, "GetBaasAccountAdministrator"}, | 53 | {250, nullptr, "GetBaasAccountAdministrator"}, |
| 54 | {290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"}, | 54 | {290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"}, |
| 55 | {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, | 55 | {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, // 3.0.0+ |
| 56 | {299, nullptr, "SuspendBackgroundDaemon"}, | 56 | {299, nullptr, "SuspendBackgroundDaemon"}, |
| 57 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, | 57 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+ |
| 58 | {998, nullptr, "DebugSetUserStateClose"}, | 58 | {998, nullptr, "DebugSetUserStateClose"}, |
| 59 | {999, nullptr, "DebugSetUserStateOpen"}, | 59 | {999, nullptr, "DebugSetUserStateOpen"}, |
| 60 | }; | 60 | }; |
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index fb4e7e772..49f6e20f1 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp | |||
| @@ -17,23 +17,23 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 17 | {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"}, | 17 | {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"}, |
| 18 | {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, | 18 | {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, |
| 19 | {5, &ACC_U0::GetProfile, "GetProfile"}, | 19 | {5, &ACC_U0::GetProfile, "GetProfile"}, |
| 20 | {6, nullptr, "GetProfileDigest"}, | 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ |
| 21 | {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 21 | {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 22 | {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 22 | {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 23 | {60, nullptr, "ListOpenContextStoredUsers"}, | 23 | {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 |
| 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, | 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ |
| 25 | {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, | 25 | {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, |
| 26 | {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, | 26 | {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, |
| 27 | {102, nullptr, "AuthenticateApplicationAsync"}, | 27 | {102, nullptr, "AuthenticateApplicationAsync"}, |
| 28 | {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, | 28 | {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ |
| 29 | {110, nullptr, "StoreSaveDataThumbnail"}, | 29 | {110, nullptr, "StoreSaveDataThumbnail"}, |
| 30 | {111, nullptr, "ClearSaveDataThumbnail"}, | 30 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 31 | {120, nullptr, "CreateGuestLoginRequest"}, | 31 | {120, nullptr, "CreateGuestLoginRequest"}, |
| 32 | {130, nullptr, "LoadOpenContext"}, | 32 | {130, nullptr, "LoadOpenContext"}, // 5.0.0+ |
| 33 | {131, nullptr, "ListOpenContextStoredUsers"}, | 33 | {131, nullptr, "ListOpenContextStoredUsers"}, // 6.0.0+ |
| 34 | {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, | 34 | {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ |
| 35 | {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, | 35 | {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ |
| 36 | {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, | 36 | {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+ |
| 37 | }; | 37 | }; |
| 38 | // clang-format on | 38 | // clang-format on |
| 39 | 39 | ||
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 9f29cdc82..f47004f84 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp | |||
| @@ -17,28 +17,29 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 17 | {3, &ACC_U1::ListOpenUsers, "ListOpenUsers"}, | 17 | {3, &ACC_U1::ListOpenUsers, "ListOpenUsers"}, |
| 18 | {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"}, | 18 | {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"}, |
| 19 | {5, &ACC_U1::GetProfile, "GetProfile"}, | 19 | {5, &ACC_U1::GetProfile, "GetProfile"}, |
| 20 | {6, nullptr, "GetProfileDigest"}, | 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ |
| 21 | {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 21 | {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 22 | {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 22 | {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 23 | {60, nullptr, "ListOpenContextStoredUsers"}, | 23 | {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 |
| 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, | 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ |
| 25 | {100, nullptr, "GetUserRegistrationNotifier"}, | 25 | {100, nullptr, "GetUserRegistrationNotifier"}, |
| 26 | {101, nullptr, "GetUserStateChangeNotifier"}, | 26 | {101, nullptr, "GetUserStateChangeNotifier"}, |
| 27 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, | 27 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, |
| 28 | {103, nullptr, "GetProfileUpdateNotifier"}, | 28 | {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, |
| 29 | {104, nullptr, "CheckNetworkServiceAvailabilityAsync"}, | 29 | {104, nullptr, "GetProfileUpdateNotifier"}, |
| 30 | {105, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, | 30 | {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ |
| 31 | {106, nullptr, "GetProfileSyncNotifier"}, | 31 | {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ |
| 32 | {110, nullptr, "StoreSaveDataThumbnail"}, | 32 | {110, nullptr, "StoreSaveDataThumbnail"}, |
| 33 | {111, nullptr, "ClearSaveDataThumbnail"}, | 33 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 34 | {112, nullptr, "LoadSaveDataThumbnail"}, | 34 | {112, nullptr, "LoadSaveDataThumbnail"}, |
| 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, | 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ |
| 36 | {130, nullptr, "ActivateOpenContextRetention"}, | 36 | {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+ |
| 37 | {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, | 37 | {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+ |
| 38 | {150, nullptr, "AuthenticateApplicationAsync"}, | 38 | {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ |
| 39 | {190, nullptr, "GetUserLastOpenedApplication"}, | 39 | {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+ |
| 40 | {191, nullptr, "ActivateOpenContextHolder"}, | 40 | {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0 |
| 41 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, | 41 | {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+ |
| 42 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+ | ||
| 42 | {998, nullptr, "DebugSetUserStateClose"}, | 43 | {998, nullptr, "DebugSetUserStateClose"}, |
| 43 | {999, nullptr, "DebugSetUserStateOpen"}, | 44 | {999, nullptr, "DebugSetUserStateOpen"}, |
| 44 | }; | 45 | }; |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4df74c4f9..20f366635 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -68,6 +68,7 @@ IWindowController::IWindowController(Core::System& system_) | |||
| 68 | static const FunctionInfo functions[] = { | 68 | static const FunctionInfo functions[] = { |
| 69 | {0, nullptr, "CreateWindow"}, | 69 | {0, nullptr, "CreateWindow"}, |
| 70 | {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, | 70 | {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, |
| 71 | {2, nullptr, "GetAppletResourceUserIdOfCallerApplet"}, | ||
| 71 | {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, | 72 | {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, |
| 72 | {11, nullptr, "ReleaseForegroundRights"}, | 73 | {11, nullptr, "ReleaseForegroundRights"}, |
| 73 | {12, nullptr, "RejectToChangeIntoBackground"}, | 74 | {12, nullptr, "RejectToChangeIntoBackground"}, |
| @@ -189,8 +190,8 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController" | |||
| 189 | {5, nullptr, "GetLastForegroundCaptureImageEx"}, | 190 | {5, nullptr, "GetLastForegroundCaptureImageEx"}, |
| 190 | {6, nullptr, "GetLastApplicationCaptureImageEx"}, | 191 | {6, nullptr, "GetLastApplicationCaptureImageEx"}, |
| 191 | {7, nullptr, "GetCallerAppletCaptureImageEx"}, | 192 | {7, nullptr, "GetCallerAppletCaptureImageEx"}, |
| 192 | {8, nullptr, "TakeScreenShotOfOwnLayer"}, // 2.0.0+ | 193 | {8, nullptr, "TakeScreenShotOfOwnLayer"}, |
| 193 | {9, nullptr, "CopyBetweenCaptureBuffers"}, // 5.0.0+ | 194 | {9, nullptr, "CopyBetweenCaptureBuffers"}, |
| 194 | {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, | 195 | {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, |
| 195 | {11, nullptr, "ReleaseLastApplicationCaptureBuffer"}, | 196 | {11, nullptr, "ReleaseLastApplicationCaptureBuffer"}, |
| 196 | {12, nullptr, "AcquireLastForegroundCaptureBuffer"}, | 197 | {12, nullptr, "AcquireLastForegroundCaptureBuffer"}, |
| @@ -200,17 +201,14 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController" | |||
| 200 | {16, nullptr, "AcquireLastApplicationCaptureBufferEx"}, | 201 | {16, nullptr, "AcquireLastApplicationCaptureBufferEx"}, |
| 201 | {17, nullptr, "AcquireLastForegroundCaptureBufferEx"}, | 202 | {17, nullptr, "AcquireLastForegroundCaptureBufferEx"}, |
| 202 | {18, nullptr, "AcquireCallerAppletCaptureBufferEx"}, | 203 | {18, nullptr, "AcquireCallerAppletCaptureBufferEx"}, |
| 203 | // 2.0.0+ | ||
| 204 | {20, nullptr, "ClearCaptureBuffer"}, | 204 | {20, nullptr, "ClearCaptureBuffer"}, |
| 205 | {21, nullptr, "ClearAppletTransitionBuffer"}, | 205 | {21, nullptr, "ClearAppletTransitionBuffer"}, |
| 206 | // 4.0.0+ | ||
| 207 | {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, | 206 | {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, |
| 208 | {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, | 207 | {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, |
| 209 | {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, | 208 | {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, |
| 210 | {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, | 209 | {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, |
| 211 | {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, | 210 | {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, |
| 212 | {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, | 211 | {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, |
| 213 | // 6.0.0+ | ||
| 214 | {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, | 212 | {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, |
| 215 | }; | 213 | }; |
| 216 | // clang-format on | 214 | // clang-format on |
| @@ -225,7 +223,7 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { | |||
| 225 | static const FunctionInfo functions[] = { | 223 | static const FunctionInfo functions[] = { |
| 226 | {0, nullptr, "NotifyMessageToHomeMenuForDebug"}, | 224 | {0, nullptr, "NotifyMessageToHomeMenuForDebug"}, |
| 227 | {1, nullptr, "OpenMainApplication"}, | 225 | {1, nullptr, "OpenMainApplication"}, |
| 228 | {10, nullptr, "EmulateButtonEvent"}, | 226 | {10, nullptr, "PerformSystemButtonPressing"}, |
| 229 | {20, nullptr, "InvalidateTransitionLayer"}, | 227 | {20, nullptr, "InvalidateTransitionLayer"}, |
| 230 | {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, | 228 | {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, |
| 231 | {40, nullptr, "GetAppletResourceUsageInfo"}, | 229 | {40, nullptr, "GetAppletResourceUsageInfo"}, |
| @@ -267,7 +265,7 @@ ISelfController::ISelfController(Core::System& system, | |||
| 267 | {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, | 265 | {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, |
| 268 | {17, nullptr, "SetControllerFirmwareUpdateSection"}, | 266 | {17, nullptr, "SetControllerFirmwareUpdateSection"}, |
| 269 | {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, | 267 | {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, |
| 270 | {19, &ISelfController::SetScreenShotImageOrientation, "SetScreenShotImageOrientation"}, | 268 | {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"}, |
| 271 | {20, nullptr, "SetDesirableKeyboardLayout"}, | 269 | {20, nullptr, "SetDesirableKeyboardLayout"}, |
| 272 | {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, | 270 | {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, |
| 273 | {41, nullptr, "IsSystemBufferSharingEnabled"}, | 271 | {41, nullptr, "IsSystemBufferSharingEnabled"}, |
| @@ -443,7 +441,7 @@ void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& | |||
| 443 | rb.Push(RESULT_SUCCESS); | 441 | rb.Push(RESULT_SUCCESS); |
| 444 | } | 442 | } |
| 445 | 443 | ||
| 446 | void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) { | 444 | void ISelfController::SetAlbumImageOrientation(Kernel::HLERequestContext& ctx) { |
| 447 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 445 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 448 | 446 | ||
| 449 | IPC::ResponseBuilder rb{ctx, 2}; | 447 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -607,6 +605,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system, | |||
| 607 | {20, nullptr, "PushToGeneralChannel"}, | 605 | {20, nullptr, "PushToGeneralChannel"}, |
| 608 | {30, nullptr, "GetHomeButtonReaderLockAccessor"}, | 606 | {30, nullptr, "GetHomeButtonReaderLockAccessor"}, |
| 609 | {31, nullptr, "GetReaderLockAccessorEx"}, | 607 | {31, nullptr, "GetReaderLockAccessorEx"}, |
| 608 | {32, nullptr, "GetWriterLockAccessorEx"}, | ||
| 610 | {40, nullptr, "GetCradleFwVersion"}, | 609 | {40, nullptr, "GetCradleFwVersion"}, |
| 611 | {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, | 610 | {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, |
| 612 | {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"}, | 611 | {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"}, |
| @@ -1132,6 +1131,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) | |||
| 1132 | {24, nullptr, "GetLaunchStorageInfoForDebug"}, | 1131 | {24, nullptr, "GetLaunchStorageInfoForDebug"}, |
| 1133 | {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, | 1132 | {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, |
| 1134 | {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, | 1133 | {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, |
| 1134 | {27, nullptr, "CreateCacheStorage"}, | ||
| 1135 | {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, | 1135 | {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, |
| 1136 | {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, | 1136 | {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, |
| 1137 | {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, | 1137 | {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, |
| @@ -1157,6 +1157,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) | |||
| 1157 | {120, nullptr, "ExecuteProgram"}, | 1157 | {120, nullptr, "ExecuteProgram"}, |
| 1158 | {121, nullptr, "ClearUserChannel"}, | 1158 | {121, nullptr, "ClearUserChannel"}, |
| 1159 | {122, nullptr, "UnpopToUserChannel"}, | 1159 | {122, nullptr, "UnpopToUserChannel"}, |
| 1160 | {123, nullptr, "GetPreviousProgramIndex"}, | ||
| 1161 | {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, | ||
| 1160 | {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, | 1162 | {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, |
| 1161 | {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, | 1163 | {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, |
| 1162 | {141, nullptr, "TryPopFromFriendInvitationStorageChannel"}, | 1164 | {141, nullptr, "TryPopFromFriendInvitationStorageChannel"}, |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 469f7f814..2f69466ec 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -138,7 +138,7 @@ private: | |||
| 138 | void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); | 138 | void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); |
| 139 | void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx); | 139 | void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx); |
| 140 | void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx); | 140 | void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx); |
| 141 | void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx); | 141 | void SetAlbumImageOrientation(Kernel::HLERequestContext& ctx); |
| 142 | void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); | 142 | void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); |
| 143 | void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); | 143 | void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); |
| 144 | void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); | 144 | void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 54e63c138..fbe3686ae 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp | |||
| @@ -30,7 +30,7 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( | |||
| 30 | config.sub_text.size()); | 30 | config.sub_text.size()); |
| 31 | params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), | 31 | params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), |
| 32 | config.guide_text.size()); | 32 | config.guide_text.size()); |
| 33 | params.initial_text = initial_text; | 33 | params.initial_text = std::move(initial_text); |
| 34 | params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; | 34 | params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; |
| 35 | params.password = static_cast<bool>(config.is_password); | 35 | params.password = static_cast<bool>(config.is_password); |
| 36 | params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position); | 36 | params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position); |
| @@ -60,7 +60,7 @@ void SoftwareKeyboard::Initialize() { | |||
| 60 | std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); | 60 | std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); |
| 61 | 61 | ||
| 62 | const auto work_buffer_storage = broker.PopNormalDataToApplet(); | 62 | const auto work_buffer_storage = broker.PopNormalDataToApplet(); |
| 63 | ASSERT(work_buffer_storage != nullptr); | 63 | ASSERT_OR_EXECUTE(work_buffer_storage != nullptr, { return; }); |
| 64 | const auto& work_buffer = work_buffer_storage->GetData(); | 64 | const auto& work_buffer = work_buffer_storage->GetData(); |
| 65 | 65 | ||
| 66 | if (config.initial_string_size == 0) | 66 | if (config.initial_string_size == 0) |
| @@ -109,7 +109,7 @@ void SoftwareKeyboard::Execute() { | |||
| 109 | 109 | ||
| 110 | const auto parameters = ConvertToFrontendParameters(config, initial_text); | 110 | const auto parameters = ConvertToFrontendParameters(config, initial_text); |
| 111 | 111 | ||
| 112 | frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); }, | 112 | frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, |
| 113 | parameters); | 113 | parameters); |
| 114 | } | 114 | } |
| 115 | 115 | ||
diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp index 003ee8667..f27729ce7 100644 --- a/src/core/hle/service/am/spsm.cpp +++ b/src/core/hle/service/am/spsm.cpp | |||
| @@ -10,17 +10,17 @@ SPSM::SPSM() : ServiceFramework{"spsm"} { | |||
| 10 | // clang-format off | 10 | // clang-format off |
| 11 | static const FunctionInfo functions[] = { | 11 | static const FunctionInfo functions[] = { |
| 12 | {0, nullptr, "GetState"}, | 12 | {0, nullptr, "GetState"}, |
| 13 | {1, nullptr, "SleepSystemAndWaitAwake"}, | 13 | {1, nullptr, "EnterSleep"}, |
| 14 | {2, nullptr, "Unknown1"}, | 14 | {2, nullptr, "GetLastWakeReason"}, |
| 15 | {3, nullptr, "Unknown2"}, | 15 | {3, nullptr, "Shutdown"}, |
| 16 | {4, nullptr, "GetNotificationMessageEventHandle"}, | 16 | {4, nullptr, "GetNotificationMessageEventHandle"}, |
| 17 | {5, nullptr, "Unknown3"}, | 17 | {5, nullptr, "ReceiveNotificationMessage"}, |
| 18 | {6, nullptr, "Unknown4"}, | 18 | {6, nullptr, "AnalyzeLogForLastSleepWakeSequence"}, |
| 19 | {7, nullptr, "Unknown5"}, | 19 | {7, nullptr, "ResetEventLog"}, |
| 20 | {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"}, | 20 | {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"}, |
| 21 | {9, nullptr, "ChangeHomeButtonLongPressingTime"}, | 21 | {9, nullptr, "ChangeHomeButtonLongPressingTime"}, |
| 22 | {10, nullptr, "Unknown6"}, | 22 | {10, nullptr, "PutErrorState"}, |
| 23 | {11, nullptr, "Unknown7"}, | 23 | {11, nullptr, "InvalidateCurrentHomeButtonPressing"}, |
| 24 | }; | 24 | }; |
| 25 | // clang-format on | 25 | // clang-format on |
| 26 | 26 | ||
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 4227a4adf..8e79f707b 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -60,6 +60,7 @@ AOC_U::AOC_U(Core::System& system) | |||
| 60 | {6, nullptr, "PrepareAddOnContentByApplicationId"}, | 60 | {6, nullptr, "PrepareAddOnContentByApplicationId"}, |
| 61 | {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, | 61 | {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, |
| 62 | {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, | 62 | {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, |
| 63 | {9, nullptr, "GetAddOnContentLostErrorCode"}, | ||
| 63 | {100, nullptr, "CreateEcPurchasedEventManager"}, | 64 | {100, nullptr, "CreateEcPurchasedEventManager"}, |
| 64 | {101, nullptr, "CreatePermanentEcPurchasedEventManager"}, | 65 | {101, nullptr, "CreatePermanentEcPurchasedEventManager"}, |
| 65 | }; | 66 | }; |
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp index 8bb2528c9..b31766212 100644 --- a/src/core/hle/service/bcat/bcat.cpp +++ b/src/core/hle/service/bcat/bcat.cpp | |||
| @@ -14,6 +14,8 @@ BCAT::BCAT(Core::System& system, std::shared_ptr<Module> module, | |||
| 14 | {0, &BCAT::CreateBcatService, "CreateBcatService"}, | 14 | {0, &BCAT::CreateBcatService, "CreateBcatService"}, |
| 15 | {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"}, | 15 | {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"}, |
| 16 | {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"}, | 16 | {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"}, |
| 17 | {3, nullptr, "CreateDeliveryCacheProgressService"}, | ||
| 18 | {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"}, | ||
| 17 | }; | 19 | }; |
| 18 | // clang-format on | 20 | // clang-format on |
| 19 | RegisterHandlers(functions); | 21 | RegisterHandlers(functions); |
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 34aba7a27..603b64d4f 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp | |||
| @@ -143,10 +143,13 @@ public: | |||
| 143 | {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, | 143 | {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, |
| 144 | {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, | 144 | {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, |
| 145 | {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, | 145 | {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, |
| 146 | {30101, nullptr, "Unknown"}, | ||
| 147 | {30102, nullptr, "Unknown2"}, | ||
| 146 | {30200, nullptr, "RegisterBackgroundDeliveryTask"}, | 148 | {30200, nullptr, "RegisterBackgroundDeliveryTask"}, |
| 147 | {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, | 149 | {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, |
| 148 | {30202, nullptr, "BlockDeliveryTask"}, | 150 | {30202, nullptr, "BlockDeliveryTask"}, |
| 149 | {30203, nullptr, "UnblockDeliveryTask"}, | 151 | {30203, nullptr, "UnblockDeliveryTask"}, |
| 152 | {30210, nullptr, "SetDeliveryTaskTimer"}, | ||
| 150 | {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"}, | 153 | {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"}, |
| 151 | {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, | 154 | {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, |
| 152 | {90200, nullptr, "GetDeliveryList"}, | 155 | {90200, nullptr, "GetDeliveryList"}, |
diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp index 1c1ecdb60..fac6b2f9c 100644 --- a/src/core/hle/service/bpc/bpc.cpp +++ b/src/core/hle/service/bpc/bpc.cpp | |||
| @@ -23,9 +23,14 @@ public: | |||
| 23 | {5, nullptr, "GetBoardPowerControlEvent"}, | 23 | {5, nullptr, "GetBoardPowerControlEvent"}, |
| 24 | {6, nullptr, "GetSleepButtonState"}, | 24 | {6, nullptr, "GetSleepButtonState"}, |
| 25 | {7, nullptr, "GetPowerEvent"}, | 25 | {7, nullptr, "GetPowerEvent"}, |
| 26 | {8, nullptr, "Unknown1"}, | 26 | {8, nullptr, "CreateWakeupTimer"}, |
| 27 | {9, nullptr, "Unknown2"}, | 27 | {9, nullptr, "CancelWakeupTimer"}, |
| 28 | {10, nullptr, "Unknown3"}, | 28 | {10, nullptr, "EnableWakeupTimerOnDevice"}, |
| 29 | {11, nullptr, "CreateWakeupTimerEx"}, | ||
| 30 | {12, nullptr, "GetLastEnabledWakeupTimerType"}, | ||
| 31 | {13, nullptr, "CleanAllWakeupTimers"}, | ||
| 32 | {14, nullptr, "Unknown"}, | ||
| 33 | {15, nullptr, "Unknown2"}, | ||
| 29 | }; | 34 | }; |
| 30 | // clang-format on | 35 | // clang-format on |
| 31 | 36 | ||
| @@ -38,10 +43,11 @@ public: | |||
| 38 | explicit BPC_R() : ServiceFramework{"bpc:r"} { | 43 | explicit BPC_R() : ServiceFramework{"bpc:r"} { |
| 39 | // clang-format off | 44 | // clang-format off |
| 40 | static const FunctionInfo functions[] = { | 45 | static const FunctionInfo functions[] = { |
| 41 | {0, nullptr, "GetExternalRtcValue"}, | 46 | {0, nullptr, "GetRtcTime"}, |
| 42 | {1, nullptr, "SetExternalRtcValue"}, | 47 | {1, nullptr, "SetRtcTime"}, |
| 43 | {2, nullptr, "ReadExternalRtcResetFlag"}, | 48 | {2, nullptr, "GetRtcResetDetected"}, |
| 44 | {3, nullptr, "ClearExternalRtcResetFlag"}, | 49 | {3, nullptr, "ClearRtcResetDetected"}, |
| 50 | {4, nullptr, "SetUpRtcResetOnShutdown"}, | ||
| 45 | }; | 51 | }; |
| 46 | // clang-format on | 52 | // clang-format on |
| 47 | 53 | ||
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 40a06c9fd..f311afa2f 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp | |||
| @@ -58,102 +58,103 @@ public: | |||
| 58 | {1, nullptr, "InitializeBluetooth"}, | 58 | {1, nullptr, "InitializeBluetooth"}, |
| 59 | {2, nullptr, "EnableBluetooth"}, | 59 | {2, nullptr, "EnableBluetooth"}, |
| 60 | {3, nullptr, "DisableBluetooth"}, | 60 | {3, nullptr, "DisableBluetooth"}, |
| 61 | {4, nullptr, "CleanupBluetooth"}, | 61 | {4, nullptr, "FinalizeBluetooth"}, |
| 62 | {5, nullptr, "GetAdapterProperties"}, | 62 | {5, nullptr, "GetAdapterProperties"}, |
| 63 | {6, nullptr, "GetAdapterProperty"}, | 63 | {6, nullptr, "GetAdapterProperty"}, |
| 64 | {7, nullptr, "SetAdapterProperty"}, | 64 | {7, nullptr, "SetAdapterProperty"}, |
| 65 | {8, nullptr, "StartDiscovery"}, | 65 | {8, nullptr, "StartInquiry"}, |
| 66 | {9, nullptr, "CancelDiscovery"}, | 66 | {9, nullptr, "StopInquiry"}, |
| 67 | {10, nullptr, "CreateBond"}, | 67 | {10, nullptr, "CreateBond"}, |
| 68 | {11, nullptr, "RemoveBond"}, | 68 | {11, nullptr, "RemoveBond"}, |
| 69 | {12, nullptr, "CancelBond"}, | 69 | {12, nullptr, "CancelBond"}, |
| 70 | {13, nullptr, "PinReply"}, | 70 | {13, nullptr, "RespondToPinRequest"}, |
| 71 | {14, nullptr, "SspReply"}, | 71 | {14, nullptr, "RespondToSspRequest"}, |
| 72 | {15, nullptr, "GetEventInfo"}, | 72 | {15, nullptr, "GetEventInfo"}, |
| 73 | {16, nullptr, "InitializeHid"}, | 73 | {16, nullptr, "InitializeHid"}, |
| 74 | {17, nullptr, "HidConnect"}, | 74 | {17, nullptr, "OpenHidConnection"}, |
| 75 | {18, nullptr, "HidDisconnect"}, | 75 | {18, nullptr, "CloseHidConnection"}, |
| 76 | {19, nullptr, "HidSendData"}, | 76 | {19, nullptr, "WriteHidData"}, |
| 77 | {20, nullptr, "HidSendData2"}, | 77 | {20, nullptr, "WriteHidData2"}, |
| 78 | {21, nullptr, "HidSetReport"}, | 78 | {21, nullptr, "SetHidReport"}, |
| 79 | {22, nullptr, "HidGetReport"}, | 79 | {22, nullptr, "GetHidReport"}, |
| 80 | {23, nullptr, "HidWakeController"}, | 80 | {23, nullptr, "TriggerConnection"}, |
| 81 | {24, nullptr, "HidAddPairedDevice"}, | 81 | {24, nullptr, "AddPairedDeviceInfo"}, |
| 82 | {25, nullptr, "HidGetPairedDevice"}, | 82 | {25, nullptr, "GetPairedDeviceInfo"}, |
| 83 | {26, nullptr, "CleanupHid"}, | 83 | {26, nullptr, "FinalizeHid"}, |
| 84 | {27, nullptr, "HidGetEventInfo"}, | 84 | {27, nullptr, "GetHidEventInfo"}, |
| 85 | {28, nullptr, "ExtSetTsi"}, | 85 | {28, nullptr, "SetTsi"}, |
| 86 | {29, nullptr, "ExtSetBurstMode"}, | 86 | {29, nullptr, "EnableBurstMode"}, |
| 87 | {30, nullptr, "ExtSetZeroRetran"}, | 87 | {30, nullptr, "SetZeroRetransmission"}, |
| 88 | {31, nullptr, "ExtSetMcMode"}, | 88 | {31, nullptr, "EnableMcMode"}, |
| 89 | {32, nullptr, "ExtStartLlrMode"}, | 89 | {32, nullptr, "EnableLlrScan"}, |
| 90 | {33, nullptr, "ExtExitLlrMode"}, | 90 | {33, nullptr, "DisableLlrScan"}, |
| 91 | {34, nullptr, "ExtSetRadio"}, | 91 | {34, nullptr, "EnableRadio"}, |
| 92 | {35, nullptr, "ExtSetVisibility"}, | 92 | {35, nullptr, "SetVisibility"}, |
| 93 | {36, nullptr, "ExtSetTbfcScan"}, | 93 | {36, nullptr, "EnableTbfcScan"}, |
| 94 | {37, nullptr, "RegisterHidReportEvent"}, | 94 | {37, nullptr, "RegisterHidReportEvent"}, |
| 95 | {38, nullptr, "HidGetReportEventInfo"}, | 95 | {38, nullptr, "GetHidReportEventInfo"}, |
| 96 | {39, nullptr, "GetLatestPlr"}, | 96 | {39, nullptr, "GetLatestPlr"}, |
| 97 | {40, nullptr, "ExtGetPendingConnections"}, | 97 | {40, nullptr, "GetPendingConnections"}, |
| 98 | {41, nullptr, "GetChannelMap"}, | 98 | {41, nullptr, "GetChannelMap"}, |
| 99 | {42, nullptr, "EnableBluetoothBoostSetting"}, | 99 | {42, nullptr, "EnableTxPowerBoostSetting"}, |
| 100 | {43, nullptr, "IsBluetoothBoostSettingEnabled"}, | 100 | {43, nullptr, "IsTxPowerBoostSettingEnabled"}, |
| 101 | {44, nullptr, "EnableBluetoothAfhSetting"}, | 101 | {44, nullptr, "EnableAfhSetting"}, |
| 102 | {45, nullptr, "IsBluetoothAfhSettingEnabled"}, | 102 | {45, nullptr, "IsAfhSettingEnabled"}, |
| 103 | {46, nullptr, "InitializeBluetoothLe"}, | 103 | {46, nullptr, "InitializeBle"}, |
| 104 | {47, nullptr, "EnableBluetoothLe"}, | 104 | {47, nullptr, "EnableBle"}, |
| 105 | {48, nullptr, "DisableBluetoothLe"}, | 105 | {48, nullptr, "DisableBle"}, |
| 106 | {49, nullptr, "CleanupBluetoothLe"}, | 106 | {49, nullptr, "FinalizeBle"}, |
| 107 | {50, nullptr, "SetLeVisibility"}, | 107 | {50, nullptr, "SetBleVisibility"}, |
| 108 | {51, nullptr, "SetLeConnectionParameter"}, | 108 | {51, nullptr, "SetBleConnectionParameter"}, |
| 109 | {52, nullptr, "SetLeDefaultConnectionParameter"}, | 109 | {52, nullptr, "SetBleDefaultConnectionParameter"}, |
| 110 | {53, nullptr, "SetLeAdvertiseData"}, | 110 | {53, nullptr, "SetBleAdvertiseData"}, |
| 111 | {54, nullptr, "SetLeAdvertiseParameter"}, | 111 | {54, nullptr, "SetBleAdvertiseParameter"}, |
| 112 | {55, nullptr, "StartLeScan"}, | 112 | {55, nullptr, "StartBleScan"}, |
| 113 | {56, nullptr, "StopLeScan"}, | 113 | {56, nullptr, "StopBleScan"}, |
| 114 | {57, nullptr, "AddLeScanFilterCondition"}, | 114 | {57, nullptr, "AddBleScanFilterCondition"}, |
| 115 | {58, nullptr, "DeleteLeScanFilterCondition"}, | 115 | {58, nullptr, "DeleteBleScanFilterCondition"}, |
| 116 | {59, nullptr, "DeleteLeScanFilter"}, | 116 | {59, nullptr, "DeleteBleScanFilter"}, |
| 117 | {60, nullptr, "ClearLeScanFilters"}, | 117 | {60, nullptr, "ClearBleScanFilters"}, |
| 118 | {61, nullptr, "EnableLeScanFilter"}, | 118 | {61, nullptr, "EnableBleScanFilter"}, |
| 119 | {62, nullptr, "RegisterLeClient"}, | 119 | {62, nullptr, "RegisterGattClient"}, |
| 120 | {63, nullptr, "UnregisterLeClient"}, | 120 | {63, nullptr, "UnregisterGattClient"}, |
| 121 | {64, nullptr, "UnregisterLeClientAll"}, | 121 | {64, nullptr, "UnregisterAllGattClients"}, |
| 122 | {65, nullptr, "LeClientConnect"}, | 122 | {65, nullptr, "ConnectGattServer"}, |
| 123 | {66, nullptr, "LeClientCancelConnection"}, | 123 | {66, nullptr, "CancelConnectGattServer"}, |
| 124 | {67, nullptr, "LeClientDisconnect"}, | 124 | {67, nullptr, "DisconnectGattServer"}, |
| 125 | {68, nullptr, "LeClientGetAttributes"}, | 125 | {68, nullptr, "GetGattAttribute"}, |
| 126 | {69, nullptr, "LeClientDiscoverService"}, | 126 | {69, nullptr, "GetGattService"}, |
| 127 | {70, nullptr, "LeClientConfigureMtu"}, | 127 | {70, nullptr, "ConfigureAttMtu"}, |
| 128 | {71, nullptr, "RegisterLeServer"}, | 128 | {71, nullptr, "RegisterGattServer"}, |
| 129 | {72, nullptr, "UnregisterLeServer"}, | 129 | {72, nullptr, "UnregisterGattServer"}, |
| 130 | {73, nullptr, "LeServerConnect"}, | 130 | {73, nullptr, "ConnectGattClient"}, |
| 131 | {74, nullptr, "LeServerDisconnect"}, | 131 | {74, nullptr, "DisconnectGattClient"}, |
| 132 | {75, nullptr, "CreateLeService"}, | 132 | {75, nullptr, "AddGattService"}, |
| 133 | {76, nullptr, "StartLeService"}, | 133 | {76, nullptr, "EnableGattService"}, |
| 134 | {77, nullptr, "AddLeCharacteristic"}, | 134 | {77, nullptr, "AddGattCharacteristic"}, |
| 135 | {78, nullptr, "AddLeDescriptor"}, | 135 | {78, nullptr, "AddGattDescriptor"}, |
| 136 | {79, nullptr, "GetLeCoreEventInfo"}, | 136 | {79, nullptr, "GetBleManagedEventInfo"}, |
| 137 | {80, nullptr, "LeGetFirstCharacteristic"}, | 137 | {80, nullptr, "GetGattFirstCharacteristic"}, |
| 138 | {81, nullptr, "LeGetNextCharacteristic"}, | 138 | {81, nullptr, "GetGattNextCharacteristic"}, |
| 139 | {82, nullptr, "LeGetFirstDescriptor"}, | 139 | {82, nullptr, "GetGattFirstDescriptor"}, |
| 140 | {83, nullptr, "LeGetNextDescriptor"}, | 140 | {83, nullptr, "GetGattNextDescriptor"}, |
| 141 | {84, nullptr, "RegisterLeCoreDataPath"}, | 141 | {84, nullptr, "RegisterGattManagedDataPath"}, |
| 142 | {85, nullptr, "UnregisterLeCoreDataPath"}, | 142 | {85, nullptr, "UnregisterGattManagedDataPath"}, |
| 143 | {86, nullptr, "RegisterLeHidDataPath"}, | 143 | {86, nullptr, "RegisterGattHidDataPath"}, |
| 144 | {87, nullptr, "UnregisterLeHidDataPath"}, | 144 | {87, nullptr, "UnregisterGattHidDataPath"}, |
| 145 | {88, nullptr, "RegisterLeDataPath"}, | 145 | {88, nullptr, "RegisterGattDataPath"}, |
| 146 | {89, nullptr, "UnregisterLeDataPath"}, | 146 | {89, nullptr, "UnregisterGattDataPath"}, |
| 147 | {90, nullptr, "LeClientReadCharacteristic"}, | 147 | {90, nullptr, "ReadGattCharacteristic"}, |
| 148 | {91, nullptr, "LeClientReadDescriptor"}, | 148 | {91, nullptr, "ReadGattDescriptor"}, |
| 149 | {92, nullptr, "LeClientWriteCharacteristic"}, | 149 | {92, nullptr, "WriteGattCharacteristic"}, |
| 150 | {93, nullptr, "LeClientWriteDescriptor"}, | 150 | {93, nullptr, "WriteGattDescriptor"}, |
| 151 | {94, nullptr, "LeClientRegisterNotification"}, | 151 | {94, nullptr, "RegisterGattNotification"}, |
| 152 | {95, nullptr, "LeClientDeregisterNotification"}, | 152 | {95, nullptr, "UnregisterGattNotification"}, |
| 153 | {96, nullptr, "GetLeHidEventInfo"}, | 153 | {96, nullptr, "GetLeHidEventInfo"}, |
| 154 | {97, nullptr, "RegisterBleHidEvent"}, | 154 | {97, nullptr, "RegisterBleHidEvent"}, |
| 155 | {98, nullptr, "SetLeScanParameter"}, | 155 | {98, nullptr, "SetBleScanParameter"}, |
| 156 | {256, nullptr, "GetIsManufacturingMode"}, | 156 | {99, nullptr, "MoveToSecondaryPiconet"}, |
| 157 | {256, nullptr, "IsManufacturingMode"}, | ||
| 157 | {257, nullptr, "EmulateBluetoothCrash"}, | 158 | {257, nullptr, "EmulateBluetoothCrash"}, |
| 158 | {258, nullptr, "GetBleChannelMap"}, | 159 | {258, nullptr, "GetBleChannelMap"}, |
| 159 | }; | 160 | }; |
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 251b3c9df..0d251c6d0 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp | |||
| @@ -132,66 +132,71 @@ public: | |||
| 132 | explicit BTM() : ServiceFramework{"btm"} { | 132 | explicit BTM() : ServiceFramework{"btm"} { |
| 133 | // clang-format off | 133 | // clang-format off |
| 134 | static const FunctionInfo functions[] = { | 134 | static const FunctionInfo functions[] = { |
| 135 | {0, nullptr, "Unknown1"}, | 135 | {0, nullptr, "GetState"}, |
| 136 | {1, nullptr, "Unknown2"}, | 136 | {1, nullptr, "GetHostDeviceProperty"}, |
| 137 | {2, nullptr, "RegisterSystemEventForConnectedDeviceCondition"}, | 137 | {2, nullptr, "AcquireDeviceConditionEvent"}, |
| 138 | {3, nullptr, "Unknown3"}, | 138 | {3, nullptr, "GetDeviceCondition"}, |
| 139 | {4, nullptr, "Unknown4"}, | 139 | {4, nullptr, "SetBurstMode"}, |
| 140 | {5, nullptr, "Unknown5"}, | 140 | {5, nullptr, "SetSlotMode"}, |
| 141 | {6, nullptr, "Unknown6"}, | 141 | {6, nullptr, "SetBluetoothMode"}, |
| 142 | {7, nullptr, "Unknown7"}, | 142 | {7, nullptr, "SetWlanMode"}, |
| 143 | {8, nullptr, "RegisterSystemEventForRegisteredDeviceInfo"}, | 143 | {8, nullptr, "AcquireDeviceInfoEvent"}, |
| 144 | {9, nullptr, "Unknown8"}, | 144 | {9, nullptr, "GetDeviceInfo"}, |
| 145 | {10, nullptr, "Unknown9"}, | 145 | {10, nullptr, "AddDeviceInfo"}, |
| 146 | {11, nullptr, "Unknown10"}, | 146 | {11, nullptr, "RemoveDeviceInfo"}, |
| 147 | {12, nullptr, "Unknown11"}, | 147 | {12, nullptr, "IncreaseDeviceInfoOrder"}, |
| 148 | {13, nullptr, "Unknown12"}, | 148 | {13, nullptr, "LlrNotify"}, |
| 149 | {14, nullptr, "EnableRadio"}, | 149 | {14, nullptr, "EnableRadio"}, |
| 150 | {15, nullptr, "DisableRadio"}, | 150 | {15, nullptr, "DisableRadio"}, |
| 151 | {16, nullptr, "Unknown13"}, | 151 | {16, nullptr, "HidDisconnect"}, |
| 152 | {17, nullptr, "Unknown14"}, | 152 | {17, nullptr, "HidSetRetransmissionMode"}, |
| 153 | {18, nullptr, "Unknown15"}, | 153 | {18, nullptr, "AcquireAwakeReqEvent"}, |
| 154 | {19, nullptr, "Unknown16"}, | 154 | {19, nullptr, "AcquireLlrStateEvent"}, |
| 155 | {20, nullptr, "Unknown17"}, | 155 | {20, nullptr, "IsLlrStarted"}, |
| 156 | {21, nullptr, "Unknown18"}, | 156 | {21, nullptr, "EnableSlotSaving"}, |
| 157 | {22, nullptr, "Unknown19"}, | 157 | {22, nullptr, "ProtectDeviceInfo"}, |
| 158 | {23, nullptr, "Unknown20"}, | 158 | {23, nullptr, "AcquireBleScanEvent"}, |
| 159 | {24, nullptr, "Unknown21"}, | 159 | {24, nullptr, "GetBleScanParameterGeneral"}, |
| 160 | {25, nullptr, "Unknown22"}, | 160 | {25, nullptr, "GetBleScanParameterSmartDevice"}, |
| 161 | {26, nullptr, "Unknown23"}, | 161 | {26, nullptr, "StartBleScanForGeneral"}, |
| 162 | {27, nullptr, "Unknown24"}, | 162 | {27, nullptr, "StopBleScanForGeneral"}, |
| 163 | {28, nullptr, "Unknown25"}, | 163 | {28, nullptr, "GetBleScanResultsForGeneral"}, |
| 164 | {29, nullptr, "Unknown26"}, | 164 | {29, nullptr, "StartBleScanForPairedDevice"}, |
| 165 | {30, nullptr, "Unknown27"}, | 165 | {30, nullptr, "StopBleScanForPairedDevice"}, |
| 166 | {31, nullptr, "Unknown28"}, | 166 | {31, nullptr, "StartBleScanForSmartDevice"}, |
| 167 | {32, nullptr, "Unknown29"}, | 167 | {32, nullptr, "StopBleScanForSmartDevice"}, |
| 168 | {33, nullptr, "Unknown30"}, | 168 | {33, nullptr, "GetBleScanResultsForSmartDevice"}, |
| 169 | {34, nullptr, "Unknown31"}, | 169 | {34, nullptr, "AcquireBleConnectionEvent"}, |
| 170 | {35, nullptr, "Unknown32"}, | 170 | {35, nullptr, "BleConnect"}, |
| 171 | {36, nullptr, "Unknown33"}, | 171 | {36, nullptr, "BleOverrideConnection"}, |
| 172 | {37, nullptr, "Unknown34"}, | 172 | {37, nullptr, "BleDisconnect"}, |
| 173 | {38, nullptr, "Unknown35"}, | 173 | {38, nullptr, "BleGetConnectionState"}, |
| 174 | {39, nullptr, "Unknown36"}, | 174 | {39, nullptr, "BleGetGattClientConditionList"}, |
| 175 | {40, nullptr, "Unknown37"}, | 175 | {40, nullptr, "AcquireBlePairingEvent"}, |
| 176 | {41, nullptr, "Unknown38"}, | 176 | {41, nullptr, "BlePairDevice"}, |
| 177 | {42, nullptr, "Unknown39"}, | 177 | {42, nullptr, "BleUnpairDeviceOnBoth"}, |
| 178 | {43, nullptr, "Unknown40"}, | 178 | {43, nullptr, "BleUnpairDevice"}, |
| 179 | {44, nullptr, "Unknown41"}, | 179 | {44, nullptr, "BleGetPairedAddresses"}, |
| 180 | {45, nullptr, "Unknown42"}, | 180 | {45, nullptr, "AcquireBleServiceDiscoveryEvent"}, |
| 181 | {46, nullptr, "Unknown43"}, | 181 | {46, nullptr, "GetGattServices"}, |
| 182 | {47, nullptr, "Unknown44"}, | 182 | {47, nullptr, "GetGattService"}, |
| 183 | {48, nullptr, "Unknown45"}, | 183 | {48, nullptr, "GetGattIncludedServices"}, |
| 184 | {49, nullptr, "Unknown46"}, | 184 | {49, nullptr, "GetBelongingService"}, |
| 185 | {50, nullptr, "Unknown47"}, | 185 | {50, nullptr, "GetGattCharacteristics"}, |
| 186 | {51, nullptr, "Unknown48"}, | 186 | {51, nullptr, "GetGattDescriptors"}, |
| 187 | {52, nullptr, "Unknown49"}, | 187 | {52, nullptr, "AcquireBleMtuConfigEvent"}, |
| 188 | {53, nullptr, "Unknown50"}, | 188 | {53, nullptr, "ConfigureBleMtu"}, |
| 189 | {54, nullptr, "Unknown51"}, | 189 | {54, nullptr, "GetBleMtu"}, |
| 190 | {55, nullptr, "Unknown52"}, | 190 | {55, nullptr, "RegisterBleGattDataPath"}, |
| 191 | {56, nullptr, "Unknown53"}, | 191 | {56, nullptr, "UnregisterBleGattDataPath"}, |
| 192 | {57, nullptr, "Unknown54"}, | 192 | {57, nullptr, "RegisterAppletResourceUserId"}, |
| 193 | {58, nullptr, "Unknown55"}, | 193 | {58, nullptr, "UnregisterAppletResourceUserId"}, |
| 194 | {59, nullptr, "Unknown56"}, | 194 | {59, nullptr, "SetAppletResourceUserId"}, |
| 195 | {60, nullptr, "Unknown60"}, | ||
| 196 | {61, nullptr, "Unknown61"}, | ||
| 197 | {62, nullptr, "Unknown62"}, | ||
| 198 | {63, nullptr, "Unknown63"}, | ||
| 199 | {64, nullptr, "Unknown64"}, | ||
| 195 | }; | 200 | }; |
| 196 | // clang-format on | 201 | // clang-format on |
| 197 | 202 | ||
| @@ -204,19 +209,19 @@ public: | |||
| 204 | explicit BTM_DBG() : ServiceFramework{"btm:dbg"} { | 209 | explicit BTM_DBG() : ServiceFramework{"btm:dbg"} { |
| 205 | // clang-format off | 210 | // clang-format off |
| 206 | static const FunctionInfo functions[] = { | 211 | static const FunctionInfo functions[] = { |
| 207 | {0, nullptr, "RegisterSystemEventForDiscovery"}, | 212 | {0, nullptr, "AcquireDiscoveryEvent"}, |
| 208 | {1, nullptr, "Unknown1"}, | 213 | {1, nullptr, "StartDiscovery"}, |
| 209 | {2, nullptr, "Unknown2"}, | 214 | {2, nullptr, "CancelDiscovery"}, |
| 210 | {3, nullptr, "Unknown3"}, | 215 | {3, nullptr, "GetDeviceProperty"}, |
| 211 | {4, nullptr, "Unknown4"}, | 216 | {4, nullptr, "CreateBond"}, |
| 212 | {5, nullptr, "Unknown5"}, | 217 | {5, nullptr, "CancelBond"}, |
| 213 | {6, nullptr, "Unknown6"}, | 218 | {6, nullptr, "SetTsiMode"}, |
| 214 | {7, nullptr, "Unknown7"}, | 219 | {7, nullptr, "GeneralTest"}, |
| 215 | {8, nullptr, "Unknown8"}, | 220 | {8, nullptr, "HidConnect"}, |
| 216 | {9, nullptr, "Unknown9"}, | 221 | {9, nullptr, "GeneralGet"}, |
| 217 | {10, nullptr, "Unknown10"}, | 222 | {10, nullptr, "GetGattClientDisconnectionReason"}, |
| 218 | {11, nullptr, "Unknown11"}, | 223 | {11, nullptr, "GetBleConnectionParameter"}, |
| 219 | {12, nullptr, "Unknown11"}, | 224 | {12, nullptr, "GetBleConnectionParameterRequest"}, |
| 220 | }; | 225 | }; |
| 221 | // clang-format on | 226 | // clang-format on |
| 222 | 227 | ||
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index 26c8a7081..ba5749b84 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | 1 | // Copyright 2018 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index fc70a4c27..b8c67b6e2 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | 1 | // Copyright 2018 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| @@ -12,73 +12,79 @@ class ServiceManager; | |||
| 12 | 12 | ||
| 13 | namespace Service::Capture { | 13 | namespace Service::Capture { |
| 14 | 14 | ||
| 15 | enum AlbumImageOrientation { | 15 | enum class AlbumImageOrientation { |
| 16 | Orientation0 = 0, | 16 | Orientation0 = 0, |
| 17 | Orientation1 = 1, | 17 | Orientation1 = 1, |
| 18 | Orientation2 = 2, | 18 | Orientation2 = 2, |
| 19 | Orientation3 = 3, | 19 | Orientation3 = 3, |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | enum AlbumReportOption { | 22 | enum class AlbumReportOption { |
| 23 | Disable = 0, | 23 | Disable = 0, |
| 24 | Enable = 1, | 24 | Enable = 1, |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | enum ContentType : u8 { | 27 | enum class ContentType : u8 { |
| 28 | Screenshot = 0, | 28 | Screenshot = 0, |
| 29 | Movie = 1, | 29 | Movie = 1, |
| 30 | ExtraMovie = 3, | 30 | ExtraMovie = 3, |
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | enum AlbumStorage : u8 { | 33 | enum class AlbumStorage : u8 { |
| 34 | NAND = 0, | 34 | NAND = 0, |
| 35 | SD = 1, | 35 | SD = 1, |
| 36 | }; | 36 | }; |
| 37 | 37 | ||
| 38 | struct AlbumFileDateTime { | 38 | struct AlbumFileDateTime { |
| 39 | u16 year; | 39 | s16 year{}; |
| 40 | u8 month; | 40 | s8 month{}; |
| 41 | u8 day; | 41 | s8 day{}; |
| 42 | u8 hour; | 42 | s8 hour{}; |
| 43 | u8 minute; | 43 | s8 minute{}; |
| 44 | u8 second; | 44 | s8 second{}; |
| 45 | u8 uid; | 45 | s8 uid{}; |
| 46 | }; | 46 | }; |
| 47 | static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size."); | ||
| 47 | 48 | ||
| 48 | struct AlbumEntry { | 49 | struct AlbumEntry { |
| 49 | u64 size; | 50 | u64 size{}; |
| 50 | u64 application_id; | 51 | u64 application_id{}; |
| 51 | AlbumFileDateTime datetime; | 52 | AlbumFileDateTime datetime{}; |
| 52 | AlbumStorage storage; | 53 | AlbumStorage storage{}; |
| 53 | ContentType content; | 54 | ContentType content{}; |
| 54 | u8 padding[6]; | 55 | INSERT_PADDING_BYTES(6); |
| 55 | }; | 56 | }; |
| 57 | static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size."); | ||
| 56 | 58 | ||
| 57 | struct AlbumFileEntry { | 59 | struct AlbumFileEntry { |
| 58 | u64 size; | 60 | u64 size{}; // Size of the entry |
| 59 | u64 hash; | 61 | u64 hash{}; // AES256 with hardcoded key over AlbumEntry |
| 60 | AlbumFileDateTime datetime; | 62 | AlbumFileDateTime datetime{}; |
| 61 | AlbumStorage storage; | 63 | AlbumStorage storage{}; |
| 62 | ContentType content; | 64 | ContentType content{}; |
| 63 | u8 padding[5]; | 65 | INSERT_PADDING_BYTES(5); |
| 64 | u8 unknown; | 66 | u8 unknown{1}; // Set to 1 on official SW |
| 65 | }; | 67 | }; |
| 68 | static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size."); | ||
| 66 | 69 | ||
| 67 | struct ApplicationAlbumEntry { | 70 | struct ApplicationAlbumEntry { |
| 68 | u64 size; | 71 | u64 size{}; // Size of the entry |
| 69 | u64 hash; | 72 | u64 hash{}; // AES256 with hardcoded key over AlbumEntry |
| 70 | AlbumFileDateTime datetime; | 73 | AlbumFileDateTime datetime{}; |
| 71 | AlbumStorage storage; | 74 | AlbumStorage storage{}; |
| 72 | ContentType content; | 75 | ContentType content{}; |
| 73 | u8 padding[5]; | 76 | INSERT_PADDING_BYTES(5); |
| 74 | u8 unknown; | 77 | u8 unknown{1}; // Set to 1 on official SW |
| 75 | }; | 78 | }; |
| 79 | static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size."); | ||
| 76 | 80 | ||
| 77 | struct ApplicationAlbumFileEntry { | 81 | struct ApplicationAlbumFileEntry { |
| 78 | ApplicationAlbumEntry entry; | 82 | ApplicationAlbumEntry entry{}; |
| 79 | AlbumFileDateTime datetime; | 83 | AlbumFileDateTime datetime{}; |
| 80 | u64 unknown; | 84 | u64 unknown{}; |
| 81 | }; | 85 | }; |
| 86 | static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30, | ||
| 87 | "ApplicationAlbumFileEntry has incorrect size."); | ||
| 82 | 88 | ||
| 83 | /// Registers all Capture services with the specified service manager. | 89 | /// Registers all Capture services with the specified service manager. |
| 84 | void InstallInterfaces(SM::ServiceManager& sm); | 90 | void InstallInterfaces(SM::ServiceManager& sm); |
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 88a3fdc05..a0a3b2ae3 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h index 8de832491..cb93aad5b 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp index ea6452ffa..ab17a187e 100644 --- a/src/core/hle/service/caps/caps_c.cpp +++ b/src/core/hle/service/caps/caps_c.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h index d07cdb441..a9d028689 100644 --- a/src/core/hle/service/caps/caps_c.h +++ b/src/core/hle/service/caps/caps_c.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp index d01a8a58e..822ee96c8 100644 --- a/src/core/hle/service/caps/caps_sc.cpp +++ b/src/core/hle/service/caps/caps_sc.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h index 9ba372f7a..ac3e929ca 100644 --- a/src/core/hle/service/caps/caps_sc.h +++ b/src/core/hle/service/caps/caps_sc.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp index eaa3a7494..24dc716e7 100644 --- a/src/core/hle/service/caps/caps_ss.cpp +++ b/src/core/hle/service/caps/caps_ss.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h index e258a6925..450686e4f 100644 --- a/src/core/hle/service/caps/caps_ss.h +++ b/src/core/hle/service/caps/caps_ss.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index e8b0698e8..fffb2ecf9 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h index c494d7c84..62c9603a9 100644 --- a/src/core/hle/service/caps/caps_su.h +++ b/src/core/hle/service/caps/caps_su.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index 78bab6ed8..f36d8de2d 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| @@ -58,19 +58,25 @@ void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& c | |||
| 58 | // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total | 58 | // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total |
| 59 | // output entries (which is copied to a s32 by official SW). | 59 | // output entries (which is copied to a s32 by official SW). |
| 60 | IPC::RequestParser rp{ctx}; | 60 | IPC::RequestParser rp{ctx}; |
| 61 | [[maybe_unused]] const auto application_album_file_entries = rp.PopRaw<std::array<u8, 0x30>>(); | 61 | const auto pid{rp.Pop<s32>()}; |
| 62 | const auto pid = rp.Pop<s32>(); | 62 | const auto content_type{rp.PopEnum<ContentType>()}; |
| 63 | const auto content_type = rp.PopRaw<ContentType>(); | 63 | const auto start_posix_time{rp.Pop<s64>()}; |
| 64 | [[maybe_unused]] const auto start_datetime = rp.PopRaw<AlbumFileDateTime>(); | 64 | const auto end_posix_time{rp.Pop<s64>()}; |
| 65 | [[maybe_unused]] const auto end_datetime = rp.PopRaw<AlbumFileDateTime>(); | 65 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 66 | const auto applet_resource_user_id = rp.Pop<u64>(); | 66 | |
| 67 | // TODO: Update this when we implement the album. | ||
| 68 | // Currently we do not have a method of accessing album entries, set this to 0 for now. | ||
| 69 | constexpr s32 total_entries{0}; | ||
| 70 | |||
| 67 | LOG_WARNING(Service_Capture, | 71 | LOG_WARNING(Service_Capture, |
| 68 | "(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid, | 72 | "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, " |
| 69 | content_type, applet_resource_user_id); | 73 | "end_posix_time={}, applet_resource_user_id={}, total_entries={}", |
| 74 | pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id, | ||
| 75 | total_entries); | ||
| 70 | 76 | ||
| 71 | IPC::ResponseBuilder rb{ctx, 3}; | 77 | IPC::ResponseBuilder rb{ctx, 3}; |
| 72 | rb.Push(RESULT_SUCCESS); | 78 | rb.Push(RESULT_SUCCESS); |
| 73 | rb.Push<s32>(0); | 79 | rb.Push(total_entries); |
| 74 | } | 80 | } |
| 75 | 81 | ||
| 76 | } // namespace Service::Capture | 82 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index e6e0716ff..689364de4 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | 1 | // Copyright 2020 yuzu Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index f8e9df4b1..9365f27e1 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp | |||
| @@ -27,8 +27,8 @@ public: | |||
| 27 | {8, &ETicket::GetTitleKey, "GetTitleKey"}, | 27 | {8, &ETicket::GetTitleKey, "GetTitleKey"}, |
| 28 | {9, &ETicket::CountCommonTicket, "CountCommonTicket"}, | 28 | {9, &ETicket::CountCommonTicket, "CountCommonTicket"}, |
| 29 | {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"}, | 29 | {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"}, |
| 30 | {11, &ETicket::ListCommonTicket, "ListCommonTicket"}, | 30 | {11, &ETicket::ListCommonTicketRightsIds, "ListCommonTicketRightsIds"}, |
| 31 | {12, &ETicket::ListPersonalizedTicket, "ListPersonalizedTicket"}, | 31 | {12, &ETicket::ListPersonalizedTicketRightsIds, "ListPersonalizedTicketRightsIds"}, |
| 32 | {13, nullptr, "ListMissingPersonalizedTicket"}, | 32 | {13, nullptr, "ListMissingPersonalizedTicket"}, |
| 33 | {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"}, | 33 | {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"}, |
| 34 | {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"}, | 34 | {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"}, |
| @@ -55,7 +55,46 @@ public: | |||
| 55 | {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"}, | 55 | {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"}, |
| 56 | {37, nullptr, "OwnTicket2"}, | 56 | {37, nullptr, "OwnTicket2"}, |
| 57 | {38, nullptr, "OwnTicket3"}, | 57 | {38, nullptr, "OwnTicket3"}, |
| 58 | {501, nullptr, "Unknown501"}, | ||
| 59 | {502, nullptr, "Unknown502"}, | ||
| 58 | {503, nullptr, "GetTitleKey"}, | 60 | {503, nullptr, "GetTitleKey"}, |
| 61 | {504, nullptr, "Unknown504"}, | ||
| 62 | {508, nullptr, "Unknown508"}, | ||
| 63 | {509, nullptr, "Unknown509"}, | ||
| 64 | {510, nullptr, "Unknown510"}, | ||
| 65 | {511, nullptr, "Unknown511"}, | ||
| 66 | {1001, nullptr, "Unknown1001"}, | ||
| 67 | {1002, nullptr, "Unknown1001"}, | ||
| 68 | {1003, nullptr, "Unknown1003"}, | ||
| 69 | {1004, nullptr, "Unknown1004"}, | ||
| 70 | {1005, nullptr, "Unknown1005"}, | ||
| 71 | {1006, nullptr, "Unknown1006"}, | ||
| 72 | {1007, nullptr, "Unknown1007"}, | ||
| 73 | {1009, nullptr, "Unknown1009"}, | ||
| 74 | {1010, nullptr, "Unknown1010"}, | ||
| 75 | {1011, nullptr, "Unknown1011"}, | ||
| 76 | {1012, nullptr, "Unknown1012"}, | ||
| 77 | {1013, nullptr, "Unknown1013"}, | ||
| 78 | {1014, nullptr, "Unknown1014"}, | ||
| 79 | {1015, nullptr, "Unknown1015"}, | ||
| 80 | {1016, nullptr, "Unknown1016"}, | ||
| 81 | {1017, nullptr, "Unknown1017"}, | ||
| 82 | {1018, nullptr, "Unknown1018"}, | ||
| 83 | {1019, nullptr, "Unknown1019"}, | ||
| 84 | {1020, nullptr, "Unknown1020"}, | ||
| 85 | {1021, nullptr, "Unknown1021"}, | ||
| 86 | {1501, nullptr, "Unknown1501"}, | ||
| 87 | {1502, nullptr, "Unknown1502"}, | ||
| 88 | {1503, nullptr, "Unknown1503"}, | ||
| 89 | {1504, nullptr, "Unknown1504"}, | ||
| 90 | {1505, nullptr, "Unknown1505"}, | ||
| 91 | {2000, nullptr, "Unknown2000"}, | ||
| 92 | {2001, nullptr, "Unknown2001"}, | ||
| 93 | {2100, nullptr, "Unknown2100"}, | ||
| 94 | {2501, nullptr, "Unknown2501"}, | ||
| 95 | {2502, nullptr, "Unknown2502"}, | ||
| 96 | {3001, nullptr, "Unknown3001"}, | ||
| 97 | {3002, nullptr, "Unknown3002"}, | ||
| 59 | }; | 98 | }; |
| 60 | // clang-format on | 99 | // clang-format on |
| 61 | RegisterHandlers(functions); | 100 | RegisterHandlers(functions); |
| @@ -147,7 +186,7 @@ private: | |||
| 147 | rb.Push<u32>(count); | 186 | rb.Push<u32>(count); |
| 148 | } | 187 | } |
| 149 | 188 | ||
| 150 | void ListCommonTicket(Kernel::HLERequestContext& ctx) { | 189 | void ListCommonTicketRightsIds(Kernel::HLERequestContext& ctx) { |
| 151 | u32 out_entries; | 190 | u32 out_entries; |
| 152 | if (keys.GetCommonTickets().empty()) | 191 | if (keys.GetCommonTickets().empty()) |
| 153 | out_entries = 0; | 192 | out_entries = 0; |
| @@ -170,7 +209,7 @@ private: | |||
| 170 | rb.Push<u32>(out_entries); | 209 | rb.Push<u32>(out_entries); |
| 171 | } | 210 | } |
| 172 | 211 | ||
| 173 | void ListPersonalizedTicket(Kernel::HLERequestContext& ctx) { | 212 | void ListPersonalizedTicketRightsIds(Kernel::HLERequestContext& ctx) { |
| 174 | u32 out_entries; | 213 | u32 out_entries; |
| 175 | if (keys.GetPersonalizedTickets().empty()) | 214 | if (keys.GetPersonalizedTickets().empty()) |
| 176 | out_entries = 0; | 215 | out_entries = 0; |
diff --git a/src/core/hle/service/eupld/eupld.cpp b/src/core/hle/service/eupld/eupld.cpp index 2df30acee..0d6d244f4 100644 --- a/src/core/hle/service/eupld/eupld.cpp +++ b/src/core/hle/service/eupld/eupld.cpp | |||
| @@ -19,6 +19,7 @@ public: | |||
| 19 | {1, nullptr, "ImportCrt"}, | 19 | {1, nullptr, "ImportCrt"}, |
| 20 | {2, nullptr, "ImportPki"}, | 20 | {2, nullptr, "ImportPki"}, |
| 21 | {3, nullptr, "SetAutoUpload"}, | 21 | {3, nullptr, "SetAutoUpload"}, |
| 22 | {4, nullptr, "GetAutoUpload"}, | ||
| 22 | }; | 23 | }; |
| 23 | // clang-format on | 24 | // clang-format on |
| 24 | 25 | ||
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 68f259b70..b7adaffc7 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -25,9 +25,13 @@ public: | |||
| 25 | {10101, &IFriendService::GetFriendList, "GetFriendList"}, | 25 | {10101, &IFriendService::GetFriendList, "GetFriendList"}, |
| 26 | {10102, nullptr, "UpdateFriendInfo"}, | 26 | {10102, nullptr, "UpdateFriendInfo"}, |
| 27 | {10110, nullptr, "GetFriendProfileImage"}, | 27 | {10110, nullptr, "GetFriendProfileImage"}, |
| 28 | {10120, nullptr, "Unknown10120"}, | ||
| 29 | {10121, nullptr, "Unknown10121"}, | ||
| 28 | {10200, nullptr, "SendFriendRequestForApplication"}, | 30 | {10200, nullptr, "SendFriendRequestForApplication"}, |
| 29 | {10211, nullptr, "AddFacedFriendRequestForApplication"}, | 31 | {10211, nullptr, "AddFacedFriendRequestForApplication"}, |
| 30 | {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, | 32 | {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, |
| 33 | {10420, nullptr, "Unknown10420"}, | ||
| 34 | {10421, nullptr, "Unknown10421"}, | ||
| 31 | {10500, nullptr, "GetProfileList"}, | 35 | {10500, nullptr, "GetProfileList"}, |
| 32 | {10600, nullptr, "DeclareOpenOnlinePlaySession"}, | 36 | {10600, nullptr, "DeclareOpenOnlinePlaySession"}, |
| 33 | {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"}, | 37 | {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"}, |
| @@ -97,6 +101,8 @@ public: | |||
| 97 | {30900, nullptr, "SendFriendInvitation"}, | 101 | {30900, nullptr, "SendFriendInvitation"}, |
| 98 | {30910, nullptr, "ReadFriendInvitation"}, | 102 | {30910, nullptr, "ReadFriendInvitation"}, |
| 99 | {30911, nullptr, "ReadAllFriendInvitations"}, | 103 | {30911, nullptr, "ReadAllFriendInvitations"}, |
| 104 | {40100, nullptr, "Unknown40100"}, | ||
| 105 | {40400, nullptr, "Unknown40400"}, | ||
| 100 | {49900, nullptr, "DeleteNetworkServiceAccountCache"}, | 106 | {49900, nullptr, "DeleteNetworkServiceAccountCache"}, |
| 101 | }; | 107 | }; |
| 102 | // clang-format on | 108 | // clang-format on |
diff --git a/src/core/hle/service/grc/grc.cpp b/src/core/hle/service/grc/grc.cpp index 24910ac6c..401e0b208 100644 --- a/src/core/hle/service/grc/grc.cpp +++ b/src/core/hle/service/grc/grc.cpp | |||
| @@ -17,6 +17,9 @@ public: | |||
| 17 | static const FunctionInfo functions[] = { | 17 | static const FunctionInfo functions[] = { |
| 18 | {1, nullptr, "OpenContinuousRecorder"}, | 18 | {1, nullptr, "OpenContinuousRecorder"}, |
| 19 | {2, nullptr, "OpenGameMovieTrimmer"}, | 19 | {2, nullptr, "OpenGameMovieTrimmer"}, |
| 20 | {3, nullptr, "OpenOffscreenRecorder"}, | ||
| 21 | {101, nullptr, "CreateMovieMaker"}, | ||
| 22 | {9903, nullptr, "SetOffscreenRecordingMarker"} | ||
| 20 | }; | 23 | }; |
| 21 | // clang-format on | 24 | // clang-format on |
| 22 | 25 | ||
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 1f2131ec8..cb35919e9 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -23,7 +23,7 @@ void Controller_DebugPad::OnRelease() {} | |||
| 23 | 23 | ||
| 24 | void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 24 | void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 25 | std::size_t size) { | 25 | std::size_t size) { |
| 26 | shared_memory.header.timestamp = core_timing.GetTicks(); | 26 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 27 | shared_memory.header.total_entry_count = 17; | 27 | shared_memory.header.total_entry_count = 17; |
| 28 | 28 | ||
| 29 | if (!IsControllerActivated()) { | 29 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 6e990dd00..b7b7bfeae 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp | |||
| @@ -19,7 +19,7 @@ void Controller_Gesture::OnRelease() {} | |||
| 19 | 19 | ||
| 20 | void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 20 | void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 21 | std::size_t size) { |
| 22 | shared_memory.header.timestamp = core_timing.GetTicks(); | 22 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 23 | shared_memory.header.total_entry_count = 17; | 23 | shared_memory.header.total_entry_count = 17; |
| 24 | 24 | ||
| 25 | if (!IsControllerActivated()) { | 25 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 9a8d354ba..feae89525 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp | |||
| @@ -21,7 +21,7 @@ void Controller_Keyboard::OnRelease() {} | |||
| 21 | 21 | ||
| 22 | void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 22 | void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 23 | std::size_t size) { | 23 | std::size_t size) { |
| 24 | shared_memory.header.timestamp = core_timing.GetTicks(); | 24 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 25 | shared_memory.header.total_entry_count = 17; | 25 | shared_memory.header.total_entry_count = 17; |
| 26 | 26 | ||
| 27 | if (!IsControllerActivated()) { | 27 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 93d88ea50..ac40989c5 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp | |||
| @@ -19,7 +19,7 @@ void Controller_Mouse::OnRelease() {} | |||
| 19 | 19 | ||
| 20 | void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 20 | void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 21 | std::size_t size) { |
| 22 | shared_memory.header.timestamp = core_timing.GetTicks(); | 22 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 23 | shared_memory.header.total_entry_count = 17; | 23 | shared_memory.header.total_entry_count = 17; |
| 24 | 24 | ||
| 25 | if (!IsControllerActivated()) { | 25 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index c55d900e2..ef67ad690 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -328,7 +328,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* | |||
| 328 | const auto& last_entry = | 328 | const auto& last_entry = |
| 329 | main_controller->npad[main_controller->common.last_entry_index]; | 329 | main_controller->npad[main_controller->common.last_entry_index]; |
| 330 | 330 | ||
| 331 | main_controller->common.timestamp = core_timing.GetTicks(); | 331 | main_controller->common.timestamp = core_timing.GetCPUTicks(); |
| 332 | main_controller->common.last_entry_index = | 332 | main_controller->common.last_entry_index = |
| 333 | (main_controller->common.last_entry_index + 1) % 17; | 333 | (main_controller->common.last_entry_index + 1) % 17; |
| 334 | 334 | ||
| @@ -566,6 +566,14 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) { | |||
| 566 | connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; | 566 | connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; |
| 567 | } | 567 | } |
| 568 | 568 | ||
| 569 | void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { | ||
| 570 | gyroscope_zero_drift_mode = drift_mode; | ||
| 571 | } | ||
| 572 | |||
| 573 | Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode() const { | ||
| 574 | return gyroscope_zero_drift_mode; | ||
| 575 | } | ||
| 576 | |||
| 569 | void Controller_NPad::StartLRAssignmentMode() { | 577 | void Controller_NPad::StartLRAssignmentMode() { |
| 570 | // Nothing internally is used for lr assignment mode. Since we have the ability to set the | 578 | // Nothing internally is used for lr assignment mode. Since we have the ability to set the |
| 571 | // controller types from boot, it doesn't really matter about showing a selection screen | 579 | // controller types from boot, it doesn't really matter about showing a selection screen |
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 931f03430..5d4c58a43 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h | |||
| @@ -58,6 +58,12 @@ public: | |||
| 58 | }; | 58 | }; |
| 59 | static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size"); | 59 | static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size"); |
| 60 | 60 | ||
| 61 | enum class GyroscopeZeroDriftMode : u32 { | ||
| 62 | Loose = 0, | ||
| 63 | Standard = 1, | ||
| 64 | Tight = 2, | ||
| 65 | }; | ||
| 66 | |||
| 61 | enum class NpadHoldType : u64 { | 67 | enum class NpadHoldType : u64 { |
| 62 | Vertical = 0, | 68 | Vertical = 0, |
| 63 | Horizontal = 1, | 69 | Horizontal = 1, |
| @@ -117,6 +123,8 @@ public: | |||
| 117 | 123 | ||
| 118 | void ConnectNPad(u32 npad_id); | 124 | void ConnectNPad(u32 npad_id); |
| 119 | void DisconnectNPad(u32 npad_id); | 125 | void DisconnectNPad(u32 npad_id); |
| 126 | void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); | ||
| 127 | GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; | ||
| 120 | LedPattern GetLedPattern(u32 npad_id); | 128 | LedPattern GetLedPattern(u32 npad_id); |
| 121 | void SetVibrationEnabled(bool can_vibrate); | 129 | void SetVibrationEnabled(bool can_vibrate); |
| 122 | bool IsVibrationEnabled() const; | 130 | bool IsVibrationEnabled() const; |
| @@ -324,8 +332,8 @@ private: | |||
| 324 | std::array<Kernel::EventPair, 10> styleset_changed_events; | 332 | std::array<Kernel::EventPair, 10> styleset_changed_events; |
| 325 | Vibration last_processed_vibration{}; | 333 | Vibration last_processed_vibration{}; |
| 326 | std::array<ControllerHolder, 10> connected_controllers{}; | 334 | std::array<ControllerHolder, 10> connected_controllers{}; |
| 335 | GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; | ||
| 327 | bool can_controllers_vibrate{true}; | 336 | bool can_controllers_vibrate{true}; |
| 328 | |||
| 329 | std::array<ControllerPad, 10> npad_pad_states{}; | 337 | std::array<ControllerPad, 10> npad_pad_states{}; |
| 330 | bool is_in_lr_assignment_mode{false}; | 338 | bool is_in_lr_assignment_mode{false}; |
| 331 | Core::System& system; | 339 | Core::System& system; |
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 9e527d176..e7483bfa2 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp | |||
| @@ -23,7 +23,7 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u | |||
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | CommonHeader header{}; | 25 | CommonHeader header{}; |
| 26 | header.timestamp = core_timing.GetTicks(); | 26 | header.timestamp = core_timing.GetCPUTicks(); |
| 27 | header.total_entry_count = 17; | 27 | header.total_entry_count = 17; |
| 28 | header.entry_count = 0; | 28 | header.entry_count = 0; |
| 29 | header.last_entry_index = 0; | 29 | header.last_entry_index = 0; |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 1c6e55566..e326f8f5c 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp | |||
| @@ -22,7 +22,7 @@ void Controller_Touchscreen::OnRelease() {} | |||
| 22 | 22 | ||
| 23 | void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 23 | void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 24 | std::size_t size) { | 24 | std::size_t size) { |
| 25 | shared_memory.header.timestamp = core_timing.GetTicks(); | 25 | shared_memory.header.timestamp = core_timing.GetCPUTicks(); |
| 26 | shared_memory.header.total_entry_count = 17; | 26 | shared_memory.header.total_entry_count = 17; |
| 27 | 27 | ||
| 28 | if (!IsControllerActivated()) { | 28 | if (!IsControllerActivated()) { |
| @@ -49,7 +49,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin | |||
| 49 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; | 49 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; |
| 50 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; | 50 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; |
| 51 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; | 51 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; |
| 52 | const u64 tick = core_timing.GetTicks(); | 52 | const u64 tick = core_timing.GetCPUTicks(); |
| 53 | touch_entry.delta_time = tick - last_touch; | 53 | touch_entry.delta_time = tick - last_touch; |
| 54 | last_touch = tick; | 54 | last_touch = tick; |
| 55 | touch_entry.finger = Settings::values.touchscreen.finger; | 55 | touch_entry.finger = Settings::values.touchscreen.finger; |
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index 27511b27b..2503ef241 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp | |||
| @@ -20,7 +20,7 @@ void Controller_XPad::OnRelease() {} | |||
| 20 | void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 20 | void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 21 | std::size_t size) { | 21 | std::size_t size) { |
| 22 | for (auto& xpad_entry : shared_memory.shared_memory_entries) { | 22 | for (auto& xpad_entry : shared_memory.shared_memory_entries) { |
| 23 | xpad_entry.header.timestamp = core_timing.GetTicks(); | 23 | xpad_entry.header.timestamp = core_timing.GetCPUTicks(); |
| 24 | xpad_entry.header.total_entry_count = 17; | 24 | xpad_entry.header.total_entry_count = 17; |
| 25 | 25 | ||
| 26 | if (!IsControllerActivated()) { | 26 | if (!IsControllerActivated()) { |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 72a050de2..e9020e0dc 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -39,11 +39,9 @@ namespace Service::HID { | |||
| 39 | 39 | ||
| 40 | // Updating period for each HID device. | 40 | // Updating period for each HID device. |
| 41 | // TODO(ogniK): Find actual polling rate of hid | 41 | // TODO(ogniK): Find actual polling rate of hid |
| 42 | constexpr s64 pad_update_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 66); | 42 | constexpr s64 pad_update_ticks = static_cast<s64>(1000000000 / 66); |
| 43 | [[maybe_unused]] constexpr s64 accelerometer_update_ticks = | 43 | [[maybe_unused]] constexpr s64 accelerometer_update_ticks = static_cast<s64>(1000000000 / 100); |
| 44 | static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100); | 44 | [[maybe_unused]] constexpr s64 gyroscope_update_ticks = static_cast<s64>(1000000000 / 100); |
| 45 | [[maybe_unused]] constexpr s64 gyroscope_update_ticks = | ||
| 46 | static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100); | ||
| 47 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 45 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
| 48 | 46 | ||
| 49 | IAppletResource::IAppletResource(Core::System& system) | 47 | IAppletResource::IAppletResource(Core::System& system) |
| @@ -78,8 +76,8 @@ IAppletResource::IAppletResource(Core::System& system) | |||
| 78 | 76 | ||
| 79 | // Register update callbacks | 77 | // Register update callbacks |
| 80 | pad_update_event = | 78 | pad_update_event = |
| 81 | Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { | 79 | Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 ns_late) { |
| 82 | UpdateControllers(userdata, cycles_late); | 80 | UpdateControllers(userdata, ns_late); |
| 83 | }); | 81 | }); |
| 84 | 82 | ||
| 85 | // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) | 83 | // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) |
| @@ -109,7 +107,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | |||
| 109 | rb.PushCopyObjects(shared_mem); | 107 | rb.PushCopyObjects(shared_mem); |
| 110 | } | 108 | } |
| 111 | 109 | ||
| 112 | void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { | 110 | void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) { |
| 113 | auto& core_timing = system.CoreTiming(); | 111 | auto& core_timing = system.CoreTiming(); |
| 114 | 112 | ||
| 115 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); | 113 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); |
| @@ -120,7 +118,7 @@ void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { | |||
| 120 | controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); | 118 | controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); |
| 121 | } | 119 | } |
| 122 | 120 | ||
| 123 | core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); | 121 | core_timing.ScheduleEvent(pad_update_ticks - ns_late, pad_update_event); |
| 124 | } | 122 | } |
| 125 | 123 | ||
| 126 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { | 124 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { |
| @@ -185,8 +183,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { | |||
| 185 | {77, nullptr, "GetAccelerometerPlayMode"}, | 183 | {77, nullptr, "GetAccelerometerPlayMode"}, |
| 186 | {78, nullptr, "ResetAccelerometerPlayMode"}, | 184 | {78, nullptr, "ResetAccelerometerPlayMode"}, |
| 187 | {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, | 185 | {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, |
| 188 | {80, nullptr, "GetGyroscopeZeroDriftMode"}, | 186 | {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"}, |
| 189 | {81, nullptr, "ResetGyroscopeZeroDriftMode"}, | 187 | {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, |
| 190 | {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, | 188 | {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, |
| 191 | {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"}, | 189 | {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"}, |
| 192 | {91, &Hid::ActivateGesture, "ActivateGesture"}, | 190 | {91, &Hid::ActivateGesture, "ActivateGesture"}, |
| @@ -230,15 +228,15 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { | |||
| 230 | {211, nullptr, "IsVibrationDeviceMounted"}, | 228 | {211, nullptr, "IsVibrationDeviceMounted"}, |
| 231 | {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, | 229 | {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, |
| 232 | {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, | 230 | {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, |
| 233 | {302, nullptr, "StopConsoleSixAxisSensor"}, | 231 | {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, |
| 234 | {303, nullptr, "ActivateSevenSixAxisSensor"}, | 232 | {303, &Hid::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"}, |
| 235 | {304, nullptr, "StartSevenSixAxisSensor"}, | 233 | {304, &Hid::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"}, |
| 236 | {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, | 234 | {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, |
| 237 | {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, | 235 | {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, |
| 238 | {307, nullptr, "FinalizeSevenSixAxisSensor"}, | 236 | {307, &Hid::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"}, |
| 239 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, | 237 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, |
| 240 | {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, | 238 | {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, |
| 241 | {310, nullptr, "ResetSevenSixAxisSensorTimestamp"}, | 239 | {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, |
| 242 | {400, nullptr, "IsUsbFullKeyControllerEnabled"}, | 240 | {400, nullptr, "IsUsbFullKeyControllerEnabled"}, |
| 243 | {401, nullptr, "EnableUsbFullKeyController"}, | 241 | {401, nullptr, "EnableUsbFullKeyController"}, |
| 244 | {402, nullptr, "IsUsbFullKeyControllerConnected"}, | 242 | {402, nullptr, "IsUsbFullKeyControllerConnected"}, |
| @@ -374,6 +372,15 @@ void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) { | |||
| 374 | rb.Push(RESULT_SUCCESS); | 372 | rb.Push(RESULT_SUCCESS); |
| 375 | } | 373 | } |
| 376 | 374 | ||
| 375 | void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { | ||
| 376 | IPC::RequestParser rp{ctx}; | ||
| 377 | const auto flags{rp.Pop<u32>()}; | ||
| 378 | LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); | ||
| 379 | |||
| 380 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 381 | rb.Push(RESULT_SUCCESS); | ||
| 382 | } | ||
| 383 | |||
| 377 | void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { | 384 | void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { |
| 378 | IPC::RequestParser rp{ctx}; | 385 | IPC::RequestParser rp{ctx}; |
| 379 | const auto unknown{rp.Pop<u32>()}; | 386 | const auto unknown{rp.Pop<u32>()}; |
| @@ -413,15 +420,59 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 413 | rb.Push(RESULT_SUCCESS); | 420 | rb.Push(RESULT_SUCCESS); |
| 414 | } | 421 | } |
| 415 | 422 | ||
| 423 | void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { | ||
| 424 | IPC::RequestParser rp{ctx}; | ||
| 425 | const auto handle{rp.Pop<u32>()}; | ||
| 426 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 427 | |||
| 428 | LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, | ||
| 429 | applet_resource_user_id); | ||
| 430 | |||
| 431 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 432 | rb.Push(RESULT_SUCCESS); | ||
| 433 | } | ||
| 434 | |||
| 416 | void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | 435 | void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { |
| 417 | IPC::RequestParser rp{ctx}; | 436 | IPC::RequestParser rp{ctx}; |
| 418 | const auto handle{rp.Pop<u32>()}; | 437 | const auto handle{rp.Pop<u32>()}; |
| 419 | const auto drift_mode{rp.Pop<u32>()}; | 438 | const auto drift_mode{rp.Pop<u32>()}; |
| 420 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 439 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 421 | 440 | ||
| 422 | LOG_WARNING(Service_HID, | 441 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 423 | "(STUBBED) called, handle={}, drift_mode={}, applet_resource_user_id={}", handle, | 442 | .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode{drift_mode}); |
| 424 | drift_mode, applet_resource_user_id); | 443 | |
| 444 | LOG_DEBUG(Service_HID, "called, handle={}, drift_mode={}, applet_resource_user_id={}", handle, | ||
| 445 | drift_mode, applet_resource_user_id); | ||
| 446 | |||
| 447 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 448 | rb.Push(RESULT_SUCCESS); | ||
| 449 | } | ||
| 450 | |||
| 451 | void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | ||
| 452 | IPC::RequestParser rp{ctx}; | ||
| 453 | const auto handle{rp.Pop<u32>()}; | ||
| 454 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 455 | |||
| 456 | LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, | ||
| 457 | applet_resource_user_id); | ||
| 458 | |||
| 459 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 460 | rb.Push(RESULT_SUCCESS); | ||
| 461 | rb.Push<u32>( | ||
| 462 | static_cast<u32>(applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 463 | .GetGyroscopeZeroDriftMode())); | ||
| 464 | } | ||
| 465 | |||
| 466 | void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | ||
| 467 | IPC::RequestParser rp{ctx}; | ||
| 468 | const auto handle{rp.Pop<u32>()}; | ||
| 469 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 470 | |||
| 471 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 472 | .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard); | ||
| 473 | |||
| 474 | LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, | ||
| 475 | applet_resource_user_id); | ||
| 425 | 476 | ||
| 426 | IPC::ResponseBuilder rb{ctx, 2}; | 477 | IPC::ResponseBuilder rb{ctx, 2}; |
| 427 | rb.Push(RESULT_SUCCESS); | 478 | rb.Push(RESULT_SUCCESS); |
| @@ -832,33 +883,35 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 832 | rb.Push(RESULT_SUCCESS); | 883 | rb.Push(RESULT_SUCCESS); |
| 833 | } | 884 | } |
| 834 | 885 | ||
| 835 | void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { | 886 | void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 836 | IPC::RequestParser rp{ctx}; | 887 | IPC::RequestParser rp{ctx}; |
| 837 | const auto handle{rp.Pop<u32>()}; | 888 | const auto handle{rp.Pop<u32>()}; |
| 889 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 838 | 890 | ||
| 839 | LOG_WARNING(Service_HID, "(STUBBED) called, handle={}", handle); | 891 | LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, |
| 892 | applet_resource_user_id); | ||
| 840 | 893 | ||
| 841 | IPC::ResponseBuilder rb{ctx, 2}; | 894 | IPC::ResponseBuilder rb{ctx, 2}; |
| 842 | rb.Push(RESULT_SUCCESS); | 895 | rb.Push(RESULT_SUCCESS); |
| 843 | } | 896 | } |
| 844 | 897 | ||
| 845 | void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { | 898 | void Hid::ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 846 | IPC::RequestParser rp{ctx}; | 899 | IPC::RequestParser rp{ctx}; |
| 847 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 900 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 848 | const auto unknown{rp.Pop<u32>()}; | ||
| 849 | 901 | ||
| 850 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, unknown={}", | 902 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", |
| 851 | applet_resource_user_id, unknown); | 903 | applet_resource_user_id); |
| 852 | 904 | ||
| 853 | IPC::ResponseBuilder rb{ctx, 2}; | 905 | IPC::ResponseBuilder rb{ctx, 2}; |
| 854 | rb.Push(RESULT_SUCCESS); | 906 | rb.Push(RESULT_SUCCESS); |
| 855 | } | 907 | } |
| 856 | 908 | ||
| 857 | void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { | 909 | void Hid::StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 858 | IPC::RequestParser rp{ctx}; | 910 | IPC::RequestParser rp{ctx}; |
| 859 | const auto unknown{rp.Pop<u32>()}; | 911 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 860 | 912 | ||
| 861 | LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}", unknown); | 913 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", |
| 914 | applet_resource_user_id); | ||
| 862 | 915 | ||
| 863 | IPC::ResponseBuilder rb{ctx, 2}; | 916 | IPC::ResponseBuilder rb{ctx, 2}; |
| 864 | rb.Push(RESULT_SUCCESS); | 917 | rb.Push(RESULT_SUCCESS); |
| @@ -882,10 +935,46 @@ void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 882 | rb.Push(RESULT_SUCCESS); | 935 | rb.Push(RESULT_SUCCESS); |
| 883 | } | 936 | } |
| 884 | 937 | ||
| 885 | void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { | 938 | void Hid::FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 886 | IPC::RequestParser rp{ctx}; | 939 | IPC::RequestParser rp{ctx}; |
| 887 | const auto flags{rp.Pop<u32>()}; | 940 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 888 | LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); | 941 | |
| 942 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 943 | applet_resource_user_id); | ||
| 944 | |||
| 945 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 946 | rb.Push(RESULT_SUCCESS); | ||
| 947 | } | ||
| 948 | |||
| 949 | void Hid::ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx) { | ||
| 950 | IPC::RequestParser rp{ctx}; | ||
| 951 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 952 | |||
| 953 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 954 | applet_resource_user_id); | ||
| 955 | |||
| 956 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 957 | rb.Push(RESULT_SUCCESS); | ||
| 958 | } | ||
| 959 | |||
| 960 | void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { | ||
| 961 | IPC::RequestParser rp{ctx}; | ||
| 962 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 963 | const auto is_palma_all_connectable{rp.Pop<bool>()}; | ||
| 964 | |||
| 965 | LOG_WARNING(Service_HID, | ||
| 966 | "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}", | ||
| 967 | applet_resource_user_id, is_palma_all_connectable); | ||
| 968 | |||
| 969 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 970 | rb.Push(RESULT_SUCCESS); | ||
| 971 | } | ||
| 972 | |||
| 973 | void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { | ||
| 974 | IPC::RequestParser rp{ctx}; | ||
| 975 | const auto palma_boost_mode{rp.Pop<bool>()}; | ||
| 976 | |||
| 977 | LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); | ||
| 889 | 978 | ||
| 890 | IPC::ResponseBuilder rb{ctx, 2}; | 979 | IPC::ResponseBuilder rb{ctx, 2}; |
| 891 | rb.Push(RESULT_SUCCESS); | 980 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index d481a75f8..6fb048360 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -91,10 +91,14 @@ private: | |||
| 91 | void ActivateTouchScreen(Kernel::HLERequestContext& ctx); | 91 | void ActivateTouchScreen(Kernel::HLERequestContext& ctx); |
| 92 | void ActivateMouse(Kernel::HLERequestContext& ctx); | 92 | void ActivateMouse(Kernel::HLERequestContext& ctx); |
| 93 | void ActivateKeyboard(Kernel::HLERequestContext& ctx); | 93 | void ActivateKeyboard(Kernel::HLERequestContext& ctx); |
| 94 | void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx); | ||
| 94 | void ActivateGesture(Kernel::HLERequestContext& ctx); | 95 | void ActivateGesture(Kernel::HLERequestContext& ctx); |
| 95 | void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); | 96 | void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); |
| 96 | void StartSixAxisSensor(Kernel::HLERequestContext& ctx); | 97 | void StartSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 98 | void StopSixAxisSensor(Kernel::HLERequestContext& ctx); | ||
| 97 | void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); | 99 | void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); |
| 100 | void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); | ||
| 101 | void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); | ||
| 98 | void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); | 102 | void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); |
| 99 | void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); | 103 | void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); |
| 100 | void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); | 104 | void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); |
| @@ -126,12 +130,15 @@ private: | |||
| 126 | void IsVibrationPermitted(Kernel::HLERequestContext& ctx); | 130 | void IsVibrationPermitted(Kernel::HLERequestContext& ctx); |
| 127 | void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); | 131 | void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 128 | void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); | 132 | void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 129 | void StopSixAxisSensor(Kernel::HLERequestContext& ctx); | 133 | void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 130 | void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); | 134 | void ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 131 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); | 135 | void StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 132 | void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx); | 136 | void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 133 | void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); | 137 | void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 134 | void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx); | 138 | void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 139 | void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); | ||
| 140 | void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); | ||
| 141 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); | ||
| 135 | 142 | ||
| 136 | std::shared_ptr<IAppletResource> applet_resource; | 143 | std::shared_ptr<IAppletResource> applet_resource; |
| 137 | Core::System& system; | 144 | Core::System& system; |
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 36ed6f7da..e82fd031b 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp | |||
| @@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { | |||
| 98 | 98 | ||
| 99 | IPC::ResponseBuilder rb{ctx, 5}; | 99 | IPC::ResponseBuilder rb{ctx, 5}; |
| 100 | rb.Push(RESULT_SUCCESS); | 100 | rb.Push(RESULT_SUCCESS); |
| 101 | rb.PushRaw<u64>(system.CoreTiming().GetTicks()); | 101 | rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks()); |
| 102 | rb.PushRaw<u32>(0); | 102 | rb.PushRaw<u32>(0); |
| 103 | } | 103 | } |
| 104 | 104 | ||
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index e8f9f2d29..17350b403 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp | |||
| @@ -47,6 +47,7 @@ public: | |||
| 47 | {26, &LBL::EnableVrMode, "EnableVrMode"}, | 47 | {26, &LBL::EnableVrMode, "EnableVrMode"}, |
| 48 | {27, &LBL::DisableVrMode, "DisableVrMode"}, | 48 | {27, &LBL::DisableVrMode, "DisableVrMode"}, |
| 49 | {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"}, | 49 | {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"}, |
| 50 | {29, nullptr, "IsAutoBrightnessControlSupported"}, | ||
| 50 | }; | 51 | }; |
| 51 | // clang-format on | 52 | // clang-format on |
| 52 | 53 | ||
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 92adde6d4..49972cd69 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp | |||
| @@ -69,6 +69,7 @@ public: | |||
| 69 | {101, nullptr, "GetNetworkInfoLatestUpdate"}, | 69 | {101, nullptr, "GetNetworkInfoLatestUpdate"}, |
| 70 | {102, nullptr, "Scan"}, | 70 | {102, nullptr, "Scan"}, |
| 71 | {103, nullptr, "ScanPrivate"}, | 71 | {103, nullptr, "ScanPrivate"}, |
| 72 | {104, nullptr, "SetWirelessControllerRestriction"}, | ||
| 72 | {200, nullptr, "OpenAccessPoint"}, | 73 | {200, nullptr, "OpenAccessPoint"}, |
| 73 | {201, nullptr, "CloseAccessPoint"}, | 74 | {201, nullptr, "CloseAccessPoint"}, |
| 74 | {202, nullptr, "CreateNetwork"}, | 75 | {202, nullptr, "CreateNetwork"}, |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 6ad3be1b3..64a526b9e 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -39,42 +39,61 @@ constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; | |||
| 39 | constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; | 39 | constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; |
| 40 | constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; | 40 | constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; |
| 41 | 41 | ||
| 42 | constexpr std::size_t TEXT_INDEX{0}; | ||
| 43 | constexpr std::size_t RO_INDEX{1}; | ||
| 44 | constexpr std::size_t DATA_INDEX{2}; | ||
| 45 | |||
| 46 | struct NRRCertification { | ||
| 47 | u64_le application_id_mask; | ||
| 48 | u64_le application_id_pattern; | ||
| 49 | INSERT_PADDING_BYTES(0x10); | ||
| 50 | std::array<u8, 0x100> public_key; // Also known as modulus | ||
| 51 | std::array<u8, 0x100> signature; | ||
| 52 | }; | ||
| 53 | static_assert(sizeof(NRRCertification) == 0x220, "NRRCertification has invalid size."); | ||
| 54 | |||
| 42 | struct NRRHeader { | 55 | struct NRRHeader { |
| 43 | u32_le magic; | 56 | u32_le magic; |
| 44 | INSERT_PADDING_BYTES(12); | 57 | u32_le certification_signature_key_generation; // 9.0.0+ |
| 45 | u64_le title_id_mask; | 58 | INSERT_PADDING_WORDS(2); |
| 46 | u64_le title_id_pattern; | 59 | NRRCertification certification; |
| 47 | INSERT_PADDING_BYTES(16); | 60 | std::array<u8, 0x100> signature; |
| 48 | std::array<u8, 0x100> modulus; | 61 | u64_le application_id; |
| 49 | std::array<u8, 0x100> signature_1; | ||
| 50 | std::array<u8, 0x100> signature_2; | ||
| 51 | u64_le title_id; | ||
| 52 | u32_le size; | 62 | u32_le size; |
| 53 | INSERT_PADDING_BYTES(4); | 63 | u8 nrr_kind; // 7.0.0+ |
| 64 | INSERT_PADDING_BYTES(3); | ||
| 54 | u32_le hash_offset; | 65 | u32_le hash_offset; |
| 55 | u32_le hash_count; | 66 | u32_le hash_count; |
| 56 | INSERT_PADDING_BYTES(8); | 67 | INSERT_PADDING_WORDS(2); |
| 68 | }; | ||
| 69 | static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size."); | ||
| 70 | |||
| 71 | struct SegmentHeader { | ||
| 72 | u32_le memory_offset; | ||
| 73 | u32_le memory_size; | ||
| 57 | }; | 74 | }; |
| 58 | static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); | 75 | static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size."); |
| 59 | 76 | ||
| 60 | struct NROHeader { | 77 | struct NROHeader { |
| 78 | // Switchbrew calls this "Start" (0x10) | ||
| 61 | INSERT_PADDING_WORDS(1); | 79 | INSERT_PADDING_WORDS(1); |
| 62 | u32_le mod_offset; | 80 | u32_le mod_offset; |
| 63 | INSERT_PADDING_WORDS(2); | 81 | INSERT_PADDING_WORDS(2); |
| 82 | |||
| 83 | // Switchbrew calls this "Header" (0x70) | ||
| 64 | u32_le magic; | 84 | u32_le magic; |
| 65 | u32_le version; | 85 | u32_le version; |
| 66 | u32_le nro_size; | 86 | u32_le nro_size; |
| 67 | u32_le flags; | 87 | u32_le flags; |
| 68 | u32_le text_offset; | 88 | // .text, .ro, .data |
| 69 | u32_le text_size; | 89 | std::array<SegmentHeader, 3> segment_headers; |
| 70 | u32_le ro_offset; | ||
| 71 | u32_le ro_size; | ||
| 72 | u32_le rw_offset; | ||
| 73 | u32_le rw_size; | ||
| 74 | u32_le bss_size; | 90 | u32_le bss_size; |
| 75 | INSERT_PADDING_WORDS(1); | 91 | INSERT_PADDING_WORDS(1); |
| 76 | std::array<u8, 0x20> build_id; | 92 | std::array<u8, 0x20> build_id; |
| 77 | INSERT_PADDING_BYTES(0x20); | 93 | u32_le dso_handle_offset; |
| 94 | INSERT_PADDING_WORDS(1); | ||
| 95 | // .apiInfo, .dynstr, .dynsym | ||
| 96 | std::array<SegmentHeader, 3> segment_headers_2; | ||
| 78 | }; | 97 | }; |
| 79 | static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); | 98 | static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); |
| 80 | 99 | ||
| @@ -91,6 +110,7 @@ struct NROInfo { | |||
| 91 | std::size_t data_size{}; | 110 | std::size_t data_size{}; |
| 92 | VAddr src_addr{}; | 111 | VAddr src_addr{}; |
| 93 | }; | 112 | }; |
| 113 | static_assert(sizeof(NROInfo) == 0x60, "NROInfo has invalid size."); | ||
| 94 | 114 | ||
| 95 | class DebugMonitor final : public ServiceFramework<DebugMonitor> { | 115 | class DebugMonitor final : public ServiceFramework<DebugMonitor> { |
| 96 | public: | 116 | public: |
| @@ -226,11 +246,11 @@ public: | |||
| 226 | return; | 246 | return; |
| 227 | } | 247 | } |
| 228 | 248 | ||
| 229 | if (system.CurrentProcess()->GetTitleID() != header.title_id) { | 249 | if (system.CurrentProcess()->GetTitleID() != header.application_id) { |
| 230 | LOG_ERROR(Service_LDR, | 250 | LOG_ERROR(Service_LDR, |
| 231 | "Attempting to load NRR with title ID other than current process. (actual " | 251 | "Attempting to load NRR with title ID other than current process. (actual " |
| 232 | "{:016X})!", | 252 | "{:016X})!", |
| 233 | header.title_id); | 253 | header.application_id); |
| 234 | IPC::ResponseBuilder rb{ctx, 2}; | 254 | IPC::ResponseBuilder rb{ctx, 2}; |
| 235 | rb.Push(ERROR_INVALID_NRR); | 255 | rb.Push(ERROR_INVALID_NRR); |
| 236 | return; | 256 | return; |
| @@ -348,10 +368,10 @@ public: | |||
| 348 | 368 | ||
| 349 | ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, | 369 | ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, |
| 350 | VAddr start) const { | 370 | VAddr start) const { |
| 351 | const VAddr text_start{start + nro_header.text_offset}; | 371 | const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset}; |
| 352 | const VAddr ro_start{start + nro_header.ro_offset}; | 372 | const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset}; |
| 353 | const VAddr data_start{start + nro_header.rw_offset}; | 373 | const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset}; |
| 354 | const VAddr bss_start{data_start + nro_header.rw_size}; | 374 | const VAddr bss_start{data_start + nro_header.segment_headers[DATA_INDEX].memory_size}; |
| 355 | const VAddr bss_end_addr{ | 375 | const VAddr bss_end_addr{ |
| 356 | Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; | 376 | Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; |
| 357 | 377 | ||
| @@ -360,9 +380,12 @@ public: | |||
| 360 | system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); | 380 | system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); |
| 361 | system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); | 381 | system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); |
| 362 | }}; | 382 | }}; |
| 363 | CopyCode(nro_addr + nro_header.text_offset, text_start, nro_header.text_size); | 383 | CopyCode(nro_addr + nro_header.segment_headers[TEXT_INDEX].memory_offset, text_start, |
| 364 | CopyCode(nro_addr + nro_header.ro_offset, ro_start, nro_header.ro_size); | 384 | nro_header.segment_headers[TEXT_INDEX].memory_size); |
| 365 | CopyCode(nro_addr + nro_header.rw_offset, data_start, nro_header.rw_size); | 385 | CopyCode(nro_addr + nro_header.segment_headers[RO_INDEX].memory_offset, ro_start, |
| 386 | nro_header.segment_headers[RO_INDEX].memory_size); | ||
| 387 | CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, | ||
| 388 | nro_header.segment_headers[DATA_INDEX].memory_size); | ||
| 366 | 389 | ||
| 367 | CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( | 390 | CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( |
| 368 | text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); | 391 | text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); |
| @@ -484,9 +507,11 @@ public: | |||
| 484 | } | 507 | } |
| 485 | 508 | ||
| 486 | // Track the loaded NRO | 509 | // Track the loaded NRO |
| 487 | nro.insert_or_assign(*map_result, NROInfo{hash, *map_result, nro_size, bss_address, | 510 | nro.insert_or_assign(*map_result, |
| 488 | bss_size, header.text_size, header.ro_size, | 511 | NROInfo{hash, *map_result, nro_size, bss_address, bss_size, |
| 489 | header.rw_size, nro_address}); | 512 | header.segment_headers[TEXT_INDEX].memory_size, |
| 513 | header.segment_headers[RO_INDEX].memory_size, | ||
| 514 | header.segment_headers[DATA_INDEX].memory_size, nro_address}); | ||
| 490 | 515 | ||
| 491 | // Invalidate JIT caches for the newly mapped process code | 516 | // Invalidate JIT caches for the newly mapped process code |
| 492 | system.InvalidateCpuInstructionCaches(); | 517 | system.InvalidateCpuInstructionCaches(); |
| @@ -584,11 +609,21 @@ private: | |||
| 584 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { | 609 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { |
| 585 | return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && | 610 | return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && |
| 586 | header.nro_size == nro_size && header.bss_size == bss_size && | 611 | header.nro_size == nro_size && header.bss_size == bss_size && |
| 587 | header.ro_offset == header.text_offset + header.text_size && | 612 | |
| 588 | header.rw_offset == header.ro_offset + header.ro_size && | 613 | header.segment_headers[RO_INDEX].memory_offset == |
| 589 | nro_size == header.rw_offset + header.rw_size && | 614 | header.segment_headers[TEXT_INDEX].memory_offset + |
| 590 | Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && | 615 | header.segment_headers[TEXT_INDEX].memory_size && |
| 591 | Common::Is4KBAligned(header.rw_size); | 616 | |
| 617 | header.segment_headers[DATA_INDEX].memory_offset == | ||
| 618 | header.segment_headers[RO_INDEX].memory_offset + | ||
| 619 | header.segment_headers[RO_INDEX].memory_size && | ||
| 620 | |||
| 621 | nro_size == header.segment_headers[DATA_INDEX].memory_offset + | ||
| 622 | header.segment_headers[DATA_INDEX].memory_size && | ||
| 623 | |||
| 624 | Common::Is4KBAligned(header.segment_headers[TEXT_INDEX].memory_size) && | ||
| 625 | Common::Is4KBAligned(header.segment_headers[RO_INDEX].memory_size) && | ||
| 626 | Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size); | ||
| 592 | } | 627 | } |
| 593 | Core::System& system; | 628 | Core::System& system; |
| 594 | }; | 629 | }; |
diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp index b67081b86..3ee2374e7 100644 --- a/src/core/hle/service/lm/manager.cpp +++ b/src/core/hle/service/lm/manager.cpp | |||
| @@ -86,7 +86,8 @@ std::string FormatField(Field type, const std::vector<u8>& data) { | |||
| 86 | return Common::StringFromFixedZeroTerminatedBuffer( | 86 | return Common::StringFromFixedZeroTerminatedBuffer( |
| 87 | reinterpret_cast<const char*>(data.data()), data.size()); | 87 | reinterpret_cast<const char*>(data.data()), data.size()); |
| 88 | default: | 88 | default: |
| 89 | UNIMPLEMENTED(); | 89 | UNIMPLEMENTED_MSG("Unimplemented field type={}", type); |
| 90 | return ""; | ||
| 90 | } | 91 | } |
| 91 | } | 92 | } |
| 92 | 93 | ||
diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp index d16367f2c..113a4665c 100644 --- a/src/core/hle/service/mig/mig.cpp +++ b/src/core/hle/service/mig/mig.cpp | |||
| @@ -20,6 +20,12 @@ public: | |||
| 20 | {101, nullptr, "ResumeServer"}, | 20 | {101, nullptr, "ResumeServer"}, |
| 21 | {200, nullptr, "CreateClient"}, | 21 | {200, nullptr, "CreateClient"}, |
| 22 | {201, nullptr, "ResumeClient"}, | 22 | {201, nullptr, "ResumeClient"}, |
| 23 | {1001, nullptr, "Unknown1001"}, | ||
| 24 | {1010, nullptr, "Unknown1010"}, | ||
| 25 | {1100, nullptr, "Unknown1100"}, | ||
| 26 | {1101, nullptr, "Unknown1101"}, | ||
| 27 | {1200, nullptr, "Unknown1200"}, | ||
| 28 | {1201, nullptr, "Unknown1201"} | ||
| 23 | }; | 29 | }; |
| 24 | // clang-format on | 30 | // clang-format on |
| 25 | 31 | ||
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp index def63dc8a..25c24e537 100644 --- a/src/core/hle/service/mm/mm_u.cpp +++ b/src/core/hle/service/mm/mm_u.cpp | |||
| @@ -14,14 +14,14 @@ public: | |||
| 14 | explicit MM_U() : ServiceFramework{"mm:u"} { | 14 | explicit MM_U() : ServiceFramework{"mm:u"} { |
| 15 | // clang-format off | 15 | // clang-format off |
| 16 | static const FunctionInfo functions[] = { | 16 | static const FunctionInfo functions[] = { |
| 17 | {0, &MM_U::Initialize, "Initialize"}, | 17 | {0, &MM_U::InitializeOld, "InitializeOld"}, |
| 18 | {1, &MM_U::Finalize, "Finalize"}, | 18 | {1, &MM_U::FinalizeOld, "FinalizeOld"}, |
| 19 | {2, &MM_U::SetAndWait, "SetAndWait"}, | 19 | {2, &MM_U::SetAndWaitOld, "SetAndWaitOld"}, |
| 20 | {3, &MM_U::Get, "Get"}, | 20 | {3, &MM_U::GetOld, "GetOld"}, |
| 21 | {4, &MM_U::InitializeWithId, "InitializeWithId"}, | 21 | {4, &MM_U::Initialize, "Initialize"}, |
| 22 | {5, &MM_U::FinalizeWithId, "FinalizeWithId"}, | 22 | {5, &MM_U::Finalize, "Finalize"}, |
| 23 | {6, &MM_U::SetAndWaitWithId, "SetAndWaitWithId"}, | 23 | {6, &MM_U::SetAndWait, "SetAndWait"}, |
| 24 | {7, &MM_U::GetWithId, "GetWithId"}, | 24 | {7, &MM_U::Get, "Get"}, |
| 25 | }; | 25 | }; |
| 26 | // clang-format on | 26 | // clang-format on |
| 27 | 27 | ||
| @@ -29,21 +29,21 @@ public: | |||
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | private: | 31 | private: |
| 32 | void Initialize(Kernel::HLERequestContext& ctx) { | 32 | void InitializeOld(Kernel::HLERequestContext& ctx) { |
| 33 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 33 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 34 | 34 | ||
| 35 | IPC::ResponseBuilder rb{ctx, 2}; | 35 | IPC::ResponseBuilder rb{ctx, 2}; |
| 36 | rb.Push(RESULT_SUCCESS); | 36 | rb.Push(RESULT_SUCCESS); |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | void Finalize(Kernel::HLERequestContext& ctx) { | 39 | void FinalizeOld(Kernel::HLERequestContext& ctx) { |
| 40 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 40 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 41 | 41 | ||
| 42 | IPC::ResponseBuilder rb{ctx, 2}; | 42 | IPC::ResponseBuilder rb{ctx, 2}; |
| 43 | rb.Push(RESULT_SUCCESS); | 43 | rb.Push(RESULT_SUCCESS); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | void SetAndWait(Kernel::HLERequestContext& ctx) { | 46 | void SetAndWaitOld(Kernel::HLERequestContext& ctx) { |
| 47 | IPC::RequestParser rp{ctx}; | 47 | IPC::RequestParser rp{ctx}; |
| 48 | min = rp.Pop<u32>(); | 48 | min = rp.Pop<u32>(); |
| 49 | max = rp.Pop<u32>(); | 49 | max = rp.Pop<u32>(); |
| @@ -54,7 +54,7 @@ private: | |||
| 54 | rb.Push(RESULT_SUCCESS); | 54 | rb.Push(RESULT_SUCCESS); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | void Get(Kernel::HLERequestContext& ctx) { | 57 | void GetOld(Kernel::HLERequestContext& ctx) { |
| 58 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 58 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 59 | 59 | ||
| 60 | IPC::ResponseBuilder rb{ctx, 3}; | 60 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -62,7 +62,7 @@ private: | |||
| 62 | rb.Push(current); | 62 | rb.Push(current); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | void InitializeWithId(Kernel::HLERequestContext& ctx) { | 65 | void Initialize(Kernel::HLERequestContext& ctx) { |
| 66 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 66 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 67 | 67 | ||
| 68 | IPC::ResponseBuilder rb{ctx, 3}; | 68 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -70,14 +70,14 @@ private: | |||
| 70 | rb.Push<u32>(id); // Any non zero value | 70 | rb.Push<u32>(id); // Any non zero value |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | void FinalizeWithId(Kernel::HLERequestContext& ctx) { | 73 | void Finalize(Kernel::HLERequestContext& ctx) { |
| 74 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 74 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 75 | 75 | ||
| 76 | IPC::ResponseBuilder rb{ctx, 2}; | 76 | IPC::ResponseBuilder rb{ctx, 2}; |
| 77 | rb.Push(RESULT_SUCCESS); | 77 | rb.Push(RESULT_SUCCESS); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | void SetAndWaitWithId(Kernel::HLERequestContext& ctx) { | 80 | void SetAndWait(Kernel::HLERequestContext& ctx) { |
| 81 | IPC::RequestParser rp{ctx}; | 81 | IPC::RequestParser rp{ctx}; |
| 82 | u32 input_id = rp.Pop<u32>(); | 82 | u32 input_id = rp.Pop<u32>(); |
| 83 | min = rp.Pop<u32>(); | 83 | min = rp.Pop<u32>(); |
| @@ -90,7 +90,7 @@ private: | |||
| 90 | rb.Push(RESULT_SUCCESS); | 90 | rb.Push(RESULT_SUCCESS); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | void GetWithId(Kernel::HLERequestContext& ctx) { | 93 | void Get(Kernel::HLERequestContext& ctx) { |
| 94 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 94 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 95 | 95 | ||
| 96 | IPC::ResponseBuilder rb{ctx, 3}; | 96 | IPC::ResponseBuilder rb{ctx, 3}; |
diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp index ec9aae04a..e38dea1f4 100644 --- a/src/core/hle/service/ncm/ncm.cpp +++ b/src/core/hle/service/ncm/ncm.cpp | |||
| @@ -28,16 +28,16 @@ public: | |||
| 28 | {7, nullptr, "ResolveApplicationLegalInformationPath"}, | 28 | {7, nullptr, "ResolveApplicationLegalInformationPath"}, |
| 29 | {8, nullptr, "RedirectApplicationLegalInformationPath"}, | 29 | {8, nullptr, "RedirectApplicationLegalInformationPath"}, |
| 30 | {9, nullptr, "Refresh"}, | 30 | {9, nullptr, "Refresh"}, |
| 31 | {10, nullptr, "RedirectProgramPath2"}, | 31 | {10, nullptr, "RedirectApplicationProgramPath"}, |
| 32 | {11, nullptr, "Refresh2"}, | 32 | {11, nullptr, "ClearApplicationRedirection"}, |
| 33 | {12, nullptr, "DeleteProgramPath"}, | 33 | {12, nullptr, "EraseProgramRedirection"}, |
| 34 | {13, nullptr, "DeleteApplicationControlPath"}, | 34 | {13, nullptr, "EraseApplicationControlRedirection"}, |
| 35 | {14, nullptr, "DeleteApplicationHtmlDocumentPath"}, | 35 | {14, nullptr, "EraseApplicationHtmlDocumentRedirection"}, |
| 36 | {15, nullptr, "DeleteApplicationLegalInformationPath"}, | 36 | {15, nullptr, "EraseApplicationLegalInformationRedirection"}, |
| 37 | {16, nullptr, ""}, | 37 | {16, nullptr, "ResolveProgramPathForDebug"}, |
| 38 | {17, nullptr, ""}, | 38 | {17, nullptr, "RedirectProgramPathForDebug"}, |
| 39 | {18, nullptr, ""}, | 39 | {18, nullptr, "RedirectApplicationProgramPathForDebug"}, |
| 40 | {19, nullptr, ""}, | 40 | {19, nullptr, "EraseProgramRedirectionForDebug"}, |
| 41 | }; | 41 | }; |
| 42 | // clang-format on | 42 | // clang-format on |
| 43 | 43 | ||
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index b7b34ce7e..780ea30fe 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp | |||
| @@ -198,9 +198,9 @@ public: | |||
| 198 | static const FunctionInfo functions[] = { | 198 | static const FunctionInfo functions[] = { |
| 199 | {0, nullptr, "Initialize"}, | 199 | {0, nullptr, "Initialize"}, |
| 200 | {1, nullptr, "Finalize"}, | 200 | {1, nullptr, "Finalize"}, |
| 201 | {2, nullptr, "GetState"}, | 201 | {2, nullptr, "GetStateOld"}, |
| 202 | {3, nullptr, "IsNfcEnabled"}, | 202 | {3, nullptr, "IsNfcEnabledOld"}, |
| 203 | {100, nullptr, "SetNfcEnabled"}, | 203 | {100, nullptr, "SetNfcEnabledOld"}, |
| 204 | {400, nullptr, "InitializeSystem"}, | 204 | {400, nullptr, "InitializeSystem"}, |
| 205 | {401, nullptr, "FinalizeSystem"}, | 205 | {401, nullptr, "FinalizeSystem"}, |
| 206 | {402, nullptr, "GetState"}, | 206 | {402, nullptr, "GetState"}, |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 0d913334e..fba89e7a6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp | |||
| @@ -200,8 +200,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o | |||
| 200 | 200 | ||
| 201 | IoctlGetGpuTime params{}; | 201 | IoctlGetGpuTime params{}; |
| 202 | std::memcpy(¶ms, input.data(), input.size()); | 202 | std::memcpy(¶ms, input.data(), input.size()); |
| 203 | const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks()); | 203 | params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); |
| 204 | params.gpu_time = static_cast<u64_le>(ns.count()); | ||
| 205 | std::memcpy(output.data(), ¶ms, output.size()); | 204 | std::memcpy(output.data(), ¶ms, output.size()); |
| 206 | return 0; | 205 | return 0; |
| 207 | } | 206 | } |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 437bc5dee..2f44d3779 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/microprofile.h" | 10 | #include "common/microprofile.h" |
| 11 | #include "common/scope_exit.h" | 11 | #include "common/scope_exit.h" |
| 12 | #include "common/thread.h" | ||
| 12 | #include "core/core.h" | 13 | #include "core/core.h" |
| 13 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 14 | #include "core/core_timing_util.h" | 15 | #include "core/core_timing_util.h" |
| @@ -27,8 +28,35 @@ | |||
| 27 | 28 | ||
| 28 | namespace Service::NVFlinger { | 29 | namespace Service::NVFlinger { |
| 29 | 30 | ||
| 30 | constexpr s64 frame_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); | 31 | constexpr s64 frame_ticks = static_cast<s64>(1000000000 / 60); |
| 31 | constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 30); | 32 | constexpr s64 frame_ticks_30fps = static_cast<s64>(1000000000 / 30); |
| 33 | |||
| 34 | void NVFlinger::VSyncThread(NVFlinger& nv_flinger) { | ||
| 35 | nv_flinger.SplitVSync(); | ||
| 36 | } | ||
| 37 | |||
| 38 | void NVFlinger::SplitVSync() { | ||
| 39 | system.RegisterHostThread(); | ||
| 40 | std::string name = "yuzu:VSyncThread"; | ||
| 41 | MicroProfileOnThreadCreate(name.c_str()); | ||
| 42 | Common::SetCurrentThreadName(name.c_str()); | ||
| 43 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||
| 44 | s64 delay = 0; | ||
| 45 | while (is_running) { | ||
| 46 | guard->lock(); | ||
| 47 | const s64 time_start = system.CoreTiming().GetGlobalTimeNs().count(); | ||
| 48 | Compose(); | ||
| 49 | const auto ticks = GetNextTicks(); | ||
| 50 | const s64 time_end = system.CoreTiming().GetGlobalTimeNs().count(); | ||
| 51 | const s64 time_passed = time_end - time_start; | ||
| 52 | const s64 next_time = std::max<s64>(0, ticks - time_passed - delay); | ||
| 53 | guard->unlock(); | ||
| 54 | if (next_time > 0) { | ||
| 55 | wait_event->WaitFor(std::chrono::nanoseconds{next_time}); | ||
| 56 | } | ||
| 57 | delay = (system.CoreTiming().GetGlobalTimeNs().count() - time_end) - next_time; | ||
| 58 | } | ||
| 59 | } | ||
| 32 | 60 | ||
| 33 | NVFlinger::NVFlinger(Core::System& system) : system(system) { | 61 | NVFlinger::NVFlinger(Core::System& system) : system(system) { |
| 34 | displays.emplace_back(0, "Default", system); | 62 | displays.emplace_back(0, "Default", system); |
| @@ -36,22 +64,36 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { | |||
| 36 | displays.emplace_back(2, "Edid", system); | 64 | displays.emplace_back(2, "Edid", system); |
| 37 | displays.emplace_back(3, "Internal", system); | 65 | displays.emplace_back(3, "Internal", system); |
| 38 | displays.emplace_back(4, "Null", system); | 66 | displays.emplace_back(4, "Null", system); |
| 67 | guard = std::make_shared<std::mutex>(); | ||
| 39 | 68 | ||
| 40 | // Schedule the screen composition events | 69 | // Schedule the screen composition events |
| 41 | composition_event = | 70 | composition_event = |
| 42 | Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { | 71 | Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 ns_late) { |
| 72 | Lock(); | ||
| 43 | Compose(); | 73 | Compose(); |
| 44 | const auto ticks = | 74 | const auto ticks = GetNextTicks(); |
| 45 | Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); | 75 | this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - ns_late), |
| 46 | this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), | ||
| 47 | composition_event); | 76 | composition_event); |
| 48 | }); | 77 | }); |
| 49 | 78 | if (system.IsMulticore()) { | |
| 50 | system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); | 79 | is_running = true; |
| 80 | wait_event = std::make_unique<Common::Event>(); | ||
| 81 | vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this)); | ||
| 82 | } else { | ||
| 83 | system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); | ||
| 84 | } | ||
| 51 | } | 85 | } |
| 52 | 86 | ||
| 53 | NVFlinger::~NVFlinger() { | 87 | NVFlinger::~NVFlinger() { |
| 54 | system.CoreTiming().UnscheduleEvent(composition_event, 0); | 88 | if (system.IsMulticore()) { |
| 89 | is_running = false; | ||
| 90 | wait_event->Set(); | ||
| 91 | vsync_thread->join(); | ||
| 92 | vsync_thread.reset(); | ||
| 93 | wait_event.reset(); | ||
| 94 | } else { | ||
| 95 | system.CoreTiming().UnscheduleEvent(composition_event, 0); | ||
| 96 | } | ||
| 55 | } | 97 | } |
| 56 | 98 | ||
| 57 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { | 99 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { |
| @@ -199,10 +241,12 @@ void NVFlinger::Compose() { | |||
| 199 | 241 | ||
| 200 | auto& gpu = system.GPU(); | 242 | auto& gpu = system.GPU(); |
| 201 | const auto& multi_fence = buffer->get().multi_fence; | 243 | const auto& multi_fence = buffer->get().multi_fence; |
| 244 | guard->unlock(); | ||
| 202 | for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { | 245 | for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { |
| 203 | const auto& fence = multi_fence.fences[fence_id]; | 246 | const auto& fence = multi_fence.fences[fence_id]; |
| 204 | gpu.WaitFence(fence.id, fence.value); | 247 | gpu.WaitFence(fence.id, fence.value); |
| 205 | } | 248 | } |
| 249 | guard->lock(); | ||
| 206 | 250 | ||
| 207 | MicroProfileFlip(); | 251 | MicroProfileFlip(); |
| 208 | 252 | ||
| @@ -223,7 +267,7 @@ void NVFlinger::Compose() { | |||
| 223 | 267 | ||
| 224 | s64 NVFlinger::GetNextTicks() const { | 268 | s64 NVFlinger::GetNextTicks() const { |
| 225 | constexpr s64 max_hertz = 120LL; | 269 | constexpr s64 max_hertz = 120LL; |
| 226 | return (Core::Hardware::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz; | 270 | return (1000000000 * (1LL << swap_interval)) / max_hertz; |
| 227 | } | 271 | } |
| 228 | 272 | ||
| 229 | } // namespace Service::NVFlinger | 273 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 57a21f33b..e4959a9af 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h | |||
| @@ -4,15 +4,22 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 9 | #include <mutex> | ||
| 8 | #include <optional> | 10 | #include <optional> |
| 9 | #include <string> | 11 | #include <string> |
| 10 | #include <string_view> | 12 | #include <string_view> |
| 13 | #include <thread> | ||
| 11 | #include <vector> | 14 | #include <vector> |
| 12 | 15 | ||
| 13 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 14 | #include "core/hle/kernel/object.h" | 17 | #include "core/hle/kernel/object.h" |
| 15 | 18 | ||
| 19 | namespace Common { | ||
| 20 | class Event; | ||
| 21 | } // namespace Common | ||
| 22 | |||
| 16 | namespace Core::Timing { | 23 | namespace Core::Timing { |
| 17 | class CoreTiming; | 24 | class CoreTiming; |
| 18 | struct EventType; | 25 | struct EventType; |
| @@ -79,6 +86,10 @@ public: | |||
| 79 | 86 | ||
| 80 | s64 GetNextTicks() const; | 87 | s64 GetNextTicks() const; |
| 81 | 88 | ||
| 89 | std::unique_lock<std::mutex> Lock() { | ||
| 90 | return std::unique_lock{*guard}; | ||
| 91 | } | ||
| 92 | |||
| 82 | private: | 93 | private: |
| 83 | /// Finds the display identified by the specified ID. | 94 | /// Finds the display identified by the specified ID. |
| 84 | VI::Display* FindDisplay(u64 display_id); | 95 | VI::Display* FindDisplay(u64 display_id); |
| @@ -92,6 +103,10 @@ private: | |||
| 92 | /// Finds the layer identified by the specified ID in the desired display. | 103 | /// Finds the layer identified by the specified ID in the desired display. |
| 93 | const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; | 104 | const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; |
| 94 | 105 | ||
| 106 | static void VSyncThread(NVFlinger& nv_flinger); | ||
| 107 | |||
| 108 | void SplitVSync(); | ||
| 109 | |||
| 95 | std::shared_ptr<Nvidia::Module> nvdrv; | 110 | std::shared_ptr<Nvidia::Module> nvdrv; |
| 96 | 111 | ||
| 97 | std::vector<VI::Display> displays; | 112 | std::vector<VI::Display> displays; |
| @@ -108,7 +123,13 @@ private: | |||
| 108 | /// Event that handles screen composition. | 123 | /// Event that handles screen composition. |
| 109 | std::shared_ptr<Core::Timing::EventType> composition_event; | 124 | std::shared_ptr<Core::Timing::EventType> composition_event; |
| 110 | 125 | ||
| 126 | std::shared_ptr<std::mutex> guard; | ||
| 127 | |||
| 111 | Core::System& system; | 128 | Core::System& system; |
| 129 | |||
| 130 | std::unique_ptr<std::thread> vsync_thread; | ||
| 131 | std::unique_ptr<Common::Event> wait_event; | ||
| 132 | std::atomic<bool> is_running{}; | ||
| 112 | }; | 133 | }; |
| 113 | 134 | ||
| 114 | } // namespace Service::NVFlinger | 135 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 6ada13be4..d872de16c 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp | |||
| @@ -142,7 +142,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { | |||
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | // Wake the threads waiting on the ServerPort | 144 | // Wake the threads waiting on the ServerPort |
| 145 | server_port->WakeupAllWaitingThreads(); | 145 | server_port->Signal(); |
| 146 | 146 | ||
| 147 | LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); | 147 | LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); |
| 148 | IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; | 148 | IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; |
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp index 1575f0b49..59a272f4a 100644 --- a/src/core/hle/service/time/standard_steady_clock_core.cpp +++ b/src/core/hle/service/time/standard_steady_clock_core.cpp | |||
| @@ -11,9 +11,8 @@ | |||
| 11 | namespace Service::Time::Clock { | 11 | namespace Service::Time::Clock { |
| 12 | 12 | ||
| 13 | TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { | 13 | TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { |
| 14 | const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( | 14 | const TimeSpanType ticks_time_span{ |
| 15 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 15 | TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; |
| 16 | Core::Hardware::CNTFREQ)}; | ||
| 17 | TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; | 16 | TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; |
| 18 | 17 | ||
| 19 | if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { | 18 | if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { |
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp index 44d5bc651..8baaa2a6a 100644 --- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp +++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp | |||
| @@ -11,9 +11,8 @@ | |||
| 11 | namespace Service::Time::Clock { | 11 | namespace Service::Time::Clock { |
| 12 | 12 | ||
| 13 | SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { | 13 | SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { |
| 14 | const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( | 14 | const TimeSpanType ticks_time_span{ |
| 15 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 15 | TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; |
| 16 | Core::Hardware::CNTFREQ)}; | ||
| 17 | 16 | ||
| 18 | return {ticks_time_span.ToSeconds(), GetClockSourceId()}; | 17 | return {ticks_time_span.ToSeconds(), GetClockSourceId()}; |
| 19 | } | 18 | } |
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 67f1bbcf3..4cf58a61a 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp | |||
| @@ -234,9 +234,8 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe | |||
| 234 | const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; | 234 | const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; |
| 235 | 235 | ||
| 236 | if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { | 236 | if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { |
| 237 | const auto ticks{Clock::TimeSpanType::FromTicks( | 237 | const auto ticks{Clock::TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), |
| 238 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 238 | Core::Hardware::CNTFREQ)}; |
| 239 | Core::Hardware::CNTFREQ)}; | ||
| 240 | const s64 base_time_point{context.offset + current_time_point.time_point - | 239 | const s64 base_time_point{context.offset + current_time_point.time_point - |
| 241 | ticks.ToSeconds()}; | 240 | ticks.ToSeconds()}; |
| 242 | IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; | 241 | IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; |
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp index 999ec1e51..e0ae9f874 100644 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ b/src/core/hle/service/time/time_sharedmemory.cpp | |||
| @@ -30,8 +30,7 @@ void SharedMemory::SetupStandardSteadyClock(Core::System& system, | |||
| 30 | const Common::UUID& clock_source_id, | 30 | const Common::UUID& clock_source_id, |
| 31 | Clock::TimeSpanType current_time_point) { | 31 | Clock::TimeSpanType current_time_point) { |
| 32 | const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( | 32 | const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( |
| 33 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 33 | system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)}; |
| 34 | Core::Hardware::CNTFREQ)}; | ||
| 35 | const Clock::SteadyClockContext context{ | 34 | const Clock::SteadyClockContext context{ |
| 36 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), | 35 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), |
| 37 | clock_source_id}; | 36 | clock_source_id}; |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 46e14c2a3..157092074 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -511,6 +511,7 @@ private: | |||
| 511 | LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, | 511 | LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, |
| 512 | static_cast<u32>(transaction), flags); | 512 | static_cast<u32>(transaction), flags); |
| 513 | 513 | ||
| 514 | nv_flinger->Lock(); | ||
| 514 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); | 515 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); |
| 515 | 516 | ||
| 516 | switch (transaction) { | 517 | switch (transaction) { |
| @@ -550,6 +551,7 @@ private: | |||
| 550 | [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, | 551 | [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, |
| 551 | Kernel::ThreadWakeupReason reason) { | 552 | Kernel::ThreadWakeupReason reason) { |
| 552 | // Repeat TransactParcel DequeueBuffer when a buffer is available | 553 | // Repeat TransactParcel DequeueBuffer when a buffer is available |
| 554 | nv_flinger->Lock(); | ||
| 553 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); | 555 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); |
| 554 | auto result = buffer_queue.DequeueBuffer(width, height); | 556 | auto result = buffer_queue.DequeueBuffer(width, height); |
| 555 | ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); | 557 | ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 9d87045a0..7def00768 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <utility> | 8 | #include <utility> |
| 9 | 9 | ||
| 10 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 11 | #include "common/atomic_ops.h" | ||
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 13 | #include "common/page_table.h" | 14 | #include "common/page_table.h" |
| @@ -29,15 +30,12 @@ namespace Core::Memory { | |||
| 29 | struct Memory::Impl { | 30 | struct Memory::Impl { |
| 30 | explicit Impl(Core::System& system_) : system{system_} {} | 31 | explicit Impl(Core::System& system_) : system{system_} {} |
| 31 | 32 | ||
| 32 | void SetCurrentPageTable(Kernel::Process& process) { | 33 | void SetCurrentPageTable(Kernel::Process& process, u32 core_id) { |
| 33 | current_page_table = &process.PageTable().PageTableImpl(); | 34 | current_page_table = &process.PageTable().PageTableImpl(); |
| 34 | 35 | ||
| 35 | const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); | 36 | const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); |
| 36 | 37 | ||
| 37 | system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width); | 38 | system.ArmInterface(core_id).PageTableChanged(*current_page_table, address_space_width); |
| 38 | system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width); | ||
| 39 | system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width); | ||
| 40 | system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width); | ||
| 41 | } | 39 | } |
| 42 | 40 | ||
| 43 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { | 41 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { |
| @@ -179,6 +177,22 @@ struct Memory::Impl { | |||
| 179 | } | 177 | } |
| 180 | } | 178 | } |
| 181 | 179 | ||
| 180 | bool WriteExclusive8(const VAddr addr, const u8 data, const u8 expected) { | ||
| 181 | return WriteExclusive<u8>(addr, data, expected); | ||
| 182 | } | ||
| 183 | |||
| 184 | bool WriteExclusive16(const VAddr addr, const u16 data, const u16 expected) { | ||
| 185 | return WriteExclusive<u16_le>(addr, data, expected); | ||
| 186 | } | ||
| 187 | |||
| 188 | bool WriteExclusive32(const VAddr addr, const u32 data, const u32 expected) { | ||
| 189 | return WriteExclusive<u32_le>(addr, data, expected); | ||
| 190 | } | ||
| 191 | |||
| 192 | bool WriteExclusive64(const VAddr addr, const u64 data, const u64 expected) { | ||
| 193 | return WriteExclusive<u64_le>(addr, data, expected); | ||
| 194 | } | ||
| 195 | |||
| 182 | std::string ReadCString(VAddr vaddr, std::size_t max_length) { | 196 | std::string ReadCString(VAddr vaddr, std::size_t max_length) { |
| 183 | std::string string; | 197 | std::string string; |
| 184 | string.reserve(max_length); | 198 | string.reserve(max_length); |
| @@ -682,6 +696,67 @@ struct Memory::Impl { | |||
| 682 | } | 696 | } |
| 683 | } | 697 | } |
| 684 | 698 | ||
| 699 | template <typename T> | ||
| 700 | bool WriteExclusive(const VAddr vaddr, const T data, const T expected) { | ||
| 701 | u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||
| 702 | if (page_pointer != nullptr) { | ||
| 703 | // NOTE: Avoid adding any extra logic to this fast-path block | ||
| 704 | T volatile* pointer = reinterpret_cast<T volatile*>(&page_pointer[vaddr]); | ||
| 705 | return Common::AtomicCompareAndSwap(pointer, data, expected); | ||
| 706 | } | ||
| 707 | |||
| 708 | const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; | ||
| 709 | switch (type) { | ||
| 710 | case Common::PageType::Unmapped: | ||
| 711 | LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, | ||
| 712 | static_cast<u32>(data), vaddr); | ||
| 713 | return true; | ||
| 714 | case Common::PageType::Memory: | ||
| 715 | ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); | ||
| 716 | break; | ||
| 717 | case Common::PageType::RasterizerCachedMemory: { | ||
| 718 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; | ||
| 719 | system.GPU().InvalidateRegion(vaddr, sizeof(T)); | ||
| 720 | T volatile* pointer = reinterpret_cast<T volatile*>(&host_ptr); | ||
| 721 | return Common::AtomicCompareAndSwap(pointer, data, expected); | ||
| 722 | break; | ||
| 723 | } | ||
| 724 | default: | ||
| 725 | UNREACHABLE(); | ||
| 726 | } | ||
| 727 | return true; | ||
| 728 | } | ||
| 729 | |||
| 730 | bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) { | ||
| 731 | u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||
| 732 | if (page_pointer != nullptr) { | ||
| 733 | // NOTE: Avoid adding any extra logic to this fast-path block | ||
| 734 | u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&page_pointer[vaddr]); | ||
| 735 | return Common::AtomicCompareAndSwap(pointer, data, expected); | ||
| 736 | } | ||
| 737 | |||
| 738 | const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; | ||
| 739 | switch (type) { | ||
| 740 | case Common::PageType::Unmapped: | ||
| 741 | LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8, | ||
| 742 | static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr); | ||
| 743 | return true; | ||
| 744 | case Common::PageType::Memory: | ||
| 745 | ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); | ||
| 746 | break; | ||
| 747 | case Common::PageType::RasterizerCachedMemory: { | ||
| 748 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; | ||
| 749 | system.GPU().InvalidateRegion(vaddr, sizeof(u128)); | ||
| 750 | u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&host_ptr); | ||
| 751 | return Common::AtomicCompareAndSwap(pointer, data, expected); | ||
| 752 | break; | ||
| 753 | } | ||
| 754 | default: | ||
| 755 | UNREACHABLE(); | ||
| 756 | } | ||
| 757 | return true; | ||
| 758 | } | ||
| 759 | |||
| 685 | Common::PageTable* current_page_table = nullptr; | 760 | Common::PageTable* current_page_table = nullptr; |
| 686 | Core::System& system; | 761 | Core::System& system; |
| 687 | }; | 762 | }; |
| @@ -689,8 +764,8 @@ struct Memory::Impl { | |||
| 689 | Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {} | 764 | Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {} |
| 690 | Memory::~Memory() = default; | 765 | Memory::~Memory() = default; |
| 691 | 766 | ||
| 692 | void Memory::SetCurrentPageTable(Kernel::Process& process) { | 767 | void Memory::SetCurrentPageTable(Kernel::Process& process, u32 core_id) { |
| 693 | impl->SetCurrentPageTable(process); | 768 | impl->SetCurrentPageTable(process, core_id); |
| 694 | } | 769 | } |
| 695 | 770 | ||
| 696 | void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { | 771 | void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { |
| @@ -764,6 +839,26 @@ void Memory::Write64(VAddr addr, u64 data) { | |||
| 764 | impl->Write64(addr, data); | 839 | impl->Write64(addr, data); |
| 765 | } | 840 | } |
| 766 | 841 | ||
| 842 | bool Memory::WriteExclusive8(VAddr addr, u8 data, u8 expected) { | ||
| 843 | return impl->WriteExclusive8(addr, data, expected); | ||
| 844 | } | ||
| 845 | |||
| 846 | bool Memory::WriteExclusive16(VAddr addr, u16 data, u16 expected) { | ||
| 847 | return impl->WriteExclusive16(addr, data, expected); | ||
| 848 | } | ||
| 849 | |||
| 850 | bool Memory::WriteExclusive32(VAddr addr, u32 data, u32 expected) { | ||
| 851 | return impl->WriteExclusive32(addr, data, expected); | ||
| 852 | } | ||
| 853 | |||
| 854 | bool Memory::WriteExclusive64(VAddr addr, u64 data, u64 expected) { | ||
| 855 | return impl->WriteExclusive64(addr, data, expected); | ||
| 856 | } | ||
| 857 | |||
| 858 | bool Memory::WriteExclusive128(VAddr addr, u128 data, u128 expected) { | ||
| 859 | return impl->WriteExclusive128(addr, data, expected); | ||
| 860 | } | ||
| 861 | |||
| 767 | std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { | 862 | std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { |
| 768 | return impl->ReadCString(vaddr, max_length); | 863 | return impl->ReadCString(vaddr, max_length); |
| 769 | } | 864 | } |
diff --git a/src/core/memory.h b/src/core/memory.h index 9292f3b0a..4a1cc63f4 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -64,7 +64,7 @@ public: | |||
| 64 | * | 64 | * |
| 65 | * @param process The process to use the page table of. | 65 | * @param process The process to use the page table of. |
| 66 | */ | 66 | */ |
| 67 | void SetCurrentPageTable(Kernel::Process& process); | 67 | void SetCurrentPageTable(Kernel::Process& process, u32 core_id); |
| 68 | 68 | ||
| 69 | /** | 69 | /** |
| 70 | * Maps an allocated buffer onto a region of the emulated process address space. | 70 | * Maps an allocated buffer onto a region of the emulated process address space. |
| @@ -245,6 +245,71 @@ public: | |||
| 245 | void Write64(VAddr addr, u64 data); | 245 | void Write64(VAddr addr, u64 data); |
| 246 | 246 | ||
| 247 | /** | 247 | /** |
| 248 | * Writes a 8-bit unsigned integer to the given virtual address in | ||
| 249 | * the current process' address space if and only if the address contains | ||
| 250 | * the expected value. This operation is atomic. | ||
| 251 | * | ||
| 252 | * @param addr The virtual address to write the 8-bit unsigned integer to. | ||
| 253 | * @param data The 8-bit unsigned integer to write to the given virtual address. | ||
| 254 | * @param expected The 8-bit unsigned integer to check against the given virtual address. | ||
| 255 | * | ||
| 256 | * @post The memory range [addr, sizeof(data)) contains the given data value. | ||
| 257 | */ | ||
| 258 | bool WriteExclusive8(VAddr addr, u8 data, u8 expected); | ||
| 259 | |||
| 260 | /** | ||
| 261 | * Writes a 16-bit unsigned integer to the given virtual address in | ||
| 262 | * the current process' address space if and only if the address contains | ||
| 263 | * the expected value. This operation is atomic. | ||
| 264 | * | ||
| 265 | * @param addr The virtual address to write the 16-bit unsigned integer to. | ||
| 266 | * @param data The 16-bit unsigned integer to write to the given virtual address. | ||
| 267 | * @param expected The 16-bit unsigned integer to check against the given virtual address. | ||
| 268 | * | ||
| 269 | * @post The memory range [addr, sizeof(data)) contains the given data value. | ||
| 270 | */ | ||
| 271 | bool WriteExclusive16(VAddr addr, u16 data, u16 expected); | ||
| 272 | |||
| 273 | /** | ||
| 274 | * Writes a 32-bit unsigned integer to the given virtual address in | ||
| 275 | * the current process' address space if and only if the address contains | ||
| 276 | * the expected value. This operation is atomic. | ||
| 277 | * | ||
| 278 | * @param addr The virtual address to write the 32-bit unsigned integer to. | ||
| 279 | * @param data The 32-bit unsigned integer to write to the given virtual address. | ||
| 280 | * @param expected The 32-bit unsigned integer to check against the given virtual address. | ||
| 281 | * | ||
| 282 | * @post The memory range [addr, sizeof(data)) contains the given data value. | ||
| 283 | */ | ||
| 284 | bool WriteExclusive32(VAddr addr, u32 data, u32 expected); | ||
| 285 | |||
| 286 | /** | ||
| 287 | * Writes a 64-bit unsigned integer to the given virtual address in | ||
| 288 | * the current process' address space if and only if the address contains | ||
| 289 | * the expected value. This operation is atomic. | ||
| 290 | * | ||
| 291 | * @param addr The virtual address to write the 64-bit unsigned integer to. | ||
| 292 | * @param data The 64-bit unsigned integer to write to the given virtual address. | ||
| 293 | * @param expected The 64-bit unsigned integer to check against the given virtual address. | ||
| 294 | * | ||
| 295 | * @post The memory range [addr, sizeof(data)) contains the given data value. | ||
| 296 | */ | ||
| 297 | bool WriteExclusive64(VAddr addr, u64 data, u64 expected); | ||
| 298 | |||
| 299 | /** | ||
| 300 | * Writes a 128-bit unsigned integer to the given virtual address in | ||
| 301 | * the current process' address space if and only if the address contains | ||
| 302 | * the expected value. This operation is atomic. | ||
| 303 | * | ||
| 304 | * @param addr The virtual address to write the 128-bit unsigned integer to. | ||
| 305 | * @param data The 128-bit unsigned integer to write to the given virtual address. | ||
| 306 | * @param expected The 128-bit unsigned integer to check against the given virtual address. | ||
| 307 | * | ||
| 308 | * @post The memory range [addr, sizeof(data)) contains the given data value. | ||
| 309 | */ | ||
| 310 | bool WriteExclusive128(VAddr addr, u128 data, u128 expected); | ||
| 311 | |||
| 312 | /** | ||
| 248 | * Reads a null-terminated string from the given virtual address. | 313 | * Reads a null-terminated string from the given virtual address. |
| 249 | * This function will continually read characters until either: | 314 | * This function will continually read characters until either: |
| 250 | * | 315 | * |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index b139e8465..53d27859b 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -20,7 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | namespace Core::Memory { | 21 | namespace Core::Memory { |
| 22 | 22 | ||
| 23 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 12); | 23 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(1000000000 / 12); |
| 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; |
| 25 | 25 | ||
| 26 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) | 26 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) |
| @@ -190,7 +190,7 @@ CheatEngine::~CheatEngine() { | |||
| 190 | void CheatEngine::Initialize() { | 190 | void CheatEngine::Initialize() { |
| 191 | event = Core::Timing::CreateEvent( | 191 | event = Core::Timing::CreateEvent( |
| 192 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), | 192 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), |
| 193 | [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | 193 | [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); |
| 194 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); | 194 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); |
| 195 | 195 | ||
| 196 | metadata.process_id = system.CurrentProcess()->GetProcessID(); | 196 | metadata.process_id = system.CurrentProcess()->GetProcessID(); |
| @@ -217,7 +217,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) { | |||
| 217 | 217 | ||
| 218 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); | 218 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); |
| 219 | 219 | ||
| 220 | void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { | 220 | void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) { |
| 221 | if (is_pending_reload.exchange(false)) { | 221 | if (is_pending_reload.exchange(false)) { |
| 222 | vm.LoadProgram(cheats); | 222 | vm.LoadProgram(cheats); |
| 223 | } | 223 | } |
| @@ -230,7 +230,7 @@ void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { | |||
| 230 | 230 | ||
| 231 | vm.Execute(metadata); | 231 | vm.Execute(metadata); |
| 232 | 232 | ||
| 233 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event); | 233 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - ns_late, event); |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | } // namespace Core::Memory | 236 | } // namespace Core::Memory |
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index f1ae9d4df..9f3a6b811 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp | |||
| @@ -119,7 +119,7 @@ double PerfStats::GetLastFrameTimeScale() { | |||
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { | 121 | void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { |
| 122 | if (!Settings::values.use_frame_limit) { | 122 | if (!Settings::values.use_frame_limit || Settings::values.use_multi_core) { |
| 123 | return; | 123 | return; |
| 124 | } | 124 | } |
| 125 | 125 | ||
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 4edff9cd8..56df5e925 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -127,6 +127,13 @@ void LogSettings() { | |||
| 127 | LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local); | 127 | LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local); |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | float Volume() { | ||
| 131 | if (values.audio_muted) { | ||
| 132 | return 0.0f; | ||
| 133 | } | ||
| 134 | return values.volume; | ||
| 135 | } | ||
| 136 | |||
| 130 | bool IsGPULevelExtreme() { | 137 | bool IsGPULevelExtreme() { |
| 131 | return values.gpu_accuracy == GPUAccuracy::Extreme; | 138 | return values.gpu_accuracy == GPUAccuracy::Extreme; |
| 132 | } | 139 | } |
diff --git a/src/core/settings.h b/src/core/settings.h index 33e1e06cd..a598ccbc1 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -459,6 +459,7 @@ struct Values { | |||
| 459 | bool use_dev_keys; | 459 | bool use_dev_keys; |
| 460 | 460 | ||
| 461 | // Audio | 461 | // Audio |
| 462 | bool audio_muted; | ||
| 462 | std::string sink_id; | 463 | std::string sink_id; |
| 463 | bool enable_audio_stretching; | 464 | bool enable_audio_stretching; |
| 464 | std::string audio_device_id; | 465 | std::string audio_device_id; |
| @@ -490,6 +491,8 @@ struct Values { | |||
| 490 | std::map<u64, std::vector<std::string>> disabled_addons; | 491 | std::map<u64, std::vector<std::string>> disabled_addons; |
| 491 | } extern values; | 492 | } extern values; |
| 492 | 493 | ||
| 494 | float Volume(); | ||
| 495 | |||
| 493 | bool IsGPULevelExtreme(); | 496 | bool IsGPULevelExtreme(); |
| 494 | bool IsGPULevelHigh(); | 497 | bool IsGPULevelHigh(); |
| 495 | 498 | ||
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index b2c6c537e..8b0c50d11 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | namespace Tools { | 14 | namespace Tools { |
| 15 | namespace { | 15 | namespace { |
| 16 | 16 | ||
| 17 | constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); | 17 | constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(1000000000 / 60); |
| 18 | 18 | ||
| 19 | u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { | 19 | u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { |
| 20 | switch (width) { | 20 | switch (width) { |
| @@ -57,7 +57,7 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m | |||
| 57 | : core_timing{core_timing_}, memory{memory_} { | 57 | : core_timing{core_timing_}, memory{memory_} { |
| 58 | event = Core::Timing::CreateEvent( | 58 | event = Core::Timing::CreateEvent( |
| 59 | "MemoryFreezer::FrameCallback", | 59 | "MemoryFreezer::FrameCallback", |
| 60 | [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | 60 | [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); |
| 61 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); | 61 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| @@ -158,7 +158,7 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const { | |||
| 158 | return entries; | 158 | return entries; |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { | 161 | void Freezer::FrameCallback(u64 userdata, s64 ns_late) { |
| 162 | if (!IsActive()) { | 162 | if (!IsActive()) { |
| 163 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); | 163 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); |
| 164 | return; | 164 | return; |
| @@ -173,7 +173,7 @@ void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { | |||
| 173 | MemoryWriteWidth(memory, entry.width, entry.address, entry.value); | 173 | MemoryWriteWidth(memory, entry.width, entry.address, entry.value); |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event); | 176 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - ns_late, event); |
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | void Freezer::FillEntryReads() { | 179 | void Freezer::FillEntryReads() { |
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index c7038b217..47ef30aa9 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | add_executable(tests | 1 | add_executable(tests |
| 2 | common/bit_field.cpp | 2 | common/bit_field.cpp |
| 3 | common/bit_utils.cpp | 3 | common/bit_utils.cpp |
| 4 | common/fibers.cpp | ||
| 4 | common/multi_level_queue.cpp | 5 | common/multi_level_queue.cpp |
| 5 | common/param_package.cpp | 6 | common/param_package.cpp |
| 6 | common/ring_buffer.cpp | 7 | common/ring_buffer.cpp |
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp new file mode 100644 index 000000000..4fd92428f --- /dev/null +++ b/src/tests/common/fibers.cpp | |||
| @@ -0,0 +1,358 @@ | |||
| 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 <atomic> | ||
| 6 | #include <cstdlib> | ||
| 7 | #include <functional> | ||
| 8 | #include <memory> | ||
| 9 | #include <thread> | ||
| 10 | #include <unordered_map> | ||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include <catch2/catch.hpp> | ||
| 14 | #include <math.h> | ||
| 15 | #include "common/common_types.h" | ||
| 16 | #include "common/fiber.h" | ||
| 17 | #include "common/spin_lock.h" | ||
| 18 | |||
| 19 | namespace Common { | ||
| 20 | |||
| 21 | class TestControl1 { | ||
| 22 | public: | ||
| 23 | TestControl1() = default; | ||
| 24 | |||
| 25 | void DoWork(); | ||
| 26 | |||
| 27 | void ExecuteThread(u32 id); | ||
| 28 | |||
| 29 | std::unordered_map<std::thread::id, u32> ids; | ||
| 30 | std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; | ||
| 31 | std::vector<std::shared_ptr<Common::Fiber>> work_fibers; | ||
| 32 | std::vector<u32> items; | ||
| 33 | std::vector<u32> results; | ||
| 34 | }; | ||
| 35 | |||
| 36 | static void WorkControl1(void* control) { | ||
| 37 | auto* test_control = static_cast<TestControl1*>(control); | ||
| 38 | test_control->DoWork(); | ||
| 39 | } | ||
| 40 | |||
| 41 | void TestControl1::DoWork() { | ||
| 42 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 43 | u32 id = ids[this_id]; | ||
| 44 | u32 value = items[id]; | ||
| 45 | for (u32 i = 0; i < id; i++) { | ||
| 46 | value++; | ||
| 47 | } | ||
| 48 | results[id] = value; | ||
| 49 | Fiber::YieldTo(work_fibers[id], thread_fibers[id]); | ||
| 50 | } | ||
| 51 | |||
| 52 | void TestControl1::ExecuteThread(u32 id) { | ||
| 53 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 54 | ids[this_id] = id; | ||
| 55 | auto thread_fiber = Fiber::ThreadToFiber(); | ||
| 56 | thread_fibers[id] = thread_fiber; | ||
| 57 | work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this); | ||
| 58 | items[id] = rand() % 256; | ||
| 59 | Fiber::YieldTo(thread_fibers[id], work_fibers[id]); | ||
| 60 | thread_fibers[id]->Exit(); | ||
| 61 | } | ||
| 62 | |||
| 63 | static void ThreadStart1(u32 id, TestControl1& test_control) { | ||
| 64 | test_control.ExecuteThread(id); | ||
| 65 | } | ||
| 66 | |||
| 67 | /** This test checks for fiber setup configuration and validates that fibers are | ||
| 68 | * doing all the work required. | ||
| 69 | */ | ||
| 70 | TEST_CASE("Fibers::Setup", "[common]") { | ||
| 71 | constexpr std::size_t num_threads = 7; | ||
| 72 | TestControl1 test_control{}; | ||
| 73 | test_control.thread_fibers.resize(num_threads); | ||
| 74 | test_control.work_fibers.resize(num_threads); | ||
| 75 | test_control.items.resize(num_threads, 0); | ||
| 76 | test_control.results.resize(num_threads, 0); | ||
| 77 | std::vector<std::thread> threads; | ||
| 78 | for (u32 i = 0; i < num_threads; i++) { | ||
| 79 | threads.emplace_back(ThreadStart1, i, std::ref(test_control)); | ||
| 80 | } | ||
| 81 | for (u32 i = 0; i < num_threads; i++) { | ||
| 82 | threads[i].join(); | ||
| 83 | } | ||
| 84 | for (u32 i = 0; i < num_threads; i++) { | ||
| 85 | REQUIRE(test_control.items[i] + i == test_control.results[i]); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | class TestControl2 { | ||
| 90 | public: | ||
| 91 | TestControl2() = default; | ||
| 92 | |||
| 93 | void DoWork1() { | ||
| 94 | trap2 = false; | ||
| 95 | while (trap.load()) | ||
| 96 | ; | ||
| 97 | for (u32 i = 0; i < 12000; i++) { | ||
| 98 | value1 += i; | ||
| 99 | } | ||
| 100 | Fiber::YieldTo(fiber1, fiber3); | ||
| 101 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 102 | u32 id = ids[this_id]; | ||
| 103 | assert1 = id == 1; | ||
| 104 | value2 += 5000; | ||
| 105 | Fiber::YieldTo(fiber1, thread_fibers[id]); | ||
| 106 | } | ||
| 107 | |||
| 108 | void DoWork2() { | ||
| 109 | while (trap2.load()) | ||
| 110 | ; | ||
| 111 | value2 = 2000; | ||
| 112 | trap = false; | ||
| 113 | Fiber::YieldTo(fiber2, fiber1); | ||
| 114 | assert3 = false; | ||
| 115 | } | ||
| 116 | |||
| 117 | void DoWork3() { | ||
| 118 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 119 | u32 id = ids[this_id]; | ||
| 120 | assert2 = id == 0; | ||
| 121 | value1 += 1000; | ||
| 122 | Fiber::YieldTo(fiber3, thread_fibers[id]); | ||
| 123 | } | ||
| 124 | |||
| 125 | void ExecuteThread(u32 id); | ||
| 126 | |||
| 127 | void CallFiber1() { | ||
| 128 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 129 | u32 id = ids[this_id]; | ||
| 130 | Fiber::YieldTo(thread_fibers[id], fiber1); | ||
| 131 | } | ||
| 132 | |||
| 133 | void CallFiber2() { | ||
| 134 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 135 | u32 id = ids[this_id]; | ||
| 136 | Fiber::YieldTo(thread_fibers[id], fiber2); | ||
| 137 | } | ||
| 138 | |||
| 139 | void Exit(); | ||
| 140 | |||
| 141 | bool assert1{}; | ||
| 142 | bool assert2{}; | ||
| 143 | bool assert3{true}; | ||
| 144 | u32 value1{}; | ||
| 145 | u32 value2{}; | ||
| 146 | std::atomic<bool> trap{true}; | ||
| 147 | std::atomic<bool> trap2{true}; | ||
| 148 | std::unordered_map<std::thread::id, u32> ids; | ||
| 149 | std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; | ||
| 150 | std::shared_ptr<Common::Fiber> fiber1; | ||
| 151 | std::shared_ptr<Common::Fiber> fiber2; | ||
| 152 | std::shared_ptr<Common::Fiber> fiber3; | ||
| 153 | }; | ||
| 154 | |||
| 155 | static void WorkControl2_1(void* control) { | ||
| 156 | auto* test_control = static_cast<TestControl2*>(control); | ||
| 157 | test_control->DoWork1(); | ||
| 158 | } | ||
| 159 | |||
| 160 | static void WorkControl2_2(void* control) { | ||
| 161 | auto* test_control = static_cast<TestControl2*>(control); | ||
| 162 | test_control->DoWork2(); | ||
| 163 | } | ||
| 164 | |||
| 165 | static void WorkControl2_3(void* control) { | ||
| 166 | auto* test_control = static_cast<TestControl2*>(control); | ||
| 167 | test_control->DoWork3(); | ||
| 168 | } | ||
| 169 | |||
| 170 | void TestControl2::ExecuteThread(u32 id) { | ||
| 171 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 172 | ids[this_id] = id; | ||
| 173 | auto thread_fiber = Fiber::ThreadToFiber(); | ||
| 174 | thread_fibers[id] = thread_fiber; | ||
| 175 | } | ||
| 176 | |||
| 177 | void TestControl2::Exit() { | ||
| 178 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 179 | u32 id = ids[this_id]; | ||
| 180 | thread_fibers[id]->Exit(); | ||
| 181 | } | ||
| 182 | |||
| 183 | static void ThreadStart2_1(u32 id, TestControl2& test_control) { | ||
| 184 | test_control.ExecuteThread(id); | ||
| 185 | test_control.CallFiber1(); | ||
| 186 | test_control.Exit(); | ||
| 187 | } | ||
| 188 | |||
| 189 | static void ThreadStart2_2(u32 id, TestControl2& test_control) { | ||
| 190 | test_control.ExecuteThread(id); | ||
| 191 | test_control.CallFiber2(); | ||
| 192 | test_control.Exit(); | ||
| 193 | } | ||
| 194 | |||
| 195 | /** This test checks for fiber thread exchange configuration and validates that fibers are | ||
| 196 | * that a fiber has been succesfully transfered from one thread to another and that the TLS | ||
| 197 | * region of the thread is kept while changing fibers. | ||
| 198 | */ | ||
| 199 | TEST_CASE("Fibers::InterExchange", "[common]") { | ||
| 200 | TestControl2 test_control{}; | ||
| 201 | test_control.thread_fibers.resize(2); | ||
| 202 | test_control.fiber1 = | ||
| 203 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_1}, &test_control); | ||
| 204 | test_control.fiber2 = | ||
| 205 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_2}, &test_control); | ||
| 206 | test_control.fiber3 = | ||
| 207 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_3}, &test_control); | ||
| 208 | std::thread thread1(ThreadStart2_1, 0, std::ref(test_control)); | ||
| 209 | std::thread thread2(ThreadStart2_2, 1, std::ref(test_control)); | ||
| 210 | thread1.join(); | ||
| 211 | thread2.join(); | ||
| 212 | REQUIRE(test_control.assert1); | ||
| 213 | REQUIRE(test_control.assert2); | ||
| 214 | REQUIRE(test_control.assert3); | ||
| 215 | REQUIRE(test_control.value2 == 7000); | ||
| 216 | u32 cal_value = 0; | ||
| 217 | for (u32 i = 0; i < 12000; i++) { | ||
| 218 | cal_value += i; | ||
| 219 | } | ||
| 220 | cal_value += 1000; | ||
| 221 | REQUIRE(test_control.value1 == cal_value); | ||
| 222 | } | ||
| 223 | |||
| 224 | class TestControl3 { | ||
| 225 | public: | ||
| 226 | TestControl3() = default; | ||
| 227 | |||
| 228 | void DoWork1() { | ||
| 229 | value1 += 1; | ||
| 230 | Fiber::YieldTo(fiber1, fiber2); | ||
| 231 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 232 | u32 id = ids[this_id]; | ||
| 233 | value3 += 1; | ||
| 234 | Fiber::YieldTo(fiber1, thread_fibers[id]); | ||
| 235 | } | ||
| 236 | |||
| 237 | void DoWork2() { | ||
| 238 | value2 += 1; | ||
| 239 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 240 | u32 id = ids[this_id]; | ||
| 241 | Fiber::YieldTo(fiber2, thread_fibers[id]); | ||
| 242 | } | ||
| 243 | |||
| 244 | void ExecuteThread(u32 id); | ||
| 245 | |||
| 246 | void CallFiber1() { | ||
| 247 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 248 | u32 id = ids[this_id]; | ||
| 249 | Fiber::YieldTo(thread_fibers[id], fiber1); | ||
| 250 | } | ||
| 251 | |||
| 252 | void Exit(); | ||
| 253 | |||
| 254 | u32 value1{}; | ||
| 255 | u32 value2{}; | ||
| 256 | u32 value3{}; | ||
| 257 | std::unordered_map<std::thread::id, u32> ids; | ||
| 258 | std::vector<std::shared_ptr<Common::Fiber>> thread_fibers; | ||
| 259 | std::shared_ptr<Common::Fiber> fiber1; | ||
| 260 | std::shared_ptr<Common::Fiber> fiber2; | ||
| 261 | }; | ||
| 262 | |||
| 263 | static void WorkControl3_1(void* control) { | ||
| 264 | auto* test_control = static_cast<TestControl3*>(control); | ||
| 265 | test_control->DoWork1(); | ||
| 266 | } | ||
| 267 | |||
| 268 | static void WorkControl3_2(void* control) { | ||
| 269 | auto* test_control = static_cast<TestControl3*>(control); | ||
| 270 | test_control->DoWork2(); | ||
| 271 | } | ||
| 272 | |||
| 273 | void TestControl3::ExecuteThread(u32 id) { | ||
| 274 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 275 | ids[this_id] = id; | ||
| 276 | auto thread_fiber = Fiber::ThreadToFiber(); | ||
| 277 | thread_fibers[id] = thread_fiber; | ||
| 278 | } | ||
| 279 | |||
| 280 | void TestControl3::Exit() { | ||
| 281 | std::thread::id this_id = std::this_thread::get_id(); | ||
| 282 | u32 id = ids[this_id]; | ||
| 283 | thread_fibers[id]->Exit(); | ||
| 284 | } | ||
| 285 | |||
| 286 | static void ThreadStart3(u32 id, TestControl3& test_control) { | ||
| 287 | test_control.ExecuteThread(id); | ||
| 288 | test_control.CallFiber1(); | ||
| 289 | test_control.Exit(); | ||
| 290 | } | ||
| 291 | |||
| 292 | /** This test checks for one two threads racing for starting the same fiber. | ||
| 293 | * It checks execution occured in an ordered manner and by no time there were | ||
| 294 | * two contexts at the same time. | ||
| 295 | */ | ||
| 296 | TEST_CASE("Fibers::StartRace", "[common]") { | ||
| 297 | TestControl3 test_control{}; | ||
| 298 | test_control.thread_fibers.resize(2); | ||
| 299 | test_control.fiber1 = | ||
| 300 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_1}, &test_control); | ||
| 301 | test_control.fiber2 = | ||
| 302 | std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_2}, &test_control); | ||
| 303 | std::thread thread1(ThreadStart3, 0, std::ref(test_control)); | ||
| 304 | std::thread thread2(ThreadStart3, 1, std::ref(test_control)); | ||
| 305 | thread1.join(); | ||
| 306 | thread2.join(); | ||
| 307 | REQUIRE(test_control.value1 == 1); | ||
| 308 | REQUIRE(test_control.value2 == 1); | ||
| 309 | REQUIRE(test_control.value3 == 1); | ||
| 310 | } | ||
| 311 | |||
| 312 | class TestControl4; | ||
| 313 | |||
| 314 | static void WorkControl4(void* control); | ||
| 315 | |||
| 316 | class TestControl4 { | ||
| 317 | public: | ||
| 318 | TestControl4() { | ||
| 319 | fiber1 = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl4}, this); | ||
| 320 | goal_reached = false; | ||
| 321 | rewinded = false; | ||
| 322 | } | ||
| 323 | |||
| 324 | void Execute() { | ||
| 325 | thread_fiber = Fiber::ThreadToFiber(); | ||
| 326 | Fiber::YieldTo(thread_fiber, fiber1); | ||
| 327 | thread_fiber->Exit(); | ||
| 328 | } | ||
| 329 | |||
| 330 | void DoWork() { | ||
| 331 | fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this); | ||
| 332 | if (rewinded) { | ||
| 333 | goal_reached = true; | ||
| 334 | Fiber::YieldTo(fiber1, thread_fiber); | ||
| 335 | } | ||
| 336 | rewinded = true; | ||
| 337 | fiber1->Rewind(); | ||
| 338 | } | ||
| 339 | |||
| 340 | std::shared_ptr<Common::Fiber> fiber1; | ||
| 341 | std::shared_ptr<Common::Fiber> thread_fiber; | ||
| 342 | bool goal_reached; | ||
| 343 | bool rewinded; | ||
| 344 | }; | ||
| 345 | |||
| 346 | static void WorkControl4(void* control) { | ||
| 347 | auto* test_control = static_cast<TestControl4*>(control); | ||
| 348 | test_control->DoWork(); | ||
| 349 | } | ||
| 350 | |||
| 351 | TEST_CASE("Fibers::Rewind", "[common]") { | ||
| 352 | TestControl4 test_control{}; | ||
| 353 | test_control.Execute(); | ||
| 354 | REQUIRE(test_control.goal_reached); | ||
| 355 | REQUIRE(test_control.rewinded); | ||
| 356 | } | ||
| 357 | |||
| 358 | } // namespace Common | ||
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index ff2d11cc8..e66db1940 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp | |||
| @@ -18,29 +18,26 @@ namespace { | |||
| 18 | // Numbers are chosen randomly to make sure the correct one is given. | 18 | // Numbers are chosen randomly to make sure the correct one is given. |
| 19 | constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; | 19 | constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; |
| 20 | constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals | 20 | constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals |
| 21 | constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}}; | ||
| 22 | std::array<s64, 5> delays{}; | ||
| 21 | 23 | ||
| 22 | std::bitset<CB_IDS.size()> callbacks_ran_flags; | 24 | std::bitset<CB_IDS.size()> callbacks_ran_flags; |
| 23 | u64 expected_callback = 0; | 25 | u64 expected_callback = 0; |
| 24 | s64 lateness = 0; | ||
| 25 | 26 | ||
| 26 | template <unsigned int IDX> | 27 | template <unsigned int IDX> |
| 27 | void CallbackTemplate(u64 userdata, s64 cycles_late) { | 28 | void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) { |
| 28 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); | 29 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); |
| 29 | callbacks_ran_flags.set(IDX); | 30 | callbacks_ran_flags.set(IDX); |
| 30 | REQUIRE(CB_IDS[IDX] == userdata); | 31 | REQUIRE(CB_IDS[IDX] == userdata); |
| 31 | REQUIRE(CB_IDS[IDX] == expected_callback); | 32 | REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); |
| 32 | REQUIRE(lateness == cycles_late); | 33 | delays[IDX] = nanoseconds_late; |
| 33 | } | 34 | ++expected_callback; |
| 34 | |||
| 35 | u64 callbacks_done = 0; | ||
| 36 | |||
| 37 | void EmptyCallback(u64 userdata, s64 cycles_late) { | ||
| 38 | ++callbacks_done; | ||
| 39 | } | 35 | } |
| 40 | 36 | ||
| 41 | struct ScopeInit final { | 37 | struct ScopeInit final { |
| 42 | ScopeInit() { | 38 | ScopeInit() { |
| 43 | core_timing.Initialize(); | 39 | core_timing.SetMulticore(true); |
| 40 | core_timing.Initialize([]() {}); | ||
| 44 | } | 41 | } |
| 45 | ~ScopeInit() { | 42 | ~ScopeInit() { |
| 46 | core_timing.Shutdown(); | 43 | core_timing.Shutdown(); |
| @@ -49,110 +46,101 @@ struct ScopeInit final { | |||
| 49 | Core::Timing::CoreTiming core_timing; | 46 | Core::Timing::CoreTiming core_timing; |
| 50 | }; | 47 | }; |
| 51 | 48 | ||
| 52 | void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0, | 49 | #pragma optimize("", off) |
| 53 | int expected_lateness = 0, int cpu_downcount = 0) { | ||
| 54 | callbacks_ran_flags = 0; | ||
| 55 | expected_callback = CB_IDS[idx]; | ||
| 56 | lateness = expected_lateness; | ||
| 57 | |||
| 58 | // Pretend we executed X cycles of instructions. | ||
| 59 | core_timing.SwitchContext(context); | ||
| 60 | core_timing.AddTicks(core_timing.GetDowncount() - cpu_downcount); | ||
| 61 | core_timing.Advance(); | ||
| 62 | core_timing.SwitchContext((context + 1) % 4); | ||
| 63 | 50 | ||
| 64 | REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); | 51 | u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) { |
| 52 | u64 start = core_timing.GetGlobalTimeNs().count(); | ||
| 53 | u64 placebo = 0; | ||
| 54 | for (std::size_t i = 0; i < 1000; i++) { | ||
| 55 | placebo += core_timing.GetGlobalTimeNs().count(); | ||
| 56 | } | ||
| 57 | u64 end = core_timing.GetGlobalTimeNs().count(); | ||
| 58 | return (end - start); | ||
| 65 | } | 59 | } |
| 60 | |||
| 61 | #pragma optimize("", on) | ||
| 62 | |||
| 66 | } // Anonymous namespace | 63 | } // Anonymous namespace |
| 67 | 64 | ||
| 68 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { | 65 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { |
| 69 | ScopeInit guard; | 66 | ScopeInit guard; |
| 70 | auto& core_timing = guard.core_timing; | 67 | auto& core_timing = guard.core_timing; |
| 68 | std::vector<std::shared_ptr<Core::Timing::EventType>> events{ | ||
| 69 | Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>), | ||
| 70 | Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>), | ||
| 71 | Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>), | ||
| 72 | Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>), | ||
| 73 | Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>), | ||
| 74 | }; | ||
| 75 | |||
| 76 | expected_callback = 0; | ||
| 77 | |||
| 78 | core_timing.SyncPause(true); | ||
| 79 | |||
| 80 | u64 one_micro = 1000U; | ||
| 81 | for (std::size_t i = 0; i < events.size(); i++) { | ||
| 82 | u64 order = calls_order[i]; | ||
| 83 | core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); | ||
| 84 | } | ||
| 85 | /// test pause | ||
| 86 | REQUIRE(callbacks_ran_flags.none()); | ||
| 71 | 87 | ||
| 72 | std::shared_ptr<Core::Timing::EventType> cb_a = | 88 | core_timing.Pause(false); // No need to sync |
| 73 | Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); | ||
| 74 | std::shared_ptr<Core::Timing::EventType> cb_b = | ||
| 75 | Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); | ||
| 76 | std::shared_ptr<Core::Timing::EventType> cb_c = | ||
| 77 | Core::Timing::CreateEvent("callbackC", CallbackTemplate<2>); | ||
| 78 | std::shared_ptr<Core::Timing::EventType> cb_d = | ||
| 79 | Core::Timing::CreateEvent("callbackD", CallbackTemplate<3>); | ||
| 80 | std::shared_ptr<Core::Timing::EventType> cb_e = | ||
| 81 | Core::Timing::CreateEvent("callbackE", CallbackTemplate<4>); | ||
| 82 | |||
| 83 | // Enter slice 0 | ||
| 84 | core_timing.ResetRun(); | ||
| 85 | |||
| 86 | // D -> B -> C -> A -> E | ||
| 87 | core_timing.SwitchContext(0); | ||
| 88 | core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]); | ||
| 89 | REQUIRE(1000 == core_timing.GetDowncount()); | ||
| 90 | core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]); | ||
| 91 | REQUIRE(500 == core_timing.GetDowncount()); | ||
| 92 | core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]); | ||
| 93 | REQUIRE(500 == core_timing.GetDowncount()); | ||
| 94 | core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]); | ||
| 95 | REQUIRE(100 == core_timing.GetDowncount()); | ||
| 96 | core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]); | ||
| 97 | REQUIRE(100 == core_timing.GetDowncount()); | ||
| 98 | |||
| 99 | AdvanceAndCheck(core_timing, 3, 0); | ||
| 100 | AdvanceAndCheck(core_timing, 1, 1); | ||
| 101 | AdvanceAndCheck(core_timing, 2, 2); | ||
| 102 | AdvanceAndCheck(core_timing, 0, 3); | ||
| 103 | AdvanceAndCheck(core_timing, 4, 0); | ||
| 104 | } | ||
| 105 | |||
| 106 | TEST_CASE("CoreTiming[FairSharing]", "[core]") { | ||
| 107 | 89 | ||
| 108 | ScopeInit guard; | 90 | while (core_timing.HasPendingEvents()) |
| 109 | auto& core_timing = guard.core_timing; | 91 | ; |
| 110 | 92 | ||
| 111 | std::shared_ptr<Core::Timing::EventType> empty_callback = | 93 | REQUIRE(callbacks_ran_flags.all()); |
| 112 | Core::Timing::CreateEvent("empty_callback", EmptyCallback); | ||
| 113 | 94 | ||
| 114 | callbacks_done = 0; | 95 | for (std::size_t i = 0; i < delays.size(); i++) { |
| 115 | u64 MAX_CALLBACKS = 10; | 96 | const double delay = static_cast<double>(delays[i]); |
| 116 | for (std::size_t i = 0; i < 10; i++) { | 97 | const double micro = delay / 1000.0f; |
| 117 | core_timing.ScheduleEvent(i * 3333U, empty_callback, 0); | 98 | const double mili = micro / 1000.0f; |
| 99 | printf("HostTimer Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); | ||
| 118 | } | 100 | } |
| 119 | |||
| 120 | const s64 advances = MAX_SLICE_LENGTH / 10; | ||
| 121 | core_timing.ResetRun(); | ||
| 122 | u64 current_time = core_timing.GetTicks(); | ||
| 123 | bool keep_running{}; | ||
| 124 | do { | ||
| 125 | keep_running = false; | ||
| 126 | for (u32 active_core = 0; active_core < 4; ++active_core) { | ||
| 127 | core_timing.SwitchContext(active_core); | ||
| 128 | if (core_timing.CanCurrentContextRun()) { | ||
| 129 | core_timing.AddTicks(std::min<s64>(advances, core_timing.GetDowncount())); | ||
| 130 | core_timing.Advance(); | ||
| 131 | } | ||
| 132 | keep_running |= core_timing.CanCurrentContextRun(); | ||
| 133 | } | ||
| 134 | } while (keep_running); | ||
| 135 | u64 current_time_2 = core_timing.GetTicks(); | ||
| 136 | |||
| 137 | REQUIRE(MAX_CALLBACKS == callbacks_done); | ||
| 138 | REQUIRE(current_time_2 == current_time + MAX_SLICE_LENGTH * 4); | ||
| 139 | } | 101 | } |
| 140 | 102 | ||
| 141 | TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { | 103 | TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") { |
| 142 | ScopeInit guard; | 104 | ScopeInit guard; |
| 143 | auto& core_timing = guard.core_timing; | 105 | auto& core_timing = guard.core_timing; |
| 106 | std::vector<std::shared_ptr<Core::Timing::EventType>> events{ | ||
| 107 | Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>), | ||
| 108 | Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>), | ||
| 109 | Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>), | ||
| 110 | Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>), | ||
| 111 | Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>), | ||
| 112 | }; | ||
| 113 | |||
| 114 | core_timing.SyncPause(true); | ||
| 115 | core_timing.SyncPause(false); | ||
| 116 | |||
| 117 | expected_callback = 0; | ||
| 118 | |||
| 119 | u64 start = core_timing.GetGlobalTimeNs().count(); | ||
| 120 | u64 one_micro = 1000U; | ||
| 121 | for (std::size_t i = 0; i < events.size(); i++) { | ||
| 122 | u64 order = calls_order[i]; | ||
| 123 | core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]); | ||
| 124 | } | ||
| 125 | u64 end = core_timing.GetGlobalTimeNs().count(); | ||
| 126 | const double scheduling_time = static_cast<double>(end - start); | ||
| 127 | const double timer_time = static_cast<double>(TestTimerSpeed(core_timing)); | ||
| 144 | 128 | ||
| 145 | std::shared_ptr<Core::Timing::EventType> cb_a = | 129 | while (core_timing.HasPendingEvents()) |
| 146 | Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); | 130 | ; |
| 147 | std::shared_ptr<Core::Timing::EventType> cb_b = | ||
| 148 | Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); | ||
| 149 | 131 | ||
| 150 | // Enter slice 0 | 132 | REQUIRE(callbacks_ran_flags.all()); |
| 151 | core_timing.ResetRun(); | ||
| 152 | 133 | ||
| 153 | core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]); | 134 | for (std::size_t i = 0; i < delays.size(); i++) { |
| 154 | core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]); | 135 | const double delay = static_cast<double>(delays[i]); |
| 136 | const double micro = delay / 1000.0f; | ||
| 137 | const double mili = micro / 1000.0f; | ||
| 138 | printf("HostTimer No Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili); | ||
| 139 | } | ||
| 155 | 140 | ||
| 156 | AdvanceAndCheck(core_timing, 0, 0, 10, -10); // (100 - 10) | 141 | const double micro = scheduling_time / 1000.0f; |
| 157 | AdvanceAndCheck(core_timing, 1, 1, 50, -50); | 142 | const double mili = micro / 1000.0f; |
| 143 | printf("HostTimer No Pausing Scheduling Time: %.3f %.6f\n", micro, mili); | ||
| 144 | printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f, | ||
| 145 | timer_time / 1000000.f); | ||
| 158 | } | 146 | } |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 099bb446e..2dc752aa9 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -27,6 +27,8 @@ add_library(video_core STATIC | |||
| 27 | engines/shader_type.h | 27 | engines/shader_type.h |
| 28 | macro/macro.cpp | 28 | macro/macro.cpp |
| 29 | macro/macro.h | 29 | macro/macro.h |
| 30 | macro/macro_hle.cpp | ||
| 31 | macro/macro_hle.h | ||
| 30 | macro/macro_interpreter.cpp | 32 | macro/macro_interpreter.cpp |
| 31 | macro/macro_interpreter.h | 33 | macro/macro_interpreter.h |
| 32 | macro/macro_jit_x64.cpp | 34 | macro/macro_jit_x64.cpp |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index bae1d527c..cf8bdd021 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -41,7 +41,11 @@ class BufferCache { | |||
| 41 | static constexpr u64 BLOCK_PAGE_SIZE = 1ULL << BLOCK_PAGE_BITS; | 41 | static constexpr u64 BLOCK_PAGE_SIZE = 1ULL << BLOCK_PAGE_BITS; |
| 42 | 42 | ||
| 43 | public: | 43 | public: |
| 44 | using BufferInfo = std::pair<BufferType, u64>; | 44 | struct BufferInfo { |
| 45 | BufferType handle; | ||
| 46 | u64 offset; | ||
| 47 | u64 address; | ||
| 48 | }; | ||
| 45 | 49 | ||
| 46 | BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, | 50 | BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, |
| 47 | bool is_written = false, bool use_fast_cbuf = false) { | 51 | bool is_written = false, bool use_fast_cbuf = false) { |
| @@ -50,7 +54,7 @@ public: | |||
| 50 | auto& memory_manager = system.GPU().MemoryManager(); | 54 | auto& memory_manager = system.GPU().MemoryManager(); |
| 51 | const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr); | 55 | const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr); |
| 52 | if (!cpu_addr_opt) { | 56 | if (!cpu_addr_opt) { |
| 53 | return {GetEmptyBuffer(size), 0}; | 57 | return GetEmptyBuffer(size); |
| 54 | } | 58 | } |
| 55 | const VAddr cpu_addr = *cpu_addr_opt; | 59 | const VAddr cpu_addr = *cpu_addr_opt; |
| 56 | 60 | ||
| @@ -88,7 +92,7 @@ public: | |||
| 88 | Buffer* const block = GetBlock(cpu_addr, size); | 92 | Buffer* const block = GetBlock(cpu_addr, size); |
| 89 | MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size); | 93 | MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size); |
| 90 | if (!map) { | 94 | if (!map) { |
| 91 | return {GetEmptyBuffer(size), 0}; | 95 | return GetEmptyBuffer(size); |
| 92 | } | 96 | } |
| 93 | if (is_written) { | 97 | if (is_written) { |
| 94 | map->MarkAsModified(true, GetModifiedTicks()); | 98 | map->MarkAsModified(true, GetModifiedTicks()); |
| @@ -101,7 +105,7 @@ public: | |||
| 101 | } | 105 | } |
| 102 | } | 106 | } |
| 103 | 107 | ||
| 104 | return {block->Handle(), static_cast<u64>(block->Offset(cpu_addr))}; | 108 | return BufferInfo{block->Handle(), block->Offset(cpu_addr), block->Address()}; |
| 105 | } | 109 | } |
| 106 | 110 | ||
| 107 | /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset. | 111 | /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset. |
| @@ -254,27 +258,17 @@ public: | |||
| 254 | committed_flushes.pop_front(); | 258 | committed_flushes.pop_front(); |
| 255 | } | 259 | } |
| 256 | 260 | ||
| 257 | virtual BufferType GetEmptyBuffer(std::size_t size) = 0; | 261 | virtual BufferInfo GetEmptyBuffer(std::size_t size) = 0; |
| 258 | 262 | ||
| 259 | protected: | 263 | protected: |
| 260 | explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, | 264 | explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, |
| 261 | std::unique_ptr<StreamBuffer> stream_buffer_) | 265 | std::unique_ptr<StreamBuffer> stream_buffer) |
| 262 | : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer_)}, | 266 | : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer)} {} |
| 263 | stream_buffer_handle{stream_buffer->Handle()} {} | ||
| 264 | 267 | ||
| 265 | ~BufferCache() = default; | 268 | ~BufferCache() = default; |
| 266 | 269 | ||
| 267 | virtual std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) = 0; | 270 | virtual std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) = 0; |
| 268 | 271 | ||
| 269 | virtual void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 270 | const u8* data) = 0; | ||
| 271 | |||
| 272 | virtual void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 273 | u8* data) = 0; | ||
| 274 | |||
| 275 | virtual void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, | ||
| 276 | std::size_t dst_offset, std::size_t size) = 0; | ||
| 277 | |||
| 278 | virtual BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) { | 272 | virtual BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) { |
| 279 | return {}; | 273 | return {}; |
| 280 | } | 274 | } |
| @@ -336,11 +330,11 @@ private: | |||
| 336 | const VAddr cpu_addr_end = cpu_addr + size; | 330 | const VAddr cpu_addr_end = cpu_addr + size; |
| 337 | if (memory_manager.IsGranularRange(gpu_addr, size)) { | 331 | if (memory_manager.IsGranularRange(gpu_addr, size)) { |
| 338 | u8* host_ptr = memory_manager.GetPointer(gpu_addr); | 332 | u8* host_ptr = memory_manager.GetPointer(gpu_addr); |
| 339 | UploadBlockData(*block, block->Offset(cpu_addr), size, host_ptr); | 333 | block->Upload(block->Offset(cpu_addr), size, host_ptr); |
| 340 | } else { | 334 | } else { |
| 341 | staging_buffer.resize(size); | 335 | staging_buffer.resize(size); |
| 342 | memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); | 336 | memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); |
| 343 | UploadBlockData(*block, block->Offset(cpu_addr), size, staging_buffer.data()); | 337 | block->Upload(block->Offset(cpu_addr), size, staging_buffer.data()); |
| 344 | } | 338 | } |
| 345 | return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr)); | 339 | return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr)); |
| 346 | } | 340 | } |
| @@ -399,7 +393,7 @@ private: | |||
| 399 | } | 393 | } |
| 400 | staging_buffer.resize(size); | 394 | staging_buffer.resize(size); |
| 401 | system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size); | 395 | system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size); |
| 402 | UploadBlockData(*block, block->Offset(interval.lower()), size, staging_buffer.data()); | 396 | block->Upload(block->Offset(interval.lower()), size, staging_buffer.data()); |
| 403 | } | 397 | } |
| 404 | } | 398 | } |
| 405 | 399 | ||
| @@ -436,7 +430,7 @@ private: | |||
| 436 | 430 | ||
| 437 | const std::size_t size = map->end - map->start; | 431 | const std::size_t size = map->end - map->start; |
| 438 | staging_buffer.resize(size); | 432 | staging_buffer.resize(size); |
| 439 | DownloadBlockData(*block, block->Offset(map->start), size, staging_buffer.data()); | 433 | block->Download(block->Offset(map->start), size, staging_buffer.data()); |
| 440 | system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size); | 434 | system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size); |
| 441 | map->MarkAsModified(false, 0); | 435 | map->MarkAsModified(false, 0); |
| 442 | } | 436 | } |
| @@ -449,7 +443,7 @@ private: | |||
| 449 | 443 | ||
| 450 | buffer_ptr += size; | 444 | buffer_ptr += size; |
| 451 | buffer_offset += size; | 445 | buffer_offset += size; |
| 452 | return {stream_buffer_handle, uploaded_offset}; | 446 | return BufferInfo{stream_buffer->Handle(), uploaded_offset, stream_buffer->Address()}; |
| 453 | } | 447 | } |
| 454 | 448 | ||
| 455 | void AlignBuffer(std::size_t alignment) { | 449 | void AlignBuffer(std::size_t alignment) { |
| @@ -464,7 +458,7 @@ private: | |||
| 464 | const std::size_t new_size = old_size + BLOCK_PAGE_SIZE; | 458 | const std::size_t new_size = old_size + BLOCK_PAGE_SIZE; |
| 465 | const VAddr cpu_addr = buffer->CpuAddr(); | 459 | const VAddr cpu_addr = buffer->CpuAddr(); |
| 466 | std::shared_ptr<Buffer> new_buffer = CreateBlock(cpu_addr, new_size); | 460 | std::shared_ptr<Buffer> new_buffer = CreateBlock(cpu_addr, new_size); |
| 467 | CopyBlock(*buffer, *new_buffer, 0, 0, old_size); | 461 | new_buffer->CopyFrom(*buffer, 0, 0, old_size); |
| 468 | QueueDestruction(std::move(buffer)); | 462 | QueueDestruction(std::move(buffer)); |
| 469 | 463 | ||
| 470 | const VAddr cpu_addr_end = cpu_addr + new_size - 1; | 464 | const VAddr cpu_addr_end = cpu_addr + new_size - 1; |
| @@ -486,8 +480,8 @@ private: | |||
| 486 | const std::size_t new_size = size_1 + size_2; | 480 | const std::size_t new_size = size_1 + size_2; |
| 487 | 481 | ||
| 488 | std::shared_ptr<Buffer> new_buffer = CreateBlock(new_addr, new_size); | 482 | std::shared_ptr<Buffer> new_buffer = CreateBlock(new_addr, new_size); |
| 489 | CopyBlock(*first, *new_buffer, 0, new_buffer->Offset(first_addr), size_1); | 483 | new_buffer->CopyFrom(*first, 0, new_buffer->Offset(first_addr), size_1); |
| 490 | CopyBlock(*second, *new_buffer, 0, new_buffer->Offset(second_addr), size_2); | 484 | new_buffer->CopyFrom(*second, 0, new_buffer->Offset(second_addr), size_2); |
| 491 | QueueDestruction(std::move(first)); | 485 | QueueDestruction(std::move(first)); |
| 492 | QueueDestruction(std::move(second)); | 486 | QueueDestruction(std::move(second)); |
| 493 | 487 | ||
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index ea3c8a963..c01436295 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -128,7 +128,7 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) | |||
| 128 | ((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size()); | 128 | ((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size()); |
| 129 | 129 | ||
| 130 | // Execute the current macro. | 130 | // Execute the current macro. |
| 131 | macro_engine->Execute(macro_positions[entry], parameters); | 131 | macro_engine->Execute(*this, macro_positions[entry], parameters); |
| 132 | if (mme_draw.current_mode != MMEDrawMode::Undefined) { | 132 | if (mme_draw.current_mode != MMEDrawMode::Undefined) { |
| 133 | FlushMMEInlineDraw(); | 133 | FlushMMEInlineDraw(); |
| 134 | } | 134 | } |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index d5fe25065..ef1618990 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -1418,6 +1418,14 @@ public: | |||
| 1418 | return execute_on; | 1418 | return execute_on; |
| 1419 | } | 1419 | } |
| 1420 | 1420 | ||
| 1421 | VideoCore::RasterizerInterface& GetRasterizer() { | ||
| 1422 | return rasterizer; | ||
| 1423 | } | ||
| 1424 | |||
| 1425 | const VideoCore::RasterizerInterface& GetRasterizer() const { | ||
| 1426 | return rasterizer; | ||
| 1427 | } | ||
| 1428 | |||
| 1421 | /// Notify a memory write has happened. | 1429 | /// Notify a memory write has happened. |
| 1422 | void OnMemoryWrite() { | 1430 | void OnMemoryWrite() { |
| 1423 | dirty.flags |= dirty.on_write_stores; | 1431 | dirty.flags |= dirty.on_write_stores; |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index e7cb87589..d374b73cf 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -661,6 +661,10 @@ union Instruction { | |||
| 661 | constexpr Instruction(u64 value) : value{value} {} | 661 | constexpr Instruction(u64 value) : value{value} {} |
| 662 | constexpr Instruction(const Instruction& instr) : value(instr.value) {} | 662 | constexpr Instruction(const Instruction& instr) : value(instr.value) {} |
| 663 | 663 | ||
| 664 | constexpr bool Bit(u64 offset) const { | ||
| 665 | return ((value >> offset) & 1) != 0; | ||
| 666 | } | ||
| 667 | |||
| 664 | BitField<0, 8, Register> gpr0; | 668 | BitField<0, 8, Register> gpr0; |
| 665 | BitField<8, 8, Register> gpr8; | 669 | BitField<8, 8, Register> gpr8; |
| 666 | union { | 670 | union { |
| @@ -1874,7 +1878,9 @@ public: | |||
| 1874 | HSETP2_C, | 1878 | HSETP2_C, |
| 1875 | HSETP2_R, | 1879 | HSETP2_R, |
| 1876 | HSETP2_IMM, | 1880 | HSETP2_IMM, |
| 1881 | HSET2_C, | ||
| 1877 | HSET2_R, | 1882 | HSET2_R, |
| 1883 | HSET2_IMM, | ||
| 1878 | POPC_C, | 1884 | POPC_C, |
| 1879 | POPC_R, | 1885 | POPC_R, |
| 1880 | POPC_IMM, | 1886 | POPC_IMM, |
| @@ -2194,7 +2200,9 @@ private: | |||
| 2194 | INST("0111111-1-------", Id::HSETP2_C, Type::HalfSetPredicate, "HSETP2_C"), | 2200 | INST("0111111-1-------", Id::HSETP2_C, Type::HalfSetPredicate, "HSETP2_C"), |
| 2195 | INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), | 2201 | INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), |
| 2196 | INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), | 2202 | INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), |
| 2203 | INST("0111110-1-------", Id::HSET2_C, Type::HalfSet, "HSET2_C"), | ||
| 2197 | INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), | 2204 | INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), |
| 2205 | INST("0111110-0-------", Id::HSET2_IMM, Type::HalfSet, "HSET2_IMM"), | ||
| 2198 | INST("010110111010----", Id::FCMP_RR, Type::Arithmetic, "FCMP_RR"), | 2206 | INST("010110111010----", Id::FCMP_RR, Type::Arithmetic, "FCMP_RR"), |
| 2199 | INST("010010111010----", Id::FCMP_RC, Type::Arithmetic, "FCMP_RC"), | 2207 | INST("010010111010----", Id::FCMP_RC, Type::Arithmetic, "FCMP_RC"), |
| 2200 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), | 2208 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 8eb017f65..482e49711 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <chrono> | ||
| 6 | |||
| 5 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 6 | #include "common/microprofile.h" | 8 | #include "common/microprofile.h" |
| 7 | #include "core/core.h" | 9 | #include "core/core.h" |
| @@ -154,8 +156,7 @@ u64 GPU::GetTicks() const { | |||
| 154 | constexpr u64 gpu_ticks_num = 384; | 156 | constexpr u64 gpu_ticks_num = 384; |
| 155 | constexpr u64 gpu_ticks_den = 625; | 157 | constexpr u64 gpu_ticks_den = 625; |
| 156 | 158 | ||
| 157 | const u64 cpu_ticks = system.CoreTiming().GetTicks(); | 159 | u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count(); |
| 158 | u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count(); | ||
| 159 | if (Settings::values.use_fast_gpu_time) { | 160 | if (Settings::values.use_fast_gpu_time) { |
| 160 | nanoseconds /= 256; | 161 | nanoseconds /= 256; |
| 161 | } | 162 | } |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index a1b4c305c..2c42483bd 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -284,6 +284,12 @@ public: | |||
| 284 | /// core timing events. | 284 | /// core timing events. |
| 285 | virtual void Start() = 0; | 285 | virtual void Start() = 0; |
| 286 | 286 | ||
| 287 | /// Obtain the CPU Context | ||
| 288 | virtual void ObtainContext() = 0; | ||
| 289 | |||
| 290 | /// Release the CPU Context | ||
| 291 | virtual void ReleaseContext() = 0; | ||
| 292 | |||
| 287 | /// Push GPU command entries to be processed | 293 | /// Push GPU command entries to be processed |
| 288 | virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; | 294 | virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; |
| 289 | 295 | ||
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp index 53305ab43..7b855f63e 100644 --- a/src/video_core/gpu_asynch.cpp +++ b/src/video_core/gpu_asynch.cpp | |||
| @@ -19,10 +19,17 @@ GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBa | |||
| 19 | GPUAsynch::~GPUAsynch() = default; | 19 | GPUAsynch::~GPUAsynch() = default; |
| 20 | 20 | ||
| 21 | void GPUAsynch::Start() { | 21 | void GPUAsynch::Start() { |
| 22 | cpu_context->MakeCurrent(); | ||
| 23 | gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher); | 22 | gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher); |
| 24 | } | 23 | } |
| 25 | 24 | ||
| 25 | void GPUAsynch::ObtainContext() { | ||
| 26 | cpu_context->MakeCurrent(); | ||
| 27 | } | ||
| 28 | |||
| 29 | void GPUAsynch::ReleaseContext() { | ||
| 30 | cpu_context->DoneCurrent(); | ||
| 31 | } | ||
| 32 | |||
| 26 | void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { | 33 | void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { |
| 27 | gpu_thread.SubmitList(std::move(entries)); | 34 | gpu_thread.SubmitList(std::move(entries)); |
| 28 | } | 35 | } |
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h index 517658612..15e9f1d38 100644 --- a/src/video_core/gpu_asynch.h +++ b/src/video_core/gpu_asynch.h | |||
| @@ -25,6 +25,8 @@ public: | |||
| 25 | ~GPUAsynch() override; | 25 | ~GPUAsynch() override; |
| 26 | 26 | ||
| 27 | void Start() override; | 27 | void Start() override; |
| 28 | void ObtainContext() override; | ||
| 29 | void ReleaseContext() override; | ||
| 28 | void PushGPUEntries(Tegra::CommandList&& entries) override; | 30 | void PushGPUEntries(Tegra::CommandList&& entries) override; |
| 29 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 31 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 30 | void FlushRegion(VAddr addr, u64 size) override; | 32 | void FlushRegion(VAddr addr, u64 size) override; |
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp index 6f38a672a..aaeb9811d 100644 --- a/src/video_core/gpu_synch.cpp +++ b/src/video_core/gpu_synch.cpp | |||
| @@ -13,10 +13,16 @@ GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase | |||
| 13 | 13 | ||
| 14 | GPUSynch::~GPUSynch() = default; | 14 | GPUSynch::~GPUSynch() = default; |
| 15 | 15 | ||
| 16 | void GPUSynch::Start() { | 16 | void GPUSynch::Start() {} |
| 17 | |||
| 18 | void GPUSynch::ObtainContext() { | ||
| 17 | context->MakeCurrent(); | 19 | context->MakeCurrent(); |
| 18 | } | 20 | } |
| 19 | 21 | ||
| 22 | void GPUSynch::ReleaseContext() { | ||
| 23 | context->DoneCurrent(); | ||
| 24 | } | ||
| 25 | |||
| 20 | void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { | 26 | void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { |
| 21 | dma_pusher->Push(std::move(entries)); | 27 | dma_pusher->Push(std::move(entries)); |
| 22 | dma_pusher->DispatchCalls(); | 28 | dma_pusher->DispatchCalls(); |
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h index 4a6e9a01d..762c20aa5 100644 --- a/src/video_core/gpu_synch.h +++ b/src/video_core/gpu_synch.h | |||
| @@ -24,6 +24,8 @@ public: | |||
| 24 | ~GPUSynch() override; | 24 | ~GPUSynch() override; |
| 25 | 25 | ||
| 26 | void Start() override; | 26 | void Start() override; |
| 27 | void ObtainContext() override; | ||
| 28 | void ReleaseContext() override; | ||
| 27 | void PushGPUEntries(Tegra::CommandList&& entries) override; | 29 | void PushGPUEntries(Tegra::CommandList&& entries) override; |
| 28 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 30 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 29 | void FlushRegion(VAddr addr, u64 size) override; | 31 | void FlushRegion(VAddr addr, u64 size) override; |
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index c3bb4fe06..738c6f0c1 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 7 | #include "common/thread.h" | ||
| 7 | #include "core/core.h" | 8 | #include "core/core.h" |
| 8 | #include "core/frontend/emu_window.h" | 9 | #include "core/frontend/emu_window.h" |
| 9 | #include "core/settings.h" | 10 | #include "core/settings.h" |
| @@ -18,7 +19,11 @@ namespace VideoCommon::GPUThread { | |||
| 18 | static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, | 19 | static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, |
| 19 | Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, | 20 | Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, |
| 20 | SynchState& state) { | 21 | SynchState& state) { |
| 21 | MicroProfileOnThreadCreate("GpuThread"); | 22 | std::string name = "yuzu:GPU"; |
| 23 | MicroProfileOnThreadCreate(name.c_str()); | ||
| 24 | Common::SetCurrentThreadName(name.c_str()); | ||
| 25 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||
| 26 | system.RegisterHostThread(); | ||
| 22 | 27 | ||
| 23 | // Wait for first GPU command before acquiring the window context | 28 | // Wait for first GPU command before acquiring the window context |
| 24 | while (state.queue.Empty()) | 29 | while (state.queue.Empty()) |
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index 89077a2d8..ef7dad349 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp | |||
| @@ -2,23 +2,37 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <boost/container_hash/hash.hpp> | ||
| 5 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 6 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 7 | #include "core/settings.h" | 8 | #include "core/settings.h" |
| 9 | #include "video_core/engines/maxwell_3d.h" | ||
| 8 | #include "video_core/macro/macro.h" | 10 | #include "video_core/macro/macro.h" |
| 11 | #include "video_core/macro/macro_hle.h" | ||
| 9 | #include "video_core/macro/macro_interpreter.h" | 12 | #include "video_core/macro/macro_interpreter.h" |
| 10 | #include "video_core/macro/macro_jit_x64.h" | 13 | #include "video_core/macro/macro_jit_x64.h" |
| 11 | 14 | ||
| 12 | namespace Tegra { | 15 | namespace Tegra { |
| 13 | 16 | ||
| 17 | MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d) | ||
| 18 | : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {} | ||
| 19 | |||
| 20 | MacroEngine::~MacroEngine() = default; | ||
| 21 | |||
| 14 | void MacroEngine::AddCode(u32 method, u32 data) { | 22 | void MacroEngine::AddCode(u32 method, u32 data) { |
| 15 | uploaded_macro_code[method].push_back(data); | 23 | uploaded_macro_code[method].push_back(data); |
| 16 | } | 24 | } |
| 17 | 25 | ||
| 18 | void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) { | 26 | void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, |
| 27 | const std::vector<u32>& parameters) { | ||
| 19 | auto compiled_macro = macro_cache.find(method); | 28 | auto compiled_macro = macro_cache.find(method); |
| 20 | if (compiled_macro != macro_cache.end()) { | 29 | if (compiled_macro != macro_cache.end()) { |
| 21 | compiled_macro->second->Execute(parameters, method); | 30 | const auto& cache_info = compiled_macro->second; |
| 31 | if (cache_info.has_hle_program) { | ||
| 32 | cache_info.hle_program->Execute(parameters, method); | ||
| 33 | } else { | ||
| 34 | cache_info.lle_program->Execute(parameters, method); | ||
| 35 | } | ||
| 22 | } else { | 36 | } else { |
| 23 | // Macro not compiled, check if it's uploaded and if so, compile it | 37 | // Macro not compiled, check if it's uploaded and if so, compile it |
| 24 | auto macro_code = uploaded_macro_code.find(method); | 38 | auto macro_code = uploaded_macro_code.find(method); |
| @@ -26,8 +40,21 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) { | |||
| 26 | UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); | 40 | UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); |
| 27 | return; | 41 | return; |
| 28 | } | 42 | } |
| 29 | macro_cache[method] = Compile(macro_code->second); | 43 | auto& cache_info = macro_cache[method]; |
| 30 | macro_cache[method]->Execute(parameters, method); | 44 | cache_info.hash = boost::hash_value(macro_code->second); |
| 45 | cache_info.lle_program = Compile(macro_code->second); | ||
| 46 | |||
| 47 | auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); | ||
| 48 | if (hle_program.has_value()) { | ||
| 49 | cache_info.has_hle_program = true; | ||
| 50 | cache_info.hle_program = std::move(hle_program.value()); | ||
| 51 | } | ||
| 52 | |||
| 53 | if (cache_info.has_hle_program) { | ||
| 54 | cache_info.hle_program->Execute(parameters, method); | ||
| 55 | } else { | ||
| 56 | cache_info.lle_program->Execute(parameters, method); | ||
| 57 | } | ||
| 31 | } | 58 | } |
| 32 | } | 59 | } |
| 33 | 60 | ||
diff --git a/src/video_core/macro/macro.h b/src/video_core/macro/macro.h index b76ed891f..4d00b84b0 100644 --- a/src/video_core/macro/macro.h +++ b/src/video_core/macro/macro.h | |||
| @@ -11,9 +11,11 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | namespace Tegra { | 13 | namespace Tegra { |
| 14 | |||
| 14 | namespace Engines { | 15 | namespace Engines { |
| 15 | class Maxwell3D; | 16 | class Maxwell3D; |
| 16 | } | 17 | } |
| 18 | |||
| 17 | namespace Macro { | 19 | namespace Macro { |
| 18 | constexpr std::size_t NUM_MACRO_REGISTERS = 8; | 20 | constexpr std::size_t NUM_MACRO_REGISTERS = 8; |
| 19 | enum class Operation : u32 { | 21 | enum class Operation : u32 { |
| @@ -94,6 +96,8 @@ union MethodAddress { | |||
| 94 | 96 | ||
| 95 | } // namespace Macro | 97 | } // namespace Macro |
| 96 | 98 | ||
| 99 | class HLEMacro; | ||
| 100 | |||
| 97 | class CachedMacro { | 101 | class CachedMacro { |
| 98 | public: | 102 | public: |
| 99 | virtual ~CachedMacro() = default; | 103 | virtual ~CachedMacro() = default; |
| @@ -107,20 +111,29 @@ public: | |||
| 107 | 111 | ||
| 108 | class MacroEngine { | 112 | class MacroEngine { |
| 109 | public: | 113 | public: |
| 110 | virtual ~MacroEngine() = default; | 114 | explicit MacroEngine(Engines::Maxwell3D& maxwell3d); |
| 115 | virtual ~MacroEngine(); | ||
| 111 | 116 | ||
| 112 | // Store the uploaded macro code to compile them when they're called. | 117 | // Store the uploaded macro code to compile them when they're called. |
| 113 | void AddCode(u32 method, u32 data); | 118 | void AddCode(u32 method, u32 data); |
| 114 | 119 | ||
| 115 | // Compiles the macro if its not in the cache, and executes the compiled macro | 120 | // Compiles the macro if its not in the cache, and executes the compiled macro |
| 116 | void Execute(u32 method, const std::vector<u32>& parameters); | 121 | void Execute(Engines::Maxwell3D& maxwell3d, u32 method, const std::vector<u32>& parameters); |
| 117 | 122 | ||
| 118 | protected: | 123 | protected: |
| 119 | virtual std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) = 0; | 124 | virtual std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) = 0; |
| 120 | 125 | ||
| 121 | private: | 126 | private: |
| 122 | std::unordered_map<u32, std::unique_ptr<CachedMacro>> macro_cache; | 127 | struct CacheInfo { |
| 128 | std::unique_ptr<CachedMacro> lle_program{}; | ||
| 129 | std::unique_ptr<CachedMacro> hle_program{}; | ||
| 130 | u64 hash{}; | ||
| 131 | bool has_hle_program{}; | ||
| 132 | }; | ||
| 133 | |||
| 134 | std::unordered_map<u32, CacheInfo> macro_cache; | ||
| 123 | std::unordered_map<u32, std::vector<u32>> uploaded_macro_code; | 135 | std::unordered_map<u32, std::vector<u32>> uploaded_macro_code; |
| 136 | std::unique_ptr<HLEMacro> hle_macros; | ||
| 124 | }; | 137 | }; |
| 125 | 138 | ||
| 126 | std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d); | 139 | std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d); |
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp new file mode 100644 index 000000000..410f99018 --- /dev/null +++ b/src/video_core/macro/macro_hle.cpp | |||
| @@ -0,0 +1,113 @@ | |||
| 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 <array> | ||
| 6 | #include <vector> | ||
| 7 | #include "video_core/engines/maxwell_3d.h" | ||
| 8 | #include "video_core/macro/macro_hle.h" | ||
| 9 | #include "video_core/rasterizer_interface.h" | ||
| 10 | |||
| 11 | namespace Tegra { | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | // HLE'd functions | ||
| 15 | static void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, | ||
| 16 | const std::vector<u32>& parameters) { | ||
| 17 | const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B); | ||
| 18 | |||
| 19 | maxwell3d.regs.draw.topology.Assign( | ||
| 20 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & | ||
| 21 | ~(0x3ffffff << 26))); | ||
| 22 | maxwell3d.regs.vb_base_instance = parameters[5]; | ||
| 23 | maxwell3d.mme_draw.instance_count = instance_count; | ||
| 24 | maxwell3d.regs.vb_element_base = parameters[3]; | ||
| 25 | maxwell3d.regs.index_array.count = parameters[1]; | ||
| 26 | maxwell3d.regs.index_array.first = parameters[4]; | ||
| 27 | |||
| 28 | if (maxwell3d.ShouldExecute()) { | ||
| 29 | maxwell3d.GetRasterizer().Draw(true, true); | ||
| 30 | } | ||
| 31 | maxwell3d.regs.index_array.count = 0; | ||
| 32 | maxwell3d.mme_draw.instance_count = 0; | ||
| 33 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 34 | } | ||
| 35 | |||
| 36 | static void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, | ||
| 37 | const std::vector<u32>& parameters) { | ||
| 38 | const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); | ||
| 39 | |||
| 40 | maxwell3d.regs.vertex_buffer.first = parameters[3]; | ||
| 41 | maxwell3d.regs.vertex_buffer.count = parameters[1]; | ||
| 42 | maxwell3d.regs.vb_base_instance = parameters[4]; | ||
| 43 | maxwell3d.regs.draw.topology.Assign( | ||
| 44 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); | ||
| 45 | maxwell3d.mme_draw.instance_count = count; | ||
| 46 | |||
| 47 | if (maxwell3d.ShouldExecute()) { | ||
| 48 | maxwell3d.GetRasterizer().Draw(false, true); | ||
| 49 | } | ||
| 50 | maxwell3d.regs.vertex_buffer.count = 0; | ||
| 51 | maxwell3d.mme_draw.instance_count = 0; | ||
| 52 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 53 | } | ||
| 54 | |||
| 55 | static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, | ||
| 56 | const std::vector<u32>& parameters) { | ||
| 57 | const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); | ||
| 58 | const u32 element_base = parameters[4]; | ||
| 59 | const u32 base_instance = parameters[5]; | ||
| 60 | maxwell3d.regs.index_array.first = parameters[3]; | ||
| 61 | maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base? | ||
| 62 | maxwell3d.regs.index_array.count = parameters[1]; | ||
| 63 | maxwell3d.regs.vb_element_base = element_base; | ||
| 64 | maxwell3d.regs.vb_base_instance = base_instance; | ||
| 65 | maxwell3d.mme_draw.instance_count = instance_count; | ||
| 66 | maxwell3d.CallMethodFromMME(0x8e3, 0x640); | ||
| 67 | maxwell3d.CallMethodFromMME(0x8e4, element_base); | ||
| 68 | maxwell3d.CallMethodFromMME(0x8e5, base_instance); | ||
| 69 | maxwell3d.regs.draw.topology.Assign( | ||
| 70 | static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); | ||
| 71 | if (maxwell3d.ShouldExecute()) { | ||
| 72 | maxwell3d.GetRasterizer().Draw(true, true); | ||
| 73 | } | ||
| 74 | maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? | ||
| 75 | maxwell3d.regs.index_array.count = 0; | ||
| 76 | maxwell3d.regs.vb_element_base = 0x0; | ||
| 77 | maxwell3d.regs.vb_base_instance = 0x0; | ||
| 78 | maxwell3d.mme_draw.instance_count = 0; | ||
| 79 | maxwell3d.CallMethodFromMME(0x8e3, 0x640); | ||
| 80 | maxwell3d.CallMethodFromMME(0x8e4, 0x0); | ||
| 81 | maxwell3d.CallMethodFromMME(0x8e5, 0x0); | ||
| 82 | maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; | ||
| 83 | } | ||
| 84 | } // namespace | ||
| 85 | |||
| 86 | constexpr std::array<std::pair<u64, HLEFunction>, 3> hle_funcs{{ | ||
| 87 | std::make_pair<u64, HLEFunction>(0x771BB18C62444DA0, &HLE_771BB18C62444DA0), | ||
| 88 | std::make_pair<u64, HLEFunction>(0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD), | ||
| 89 | std::make_pair<u64, HLEFunction>(0x0217920100488FF7, &HLE_0217920100488FF7), | ||
| 90 | }}; | ||
| 91 | |||
| 92 | HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} | ||
| 93 | HLEMacro::~HLEMacro() = default; | ||
| 94 | |||
| 95 | std::optional<std::unique_ptr<CachedMacro>> HLEMacro::GetHLEProgram(u64 hash) const { | ||
| 96 | const auto it = std::find_if(hle_funcs.cbegin(), hle_funcs.cend(), | ||
| 97 | [hash](const auto& pair) { return pair.first == hash; }); | ||
| 98 | if (it == hle_funcs.end()) { | ||
| 99 | return std::nullopt; | ||
| 100 | } | ||
| 101 | return std::make_unique<HLEMacroImpl>(maxwell3d, it->second); | ||
| 102 | } | ||
| 103 | |||
| 104 | HLEMacroImpl::~HLEMacroImpl() = default; | ||
| 105 | |||
| 106 | HLEMacroImpl::HLEMacroImpl(Engines::Maxwell3D& maxwell3d, HLEFunction func) | ||
| 107 | : maxwell3d(maxwell3d), func(func) {} | ||
| 108 | |||
| 109 | void HLEMacroImpl::Execute(const std::vector<u32>& parameters, u32 method) { | ||
| 110 | func(maxwell3d, parameters); | ||
| 111 | } | ||
| 112 | |||
| 113 | } // namespace Tegra | ||
diff --git a/src/video_core/macro/macro_hle.h b/src/video_core/macro/macro_hle.h new file mode 100644 index 000000000..37af875a0 --- /dev/null +++ b/src/video_core/macro/macro_hle.h | |||
| @@ -0,0 +1,44 @@ | |||
| 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 <memory> | ||
| 8 | #include <optional> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "video_core/macro/macro.h" | ||
| 12 | |||
| 13 | namespace Tegra { | ||
| 14 | |||
| 15 | namespace Engines { | ||
| 16 | class Maxwell3D; | ||
| 17 | } | ||
| 18 | |||
| 19 | using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters); | ||
| 20 | |||
| 21 | class HLEMacro { | ||
| 22 | public: | ||
| 23 | explicit HLEMacro(Engines::Maxwell3D& maxwell3d); | ||
| 24 | ~HLEMacro(); | ||
| 25 | |||
| 26 | std::optional<std::unique_ptr<CachedMacro>> GetHLEProgram(u64 hash) const; | ||
| 27 | |||
| 28 | private: | ||
| 29 | Engines::Maxwell3D& maxwell3d; | ||
| 30 | }; | ||
| 31 | |||
| 32 | class HLEMacroImpl : public CachedMacro { | ||
| 33 | public: | ||
| 34 | explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d, HLEFunction func); | ||
| 35 | ~HLEMacroImpl(); | ||
| 36 | |||
| 37 | void Execute(const std::vector<u32>& parameters, u32 method) override; | ||
| 38 | |||
| 39 | private: | ||
| 40 | Engines::Maxwell3D& maxwell3d; | ||
| 41 | HLEFunction func; | ||
| 42 | }; | ||
| 43 | |||
| 44 | } // namespace Tegra | ||
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp index 5edff27aa..aa5256419 100644 --- a/src/video_core/macro/macro_interpreter.cpp +++ b/src/video_core/macro/macro_interpreter.cpp | |||
| @@ -11,7 +11,8 @@ | |||
| 11 | MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192)); | 11 | MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192)); |
| 12 | 12 | ||
| 13 | namespace Tegra { | 13 | namespace Tegra { |
| 14 | MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} | 14 | MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) |
| 15 | : MacroEngine::MacroEngine(maxwell3d), maxwell3d(maxwell3d) {} | ||
| 15 | 16 | ||
| 16 | std::unique_ptr<CachedMacro> MacroInterpreter::Compile(const std::vector<u32>& code) { | 17 | std::unique_ptr<CachedMacro> MacroInterpreter::Compile(const std::vector<u32>& code) { |
| 17 | return std::make_unique<MacroInterpreterImpl>(maxwell3d, code); | 18 | return std::make_unique<MacroInterpreterImpl>(maxwell3d, code); |
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp index 30abb66e5..07292702f 100644 --- a/src/video_core/macro/macro_jit_x64.cpp +++ b/src/video_core/macro/macro_jit_x64.cpp | |||
| @@ -28,7 +28,8 @@ static const std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({ | |||
| 28 | BRANCH_HOLDER, | 28 | BRANCH_HOLDER, |
| 29 | }); | 29 | }); |
| 30 | 30 | ||
| 31 | MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} | 31 | MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d) |
| 32 | : MacroEngine::MacroEngine(maxwell3d), maxwell3d(maxwell3d) {} | ||
| 32 | 33 | ||
| 33 | std::unique_ptr<CachedMacro> MacroJITx64::Compile(const std::vector<u32>& code) { | 34 | std::unique_ptr<CachedMacro> MacroJITx64::Compile(const std::vector<u32>& code) { |
| 34 | return std::make_unique<MacroJITx64Impl>(maxwell3d, code); | 35 | return std::make_unique<MacroJITx64Impl>(maxwell3d, code); |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index ad0577a4f..d9f7b4cc6 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -22,21 +22,46 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs; | |||
| 22 | 22 | ||
| 23 | MICROPROFILE_DEFINE(OpenGL_Buffer_Download, "OpenGL", "Buffer Download", MP_RGB(192, 192, 128)); | 23 | MICROPROFILE_DEFINE(OpenGL_Buffer_Download, "OpenGL", "Buffer Download", MP_RGB(192, 192, 128)); |
| 24 | 24 | ||
| 25 | Buffer::Buffer(VAddr cpu_addr, const std::size_t size) : VideoCommon::BufferBlock{cpu_addr, size} { | 25 | Buffer::Buffer(const Device& device, VAddr cpu_addr, std::size_t size) |
| 26 | : VideoCommon::BufferBlock{cpu_addr, size} { | ||
| 26 | gl_buffer.Create(); | 27 | gl_buffer.Create(); |
| 27 | glNamedBufferData(gl_buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW); | 28 | glNamedBufferData(gl_buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW); |
| 29 | if (device.HasVertexBufferUnifiedMemory()) { | ||
| 30 | glMakeNamedBufferResidentNV(gl_buffer.handle, GL_READ_WRITE); | ||
| 31 | glGetNamedBufferParameterui64vNV(gl_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &gpu_address); | ||
| 32 | } | ||
| 28 | } | 33 | } |
| 29 | 34 | ||
| 30 | Buffer::~Buffer() = default; | 35 | Buffer::~Buffer() = default; |
| 31 | 36 | ||
| 37 | void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { | ||
| 38 | glNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), | ||
| 39 | data); | ||
| 40 | } | ||
| 41 | |||
| 42 | void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { | ||
| 43 | MICROPROFILE_SCOPE(OpenGL_Buffer_Download); | ||
| 44 | glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); | ||
| 45 | glGetNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), | ||
| 46 | data); | ||
| 47 | } | ||
| 48 | |||
| 49 | void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, | ||
| 50 | std::size_t size) const { | ||
| 51 | glCopyNamedBufferSubData(src.Handle(), Handle(), static_cast<GLintptr>(src_offset), | ||
| 52 | static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size)); | ||
| 53 | } | ||
| 54 | |||
| 32 | OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, | 55 | OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, |
| 33 | const Device& device, std::size_t stream_size) | 56 | const Device& device_, std::size_t stream_size) |
| 34 | : GenericBufferCache{rasterizer, system, std::make_unique<OGLStreamBuffer>(stream_size, true)} { | 57 | : GenericBufferCache{rasterizer, system, |
| 58 | std::make_unique<OGLStreamBuffer>(device_, stream_size, true)}, | ||
| 59 | device{device_} { | ||
| 35 | if (!device.HasFastBufferSubData()) { | 60 | if (!device.HasFastBufferSubData()) { |
| 36 | return; | 61 | return; |
| 37 | } | 62 | } |
| 38 | 63 | ||
| 39 | static constexpr auto size = static_cast<GLsizeiptr>(Maxwell::MaxConstBufferSize); | 64 | static constexpr GLsizeiptr size = static_cast<GLsizeiptr>(Maxwell::MaxConstBufferSize); |
| 40 | glCreateBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs)); | 65 | glCreateBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs)); |
| 41 | for (const GLuint cbuf : cbufs) { | 66 | for (const GLuint cbuf : cbufs) { |
| 42 | glNamedBufferData(cbuf, size, nullptr, GL_STREAM_DRAW); | 67 | glNamedBufferData(cbuf, size, nullptr, GL_STREAM_DRAW); |
| @@ -48,39 +73,20 @@ OGLBufferCache::~OGLBufferCache() { | |||
| 48 | } | 73 | } |
| 49 | 74 | ||
| 50 | std::shared_ptr<Buffer> OGLBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { | 75 | std::shared_ptr<Buffer> OGLBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { |
| 51 | return std::make_shared<Buffer>(cpu_addr, size); | 76 | return std::make_shared<Buffer>(device, cpu_addr, size); |
| 52 | } | 77 | } |
| 53 | 78 | ||
| 54 | GLuint OGLBufferCache::GetEmptyBuffer(std::size_t) { | 79 | OGLBufferCache::BufferInfo OGLBufferCache::GetEmptyBuffer(std::size_t) { |
| 55 | return 0; | 80 | return {0, 0, 0}; |
| 56 | } | ||
| 57 | |||
| 58 | void OGLBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 59 | const u8* data) { | ||
| 60 | glNamedBufferSubData(buffer.Handle(), static_cast<GLintptr>(offset), | ||
| 61 | static_cast<GLsizeiptr>(size), data); | ||
| 62 | } | ||
| 63 | |||
| 64 | void OGLBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 65 | u8* data) { | ||
| 66 | MICROPROFILE_SCOPE(OpenGL_Buffer_Download); | ||
| 67 | glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); | ||
| 68 | glGetNamedBufferSubData(buffer.Handle(), static_cast<GLintptr>(offset), | ||
| 69 | static_cast<GLsizeiptr>(size), data); | ||
| 70 | } | ||
| 71 | |||
| 72 | void OGLBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, | ||
| 73 | std::size_t dst_offset, std::size_t size) { | ||
| 74 | glCopyNamedBufferSubData(src.Handle(), dst.Handle(), static_cast<GLintptr>(src_offset), | ||
| 75 | static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size)); | ||
| 76 | } | 81 | } |
| 77 | 82 | ||
| 78 | OGLBufferCache::BufferInfo OGLBufferCache::ConstBufferUpload(const void* raw_pointer, | 83 | OGLBufferCache::BufferInfo OGLBufferCache::ConstBufferUpload(const void* raw_pointer, |
| 79 | std::size_t size) { | 84 | std::size_t size) { |
| 80 | DEBUG_ASSERT(cbuf_cursor < std::size(cbufs)); | 85 | DEBUG_ASSERT(cbuf_cursor < std::size(cbufs)); |
| 81 | const GLuint cbuf = cbufs[cbuf_cursor++]; | 86 | const GLuint cbuf = cbufs[cbuf_cursor++]; |
| 87 | |||
| 82 | glNamedBufferSubData(cbuf, 0, static_cast<GLsizeiptr>(size), raw_pointer); | 88 | glNamedBufferSubData(cbuf, 0, static_cast<GLsizeiptr>(size), raw_pointer); |
| 83 | return {cbuf, 0}; | 89 | return {cbuf, 0, 0}; |
| 84 | } | 90 | } |
| 85 | 91 | ||
| 86 | } // namespace OpenGL | 92 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index a49aaf9c4..59d95adbc 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -25,15 +25,27 @@ class RasterizerOpenGL; | |||
| 25 | 25 | ||
| 26 | class Buffer : public VideoCommon::BufferBlock { | 26 | class Buffer : public VideoCommon::BufferBlock { |
| 27 | public: | 27 | public: |
| 28 | explicit Buffer(VAddr cpu_addr, const std::size_t size); | 28 | explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size); |
| 29 | ~Buffer(); | 29 | ~Buffer(); |
| 30 | 30 | ||
| 31 | GLuint Handle() const { | 31 | void Upload(std::size_t offset, std::size_t size, const u8* data) const; |
| 32 | |||
| 33 | void Download(std::size_t offset, std::size_t size, u8* data) const; | ||
| 34 | |||
| 35 | void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, | ||
| 36 | std::size_t size) const; | ||
| 37 | |||
| 38 | GLuint Handle() const noexcept { | ||
| 32 | return gl_buffer.handle; | 39 | return gl_buffer.handle; |
| 33 | } | 40 | } |
| 34 | 41 | ||
| 42 | u64 Address() const noexcept { | ||
| 43 | return gpu_address; | ||
| 44 | } | ||
| 45 | |||
| 35 | private: | 46 | private: |
| 36 | OGLBuffer gl_buffer; | 47 | OGLBuffer gl_buffer; |
| 48 | u64 gpu_address = 0; | ||
| 37 | }; | 49 | }; |
| 38 | 50 | ||
| 39 | using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>; | 51 | using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>; |
| @@ -43,7 +55,7 @@ public: | |||
| 43 | const Device& device, std::size_t stream_size); | 55 | const Device& device, std::size_t stream_size); |
| 44 | ~OGLBufferCache(); | 56 | ~OGLBufferCache(); |
| 45 | 57 | ||
| 46 | GLuint GetEmptyBuffer(std::size_t) override; | 58 | BufferInfo GetEmptyBuffer(std::size_t) override; |
| 47 | 59 | ||
| 48 | void Acquire() noexcept { | 60 | void Acquire() noexcept { |
| 49 | cbuf_cursor = 0; | 61 | cbuf_cursor = 0; |
| @@ -52,22 +64,16 @@ public: | |||
| 52 | protected: | 64 | protected: |
| 53 | std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override; | 65 | std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override; |
| 54 | 66 | ||
| 55 | void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 56 | const u8* data) override; | ||
| 57 | |||
| 58 | void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 59 | u8* data) override; | ||
| 60 | |||
| 61 | void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, | ||
| 62 | std::size_t dst_offset, std::size_t size) override; | ||
| 63 | |||
| 64 | BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) override; | 67 | BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) override; |
| 65 | 68 | ||
| 66 | private: | 69 | private: |
| 70 | static constexpr std::size_t NUM_CBUFS = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers * | ||
| 71 | Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; | ||
| 72 | |||
| 73 | const Device& device; | ||
| 74 | |||
| 67 | std::size_t cbuf_cursor = 0; | 75 | std::size_t cbuf_cursor = 0; |
| 68 | std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers * | 76 | std::array<GLuint, NUM_CBUFS> cbufs{}; |
| 69 | Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram> | ||
| 70 | cbufs; | ||
| 71 | }; | 77 | }; |
| 72 | 78 | ||
| 73 | } // namespace OpenGL | 79 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index b31d604e4..b6b6659c1 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -178,7 +178,7 @@ bool IsASTCSupported() { | |||
| 178 | for (const GLenum format : formats) { | 178 | for (const GLenum format : formats) { |
| 179 | for (const GLenum support : required_support) { | 179 | for (const GLenum support : required_support) { |
| 180 | GLint value; | 180 | GLint value; |
| 181 | glGetInternalformativ(GL_TEXTURE_2D, format, support, 1, &value); | 181 | glGetInternalformativ(target, format, support, 1, &value); |
| 182 | if (value != GL_FULL_SUPPORT) { | 182 | if (value != GL_FULL_SUPPORT) { |
| 183 | return false; | 183 | return false; |
| 184 | } | 184 | } |
| @@ -188,16 +188,32 @@ bool IsASTCSupported() { | |||
| 188 | return true; | 188 | return true; |
| 189 | } | 189 | } |
| 190 | 190 | ||
| 191 | /// @brief Returns true when a GL_RENDERER is a Turing GPU | ||
| 192 | /// @param renderer GL_RENDERER string | ||
| 193 | bool IsTuring(std::string_view renderer) { | ||
| 194 | static constexpr std::array<std::string_view, 12> TURING_GPUS = { | ||
| 195 | "GTX 1650", "GTX 1660", "RTX 2060", "RTX 2070", | ||
| 196 | "RTX 2080", "TITAN RTX", "Quadro RTX 3000", "Quadro RTX 4000", | ||
| 197 | "Quadro RTX 5000", "Quadro RTX 6000", "Quadro RTX 8000", "Tesla T4", | ||
| 198 | }; | ||
| 199 | return std::any_of(TURING_GPUS.begin(), TURING_GPUS.end(), | ||
| 200 | [renderer](std::string_view candidate) { | ||
| 201 | return renderer.find(candidate) != std::string_view::npos; | ||
| 202 | }); | ||
| 203 | } | ||
| 204 | |||
| 191 | } // Anonymous namespace | 205 | } // Anonymous namespace |
| 192 | 206 | ||
| 193 | Device::Device() | 207 | Device::Device() |
| 194 | : max_uniform_buffers{BuildMaxUniformBuffers()}, base_bindings{BuildBaseBindings()} { | 208 | : max_uniform_buffers{BuildMaxUniformBuffers()}, base_bindings{BuildBaseBindings()} { |
| 195 | const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); | 209 | const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); |
| 210 | const std::string_view renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); | ||
| 196 | const std::string_view version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); | 211 | const std::string_view version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); |
| 197 | const std::vector extensions = GetExtensions(); | 212 | const std::vector extensions = GetExtensions(); |
| 198 | 213 | ||
| 199 | const bool is_nvidia = vendor == "NVIDIA Corporation"; | 214 | const bool is_nvidia = vendor == "NVIDIA Corporation"; |
| 200 | const bool is_amd = vendor == "ATI Technologies Inc."; | 215 | const bool is_amd = vendor == "ATI Technologies Inc."; |
| 216 | const bool is_turing = is_nvidia && IsTuring(renderer); | ||
| 201 | 217 | ||
| 202 | bool disable_fast_buffer_sub_data = false; | 218 | bool disable_fast_buffer_sub_data = false; |
| 203 | if (is_nvidia && version == "4.6.0 NVIDIA 443.24") { | 219 | if (is_nvidia && version == "4.6.0 NVIDIA 443.24") { |
| @@ -216,12 +232,21 @@ Device::Device() | |||
| 216 | has_shader_ballot = GLAD_GL_ARB_shader_ballot; | 232 | has_shader_ballot = GLAD_GL_ARB_shader_ballot; |
| 217 | has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; | 233 | has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; |
| 218 | has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted"); | 234 | has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted"); |
| 235 | has_texture_shadow_lod = HasExtension(extensions, "GL_EXT_texture_shadow_lod"); | ||
| 219 | has_astc = IsASTCSupported(); | 236 | has_astc = IsASTCSupported(); |
| 220 | has_variable_aoffi = TestVariableAoffi(); | 237 | has_variable_aoffi = TestVariableAoffi(); |
| 221 | has_component_indexing_bug = is_amd; | 238 | has_component_indexing_bug = is_amd; |
| 222 | has_precise_bug = TestPreciseBug(); | 239 | has_precise_bug = TestPreciseBug(); |
| 223 | has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; | ||
| 224 | has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; | 240 | has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; |
| 241 | |||
| 242 | // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive | ||
| 243 | // uniform buffers as "push constants" | ||
| 244 | has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; | ||
| 245 | |||
| 246 | // Nvidia's driver on Turing GPUs randomly crashes when the buffer is made resident, or on | ||
| 247 | // DeleteBuffers. Disable unified memory on these devices. | ||
| 248 | has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory && !is_turing; | ||
| 249 | |||
| 225 | use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 && | 250 | use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 && |
| 226 | GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback && | 251 | GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback && |
| 227 | GLAD_GL_NV_transform_feedback2; | 252 | GLAD_GL_NV_transform_feedback2; |
| @@ -245,6 +270,7 @@ Device::Device(std::nullptr_t) { | |||
| 245 | has_shader_ballot = true; | 270 | has_shader_ballot = true; |
| 246 | has_vertex_viewport_layer = true; | 271 | has_vertex_viewport_layer = true; |
| 247 | has_image_load_formatted = true; | 272 | has_image_load_formatted = true; |
| 273 | has_texture_shadow_lod = true; | ||
| 248 | has_variable_aoffi = true; | 274 | has_variable_aoffi = true; |
| 249 | } | 275 | } |
| 250 | 276 | ||
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 145347943..e1d811966 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h | |||
| @@ -68,6 +68,14 @@ public: | |||
| 68 | return has_image_load_formatted; | 68 | return has_image_load_formatted; |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | bool HasTextureShadowLod() const { | ||
| 72 | return has_texture_shadow_lod; | ||
| 73 | } | ||
| 74 | |||
| 75 | bool HasVertexBufferUnifiedMemory() const { | ||
| 76 | return has_vertex_buffer_unified_memory; | ||
| 77 | } | ||
| 78 | |||
| 71 | bool HasASTC() const { | 79 | bool HasASTC() const { |
| 72 | return has_astc; | 80 | return has_astc; |
| 73 | } | 81 | } |
| @@ -110,6 +118,8 @@ private: | |||
| 110 | bool has_shader_ballot{}; | 118 | bool has_shader_ballot{}; |
| 111 | bool has_vertex_viewport_layer{}; | 119 | bool has_vertex_viewport_layer{}; |
| 112 | bool has_image_load_formatted{}; | 120 | bool has_image_load_formatted{}; |
| 121 | bool has_texture_shadow_lod{}; | ||
| 122 | bool has_vertex_buffer_unified_memory{}; | ||
| 113 | bool has_astc{}; | 123 | bool has_astc{}; |
| 114 | bool has_variable_aoffi{}; | 124 | bool has_variable_aoffi{}; |
| 115 | bool has_component_indexing_bug{}; | 125 | bool has_component_indexing_bug{}; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 2d6c11320..362457ffe 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -61,7 +61,8 @@ constexpr std::size_t NUM_CONST_BUFFERS_BYTES_PER_STAGE = | |||
| 61 | constexpr std::size_t TOTAL_CONST_BUFFER_BYTES = | 61 | constexpr std::size_t TOTAL_CONST_BUFFER_BYTES = |
| 62 | NUM_CONST_BUFFERS_BYTES_PER_STAGE * Maxwell::MaxShaderStage; | 62 | NUM_CONST_BUFFERS_BYTES_PER_STAGE * Maxwell::MaxShaderStage; |
| 63 | 63 | ||
| 64 | constexpr std::size_t NumSupportedVertexAttributes = 16; | 64 | constexpr std::size_t NUM_SUPPORTED_VERTEX_ATTRIBUTES = 16; |
| 65 | constexpr std::size_t NUM_SUPPORTED_VERTEX_BINDINGS = 16; | ||
| 65 | 66 | ||
| 66 | template <typename Engine, typename Entry> | 67 | template <typename Engine, typename Entry> |
| 67 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, | 68 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, |
| @@ -193,7 +194,7 @@ void RasterizerOpenGL::SetupVertexFormat() { | |||
| 193 | // avoid OpenGL errors. | 194 | // avoid OpenGL errors. |
| 194 | // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't | 195 | // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't |
| 195 | // assume every shader uses them all. | 196 | // assume every shader uses them all. |
| 196 | for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) { | 197 | for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) { |
| 197 | if (!flags[Dirty::VertexFormat0 + index]) { | 198 | if (!flags[Dirty::VertexFormat0 + index]) { |
| 198 | continue; | 199 | continue; |
| 199 | } | 200 | } |
| @@ -231,9 +232,11 @@ void RasterizerOpenGL::SetupVertexBuffer() { | |||
| 231 | 232 | ||
| 232 | MICROPROFILE_SCOPE(OpenGL_VB); | 233 | MICROPROFILE_SCOPE(OpenGL_VB); |
| 233 | 234 | ||
| 235 | const bool use_unified_memory = device.HasVertexBufferUnifiedMemory(); | ||
| 236 | |||
| 234 | // Upload all guest vertex arrays sequentially to our buffer | 237 | // Upload all guest vertex arrays sequentially to our buffer |
| 235 | const auto& regs = gpu.regs; | 238 | const auto& regs = gpu.regs; |
| 236 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | 239 | for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_BINDINGS; ++index) { |
| 237 | if (!flags[Dirty::VertexBuffer0 + index]) { | 240 | if (!flags[Dirty::VertexBuffer0 + index]) { |
| 238 | continue; | 241 | continue; |
| 239 | } | 242 | } |
| @@ -246,16 +249,25 @@ void RasterizerOpenGL::SetupVertexBuffer() { | |||
| 246 | 249 | ||
| 247 | const GPUVAddr start = vertex_array.StartAddress(); | 250 | const GPUVAddr start = vertex_array.StartAddress(); |
| 248 | const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); | 251 | const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); |
| 249 | |||
| 250 | ASSERT(end >= start); | 252 | ASSERT(end >= start); |
| 253 | |||
| 254 | const GLuint gl_index = static_cast<GLuint>(index); | ||
| 251 | const u64 size = end - start; | 255 | const u64 size = end - start; |
| 252 | if (size == 0) { | 256 | if (size == 0) { |
| 253 | glBindVertexBuffer(static_cast<GLuint>(index), 0, 0, vertex_array.stride); | 257 | glBindVertexBuffer(gl_index, 0, 0, vertex_array.stride); |
| 258 | if (use_unified_memory) { | ||
| 259 | glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, gl_index, 0, 0); | ||
| 260 | } | ||
| 254 | continue; | 261 | continue; |
| 255 | } | 262 | } |
| 256 | const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size); | 263 | const auto info = buffer_cache.UploadMemory(start, size); |
| 257 | glBindVertexBuffer(static_cast<GLuint>(index), vertex_buffer, vertex_buffer_offset, | 264 | if (use_unified_memory) { |
| 258 | vertex_array.stride); | 265 | glBindVertexBuffer(gl_index, 0, 0, vertex_array.stride); |
| 266 | glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, gl_index, | ||
| 267 | info.address + info.offset, size); | ||
| 268 | } else { | ||
| 269 | glBindVertexBuffer(gl_index, info.handle, info.offset, vertex_array.stride); | ||
| 270 | } | ||
| 259 | } | 271 | } |
| 260 | } | 272 | } |
| 261 | 273 | ||
| @@ -268,7 +280,7 @@ void RasterizerOpenGL::SetupVertexInstances() { | |||
| 268 | flags[Dirty::VertexInstances] = false; | 280 | flags[Dirty::VertexInstances] = false; |
| 269 | 281 | ||
| 270 | const auto& regs = gpu.regs; | 282 | const auto& regs = gpu.regs; |
| 271 | for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) { | 283 | for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) { |
| 272 | if (!flags[Dirty::VertexInstance0 + index]) { | 284 | if (!flags[Dirty::VertexInstance0 + index]) { |
| 273 | continue; | 285 | continue; |
| 274 | } | 286 | } |
| @@ -285,9 +297,9 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() { | |||
| 285 | MICROPROFILE_SCOPE(OpenGL_Index); | 297 | MICROPROFILE_SCOPE(OpenGL_Index); |
| 286 | const auto& regs = system.GPU().Maxwell3D().regs; | 298 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 287 | const std::size_t size = CalculateIndexBufferSize(); | 299 | const std::size_t size = CalculateIndexBufferSize(); |
| 288 | const auto [buffer, offset] = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); | 300 | const auto info = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); |
| 289 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); | 301 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, info.handle); |
| 290 | return offset; | 302 | return info.offset; |
| 291 | } | 303 | } |
| 292 | 304 | ||
| 293 | void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | 305 | void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { |
| @@ -643,9 +655,9 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | |||
| 643 | if (!device.UseAssemblyShaders()) { | 655 | if (!device.UseAssemblyShaders()) { |
| 644 | MaxwellUniformData ubo; | 656 | MaxwellUniformData ubo; |
| 645 | ubo.SetFromRegs(gpu); | 657 | ubo.SetFromRegs(gpu); |
| 646 | const auto [buffer, offset] = | 658 | const auto info = |
| 647 | buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment()); | 659 | buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment()); |
| 648 | glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, buffer, offset, | 660 | glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, info.handle, info.offset, |
| 649 | static_cast<GLsizeiptr>(sizeof(ubo))); | 661 | static_cast<GLsizeiptr>(sizeof(ubo))); |
| 650 | } | 662 | } |
| 651 | 663 | ||
| @@ -956,8 +968,7 @@ void RasterizerOpenGL::SetupConstBuffer(GLenum stage, u32 binding, | |||
| 956 | if (device.UseAssemblyShaders()) { | 968 | if (device.UseAssemblyShaders()) { |
| 957 | glBindBufferRangeNV(stage, entry.GetIndex(), 0, 0, 0); | 969 | glBindBufferRangeNV(stage, entry.GetIndex(), 0, 0, 0); |
| 958 | } else { | 970 | } else { |
| 959 | glBindBufferRange(GL_UNIFORM_BUFFER, binding, | 971 | glBindBufferRange(GL_UNIFORM_BUFFER, binding, 0, 0, sizeof(float)); |
| 960 | buffer_cache.GetEmptyBuffer(sizeof(float)), 0, sizeof(float)); | ||
| 961 | } | 972 | } |
| 962 | return; | 973 | return; |
| 963 | } | 974 | } |
| @@ -970,24 +981,25 @@ void RasterizerOpenGL::SetupConstBuffer(GLenum stage, u32 binding, | |||
| 970 | 981 | ||
| 971 | const std::size_t alignment = use_unified ? 4 : device.GetUniformBufferAlignment(); | 982 | const std::size_t alignment = use_unified ? 4 : device.GetUniformBufferAlignment(); |
| 972 | const GPUVAddr gpu_addr = buffer.address; | 983 | const GPUVAddr gpu_addr = buffer.address; |
| 973 | auto [cbuf, offset] = buffer_cache.UploadMemory(gpu_addr, size, alignment, false, fast_upload); | 984 | auto info = buffer_cache.UploadMemory(gpu_addr, size, alignment, false, fast_upload); |
| 974 | 985 | ||
| 975 | if (device.UseAssemblyShaders()) { | 986 | if (device.UseAssemblyShaders()) { |
| 976 | UNIMPLEMENTED_IF(use_unified); | 987 | UNIMPLEMENTED_IF(use_unified); |
| 977 | if (offset != 0) { | 988 | if (info.offset != 0) { |
| 978 | const GLuint staging_cbuf = staging_cbufs[current_cbuf++]; | 989 | const GLuint staging_cbuf = staging_cbufs[current_cbuf++]; |
| 979 | glCopyNamedBufferSubData(cbuf, staging_cbuf, offset, 0, size); | 990 | glCopyNamedBufferSubData(info.handle, staging_cbuf, info.offset, 0, size); |
| 980 | cbuf = staging_cbuf; | 991 | info.handle = staging_cbuf; |
| 981 | offset = 0; | 992 | info.offset = 0; |
| 982 | } | 993 | } |
| 983 | glBindBufferRangeNV(stage, binding, cbuf, offset, size); | 994 | glBindBufferRangeNV(stage, binding, info.handle, info.offset, size); |
| 984 | return; | 995 | return; |
| 985 | } | 996 | } |
| 986 | 997 | ||
| 987 | if (use_unified) { | 998 | if (use_unified) { |
| 988 | glCopyNamedBufferSubData(cbuf, unified_uniform_buffer.handle, offset, unified_offset, size); | 999 | glCopyNamedBufferSubData(info.handle, unified_uniform_buffer.handle, info.offset, |
| 1000 | unified_offset, size); | ||
| 989 | } else { | 1001 | } else { |
| 990 | glBindBufferRange(GL_UNIFORM_BUFFER, binding, cbuf, offset, size); | 1002 | glBindBufferRange(GL_UNIFORM_BUFFER, binding, info.handle, info.offset, size); |
| 991 | } | 1003 | } |
| 992 | } | 1004 | } |
| 993 | 1005 | ||
| @@ -1023,9 +1035,8 @@ void RasterizerOpenGL::SetupComputeGlobalMemory(Shader* kernel) { | |||
| 1023 | void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, | 1035 | void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, |
| 1024 | GPUVAddr gpu_addr, std::size_t size) { | 1036 | GPUVAddr gpu_addr, std::size_t size) { |
| 1025 | const auto alignment{device.GetShaderStorageBufferAlignment()}; | 1037 | const auto alignment{device.GetShaderStorageBufferAlignment()}; |
| 1026 | const auto [ssbo, buffer_offset] = | 1038 | const auto info = buffer_cache.UploadMemory(gpu_addr, size, alignment, entry.is_written); |
| 1027 | buffer_cache.UploadMemory(gpu_addr, size, alignment, entry.is_written); | 1039 | glBindBufferRange(GL_SHADER_STORAGE_BUFFER, binding, info.handle, info.offset, |
| 1028 | glBindBufferRange(GL_SHADER_STORAGE_BUFFER, binding, ssbo, buffer_offset, | ||
| 1029 | static_cast<GLsizeiptr>(size)); | 1040 | static_cast<GLsizeiptr>(size)); |
| 1030 | } | 1041 | } |
| 1031 | 1042 | ||
| @@ -1712,8 +1723,9 @@ void RasterizerOpenGL::EndTransformFeedback() { | |||
| 1712 | const GLuint handle = transform_feedback_buffers[index].handle; | 1723 | const GLuint handle = transform_feedback_buffers[index].handle; |
| 1713 | const GPUVAddr gpu_addr = binding.Address(); | 1724 | const GPUVAddr gpu_addr = binding.Address(); |
| 1714 | const std::size_t size = binding.buffer_size; | 1725 | const std::size_t size = binding.buffer_size; |
| 1715 | const auto [dest_buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true); | 1726 | const auto info = buffer_cache.UploadMemory(gpu_addr, size, 4, true); |
| 1716 | glCopyNamedBufferSubData(handle, dest_buffer, 0, offset, static_cast<GLsizeiptr>(size)); | 1727 | glCopyNamedBufferSubData(handle, info.handle, 0, info.offset, |
| 1728 | static_cast<GLsizeiptr>(size)); | ||
| 1717 | } | 1729 | } |
| 1718 | } | 1730 | } |
| 1719 | 1731 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 46e780a06..c6a3bf3a1 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -460,8 +460,9 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 460 | const u8* host_ptr_b = memory_manager.GetPointer(address_b); | 460 | const u8* host_ptr_b = memory_manager.GetPointer(address_b); |
| 461 | code_b = GetShaderCode(memory_manager, address_b, host_ptr_b, false); | 461 | code_b = GetShaderCode(memory_manager, address_b, host_ptr_b, false); |
| 462 | } | 462 | } |
| 463 | const std::size_t code_size = code.size() * sizeof(u64); | ||
| 463 | 464 | ||
| 464 | const auto unique_identifier = GetUniqueIdentifier( | 465 | const u64 unique_identifier = GetUniqueIdentifier( |
| 465 | GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b); | 466 | GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b); |
| 466 | 467 | ||
| 467 | const ShaderParameters params{system, disk_cache, device, | 468 | const ShaderParameters params{system, disk_cache, device, |
| @@ -477,7 +478,7 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 477 | 478 | ||
| 478 | Shader* const result = shader.get(); | 479 | Shader* const result = shader.get(); |
| 479 | if (cpu_addr) { | 480 | if (cpu_addr) { |
| 480 | Register(std::move(shader), *cpu_addr, code.size() * sizeof(u64)); | 481 | Register(std::move(shader), *cpu_addr, code_size); |
| 481 | } else { | 482 | } else { |
| 482 | null_shader = std::move(shader); | 483 | null_shader = std::move(shader); |
| 483 | } | 484 | } |
| @@ -495,8 +496,9 @@ Shader* ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { | |||
| 495 | 496 | ||
| 496 | const auto host_ptr{memory_manager.GetPointer(code_addr)}; | 497 | const auto host_ptr{memory_manager.GetPointer(code_addr)}; |
| 497 | // No kernel found, create a new one | 498 | // No kernel found, create a new one |
| 498 | auto code{GetShaderCode(memory_manager, code_addr, host_ptr, true)}; | 499 | ProgramCode code{GetShaderCode(memory_manager, code_addr, host_ptr, true)}; |
| 499 | const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)}; | 500 | const std::size_t code_size{code.size() * sizeof(u64)}; |
| 501 | const u64 unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)}; | ||
| 500 | 502 | ||
| 501 | const ShaderParameters params{system, disk_cache, device, | 503 | const ShaderParameters params{system, disk_cache, device, |
| 502 | *cpu_addr, host_ptr, unique_identifier}; | 504 | *cpu_addr, host_ptr, unique_identifier}; |
| @@ -511,7 +513,7 @@ Shader* ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { | |||
| 511 | 513 | ||
| 512 | Shader* const result = kernel.get(); | 514 | Shader* const result = kernel.get(); |
| 513 | if (cpu_addr) { | 515 | if (cpu_addr) { |
| 514 | Register(std::move(kernel), *cpu_addr, code.size() * sizeof(u64)); | 516 | Register(std::move(kernel), *cpu_addr, code_size); |
| 515 | } else { | 517 | } else { |
| 516 | null_kernel = std::move(kernel); | 518 | null_kernel = std::move(kernel); |
| 517 | } | 519 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 6848f1388..994aaeaf2 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -37,7 +37,6 @@ namespace OpenGL { | |||
| 37 | 37 | ||
| 38 | class Device; | 38 | class Device; |
| 39 | class RasterizerOpenGL; | 39 | class RasterizerOpenGL; |
| 40 | struct UnspecializedShader; | ||
| 41 | 40 | ||
| 42 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 41 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 43 | 42 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index d6e30b321..2c49aeaac 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -37,6 +37,7 @@ using Tegra::Shader::IpaMode; | |||
| 37 | using Tegra::Shader::IpaSampleMode; | 37 | using Tegra::Shader::IpaSampleMode; |
| 38 | using Tegra::Shader::PixelImap; | 38 | using Tegra::Shader::PixelImap; |
| 39 | using Tegra::Shader::Register; | 39 | using Tegra::Shader::Register; |
| 40 | using Tegra::Shader::TextureType; | ||
| 40 | using VideoCommon::Shader::BuildTransformFeedback; | 41 | using VideoCommon::Shader::BuildTransformFeedback; |
| 41 | using VideoCommon::Shader::Registry; | 42 | using VideoCommon::Shader::Registry; |
| 42 | 43 | ||
| @@ -526,6 +527,9 @@ private: | |||
| 526 | if (device.HasImageLoadFormatted()) { | 527 | if (device.HasImageLoadFormatted()) { |
| 527 | code.AddLine("#extension GL_EXT_shader_image_load_formatted : require"); | 528 | code.AddLine("#extension GL_EXT_shader_image_load_formatted : require"); |
| 528 | } | 529 | } |
| 530 | if (device.HasTextureShadowLod()) { | ||
| 531 | code.AddLine("#extension GL_EXT_texture_shadow_lod : require"); | ||
| 532 | } | ||
| 529 | if (device.HasWarpIntrinsics()) { | 533 | if (device.HasWarpIntrinsics()) { |
| 530 | code.AddLine("#extension GL_NV_gpu_shader5 : require"); | 534 | code.AddLine("#extension GL_NV_gpu_shader5 : require"); |
| 531 | code.AddLine("#extension GL_NV_shader_thread_group : require"); | 535 | code.AddLine("#extension GL_NV_shader_thread_group : require"); |
| @@ -909,13 +913,13 @@ private: | |||
| 909 | return "samplerBuffer"; | 913 | return "samplerBuffer"; |
| 910 | } | 914 | } |
| 911 | switch (sampler.type) { | 915 | switch (sampler.type) { |
| 912 | case Tegra::Shader::TextureType::Texture1D: | 916 | case TextureType::Texture1D: |
| 913 | return "sampler1D"; | 917 | return "sampler1D"; |
| 914 | case Tegra::Shader::TextureType::Texture2D: | 918 | case TextureType::Texture2D: |
| 915 | return "sampler2D"; | 919 | return "sampler2D"; |
| 916 | case Tegra::Shader::TextureType::Texture3D: | 920 | case TextureType::Texture3D: |
| 917 | return "sampler3D"; | 921 | return "sampler3D"; |
| 918 | case Tegra::Shader::TextureType::TextureCube: | 922 | case TextureType::TextureCube: |
| 919 | return "samplerCube"; | 923 | return "samplerCube"; |
| 920 | default: | 924 | default: |
| 921 | UNREACHABLE(); | 925 | UNREACHABLE(); |
| @@ -1380,8 +1384,19 @@ private: | |||
| 1380 | const std::size_t count = operation.GetOperandsCount(); | 1384 | const std::size_t count = operation.GetOperandsCount(); |
| 1381 | const bool has_array = meta->sampler.is_array; | 1385 | const bool has_array = meta->sampler.is_array; |
| 1382 | const bool has_shadow = meta->sampler.is_shadow; | 1386 | const bool has_shadow = meta->sampler.is_shadow; |
| 1387 | const bool workaround_lod_array_shadow_as_grad = | ||
| 1388 | !device.HasTextureShadowLod() && function_suffix == "Lod" && meta->sampler.is_shadow && | ||
| 1389 | ((meta->sampler.type == TextureType::Texture2D && meta->sampler.is_array) || | ||
| 1390 | meta->sampler.type == TextureType::TextureCube); | ||
| 1391 | |||
| 1392 | std::string expr = "texture"; | ||
| 1393 | |||
| 1394 | if (workaround_lod_array_shadow_as_grad) { | ||
| 1395 | expr += "Grad"; | ||
| 1396 | } else { | ||
| 1397 | expr += function_suffix; | ||
| 1398 | } | ||
| 1383 | 1399 | ||
| 1384 | std::string expr = "texture" + function_suffix; | ||
| 1385 | if (!meta->aoffi.empty()) { | 1400 | if (!meta->aoffi.empty()) { |
| 1386 | expr += "Offset"; | 1401 | expr += "Offset"; |
| 1387 | } else if (!meta->ptp.empty()) { | 1402 | } else if (!meta->ptp.empty()) { |
| @@ -1415,6 +1430,16 @@ private: | |||
| 1415 | expr += ')'; | 1430 | expr += ')'; |
| 1416 | } | 1431 | } |
| 1417 | 1432 | ||
| 1433 | if (workaround_lod_array_shadow_as_grad) { | ||
| 1434 | switch (meta->sampler.type) { | ||
| 1435 | case TextureType::Texture2D: | ||
| 1436 | return expr + ", vec2(0.0), vec2(0.0))"; | ||
| 1437 | case TextureType::TextureCube: | ||
| 1438 | return expr + ", vec3(0.0), vec3(0.0))"; | ||
| 1439 | } | ||
| 1440 | UNREACHABLE(); | ||
| 1441 | } | ||
| 1442 | |||
| 1418 | for (const auto& variant : extras) { | 1443 | for (const auto& variant : extras) { |
| 1419 | if (const auto argument = std::get_if<TextureArgument>(&variant)) { | 1444 | if (const auto argument = std::get_if<TextureArgument>(&variant)) { |
| 1420 | expr += GenerateTextureArgument(*argument); | 1445 | expr += GenerateTextureArgument(*argument); |
| @@ -2041,8 +2066,19 @@ private: | |||
| 2041 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); | 2066 | const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); |
| 2042 | ASSERT(meta); | 2067 | ASSERT(meta); |
| 2043 | 2068 | ||
| 2044 | std::string expr = GenerateTexture( | 2069 | std::string expr{}; |
| 2045 | operation, "Lod", {TextureArgument{Type::Float, meta->lod}, TextureOffset{}}); | 2070 | |
| 2071 | if (!device.HasTextureShadowLod() && meta->sampler.is_shadow && | ||
| 2072 | ((meta->sampler.type == TextureType::Texture2D && meta->sampler.is_array) || | ||
| 2073 | meta->sampler.type == TextureType::TextureCube)) { | ||
| 2074 | LOG_ERROR(Render_OpenGL, | ||
| 2075 | "Device lacks GL_EXT_texture_shadow_lod, using textureGrad as a workaround"); | ||
| 2076 | expr = GenerateTexture(operation, "Lod", {}); | ||
| 2077 | } else { | ||
| 2078 | expr = GenerateTexture(operation, "Lod", | ||
| 2079 | {TextureArgument{Type::Float, meta->lod}, TextureOffset{}}); | ||
| 2080 | } | ||
| 2081 | |||
| 2046 | if (meta->sampler.is_shadow) { | 2082 | if (meta->sampler.is_shadow) { |
| 2047 | expr = "vec4(" + expr + ')'; | 2083 | expr = "vec4(" + expr + ')'; |
| 2048 | } | 2084 | } |
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index 932a2f69e..3655ff629 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp | |||
| @@ -2,11 +2,13 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <deque> | 5 | #include <tuple> |
| 6 | #include <vector> | 6 | #include <vector> |
| 7 | |||
| 7 | #include "common/alignment.h" | 8 | #include "common/alignment.h" |
| 8 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 9 | #include "common/microprofile.h" | 10 | #include "common/microprofile.h" |
| 11 | #include "video_core/renderer_opengl/gl_device.h" | ||
| 10 | #include "video_core/renderer_opengl/gl_stream_buffer.h" | 12 | #include "video_core/renderer_opengl/gl_stream_buffer.h" |
| 11 | 13 | ||
| 12 | MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", | 14 | MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", |
| @@ -14,8 +16,7 @@ MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", | |||
| 14 | 16 | ||
| 15 | namespace OpenGL { | 17 | namespace OpenGL { |
| 16 | 18 | ||
| 17 | OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent, | 19 | OGLStreamBuffer::OGLStreamBuffer(const Device& device, GLsizeiptr size, bool vertex_data_usage) |
| 18 | bool use_persistent) | ||
| 19 | : buffer_size(size) { | 20 | : buffer_size(size) { |
| 20 | gl_buffer.Create(); | 21 | gl_buffer.Create(); |
| 21 | 22 | ||
| @@ -29,23 +30,19 @@ OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool p | |||
| 29 | allocate_size *= 2; | 30 | allocate_size *= 2; |
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | if (use_persistent) { | 33 | static constexpr GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT; |
| 33 | persistent = true; | 34 | glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags); |
| 34 | coherent = prefer_coherent; | 35 | mapped_ptr = static_cast<u8*>( |
| 35 | const GLbitfield flags = | 36 | glMapNamedBufferRange(gl_buffer.handle, 0, buffer_size, flags | GL_MAP_FLUSH_EXPLICIT_BIT)); |
| 36 | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); | 37 | |
| 37 | glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags); | 38 | if (device.HasVertexBufferUnifiedMemory()) { |
| 38 | mapped_ptr = static_cast<u8*>(glMapNamedBufferRange( | 39 | glMakeNamedBufferResidentNV(gl_buffer.handle, GL_READ_ONLY); |
| 39 | gl_buffer.handle, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT))); | 40 | glGetNamedBufferParameterui64vNV(gl_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &gpu_address); |
| 40 | } else { | ||
| 41 | glNamedBufferData(gl_buffer.handle, allocate_size, nullptr, GL_STREAM_DRAW); | ||
| 42 | } | 41 | } |
| 43 | } | 42 | } |
| 44 | 43 | ||
| 45 | OGLStreamBuffer::~OGLStreamBuffer() { | 44 | OGLStreamBuffer::~OGLStreamBuffer() { |
| 46 | if (persistent) { | 45 | glUnmapNamedBuffer(gl_buffer.handle); |
| 47 | glUnmapNamedBuffer(gl_buffer.handle); | ||
| 48 | } | ||
| 49 | gl_buffer.Release(); | 46 | gl_buffer.Release(); |
| 50 | } | 47 | } |
| 51 | 48 | ||
| @@ -60,36 +57,21 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a | |||
| 60 | 57 | ||
| 61 | bool invalidate = false; | 58 | bool invalidate = false; |
| 62 | if (buffer_pos + size > buffer_size) { | 59 | if (buffer_pos + size > buffer_size) { |
| 60 | MICROPROFILE_SCOPE(OpenGL_StreamBuffer); | ||
| 61 | glInvalidateBufferData(gl_buffer.handle); | ||
| 62 | |||
| 63 | buffer_pos = 0; | 63 | buffer_pos = 0; |
| 64 | invalidate = true; | 64 | invalidate = true; |
| 65 | |||
| 66 | if (persistent) { | ||
| 67 | glUnmapNamedBuffer(gl_buffer.handle); | ||
| 68 | } | ||
| 69 | } | 65 | } |
| 70 | 66 | ||
| 71 | if (invalidate || !persistent) { | 67 | return std::make_tuple(mapped_ptr + buffer_pos, buffer_pos, invalidate); |
| 72 | MICROPROFILE_SCOPE(OpenGL_StreamBuffer); | ||
| 73 | GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) | | ||
| 74 | (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) | | ||
| 75 | (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT); | ||
| 76 | mapped_ptr = static_cast<u8*>( | ||
| 77 | glMapNamedBufferRange(gl_buffer.handle, buffer_pos, buffer_size - buffer_pos, flags)); | ||
| 78 | mapped_offset = buffer_pos; | ||
| 79 | } | ||
| 80 | |||
| 81 | return std::make_tuple(mapped_ptr + buffer_pos - mapped_offset, buffer_pos, invalidate); | ||
| 82 | } | 68 | } |
| 83 | 69 | ||
| 84 | void OGLStreamBuffer::Unmap(GLsizeiptr size) { | 70 | void OGLStreamBuffer::Unmap(GLsizeiptr size) { |
| 85 | ASSERT(size <= mapped_size); | 71 | ASSERT(size <= mapped_size); |
| 86 | 72 | ||
| 87 | if (!coherent && size > 0) { | 73 | if (size > 0) { |
| 88 | glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos - mapped_offset, size); | 74 | glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos, size); |
| 89 | } | ||
| 90 | |||
| 91 | if (!persistent) { | ||
| 92 | glUnmapNamedBuffer(gl_buffer.handle); | ||
| 93 | } | 75 | } |
| 94 | 76 | ||
| 95 | buffer_pos += size; | 77 | buffer_pos += size; |
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h index 866da3594..307a67113 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.h +++ b/src/video_core/renderer_opengl/gl_stream_buffer.h | |||
| @@ -11,10 +11,11 @@ | |||
| 11 | 11 | ||
| 12 | namespace OpenGL { | 12 | namespace OpenGL { |
| 13 | 13 | ||
| 14 | class Device; | ||
| 15 | |||
| 14 | class OGLStreamBuffer : private NonCopyable { | 16 | class OGLStreamBuffer : private NonCopyable { |
| 15 | public: | 17 | public: |
| 16 | explicit OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent = false, | 18 | explicit OGLStreamBuffer(const Device& device, GLsizeiptr size, bool vertex_data_usage); |
| 17 | bool use_persistent = true); | ||
| 18 | ~OGLStreamBuffer(); | 19 | ~OGLStreamBuffer(); |
| 19 | 20 | ||
| 20 | /* | 21 | /* |
| @@ -33,19 +34,20 @@ public: | |||
| 33 | return gl_buffer.handle; | 34 | return gl_buffer.handle; |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | GLsizeiptr Size() const { | 37 | u64 Address() const { |
| 38 | return gpu_address; | ||
| 39 | } | ||
| 40 | |||
| 41 | GLsizeiptr Size() const noexcept { | ||
| 37 | return buffer_size; | 42 | return buffer_size; |
| 38 | } | 43 | } |
| 39 | 44 | ||
| 40 | private: | 45 | private: |
| 41 | OGLBuffer gl_buffer; | 46 | OGLBuffer gl_buffer; |
| 42 | 47 | ||
| 43 | bool coherent = false; | 48 | GLuint64EXT gpu_address = 0; |
| 44 | bool persistent = false; | ||
| 45 | |||
| 46 | GLintptr buffer_pos = 0; | 49 | GLintptr buffer_pos = 0; |
| 47 | GLsizeiptr buffer_size = 0; | 50 | GLsizeiptr buffer_size = 0; |
| 48 | GLintptr mapped_offset = 0; | ||
| 49 | GLsizeiptr mapped_size = 0; | 51 | GLsizeiptr mapped_size = 0; |
| 50 | u8* mapped_ptr = nullptr; | 52 | u8* mapped_ptr = nullptr; |
| 51 | }; | 53 | }; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 6214fcbc3..c40adb6e7 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -488,6 +488,15 @@ void RendererOpenGL::InitOpenGLObjects() { | |||
| 488 | 488 | ||
| 489 | // Clear screen to black | 489 | // Clear screen to black |
| 490 | LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); | 490 | LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); |
| 491 | |||
| 492 | // Enable unified vertex attributes and query vertex buffer address when the driver supports it | ||
| 493 | if (device.HasVertexBufferUnifiedMemory()) { | ||
| 494 | glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); | ||
| 495 | |||
| 496 | glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); | ||
| 497 | glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, | ||
| 498 | &vertex_buffer_address); | ||
| 499 | } | ||
| 491 | } | 500 | } |
| 492 | 501 | ||
| 493 | void RendererOpenGL::AddTelemetryFields() { | 502 | void RendererOpenGL::AddTelemetryFields() { |
| @@ -656,7 +665,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | |||
| 656 | offsetof(ScreenRectVertex, tex_coord)); | 665 | offsetof(ScreenRectVertex, tex_coord)); |
| 657 | glVertexAttribBinding(PositionLocation, 0); | 666 | glVertexAttribBinding(PositionLocation, 0); |
| 658 | glVertexAttribBinding(TexCoordLocation, 0); | 667 | glVertexAttribBinding(TexCoordLocation, 0); |
| 659 | glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); | 668 | if (device.HasVertexBufferUnifiedMemory()) { |
| 669 | glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); | ||
| 670 | glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, | ||
| 671 | sizeof(vertices)); | ||
| 672 | } else { | ||
| 673 | glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); | ||
| 674 | } | ||
| 660 | 675 | ||
| 661 | glBindTextureUnit(0, screen_info.display_texture); | 676 | glBindTextureUnit(0, screen_info.display_texture); |
| 662 | glBindSampler(0, 0); | 677 | glBindSampler(0, 0); |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 61bf507f4..8b18d32e6 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -107,6 +107,9 @@ private: | |||
| 107 | OGLPipeline pipeline; | 107 | OGLPipeline pipeline; |
| 108 | OGLFramebuffer screenshot_framebuffer; | 108 | OGLFramebuffer screenshot_framebuffer; |
| 109 | 109 | ||
| 110 | // GPU address of the vertex buffer | ||
| 111 | GLuint64EXT vertex_buffer_address = 0; | ||
| 112 | |||
| 110 | /// Display information for Switch screen | 113 | /// Display information for Switch screen |
| 111 | ScreenInfo screen_info; | 114 | ScreenInfo screen_info; |
| 112 | 115 | ||
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 1fde38328..f10f96cd8 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -37,9 +37,9 @@ std::unique_ptr<VKStreamBuffer> CreateStreamBuffer(const VKDevice& device, VKSch | |||
| 37 | 37 | ||
| 38 | } // Anonymous namespace | 38 | } // Anonymous namespace |
| 39 | 39 | ||
| 40 | Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VAddr cpu_addr, | 40 | Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler_, |
| 41 | std::size_t size) | 41 | VKStagingBufferPool& staging_pool_, VAddr cpu_addr, std::size_t size) |
| 42 | : VideoCommon::BufferBlock{cpu_addr, size} { | 42 | : VideoCommon::BufferBlock{cpu_addr, size}, scheduler{scheduler_}, staging_pool{staging_pool_} { |
| 43 | VkBufferCreateInfo ci; | 43 | VkBufferCreateInfo ci; |
| 44 | ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; | 44 | ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
| 45 | ci.pNext = nullptr; | 45 | ci.pNext = nullptr; |
| @@ -56,40 +56,15 @@ Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VAddr cp | |||
| 56 | 56 | ||
| 57 | Buffer::~Buffer() = default; | 57 | Buffer::~Buffer() = default; |
| 58 | 58 | ||
| 59 | VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, | 59 | void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { |
| 60 | const VKDevice& device, VKMemoryManager& memory_manager, | ||
| 61 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool) | ||
| 62 | : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer, system, | ||
| 63 | CreateStreamBuffer(device, | ||
| 64 | scheduler)}, | ||
| 65 | device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{ | ||
| 66 | staging_pool} {} | ||
| 67 | |||
| 68 | VKBufferCache::~VKBufferCache() = default; | ||
| 69 | |||
| 70 | std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { | ||
| 71 | return std::make_shared<Buffer>(device, memory_manager, cpu_addr, size); | ||
| 72 | } | ||
| 73 | |||
| 74 | VkBuffer VKBufferCache::GetEmptyBuffer(std::size_t size) { | ||
| 75 | size = std::max(size, std::size_t(4)); | ||
| 76 | const auto& empty = staging_pool.GetUnusedBuffer(size, false); | ||
| 77 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 78 | scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) { | ||
| 79 | cmdbuf.FillBuffer(buffer, 0, size, 0); | ||
| 80 | }); | ||
| 81 | return *empty.handle; | ||
| 82 | } | ||
| 83 | |||
| 84 | void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 85 | const u8* data) { | ||
| 86 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); | 60 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); |
| 87 | std::memcpy(staging.commit->Map(size), data, size); | 61 | std::memcpy(staging.commit->Map(size), data, size); |
| 88 | 62 | ||
| 89 | scheduler.RequestOutsideRenderPassOperationContext(); | 63 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 90 | scheduler.Record([staging = *staging.handle, buffer = buffer.Handle(), offset, | 64 | |
| 91 | size](vk::CommandBuffer cmdbuf) { | 65 | const VkBuffer handle = Handle(); |
| 92 | cmdbuf.CopyBuffer(staging, buffer, VkBufferCopy{0, offset, size}); | 66 | scheduler.Record([staging = *staging.handle, handle, offset, size](vk::CommandBuffer cmdbuf) { |
| 67 | cmdbuf.CopyBuffer(staging, handle, VkBufferCopy{0, offset, size}); | ||
| 93 | 68 | ||
| 94 | VkBufferMemoryBarrier barrier; | 69 | VkBufferMemoryBarrier barrier; |
| 95 | barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; | 70 | barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; |
| @@ -98,7 +73,7 @@ void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, st | |||
| 98 | barrier.dstAccessMask = UPLOAD_ACCESS_BARRIERS; | 73 | barrier.dstAccessMask = UPLOAD_ACCESS_BARRIERS; |
| 99 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | 74 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| 100 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | 75 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| 101 | barrier.buffer = buffer; | 76 | barrier.buffer = handle; |
| 102 | barrier.offset = offset; | 77 | barrier.offset = offset; |
| 103 | barrier.size = size; | 78 | barrier.size = size; |
| 104 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, UPLOAD_PIPELINE_STAGE, 0, {}, | 79 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, UPLOAD_PIPELINE_STAGE, 0, {}, |
| @@ -106,12 +81,12 @@ void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, st | |||
| 106 | }); | 81 | }); |
| 107 | } | 82 | } |
| 108 | 83 | ||
| 109 | void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | 84 | void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { |
| 110 | u8* data) { | ||
| 111 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); | 85 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); |
| 112 | scheduler.RequestOutsideRenderPassOperationContext(); | 86 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 113 | scheduler.Record([staging = *staging.handle, buffer = buffer.Handle(), offset, | 87 | |
| 114 | size](vk::CommandBuffer cmdbuf) { | 88 | const VkBuffer handle = Handle(); |
| 89 | scheduler.Record([staging = *staging.handle, handle, offset, size](vk::CommandBuffer cmdbuf) { | ||
| 115 | VkBufferMemoryBarrier barrier; | 90 | VkBufferMemoryBarrier barrier; |
| 116 | barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; | 91 | barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; |
| 117 | barrier.pNext = nullptr; | 92 | barrier.pNext = nullptr; |
| @@ -119,7 +94,7 @@ void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, | |||
| 119 | barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; | 94 | barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; |
| 120 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | 95 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| 121 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | 96 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| 122 | barrier.buffer = buffer; | 97 | barrier.buffer = handle; |
| 123 | barrier.offset = offset; | 98 | barrier.offset = offset; |
| 124 | barrier.size = size; | 99 | barrier.size = size; |
| 125 | 100 | ||
| @@ -127,17 +102,19 @@ void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, | |||
| 127 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | | 102 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | |
| 128 | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | 103 | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, |
| 129 | VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, barrier, {}); | 104 | VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, barrier, {}); |
| 130 | cmdbuf.CopyBuffer(buffer, staging, VkBufferCopy{offset, 0, size}); | 105 | cmdbuf.CopyBuffer(handle, staging, VkBufferCopy{offset, 0, size}); |
| 131 | }); | 106 | }); |
| 132 | scheduler.Finish(); | 107 | scheduler.Finish(); |
| 133 | 108 | ||
| 134 | std::memcpy(data, staging.commit->Map(size), size); | 109 | std::memcpy(data, staging.commit->Map(size), size); |
| 135 | } | 110 | } |
| 136 | 111 | ||
| 137 | void VKBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, | 112 | void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, |
| 138 | std::size_t dst_offset, std::size_t size) { | 113 | std::size_t size) const { |
| 139 | scheduler.RequestOutsideRenderPassOperationContext(); | 114 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 140 | scheduler.Record([src_buffer = src.Handle(), dst_buffer = dst.Handle(), src_offset, dst_offset, | 115 | |
| 116 | const VkBuffer dst_buffer = Handle(); | ||
| 117 | scheduler.Record([src_buffer = src.Handle(), dst_buffer, src_offset, dst_offset, | ||
| 141 | size](vk::CommandBuffer cmdbuf) { | 118 | size](vk::CommandBuffer cmdbuf) { |
| 142 | cmdbuf.CopyBuffer(src_buffer, dst_buffer, VkBufferCopy{src_offset, dst_offset, size}); | 119 | cmdbuf.CopyBuffer(src_buffer, dst_buffer, VkBufferCopy{src_offset, dst_offset, size}); |
| 143 | 120 | ||
| @@ -165,4 +142,30 @@ void VKBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t | |||
| 165 | }); | 142 | }); |
| 166 | } | 143 | } |
| 167 | 144 | ||
| 145 | VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, | ||
| 146 | const VKDevice& device, VKMemoryManager& memory_manager, | ||
| 147 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool) | ||
| 148 | : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer, system, | ||
| 149 | CreateStreamBuffer(device, | ||
| 150 | scheduler)}, | ||
| 151 | device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{ | ||
| 152 | staging_pool} {} | ||
| 153 | |||
| 154 | VKBufferCache::~VKBufferCache() = default; | ||
| 155 | |||
| 156 | std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { | ||
| 157 | return std::make_shared<Buffer>(device, memory_manager, scheduler, staging_pool, cpu_addr, | ||
| 158 | size); | ||
| 159 | } | ||
| 160 | |||
| 161 | VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) { | ||
| 162 | size = std::max(size, std::size_t(4)); | ||
| 163 | const auto& empty = staging_pool.GetUnusedBuffer(size, false); | ||
| 164 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 165 | scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) { | ||
| 166 | cmdbuf.FillBuffer(buffer, 0, size, 0); | ||
| 167 | }); | ||
| 168 | return {*empty.handle, 0, 0}; | ||
| 169 | } | ||
| 170 | |||
| 168 | } // namespace Vulkan | 171 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 9ebbef835..3630aca77 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -25,15 +25,29 @@ class VKScheduler; | |||
| 25 | 25 | ||
| 26 | class Buffer final : public VideoCommon::BufferBlock { | 26 | class Buffer final : public VideoCommon::BufferBlock { |
| 27 | public: | 27 | public: |
| 28 | explicit Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VAddr cpu_addr, | 28 | explicit Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler, |
| 29 | std::size_t size); | 29 | VKStagingBufferPool& staging_pool, VAddr cpu_addr, std::size_t size); |
| 30 | ~Buffer(); | 30 | ~Buffer(); |
| 31 | 31 | ||
| 32 | void Upload(std::size_t offset, std::size_t size, const u8* data) const; | ||
| 33 | |||
| 34 | void Download(std::size_t offset, std::size_t size, u8* data) const; | ||
| 35 | |||
| 36 | void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, | ||
| 37 | std::size_t size) const; | ||
| 38 | |||
| 32 | VkBuffer Handle() const { | 39 | VkBuffer Handle() const { |
| 33 | return *buffer.handle; | 40 | return *buffer.handle; |
| 34 | } | 41 | } |
| 35 | 42 | ||
| 43 | u64 Address() const { | ||
| 44 | return 0; | ||
| 45 | } | ||
| 46 | |||
| 36 | private: | 47 | private: |
| 48 | VKScheduler& scheduler; | ||
| 49 | VKStagingBufferPool& staging_pool; | ||
| 50 | |||
| 37 | VKBuffer buffer; | 51 | VKBuffer buffer; |
| 38 | }; | 52 | }; |
| 39 | 53 | ||
| @@ -44,20 +58,11 @@ public: | |||
| 44 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool); | 58 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool); |
| 45 | ~VKBufferCache(); | 59 | ~VKBufferCache(); |
| 46 | 60 | ||
| 47 | VkBuffer GetEmptyBuffer(std::size_t size) override; | 61 | BufferInfo GetEmptyBuffer(std::size_t size) override; |
| 48 | 62 | ||
| 49 | protected: | 63 | protected: |
| 50 | std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override; | 64 | std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override; |
| 51 | 65 | ||
| 52 | void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 53 | const u8* data) override; | ||
| 54 | |||
| 55 | void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | ||
| 56 | u8* data) override; | ||
| 57 | |||
| 58 | void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, | ||
| 59 | std::size_t dst_offset, std::size_t size) override; | ||
| 60 | |||
| 61 | private: | 66 | private: |
| 62 | const VKDevice& device; | 67 | const VKDevice& device; |
| 63 | VKMemoryManager& memory_manager; | 68 | VKMemoryManager& memory_manager; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a77fa35c3..a8d94eac3 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -143,6 +143,49 @@ Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry | |||
| 143 | } | 143 | } |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | /// @brief Determine if an attachment to be updated has to preserve contents | ||
| 147 | /// @param is_clear True when a clear is being executed | ||
| 148 | /// @param regs 3D registers | ||
| 149 | /// @return True when the contents have to be preserved | ||
| 150 | bool HasToPreserveColorContents(bool is_clear, const Maxwell& regs) { | ||
| 151 | if (!is_clear) { | ||
| 152 | return true; | ||
| 153 | } | ||
| 154 | // First we have to make sure all clear masks are enabled. | ||
| 155 | if (!regs.clear_buffers.R || !regs.clear_buffers.G || !regs.clear_buffers.B || | ||
| 156 | !regs.clear_buffers.A) { | ||
| 157 | return true; | ||
| 158 | } | ||
| 159 | // If scissors are disabled, the whole screen is cleared | ||
| 160 | if (!regs.clear_flags.scissor) { | ||
| 161 | return false; | ||
| 162 | } | ||
| 163 | // Then we have to confirm scissor testing clears the whole image | ||
| 164 | const std::size_t index = regs.clear_buffers.RT; | ||
| 165 | const auto& scissor = regs.scissor_test[0]; | ||
| 166 | return scissor.min_x > 0 || scissor.min_y > 0 || scissor.max_x < regs.rt[index].width || | ||
| 167 | scissor.max_y < regs.rt[index].height; | ||
| 168 | } | ||
| 169 | |||
| 170 | /// @brief Determine if an attachment to be updated has to preserve contents | ||
| 171 | /// @param is_clear True when a clear is being executed | ||
| 172 | /// @param regs 3D registers | ||
| 173 | /// @return True when the contents have to be preserved | ||
| 174 | bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) { | ||
| 175 | // If we are not clearing, the contents have to be preserved | ||
| 176 | if (!is_clear) { | ||
| 177 | return true; | ||
| 178 | } | ||
| 179 | // For depth stencil clears we only have to confirm scissor test covers the whole image | ||
| 180 | if (!regs.clear_flags.scissor) { | ||
| 181 | return false; | ||
| 182 | } | ||
| 183 | // Make sure the clear cover the whole image | ||
| 184 | const auto& scissor = regs.scissor_test[0]; | ||
| 185 | return scissor.min_x > 0 || scissor.min_y > 0 || scissor.max_x < regs.zeta_width || | ||
| 186 | scissor.max_y < regs.zeta_height; | ||
| 187 | } | ||
| 188 | |||
| 146 | } // Anonymous namespace | 189 | } // Anonymous namespace |
| 147 | 190 | ||
| 148 | class BufferBindings final { | 191 | class BufferBindings final { |
| @@ -344,7 +387,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 344 | 387 | ||
| 345 | buffer_cache.Unmap(); | 388 | buffer_cache.Unmap(); |
| 346 | 389 | ||
| 347 | const Texceptions texceptions = UpdateAttachments(); | 390 | const Texceptions texceptions = UpdateAttachments(false); |
| 348 | SetupImageTransitions(texceptions, color_attachments, zeta_attachment); | 391 | SetupImageTransitions(texceptions, color_attachments, zeta_attachment); |
| 349 | 392 | ||
| 350 | key.renderpass_params = GetRenderPassParams(texceptions); | 393 | key.renderpass_params = GetRenderPassParams(texceptions); |
| @@ -400,7 +443,7 @@ void RasterizerVulkan::Clear() { | |||
| 400 | return; | 443 | return; |
| 401 | } | 444 | } |
| 402 | 445 | ||
| 403 | [[maybe_unused]] const auto texceptions = UpdateAttachments(); | 446 | [[maybe_unused]] const auto texceptions = UpdateAttachments(true); |
| 404 | DEBUG_ASSERT(texceptions.none()); | 447 | DEBUG_ASSERT(texceptions.none()); |
| 405 | SetupImageTransitions(0, color_attachments, zeta_attachment); | 448 | SetupImageTransitions(0, color_attachments, zeta_attachment); |
| 406 | 449 | ||
| @@ -677,9 +720,12 @@ void RasterizerVulkan::FlushWork() { | |||
| 677 | draw_counter = 0; | 720 | draw_counter = 0; |
| 678 | } | 721 | } |
| 679 | 722 | ||
| 680 | RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { | 723 | RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments(bool is_clear) { |
| 681 | MICROPROFILE_SCOPE(Vulkan_RenderTargets); | 724 | MICROPROFILE_SCOPE(Vulkan_RenderTargets); |
| 682 | auto& dirty = system.GPU().Maxwell3D().dirty.flags; | 725 | auto& maxwell3d = system.GPU().Maxwell3D(); |
| 726 | auto& dirty = maxwell3d.dirty.flags; | ||
| 727 | auto& regs = maxwell3d.regs; | ||
| 728 | |||
| 683 | const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets]; | 729 | const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets]; |
| 684 | dirty[VideoCommon::Dirty::RenderTargets] = false; | 730 | dirty[VideoCommon::Dirty::RenderTargets] = false; |
| 685 | 731 | ||
| @@ -688,7 +734,8 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { | |||
| 688 | Texceptions texceptions; | 734 | Texceptions texceptions; |
| 689 | for (std::size_t rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { | 735 | for (std::size_t rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { |
| 690 | if (update_rendertargets) { | 736 | if (update_rendertargets) { |
| 691 | color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true); | 737 | const bool preserve_contents = HasToPreserveColorContents(is_clear, regs); |
| 738 | color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, preserve_contents); | ||
| 692 | } | 739 | } |
| 693 | if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { | 740 | if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { |
| 694 | texceptions[rt] = true; | 741 | texceptions[rt] = true; |
| @@ -696,7 +743,8 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { | |||
| 696 | } | 743 | } |
| 697 | 744 | ||
| 698 | if (update_rendertargets) { | 745 | if (update_rendertargets) { |
| 699 | zeta_attachment = texture_cache.GetDepthBufferSurface(true); | 746 | const bool preserve_contents = HasToPreserveDepthContents(is_clear, regs); |
| 747 | zeta_attachment = texture_cache.GetDepthBufferSurface(preserve_contents); | ||
| 700 | } | 748 | } |
| 701 | if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { | 749 | if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { |
| 702 | texceptions[ZETA_TEXCEPTION_INDEX] = true; | 750 | texceptions[ZETA_TEXCEPTION_INDEX] = true; |
| @@ -870,10 +918,10 @@ void RasterizerVulkan::BeginTransformFeedback() { | |||
| 870 | UNIMPLEMENTED_IF(binding.buffer_offset != 0); | 918 | UNIMPLEMENTED_IF(binding.buffer_offset != 0); |
| 871 | 919 | ||
| 872 | const GPUVAddr gpu_addr = binding.Address(); | 920 | const GPUVAddr gpu_addr = binding.Address(); |
| 873 | const auto size = static_cast<VkDeviceSize>(binding.buffer_size); | 921 | const VkDeviceSize size = static_cast<VkDeviceSize>(binding.buffer_size); |
| 874 | const auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true); | 922 | const auto info = buffer_cache.UploadMemory(gpu_addr, size, 4, true); |
| 875 | 923 | ||
| 876 | scheduler.Record([buffer = buffer, offset = offset, size](vk::CommandBuffer cmdbuf) { | 924 | scheduler.Record([buffer = info.handle, offset = info.offset, size](vk::CommandBuffer cmdbuf) { |
| 877 | cmdbuf.BindTransformFeedbackBuffersEXT(0, 1, &buffer, &offset, &size); | 925 | cmdbuf.BindTransformFeedbackBuffersEXT(0, 1, &buffer, &offset, &size); |
| 878 | cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); | 926 | cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); |
| 879 | }); | 927 | }); |
| @@ -925,8 +973,8 @@ void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex | |||
| 925 | buffer_bindings.AddVertexBinding(DefaultBuffer(), 0); | 973 | buffer_bindings.AddVertexBinding(DefaultBuffer(), 0); |
| 926 | continue; | 974 | continue; |
| 927 | } | 975 | } |
| 928 | const auto [buffer, offset] = buffer_cache.UploadMemory(start, size); | 976 | const auto info = buffer_cache.UploadMemory(start, size); |
| 929 | buffer_bindings.AddVertexBinding(buffer, offset); | 977 | buffer_bindings.AddVertexBinding(info.handle, info.offset); |
| 930 | } | 978 | } |
| 931 | } | 979 | } |
| 932 | 980 | ||
| @@ -948,7 +996,9 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar | |||
| 948 | break; | 996 | break; |
| 949 | } | 997 | } |
| 950 | const GPUVAddr gpu_addr = regs.index_array.IndexStart(); | 998 | const GPUVAddr gpu_addr = regs.index_array.IndexStart(); |
| 951 | auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); | 999 | const auto info = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); |
| 1000 | VkBuffer buffer = info.handle; | ||
| 1001 | u64 offset = info.offset; | ||
| 952 | std::tie(buffer, offset) = quad_indexed_pass.Assemble( | 1002 | std::tie(buffer, offset) = quad_indexed_pass.Assemble( |
| 953 | regs.index_array.format, params.num_vertices, params.base_vertex, buffer, offset); | 1003 | regs.index_array.format, params.num_vertices, params.base_vertex, buffer, offset); |
| 954 | 1004 | ||
| @@ -962,7 +1012,9 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar | |||
| 962 | break; | 1012 | break; |
| 963 | } | 1013 | } |
| 964 | const GPUVAddr gpu_addr = regs.index_array.IndexStart(); | 1014 | const GPUVAddr gpu_addr = regs.index_array.IndexStart(); |
| 965 | auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); | 1015 | const auto info = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); |
| 1016 | VkBuffer buffer = info.handle; | ||
| 1017 | u64 offset = info.offset; | ||
| 966 | 1018 | ||
| 967 | auto format = regs.index_array.format; | 1019 | auto format = regs.index_array.format; |
| 968 | const bool is_uint8 = format == Maxwell::IndexFormat::UnsignedByte; | 1020 | const bool is_uint8 = format == Maxwell::IndexFormat::UnsignedByte; |
| @@ -1109,10 +1161,9 @@ void RasterizerVulkan::SetupConstBuffer(const ConstBufferEntry& entry, | |||
| 1109 | Common::AlignUp(CalculateConstBufferSize(entry, buffer), 4 * sizeof(float)); | 1161 | Common::AlignUp(CalculateConstBufferSize(entry, buffer), 4 * sizeof(float)); |
| 1110 | ASSERT(size <= MaxConstbufferSize); | 1162 | ASSERT(size <= MaxConstbufferSize); |
| 1111 | 1163 | ||
| 1112 | const auto [buffer_handle, offset] = | 1164 | const auto info = |
| 1113 | buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment()); | 1165 | buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment()); |
| 1114 | 1166 | update_descriptor_queue.AddBuffer(info.handle, info.offset, size); | |
| 1115 | update_descriptor_queue.AddBuffer(buffer_handle, offset, size); | ||
| 1116 | } | 1167 | } |
| 1117 | 1168 | ||
| 1118 | void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address) { | 1169 | void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address) { |
| @@ -1126,14 +1177,14 @@ void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAdd | |||
| 1126 | // Note: Do *not* use DefaultBuffer() here, storage buffers can be written breaking the | 1177 | // Note: Do *not* use DefaultBuffer() here, storage buffers can be written breaking the |
| 1127 | // default buffer. | 1178 | // default buffer. |
| 1128 | static constexpr std::size_t dummy_size = 4; | 1179 | static constexpr std::size_t dummy_size = 4; |
| 1129 | const auto buffer = buffer_cache.GetEmptyBuffer(dummy_size); | 1180 | const auto info = buffer_cache.GetEmptyBuffer(dummy_size); |
| 1130 | update_descriptor_queue.AddBuffer(buffer, 0, dummy_size); | 1181 | update_descriptor_queue.AddBuffer(info.handle, info.offset, dummy_size); |
| 1131 | return; | 1182 | return; |
| 1132 | } | 1183 | } |
| 1133 | 1184 | ||
| 1134 | const auto [buffer, offset] = buffer_cache.UploadMemory( | 1185 | const auto info = buffer_cache.UploadMemory( |
| 1135 | actual_addr, size, device.GetStorageBufferAlignment(), entry.IsWritten()); | 1186 | actual_addr, size, device.GetStorageBufferAlignment(), entry.IsWritten()); |
| 1136 | update_descriptor_queue.AddBuffer(buffer, offset, size); | 1187 | update_descriptor_queue.AddBuffer(info.handle, info.offset, size); |
| 1137 | } | 1188 | } |
| 1138 | 1189 | ||
| 1139 | void RasterizerVulkan::SetupUniformTexels(const Tegra::Texture::TICEntry& tic, | 1190 | void RasterizerVulkan::SetupUniformTexels(const Tegra::Texture::TICEntry& tic, |
| @@ -1154,7 +1205,7 @@ void RasterizerVulkan::SetupTexture(const Tegra::Texture::FullTextureInfo& textu | |||
| 1154 | const auto sampler = sampler_cache.GetSampler(texture.tsc); | 1205 | const auto sampler = sampler_cache.GetSampler(texture.tsc); |
| 1155 | update_descriptor_queue.AddSampledImage(sampler, image_view); | 1206 | update_descriptor_queue.AddSampledImage(sampler, image_view); |
| 1156 | 1207 | ||
| 1157 | const auto image_layout = update_descriptor_queue.GetLastImageLayout(); | 1208 | VkImageLayout* const image_layout = update_descriptor_queue.LastImageLayout(); |
| 1158 | *image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; | 1209 | *image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| 1159 | sampled_views.push_back(ImageView{std::move(view), image_layout}); | 1210 | sampled_views.push_back(ImageView{std::move(view), image_layout}); |
| 1160 | } | 1211 | } |
| @@ -1180,7 +1231,7 @@ void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const Ima | |||
| 1180 | view->GetImageView(tic.x_source, tic.y_source, tic.z_source, tic.w_source); | 1231 | view->GetImageView(tic.x_source, tic.y_source, tic.z_source, tic.w_source); |
| 1181 | update_descriptor_queue.AddImage(image_view); | 1232 | update_descriptor_queue.AddImage(image_view); |
| 1182 | 1233 | ||
| 1183 | const auto image_layout = update_descriptor_queue.GetLastImageLayout(); | 1234 | VkImageLayout* const image_layout = update_descriptor_queue.LastImageLayout(); |
| 1184 | *image_layout = VK_IMAGE_LAYOUT_GENERAL; | 1235 | *image_layout = VK_IMAGE_LAYOUT_GENERAL; |
| 1185 | image_views.push_back(ImageView{std::move(view), image_layout}); | 1236 | image_views.push_back(ImageView{std::move(view), image_layout}); |
| 1186 | } | 1237 | } |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index c8c187606..83e00e7e9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -159,7 +159,10 @@ private: | |||
| 159 | 159 | ||
| 160 | void FlushWork(); | 160 | void FlushWork(); |
| 161 | 161 | ||
| 162 | Texceptions UpdateAttachments(); | 162 | /// @brief Updates the currently bound attachments |
| 163 | /// @param is_clear True when the framebuffer is updated as a clear | ||
| 164 | /// @return Bitfield of attachments being used as sampled textures | ||
| 165 | Texceptions UpdateAttachments(bool is_clear); | ||
| 163 | 166 | ||
| 164 | std::tuple<VkFramebuffer, VkExtent2D> ConfigureFramebuffers(VkRenderPass renderpass); | 167 | std::tuple<VkFramebuffer, VkExtent2D> ConfigureFramebuffers(VkRenderPass renderpass); |
| 165 | 168 | ||
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 82ec9180e..56524e6f3 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <utility> | 9 | #include <utility> |
| 10 | 10 | ||
| 11 | #include "common/microprofile.h" | 11 | #include "common/microprofile.h" |
| 12 | #include "common/thread.h" | ||
| 12 | #include "video_core/renderer_vulkan/vk_device.h" | 13 | #include "video_core/renderer_vulkan/vk_device.h" |
| 13 | #include "video_core/renderer_vulkan/vk_query_cache.h" | 14 | #include "video_core/renderer_vulkan/vk_query_cache.h" |
| 14 | #include "video_core/renderer_vulkan/vk_resource_manager.h" | 15 | #include "video_core/renderer_vulkan/vk_resource_manager.h" |
| @@ -133,6 +134,7 @@ void VKScheduler::BindGraphicsPipeline(VkPipeline pipeline) { | |||
| 133 | } | 134 | } |
| 134 | 135 | ||
| 135 | void VKScheduler::WorkerThread() { | 136 | void VKScheduler::WorkerThread() { |
| 137 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||
| 136 | std::unique_lock lock{mutex}; | 138 | std::unique_lock lock{mutex}; |
| 137 | do { | 139 | do { |
| 138 | cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; }); | 140 | cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; }); |
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h index c765c60a0..689f0d276 100644 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.h +++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h | |||
| @@ -35,10 +35,14 @@ public: | |||
| 35 | /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy. | 35 | /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy. |
| 36 | void Unmap(u64 size); | 36 | void Unmap(u64 size); |
| 37 | 37 | ||
| 38 | VkBuffer Handle() const { | 38 | VkBuffer Handle() const noexcept { |
| 39 | return *buffer; | 39 | return *buffer; |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | u64 Address() const noexcept { | ||
| 43 | return 0; | ||
| 44 | } | ||
| 45 | |||
| 42 | private: | 46 | private: |
| 43 | struct Watch final { | 47 | struct Watch final { |
| 44 | VKFenceWatch fence; | 48 | VKFenceWatch fence; |
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp index 681ecde98..351c048d2 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp | |||
| @@ -24,35 +24,25 @@ void VKUpdateDescriptorQueue::TickFrame() { | |||
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | void VKUpdateDescriptorQueue::Acquire() { | 26 | void VKUpdateDescriptorQueue::Acquire() { |
| 27 | entries.clear(); | 27 | // Minimum number of entries required. |
| 28 | } | 28 | // This is the maximum number of entries a single draw call migth use. |
| 29 | static constexpr std::size_t MIN_ENTRIES = 0x400; | ||
| 29 | 30 | ||
| 30 | void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template, | 31 | if (payload.size() + MIN_ENTRIES >= payload.max_size()) { |
| 31 | VkDescriptorSet set) { | ||
| 32 | if (payload.size() + entries.size() >= payload.max_size()) { | ||
| 33 | LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); | 32 | LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); |
| 34 | scheduler.WaitWorker(); | 33 | scheduler.WaitWorker(); |
| 35 | payload.clear(); | 34 | payload.clear(); |
| 36 | } | 35 | } |
| 36 | upload_start = &*payload.end(); | ||
| 37 | } | ||
| 37 | 38 | ||
| 38 | // TODO(Rodrigo): Rework to write the payload directly | 39 | void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template, |
| 39 | const auto payload_start = payload.data() + payload.size(); | 40 | VkDescriptorSet set) { |
| 40 | for (const auto& entry : entries) { | 41 | const void* const data = upload_start; |
| 41 | if (const auto image = std::get_if<VkDescriptorImageInfo>(&entry)) { | 42 | const vk::Device* const logical = &device.GetLogical(); |
| 42 | payload.push_back(*image); | 43 | scheduler.Record([data, logical, set, update_template](vk::CommandBuffer) { |
| 43 | } else if (const auto buffer = std::get_if<VkDescriptorBufferInfo>(&entry)) { | 44 | logical->UpdateDescriptorSet(set, update_template, data); |
| 44 | payload.push_back(*buffer); | 45 | }); |
| 45 | } else if (const auto texel = std::get_if<VkBufferView>(&entry)) { | ||
| 46 | payload.push_back(*texel); | ||
| 47 | } else { | ||
| 48 | UNREACHABLE(); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | scheduler.Record( | ||
| 53 | [payload_start, set, update_template, logical = &device.GetLogical()](vk::CommandBuffer) { | ||
| 54 | logical->UpdateDescriptorSet(set, update_template, payload_start); | ||
| 55 | }); | ||
| 56 | } | 46 | } |
| 57 | 47 | ||
| 58 | } // namespace Vulkan | 48 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h index cc7e3dff4..945320c72 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.h +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h | |||
| @@ -15,17 +15,13 @@ namespace Vulkan { | |||
| 15 | class VKDevice; | 15 | class VKDevice; |
| 16 | class VKScheduler; | 16 | class VKScheduler; |
| 17 | 17 | ||
| 18 | class DescriptorUpdateEntry { | 18 | struct DescriptorUpdateEntry { |
| 19 | public: | 19 | DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {} |
| 20 | explicit DescriptorUpdateEntry() {} | ||
| 21 | |||
| 22 | DescriptorUpdateEntry(VkDescriptorImageInfo image) : image{image} {} | ||
| 23 | 20 | ||
| 24 | DescriptorUpdateEntry(VkDescriptorBufferInfo buffer) : buffer{buffer} {} | 21 | DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {} |
| 25 | 22 | ||
| 26 | DescriptorUpdateEntry(VkBufferView texel_buffer) : texel_buffer{texel_buffer} {} | 23 | DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {} |
| 27 | 24 | ||
| 28 | private: | ||
| 29 | union { | 25 | union { |
| 30 | VkDescriptorImageInfo image; | 26 | VkDescriptorImageInfo image; |
| 31 | VkDescriptorBufferInfo buffer; | 27 | VkDescriptorBufferInfo buffer; |
| @@ -45,32 +41,34 @@ public: | |||
| 45 | void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set); | 41 | void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set); |
| 46 | 42 | ||
| 47 | void AddSampledImage(VkSampler sampler, VkImageView image_view) { | 43 | void AddSampledImage(VkSampler sampler, VkImageView image_view) { |
| 48 | entries.emplace_back(VkDescriptorImageInfo{sampler, image_view, {}}); | 44 | payload.emplace_back(VkDescriptorImageInfo{sampler, image_view, {}}); |
| 49 | } | 45 | } |
| 50 | 46 | ||
| 51 | void AddImage(VkImageView image_view) { | 47 | void AddImage(VkImageView image_view) { |
| 52 | entries.emplace_back(VkDescriptorImageInfo{{}, image_view, {}}); | 48 | payload.emplace_back(VkDescriptorImageInfo{{}, image_view, {}}); |
| 53 | } | 49 | } |
| 54 | 50 | ||
| 55 | void AddBuffer(VkBuffer buffer, u64 offset, std::size_t size) { | 51 | void AddBuffer(VkBuffer buffer, u64 offset, std::size_t size) { |
| 56 | entries.emplace_back(VkDescriptorBufferInfo{buffer, offset, size}); | 52 | payload.emplace_back(VkDescriptorBufferInfo{buffer, offset, size}); |
| 57 | } | 53 | } |
| 58 | 54 | ||
| 59 | void AddTexelBuffer(VkBufferView texel_buffer) { | 55 | void AddTexelBuffer(VkBufferView texel_buffer) { |
| 60 | entries.emplace_back(texel_buffer); | 56 | payload.emplace_back(texel_buffer); |
| 61 | } | 57 | } |
| 62 | 58 | ||
| 63 | VkImageLayout* GetLastImageLayout() { | 59 | VkImageLayout* LastImageLayout() { |
| 64 | return &std::get<VkDescriptorImageInfo>(entries.back()).imageLayout; | 60 | return &payload.back().image.imageLayout; |
| 65 | } | 61 | } |
| 66 | 62 | ||
| 67 | private: | 63 | const VkImageLayout* LastImageLayout() const { |
| 68 | using Variant = std::variant<VkDescriptorImageInfo, VkDescriptorBufferInfo, VkBufferView>; | 64 | return &payload.back().image.imageLayout; |
| 65 | } | ||
| 69 | 66 | ||
| 67 | private: | ||
| 70 | const VKDevice& device; | 68 | const VKDevice& device; |
| 71 | VKScheduler& scheduler; | 69 | VKScheduler& scheduler; |
| 72 | 70 | ||
| 73 | boost::container::static_vector<Variant, 0x400> entries; | 71 | const DescriptorUpdateEntry* upload_start = nullptr; |
| 74 | boost::container::static_vector<DescriptorUpdateEntry, 0x10000> payload; | 72 | boost::container::static_vector<DescriptorUpdateEntry, 0x10000> payload; |
| 75 | }; | 73 | }; |
| 76 | 74 | ||
diff --git a/src/video_core/shader/decode/half_set.cpp b/src/video_core/shader/decode/half_set.cpp index 848e46874..b2e88fa20 100644 --- a/src/video_core/shader/decode/half_set.cpp +++ b/src/video_core/shader/decode/half_set.cpp | |||
| @@ -13,55 +13,101 @@ | |||
| 13 | 13 | ||
| 14 | namespace VideoCommon::Shader { | 14 | namespace VideoCommon::Shader { |
| 15 | 15 | ||
| 16 | using std::move; | ||
| 16 | using Tegra::Shader::Instruction; | 17 | using Tegra::Shader::Instruction; |
| 17 | using Tegra::Shader::OpCode; | 18 | using Tegra::Shader::OpCode; |
| 19 | using Tegra::Shader::PredCondition; | ||
| 18 | 20 | ||
| 19 | u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) { | 21 | u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) { |
| 20 | const Instruction instr = {program_code[pc]}; | 22 | const Instruction instr = {program_code[pc]}; |
| 21 | const auto opcode = OpCode::Decode(instr); | 23 | const auto opcode = OpCode::Decode(instr); |
| 22 | 24 | ||
| 23 | if (instr.hset2.ftz == 0) { | 25 | PredCondition cond; |
| 24 | LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); | 26 | bool bf; |
| 27 | bool ftz; | ||
| 28 | bool neg_a; | ||
| 29 | bool abs_a; | ||
| 30 | bool neg_b; | ||
| 31 | bool abs_b; | ||
| 32 | switch (opcode->get().GetId()) { | ||
| 33 | case OpCode::Id::HSET2_C: | ||
| 34 | case OpCode::Id::HSET2_IMM: | ||
| 35 | cond = instr.hsetp2.cbuf_and_imm.cond; | ||
| 36 | bf = instr.Bit(53); | ||
| 37 | ftz = instr.Bit(54); | ||
| 38 | neg_a = instr.Bit(43); | ||
| 39 | abs_a = instr.Bit(44); | ||
| 40 | neg_b = instr.Bit(56); | ||
| 41 | abs_b = instr.Bit(54); | ||
| 42 | break; | ||
| 43 | case OpCode::Id::HSET2_R: | ||
| 44 | cond = instr.hsetp2.reg.cond; | ||
| 45 | bf = instr.Bit(49); | ||
| 46 | ftz = instr.Bit(50); | ||
| 47 | neg_a = instr.Bit(43); | ||
| 48 | abs_a = instr.Bit(44); | ||
| 49 | neg_b = instr.Bit(31); | ||
| 50 | abs_b = instr.Bit(30); | ||
| 51 | break; | ||
| 52 | default: | ||
| 53 | UNREACHABLE(); | ||
| 25 | } | 54 | } |
| 26 | 55 | ||
| 27 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a); | 56 | Node op_b = [this, instr, opcode] { |
| 28 | op_a = GetOperandAbsNegHalf(op_a, instr.hset2.abs_a, instr.hset2.negate_a); | ||
| 29 | |||
| 30 | Node op_b = [&]() { | ||
| 31 | switch (opcode->get().GetId()) { | 57 | switch (opcode->get().GetId()) { |
| 58 | case OpCode::Id::HSET2_C: | ||
| 59 | // Inform as unimplemented as this is not tested. | ||
| 60 | UNIMPLEMENTED_MSG("HSET2_C is not implemented"); | ||
| 61 | return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | ||
| 32 | case OpCode::Id::HSET2_R: | 62 | case OpCode::Id::HSET2_R: |
| 33 | return GetRegister(instr.gpr20); | 63 | return GetRegister(instr.gpr20); |
| 64 | case OpCode::Id::HSET2_IMM: | ||
| 65 | return UnpackHalfImmediate(instr, true); | ||
| 34 | default: | 66 | default: |
| 35 | UNREACHABLE(); | 67 | UNREACHABLE(); |
| 36 | return Immediate(0); | 68 | return Node{}; |
| 37 | } | 69 | } |
| 38 | }(); | 70 | }(); |
| 39 | op_b = UnpackHalfFloat(op_b, instr.hset2.type_b); | ||
| 40 | op_b = GetOperandAbsNegHalf(op_b, instr.hset2.abs_b, instr.hset2.negate_b); | ||
| 41 | 71 | ||
| 42 | const Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred); | 72 | if (!ftz) { |
| 73 | LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); | ||
| 74 | } | ||
| 75 | |||
| 76 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a); | ||
| 77 | op_a = GetOperandAbsNegHalf(op_a, abs_a, neg_a); | ||
| 78 | |||
| 79 | switch (opcode->get().GetId()) { | ||
| 80 | case OpCode::Id::HSET2_R: | ||
| 81 | op_b = GetOperandAbsNegHalf(move(op_b), abs_b, neg_b); | ||
| 82 | [[fallthrough]]; | ||
| 83 | case OpCode::Id::HSET2_C: | ||
| 84 | op_b = UnpackHalfFloat(move(op_b), instr.hset2.type_b); | ||
| 85 | break; | ||
| 86 | default: | ||
| 87 | break; | ||
| 88 | } | ||
| 43 | 89 | ||
| 44 | const Node comparison_pair = GetPredicateComparisonHalf(instr.hset2.cond, op_a, op_b); | 90 | Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred); |
| 91 | |||
| 92 | Node comparison_pair = GetPredicateComparisonHalf(cond, op_a, op_b); | ||
| 45 | 93 | ||
| 46 | const OperationCode combiner = GetPredicateCombiner(instr.hset2.op); | 94 | const OperationCode combiner = GetPredicateCombiner(instr.hset2.op); |
| 47 | 95 | ||
| 48 | // HSET2 operates on each half float in the pack. | 96 | // HSET2 operates on each half float in the pack. |
| 49 | std::array<Node, 2> values; | 97 | std::array<Node, 2> values; |
| 50 | for (u32 i = 0; i < 2; ++i) { | 98 | for (u32 i = 0; i < 2; ++i) { |
| 51 | const u32 raw_value = instr.hset2.bf ? 0x3c00 : 0xffff; | 99 | const u32 raw_value = bf ? 0x3c00 : 0xffff; |
| 52 | const Node true_value = Immediate(raw_value << (i * 16)); | 100 | Node true_value = Immediate(raw_value << (i * 16)); |
| 53 | const Node false_value = Immediate(0); | 101 | Node false_value = Immediate(0); |
| 54 | |||
| 55 | const Node comparison = | ||
| 56 | Operation(OperationCode::LogicalPick2, comparison_pair, Immediate(i)); | ||
| 57 | const Node predicate = Operation(combiner, comparison, second_pred); | ||
| 58 | 102 | ||
| 103 | Node comparison = Operation(OperationCode::LogicalPick2, comparison_pair, Immediate(i)); | ||
| 104 | Node predicate = Operation(combiner, comparison, second_pred); | ||
| 59 | values[i] = | 105 | values[i] = |
| 60 | Operation(OperationCode::Select, NO_PRECISE, predicate, true_value, false_value); | 106 | Operation(OperationCode::Select, predicate, move(true_value), move(false_value)); |
| 61 | } | 107 | } |
| 62 | 108 | ||
| 63 | const Node value = Operation(OperationCode::UBitwiseOr, NO_PRECISE, values[0], values[1]); | 109 | Node value = Operation(OperationCode::UBitwiseOr, values[0], values[1]); |
| 64 | SetRegister(bb, instr.gpr0, value); | 110 | SetRegister(bb, instr.gpr0, move(value)); |
| 65 | 111 | ||
| 66 | return pc; | 112 | return pc; |
| 67 | } | 113 | } |
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp index 60b6ad72a..07778dc3e 100644 --- a/src/video_core/shader/decode/image.cpp +++ b/src/video_core/shader/decode/image.cpp | |||
| @@ -97,6 +97,7 @@ ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor, | |||
| 97 | break; | 97 | break; |
| 98 | case TextureFormat::B5G6R5: | 98 | case TextureFormat::B5G6R5: |
| 99 | case TextureFormat::B6G5R5: | 99 | case TextureFormat::B6G5R5: |
| 100 | case TextureFormat::BF10GF11RF11: | ||
| 100 | if (component == 0) { | 101 | if (component == 0) { |
| 101 | return descriptor.b_type; | 102 | return descriptor.b_type; |
| 102 | } | 103 | } |
| @@ -119,7 +120,7 @@ ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor, | |||
| 119 | } | 120 | } |
| 120 | break; | 121 | break; |
| 121 | } | 122 | } |
| 122 | UNIMPLEMENTED_MSG("texture format not implement={}", format); | 123 | UNIMPLEMENTED_MSG("Texture format not implemented={}", format); |
| 123 | return ComponentType::FLOAT; | 124 | return ComponentType::FLOAT; |
| 124 | } | 125 | } |
| 125 | 126 | ||
| @@ -191,6 +192,14 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) { | |||
| 191 | return 6; | 192 | return 6; |
| 192 | } | 193 | } |
| 193 | return 0; | 194 | return 0; |
| 195 | case TextureFormat::BF10GF11RF11: | ||
| 196 | if (component == 1 || component == 2) { | ||
| 197 | return 11; | ||
| 198 | } | ||
| 199 | if (component == 0) { | ||
| 200 | return 10; | ||
| 201 | } | ||
| 202 | return 0; | ||
| 194 | case TextureFormat::G8R24: | 203 | case TextureFormat::G8R24: |
| 195 | if (component == 0) { | 204 | if (component == 0) { |
| 196 | return 8; | 205 | return 8; |
| @@ -211,10 +220,9 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) { | |||
| 211 | return (component == 0 || component == 1) ? 8 : 0; | 220 | return (component == 0 || component == 1) ? 8 : 0; |
| 212 | case TextureFormat::G4R4: | 221 | case TextureFormat::G4R4: |
| 213 | return (component == 0 || component == 1) ? 4 : 0; | 222 | return (component == 0 || component == 1) ? 4 : 0; |
| 214 | default: | ||
| 215 | UNIMPLEMENTED_MSG("texture format not implement={}", format); | ||
| 216 | return 0; | ||
| 217 | } | 223 | } |
| 224 | UNIMPLEMENTED_MSG("Texture format not implemented={}", format); | ||
| 225 | return 0; | ||
| 218 | } | 226 | } |
| 219 | 227 | ||
| 220 | std::size_t GetImageComponentMask(TextureFormat format) { | 228 | std::size_t GetImageComponentMask(TextureFormat format) { |
| @@ -235,6 +243,7 @@ std::size_t GetImageComponentMask(TextureFormat format) { | |||
| 235 | case TextureFormat::R32_B24G8: | 243 | case TextureFormat::R32_B24G8: |
| 236 | case TextureFormat::B5G6R5: | 244 | case TextureFormat::B5G6R5: |
| 237 | case TextureFormat::B6G5R5: | 245 | case TextureFormat::B6G5R5: |
| 246 | case TextureFormat::BF10GF11RF11: | ||
| 238 | return std::size_t{R | G | B}; | 247 | return std::size_t{R | G | B}; |
| 239 | case TextureFormat::R32_G32: | 248 | case TextureFormat::R32_G32: |
| 240 | case TextureFormat::R16_G16: | 249 | case TextureFormat::R16_G16: |
| @@ -248,10 +257,9 @@ std::size_t GetImageComponentMask(TextureFormat format) { | |||
| 248 | case TextureFormat::R8: | 257 | case TextureFormat::R8: |
| 249 | case TextureFormat::R1: | 258 | case TextureFormat::R1: |
| 250 | return std::size_t{R}; | 259 | return std::size_t{R}; |
| 251 | default: | ||
| 252 | UNIMPLEMENTED_MSG("texture format not implement={}", format); | ||
| 253 | return std::size_t{R | G | B | A}; | ||
| 254 | } | 260 | } |
| 261 | UNIMPLEMENTED_MSG("Texture format not implemented={}", format); | ||
| 262 | return std::size_t{R | G | B | A}; | ||
| 255 | } | 263 | } |
| 256 | 264 | ||
| 257 | std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) { | 265 | std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) { |
| @@ -299,7 +307,7 @@ std::pair<Node, bool> ShaderIR::GetComponentValue(ComponentType component_type, | |||
| 299 | return {std::move(original_value), true}; | 307 | return {std::move(original_value), true}; |
| 300 | } | 308 | } |
| 301 | default: | 309 | default: |
| 302 | UNIMPLEMENTED_MSG("Unimplement component type={}", component_type); | 310 | UNIMPLEMENTED_MSG("Unimplemented component type={}", component_type); |
| 303 | return {std::move(original_value), true}; | 311 | return {std::move(original_value), true}; |
| 304 | } | 312 | } |
| 305 | } | 313 | } |
| @@ -459,7 +467,7 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { | |||
| 459 | default: | 467 | default: |
| 460 | break; | 468 | break; |
| 461 | } | 469 | } |
| 462 | UNIMPLEMENTED_MSG("Unimplemented operation={} type={}", | 470 | UNIMPLEMENTED_MSG("Unimplemented operation={}, type={}", |
| 463 | static_cast<u64>(instr.suatom_d.operation.Value()), | 471 | static_cast<u64>(instr.suatom_d.operation.Value()), |
| 464 | static_cast<u64>(instr.suatom_d.operation_type.Value())); | 472 | static_cast<u64>(instr.suatom_d.operation_type.Value())); |
| 465 | return OperationCode::AtomicImageAdd; | 473 | return OperationCode::AtomicImageAdd; |
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp index 94d3a6ae5..0caf3b4f0 100644 --- a/src/video_core/texture_cache/surface_base.cpp +++ b/src/video_core/texture_cache/surface_base.cpp | |||
| @@ -120,6 +120,9 @@ std::optional<std::pair<u32, u32>> SurfaceBaseImpl::GetLayerMipmap( | |||
| 120 | } | 120 | } |
| 121 | const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)}; | 121 | const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)}; |
| 122 | const auto layer{static_cast<u32>(relative_address / layer_size)}; | 122 | const auto layer{static_cast<u32>(relative_address / layer_size)}; |
| 123 | if (layer >= params.depth) { | ||
| 124 | return {}; | ||
| 125 | } | ||
| 123 | const GPUVAddr mipmap_address = relative_address - layer_size * layer; | 126 | const GPUVAddr mipmap_address = relative_address - layer_size * layer; |
| 124 | const auto mipmap_it = | 127 | const auto mipmap_it = |
| 125 | Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address); | 128 | Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address); |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index b543fc8c0..85075e868 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -1053,7 +1053,7 @@ private: | |||
| 1053 | void DeduceBestBlit(SurfaceParams& src_params, SurfaceParams& dst_params, | 1053 | void DeduceBestBlit(SurfaceParams& src_params, SurfaceParams& dst_params, |
| 1054 | const GPUVAddr src_gpu_addr, const GPUVAddr dst_gpu_addr) { | 1054 | const GPUVAddr src_gpu_addr, const GPUVAddr dst_gpu_addr) { |
| 1055 | auto deduced_src = DeduceSurface(src_gpu_addr, src_params); | 1055 | auto deduced_src = DeduceSurface(src_gpu_addr, src_params); |
| 1056 | auto deduced_dst = DeduceSurface(src_gpu_addr, src_params); | 1056 | auto deduced_dst = DeduceSurface(dst_gpu_addr, dst_params); |
| 1057 | if (deduced_src.Failed() || deduced_dst.Failed()) { | 1057 | if (deduced_src.Failed() || deduced_dst.Failed()) { |
| 1058 | return; | 1058 | return; |
| 1059 | } | 1059 | } |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 696da2137..4bfce48a4 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -44,49 +44,65 @@ EmuThread::EmuThread() = default; | |||
| 44 | EmuThread::~EmuThread() = default; | 44 | EmuThread::~EmuThread() = default; |
| 45 | 45 | ||
| 46 | void EmuThread::run() { | 46 | void EmuThread::run() { |
| 47 | MicroProfileOnThreadCreate("EmuThread"); | 47 | std::string name = "yuzu:EmuControlThread"; |
| 48 | MicroProfileOnThreadCreate(name.c_str()); | ||
| 49 | Common::SetCurrentThreadName(name.c_str()); | ||
| 50 | |||
| 51 | auto& system = Core::System::GetInstance(); | ||
| 52 | |||
| 53 | system.RegisterHostThread(); | ||
| 54 | |||
| 55 | auto& gpu = system.GPU(); | ||
| 48 | 56 | ||
| 49 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU | 57 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU |
| 50 | // execution. | 58 | // execution. |
| 51 | Core::System::GetInstance().GPU().Start(); | 59 | gpu.Start(); |
| 60 | |||
| 61 | gpu.ObtainContext(); | ||
| 52 | 62 | ||
| 53 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 63 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 54 | 64 | ||
| 55 | Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( | 65 | system.Renderer().Rasterizer().LoadDiskResources( |
| 56 | stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { | 66 | stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { |
| 57 | emit LoadProgress(stage, value, total); | 67 | emit LoadProgress(stage, value, total); |
| 58 | }); | 68 | }); |
| 59 | 69 | ||
| 60 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | 70 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); |
| 61 | 71 | ||
| 72 | gpu.ReleaseContext(); | ||
| 73 | |||
| 62 | // Holds whether the cpu was running during the last iteration, | 74 | // Holds whether the cpu was running during the last iteration, |
| 63 | // so that the DebugModeLeft signal can be emitted before the | 75 | // so that the DebugModeLeft signal can be emitted before the |
| 64 | // next execution step | 76 | // next execution step |
| 65 | bool was_active = false; | 77 | bool was_active = false; |
| 66 | while (!stop_run) { | 78 | while (!stop_run) { |
| 67 | if (running) { | 79 | if (running) { |
| 68 | if (!was_active) | 80 | if (was_active) { |
| 69 | emit DebugModeLeft(); | 81 | emit DebugModeLeft(); |
| 82 | } | ||
| 70 | 83 | ||
| 71 | Core::System::ResultStatus result = Core::System::GetInstance().RunLoop(); | 84 | running_guard = true; |
| 85 | Core::System::ResultStatus result = system.Run(); | ||
| 72 | if (result != Core::System::ResultStatus::Success) { | 86 | if (result != Core::System::ResultStatus::Success) { |
| 87 | running_guard = false; | ||
| 73 | this->SetRunning(false); | 88 | this->SetRunning(false); |
| 74 | emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); | 89 | emit ErrorThrown(result, system.GetStatusDetails()); |
| 75 | } | 90 | } |
| 91 | running_wait.Wait(); | ||
| 92 | result = system.Pause(); | ||
| 93 | if (result != Core::System::ResultStatus::Success) { | ||
| 94 | running_guard = false; | ||
| 95 | this->SetRunning(false); | ||
| 96 | emit ErrorThrown(result, system.GetStatusDetails()); | ||
| 97 | } | ||
| 98 | running_guard = false; | ||
| 76 | 99 | ||
| 77 | was_active = running || exec_step; | 100 | if (!stop_run) { |
| 78 | if (!was_active && !stop_run) | 101 | was_active = true; |
| 79 | emit DebugModeEntered(); | 102 | emit DebugModeEntered(); |
| 103 | } | ||
| 80 | } else if (exec_step) { | 104 | } else if (exec_step) { |
| 81 | if (!was_active) | 105 | UNIMPLEMENTED(); |
| 82 | emit DebugModeLeft(); | ||
| 83 | |||
| 84 | exec_step = false; | ||
| 85 | Core::System::GetInstance().SingleStep(); | ||
| 86 | emit DebugModeEntered(); | ||
| 87 | yieldCurrentThread(); | ||
| 88 | |||
| 89 | was_active = false; | ||
| 90 | } else { | 106 | } else { |
| 91 | std::unique_lock lock{running_mutex}; | 107 | std::unique_lock lock{running_mutex}; |
| 92 | running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); | 108 | running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); |
| @@ -94,7 +110,7 @@ void EmuThread::run() { | |||
| 94 | } | 110 | } |
| 95 | 111 | ||
| 96 | // Shutdown the core emulation | 112 | // Shutdown the core emulation |
| 97 | Core::System::GetInstance().Shutdown(); | 113 | system.Shutdown(); |
| 98 | 114 | ||
| 99 | #if MICROPROFILE_ENABLED | 115 | #if MICROPROFILE_ENABLED |
| 100 | MicroProfileOnThreadExit(); | 116 | MicroProfileOnThreadExit(); |
| @@ -360,7 +376,7 @@ QByteArray GRenderWindow::saveGeometry() { | |||
| 360 | } | 376 | } |
| 361 | 377 | ||
| 362 | qreal GRenderWindow::windowPixelRatio() const { | 378 | qreal GRenderWindow::windowPixelRatio() const { |
| 363 | return devicePixelRatio(); | 379 | return devicePixelRatioF(); |
| 364 | } | 380 | } |
| 365 | 381 | ||
| 366 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const { | 382 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const { |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 3626604ca..6c59b4d5c 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -59,6 +59,12 @@ public: | |||
| 59 | this->running = running; | 59 | this->running = running; |
| 60 | lock.unlock(); | 60 | lock.unlock(); |
| 61 | running_cv.notify_all(); | 61 | running_cv.notify_all(); |
| 62 | if (!running) { | ||
| 63 | running_wait.Set(); | ||
| 64 | /// Wait until effectively paused | ||
| 65 | while (running_guard) | ||
| 66 | ; | ||
| 67 | } | ||
| 62 | } | 68 | } |
| 63 | 69 | ||
| 64 | /** | 70 | /** |
| @@ -84,6 +90,8 @@ private: | |||
| 84 | std::atomic_bool stop_run{false}; | 90 | std::atomic_bool stop_run{false}; |
| 85 | std::mutex running_mutex; | 91 | std::mutex running_mutex; |
| 86 | std::condition_variable running_cv; | 92 | std::condition_variable running_cv; |
| 93 | Common::Event running_wait{}; | ||
| 94 | std::atomic_bool running_guard{false}; | ||
| 87 | 95 | ||
| 88 | signals: | 96 | signals: |
| 89 | /** | 97 | /** |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 32c81dc70..bbbd96113 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -211,7 +211,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default | |||
| 211 | // This must be in alphabetical order according to action name as it must have the same order as | 211 | // This must be in alphabetical order according to action name as it must have the same order as |
| 212 | // UISetting::values.shortcuts, which is alphabetically ordered. | 212 | // UISetting::values.shortcuts, which is alphabetically ordered. |
| 213 | // clang-format off | 213 | // clang-format off |
| 214 | const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{{ | 214 | const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ |
| 215 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, | 215 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, |
| 216 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, | 216 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, |
| 217 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, | 217 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, |
| @@ -222,6 +222,7 @@ const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{{ | |||
| 222 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, | 222 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, |
| 223 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, | 223 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, |
| 224 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}}, | 224 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}}, |
| 225 | {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, | ||
| 225 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, | 226 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, |
| 226 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, | 227 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, |
| 227 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, | 228 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 5cd2a5feb..09316382c 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -27,7 +27,7 @@ public: | |||
| 27 | default_mouse_buttons; | 27 | default_mouse_buttons; |
| 28 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; | 28 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; |
| 29 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; | 29 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; |
| 30 | static const std::array<UISettings::Shortcut, 15> default_hotkeys; | 30 | static const std::array<UISettings::Shortcut, 16> default_hotkeys; |
| 31 | 31 | ||
| 32 | private: | 32 | private: |
| 33 | void ReadValues(); | 33 | void ReadValues(); |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index cb95423e0..74b2ad537 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -23,6 +23,11 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | |||
| 23 | ConfigureGeneral::~ConfigureGeneral() = default; | 23 | ConfigureGeneral::~ConfigureGeneral() = default; |
| 24 | 24 | ||
| 25 | void ConfigureGeneral::SetConfiguration() { | 25 | void ConfigureGeneral::SetConfiguration() { |
| 26 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); | ||
| 27 | |||
| 28 | ui->use_multi_core->setEnabled(runtime_lock); | ||
| 29 | ui->use_multi_core->setChecked(Settings::values.use_multi_core); | ||
| 30 | |||
| 26 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); | 31 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); |
| 27 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); | 32 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); |
| 28 | ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); | 33 | ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); |
| @@ -41,6 +46,7 @@ void ConfigureGeneral::ApplyConfiguration() { | |||
| 41 | 46 | ||
| 42 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); | 47 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); |
| 43 | Settings::values.frame_limit = ui->frame_limit->value(); | 48 | Settings::values.frame_limit = ui->frame_limit->value(); |
| 49 | Settings::values.use_multi_core = ui->use_multi_core->isChecked(); | ||
| 44 | } | 50 | } |
| 45 | 51 | ||
| 46 | void ConfigureGeneral::changeEvent(QEvent* event) { | 52 | void ConfigureGeneral::changeEvent(QEvent* event) { |
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index fc3b7e65a..2711116a2 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -52,6 +52,13 @@ | |||
| 52 | </layout> | 52 | </layout> |
| 53 | </item> | 53 | </item> |
| 54 | <item> | 54 | <item> |
| 55 | <widget class="QCheckBox" name="use_multi_core"> | ||
| 56 | <property name="text"> | ||
| 57 | <string>Multicore CPU Emulation</string> | ||
| 58 | </property> | ||
| 59 | </widget> | ||
| 60 | </item> | ||
| 61 | <item> | ||
| 55 | <widget class="QCheckBox" name="toggle_check_exit"> | 62 | <widget class="QCheckBox" name="toggle_check_exit"> |
| 56 | <property name="text"> | 63 | <property name="text"> |
| 57 | <string>Confirm exit while emulation is running</string> | 64 | <string>Confirm exit while emulation is running</string> |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index c1ea25fb8..9bb0a0109 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -2,10 +2,13 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <fmt/format.h> | ||
| 6 | |||
| 5 | #include "yuzu/debugger/wait_tree.h" | 7 | #include "yuzu/debugger/wait_tree.h" |
| 6 | #include "yuzu/util/util.h" | 8 | #include "yuzu/util/util.h" |
| 7 | 9 | ||
| 8 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 11 | #include "core/arm/arm_interface.h" | ||
| 9 | #include "core/core.h" | 12 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/handle_table.h" | 13 | #include "core/hle/kernel/handle_table.h" |
| 11 | #include "core/hle/kernel/mutex.h" | 14 | #include "core/hle/kernel/mutex.h" |
| @@ -59,8 +62,10 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() | |||
| 59 | std::size_t row = 0; | 62 | std::size_t row = 0; |
| 60 | auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) { | 63 | auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) { |
| 61 | for (std::size_t i = 0; i < threads.size(); ++i) { | 64 | for (std::size_t i = 0; i < threads.size(); ++i) { |
| 62 | item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i])); | 65 | if (!threads[i]->IsHLEThread()) { |
| 63 | item_list.back()->row = row; | 66 | item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i])); |
| 67 | item_list.back()->row = row; | ||
| 68 | } | ||
| 64 | ++row; | 69 | ++row; |
| 65 | } | 70 | } |
| 66 | }; | 71 | }; |
| @@ -114,20 +119,21 @@ QString WaitTreeCallstack::GetText() const { | |||
| 114 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { | 119 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { |
| 115 | std::vector<std::unique_ptr<WaitTreeItem>> list; | 120 | std::vector<std::unique_ptr<WaitTreeItem>> list; |
| 116 | 121 | ||
| 117 | constexpr std::size_t BaseRegister = 29; | 122 | if (thread.IsHLEThread()) { |
| 118 | auto& memory = Core::System::GetInstance().Memory(); | 123 | return list; |
| 119 | u64 base_pointer = thread.GetContext64().cpu_registers[BaseRegister]; | 124 | } |
| 120 | 125 | ||
| 121 | while (base_pointer != 0) { | 126 | if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64BitProcess()) { |
| 122 | const u64 lr = memory.Read64(base_pointer + sizeof(u64)); | 127 | return list; |
| 123 | if (lr == 0) { | 128 | } |
| 124 | break; | ||
| 125 | } | ||
| 126 | 129 | ||
| 127 | list.push_back(std::make_unique<WaitTreeText>( | 130 | auto backtrace = Core::ARM_Interface::GetBacktraceFromContext(Core::System::GetInstance(), |
| 128 | tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'}))); | 131 | thread.GetContext64()); |
| 129 | 132 | ||
| 130 | base_pointer = memory.Read64(base_pointer); | 133 | for (auto& entry : backtrace) { |
| 134 | std::string s = fmt::format("{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address, | ||
| 135 | entry.original_address, entry.offset, entry.name); | ||
| 136 | list.push_back(std::make_unique<WaitTreeText>(QString::fromStdString(s))); | ||
| 131 | } | 137 | } |
| 132 | 138 | ||
| 133 | return list; | 139 | return list; |
| @@ -206,7 +212,15 @@ QString WaitTreeThread::GetText() const { | |||
| 206 | status = tr("running"); | 212 | status = tr("running"); |
| 207 | break; | 213 | break; |
| 208 | case Kernel::ThreadStatus::Ready: | 214 | case Kernel::ThreadStatus::Ready: |
| 209 | status = tr("ready"); | 215 | if (!thread.IsPaused()) { |
| 216 | if (thread.WasRunning()) { | ||
| 217 | status = tr("running"); | ||
| 218 | } else { | ||
| 219 | status = tr("ready"); | ||
| 220 | } | ||
| 221 | } else { | ||
| 222 | status = tr("paused"); | ||
| 223 | } | ||
| 210 | break; | 224 | break; |
| 211 | case Kernel::ThreadStatus::Paused: | 225 | case Kernel::ThreadStatus::Paused: |
| 212 | status = tr("paused"); | 226 | status = tr("paused"); |
| @@ -254,7 +268,15 @@ QColor WaitTreeThread::GetColor() const { | |||
| 254 | case Kernel::ThreadStatus::Running: | 268 | case Kernel::ThreadStatus::Running: |
| 255 | return QColor(Qt::GlobalColor::darkGreen); | 269 | return QColor(Qt::GlobalColor::darkGreen); |
| 256 | case Kernel::ThreadStatus::Ready: | 270 | case Kernel::ThreadStatus::Ready: |
| 257 | return QColor(Qt::GlobalColor::darkBlue); | 271 | if (!thread.IsPaused()) { |
| 272 | if (thread.WasRunning()) { | ||
| 273 | return QColor(Qt::GlobalColor::darkGreen); | ||
| 274 | } else { | ||
| 275 | return QColor(Qt::GlobalColor::darkBlue); | ||
| 276 | } | ||
| 277 | } else { | ||
| 278 | return QColor(Qt::GlobalColor::lightGray); | ||
| 279 | } | ||
| 258 | case Kernel::ThreadStatus::Paused: | 280 | case Kernel::ThreadStatus::Paused: |
| 259 | return QColor(Qt::GlobalColor::lightGray); | 281 | return QColor(Qt::GlobalColor::lightGray); |
| 260 | case Kernel::ThreadStatus::WaitHLEEvent: | 282 | case Kernel::ThreadStatus::WaitHLEEvent: |
| @@ -319,7 +341,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 319 | 341 | ||
| 320 | if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { | 342 | if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { |
| 321 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetSynchronizationObjects(), | 343 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetSynchronizationObjects(), |
| 322 | thread.IsSleepingOnWait())); | 344 | thread.IsWaitingSync())); |
| 323 | } | 345 | } |
| 324 | 346 | ||
| 325 | list.push_back(std::make_unique<WaitTreeCallstack>(thread)); | 347 | list.push_back(std::make_unique<WaitTreeCallstack>(thread)); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4119d7907..82625e67f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -56,6 +56,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 56 | #include <QShortcut> | 56 | #include <QShortcut> |
| 57 | #include <QStatusBar> | 57 | #include <QStatusBar> |
| 58 | #include <QSysInfo> | 58 | #include <QSysInfo> |
| 59 | #include <QUrl> | ||
| 59 | #include <QtConcurrent/QtConcurrent> | 60 | #include <QtConcurrent/QtConcurrent> |
| 60 | 61 | ||
| 61 | #include <fmt/format.h> | 62 | #include <fmt/format.h> |
| @@ -217,7 +218,20 @@ GMainWindow::GMainWindow() | |||
| 217 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", yuzu_build_version, Common::g_scm_branch, | 218 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", yuzu_build_version, Common::g_scm_branch, |
| 218 | Common::g_scm_desc); | 219 | Common::g_scm_desc); |
| 219 | #ifdef ARCHITECTURE_x86_64 | 220 | #ifdef ARCHITECTURE_x86_64 |
| 220 | LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string); | 221 | const auto& caps = Common::GetCPUCaps(); |
| 222 | std::string cpu_string = caps.cpu_string; | ||
| 223 | if (caps.avx || caps.avx2 || caps.avx512) { | ||
| 224 | cpu_string += " | AVX"; | ||
| 225 | if (caps.avx512) { | ||
| 226 | cpu_string += "512"; | ||
| 227 | } else if (caps.avx2) { | ||
| 228 | cpu_string += '2'; | ||
| 229 | } | ||
| 230 | if (caps.fma || caps.fma4) { | ||
| 231 | cpu_string += " | FMA"; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | LOG_INFO(Frontend, "Host CPU: {}", cpu_string); | ||
| 221 | #endif | 235 | #endif |
| 222 | LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); | 236 | LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); |
| 223 | LOG_INFO(Frontend, "Host RAM: {:.2f} GB", | 237 | LOG_INFO(Frontend, "Host RAM: {:.2f} GB", |
| @@ -520,14 +534,36 @@ void GMainWindow::InitializeWidgets() { | |||
| 520 | if (emulation_running) { | 534 | if (emulation_running) { |
| 521 | return; | 535 | return; |
| 522 | } | 536 | } |
| 523 | Settings::values.use_asynchronous_gpu_emulation = | 537 | bool is_async = |
| 524 | !Settings::values.use_asynchronous_gpu_emulation; | 538 | !Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; |
| 539 | Settings::values.use_asynchronous_gpu_emulation = is_async; | ||
| 525 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | 540 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); |
| 526 | Settings::Apply(); | 541 | Settings::Apply(); |
| 527 | }); | 542 | }); |
| 528 | async_status_button->setText(tr("ASYNC")); | 543 | async_status_button->setText(tr("ASYNC")); |
| 529 | async_status_button->setCheckable(true); | 544 | async_status_button->setCheckable(true); |
| 530 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | 545 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); |
| 546 | |||
| 547 | // Setup Multicore button | ||
| 548 | multicore_status_button = new QPushButton(); | ||
| 549 | multicore_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); | ||
| 550 | multicore_status_button->setFocusPolicy(Qt::NoFocus); | ||
| 551 | connect(multicore_status_button, &QPushButton::clicked, [&] { | ||
| 552 | if (emulation_running) { | ||
| 553 | return; | ||
| 554 | } | ||
| 555 | Settings::values.use_multi_core = !Settings::values.use_multi_core; | ||
| 556 | bool is_async = | ||
| 557 | Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; | ||
| 558 | Settings::values.use_asynchronous_gpu_emulation = is_async; | ||
| 559 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | ||
| 560 | multicore_status_button->setChecked(Settings::values.use_multi_core); | ||
| 561 | Settings::Apply(); | ||
| 562 | }); | ||
| 563 | multicore_status_button->setText(tr("MULTICORE")); | ||
| 564 | multicore_status_button->setCheckable(true); | ||
| 565 | multicore_status_button->setChecked(Settings::values.use_multi_core); | ||
| 566 | statusBar()->insertPermanentWidget(0, multicore_status_button); | ||
| 531 | statusBar()->insertPermanentWidget(0, async_status_button); | 567 | statusBar()->insertPermanentWidget(0, async_status_button); |
| 532 | 568 | ||
| 533 | // Setup Renderer API button | 569 | // Setup Renderer API button |
| @@ -723,6 +759,9 @@ void GMainWindow::InitializeHotkeys() { | |||
| 723 | Settings::values.use_docked_mode); | 759 | Settings::values.use_docked_mode); |
| 724 | dock_status_button->setChecked(Settings::values.use_docked_mode); | 760 | dock_status_button->setChecked(Settings::values.use_docked_mode); |
| 725 | }); | 761 | }); |
| 762 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this), | ||
| 763 | &QShortcut::activated, this, | ||
| 764 | [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); | ||
| 726 | } | 765 | } |
| 727 | 766 | ||
| 728 | void GMainWindow::SetDefaultUIGeometry() { | 767 | void GMainWindow::SetDefaultUIGeometry() { |
| @@ -823,6 +862,7 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 823 | connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame); | 862 | connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame); |
| 824 | connect(ui.action_Report_Compatibility, &QAction::triggered, this, | 863 | connect(ui.action_Report_Compatibility, &QAction::triggered, this, |
| 825 | &GMainWindow::OnMenuReportCompatibility); | 864 | &GMainWindow::OnMenuReportCompatibility); |
| 865 | connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); | ||
| 826 | connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); | 866 | connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); |
| 827 | connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); | 867 | connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); |
| 828 | 868 | ||
| @@ -907,6 +947,8 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||
| 907 | nullptr, // E-Commerce | 947 | nullptr, // E-Commerce |
| 908 | }); | 948 | }); |
| 909 | 949 | ||
| 950 | system.RegisterHostThread(); | ||
| 951 | |||
| 910 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; | 952 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; |
| 911 | 953 | ||
| 912 | const auto drd_callout = | 954 | const auto drd_callout = |
| @@ -1023,6 +1065,7 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 1023 | } | 1065 | } |
| 1024 | status_bar_update_timer.start(2000); | 1066 | status_bar_update_timer.start(2000); |
| 1025 | async_status_button->setDisabled(true); | 1067 | async_status_button->setDisabled(true); |
| 1068 | multicore_status_button->setDisabled(true); | ||
| 1026 | renderer_status_button->setDisabled(true); | 1069 | renderer_status_button->setDisabled(true); |
| 1027 | 1070 | ||
| 1028 | if (UISettings::values.hide_mouse) { | 1071 | if (UISettings::values.hide_mouse) { |
| @@ -1110,6 +1153,7 @@ void GMainWindow::ShutdownGame() { | |||
| 1110 | game_fps_label->setVisible(false); | 1153 | game_fps_label->setVisible(false); |
| 1111 | emu_frametime_label->setVisible(false); | 1154 | emu_frametime_label->setVisible(false); |
| 1112 | async_status_button->setEnabled(true); | 1155 | async_status_button->setEnabled(true); |
| 1156 | multicore_status_button->setEnabled(true); | ||
| 1113 | #ifdef HAS_VULKAN | 1157 | #ifdef HAS_VULKAN |
| 1114 | renderer_status_button->setEnabled(true); | 1158 | renderer_status_button->setEnabled(true); |
| 1115 | #endif | 1159 | #endif |
| @@ -1794,6 +1838,16 @@ void GMainWindow::OnMenuReportCompatibility() { | |||
| 1794 | } | 1838 | } |
| 1795 | } | 1839 | } |
| 1796 | 1840 | ||
| 1841 | void GMainWindow::OnOpenModsPage() { | ||
| 1842 | const auto mods_page_url = QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"); | ||
| 1843 | const QUrl mods_page(mods_page_url); | ||
| 1844 | const bool open = QDesktopServices::openUrl(mods_page); | ||
| 1845 | if (!open) { | ||
| 1846 | QMessageBox::warning(this, tr("Error opening URL"), | ||
| 1847 | tr("Unable to open the URL \"%1\".").arg(mods_page_url)); | ||
| 1848 | } | ||
| 1849 | } | ||
| 1850 | |||
| 1797 | void GMainWindow::ToggleFullscreen() { | 1851 | void GMainWindow::ToggleFullscreen() { |
| 1798 | if (!emulation_running) { | 1852 | if (!emulation_running) { |
| 1799 | return; | 1853 | return; |
| @@ -1905,7 +1959,11 @@ void GMainWindow::OnConfigure() { | |||
| 1905 | } | 1959 | } |
| 1906 | 1960 | ||
| 1907 | dock_status_button->setChecked(Settings::values.use_docked_mode); | 1961 | dock_status_button->setChecked(Settings::values.use_docked_mode); |
| 1962 | multicore_status_button->setChecked(Settings::values.use_multi_core); | ||
| 1963 | Settings::values.use_asynchronous_gpu_emulation = | ||
| 1964 | Settings::values.use_asynchronous_gpu_emulation || Settings::values.use_multi_core; | ||
| 1908 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | 1965 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); |
| 1966 | |||
| 1909 | #ifdef HAS_VULKAN | 1967 | #ifdef HAS_VULKAN |
| 1910 | renderer_status_button->setChecked(Settings::values.renderer_backend == | 1968 | renderer_status_button->setChecked(Settings::values.renderer_backend == |
| 1911 | Settings::RendererBackend::Vulkan); | 1969 | Settings::RendererBackend::Vulkan); |
| @@ -2032,7 +2090,7 @@ void GMainWindow::UpdateStatusBar() { | |||
| 2032 | game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0)); | 2090 | game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0)); |
| 2033 | emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); | 2091 | emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); |
| 2034 | 2092 | ||
| 2035 | emu_speed_label->setVisible(true); | 2093 | emu_speed_label->setVisible(!Settings::values.use_multi_core); |
| 2036 | game_fps_label->setVisible(true); | 2094 | game_fps_label->setVisible(true); |
| 2037 | emu_frametime_label->setVisible(true); | 2095 | emu_frametime_label->setVisible(true); |
| 2038 | } | 2096 | } |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 4f4c8ddbe..5581874ed 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -181,6 +181,7 @@ private slots: | |||
| 181 | void OnPauseGame(); | 181 | void OnPauseGame(); |
| 182 | void OnStopGame(); | 182 | void OnStopGame(); |
| 183 | void OnMenuReportCompatibility(); | 183 | void OnMenuReportCompatibility(); |
| 184 | void OnOpenModsPage(); | ||
| 184 | /// Called whenever a user selects a game in the game list widget. | 185 | /// Called whenever a user selects a game in the game list widget. |
| 185 | void OnGameListLoadFile(QString game_path); | 186 | void OnGameListLoadFile(QString game_path); |
| 186 | void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); | 187 | void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); |
| @@ -234,6 +235,7 @@ private: | |||
| 234 | QLabel* game_fps_label = nullptr; | 235 | QLabel* game_fps_label = nullptr; |
| 235 | QLabel* emu_frametime_label = nullptr; | 236 | QLabel* emu_frametime_label = nullptr; |
| 236 | QPushButton* async_status_button = nullptr; | 237 | QPushButton* async_status_button = nullptr; |
| 238 | QPushButton* multicore_status_button = nullptr; | ||
| 237 | QPushButton* renderer_status_button = nullptr; | 239 | QPushButton* renderer_status_button = nullptr; |
| 238 | QPushButton* dock_status_button = nullptr; | 240 | QPushButton* dock_status_button = nullptr; |
| 239 | QTimer status_bar_update_timer; | 241 | QTimer status_bar_update_timer; |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 97c90f50b..b5745dfd5 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -113,6 +113,7 @@ | |||
| 113 | <string>&Help</string> | 113 | <string>&Help</string> |
| 114 | </property> | 114 | </property> |
| 115 | <addaction name="action_Report_Compatibility"/> | 115 | <addaction name="action_Report_Compatibility"/> |
| 116 | <addaction name="action_Open_Mods_Page"/> | ||
| 116 | <addaction name="separator"/> | 117 | <addaction name="separator"/> |
| 117 | <addaction name="action_About"/> | 118 | <addaction name="action_About"/> |
| 118 | </widget> | 119 | </widget> |
| @@ -256,6 +257,11 @@ | |||
| 256 | <bool>false</bool> | 257 | <bool>false</bool> |
| 257 | </property> | 258 | </property> |
| 258 | </action> | 259 | </action> |
| 260 | <action name="action_Open_Mods_Page"> | ||
| 261 | <property name="text"> | ||
| 262 | <string>Open Mods Page</string> | ||
| 263 | </property> | ||
| 264 | </action> | ||
| 259 | <action name="action_Open_yuzu_Folder"> | 265 | <action name="action_Open_yuzu_Folder"> |
| 260 | <property name="text"> | 266 | <property name="text"> |
| 261 | <string>Open yuzu Folder</string> | 267 | <string>Open yuzu Folder</string> |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 4d2ea7e9e..e6c6a839d 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <chrono> | ||
| 5 | #include <iostream> | 6 | #include <iostream> |
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | #include <string> | 8 | #include <string> |
| @@ -236,9 +237,11 @@ int main(int argc, char** argv) { | |||
| 236 | system.Renderer().Rasterizer().LoadDiskResources(); | 237 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 237 | 238 | ||
| 238 | std::thread render_thread([&emu_window] { emu_window->Present(); }); | 239 | std::thread render_thread([&emu_window] { emu_window->Present(); }); |
| 240 | system.Run(); | ||
| 239 | while (emu_window->IsOpen()) { | 241 | while (emu_window->IsOpen()) { |
| 240 | system.RunLoop(); | 242 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| 241 | } | 243 | } |
| 244 | system.Pause(); | ||
| 242 | render_thread.join(); | 245 | render_thread.join(); |
| 243 | 246 | ||
| 244 | system.Shutdown(); | 247 | system.Shutdown(); |
diff --git a/src/yuzu_tester/service/yuzutest.cpp b/src/yuzu_tester/service/yuzutest.cpp index 85d3f436b..2d3f6e3a7 100644 --- a/src/yuzu_tester/service/yuzutest.cpp +++ b/src/yuzu_tester/service/yuzutest.cpp | |||
| @@ -53,7 +53,7 @@ private: | |||
| 53 | 53 | ||
| 54 | IPC::ResponseBuilder rb{ctx, 3}; | 54 | IPC::ResponseBuilder rb{ctx, 3}; |
| 55 | rb.Push(RESULT_SUCCESS); | 55 | rb.Push(RESULT_SUCCESS); |
| 56 | rb.Push<u32>(write_size); | 56 | rb.Push<u32>(static_cast<u32>(write_size)); |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | void StartIndividual(Kernel::HLERequestContext& ctx) { | 59 | void StartIndividual(Kernel::HLERequestContext& ctx) { |
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index 676e70ebd..083667baf 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <chrono> | ||
| 5 | #include <iostream> | 6 | #include <iostream> |
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | #include <string> | 8 | #include <string> |
| @@ -255,9 +256,11 @@ int main(int argc, char** argv) { | |||
| 255 | system.GPU().Start(); | 256 | system.GPU().Start(); |
| 256 | system.Renderer().Rasterizer().LoadDiskResources(); | 257 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 257 | 258 | ||
| 259 | system.Run(); | ||
| 258 | while (!finished) { | 260 | while (!finished) { |
| 259 | system.RunLoop(); | 261 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| 260 | } | 262 | } |
| 263 | system.Pause(); | ||
| 261 | 264 | ||
| 262 | detached_tasks.WaitForAllTasks(); | 265 | detached_tasks.WaitForAllTasks(); |
| 263 | return return_value; | 266 | return return_value; |