diff options
| -rw-r--r-- | src/audio_core/audio_out.cpp | 3 | ||||
| -rw-r--r-- | src/audio_core/cubeb_sink.cpp | 2 | ||||
| -rw-r--r-- | src/audio_core/cubeb_sink.h | 2 | ||||
| -rw-r--r-- | src/audio_core/null_sink.h | 2 | ||||
| -rw-r--r-- | src/audio_core/sink_details.cpp | 53 | ||||
| -rw-r--r-- | src/audio_core/sink_details.h | 25 | ||||
| -rw-r--r-- | src/common/thread_queue_list.h | 16 | ||||
| -rw-r--r-- | src/core/hle/kernel/process.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/process.h | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/scheduler.cpp | 66 | ||||
| -rw-r--r-- | src/core/hle/kernel/scheduler.h | 69 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 90 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.h | 16 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc_wrap.h | 24 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.cpp | 25 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.h | 152 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_audio.cpp | 7 | ||||
| -rw-r--r-- | src/yuzu/debugger/wait_tree.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/debugger/wait_tree.h | 2 |
20 files changed, 430 insertions, 132 deletions
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp index 0c8f5b18e..cbba17632 100644 --- a/src/audio_core/audio_out.cpp +++ b/src/audio_core/audio_out.cpp | |||
| @@ -30,8 +30,7 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) { | |||
| 30 | StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, | 30 | StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, |
| 31 | Stream::ReleaseCallback&& release_callback) { | 31 | Stream::ReleaseCallback&& release_callback) { |
| 32 | if (!sink) { | 32 | if (!sink) { |
| 33 | const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id); | 33 | sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id); |
| 34 | sink = sink_details.factory(Settings::values.audio_device_id); | ||
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | return std::make_shared<Stream>( | 36 | return std::make_shared<Stream>( |
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index d31a1c844..097328901 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp | |||
| @@ -107,7 +107,7 @@ private: | |||
| 107 | static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state); | 107 | static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state); |
| 108 | }; | 108 | }; |
| 109 | 109 | ||
| 110 | CubebSink::CubebSink(std::string target_device_name) { | 110 | CubebSink::CubebSink(std::string_view target_device_name) { |
| 111 | if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) { | 111 | if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) { |
| 112 | LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); | 112 | LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); |
| 113 | return; | 113 | return; |
diff --git a/src/audio_core/cubeb_sink.h b/src/audio_core/cubeb_sink.h index 59cbf05e9..efb9d1634 100644 --- a/src/audio_core/cubeb_sink.h +++ b/src/audio_core/cubeb_sink.h | |||
| @@ -15,7 +15,7 @@ namespace AudioCore { | |||
| 15 | 15 | ||
| 16 | class CubebSink final : public Sink { | 16 | class CubebSink final : public Sink { |
| 17 | public: | 17 | public: |
| 18 | explicit CubebSink(std::string device_id); | 18 | explicit CubebSink(std::string_view device_id); |
| 19 | ~CubebSink() override; | 19 | ~CubebSink() override; |
| 20 | 20 | ||
| 21 | SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels, | 21 | SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels, |
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h index a78d78893..61a28d542 100644 --- a/src/audio_core/null_sink.h +++ b/src/audio_core/null_sink.h | |||
| @@ -10,7 +10,7 @@ namespace AudioCore { | |||
| 10 | 10 | ||
| 11 | class NullSink final : public Sink { | 11 | class NullSink final : public Sink { |
| 12 | public: | 12 | public: |
| 13 | explicit NullSink(std::string){}; | 13 | explicit NullSink(std::string_view) {} |
| 14 | ~NullSink() override = default; | 14 | ~NullSink() override = default; |
| 15 | 15 | ||
| 16 | SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/, | 16 | SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/, |
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp index 67cf1f3b2..a848eb1c9 100644 --- a/src/audio_core/sink_details.cpp +++ b/src/audio_core/sink_details.cpp | |||
| @@ -14,31 +14,68 @@ | |||
| 14 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 15 | 15 | ||
| 16 | namespace AudioCore { | 16 | namespace AudioCore { |
| 17 | namespace { | ||
| 18 | struct SinkDetails { | ||
| 19 | using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view); | ||
| 20 | using ListDevicesFn = std::vector<std::string> (*)(); | ||
| 17 | 21 | ||
| 18 | // g_sink_details is ordered in terms of desirability, with the best choice at the top. | 22 | /// Name for this sink. |
| 19 | const std::vector<SinkDetails> g_sink_details = { | 23 | const char* id; |
| 24 | /// A method to call to construct an instance of this type of sink. | ||
| 25 | FactoryFn factory; | ||
| 26 | /// A method to call to list available devices. | ||
| 27 | ListDevicesFn list_devices; | ||
| 28 | }; | ||
| 29 | |||
| 30 | // sink_details is ordered in terms of desirability, with the best choice at the top. | ||
| 31 | constexpr SinkDetails sink_details[] = { | ||
| 20 | #ifdef HAVE_CUBEB | 32 | #ifdef HAVE_CUBEB |
| 21 | SinkDetails{"cubeb", &std::make_unique<CubebSink, std::string>, &ListCubebSinkDevices}, | 33 | SinkDetails{"cubeb", |
| 34 | [](std::string_view device_id) -> std::unique_ptr<Sink> { | ||
| 35 | return std::make_unique<CubebSink>(device_id); | ||
| 36 | }, | ||
| 37 | &ListCubebSinkDevices}, | ||
| 22 | #endif | 38 | #endif |
| 23 | SinkDetails{"null", &std::make_unique<NullSink, std::string>, | 39 | SinkDetails{"null", |
| 40 | [](std::string_view device_id) -> std::unique_ptr<Sink> { | ||
| 41 | return std::make_unique<NullSink>(device_id); | ||
| 42 | }, | ||
| 24 | [] { return std::vector<std::string>{"null"}; }}, | 43 | [] { return std::vector<std::string>{"null"}; }}, |
| 25 | }; | 44 | }; |
| 26 | 45 | ||
| 27 | const SinkDetails& GetSinkDetails(std::string_view sink_id) { | 46 | const SinkDetails& GetSinkDetails(std::string_view sink_id) { |
| 28 | auto iter = | 47 | auto iter = |
| 29 | std::find_if(g_sink_details.begin(), g_sink_details.end(), | 48 | std::find_if(std::begin(sink_details), std::end(sink_details), |
| 30 | [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); | 49 | [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); |
| 31 | 50 | ||
| 32 | if (sink_id == "auto" || iter == g_sink_details.end()) { | 51 | if (sink_id == "auto" || iter == std::end(sink_details)) { |
| 33 | if (sink_id != "auto") { | 52 | if (sink_id != "auto") { |
| 34 | LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id {}", sink_id); | 53 | LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id {}", sink_id); |
| 35 | } | 54 | } |
| 36 | // Auto-select. | 55 | // Auto-select. |
| 37 | // g_sink_details is ordered in terms of desirability, with the best choice at the front. | 56 | // sink_details is ordered in terms of desirability, with the best choice at the front. |
| 38 | iter = g_sink_details.begin(); | 57 | iter = std::begin(sink_details); |
| 39 | } | 58 | } |
| 40 | 59 | ||
| 41 | return *iter; | 60 | return *iter; |
| 42 | } | 61 | } |
| 62 | } // Anonymous namespace | ||
| 63 | |||
| 64 | std::vector<const char*> GetSinkIDs() { | ||
| 65 | std::vector<const char*> sink_ids(std::size(sink_details)); | ||
| 66 | |||
| 67 | std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), | ||
| 68 | [](const auto& sink) { return sink.id; }); | ||
| 69 | |||
| 70 | return sink_ids; | ||
| 71 | } | ||
| 72 | |||
| 73 | std::vector<std::string> GetDeviceListForSink(std::string_view sink_id) { | ||
| 74 | return GetSinkDetails(sink_id).list_devices(); | ||
| 75 | } | ||
| 76 | |||
| 77 | std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id) { | ||
| 78 | return GetSinkDetails(sink_id).factory(device_id); | ||
| 79 | } | ||
| 43 | 80 | ||
| 44 | } // namespace AudioCore | 81 | } // namespace AudioCore |
diff --git a/src/audio_core/sink_details.h b/src/audio_core/sink_details.h index 03534b187..bc8786270 100644 --- a/src/audio_core/sink_details.h +++ b/src/audio_core/sink_details.h | |||
| @@ -4,34 +4,21 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <functional> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | 7 | #include <string> |
| 10 | #include <string_view> | 8 | #include <string_view> |
| 11 | #include <utility> | ||
| 12 | #include <vector> | 9 | #include <vector> |
| 13 | 10 | ||
| 14 | namespace AudioCore { | 11 | namespace AudioCore { |
| 15 | 12 | ||
| 16 | class Sink; | 13 | class Sink; |
| 17 | 14 | ||
| 18 | struct SinkDetails { | 15 | /// Retrieves the IDs for all available audio sinks. |
| 19 | using FactoryFn = std::function<std::unique_ptr<Sink>(std::string)>; | 16 | std::vector<const char*> GetSinkIDs(); |
| 20 | using ListDevicesFn = std::function<std::vector<std::string>()>; | ||
| 21 | 17 | ||
| 22 | SinkDetails(const char* id_, FactoryFn factory_, ListDevicesFn list_devices_) | 18 | /// Gets the list of devices for a particular sink identified by the given ID. |
| 23 | : id(id_), factory(std::move(factory_)), list_devices(std::move(list_devices_)) {} | 19 | std::vector<std::string> GetDeviceListForSink(std::string_view sink_id); |
| 24 | 20 | ||
| 25 | /// Name for this sink. | 21 | /// Creates an audio sink identified by the given device ID. |
| 26 | const char* id; | 22 | std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id); |
| 27 | /// A method to call to construct an instance of this type of sink. | ||
| 28 | FactoryFn factory; | ||
| 29 | /// A method to call to list available devices. | ||
| 30 | ListDevicesFn list_devices; | ||
| 31 | }; | ||
| 32 | |||
| 33 | extern const std::vector<SinkDetails> g_sink_details; | ||
| 34 | |||
| 35 | const SinkDetails& GetSinkDetails(std::string_view sink_id); | ||
| 36 | 23 | ||
| 37 | } // namespace AudioCore | 24 | } // namespace AudioCore |
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h index 133122c5f..e7594db68 100644 --- a/src/common/thread_queue_list.h +++ b/src/common/thread_queue_list.h | |||
| @@ -49,6 +49,22 @@ struct ThreadQueueList { | |||
| 49 | return T(); | 49 | return T(); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | template <typename UnaryPredicate> | ||
| 53 | T get_first_filter(UnaryPredicate filter) const { | ||
| 54 | const Queue* cur = first; | ||
| 55 | while (cur != nullptr) { | ||
| 56 | if (!cur->data.empty()) { | ||
| 57 | for (const auto& item : cur->data) { | ||
| 58 | if (filter(item)) | ||
| 59 | return item; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | cur = cur->next_nonempty; | ||
| 63 | } | ||
| 64 | |||
| 65 | return T(); | ||
| 66 | } | ||
| 67 | |||
| 52 | T pop_first() { | 68 | T pop_first() { |
| 53 | Queue* cur = first; | 69 | Queue* cur = first; |
| 54 | while (cur != nullptr) { | 70 | while (cur != nullptr) { |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index c817fb449..5356a4a3f 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -150,7 +150,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { | |||
| 150 | vm_manager | 150 | vm_manager |
| 151 | .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size, | 151 | .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size, |
| 152 | std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, | 152 | std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, |
| 153 | MemoryState::Mapped) | 153 | MemoryState::Stack) |
| 154 | .Unwrap(); | 154 | .Unwrap(); |
| 155 | 155 | ||
| 156 | vm_manager.LogLayout(); | 156 | vm_manager.LogLayout(); |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index bcb9ac4b8..459eedfa6 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -262,8 +262,7 @@ public: | |||
| 262 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); | 262 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); |
| 263 | ResultCode HeapFree(VAddr target, u32 size); | 263 | ResultCode HeapFree(VAddr target, u32 size); |
| 264 | 264 | ||
| 265 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, | 265 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state); |
| 266 | MemoryState state = MemoryState::Mapped); | ||
| 267 | 266 | ||
| 268 | ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); | 267 | ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); |
| 269 | 268 | ||
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 5a5f4cef1..df4d6cf0a 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "core/arm/arm_interface.h" | 10 | #include "core/arm/arm_interface.h" |
| 11 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/core_cpu.h" | ||
| 12 | #include "core/core_timing.h" | 13 | #include "core/core_timing.h" |
| 13 | #include "core/hle/kernel/kernel.h" | 14 | #include "core/hle/kernel/kernel.h" |
| 14 | #include "core/hle/kernel/process.h" | 15 | #include "core/hle/kernel/process.h" |
| @@ -179,4 +180,69 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { | |||
| 179 | ready_queue.prepare(priority); | 180 | ready_queue.prepare(priority); |
| 180 | } | 181 | } |
| 181 | 182 | ||
| 183 | Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const { | ||
| 184 | std::lock_guard<std::mutex> lock(scheduler_mutex); | ||
| 185 | |||
| 186 | const u32 mask = 1U << core; | ||
| 187 | return ready_queue.get_first_filter([mask, maximum_priority](Thread const* thread) { | ||
| 188 | return (thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority; | ||
| 189 | }); | ||
| 190 | } | ||
| 191 | |||
| 192 | void Scheduler::YieldWithoutLoadBalancing(Thread* thread) { | ||
| 193 | ASSERT(thread != nullptr); | ||
| 194 | // Avoid yielding if the thread isn't even running. | ||
| 195 | ASSERT(thread->GetStatus() == ThreadStatus::Running); | ||
| 196 | |||
| 197 | // Sanity check that the priority is valid | ||
| 198 | ASSERT(thread->GetPriority() < THREADPRIO_COUNT); | ||
| 199 | |||
| 200 | // Yield this thread -- sleep for zero time and force reschedule to different thread | ||
| 201 | WaitCurrentThread_Sleep(); | ||
| 202 | GetCurrentThread()->WakeAfterDelay(0); | ||
| 203 | } | ||
| 204 | |||
| 205 | void Scheduler::YieldWithLoadBalancing(Thread* thread) { | ||
| 206 | ASSERT(thread != nullptr); | ||
| 207 | const auto priority = thread->GetPriority(); | ||
| 208 | const auto core = static_cast<u32>(thread->GetProcessorID()); | ||
| 209 | |||
| 210 | // Avoid yielding if the thread isn't even running. | ||
| 211 | ASSERT(thread->GetStatus() == ThreadStatus::Running); | ||
| 212 | |||
| 213 | // Sanity check that the priority is valid | ||
| 214 | ASSERT(priority < THREADPRIO_COUNT); | ||
| 215 | |||
| 216 | // Sleep for zero time to be able to force reschedule to different thread | ||
| 217 | WaitCurrentThread_Sleep(); | ||
| 218 | GetCurrentThread()->WakeAfterDelay(0); | ||
| 219 | |||
| 220 | Thread* suggested_thread = nullptr; | ||
| 221 | |||
| 222 | // Search through all of the cpu cores (except this one) for a suggested thread. | ||
| 223 | // Take the first non-nullptr one | ||
| 224 | for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) { | ||
| 225 | const auto res = | ||
| 226 | Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread( | ||
| 227 | core, priority); | ||
| 228 | |||
| 229 | // If scheduler provides a suggested thread | ||
| 230 | if (res != nullptr) { | ||
| 231 | // And its better than the current suggested thread (or is the first valid one) | ||
| 232 | if (suggested_thread == nullptr || | ||
| 233 | suggested_thread->GetPriority() > res->GetPriority()) { | ||
| 234 | suggested_thread = res; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | // If a suggested thread was found, queue that for this core | ||
| 240 | if (suggested_thread != nullptr) | ||
| 241 | suggested_thread->ChangeCore(core, suggested_thread->GetAffinityMask()); | ||
| 242 | } | ||
| 243 | |||
| 244 | void Scheduler::YieldAndWaitForLoadBalancing(Thread* thread) { | ||
| 245 | UNIMPLEMENTED_MSG("Wait for load balancing thread yield type is not implemented!"); | ||
| 246 | } | ||
| 247 | |||
| 182 | } // namespace Kernel | 248 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index c63032b7d..97ced4dfc 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -51,6 +51,75 @@ public: | |||
| 51 | /// Sets the priority of a thread in the scheduler | 51 | /// Sets the priority of a thread in the scheduler |
| 52 | void SetThreadPriority(Thread* thread, u32 priority); | 52 | void SetThreadPriority(Thread* thread, u32 priority); |
| 53 | 53 | ||
| 54 | /// Gets the next suggested thread for load balancing | ||
| 55 | Thread* GetNextSuggestedThread(u32 core, u32 minimum_priority) const; | ||
| 56 | |||
| 57 | /** | ||
| 58 | * YieldWithoutLoadBalancing -- analogous to normal yield on a system | ||
| 59 | * Moves the thread to the end of the ready queue for its priority, and then reschedules the | ||
| 60 | * system to the new head of the queue. | ||
| 61 | * | ||
| 62 | * Example (Single Core -- but can be extrapolated to multi): | ||
| 63 | * ready_queue[prio=0]: ThreadA, ThreadB, ThreadC (->exec order->) | ||
| 64 | * Currently Running: ThreadR | ||
| 65 | * | ||
| 66 | * ThreadR calls YieldWithoutLoadBalancing | ||
| 67 | * | ||
| 68 | * ThreadR is moved to the end of ready_queue[prio=0]: | ||
| 69 | * ready_queue[prio=0]: ThreadA, ThreadB, ThreadC, ThreadR (->exec order->) | ||
| 70 | * Currently Running: Nothing | ||
| 71 | * | ||
| 72 | * System is rescheduled (ThreadA is popped off of queue): | ||
| 73 | * ready_queue[prio=0]: ThreadB, ThreadC, ThreadR (->exec order->) | ||
| 74 | * Currently Running: ThreadA | ||
| 75 | * | ||
| 76 | * If the queue is empty at time of call, no yielding occurs. This does not cross between cores | ||
| 77 | * or priorities at all. | ||
| 78 | */ | ||
| 79 | void YieldWithoutLoadBalancing(Thread* thread); | ||
| 80 | |||
| 81 | /** | ||
| 82 | * YieldWithLoadBalancing -- yield but with better selection of the new running thread | ||
| 83 | * Moves the current thread to the end of the ready queue for its priority, then selects a | ||
| 84 | * 'suggested thread' (a thread on a different core that could run on this core) from the | ||
| 85 | * scheduler, changes its core, and reschedules the current core to that thread. | ||
| 86 | * | ||
| 87 | * Example (Dual Core -- can be extrapolated to Quad Core, this is just normal yield if it were | ||
| 88 | * single core): | ||
| 89 | * ready_queue[core=0][prio=0]: ThreadA, ThreadB (affinities not pictured as irrelevant | ||
| 90 | * ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only] | ||
| 91 | * Currently Running: ThreadQ on Core 0 || ThreadP on Core 1 | ||
| 92 | * | ||
| 93 | * ThreadQ calls YieldWithLoadBalancing | ||
| 94 | * | ||
| 95 | * ThreadQ is moved to the end of ready_queue[core=0][prio=0]: | ||
| 96 | * ready_queue[core=0][prio=0]: ThreadA, ThreadB | ||
| 97 | * ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only] | ||
| 98 | * Currently Running: ThreadQ on Core 0 || ThreadP on Core 1 | ||
| 99 | * | ||
| 100 | * A list of suggested threads for each core is compiled | ||
| 101 | * Suggested Threads: {ThreadC on Core 1} | ||
| 102 | * If this were quad core (as the switch is), there could be between 0 and 3 threads in this | ||
| 103 | * list. If there are more than one, the thread is selected by highest prio. | ||
| 104 | * | ||
| 105 | * ThreadC is core changed to Core 0: | ||
| 106 | * ready_queue[core=0][prio=0]: ThreadC, ThreadA, ThreadB, ThreadQ | ||
| 107 | * ready_queue[core=1][prio=0]: ThreadD | ||
| 108 | * Currently Running: None on Core 0 || ThreadP on Core 1 | ||
| 109 | * | ||
| 110 | * System is rescheduled (ThreadC is popped off of queue): | ||
| 111 | * ready_queue[core=0][prio=0]: ThreadA, ThreadB, ThreadQ | ||
| 112 | * ready_queue[core=1][prio=0]: ThreadD | ||
| 113 | * Currently Running: ThreadC on Core 0 || ThreadP on Core 1 | ||
| 114 | * | ||
| 115 | * If no suggested threads can be found this will behave just as normal yield. If there are | ||
| 116 | * multiple candidates for the suggested thread on a core, the highest prio is taken. | ||
| 117 | */ | ||
| 118 | void YieldWithLoadBalancing(Thread* thread); | ||
| 119 | |||
| 120 | /// Currently unknown -- asserts as unimplemented on call | ||
| 121 | void YieldAndWaitForLoadBalancing(Thread* thread); | ||
| 122 | |||
| 54 | /// Returns a list of all threads managed by the scheduler | 123 | /// Returns a list of all threads managed by the scheduler |
| 55 | const std::vector<SharedPtr<Thread>>& GetThreadList() const { | 124 | const std::vector<SharedPtr<Thread>>& GetThreadList() const { |
| 56 | return thread_list; | 125 | return thread_list; |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 8b0b3671a..348a22904 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #include "core/hle/lock.h" | 35 | #include "core/hle/lock.h" |
| 36 | #include "core/hle/result.h" | 36 | #include "core/hle/result.h" |
| 37 | #include "core/hle/service/service.h" | 37 | #include "core/hle/service/service.h" |
| 38 | #include "core/memory.h" | ||
| 38 | 39 | ||
| 39 | namespace Kernel { | 40 | namespace Kernel { |
| 40 | namespace { | 41 | namespace { |
| @@ -273,7 +274,7 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 273 | return result; | 274 | return result; |
| 274 | } | 275 | } |
| 275 | 276 | ||
| 276 | return current_process->MirrorMemory(dst_addr, src_addr, size); | 277 | return current_process->MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack); |
| 277 | } | 278 | } |
| 278 | 279 | ||
| 279 | /// Unmaps a region that was previously mapped with svcMapMemory | 280 | /// Unmaps a region that was previously mapped with svcMapMemory |
| @@ -1066,10 +1067,9 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 | |||
| 1066 | return shared_memory->Unmap(*current_process, addr); | 1067 | return shared_memory->Unmap(*current_process, addr); |
| 1067 | } | 1068 | } |
| 1068 | 1069 | ||
| 1069 | /// Query process memory | 1070 | static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address, |
| 1070 | static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, | 1071 | Handle process_handle, VAddr address) { |
| 1071 | Handle process_handle, u64 addr) { | 1072 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); |
| 1072 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} addr={:X}", process_handle, addr); | ||
| 1073 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1073 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1074 | SharedPtr<Process> process = handle_table.Get<Process>(process_handle); | 1074 | SharedPtr<Process> process = handle_table.Get<Process>(process_handle); |
| 1075 | if (!process) { | 1075 | if (!process) { |
| @@ -1079,28 +1079,32 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i | |||
| 1079 | } | 1079 | } |
| 1080 | 1080 | ||
| 1081 | const auto& vm_manager = process->VMManager(); | 1081 | const auto& vm_manager = process->VMManager(); |
| 1082 | const auto vma = vm_manager.FindVMA(addr); | 1082 | const MemoryInfo memory_info = vm_manager.QueryMemory(address); |
| 1083 | 1083 | ||
| 1084 | memory_info->attributes = 0; | 1084 | Memory::Write64(memory_info_address, memory_info.base_address); |
| 1085 | if (vm_manager.IsValidHandle(vma)) { | 1085 | Memory::Write64(memory_info_address + 8, memory_info.size); |
| 1086 | memory_info->base_address = vma->second.base; | 1086 | Memory::Write32(memory_info_address + 16, memory_info.state); |
| 1087 | memory_info->permission = static_cast<u32>(vma->second.permissions); | 1087 | Memory::Write32(memory_info_address + 20, memory_info.attributes); |
| 1088 | memory_info->size = vma->second.size; | 1088 | Memory::Write32(memory_info_address + 24, memory_info.permission); |
| 1089 | memory_info->type = static_cast<u32>(vma->second.meminfo_state); | 1089 | Memory::Write32(memory_info_address + 32, memory_info.ipc_ref_count); |
| 1090 | } else { | 1090 | Memory::Write32(memory_info_address + 28, memory_info.device_ref_count); |
| 1091 | memory_info->base_address = 0; | 1091 | Memory::Write32(memory_info_address + 36, 0); |
| 1092 | memory_info->permission = static_cast<u32>(VMAPermission::None); | 1092 | |
| 1093 | memory_info->size = 0; | 1093 | // Page info appears to be currently unused by the kernel and is always set to zero. |
| 1094 | memory_info->type = static_cast<u32>(MemoryState::Unmapped); | 1094 | Memory::Write32(page_info_address, 0); |
| 1095 | } | ||
| 1096 | 1095 | ||
| 1097 | return RESULT_SUCCESS; | 1096 | return RESULT_SUCCESS; |
| 1098 | } | 1097 | } |
| 1099 | 1098 | ||
| 1100 | /// Query memory | 1099 | static ResultCode QueryMemory(VAddr memory_info_address, VAddr page_info_address, |
| 1101 | static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAddr addr) { | 1100 | VAddr query_address) { |
| 1102 | LOG_TRACE(Kernel_SVC, "called, addr={:X}", addr); | 1101 | LOG_TRACE(Kernel_SVC, |
| 1103 | return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr); | 1102 | "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " |
| 1103 | "query_address=0x{:016X}", | ||
| 1104 | memory_info_address, page_info_address, query_address); | ||
| 1105 | |||
| 1106 | return QueryProcessMemory(memory_info_address, page_info_address, CurrentProcess, | ||
| 1107 | query_address); | ||
| 1104 | } | 1108 | } |
| 1105 | 1109 | ||
| 1106 | /// Exits the current process | 1110 | /// Exits the current process |
| @@ -1204,18 +1208,38 @@ static void ExitThread() { | |||
| 1204 | static void SleepThread(s64 nanoseconds) { | 1208 | static void SleepThread(s64 nanoseconds) { |
| 1205 | LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); | 1209 | LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); |
| 1206 | 1210 | ||
| 1207 | // Don't attempt to yield execution if there are no available threads to run, | 1211 | enum class SleepType : s64 { |
| 1208 | // this way we avoid a useless reschedule to the idle thread. | 1212 | YieldWithoutLoadBalancing = 0, |
| 1209 | if (nanoseconds == 0 && !Core::System::GetInstance().CurrentScheduler().HaveReadyThreads()) | 1213 | YieldWithLoadBalancing = -1, |
| 1210 | return; | 1214 | YieldAndWaitForLoadBalancing = -2, |
| 1215 | }; | ||
| 1211 | 1216 | ||
| 1212 | // Sleep current thread and check for next thread to schedule | 1217 | if (nanoseconds <= 0) { |
| 1213 | WaitCurrentThread_Sleep(); | 1218 | auto& scheduler{Core::System::GetInstance().CurrentScheduler()}; |
| 1219 | switch (static_cast<SleepType>(nanoseconds)) { | ||
| 1220 | case SleepType::YieldWithoutLoadBalancing: | ||
| 1221 | scheduler.YieldWithoutLoadBalancing(GetCurrentThread()); | ||
| 1222 | break; | ||
| 1223 | case SleepType::YieldWithLoadBalancing: | ||
| 1224 | scheduler.YieldWithLoadBalancing(GetCurrentThread()); | ||
| 1225 | break; | ||
| 1226 | case SleepType::YieldAndWaitForLoadBalancing: | ||
| 1227 | scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread()); | ||
| 1228 | break; | ||
| 1229 | default: | ||
| 1230 | UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); | ||
| 1231 | } | ||
| 1232 | } else { | ||
| 1233 | // Sleep current thread and check for next thread to schedule | ||
| 1234 | WaitCurrentThread_Sleep(); | ||
| 1214 | 1235 | ||
| 1215 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 1236 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 1216 | GetCurrentThread()->WakeAfterDelay(nanoseconds); | 1237 | GetCurrentThread()->WakeAfterDelay(nanoseconds); |
| 1238 | } | ||
| 1217 | 1239 | ||
| 1218 | Core::System::GetInstance().PrepareReschedule(); | 1240 | // Reschedule all CPU cores |
| 1241 | for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) | ||
| 1242 | Core::System::GetInstance().CpuCore(i).PrepareReschedule(); | ||
| 1219 | } | 1243 | } |
| 1220 | 1244 | ||
| 1221 | /// Wait process wide key atomic | 1245 | /// Wait process wide key atomic |
| @@ -1907,7 +1931,7 @@ static const FunctionDef SVC_Table[] = { | |||
| 1907 | {0x73, nullptr, "SetProcessMemoryPermission"}, | 1931 | {0x73, nullptr, "SetProcessMemoryPermission"}, |
| 1908 | {0x74, nullptr, "MapProcessMemory"}, | 1932 | {0x74, nullptr, "MapProcessMemory"}, |
| 1909 | {0x75, nullptr, "UnmapProcessMemory"}, | 1933 | {0x75, nullptr, "UnmapProcessMemory"}, |
| 1910 | {0x76, nullptr, "QueryProcessMemory"}, | 1934 | {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, |
| 1911 | {0x77, nullptr, "MapProcessCodeMemory"}, | 1935 | {0x77, nullptr, "MapProcessCodeMemory"}, |
| 1912 | {0x78, nullptr, "UnmapProcessCodeMemory"}, | 1936 | {0x78, nullptr, "UnmapProcessCodeMemory"}, |
| 1913 | {0x79, nullptr, "CreateProcess"}, | 1937 | {0x79, nullptr, "CreateProcess"}, |
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index b06aac4ec..c37ae0f98 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h | |||
| @@ -8,22 +8,6 @@ | |||
| 8 | 8 | ||
| 9 | namespace Kernel { | 9 | namespace Kernel { |
| 10 | 10 | ||
| 11 | struct MemoryInfo { | ||
| 12 | u64 base_address; | ||
| 13 | u64 size; | ||
| 14 | u32 type; | ||
| 15 | u32 attributes; | ||
| 16 | u32 permission; | ||
| 17 | u32 device_refcount; | ||
| 18 | u32 ipc_refcount; | ||
| 19 | INSERT_PADDING_WORDS(1); | ||
| 20 | }; | ||
| 21 | static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size."); | ||
| 22 | |||
| 23 | struct PageInfo { | ||
| 24 | u64 flags; | ||
| 25 | }; | ||
| 26 | |||
| 27 | void CallSVC(u32 immediate); | 11 | void CallSVC(u32 immediate); |
| 28 | 12 | ||
| 29 | } // namespace Kernel | 13 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 24aef46c9..2f758b959 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -7,9 +7,7 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/arm/arm_interface.h" | 8 | #include "core/arm/arm_interface.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/svc.h" | ||
| 11 | #include "core/hle/result.h" | 10 | #include "core/hle/result.h" |
| 12 | #include "core/memory.h" | ||
| 13 | 11 | ||
| 14 | namespace Kernel { | 12 | namespace Kernel { |
| 15 | 13 | ||
| @@ -129,7 +127,12 @@ void SvcWrap() { | |||
| 129 | template <ResultCode func(u64, u64, u32, u32)> | 127 | template <ResultCode func(u64, u64, u32, u32)> |
| 130 | void SvcWrap() { | 128 | void SvcWrap() { |
| 131 | FuncReturn( | 129 | FuncReturn( |
| 132 | func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw); | 130 | func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw); |
| 131 | } | ||
| 132 | |||
| 133 | template <ResultCode func(u64, u64, u32, u64)> | ||
| 134 | void SvcWrap() { | ||
| 135 | FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw); | ||
| 133 | } | 136 | } |
| 134 | 137 | ||
| 135 | template <ResultCode func(u32, u64, u32)> | 138 | template <ResultCode func(u32, u64, u32)> |
| @@ -191,21 +194,6 @@ void SvcWrap() { | |||
| 191 | FuncReturn(retval); | 194 | FuncReturn(retval); |
| 192 | } | 195 | } |
| 193 | 196 | ||
| 194 | template <ResultCode func(MemoryInfo*, PageInfo*, u64)> | ||
| 195 | void SvcWrap() { | ||
| 196 | MemoryInfo memory_info = {}; | ||
| 197 | PageInfo page_info = {}; | ||
| 198 | u32 retval = func(&memory_info, &page_info, Param(2)).raw; | ||
| 199 | |||
| 200 | Memory::Write64(Param(0), memory_info.base_address); | ||
| 201 | Memory::Write64(Param(0) + 8, memory_info.size); | ||
| 202 | Memory::Write32(Param(0) + 16, memory_info.type); | ||
| 203 | Memory::Write32(Param(0) + 20, memory_info.attributes); | ||
| 204 | Memory::Write32(Param(0) + 24, memory_info.permission); | ||
| 205 | |||
| 206 | FuncReturn(retval); | ||
| 207 | } | ||
| 208 | |||
| 209 | template <ResultCode func(u32*, u64, u64, u32)> | 197 | template <ResultCode func(u32*, u64, u64, u32)> |
| 210 | void SvcWrap() { | 198 | void SvcWrap() { |
| 211 | u32 param_1 = 0; | 199 | u32 param_1 = 0; |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index d384d50db..77aec099a 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -26,6 +26,7 @@ enum ThreadPriority : u32 { | |||
| 26 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps | 26 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps |
| 27 | THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps | 27 | THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps |
| 28 | THREADPRIO_LOWEST = 63, ///< Lowest thread priority | 28 | THREADPRIO_LOWEST = 63, ///< Lowest thread priority |
| 29 | THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. | ||
| 29 | }; | 30 | }; |
| 30 | 31 | ||
| 31 | enum ThreadProcessorId : s32 { | 32 | enum ThreadProcessorId : s32 { |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 6187993ce..d3b55a51e 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -25,14 +25,14 @@ static const char* GetMemoryStateName(MemoryState state) { | |||
| 25 | "CodeMutable", "Heap", | 25 | "CodeMutable", "Heap", |
| 26 | "Shared", "Unknown1", | 26 | "Shared", "Unknown1", |
| 27 | "ModuleCodeStatic", "ModuleCodeMutable", | 27 | "ModuleCodeStatic", "ModuleCodeMutable", |
| 28 | "IpcBuffer0", "Mapped", | 28 | "IpcBuffer0", "Stack", |
| 29 | "ThreadLocal", "TransferMemoryIsolated", | 29 | "ThreadLocal", "TransferMemoryIsolated", |
| 30 | "TransferMemory", "ProcessMemory", | 30 | "TransferMemory", "ProcessMemory", |
| 31 | "Unknown2", "IpcBuffer1", | 31 | "Inaccessible", "IpcBuffer1", |
| 32 | "IpcBuffer3", "KernelStack", | 32 | "IpcBuffer3", "KernelStack", |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | return names[static_cast<int>(state)]; | 35 | return names[ToSvcMemoryState(state)]; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { | 38 | bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { |
| @@ -302,6 +302,25 @@ ResultCode VMManager::HeapFree(VAddr target, u64 size) { | |||
| 302 | return RESULT_SUCCESS; | 302 | return RESULT_SUCCESS; |
| 303 | } | 303 | } |
| 304 | 304 | ||
| 305 | MemoryInfo VMManager::QueryMemory(VAddr address) const { | ||
| 306 | const auto vma = FindVMA(address); | ||
| 307 | MemoryInfo memory_info{}; | ||
| 308 | |||
| 309 | if (IsValidHandle(vma)) { | ||
| 310 | memory_info.base_address = vma->second.base; | ||
| 311 | memory_info.permission = static_cast<u32>(vma->second.permissions); | ||
| 312 | memory_info.size = vma->second.size; | ||
| 313 | memory_info.state = ToSvcMemoryState(vma->second.meminfo_state); | ||
| 314 | } else { | ||
| 315 | memory_info.base_address = address_space_end; | ||
| 316 | memory_info.permission = static_cast<u32>(VMAPermission::None); | ||
| 317 | memory_info.size = 0 - address_space_end; | ||
| 318 | memory_info.state = static_cast<u32>(MemoryState::Inaccessible); | ||
| 319 | } | ||
| 320 | |||
| 321 | return memory_info; | ||
| 322 | } | ||
| 323 | |||
| 305 | ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { | 324 | ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { |
| 306 | const auto vma = FindVMA(src_addr); | 325 | const auto vma = FindVMA(src_addr); |
| 307 | 326 | ||
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index a12419d1e..10bacac3e 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -43,26 +43,129 @@ enum class VMAPermission : u8 { | |||
| 43 | ReadWriteExecute = Read | Write | Execute, | 43 | ReadWriteExecute = Read | Write | Execute, |
| 44 | }; | 44 | }; |
| 45 | 45 | ||
| 46 | /// Set of values returned in MemoryInfo.state by svcQueryMemory. | 46 | // clang-format off |
| 47 | /// Represents memory states and any relevant flags, as used by the kernel. | ||
| 48 | /// svcQueryMemory interprets these by masking away all but the first eight | ||
| 49 | /// bits when storing memory state into a MemoryInfo instance. | ||
| 47 | enum class MemoryState : u32 { | 50 | enum class MemoryState : u32 { |
| 48 | Unmapped = 0x0, | 51 | Mask = 0xFF, |
| 49 | Io = 0x1, | 52 | FlagProtect = 1U << 8, |
| 50 | Normal = 0x2, | 53 | FlagDebug = 1U << 9, |
| 51 | CodeStatic = 0x3, | 54 | FlagIPC0 = 1U << 10, |
| 52 | CodeMutable = 0x4, | 55 | FlagIPC3 = 1U << 11, |
| 53 | Heap = 0x5, | 56 | FlagIPC1 = 1U << 12, |
| 54 | Shared = 0x6, | 57 | FlagMapped = 1U << 13, |
| 55 | ModuleCodeStatic = 0x8, | 58 | FlagCode = 1U << 14, |
| 56 | ModuleCodeMutable = 0x9, | 59 | FlagAlias = 1U << 15, |
| 57 | IpcBuffer0 = 0xA, | 60 | FlagModule = 1U << 16, |
| 58 | Mapped = 0xB, | 61 | FlagTransfer = 1U << 17, |
| 59 | ThreadLocal = 0xC, | 62 | FlagQueryPhysicalAddressAllowed = 1U << 18, |
| 60 | TransferMemoryIsolated = 0xD, | 63 | FlagSharedDevice = 1U << 19, |
| 61 | TransferMemory = 0xE, | 64 | FlagSharedDeviceAligned = 1U << 20, |
| 62 | ProcessMemory = 0xF, | 65 | FlagIPCBuffer = 1U << 21, |
| 63 | IpcBuffer1 = 0x11, | 66 | FlagMemoryPoolAllocated = 1U << 22, |
| 64 | IpcBuffer3 = 0x12, | 67 | FlagMapProcess = 1U << 23, |
| 65 | KernelStack = 0x13, | 68 | FlagUncached = 1U << 24, |
| 69 | FlagCodeMemory = 1U << 25, | ||
| 70 | |||
| 71 | // Convenience flag sets to reduce repetition | ||
| 72 | IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, | ||
| 73 | |||
| 74 | CodeFlags = FlagDebug | IPCFlags | FlagMapped | FlagCode | FlagQueryPhysicalAddressAllowed | | ||
| 75 | FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||
| 76 | |||
| 77 | DataFlags = FlagProtect | IPCFlags | FlagMapped | FlagAlias | FlagTransfer | | ||
| 78 | FlagQueryPhysicalAddressAllowed | FlagSharedDevice | FlagSharedDeviceAligned | | ||
| 79 | FlagMemoryPoolAllocated | FlagIPCBuffer | FlagUncached, | ||
| 80 | |||
| 81 | Unmapped = 0x00, | ||
| 82 | Io = 0x01 | FlagMapped, | ||
| 83 | Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed, | ||
| 84 | CodeStatic = 0x03 | CodeFlags | FlagMapProcess, | ||
| 85 | CodeMutable = 0x04 | CodeFlags | FlagMapProcess | FlagCodeMemory, | ||
| 86 | Heap = 0x05 | DataFlags | FlagCodeMemory, | ||
| 87 | Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated, | ||
| 88 | ModuleCodeStatic = 0x08 | CodeFlags | FlagModule | FlagMapProcess, | ||
| 89 | ModuleCodeMutable = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory, | ||
| 90 | |||
| 91 | IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated | | ||
| 92 | IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned, | ||
| 93 | |||
| 94 | Stack = 0x0B | FlagMapped | IPCFlags | FlagQueryPhysicalAddressAllowed | | ||
| 95 | FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||
| 96 | |||
| 97 | ThreadLocal = 0x0C | FlagMapped | FlagMemoryPoolAllocated, | ||
| 98 | |||
| 99 | TransferMemoryIsolated = 0x0D | IPCFlags | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||
| 100 | FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated | | ||
| 101 | FlagUncached, | ||
| 102 | |||
| 103 | TransferMemory = 0x0E | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||
| 104 | FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||
| 105 | |||
| 106 | ProcessMemory = 0x0F | FlagIPC3 | FlagIPC1 | FlagMapped | FlagMemoryPoolAllocated, | ||
| 107 | |||
| 108 | // Used to signify an inaccessible or invalid memory region with memory queries | ||
| 109 | Inaccessible = 0x10, | ||
| 110 | |||
| 111 | IpcBuffer1 = 0x11 | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||
| 112 | FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||
| 113 | |||
| 114 | IpcBuffer3 = 0x12 | FlagIPC3 | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||
| 115 | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||
| 116 | |||
| 117 | KernelStack = 0x13 | FlagMapped, | ||
| 118 | }; | ||
| 119 | // clang-format on | ||
| 120 | |||
| 121 | constexpr MemoryState operator|(MemoryState lhs, MemoryState rhs) { | ||
| 122 | return static_cast<MemoryState>(u32(lhs) | u32(rhs)); | ||
| 123 | } | ||
| 124 | |||
| 125 | constexpr MemoryState operator&(MemoryState lhs, MemoryState rhs) { | ||
| 126 | return static_cast<MemoryState>(u32(lhs) & u32(rhs)); | ||
| 127 | } | ||
| 128 | |||
| 129 | constexpr MemoryState operator^(MemoryState lhs, MemoryState rhs) { | ||
| 130 | return static_cast<MemoryState>(u32(lhs) ^ u32(rhs)); | ||
| 131 | } | ||
| 132 | |||
| 133 | constexpr MemoryState operator~(MemoryState lhs) { | ||
| 134 | return static_cast<MemoryState>(~u32(lhs)); | ||
| 135 | } | ||
| 136 | |||
| 137 | constexpr MemoryState& operator|=(MemoryState& lhs, MemoryState rhs) { | ||
| 138 | lhs = lhs | rhs; | ||
| 139 | return lhs; | ||
| 140 | } | ||
| 141 | |||
| 142 | constexpr MemoryState& operator&=(MemoryState& lhs, MemoryState rhs) { | ||
| 143 | lhs = lhs & rhs; | ||
| 144 | return lhs; | ||
| 145 | } | ||
| 146 | |||
| 147 | constexpr MemoryState& operator^=(MemoryState& lhs, MemoryState rhs) { | ||
| 148 | lhs = lhs ^ rhs; | ||
| 149 | return lhs; | ||
| 150 | } | ||
| 151 | |||
| 152 | constexpr u32 ToSvcMemoryState(MemoryState state) { | ||
| 153 | return static_cast<u32>(state & MemoryState::Mask); | ||
| 154 | } | ||
| 155 | |||
| 156 | struct MemoryInfo { | ||
| 157 | u64 base_address; | ||
| 158 | u64 size; | ||
| 159 | u32 state; | ||
| 160 | u32 attributes; | ||
| 161 | u32 permission; | ||
| 162 | u32 ipc_ref_count; | ||
| 163 | u32 device_ref_count; | ||
| 164 | }; | ||
| 165 | static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size."); | ||
| 166 | |||
| 167 | struct PageInfo { | ||
| 168 | u32 flags; | ||
| 66 | }; | 169 | }; |
| 67 | 170 | ||
| 68 | /** | 171 | /** |
| @@ -186,8 +289,15 @@ public: | |||
| 186 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); | 289 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); |
| 187 | ResultCode HeapFree(VAddr target, u64 size); | 290 | ResultCode HeapFree(VAddr target, u64 size); |
| 188 | 291 | ||
| 189 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, | 292 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state); |
| 190 | MemoryState state = MemoryState::Mapped); | 293 | |
| 294 | /// Queries the memory manager for information about the given address. | ||
| 295 | /// | ||
| 296 | /// @param address The address to query the memory manager about for information. | ||
| 297 | /// | ||
| 298 | /// @return A MemoryInfo instance containing information about the given address. | ||
| 299 | /// | ||
| 300 | MemoryInfo QueryMemory(VAddr address) const; | ||
| 191 | 301 | ||
| 192 | /** | 302 | /** |
| 193 | * Scans all VMAs and updates the page table range of any that use the given vector as backing | 303 | * Scans all VMAs and updates the page table range of any that use the given vector as backing |
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index eb1da0f9e..5d9ccc6e8 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp | |||
| @@ -17,8 +17,8 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) | |||
| 17 | 17 | ||
| 18 | ui->output_sink_combo_box->clear(); | 18 | ui->output_sink_combo_box->clear(); |
| 19 | ui->output_sink_combo_box->addItem("auto"); | 19 | ui->output_sink_combo_box->addItem("auto"); |
| 20 | for (const auto& sink_detail : AudioCore::g_sink_details) { | 20 | for (const char* id : AudioCore::GetSinkIDs()) { |
| 21 | ui->output_sink_combo_box->addItem(sink_detail.id); | 21 | ui->output_sink_combo_box->addItem(id); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | connect(ui->volume_slider, &QSlider::valueChanged, this, | 24 | connect(ui->volume_slider, &QSlider::valueChanged, this, |
| @@ -97,8 +97,7 @@ void ConfigureAudio::updateAudioDevices(int sink_index) { | |||
| 97 | ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); | 97 | ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); |
| 98 | 98 | ||
| 99 | const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); | 99 | const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); |
| 100 | const std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices(); | 100 | for (const auto& device : AudioCore::GetDeviceListForSink(sink_id)) { |
| 101 | for (const auto& device : device_list) { | ||
| 102 | ui->audio_device_combo_box->addItem(QString::fromStdString(device)); | 101 | ui->audio_device_combo_box->addItem(QString::fromStdString(device)); |
| 103 | } | 102 | } |
| 104 | } | 103 | } |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index f9c18ede4..6b3a757e0 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -75,7 +75,7 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() | |||
| 75 | return item_list; | 75 | return item_list; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | WaitTreeText::WaitTreeText(const QString& t) : text(t) {} | 78 | WaitTreeText::WaitTreeText(QString t) : text(std::move(t)) {} |
| 79 | WaitTreeText::~WaitTreeText() = default; | 79 | WaitTreeText::~WaitTreeText() = default; |
| 80 | 80 | ||
| 81 | QString WaitTreeText::GetText() const { | 81 | QString WaitTreeText::GetText() const { |
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index 492fb6ac9..e639ef412 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h | |||
| @@ -52,7 +52,7 @@ private: | |||
| 52 | class WaitTreeText : public WaitTreeItem { | 52 | class WaitTreeText : public WaitTreeItem { |
| 53 | Q_OBJECT | 53 | Q_OBJECT |
| 54 | public: | 54 | public: |
| 55 | explicit WaitTreeText(const QString& text); | 55 | explicit WaitTreeText(QString text); |
| 56 | ~WaitTreeText() override; | 56 | ~WaitTreeText() override; |
| 57 | 57 | ||
| 58 | QString GetText() const override; | 58 | QString GetText() const override; |