summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/audio_renderer.cpp13
-rw-r--r--src/audio_core/audio_renderer.h8
-rw-r--r--src/audio_core/stream.cpp12
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/thread_worker.cpp58
-rw-r--r--src/common/thread_worker.h30
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/core.cpp3
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp37
-rw-r--r--src/core/hle/kernel/hle_ipc.h17
-rw-r--r--src/core/hle/kernel/kernel.cpp113
-rw-r--r--src/core/hle/kernel/kernel.h17
-rw-r--r--src/core/hle/kernel/server_session.cpp29
-rw-r--r--src/core/hle/kernel/server_session.h12
-rw-r--r--src/core/hle/kernel/service_thread.cpp110
-rw-r--r--src/core/hle/kernel/service_thread.h28
-rw-r--r--src/core/hle/service/audio/audout_u.cpp6
-rw-r--r--src/core/hle/service/audio/audren_u.cpp14
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h9
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp9
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp9
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h11
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp9
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp9
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h8
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp92
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h11
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp13
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h6
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp40
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h17
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp34
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h9
-rw-r--r--src/core/hle/service/service.cpp28
-rw-r--r--src/core/hle/service/service.h16
-rw-r--r--src/core/hle/service/sockets/blocking_worker.h161
-rw-r--r--src/core/hle/service/sockets/bsd.cpp124
-rw-r--r--src/core/hle/service/sockets/bsd.h9
-rw-r--r--src/core/hle/service/vi/vi.cpp45
-rw-r--r--src/core/settings.cpp5
-rw-r--r--src/core/settings.h3
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/framebuffer_config.h31
-rw-r--r--src/video_core/gpu.cpp79
-rw-r--r--src/video_core/gpu.h55
-rw-r--r--src/video_core/gpu_asynch.cpp86
-rw-r--r--src/video_core/gpu_asynch.h47
-rw-r--r--src/video_core/gpu_synch.cpp61
-rw-r--r--src/video_core/gpu_synch.h41
-rw-r--r--src/video_core/gpu_thread.cpp36
-rw-r--r--src/video_core/gpu_thread.h10
-rw-r--r--src/video_core/video_core.cpp10
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/main.cpp13
67 files changed, 772 insertions, 1003 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 179560cd7..d2ce8c814 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -11,7 +11,6 @@
11#include "audio_core/info_updater.h" 11#include "audio_core/info_updater.h"
12#include "audio_core/voice_context.h" 12#include "audio_core/voice_context.h"
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "core/hle/kernel/writable_event.h"
15#include "core/memory.h" 14#include "core/memory.h"
16#include "core/settings.h" 15#include "core/settings.h"
17 16
@@ -71,10 +70,9 @@ namespace {
71namespace AudioCore { 70namespace AudioCore {
72AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 71AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
73 AudioCommon::AudioRendererParameter params, 72 AudioCommon::AudioRendererParameter params,
74 std::shared_ptr<Kernel::WritableEvent> buffer_event_, 73 Stream::ReleaseCallback&& release_callback,
75 std::size_t instance_number) 74 std::size_t instance_number)
76 : worker_params{params}, buffer_event{buffer_event_}, 75 : worker_params{params}, memory_pool_info(params.effect_count + params.voice_count * 4),
77 memory_pool_info(params.effect_count + params.voice_count * 4),
78 voice_context(params.voice_count), effect_context(params.effect_count), mix_context(), 76 voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
79 sink_context(params.sink_count), splitter_context(), 77 sink_context(params.sink_count), splitter_context(),
80 voices(params.voice_count), memory{memory_}, 78 voices(params.voice_count), memory{memory_},
@@ -85,10 +83,9 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
85 params.num_splitter_send_channels); 83 params.num_splitter_send_channels);
86 mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count); 84 mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
87 audio_out = std::make_unique<AudioCore::AudioOut>(); 85 audio_out = std::make_unique<AudioCore::AudioOut>();
88 stream = 86 stream = audio_out->OpenStream(
89 audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, 87 core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
90 fmt::format("AudioRenderer-Instance{}", instance_number), 88 fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
91 [=]() { buffer_event_->Signal(); });
92 audio_out->StartStream(stream); 89 audio_out->StartStream(stream);
93 90
94 QueueMixedBuffer(0); 91 QueueMixedBuffer(0);
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 90f7eafa4..18567f618 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -27,10 +27,6 @@ namespace Core::Timing {
27class CoreTiming; 27class CoreTiming;
28} 28}
29 29
30namespace Kernel {
31class WritableEvent;
32}
33
34namespace Core::Memory { 30namespace Core::Memory {
35class Memory; 31class Memory;
36} 32}
@@ -44,8 +40,7 @@ class AudioRenderer {
44public: 40public:
45 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 41 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
46 AudioCommon::AudioRendererParameter params, 42 AudioCommon::AudioRendererParameter params,
47 std::shared_ptr<Kernel::WritableEvent> buffer_event_, 43 Stream::ReleaseCallback&& release_callback, std::size_t instance_number);
48 std::size_t instance_number);
49 ~AudioRenderer(); 44 ~AudioRenderer();
50 45
51 [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, 46 [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
@@ -61,7 +56,6 @@ private:
61 BehaviorInfo behavior_info{}; 56 BehaviorInfo behavior_info{};
62 57
63 AudioCommon::AudioRendererParameter worker_params; 58 AudioCommon::AudioRendererParameter worker_params;
64 std::shared_ptr<Kernel::WritableEvent> buffer_event;
65 std::vector<ServerMemoryPoolInfo> memory_pool_info; 59 std::vector<ServerMemoryPoolInfo> memory_pool_info;
66 VoiceContext voice_context; 60 VoiceContext voice_context;
67 EffectContext effect_context; 61 EffectContext effect_context;
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index eca296589..afe68c9ed 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -130,7 +130,11 @@ bool Stream::ContainsBuffer([[maybe_unused]] Buffer::Tag tag) const {
130std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) { 130std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
131 std::vector<Buffer::Tag> tags; 131 std::vector<Buffer::Tag> tags;
132 for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) { 132 for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
133 tags.push_back(released_buffers.front()->GetTag()); 133 if (released_buffers.front()) {
134 tags.push_back(released_buffers.front()->GetTag());
135 } else {
136 ASSERT_MSG(false, "Invalid tag in released_buffers!");
137 }
134 released_buffers.pop(); 138 released_buffers.pop();
135 } 139 }
136 return tags; 140 return tags;
@@ -140,7 +144,11 @@ std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {
140 std::vector<Buffer::Tag> tags; 144 std::vector<Buffer::Tag> tags;
141 tags.reserve(released_buffers.size()); 145 tags.reserve(released_buffers.size());
142 while (!released_buffers.empty()) { 146 while (!released_buffers.empty()) {
143 tags.push_back(released_buffers.front()->GetTag()); 147 if (released_buffers.front()) {
148 tags.push_back(released_buffers.front()->GetTag());
149 } else {
150 ASSERT_MSG(false, "Invalid tag in released_buffers!");
151 }
144 released_buffers.pop(); 152 released_buffers.pop();
145 } 153 }
146 return tags; 154 return tags;
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 943ff996e..5c8003eb1 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -162,6 +162,8 @@ add_library(common STATIC
162 thread.cpp 162 thread.cpp
163 thread.h 163 thread.h
164 thread_queue_list.h 164 thread_queue_list.h
165 thread_worker.cpp
166 thread_worker.h
165 threadsafe_queue.h 167 threadsafe_queue.h
166 time_zone.cpp 168 time_zone.cpp
167 time_zone.h 169 time_zone.h
diff --git a/src/common/thread_worker.cpp b/src/common/thread_worker.cpp
new file mode 100644
index 000000000..8f9bf447a
--- /dev/null
+++ b/src/common/thread_worker.cpp
@@ -0,0 +1,58 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/thread.h"
6#include "common/thread_worker.h"
7
8namespace Common {
9
10ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) {
11 for (std::size_t i = 0; i < num_workers; ++i)
12 threads.emplace_back([this, thread_name{std::string{name}}] {
13 Common::SetCurrentThreadName(thread_name.c_str());
14
15 // Wait for first request
16 {
17 std::unique_lock lock{queue_mutex};
18 condition.wait(lock, [this] { return stop || !requests.empty(); });
19 }
20
21 while (true) {
22 std::function<void()> task;
23
24 {
25 std::unique_lock lock{queue_mutex};
26 condition.wait(lock, [this] { return stop || !requests.empty(); });
27 if (stop || requests.empty()) {
28 return;
29 }
30 task = std::move(requests.front());
31 requests.pop();
32 }
33
34 task();
35 }
36 });
37}
38
39ThreadWorker::~ThreadWorker() {
40 {
41 std::unique_lock lock{queue_mutex};
42 stop = true;
43 }
44 condition.notify_all();
45 for (std::thread& thread : threads) {
46 thread.join();
47 }
48}
49
50void ThreadWorker::QueueWork(std::function<void()>&& work) {
51 {
52 std::unique_lock lock{queue_mutex};
53 requests.emplace(work);
54 }
55 condition.notify_one();
56}
57
58} // namespace Common
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h
new file mode 100644
index 000000000..f1859971f
--- /dev/null
+++ b/src/common/thread_worker.h
@@ -0,0 +1,30 @@
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 <atomic>
8#include <functional>
9#include <mutex>
10#include <string>
11#include <vector>
12#include <queue>
13
14namespace Common {
15
16class ThreadWorker final {
17public:
18 explicit ThreadWorker(std::size_t num_workers, const std::string& name);
19 ~ThreadWorker();
20 void QueueWork(std::function<void()>&& work);
21
22private:
23 std::vector<std::thread> threads;
24 std::queue<std::function<void()>> requests;
25 std::mutex queue_mutex;
26 std::condition_variable condition;
27 std::atomic_bool stop{};
28};
29
30} // namespace Common
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 59bd3d2a6..01f3e9419 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -202,6 +202,8 @@ add_library(core STATIC
202 hle/kernel/server_port.h 202 hle/kernel/server_port.h
203 hle/kernel/server_session.cpp 203 hle/kernel/server_session.cpp
204 hle/kernel/server_session.h 204 hle/kernel/server_session.h
205 hle/kernel/service_thread.cpp
206 hle/kernel/service_thread.h
205 hle/kernel/session.cpp 207 hle/kernel/session.cpp
206 hle/kernel/session.h 208 hle/kernel/session.h
207 hle/kernel/shared_memory.cpp 209 hle/kernel/shared_memory.cpp
@@ -500,7 +502,6 @@ add_library(core STATIC
500 hle/service/sm/controller.h 502 hle/service/sm/controller.h
501 hle/service/sm/sm.cpp 503 hle/service/sm/sm.cpp
502 hle/service/sm/sm.h 504 hle/service/sm/sm.h
503 hle/service/sockets/blocking_worker.h
504 hle/service/sockets/bsd.cpp 505 hle/service/sockets/bsd.cpp
505 hle/service/sockets/bsd.h 506 hle/service/sockets/bsd.h
506 hle/service/sockets/ethc.cpp 507 hle/service/sockets/ethc.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 0961c0819..1a2002dec 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -159,7 +159,7 @@ struct System::Impl {
159 device_memory = std::make_unique<Core::DeviceMemory>(); 159 device_memory = std::make_unique<Core::DeviceMemory>();
160 160
161 is_multicore = Settings::values.use_multi_core.GetValue(); 161 is_multicore = Settings::values.use_multi_core.GetValue();
162 is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue(); 162 is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
163 163
164 kernel.SetMulticore(is_multicore); 164 kernel.SetMulticore(is_multicore);
165 cpu_manager.SetMulticore(is_multicore); 165 cpu_manager.SetMulticore(is_multicore);
@@ -307,7 +307,6 @@ struct System::Impl {
307 service_manager.reset(); 307 service_manager.reset();
308 cheat_engine.reset(); 308 cheat_engine.reset();
309 telemetry_session.reset(); 309 telemetry_session.reset();
310 device_memory.reset();
311 310
312 // Close all CPU/threading state 311 // Close all CPU/threading state
313 cpu_manager.Shutdown(); 312 cpu_manager.Shutdown();
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index e75e80ad0..83decf6cf 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -46,43 +46,6 @@ void SessionRequestHandler::ClientDisconnected(
46 boost::range::remove_erase(connected_sessions, server_session); 46 boost::range::remove_erase(connected_sessions, server_session);
47} 47}
48 48
49std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
50 const std::string& reason, u64 timeout, WakeupCallback&& callback,
51 std::shared_ptr<WritableEvent> writable_event) {
52 // Put the client thread to sleep until the wait event is signaled or the timeout expires.
53
54 if (!writable_event) {
55 // Create event if not provided
56 const auto pair = WritableEvent::CreateEventPair(kernel, "HLE Pause Event: " + reason);
57 writable_event = pair.writable;
58 }
59
60 Handle event_handle = InvalidHandle;
61 {
62 KScopedSchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout);
63 thread->SetHLECallback(
64 [context = *this, callback](std::shared_ptr<Thread> thread) mutable -> bool {
65 ThreadWakeupReason reason = thread->GetSignalingResult() == RESULT_TIMEOUT
66 ? ThreadWakeupReason::Timeout
67 : ThreadWakeupReason::Signal;
68 callback(thread, context, reason);
69 context.WriteToOutgoingCommandBuffer(*thread);
70 return true;
71 });
72 const auto readable_event{writable_event->GetReadableEvent()};
73 writable_event->Clear();
74 thread->SetHLESyncObject(readable_event.get());
75 thread->SetStatus(ThreadStatus::WaitHLEEvent);
76 thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
77 readable_event->AddWaitingThread(thread);
78 }
79 thread->SetHLETimeEvent(event_handle);
80
81 is_thread_waiting = true;
82
83 return writable_event;
84}
85
86HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory, 49HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
87 std::shared_ptr<ServerSession> server_session, 50 std::shared_ptr<ServerSession> server_session,
88 std::shared_ptr<Thread> thread) 51 std::shared_ptr<Thread> thread)
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index c31a65476..b112e1ebd 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -129,23 +129,6 @@ public:
129 using WakeupCallback = std::function<void( 129 using WakeupCallback = std::function<void(
130 std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>; 130 std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
131 131
132 /**
133 * Puts the specified guest thread to sleep until the returned event is signaled or until the
134 * specified timeout expires.
135 * @param reason Reason for pausing the thread, to be used for debugging purposes.
136 * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
137 * invoked with a Timeout reason.
138 * @param callback Callback to be invoked when the thread is resumed. This callback must write
139 * the entire command response once again, regardless of the state of it before this function
140 * was called.
141 * @param writable_event Event to use to wake up the thread. If unspecified, an event will be
142 * created.
143 * @returns Event that when signaled will resume the thread and call the callback function.
144 */
145 std::shared_ptr<WritableEvent> SleepClientThread(
146 const std::string& reason, u64 timeout, WakeupCallback&& callback,
147 std::shared_ptr<WritableEvent> writable_event = nullptr);
148
149 /// Populates this context with data from the requesting process/thread. 132 /// Populates this context with data from the requesting process/thread.
150 ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, 133 ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
151 u32_le* src_cmdbuf); 134 u32_le* src_cmdbuf);
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 04cae3a43..e8ece8164 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -8,13 +8,14 @@
8#include <functional> 8#include <functional>
9#include <memory> 9#include <memory>
10#include <thread> 10#include <thread>
11#include <unordered_map> 11#include <unordered_set>
12#include <utility> 12#include <utility>
13 13
14#include "common/assert.h" 14#include "common/assert.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/microprofile.h" 16#include "common/microprofile.h"
17#include "common/thread.h" 17#include "common/thread.h"
18#include "common/thread_worker.h"
18#include "core/arm/arm_interface.h" 19#include "core/arm/arm_interface.h"
19#include "core/arm/cpu_interrupt_handler.h" 20#include "core/arm/cpu_interrupt_handler.h"
20#include "core/arm/exclusive_monitor.h" 21#include "core/arm/exclusive_monitor.h"
@@ -35,6 +36,7 @@
35#include "core/hle/kernel/physical_core.h" 36#include "core/hle/kernel/physical_core.h"
36#include "core/hle/kernel/process.h" 37#include "core/hle/kernel/process.h"
37#include "core/hle/kernel/resource_limit.h" 38#include "core/hle/kernel/resource_limit.h"
39#include "core/hle/kernel/service_thread.h"
38#include "core/hle/kernel/shared_memory.h" 40#include "core/hle/kernel/shared_memory.h"
39#include "core/hle/kernel/synchronization.h" 41#include "core/hle/kernel/synchronization.h"
40#include "core/hle/kernel/thread.h" 42#include "core/hle/kernel/thread.h"
@@ -60,6 +62,8 @@ struct KernelCore::Impl {
60 RegisterHostThread(); 62 RegisterHostThread();
61 63
62 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 64 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
65 service_thread_manager =
66 std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
63 67
64 InitializePhysicalCores(); 68 InitializePhysicalCores();
65 InitializeSystemResourceLimit(kernel); 69 InitializeSystemResourceLimit(kernel);
@@ -76,6 +80,12 @@ struct KernelCore::Impl {
76 } 80 }
77 81
78 void Shutdown() { 82 void Shutdown() {
83 process_list.clear();
84
85 // Ensures all service threads gracefully shutdown
86 service_thread_manager.reset();
87 service_threads.clear();
88
79 next_object_id = 0; 89 next_object_id = 0;
80 next_kernel_process_id = Process::InitialKIPIDMin; 90 next_kernel_process_id = Process::InitialKIPIDMin;
81 next_user_process_id = Process::ProcessIDMin; 91 next_user_process_id = Process::ProcessIDMin;
@@ -89,8 +99,6 @@ struct KernelCore::Impl {
89 99
90 cores.clear(); 100 cores.clear();
91 101
92 process_list.clear();
93
94 current_process = nullptr; 102 current_process = nullptr;
95 103
96 system_resource_limit = nullptr; 104 system_resource_limit = nullptr;
@@ -103,10 +111,8 @@ struct KernelCore::Impl {
103 111
104 exclusive_monitor.reset(); 112 exclusive_monitor.reset();
105 113
106 num_host_threads = 0; 114 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
107 std::fill(register_host_thread_keys.begin(), register_host_thread_keys.end(), 115 next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
108 std::thread::id{});
109 std::fill(register_host_thread_values.begin(), register_host_thread_values.end(), 0);
110 } 116 }
111 117
112 void InitializePhysicalCores() { 118 void InitializePhysicalCores() {
@@ -186,52 +192,46 @@ struct KernelCore::Impl {
186 } 192 }
187 } 193 }
188 194
195 /// Creates a new host thread ID, should only be called by GetHostThreadId
196 u32 AllocateHostThreadId(std::optional<std::size_t> core_id) {
197 if (core_id) {
198 // The first for slots are reserved for CPU core threads
199 ASSERT(*core_id < Core::Hardware::NUM_CPU_CORES);
200 return static_cast<u32>(*core_id);
201 } else {
202 return next_host_thread_id++;
203 }
204 }
205
206 /// Gets the host thread ID for the caller, allocating a new one if this is the first time
207 u32 GetHostThreadId(std::optional<std::size_t> core_id = std::nullopt) {
208 const thread_local auto host_thread_id{AllocateHostThreadId(core_id)};
209 return host_thread_id;
210 }
211
212 /// Registers a CPU core thread by allocating a host thread ID for it
189 void RegisterCoreThread(std::size_t core_id) { 213 void RegisterCoreThread(std::size_t core_id) {
190 const std::thread::id this_id = std::this_thread::get_id(); 214 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
215 const auto this_id = GetHostThreadId(core_id);
191 if (!is_multicore) { 216 if (!is_multicore) {
192 single_core_thread_id = this_id; 217 single_core_thread_id = this_id;
193 } 218 }
194 const auto end =
195 register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
196 const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
197 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
198 ASSERT(it == end);
199 InsertHostThread(static_cast<u32>(core_id));
200 } 219 }
201 220
221 /// Registers a new host thread by allocating a host thread ID for it
202 void RegisterHostThread() { 222 void RegisterHostThread() {
203 const std::thread::id this_id = std::this_thread::get_id(); 223 [[maybe_unused]] const auto this_id = GetHostThreadId();
204 const auto end =
205 register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
206 const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
207 if (it == end) {
208 InsertHostThread(registered_thread_ids++);
209 }
210 }
211
212 void InsertHostThread(u32 value) {
213 const size_t index = num_host_threads++;
214 ASSERT_MSG(index < NUM_REGISTRABLE_HOST_THREADS, "Too many host threads");
215 register_host_thread_values[index] = value;
216 register_host_thread_keys[index] = std::this_thread::get_id();
217 } 224 }
218 225
219 [[nodiscard]] u32 GetCurrentHostThreadID() const { 226 [[nodiscard]] u32 GetCurrentHostThreadID() {
220 const std::thread::id this_id = std::this_thread::get_id(); 227 const auto this_id = GetHostThreadId();
221 if (!is_multicore && single_core_thread_id == this_id) { 228 if (!is_multicore && single_core_thread_id == this_id) {
222 return static_cast<u32>(system.GetCpuManager().CurrentCore()); 229 return static_cast<u32>(system.GetCpuManager().CurrentCore());
223 } 230 }
224 const auto end = 231 return this_id;
225 register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
226 const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
227 if (it == end) {
228 return Core::INVALID_HOST_THREAD_ID;
229 }
230 return register_host_thread_values[static_cast<size_t>(
231 std::distance(register_host_thread_keys.begin(), it))];
232 } 232 }
233 233
234 Core::EmuThreadHandle GetCurrentEmuThreadID() const { 234 [[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() {
235 Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle(); 235 Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();
236 result.host_handle = GetCurrentHostThreadID(); 236 result.host_handle = GetCurrentHostThreadID();
237 if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) { 237 if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) {
@@ -325,15 +325,8 @@ struct KernelCore::Impl {
325 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; 325 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
326 std::vector<Kernel::PhysicalCore> cores; 326 std::vector<Kernel::PhysicalCore> cores;
327 327
328 // 0-3 IDs represent core threads, >3 represent others 328 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
329 std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; 329 std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
330
331 // Number of host threads is a relatively high number to avoid overflowing
332 static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64;
333 std::atomic<size_t> num_host_threads{0};
334 std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS>
335 register_host_thread_keys{};
336 std::array<std::atomic<u32>, NUM_REGISTRABLE_HOST_THREADS> register_host_thread_values{};
337 330
338 // Kernel memory management 331 // Kernel memory management
339 std::unique_ptr<Memory::MemoryManager> memory_manager; 332 std::unique_ptr<Memory::MemoryManager> memory_manager;
@@ -345,12 +338,19 @@ struct KernelCore::Impl {
345 std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; 338 std::shared_ptr<Kernel::SharedMemory> irs_shared_mem;
346 std::shared_ptr<Kernel::SharedMemory> time_shared_mem; 339 std::shared_ptr<Kernel::SharedMemory> time_shared_mem;
347 340
341 // Threads used for services
342 std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
343
344 // Service threads are managed by a worker thread, so that a calling service thread can queue up
345 // the release of itself
346 std::unique_ptr<Common::ThreadWorker> service_thread_manager;
347
348 std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; 348 std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
349 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; 349 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
350 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; 350 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
351 351
352 bool is_multicore{}; 352 bool is_multicore{};
353 std::thread::id single_core_thread_id{}; 353 u32 single_core_thread_id{};
354 354
355 std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; 355 std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
356 356
@@ -639,4 +639,19 @@ void KernelCore::ExitSVCProfile() {
639 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]); 639 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
640} 640}
641 641
642std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
643 auto service_thread = std::make_shared<Kernel::ServiceThread>(*this, 1, name);
644 impl->service_thread_manager->QueueWork(
645 [this, service_thread] { impl->service_threads.emplace(service_thread); });
646 return service_thread;
647}
648
649void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
650 impl->service_thread_manager->QueueWork([this, service_thread] {
651 if (auto strong_ptr = service_thread.lock()) {
652 impl->service_threads.erase(strong_ptr);
653 }
654 });
655}
656
642} // namespace Kernel 657} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 5846c3f39..e3169f5a7 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -42,6 +42,7 @@ class Process;
42class ResourceLimit; 42class ResourceLimit;
43class KScheduler; 43class KScheduler;
44class SharedMemory; 44class SharedMemory;
45class ServiceThread;
45class Synchronization; 46class Synchronization;
46class Thread; 47class Thread;
47class TimeManager; 48class TimeManager;
@@ -227,6 +228,22 @@ public:
227 228
228 void ExitSVCProfile(); 229 void ExitSVCProfile();
229 230
231 /**
232 * Creates an HLE service thread, which are used to execute service routines asynchronously.
233 * While these are allocated per ServerSession, these need to be owned and managed outside of
234 * ServerSession to avoid a circular dependency.
235 * @param name String name for the ServerSession creating this thread, used for debug purposes.
236 * @returns The a weak pointer newly created service thread.
237 */
238 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
239
240 /**
241 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when
242 * the ServerSession associated with the thread is destroyed.
243 * @param service_thread Service thread to release.
244 */
245 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread);
246
230private: 247private:
231 friend class Object; 248 friend class Object;
232 friend class Process; 249 friend class Process;
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index a35c8aa4b..b40fe3916 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -25,19 +25,19 @@
25namespace Kernel { 25namespace Kernel {
26 26
27ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} 27ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {}
28ServerSession::~ServerSession() = default; 28
29ServerSession::~ServerSession() {
30 kernel.ReleaseServiceThread(service_thread);
31}
29 32
30ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel, 33ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel,
31 std::shared_ptr<Session> parent, 34 std::shared_ptr<Session> parent,
32 std::string name) { 35 std::string name) {
33 std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; 36 std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
34 37
35 session->request_event =
36 Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) {
37 session->CompleteSyncRequest();
38 });
39 session->name = std::move(name); 38 session->name = std::move(name);
40 session->parent = std::move(parent); 39 session->parent = std::move(parent);
40 session->service_thread = kernel.CreateServiceThread(session->name);
41 41
42 return MakeResult(std::move(session)); 42 return MakeResult(std::move(session));
43} 43}
@@ -142,16 +142,16 @@ ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,
142 std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread)); 142 std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread));
143 143
144 context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); 144 context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
145 request_queue.Push(std::move(context)); 145
146 if (auto strong_ptr = service_thread.lock()) {
147 strong_ptr->QueueSyncRequest(*this, std::move(context));
148 return RESULT_SUCCESS;
149 }
146 150
147 return RESULT_SUCCESS; 151 return RESULT_SUCCESS;
148} 152}
149 153
150ResultCode ServerSession::CompleteSyncRequest() { 154ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
151 ASSERT(!request_queue.Empty());
152
153 auto& context = *request_queue.Front();
154
155 ResultCode result = RESULT_SUCCESS; 155 ResultCode result = RESULT_SUCCESS;
156 // If the session has been converted to a domain, handle the domain request 156 // If the session has been converted to a domain, handle the domain request
157 if (IsDomain() && context.HasDomainMessageHeader()) { 157 if (IsDomain() && context.HasDomainMessageHeader()) {
@@ -177,18 +177,13 @@ ResultCode ServerSession::CompleteSyncRequest() {
177 } 177 }
178 } 178 }
179 179
180 request_queue.Pop();
181
182 return result; 180 return result;
183} 181}
184 182
185ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, 183ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
186 Core::Memory::Memory& memory, 184 Core::Memory::Memory& memory,
187 Core::Timing::CoreTiming& core_timing) { 185 Core::Timing::CoreTiming& core_timing) {
188 const ResultCode result = QueueSyncRequest(std::move(thread), memory); 186 return QueueSyncRequest(std::move(thread), memory);
189 const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000};
190 core_timing.ScheduleEvent(delay, request_event, {});
191 return result;
192} 187}
193 188
194} // namespace Kernel 189} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index d23e9ec68..e8d1d99ea 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -10,6 +10,7 @@
10#include <vector> 10#include <vector>
11 11
12#include "common/threadsafe_queue.h" 12#include "common/threadsafe_queue.h"
13#include "core/hle/kernel/service_thread.h"
13#include "core/hle/kernel/synchronization_object.h" 14#include "core/hle/kernel/synchronization_object.h"
14#include "core/hle/result.h" 15#include "core/hle/result.h"
15 16
@@ -43,6 +44,8 @@ class Thread;
43 * TLS buffer and control is transferred back to it. 44 * TLS buffer and control is transferred back to it.
44 */ 45 */
45class ServerSession final : public SynchronizationObject { 46class ServerSession final : public SynchronizationObject {
47 friend class ServiceThread;
48
46public: 49public:
47 explicit ServerSession(KernelCore& kernel); 50 explicit ServerSession(KernelCore& kernel);
48 ~ServerSession() override; 51 ~ServerSession() override;
@@ -132,7 +135,7 @@ private:
132 ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); 135 ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
133 136
134 /// Completes a sync request from the emulated application. 137 /// Completes a sync request from the emulated application.
135 ResultCode CompleteSyncRequest(); 138 ResultCode CompleteSyncRequest(HLERequestContext& context);
136 139
137 /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an 140 /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
138 /// object handle. 141 /// object handle.
@@ -163,11 +166,8 @@ private:
163 /// The name of this session (optional) 166 /// The name of this session (optional)
164 std::string name; 167 std::string name;
165 168
166 /// Core timing event used to schedule the service request at some point in the future 169 /// Thread to dispatch service requests
167 std::shared_ptr<Core::Timing::EventType> request_event; 170 std::weak_ptr<ServiceThread> service_thread;
168
169 /// Queue of scheduled service requests
170 Common::MPSCQueue<std::shared_ptr<Kernel::HLERequestContext>> request_queue;
171}; 171};
172 172
173} // namespace Kernel 173} // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
new file mode 100644
index 000000000..ee46f3e21
--- /dev/null
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -0,0 +1,110 @@
1// Copyright 2020 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 <functional>
7#include <mutex>
8#include <thread>
9#include <vector>
10#include <queue>
11
12#include "common/assert.h"
13#include "common/scope_exit.h"
14#include "common/thread.h"
15#include "core/core.h"
16#include "core/hle/kernel/kernel.h"
17#include "core/hle/kernel/server_session.h"
18#include "core/hle/kernel/service_thread.h"
19#include "core/hle/lock.h"
20#include "video_core/renderer_base.h"
21
22namespace Kernel {
23
24class ServiceThread::Impl final {
25public:
26 explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name);
27 ~Impl();
28
29 void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
30
31private:
32 std::vector<std::thread> threads;
33 std::queue<std::function<void()>> requests;
34 std::mutex queue_mutex;
35 std::condition_variable condition;
36 const std::string service_name;
37 bool stop{};
38};
39
40ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
41 : service_name{name} {
42 for (std::size_t i = 0; i < num_threads; ++i)
43 threads.emplace_back([this, &kernel] {
44 Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
45
46 // Wait for first request before trying to acquire a render context
47 {
48 std::unique_lock lock{queue_mutex};
49 condition.wait(lock, [this] { return stop || !requests.empty(); });
50 }
51
52 kernel.RegisterHostThread();
53
54 while (true) {
55 std::function<void()> task;
56
57 {
58 std::unique_lock lock{queue_mutex};
59 condition.wait(lock, [this] { return stop || !requests.empty(); });
60 if (stop || requests.empty()) {
61 return;
62 }
63 task = std::move(requests.front());
64 requests.pop();
65 }
66
67 task();
68 }
69 });
70}
71
72void ServiceThread::Impl::QueueSyncRequest(ServerSession& session,
73 std::shared_ptr<HLERequestContext>&& context) {
74 {
75 std::unique_lock lock{queue_mutex};
76
77 // ServerSession owns the service thread, so we cannot caption a strong pointer here in the
78 // event that the ServerSession is terminated.
79 std::weak_ptr<ServerSession> weak_ptr{SharedFrom(&session)};
80 requests.emplace([weak_ptr, context{std::move(context)}]() {
81 if (auto strong_ptr = weak_ptr.lock()) {
82 strong_ptr->CompleteSyncRequest(*context);
83 }
84 });
85 }
86 condition.notify_one();
87}
88
89ServiceThread::Impl::~Impl() {
90 {
91 std::unique_lock lock{queue_mutex};
92 stop = true;
93 }
94 condition.notify_all();
95 for (std::thread& thread : threads) {
96 thread.join();
97 }
98}
99
100ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name)
101 : impl{std::make_unique<Impl>(kernel, num_threads, name)} {}
102
103ServiceThread::~ServiceThread() = default;
104
105void ServiceThread::QueueSyncRequest(ServerSession& session,
106 std::shared_ptr<HLERequestContext>&& context) {
107 impl->QueueSyncRequest(session, std::move(context));
108}
109
110} // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
new file mode 100644
index 000000000..025ab8fb5
--- /dev/null
+++ b/src/core/hle/kernel/service_thread.h
@@ -0,0 +1,28 @@
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 <string>
9
10namespace Kernel {
11
12class HLERequestContext;
13class KernelCore;
14class ServerSession;
15
16class ServiceThread final {
17public:
18 explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name);
19 ~ServiceThread();
20
21 void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
22
23private:
24 class Impl;
25 std::unique_ptr<Impl> impl;
26};
27
28} // namespace Kernel
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 145f47ee2..0cd797109 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -70,8 +70,10 @@ public:
70 Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased"); 70 Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased");
71 71
72 stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, 72 stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
73 audio_params.channel_count, std::move(unique_name), 73 audio_params.channel_count, std::move(unique_name), [this] {
74 [this] { buffer_event.writable->Signal(); }); 74 const auto guard = LockService();
75 buffer_event.writable->Signal();
76 });
75 } 77 }
76 78
77private: 79private:
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6e7b7316c..c5c22d053 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -49,16 +49,16 @@ public:
49 49
50 system_event = 50 system_event =
51 Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent"); 51 Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent");
52 renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), system.Memory(), 52 renderer = std::make_unique<AudioCore::AudioRenderer>(
53 audren_params, system_event.writable, 53 system.CoreTiming(), system.Memory(), audren_params,
54 instance_number); 54 [this]() {
55 const auto guard = LockService();
56 system_event.writable->Signal();
57 },
58 instance_number);
55 } 59 }
56 60
57private: 61private:
58 void UpdateAudioCallback() {
59 system_event.writable->Signal();
60 }
61
62 void GetSampleRate(Kernel::HLERequestContext& ctx) { 62 void GetSampleRate(Kernel::HLERequestContext& ctx) {
63 LOG_DEBUG(Service_Audio, "called"); 63 LOG_DEBUG(Service_Audio, "called");
64 64
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index b3c7234e1..8d95f74e6 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -78,11 +78,13 @@ IAppletResource::IAppletResource(Core::System& system_)
78 pad_update_event = Core::Timing::CreateEvent( 78 pad_update_event = Core::Timing::CreateEvent(
79 "HID::UpdatePadCallback", 79 "HID::UpdatePadCallback",
80 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 80 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
81 const auto guard = LockService();
81 UpdateControllers(user_data, ns_late); 82 UpdateControllers(user_data, ns_late);
82 }); 83 });
83 motion_update_event = Core::Timing::CreateEvent( 84 motion_update_event = Core::Timing::CreateEvent(
84 "HID::MotionPadCallback", 85 "HID::MotionPadCallback",
85 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 86 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
87 const auto guard = LockService();
86 UpdateMotion(user_data, ns_late); 88 UpdateMotion(user_data, ns_late);
87 }); 89 });
88 90
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 44a8bc060..5681599ba 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -31,8 +31,8 @@ public:
31 * @param output A buffer where the output data will be written to. 31 * @param output A buffer where the output data will be written to.
32 * @returns The result code of the ioctl. 32 * @returns The result code of the ioctl.
33 */ 33 */
34 virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 34 virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
35 IoctlCtrl& ctrl) = 0; 35 std::vector<u8>& output) = 0;
36 36
37 /** 37 /**
38 * Handles an ioctl2 request. 38 * Handles an ioctl2 request.
@@ -43,8 +43,7 @@ public:
43 * @returns The result code of the ioctl. 43 * @returns The result code of the ioctl.
44 */ 44 */
45 virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, 45 virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
46 const std::vector<u8>& inline_input, std::vector<u8>& output, 46 const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
47 IoctlCtrl& ctrl) = 0;
48 47
49 /** 48 /**
50 * Handles an ioctl3 request. 49 * Handles an ioctl3 request.
@@ -55,7 +54,7 @@ public:
55 * @returns The result code of the ioctl. 54 * @returns The result code of the ioctl.
56 */ 55 */
57 virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 56 virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
58 std::vector<u8>& inline_output, IoctlCtrl& ctrl) = 0; 57 std::vector<u8>& inline_output) = 0;
59 58
60protected: 59protected:
61 Core::System& system; 60 Core::System& system;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 170a7c9a0..ce615c758 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -18,21 +18,20 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
18 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 18 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
19nvdisp_disp0 ::~nvdisp_disp0() = default; 19nvdisp_disp0 ::~nvdisp_disp0() = default;
20 20
21NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 21NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input,
22 IoctlCtrl& ctrl) { 22 std::vector<u8>& output) {
23 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 23 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
24 return NvResult::NotImplemented; 24 return NvResult::NotImplemented;
25} 25}
26 26
27NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input, 27NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input,
28 const std::vector<u8>& inline_input, std::vector<u8>& output, 28 const std::vector<u8>& inline_input, std::vector<u8>& output) {
29 IoctlCtrl& ctrl) {
30 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 29 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
31 return NvResult::NotImplemented; 30 return NvResult::NotImplemented;
32} 31}
33 32
34NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 33NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
35 std::vector<u8>& inline_output, IoctlCtrl& ctrl) { 34 std::vector<u8>& inline_output) {
36 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 35 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
37 return NvResult::NotImplemented; 36 return NvResult::NotImplemented;
38} 37}
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index eb7575e40..55a33b7e4 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -20,13 +20,11 @@ public:
20 explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 20 explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
21 ~nvdisp_disp0() override; 21 ~nvdisp_disp0() override;
22 22
23 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 23 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
24 IoctlCtrl& ctrl) override;
25 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, 24 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
26 const std::vector<u8>& inline_input, std::vector<u8>& output, 25 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
27 IoctlCtrl& ctrl) override;
28 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 26 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
29 std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; 27 std::vector<u8>& inline_output) override;
30 28
31 /// Performs a screen flip, drawing the buffer pointed to by the handle. 29 /// Performs a screen flip, drawing the buffer pointed to by the handle.
32 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, 30 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 4e0652c39..6b062e10e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -21,8 +21,8 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_
21 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 21 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
22nvhost_as_gpu::~nvhost_as_gpu() = default; 22nvhost_as_gpu::~nvhost_as_gpu() = default;
23 23
24NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 24NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
25 IoctlCtrl& ctrl) { 25 std::vector<u8>& output) {
26 switch (command.group) { 26 switch (command.group) {
27 case 'A': 27 case 'A':
28 switch (command.cmd) { 28 switch (command.cmd) {
@@ -55,14 +55,13 @@ NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std:
55} 55}
56 56
57NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, 57NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
58 const std::vector<u8>& inline_input, std::vector<u8>& output, 58 const std::vector<u8>& inline_input, std::vector<u8>& output) {
59 IoctlCtrl& ctrl) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented; 60 return NvResult::NotImplemented;
62} 61}
63 62
64NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 63NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
65 std::vector<u8>& inline_output, IoctlCtrl& ctrl) { 64 std::vector<u8>& inline_output) {
66 switch (command.group) { 65 switch (command.group) {
67 case 'A': 66 case 'A':
68 switch (command.cmd) { 67 switch (command.cmd) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 2bd355af9..08035fa0e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -30,13 +30,11 @@ public:
30 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 30 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
31 ~nvhost_as_gpu() override; 31 ~nvhost_as_gpu() override;
32 32
33 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 33 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
34 IoctlCtrl& ctrl) override;
35 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, 34 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
36 const std::vector<u8>& inline_input, std::vector<u8>& output, 35 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
37 IoctlCtrl& ctrl) override;
38 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 36 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
39 std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; 37 std::vector<u8>& inline_output) override;
40 38
41private: 39private:
42 class BufferMap final { 40 class BufferMap final {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 92d31b620..fea3b7b9f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -20,8 +20,7 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
20 : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {} 20 : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
21nvhost_ctrl::~nvhost_ctrl() = default; 21nvhost_ctrl::~nvhost_ctrl() = default;
22 22
23NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 23NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
24 IoctlCtrl& ctrl) {
25 switch (command.group) { 24 switch (command.group) {
26 case 0x0: 25 case 0x0:
27 switch (command.cmd) { 26 switch (command.cmd) {
@@ -30,9 +29,9 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v
30 case 0x1c: 29 case 0x1c:
31 return IocCtrlClearEventWait(input, output); 30 return IocCtrlClearEventWait(input, output);
32 case 0x1d: 31 case 0x1d:
33 return IocCtrlEventWait(input, output, false, ctrl); 32 return IocCtrlEventWait(input, output, false);
34 case 0x1e: 33 case 0x1e:
35 return IocCtrlEventWait(input, output, true, ctrl); 34 return IocCtrlEventWait(input, output, true);
36 case 0x1f: 35 case 0x1f:
37 return IocCtrlEventRegister(input, output); 36 return IocCtrlEventRegister(input, output);
38 case 0x20: 37 case 0x20:
@@ -48,14 +47,13 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v
48} 47}
49 48
50NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input, 49NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input,
51 const std::vector<u8>& inline_input, std::vector<u8>& output, 50 const std::vector<u8>& inline_input, std::vector<u8>& output) {
52 IoctlCtrl& ctrl) {
53 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 51 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
54 return NvResult::NotImplemented; 52 return NvResult::NotImplemented;
55} 53}
56 54
57NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 55NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
58 std::vector<u8>& inline_output, IoctlCtrl& ctrl) { 56 std::vector<u8>& inline_outpu) {
59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 57 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
60 return NvResult::NotImplemented; 58 return NvResult::NotImplemented;
61} 59}
@@ -69,7 +67,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector
69} 67}
70 68
71NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 69NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
72 bool is_async, IoctlCtrl& ctrl) { 70 bool is_async) {
73 IocCtrlEventWaitParams params{}; 71 IocCtrlEventWaitParams params{};
74 std::memcpy(&params, input.data(), sizeof(params)); 72 std::memcpy(&params, input.data(), sizeof(params));
75 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", 73 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
@@ -141,12 +139,6 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
141 params.value |= event_id; 139 params.value |= event_id;
142 event.event.writable->Clear(); 140 event.event.writable->Clear();
143 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); 141 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
144 if (!is_async && ctrl.fresh_call) {
145 ctrl.must_delay = true;
146 ctrl.timeout = params.timeout;
147 ctrl.event_id = event_id;
148 return NvResult::Timeout;
149 }
150 std::memcpy(output.data(), &params, sizeof(params)); 142 std::memcpy(output.data(), &params, sizeof(params));
151 return NvResult::Timeout; 143 return NvResult::Timeout;
152 } 144 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 107168e21..c5aa1362a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -18,13 +18,11 @@ public:
18 SyncpointManager& syncpoint_manager); 18 SyncpointManager& syncpoint_manager);
19 ~nvhost_ctrl() override; 19 ~nvhost_ctrl() override;
20 20
21 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 21 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
22 IoctlCtrl& ctrl) override;
23 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, 22 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
24 const std::vector<u8>& inline_input, std::vector<u8>& output, 23 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
25 IoctlCtrl& ctrl) override;
26 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 24 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
27 std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; 25 std::vector<u8>& inline_output) override;
28 26
29private: 27private:
30 struct IocSyncptReadParams { 28 struct IocSyncptReadParams {
@@ -123,8 +121,7 @@ private:
123 static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); 121 static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
124 122
125 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); 123 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
126 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async, 124 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
127 IoctlCtrl& ctrl);
128 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); 125 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
129 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); 126 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
130 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); 127 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
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 647f5907e..0320d3ae2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -16,7 +16,7 @@ nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; 16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
17 17
18NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, 18NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
19 std::vector<u8>& output, IoctlCtrl& ctrl) { 19 std::vector<u8>& output) {
20 switch (command.group) { 20 switch (command.group) {
21 case 'G': 21 case 'G':
22 switch (command.cmd) { 22 switch (command.cmd) {
@@ -48,15 +48,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
48} 48}
49 49
50NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, 50NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
51 const std::vector<u8>& inline_input, std::vector<u8>& output, 51 const std::vector<u8>& inline_input, std::vector<u8>& output) {
52 IoctlCtrl& ctrl) {
53 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 52 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
54 return NvResult::NotImplemented; 53 return NvResult::NotImplemented;
55} 54}
56 55
57NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, 56NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input,
58 std::vector<u8>& output, std::vector<u8>& inline_output, 57 std::vector<u8>& output, std::vector<u8>& inline_output) {
59 IoctlCtrl& ctrl) {
60 switch (command.group) { 58 switch (command.group) {
61 case 'G': 59 case 'G':
62 switch (command.cmd) { 60 switch (command.cmd) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index c2fffe734..137b88238 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -16,13 +16,11 @@ public:
16 explicit nvhost_ctrl_gpu(Core::System& system); 16 explicit nvhost_ctrl_gpu(Core::System& system);
17 ~nvhost_ctrl_gpu() override; 17 ~nvhost_ctrl_gpu() override;
18 18
19 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 19 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
20 IoctlCtrl& ctrl) override;
21 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, 20 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
22 const std::vector<u8>& inline_input, std::vector<u8>& output, 21 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
23 IoctlCtrl& ctrl) override;
24 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 22 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
25 std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; 23 std::vector<u8>& inline_output) override;
26 24
27private: 25private:
28 struct IoctlGpuCharacteristics { 26 struct IoctlGpuCharacteristics {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index b0c2caba5..af8b3d9f1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -23,8 +23,7 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
23 23
24nvhost_gpu::~nvhost_gpu() = default; 24nvhost_gpu::~nvhost_gpu() = default;
25 25
26NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 26NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
27 IoctlCtrl& ctrl) {
28 switch (command.group) { 27 switch (command.group) {
29 case 0x0: 28 case 0x0:
30 switch (command.cmd) { 29 switch (command.cmd) {
@@ -76,8 +75,7 @@ NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve
76}; 75};
77 76
78NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, 77NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
79 const std::vector<u8>& inline_input, std::vector<u8>& output, 78 const std::vector<u8>& inline_input, std::vector<u8>& output) {
80 IoctlCtrl& ctrl) {
81 switch (command.group) { 79 switch (command.group) {
82 case 'H': 80 case 'H':
83 switch (command.cmd) { 81 switch (command.cmd) {
@@ -91,7 +89,7 @@ NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
91} 89}
92 90
93NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 91NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
94 std::vector<u8>& inline_output, IoctlCtrl& ctrl) { 92 std::vector<u8>& inline_output) {
95 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 93 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
96 return NvResult::NotImplemented; 94 return NvResult::NotImplemented;
97} 95}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index aa0048a9d..e0298b4fe 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -26,13 +26,11 @@ public:
26 SyncpointManager& syncpoint_manager); 26 SyncpointManager& syncpoint_manager);
27 ~nvhost_gpu() override; 27 ~nvhost_gpu() override;
28 28
29 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 29 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
30 IoctlCtrl& ctrl) override;
31 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, 30 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
32 const std::vector<u8>& inline_input, std::vector<u8>& output, 31 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
33 IoctlCtrl& ctrl) override;
34 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 32 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
35 std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; 33 std::vector<u8>& inline_output) override;
36 34
37private: 35private:
38 enum class CtxObjects : u32_le { 36 enum class CtxObjects : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index b8328c314..d8735491c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -15,8 +15,8 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_de
15 : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} 15 : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
16nvhost_nvdec::~nvhost_nvdec() = default; 16nvhost_nvdec::~nvhost_nvdec() = default;
17 17
18NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 18NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
19 IoctlCtrl& ctrl) { 19 std::vector<u8>& output) {
20 switch (command.group) { 20 switch (command.group) {
21 case 0x0: 21 case 0x0:
22 switch (command.cmd) { 22 switch (command.cmd) {
@@ -58,14 +58,13 @@ NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, std::
58} 58}
59 59
60NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input, 60NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input,
61 const std::vector<u8>& inline_input, std::vector<u8>& output, 61 const std::vector<u8>& inline_input, std::vector<u8>& output) {
62 IoctlCtrl& ctrl) {
63 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 62 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
64 return NvResult::NotImplemented; 63 return NvResult::NotImplemented;
65} 64}
66 65
67NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 66NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
68 std::vector<u8>& inline_output, IoctlCtrl& ctrl) { 67 std::vector<u8>& inline_output) {
69 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 68 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
70 return NvResult::NotImplemented; 69 return NvResult::NotImplemented;
71} 70}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 884ed6c5b..79b8b6de1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -14,13 +14,11 @@ public:
14 explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 14 explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
15 ~nvhost_nvdec() override; 15 ~nvhost_nvdec() override;
16 16
17 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 17 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
18 IoctlCtrl& ctrl) override;
19 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, 18 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
20 const std::vector<u8>& inline_input, std::vector<u8>& output, 19 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
21 IoctlCtrl& ctrl) override;
22 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 20 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
23 std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; 21 std::vector<u8>& inline_output) override;
24}; 22};
25 23
26} // namespace Service::Nvidia::Devices 24} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 6f4ab0ab3..2d06955c0 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices {
13nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} 13nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
14nvhost_nvjpg::~nvhost_nvjpg() = default; 14nvhost_nvjpg::~nvhost_nvjpg() = default;
15 15
16NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 16NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input,
17 IoctlCtrl& ctrl) { 17 std::vector<u8>& output) {
18 switch (command.group) { 18 switch (command.group) {
19 case 'H': 19 case 'H':
20 switch (command.cmd) { 20 switch (command.cmd) {
@@ -33,14 +33,13 @@ NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, std::
33} 33}
34 34
35NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input, 35NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input,
36 const std::vector<u8>& inline_input, std::vector<u8>& output, 36 const std::vector<u8>& inline_input, std::vector<u8>& output) {
37 IoctlCtrl& ctrl) {
38 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 37 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
39 return NvResult::NotImplemented; 38 return NvResult::NotImplemented;
40} 39}
41 40
42NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 41NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
43 std::vector<u8>& inline_output, IoctlCtrl& ctrl) { 42 std::vector<u8>& inline_output) {
44 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 43 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
45 return NvResult::NotImplemented; 44 return NvResult::NotImplemented;
46} 45}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 6fb99d959..43948d18d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -16,13 +16,11 @@ public:
16 explicit nvhost_nvjpg(Core::System& system); 16 explicit nvhost_nvjpg(Core::System& system);
17 ~nvhost_nvjpg() override; 17 ~nvhost_nvjpg() override;
18 18
19 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 19 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
20 IoctlCtrl& ctrl) override;
21 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, 20 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
22 const std::vector<u8>& inline_input, std::vector<u8>& output, 21 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
23 IoctlCtrl& ctrl) override;
24 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 22 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
25 std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; 23 std::vector<u8>& inline_output) override;
26 24
27private: 25private:
28 struct IoctlSetNvmapFD { 26 struct IoctlSetNvmapFD {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 55a17f423..805fe86ae 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -15,8 +15,7 @@ nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
15 15
16nvhost_vic::~nvhost_vic() = default; 16nvhost_vic::~nvhost_vic() = default;
17 17
18NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 18NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
19 IoctlCtrl& ctrl) {
20 switch (command.group) { 19 switch (command.group) {
21 case 0x0: 20 case 0x0:
22 switch (command.cmd) { 21 switch (command.cmd) {
@@ -51,14 +50,13 @@ NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve
51} 50}
52 51
53NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input, 52NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input,
54 const std::vector<u8>& inline_input, std::vector<u8>& output, 53 const std::vector<u8>& inline_input, std::vector<u8>& output) {
55 IoctlCtrl& ctrl) {
56 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 54 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
57 return NvResult::NotImplemented; 55 return NvResult::NotImplemented;
58} 56}
59 57
60NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 58NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
61 std::vector<u8>& inline_output, IoctlCtrl& ctrl) { 59 std::vector<u8>& inline_output) {
62 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
63 return NvResult::NotImplemented; 61 return NvResult::NotImplemented;
64} 62}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index 7f4858cd4..b2e11f4d4 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -14,12 +14,10 @@ public:
14 explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 14 explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
15 ~nvhost_vic(); 15 ~nvhost_vic();
16 16
17 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 17 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
18 IoctlCtrl& ctrl) override;
19 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, 18 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
20 const std::vector<u8>& inline_input, std::vector<u8>& output, 19 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
21 IoctlCtrl& ctrl) override;
22 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 20 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
23 std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; 21 std::vector<u8>& inline_output) override;
24}; 22};
25} // namespace Service::Nvidia::Devices 23} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 910cfee51..4015a2740 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -19,8 +19,7 @@ nvmap::nvmap(Core::System& system) : nvdevice(system) {
19 19
20nvmap::~nvmap() = default; 20nvmap::~nvmap() = default;
21 21
22NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 22NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
23 IoctlCtrl& ctrl) {
24 switch (command.group) { 23 switch (command.group) {
25 case 0x1: 24 case 0x1:
26 switch (command.cmd) { 25 switch (command.cmd) {
@@ -49,14 +48,13 @@ NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<
49} 48}
50 49
51NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input, 50NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input,
52 const std::vector<u8>& inline_input, std::vector<u8>& output, 51 const std::vector<u8>& inline_input, std::vector<u8>& output) {
53 IoctlCtrl& ctrl) {
54 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 52 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
55 return NvResult::NotImplemented; 53 return NvResult::NotImplemented;
56} 54}
57 55
58NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 56NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
59 std::vector<u8>& inline_output, IoctlCtrl& ctrl) { 57 std::vector<u8>& inline_output) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 58 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented; 59 return NvResult::NotImplemented;
62} 60}
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index c0c2fa5eb..4484bd79f 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -19,13 +19,11 @@ public:
19 explicit nvmap(Core::System& system); 19 explicit nvmap(Core::System& system);
20 ~nvmap() override; 20 ~nvmap() override;
21 21
22 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 22 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
23 IoctlCtrl& ctrl) override;
24 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, 23 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
25 const std::vector<u8>& inline_input, std::vector<u8>& output, 24 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
26 IoctlCtrl& ctrl) override;
27 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 25 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
28 std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; 26 std::vector<u8>& inline_output) override;
29 27
30 /// Returns the allocated address of an nvmap object given its handle. 28 /// Returns the allocated address of an nvmap object given its handle.
31 VAddr GetObjectAddress(u32 handle) const; 29 VAddr GetObjectAddress(u32 handle) const;
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index d72c531f6..cc23b001c 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -61,32 +61,9 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
61 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); 61 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
62 const auto input_buffer = ctx.ReadBuffer(0); 62 const auto input_buffer = ctx.ReadBuffer(0);
63 63
64 IoctlCtrl ctrl{}; 64 const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer);
65 65 if (command.is_out != 0) {
66 const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer, ctrl); 66 ctx.WriteBuffer(output_buffer);
67 if (ctrl.must_delay) {
68 ctrl.fresh_call = false;
69 ctx.SleepClientThread(
70 "NVServices::DelayedResponse", ctrl.timeout,
71 [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
72 Kernel::ThreadWakeupReason reason) {
73 IoctlCtrl ctrl2{ctrl};
74 std::vector<u8> tmp_output = output_buffer;
75 const auto nv_result2 = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output, ctrl2);
76
77 if (command.is_out != 0) {
78 ctx.WriteBuffer(tmp_output);
79 }
80
81 IPC::ResponseBuilder rb{ctx_, 3};
82 rb.Push(RESULT_SUCCESS);
83 rb.PushEnum(nv_result2);
84 },
85 nvdrv->GetEventWriteable(ctrl.event_id));
86 } else {
87 if (command.is_out != 0) {
88 ctx.WriteBuffer(output_buffer);
89 }
90 } 67 }
91 68
92 IPC::ResponseBuilder rb{ctx, 3}; 69 IPC::ResponseBuilder rb{ctx, 3};
@@ -110,36 +87,8 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
110 const auto input_inlined_buffer = ctx.ReadBuffer(1); 87 const auto input_inlined_buffer = ctx.ReadBuffer(1);
111 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); 88 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
112 89
113 IoctlCtrl ctrl{};
114
115 const auto nv_result = 90 const auto nv_result =
116 nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer, ctrl); 91 nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);
117 if (ctrl.must_delay) {
118 ctrl.fresh_call = false;
119 ctx.SleepClientThread(
120 "NVServices::DelayedResponse", ctrl.timeout,
121 [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
122 Kernel::ThreadWakeupReason reason) {
123 IoctlCtrl ctrl2{ctrl};
124 std::vector<u8> tmp_output = output_buffer;
125 const auto nv_result2 = nvdrv->Ioctl2(fd, command, input_buffer,
126 input_inlined_buffer, tmp_output, ctrl2);
127
128 if (command.is_out != 0) {
129 ctx.WriteBuffer(tmp_output);
130 }
131
132 IPC::ResponseBuilder rb{ctx_, 3};
133 rb.Push(RESULT_SUCCESS);
134 rb.PushEnum(nv_result2);
135 },
136 nvdrv->GetEventWriteable(ctrl.event_id));
137 } else {
138 if (command.is_out != 0) {
139 ctx.WriteBuffer(output_buffer);
140 }
141 }
142
143 if (command.is_out != 0) { 92 if (command.is_out != 0) {
144 ctx.WriteBuffer(output_buffer); 93 ctx.WriteBuffer(output_buffer);
145 } 94 }
@@ -165,36 +114,11 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
165 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); 114 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
166 std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1)); 115 std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1));
167 116
168 IoctlCtrl ctrl{};
169 const auto nv_result = 117 const auto nv_result =
170 nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline, ctrl); 118 nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
171 if (ctrl.must_delay) { 119 if (command.is_out != 0) {
172 ctrl.fresh_call = false; 120 ctx.WriteBuffer(output_buffer, 0);
173 ctx.SleepClientThread( 121 ctx.WriteBuffer(output_buffer_inline, 1);
174 "NVServices::DelayedResponse", ctrl.timeout,
175 [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
176 Kernel::ThreadWakeupReason reason) {
177 IoctlCtrl ctrl2{ctrl};
178 std::vector<u8> tmp_output = output_buffer;
179 std::vector<u8> tmp_output2 = output_buffer;
180 const auto nv_result2 =
181 nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output2, ctrl2);
182
183 if (command.is_out != 0) {
184 ctx.WriteBuffer(tmp_output, 0);
185 ctx.WriteBuffer(tmp_output2, 1);
186 }
187
188 IPC::ResponseBuilder rb{ctx_, 3};
189 rb.Push(RESULT_SUCCESS);
190 rb.PushEnum(nv_result2);
191 },
192 nvdrv->GetEventWriteable(ctrl.event_id));
193 } else {
194 if (command.is_out != 0) {
195 ctx.WriteBuffer(output_buffer, 0);
196 ctx.WriteBuffer(output_buffer_inline, 1);
197 }
198 } 122 }
199 123
200 IPC::ResponseBuilder rb{ctx, 3}; 124 IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index a3c4ecd85..3294bc0e7 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -97,15 +97,4 @@ union Ioctl {
97 BitField<31, 1, u32> is_out; 97 BitField<31, 1, u32> is_out;
98}; 98};
99 99
100struct IoctlCtrl {
101 // First call done to the servioce for services that call itself again after a call.
102 bool fresh_call{true};
103 // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
104 bool must_delay{};
105 // Timeout for the delay
106 s64 timeout{};
107 // NV Event Id
108 s32 event_id{-1};
109};
110
111} // namespace Service::Nvidia 100} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 8e0c9f093..e03195afe 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -91,7 +91,7 @@ DeviceFD Module::Open(const std::string& device_name) {
91} 91}
92 92
93NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 93NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
94 std::vector<u8>& output, IoctlCtrl& ctrl) { 94 std::vector<u8>& output) {
95 if (fd < 0) { 95 if (fd < 0) {
96 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 96 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
97 return NvResult::InvalidState; 97 return NvResult::InvalidState;
@@ -104,12 +104,11 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input
104 return NvResult::NotImplemented; 104 return NvResult::NotImplemented;
105 } 105 }
106 106
107 return itr->second->Ioctl1(command, input, output, ctrl); 107 return itr->second->Ioctl1(command, input, output);
108} 108}
109 109
110NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 110NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
111 const std::vector<u8>& inline_input, std::vector<u8>& output, 111 const std::vector<u8>& inline_input, std::vector<u8>& output) {
112 IoctlCtrl& ctrl) {
113 if (fd < 0) { 112 if (fd < 0) {
114 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 113 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
115 return NvResult::InvalidState; 114 return NvResult::InvalidState;
@@ -122,11 +121,11 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input
122 return NvResult::NotImplemented; 121 return NvResult::NotImplemented;
123 } 122 }
124 123
125 return itr->second->Ioctl2(command, input, inline_input, output, ctrl); 124 return itr->second->Ioctl2(command, input, inline_input, output);
126} 125}
127 126
128NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 127NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
129 std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl) { 128 std::vector<u8>& output, std::vector<u8>& inline_output) {
130 if (fd < 0) { 129 if (fd < 0) {
131 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 130 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
132 return NvResult::InvalidState; 131 return NvResult::InvalidState;
@@ -139,7 +138,7 @@ NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input
139 return NvResult::NotImplemented; 138 return NvResult::NotImplemented;
140 } 139 }
141 140
142 return itr->second->Ioctl3(command, input, output, inline_output, ctrl); 141 return itr->second->Ioctl3(command, input, output, inline_output);
143} 142}
144 143
145NvResult Module::Close(DeviceFD fd) { 144NvResult Module::Close(DeviceFD fd) {
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 5985d2179..144e657e5 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -119,13 +119,13 @@ public:
119 119
120 /// Sends an ioctl command to the specified file descriptor. 120 /// Sends an ioctl command to the specified file descriptor.
121 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 121 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
122 std::vector<u8>& output, IoctlCtrl& ctrl); 122 std::vector<u8>& output);
123 123
124 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 124 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
125 const std::vector<u8>& inline_input, std::vector<u8>& output, IoctlCtrl& ctrl); 125 const std::vector<u8>& inline_input, std::vector<u8>& output);
126 126
127 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 127 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
128 std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl); 128 std::vector<u8>& output, std::vector<u8>& inline_output);
129 129
130 /// Closes a device file descriptor and returns operation success. 130 /// Closes a device file descriptor and returns operation success.
131 NvResult Close(DeviceFD fd); 131 NvResult Close(DeviceFD fd);
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 377f47e8e..c8c6a4d64 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -25,7 +25,12 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
25 ASSERT(slot < buffer_slots); 25 ASSERT(slot < buffer_slots);
26 LOG_WARNING(Service, "Adding graphics buffer {}", slot); 26 LOG_WARNING(Service, "Adding graphics buffer {}", slot);
27 27
28 free_buffers.push_back(slot); 28 {
29 std::unique_lock lock{queue_mutex};
30 free_buffers.push_back(slot);
31 }
32 condition.notify_one();
33
29 buffers[slot] = { 34 buffers[slot] = {
30 .slot = slot, 35 .slot = slot,
31 .status = Buffer::Status::Free, 36 .status = Buffer::Status::Free,
@@ -41,10 +46,20 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
41 46
42std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, 47std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
43 u32 height) { 48 u32 height) {
49 // Wait for first request before trying to dequeue
50 {
51 std::unique_lock lock{queue_mutex};
52 condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
53 }
44 54
45 if (free_buffers.empty()) { 55 if (!is_connect) {
56 // Buffer was disconnected while the thread was blocked, this is most likely due to
57 // emulation being stopped
46 return std::nullopt; 58 return std::nullopt;
47 } 59 }
60
61 std::unique_lock lock{queue_mutex};
62
48 auto f_itr = free_buffers.begin(); 63 auto f_itr = free_buffers.begin();
49 auto slot = buffers.size(); 64 auto slot = buffers.size();
50 65
@@ -97,7 +112,11 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
97 buffers[slot].multi_fence = multi_fence; 112 buffers[slot].multi_fence = multi_fence;
98 buffers[slot].swap_interval = 0; 113 buffers[slot].swap_interval = 0;
99 114
100 free_buffers.push_back(slot); 115 {
116 std::unique_lock lock{queue_mutex};
117 free_buffers.push_back(slot);
118 }
119 condition.notify_one();
101 120
102 buffer_wait_event.writable->Signal(); 121 buffer_wait_event.writable->Signal();
103} 122}
@@ -127,15 +146,28 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
127 ASSERT(buffers[slot].slot == slot); 146 ASSERT(buffers[slot].slot == slot);
128 147
129 buffers[slot].status = Buffer::Status::Free; 148 buffers[slot].status = Buffer::Status::Free;
130 free_buffers.push_back(slot); 149 {
150 std::unique_lock lock{queue_mutex};
151 free_buffers.push_back(slot);
152 }
153 condition.notify_one();
131 154
132 buffer_wait_event.writable->Signal(); 155 buffer_wait_event.writable->Signal();
133} 156}
134 157
158void BufferQueue::Connect() {
159 queue_sequence.clear();
160 id = 1;
161 layer_id = 1;
162 is_connect = true;
163}
164
135void BufferQueue::Disconnect() { 165void BufferQueue::Disconnect() {
136 buffers.fill({}); 166 buffers.fill({});
137 queue_sequence.clear(); 167 queue_sequence.clear();
138 buffer_wait_event.writable->Signal(); 168 buffer_wait_event.writable->Signal();
169 is_connect = false;
170 condition.notify_one();
139} 171}
140 172
141u32 BufferQueue::Query(QueryType type) { 173u32 BufferQueue::Query(QueryType type) {
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index e610923cb..a2f60d9eb 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -4,7 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <condition_variable>
7#include <list> 8#include <list>
9#include <mutex>
8#include <optional> 10#include <optional>
9#include <vector> 11#include <vector>
10 12
@@ -99,6 +101,7 @@ public:
99 void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence); 101 void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
100 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); 102 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
101 void ReleaseBuffer(u32 slot); 103 void ReleaseBuffer(u32 slot);
104 void Connect();
102 void Disconnect(); 105 void Disconnect();
103 u32 Query(QueryType type); 106 u32 Query(QueryType type);
104 107
@@ -106,18 +109,28 @@ public:
106 return id; 109 return id;
107 } 110 }
108 111
112 bool IsConnected() const {
113 return is_connect;
114 }
115
109 std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const; 116 std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const;
110 117
111 std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const; 118 std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const;
112 119
113private: 120private:
114 u32 id; 121 BufferQueue(const BufferQueue&) = delete;
115 u64 layer_id; 122
123 u32 id{};
124 u64 layer_id{};
125 std::atomic_bool is_connect{};
116 126
117 std::list<u32> free_buffers; 127 std::list<u32> free_buffers;
118 std::array<Buffer, buffer_slots> buffers; 128 std::array<Buffer, buffer_slots> buffers;
119 std::list<u32> queue_sequence; 129 std::list<u32> queue_sequence;
120 Kernel::EventPair buffer_wait_event; 130 Kernel::EventPair buffer_wait_event;
131
132 std::mutex queue_mutex;
133 std::condition_variable condition;
121}; 134};
122 135
123} // namespace Service::NVFlinger 136} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 44aa2bdae..4b3581949 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -88,6 +88,10 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {
88} 88}
89 89
90NVFlinger::~NVFlinger() { 90NVFlinger::~NVFlinger() {
91 for (auto& buffer_queue : buffer_queues) {
92 buffer_queue->Disconnect();
93 }
94
91 if (system.IsMulticore()) { 95 if (system.IsMulticore()) {
92 is_running = false; 96 is_running = false;
93 wait_event->Set(); 97 wait_event->Set();
@@ -104,6 +108,8 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
104} 108}
105 109
106std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { 110std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
111 const auto guard = Lock();
112
107 LOG_DEBUG(Service, "Opening \"{}\" display", name); 113 LOG_DEBUG(Service, "Opening \"{}\" display", name);
108 114
109 // TODO(Subv): Currently we only support the Default display. 115 // TODO(Subv): Currently we only support the Default display.
@@ -121,6 +127,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
121} 127}
122 128
123std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { 129std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
130 const auto guard = Lock();
124 auto* const display = FindDisplay(display_id); 131 auto* const display = FindDisplay(display_id);
125 132
126 if (display == nullptr) { 133 if (display == nullptr) {
@@ -129,18 +136,22 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
129 136
130 const u64 layer_id = next_layer_id++; 137 const u64 layer_id = next_layer_id++;
131 const u32 buffer_queue_id = next_buffer_queue_id++; 138 const u32 buffer_queue_id = next_buffer_queue_id++;
132 buffer_queues.emplace_back(system.Kernel(), buffer_queue_id, layer_id); 139 buffer_queues.emplace_back(
133 display->CreateLayer(layer_id, buffer_queues.back()); 140 std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id));
141 display->CreateLayer(layer_id, *buffer_queues.back());
134 return layer_id; 142 return layer_id;
135} 143}
136 144
137void NVFlinger::CloseLayer(u64 layer_id) { 145void NVFlinger::CloseLayer(u64 layer_id) {
146 const auto guard = Lock();
147
138 for (auto& display : displays) { 148 for (auto& display : displays) {
139 display.CloseLayer(layer_id); 149 display.CloseLayer(layer_id);
140 } 150 }
141} 151}
142 152
143std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const { 153std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const {
154 const auto guard = Lock();
144 const auto* const layer = FindLayer(display_id, layer_id); 155 const auto* const layer = FindLayer(display_id, layer_id);
145 156
146 if (layer == nullptr) { 157 if (layer == nullptr) {
@@ -151,6 +162,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
151} 162}
152 163
153std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const { 164std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const {
165 const auto guard = Lock();
154 auto* const display = FindDisplay(display_id); 166 auto* const display = FindDisplay(display_id);
155 167
156 if (display == nullptr) { 168 if (display == nullptr) {
@@ -160,20 +172,16 @@ std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id)
160 return display->GetVSyncEvent(); 172 return display->GetVSyncEvent();
161} 173}
162 174
163BufferQueue& NVFlinger::FindBufferQueue(u32 id) { 175BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
176 const auto guard = Lock();
164 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), 177 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
165 [id](const auto& queue) { return queue.GetId() == id; }); 178 [id](const auto& queue) { return queue->GetId() == id; });
166 179
167 ASSERT(itr != buffer_queues.end()); 180 if (itr == buffer_queues.end()) {
168 return *itr; 181 return nullptr;
169} 182 }
170
171const BufferQueue& NVFlinger::FindBufferQueue(u32 id) const {
172 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
173 [id](const auto& queue) { return queue.GetId() == id; });
174 183
175 ASSERT(itr != buffer_queues.end()); 184 return itr->get();
176 return *itr;
177} 185}
178 186
179VI::Display* NVFlinger::FindDisplay(u64 display_id) { 187VI::Display* NVFlinger::FindDisplay(u64 display_id) {
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 1ebe949c0..c6765259f 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -75,10 +75,7 @@ public:
75 [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; 75 [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
76 76
77 /// Obtains a buffer queue identified by the ID. 77 /// Obtains a buffer queue identified by the ID.
78 [[nodiscard]] BufferQueue& FindBufferQueue(u32 id); 78 [[nodiscard]] BufferQueue* FindBufferQueue(u32 id);
79
80 /// Obtains a buffer queue identified by the ID.
81 [[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const;
82 79
83 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when 80 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
84 /// finished. 81 /// finished.
@@ -86,11 +83,11 @@ public:
86 83
87 [[nodiscard]] s64 GetNextTicks() const; 84 [[nodiscard]] s64 GetNextTicks() const;
88 85
86private:
89 [[nodiscard]] std::unique_lock<std::mutex> Lock() const { 87 [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
90 return std::unique_lock{*guard}; 88 return std::unique_lock{*guard};
91 } 89 }
92 90
93private:
94 /// Finds the display identified by the specified ID. 91 /// Finds the display identified by the specified ID.
95 [[nodiscard]] VI::Display* FindDisplay(u64 display_id); 92 [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
96 93
@@ -110,7 +107,7 @@ private:
110 std::shared_ptr<Nvidia::Module> nvdrv; 107 std::shared_ptr<Nvidia::Module> nvdrv;
111 108
112 std::vector<VI::Display> displays; 109 std::vector<VI::Display> displays;
113 std::vector<BufferQueue> buffer_queues; 110 std::vector<std::unique_ptr<BufferQueue>> buffer_queues;
114 111
115 /// Id to use for the next layer that is created, this counter is shared among all displays. 112 /// Id to use for the next layer that is created, this counter is shared among all displays.
116 u64 next_layer_id = 1; 113 u64 next_layer_id = 1;
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index abf3d1ea3..ff2a5b1db 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -95,9 +95,14 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
95 : system{system_}, service_name{service_name_}, max_sessions{max_sessions_}, 95 : system{system_}, service_name{service_name_}, max_sessions{max_sessions_},
96 handler_invoker{handler_invoker_} {} 96 handler_invoker{handler_invoker_} {}
97 97
98ServiceFrameworkBase::~ServiceFrameworkBase() = default; 98ServiceFrameworkBase::~ServiceFrameworkBase() {
99 // Wait for other threads to release access before destroying
100 const auto guard = LockService();
101}
99 102
100void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { 103void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
104 const auto guard = LockService();
105
101 ASSERT(!port_installed); 106 ASSERT(!port_installed);
102 107
103 auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); 108 auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
@@ -106,6 +111,8 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
106} 111}
107 112
108void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) { 113void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
114 const auto guard = LockService();
115
109 ASSERT(!port_installed); 116 ASSERT(!port_installed);
110 117
111 auto [server_port, client_port] = 118 auto [server_port, client_port] =
@@ -115,17 +122,6 @@ void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
115 port_installed = true; 122 port_installed = true;
116} 123}
117 124
118std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) {
119 ASSERT(!port_installed);
120
121 auto [server_port, client_port] =
122 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
123 auto port = MakeResult(std::move(server_port)).Unwrap();
124 port->SetHleHandler(shared_from_this());
125 port_installed = true;
126 return client_port;
127}
128
129void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { 125void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
130 handlers.reserve(handlers.size() + n); 126 handlers.reserve(handlers.size() + n);
131 for (std::size_t i = 0; i < n; ++i) { 127 for (std::size_t i = 0; i < n; ++i) {
@@ -164,6 +160,8 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {
164} 160}
165 161
166ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) { 162ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) {
163 const auto guard = LockService();
164
167 switch (context.GetCommandType()) { 165 switch (context.GetCommandType()) {
168 case IPC::CommandType::Close: { 166 case IPC::CommandType::Close: {
169 IPC::ResponseBuilder rb{context, 2}; 167 IPC::ResponseBuilder rb{context, 2};
@@ -184,7 +182,11 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
184 UNIMPLEMENTED_MSG("command_type={}", context.GetCommandType()); 182 UNIMPLEMENTED_MSG("command_type={}", context.GetCommandType());
185 } 183 }
186 184
187 context.WriteToOutgoingCommandBuffer(context.GetThread()); 185 // If emulation was shutdown, we are closing service threads, do not write the response back to
186 // memory that may be shutting down as well.
187 if (system.IsPoweredOn()) {
188 context.WriteToOutgoingCommandBuffer(context.GetThread());
189 }
188 190
189 return RESULT_SUCCESS; 191 return RESULT_SUCCESS;
190} 192}
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 62a182310..916445517 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -5,9 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <mutex>
8#include <string> 9#include <string>
9#include <boost/container/flat_map.hpp> 10#include <boost/container/flat_map.hpp>
10#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/spin_lock.h"
11#include "core/hle/kernel/hle_ipc.h" 13#include "core/hle/kernel/hle_ipc.h"
12#include "core/hle/kernel/object.h" 14#include "core/hle/kernel/object.h"
13 15
@@ -68,11 +70,9 @@ public:
68 void InstallAsService(SM::ServiceManager& service_manager); 70 void InstallAsService(SM::ServiceManager& service_manager);
69 /// Creates a port pair and registers it on the kernel's global port registry. 71 /// Creates a port pair and registers it on the kernel's global port registry.
70 void InstallAsNamedPort(Kernel::KernelCore& kernel); 72 void InstallAsNamedPort(Kernel::KernelCore& kernel);
71 /// Creates and returns an unregistered port for the service. 73 /// Invokes a service request routine.
72 std::shared_ptr<Kernel::ClientPort> CreatePort(Kernel::KernelCore& kernel);
73
74 void InvokeRequest(Kernel::HLERequestContext& ctx); 74 void InvokeRequest(Kernel::HLERequestContext& ctx);
75 75 /// Handles a synchronization request for the service.
76 ResultCode HandleSyncRequest(Kernel::HLERequestContext& context) override; 76 ResultCode HandleSyncRequest(Kernel::HLERequestContext& context) override;
77 77
78protected: 78protected:
@@ -80,6 +80,11 @@ protected:
80 template <typename Self> 80 template <typename Self>
81 using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); 81 using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
82 82
83 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
84 [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() {
85 return std::scoped_lock{lock_service};
86 }
87
83 /// System context that the service operates under. 88 /// System context that the service operates under.
84 Core::System& system; 89 Core::System& system;
85 90
@@ -115,6 +120,9 @@ private:
115 /// Function used to safely up-cast pointers to the derived class before invoking a handler. 120 /// Function used to safely up-cast pointers to the derived class before invoking a handler.
116 InvokerFn* handler_invoker; 121 InvokerFn* handler_invoker;
117 boost::container::flat_map<u32, FunctionInfoBase> handlers; 122 boost::container::flat_map<u32, FunctionInfoBase> handlers;
123
124 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
125 Common::SpinLock lock_service;
118}; 126};
119 127
120/** 128/**
diff --git a/src/core/hle/service/sockets/blocking_worker.h b/src/core/hle/service/sockets/blocking_worker.h
deleted file mode 100644
index 2d53e52b6..000000000
--- a/src/core/hle/service/sockets/blocking_worker.h
+++ /dev/null
@@ -1,161 +0,0 @@
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 <atomic>
8#include <memory>
9#include <string>
10#include <string_view>
11#include <thread>
12#include <variant>
13#include <vector>
14
15#include <fmt/format.h>
16
17#include "common/assert.h"
18#include "common/microprofile.h"
19#include "common/thread.h"
20#include "core/core.h"
21#include "core/hle/kernel/hle_ipc.h"
22#include "core/hle/kernel/kernel.h"
23#include "core/hle/kernel/thread.h"
24#include "core/hle/kernel/writable_event.h"
25
26namespace Service::Sockets {
27
28/**
29 * Worker abstraction to execute blocking calls on host without blocking the guest thread
30 *
31 * @tparam Service Service where the work is executed
32 * @tparam Types Types of work to execute
33 */
34template <class Service, class... Types>
35class BlockingWorker {
36 using This = BlockingWorker<Service, Types...>;
37 using WorkVariant = std::variant<std::monostate, Types...>;
38
39public:
40 /// Create a new worker
41 static std::unique_ptr<This> Create(Core::System& system, Service* service,
42 std::string_view name) {
43 return std::unique_ptr<This>(new This(system, service, name));
44 }
45
46 ~BlockingWorker() {
47 while (!is_available.load(std::memory_order_relaxed)) {
48 // Busy wait until work is finished
49 std::this_thread::yield();
50 }
51 // Monostate means to exit the thread
52 work = std::monostate{};
53 work_event.Set();
54 thread.join();
55 }
56
57 /**
58 * Try to capture the worker to send work after a success
59 * @returns True when the worker has been successfully captured
60 */
61 bool TryCapture() {
62 bool expected = true;
63 return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed,
64 std::memory_order_relaxed);
65 }
66
67 /**
68 * Send work to this worker abstraction
69 * @see TryCapture must be called before attempting to call this function
70 */
71 template <class Work>
72 void SendWork(Work new_work) {
73 ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured");
74 work = std::move(new_work);
75 work_event.Set();
76 }
77
78 /// Generate a callback for @see SleepClientThread
79 template <class Work>
80 auto Callback() {
81 return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx,
82 Kernel::ThreadWakeupReason reason) {
83 ASSERT(reason == Kernel::ThreadWakeupReason::Signal);
84 std::get<Work>(work).Response(ctx);
85 is_available.store(true);
86 };
87 }
88
89 /// Get kernel event that will be signalled by the worker when the host operation finishes
90 std::shared_ptr<Kernel::WritableEvent> KernelEvent() const {
91 return kernel_event;
92 }
93
94private:
95 explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) {
96 auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name));
97 kernel_event = std::move(pair.writable);
98 thread = std::thread([this, &system, service, name] { Run(system, service, name); });
99 }
100
101 void Run(Core::System& system, Service* service, std::string_view name) {
102 system.RegisterHostThread();
103
104 const std::string thread_name = fmt::format("yuzu:{}", name);
105 MicroProfileOnThreadCreate(thread_name.c_str());
106 Common::SetCurrentThreadName(thread_name.c_str());
107
108 bool keep_running = true;
109 while (keep_running) {
110 work_event.Wait();
111
112 const auto visit_fn = [service, &keep_running]<typename T>(T&& w) {
113 if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) {
114 keep_running = false;
115 } else {
116 w.Execute(service);
117 }
118 };
119 std::visit(visit_fn, work);
120
121 kernel_event->Signal();
122 }
123 }
124
125 std::thread thread;
126 WorkVariant work;
127 Common::Event work_event;
128 std::shared_ptr<Kernel::WritableEvent> kernel_event;
129 std::atomic_bool is_available{true};
130};
131
132template <class Service, class... Types>
133class BlockingWorkerPool {
134 using Worker = BlockingWorker<Service, Types...>;
135
136public:
137 explicit BlockingWorkerPool(Core::System& system_, Service* service_)
138 : system{system_}, service{service_} {}
139
140 /// Returns a captured worker thread, creating new ones if necessary
141 Worker* CaptureWorker() {
142 for (auto& worker : workers) {
143 if (worker->TryCapture()) {
144 return worker.get();
145 }
146 }
147 auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size()));
148 [[maybe_unused]] const bool success = new_worker->TryCapture();
149 ASSERT(success);
150
151 return workers.emplace_back(std::move(new_worker)).get();
152 }
153
154private:
155 Core::System& system;
156 Service* const service;
157
158 std::vector<std::unique_ptr<Worker>> workers;
159};
160
161} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 67b419503..2b824059d 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -178,13 +178,12 @@ void BSD::Poll(Kernel::HLERequestContext& ctx) {
178 178
179 LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout); 179 LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout);
180 180
181 ExecuteWork(ctx, "BSD:Poll", timeout != 0, 181 ExecuteWork(ctx, PollWork{
182 PollWork{ 182 .nfds = nfds,
183 .nfds = nfds, 183 .timeout = timeout,
184 .timeout = timeout, 184 .read_buffer = ctx.ReadBuffer(),
185 .read_buffer = ctx.ReadBuffer(), 185 .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
186 .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), 186 });
187 });
188} 187}
189 188
190void BSD::Accept(Kernel::HLERequestContext& ctx) { 189void BSD::Accept(Kernel::HLERequestContext& ctx) {
@@ -193,11 +192,10 @@ void BSD::Accept(Kernel::HLERequestContext& ctx) {
193 192
194 LOG_DEBUG(Service, "called. fd={}", fd); 193 LOG_DEBUG(Service, "called. fd={}", fd);
195 194
196 ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd), 195 ExecuteWork(ctx, AcceptWork{
197 AcceptWork{ 196 .fd = fd,
198 .fd = fd, 197 .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
199 .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), 198 });
200 });
201} 199}
202 200
203void BSD::Bind(Kernel::HLERequestContext& ctx) { 201void BSD::Bind(Kernel::HLERequestContext& ctx) {
@@ -215,11 +213,10 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) {
215 213
216 LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize()); 214 LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
217 215
218 ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd), 216 ExecuteWork(ctx, ConnectWork{
219 ConnectWork{ 217 .fd = fd,
220 .fd = fd, 218 .addr = ctx.ReadBuffer(),
221 .addr = ctx.ReadBuffer(), 219 });
222 });
223} 220}
224 221
225void BSD::GetPeerName(Kernel::HLERequestContext& ctx) { 222void BSD::GetPeerName(Kernel::HLERequestContext& ctx) {
@@ -327,12 +324,11 @@ void BSD::Recv(Kernel::HLERequestContext& ctx) {
327 324
328 LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize()); 325 LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());
329 326
330 ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd), 327 ExecuteWork(ctx, RecvWork{
331 RecvWork{ 328 .fd = fd,
332 .fd = fd, 329 .flags = flags,
333 .flags = flags, 330 .message = std::vector<u8>(ctx.GetWriteBufferSize()),
334 .message = std::vector<u8>(ctx.GetWriteBufferSize()), 331 });
335 });
336} 332}
337 333
338void BSD::RecvFrom(Kernel::HLERequestContext& ctx) { 334void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
@@ -344,13 +340,12 @@ void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
344 LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags, 340 LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
345 ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1)); 341 ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));
346 342
347 ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd), 343 ExecuteWork(ctx, RecvFromWork{
348 RecvFromWork{ 344 .fd = fd,
349 .fd = fd, 345 .flags = flags,
350 .flags = flags, 346 .message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
351 .message = std::vector<u8>(ctx.GetWriteBufferSize(0)), 347 .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
352 .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)), 348 });
353 });
354} 349}
355 350
356void BSD::Send(Kernel::HLERequestContext& ctx) { 351void BSD::Send(Kernel::HLERequestContext& ctx) {
@@ -361,12 +356,11 @@ void BSD::Send(Kernel::HLERequestContext& ctx) {
361 356
362 LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize()); 357 LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());
363 358
364 ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd), 359 ExecuteWork(ctx, SendWork{
365 SendWork{ 360 .fd = fd,
366 .fd = fd, 361 .flags = flags,
367 .flags = flags, 362 .message = ctx.ReadBuffer(),
368 .message = ctx.ReadBuffer(), 363 });
369 });
370} 364}
371 365
372void BSD::SendTo(Kernel::HLERequestContext& ctx) { 366void BSD::SendTo(Kernel::HLERequestContext& ctx) {
@@ -377,13 +371,12 @@ void BSD::SendTo(Kernel::HLERequestContext& ctx) {
377 LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags, 371 LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,
378 ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1)); 372 ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1));
379 373
380 ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd), 374 ExecuteWork(ctx, SendToWork{
381 SendToWork{ 375 .fd = fd,
382 .fd = fd, 376 .flags = flags,
383 .flags = flags, 377 .message = ctx.ReadBuffer(0),
384 .message = ctx.ReadBuffer(0), 378 .addr = ctx.ReadBuffer(1),
385 .addr = ctx.ReadBuffer(1), 379 });
386 });
387} 380}
388 381
389void BSD::Write(Kernel::HLERequestContext& ctx) { 382void BSD::Write(Kernel::HLERequestContext& ctx) {
@@ -392,12 +385,11 @@ void BSD::Write(Kernel::HLERequestContext& ctx) {
392 385
393 LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize()); 386 LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize());
394 387
395 ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd), 388 ExecuteWork(ctx, SendWork{
396 SendWork{ 389 .fd = fd,
397 .fd = fd, 390 .flags = 0,
398 .flags = 0, 391 .message = ctx.ReadBuffer(),
399 .message = ctx.ReadBuffer(), 392 });
400 });
401} 393}
402 394
403void BSD::Close(Kernel::HLERequestContext& ctx) { 395void BSD::Close(Kernel::HLERequestContext& ctx) {
@@ -410,24 +402,9 @@ void BSD::Close(Kernel::HLERequestContext& ctx) {
410} 402}
411 403
412template <typename Work> 404template <typename Work>
413void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason, 405void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, Work work) {
414 bool is_blocking, Work work) { 406 work.Execute(this);
415 if (!is_blocking) {
416 work.Execute(this);
417 work.Response(ctx);
418 return;
419 }
420
421 // Signal a dummy response to make IPC validation happy
422 // This will be overwritten by the SleepClientThread callback
423 work.Response(ctx); 407 work.Response(ctx);
424
425 auto worker = worker_pool.CaptureWorker();
426
427 ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(),
428 worker->Callback<Work>(), worker->KernelEvent());
429
430 worker->SendWork(std::move(work));
431} 408}
432 409
433std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) { 410std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
@@ -807,18 +784,6 @@ bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
807 return true; 784 return true;
808} 785}
809 786
810bool BSD::IsBlockingSocket(s32 fd) const noexcept {
811 // Inform invalid sockets as non-blocking
812 // This way we avoid using a worker thread as it will fail without blocking host
813 if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
814 return false;
815 }
816 if (!file_descriptors[fd]) {
817 return false;
818 }
819 return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0;
820}
821
822void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept { 787void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept {
823 IPC::ResponseBuilder rb{ctx, 4}; 788 IPC::ResponseBuilder rb{ctx, 4};
824 789
@@ -827,8 +792,7 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
827 rb.PushEnum(bsd_errno); 792 rb.PushEnum(bsd_errno);
828} 793}
829 794
830BSD::BSD(Core::System& system_, const char* name) 795BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
831 : ServiceFramework{system_, name}, worker_pool{system_, this} {
832 // clang-format off 796 // clang-format off
833 static const FunctionInfo functions[] = { 797 static const FunctionInfo functions[] = {
834 {0, &BSD::RegisterClient, "RegisterClient"}, 798 {0, &BSD::RegisterClient, "RegisterClient"},
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index f14713fc4..6da0bfeb2 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -11,7 +11,6 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/hle/kernel/hle_ipc.h" 12#include "core/hle/kernel/hle_ipc.h"
13#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
14#include "core/hle/service/sockets/blocking_worker.h"
15#include "core/hle/service/sockets/sockets.h" 14#include "core/hle/service/sockets/sockets.h"
16 15
17namespace Core { 16namespace Core {
@@ -138,8 +137,7 @@ private:
138 void Close(Kernel::HLERequestContext& ctx); 137 void Close(Kernel::HLERequestContext& ctx);
139 138
140 template <typename Work> 139 template <typename Work>
141 void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason, 140 void ExecuteWork(Kernel::HLERequestContext& ctx, Work work);
142 bool is_blocking, Work work);
143 141
144 std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol); 142 std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
145 std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, 143 std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
@@ -163,15 +161,10 @@ private:
163 161
164 s32 FindFreeFileDescriptorHandle() noexcept; 162 s32 FindFreeFileDescriptorHandle() noexcept;
165 bool IsFileDescriptorValid(s32 fd) const noexcept; 163 bool IsFileDescriptorValid(s32 fd) const noexcept;
166 bool IsBlockingSocket(s32 fd) const noexcept;
167 164
168 void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; 165 void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
169 166
170 std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; 167 std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
171
172 BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork,
173 SendToWork>
174 worker_pool;
175}; 168};
176 169
177class BSDCFG final : public ServiceFramework<BSDCFG> { 170class BSDCFG final : public ServiceFramework<BSDCFG> {
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 45cfffe06..968cd16b6 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -536,8 +536,7 @@ private:
536 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 536 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
537 transaction, flags); 537 transaction, flags);
538 538
539 const auto guard = nv_flinger.Lock(); 539 auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
540 auto& buffer_queue = nv_flinger.FindBufferQueue(id);
541 540
542 switch (transaction) { 541 switch (transaction) {
543 case TransactionId::Connect: { 542 case TransactionId::Connect: {
@@ -547,6 +546,9 @@ private:
547 Settings::values.resolution_factor.GetValue()), 546 Settings::values.resolution_factor.GetValue()),
548 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) * 547 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
549 Settings::values.resolution_factor.GetValue())}; 548 Settings::values.resolution_factor.GetValue())};
549
550 buffer_queue.Connect();
551
550 ctx.WriteBuffer(response.Serialize()); 552 ctx.WriteBuffer(response.Serialize());
551 break; 553 break;
552 } 554 }
@@ -563,40 +565,25 @@ private:
563 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; 565 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
564 const u32 width{request.data.width}; 566 const u32 width{request.data.width};
565 const u32 height{request.data.height}; 567 const u32 height{request.data.height};
566 auto result = buffer_queue.DequeueBuffer(width, height); 568
567 569 do {
568 if (result) { 570 if (auto result = buffer_queue.DequeueBuffer(width, height); result) {
569 // Buffer is available 571 // Buffer is available
570 IGBPDequeueBufferResponseParcel response{result->first, *result->second}; 572 IGBPDequeueBufferResponseParcel response{result->first, *result->second};
571 ctx.WriteBuffer(response.Serialize()); 573 ctx.WriteBuffer(response.Serialize());
572 } else { 574 break;
573 // Wait the current thread until a buffer becomes available 575 }
574 ctx.SleepClientThread( 576 } while (buffer_queue.IsConnected());
575 "IHOSBinderDriver::DequeueBuffer", UINT64_MAX, 577
576 [=, this](std::shared_ptr<Kernel::Thread> thread,
577 Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
578 // Repeat TransactParcel DequeueBuffer when a buffer is available
579 const auto guard = nv_flinger.Lock();
580 auto& buffer_queue = nv_flinger.FindBufferQueue(id);
581 auto result = buffer_queue.DequeueBuffer(width, height);
582 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
583
584 IGBPDequeueBufferResponseParcel response{result->first, *result->second};
585 ctx.WriteBuffer(response.Serialize());
586 IPC::ResponseBuilder rb{ctx, 2};
587 rb.Push(RESULT_SUCCESS);
588 },
589 buffer_queue.GetWritableBufferWaitEvent());
590 }
591 break; 578 break;
592 } 579 }
593 case TransactionId::RequestBuffer: { 580 case TransactionId::RequestBuffer: {
594 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; 581 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
595 582
596 auto& buffer = buffer_queue.RequestBuffer(request.slot); 583 auto& buffer = buffer_queue.RequestBuffer(request.slot);
597
598 IGBPRequestBufferResponseParcel response{buffer}; 584 IGBPRequestBufferResponseParcel response{buffer};
599 ctx.WriteBuffer(response.Serialize()); 585 ctx.WriteBuffer(response.Serialize());
586
600 break; 587 break;
601 } 588 }
602 case TransactionId::QueueBuffer: { 589 case TransactionId::QueueBuffer: {
@@ -682,7 +669,7 @@ private:
682 669
683 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); 670 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
684 671
685 const auto& buffer_queue = nv_flinger.FindBufferQueue(id); 672 const auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
686 673
687 // TODO(Subv): Find out what this actually is. 674 // TODO(Subv): Find out what this actually is.
688 IPC::ResponseBuilder rb{ctx, 2, 1}; 675 IPC::ResponseBuilder rb{ctx, 2, 1};
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 47d9ecf9a..39306509a 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -148,9 +148,4 @@ void RestoreGlobalState(bool is_powered_on) {
148 values.motion_enabled.SetGlobal(true); 148 values.motion_enabled.SetGlobal(true);
149} 149}
150 150
151void Sanitize() {
152 values.use_asynchronous_gpu_emulation.SetValue(
153 values.use_asynchronous_gpu_emulation.GetValue() || values.use_multi_core.GetValue());
154}
155
156} // namespace Settings 151} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index d5f8d2b7e..0cd3c0c84 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -257,7 +257,4 @@ void LogSettings();
257// Restore the global state of all applicable settings in the Values struct 257// Restore the global state of all applicable settings in the Values struct
258void RestoreGlobalState(bool is_powered_on); 258void RestoreGlobalState(bool is_powered_on);
259 259
260// Fixes settings that are known to cause issues with the emulator
261void Sanitize();
262
263} // namespace Settings 260} // namespace Settings
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 948e167c3..e050f9aed 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -48,6 +48,7 @@ add_library(video_core STATIC
48 engines/shader_bytecode.h 48 engines/shader_bytecode.h
49 engines/shader_header.h 49 engines/shader_header.h
50 engines/shader_type.h 50 engines/shader_type.h
51 framebuffer_config.h
51 macro/macro.cpp 52 macro/macro.cpp
52 macro/macro.h 53 macro/macro.h
53 macro/macro_hle.cpp 54 macro/macro_hle.cpp
@@ -59,10 +60,6 @@ add_library(video_core STATIC
59 fence_manager.h 60 fence_manager.h
60 gpu.cpp 61 gpu.cpp
61 gpu.h 62 gpu.h
62 gpu_asynch.cpp
63 gpu_asynch.h
64 gpu_synch.cpp
65 gpu_synch.h
66 gpu_thread.cpp 63 gpu_thread.cpp
67 gpu_thread.h 64 gpu_thread.h
68 guest_driver.cpp 65 guest_driver.cpp
diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h
new file mode 100644
index 000000000..b86c3a757
--- /dev/null
+++ b/src/video_core/framebuffer_config.h
@@ -0,0 +1,31 @@
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
7namespace Tegra {
8
9/**
10 * Struct describing framebuffer configuration
11 */
12struct FramebufferConfig {
13 enum class PixelFormat : u32 {
14 A8B8G8R8_UNORM = 1,
15 RGB565_UNORM = 4,
16 B8G8R8A8_UNORM = 5,
17 };
18
19 VAddr address{};
20 u32 offset{};
21 u32 width{};
22 u32 height{};
23 u32 stride{};
24 PixelFormat pixel_format{};
25
26 using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags;
27 TransformFlags transform_flags{};
28 Common::Rectangle<int> crop_rect;
29};
30
31} // namespace Tegra
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index e2512a7f2..6ab06775f 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -10,6 +10,7 @@
10#include "core/core_timing.h" 10#include "core/core_timing.h"
11#include "core/core_timing_util.h" 11#include "core/core_timing_util.h"
12#include "core/frontend/emu_window.h" 12#include "core/frontend/emu_window.h"
13#include "core/hardware_interrupt_manager.h"
13#include "core/memory.h" 14#include "core/memory.h"
14#include "core/settings.h" 15#include "core/settings.h"
15#include "video_core/engines/fermi_2d.h" 16#include "video_core/engines/fermi_2d.h"
@@ -36,7 +37,8 @@ GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_)
36 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)}, 37 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
37 maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)}, 38 maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)},
38 kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)}, 39 kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)},
39 shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_} {} 40 shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_},
41 gpu_thread{system_, is_async_} {}
40 42
41GPU::~GPU() = default; 43GPU::~GPU() = default;
42 44
@@ -198,10 +200,6 @@ void GPU::SyncGuestHost() {
198 renderer->Rasterizer().SyncGuestHost(); 200 renderer->Rasterizer().SyncGuestHost();
199} 201}
200 202
201void GPU::OnCommandListEnd() {
202 renderer->Rasterizer().ReleaseFences();
203}
204
205enum class GpuSemaphoreOperation { 203enum class GpuSemaphoreOperation {
206 AcquireEqual = 0x1, 204 AcquireEqual = 0x1,
207 WriteLong = 0x2, 205 WriteLong = 0x2,
@@ -461,4 +459,75 @@ void GPU::ProcessSemaphoreAcquire() {
461 } 459 }
462} 460}
463 461
462void GPU::Start() {
463 gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher);
464 cpu_context = renderer->GetRenderWindow().CreateSharedContext();
465 cpu_context->MakeCurrent();
466}
467
468void GPU::ObtainContext() {
469 cpu_context->MakeCurrent();
470}
471
472void GPU::ReleaseContext() {
473 cpu_context->DoneCurrent();
474}
475
476void GPU::PushGPUEntries(Tegra::CommandList&& entries) {
477 gpu_thread.SubmitList(std::move(entries));
478}
479
480void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
481 if (!use_nvdec) {
482 return;
483 }
484 // This condition fires when a video stream ends, clear all intermediary data
485 if (entries[0].raw == 0xDEADB33F) {
486 cdma_pusher.reset();
487 return;
488 }
489 if (!cdma_pusher) {
490 cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
491 }
492
493 // SubmitCommandBuffer would make the nvdec operations async, this is not currently working
494 // TODO(ameerj): RE proper async nvdec operation
495 // gpu_thread.SubmitCommandBuffer(std::move(entries));
496
497 cdma_pusher->Push(std::move(entries));
498 cdma_pusher->DispatchCalls();
499}
500
501void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
502 gpu_thread.SwapBuffers(framebuffer);
503}
504
505void GPU::FlushRegion(VAddr addr, u64 size) {
506 gpu_thread.FlushRegion(addr, size);
507}
508
509void GPU::InvalidateRegion(VAddr addr, u64 size) {
510 gpu_thread.InvalidateRegion(addr, size);
511}
512
513void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) {
514 gpu_thread.FlushAndInvalidateRegion(addr, size);
515}
516
517void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
518 auto& interrupt_manager = system.InterruptManager();
519 interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
520}
521
522void GPU::WaitIdle() const {
523 gpu_thread.WaitIdle();
524}
525
526void GPU::OnCommandListEnd() {
527 if (is_async) {
528 // This command only applies to asynchronous GPU mode
529 gpu_thread.OnCommandListEnd();
530 }
531}
532
464} // namespace Tegra 533} // namespace Tegra
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 660641d04..d81e38680 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -15,6 +15,8 @@
15#include "core/hle/service/nvflinger/buffer_queue.h" 15#include "core/hle/service/nvflinger/buffer_queue.h"
16#include "video_core/cdma_pusher.h" 16#include "video_core/cdma_pusher.h"
17#include "video_core/dma_pusher.h" 17#include "video_core/dma_pusher.h"
18#include "video_core/framebuffer_config.h"
19#include "video_core/gpu_thread.h"
18 20
19using CacheAddr = std::uintptr_t; 21using CacheAddr = std::uintptr_t;
20[[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) { 22[[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) {
@@ -101,28 +103,6 @@ enum class DepthFormat : u32 {
101struct CommandListHeader; 103struct CommandListHeader;
102class DebugContext; 104class DebugContext;
103 105
104/**
105 * Struct describing framebuffer configuration
106 */
107struct FramebufferConfig {
108 enum class PixelFormat : u32 {
109 A8B8G8R8_UNORM = 1,
110 RGB565_UNORM = 4,
111 B8G8R8A8_UNORM = 5,
112 };
113
114 VAddr address;
115 u32 offset;
116 u32 width;
117 u32 height;
118 u32 stride;
119 PixelFormat pixel_format;
120
121 using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags;
122 TransformFlags transform_flags;
123 Common::Rectangle<int> crop_rect;
124};
125
126namespace Engines { 106namespace Engines {
127class Fermi2D; 107class Fermi2D;
128class Maxwell3D; 108class Maxwell3D;
@@ -141,7 +121,7 @@ enum class EngineID {
141 121
142class MemoryManager; 122class MemoryManager;
143 123
144class GPU { 124class GPU final {
145public: 125public:
146 struct MethodCall { 126 struct MethodCall {
147 u32 method{}; 127 u32 method{};
@@ -159,7 +139,7 @@ public:
159 }; 139 };
160 140
161 explicit GPU(Core::System& system_, bool is_async_, bool use_nvdec_); 141 explicit GPU(Core::System& system_, bool is_async_, bool use_nvdec_);
162 virtual ~GPU(); 142 ~GPU();
163 143
164 /// Binds a renderer to the GPU. 144 /// Binds a renderer to the GPU.
165 void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer); 145 void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer);
@@ -176,7 +156,7 @@ public:
176 /// Synchronizes CPU writes with Host GPU memory. 156 /// Synchronizes CPU writes with Host GPU memory.
177 void SyncGuestHost(); 157 void SyncGuestHost();
178 /// Signal the ending of command list. 158 /// Signal the ending of command list.
179 virtual void OnCommandListEnd(); 159 void OnCommandListEnd();
180 160
181 /// Request a host GPU memory flush from the CPU. 161 /// Request a host GPU memory flush from the CPU.
182 [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); 162 [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size);
@@ -240,7 +220,7 @@ public:
240 } 220 }
241 221
242 // Waits for the GPU to finish working 222 // Waits for the GPU to finish working
243 virtual void WaitIdle() const = 0; 223 void WaitIdle() const;
244 224
245 /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. 225 /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
246 void WaitFence(u32 syncpoint_id, u32 value); 226 void WaitFence(u32 syncpoint_id, u32 value);
@@ -330,34 +310,34 @@ public:
330 /// Performs any additional setup necessary in order to begin GPU emulation. 310 /// Performs any additional setup necessary in order to begin GPU emulation.
331 /// This can be used to launch any necessary threads and register any necessary 311 /// This can be used to launch any necessary threads and register any necessary
332 /// core timing events. 312 /// core timing events.
333 virtual void Start() = 0; 313 void Start();
334 314
335 /// Obtain the CPU Context 315 /// Obtain the CPU Context
336 virtual void ObtainContext() = 0; 316 void ObtainContext();
337 317
338 /// Release the CPU Context 318 /// Release the CPU Context
339 virtual void ReleaseContext() = 0; 319 void ReleaseContext();
340 320
341 /// Push GPU command entries to be processed 321 /// Push GPU command entries to be processed
342 virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; 322 void PushGPUEntries(Tegra::CommandList&& entries);
343 323
344 /// Push GPU command buffer entries to be processed 324 /// Push GPU command buffer entries to be processed
345 virtual void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) = 0; 325 void PushCommandBuffer(Tegra::ChCommandHeaderList& entries);
346 326
347 /// Swap buffers (render frame) 327 /// Swap buffers (render frame)
348 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; 328 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
349 329
350 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 330 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
351 virtual void FlushRegion(VAddr addr, u64 size) = 0; 331 void FlushRegion(VAddr addr, u64 size);
352 332
353 /// Notify rasterizer that any caches of the specified region should be invalidated 333 /// Notify rasterizer that any caches of the specified region should be invalidated
354 virtual void InvalidateRegion(VAddr addr, u64 size) = 0; 334 void InvalidateRegion(VAddr addr, u64 size);
355 335
356 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated 336 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
357 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; 337 void FlushAndInvalidateRegion(VAddr addr, u64 size);
358 338
359protected: 339protected:
360 virtual void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const = 0; 340 void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const;
361 341
362private: 342private:
363 void ProcessBindMethod(const MethodCall& method_call); 343 void ProcessBindMethod(const MethodCall& method_call);
@@ -427,6 +407,9 @@ private:
427 std::mutex flush_request_mutex; 407 std::mutex flush_request_mutex;
428 408
429 const bool is_async; 409 const bool is_async;
410
411 VideoCommon::GPUThread::ThreadManager gpu_thread;
412 std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
430}; 413};
431 414
432#define ASSERT_REG_POSITION(field_name, position) \ 415#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
deleted file mode 100644
index 6cc091ecd..000000000
--- a/src/video_core/gpu_asynch.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core.h"
6#include "core/hardware_interrupt_manager.h"
7#include "video_core/gpu_asynch.h"
8#include "video_core/gpu_thread.h"
9#include "video_core/renderer_base.h"
10
11namespace VideoCommon {
12
13GPUAsynch::GPUAsynch(Core::System& system_, bool use_nvdec_)
14 : GPU{system_, true, use_nvdec_}, gpu_thread{system_} {}
15
16GPUAsynch::~GPUAsynch() = default;
17
18void GPUAsynch::Start() {
19 gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher);
20 cpu_context = renderer->GetRenderWindow().CreateSharedContext();
21 cpu_context->MakeCurrent();
22}
23
24void GPUAsynch::ObtainContext() {
25 cpu_context->MakeCurrent();
26}
27
28void GPUAsynch::ReleaseContext() {
29 cpu_context->DoneCurrent();
30}
31
32void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
33 gpu_thread.SubmitList(std::move(entries));
34}
35
36void GPUAsynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
37 if (!use_nvdec) {
38 return;
39 }
40 // This condition fires when a video stream ends, clear all intermediary data
41 if (entries[0].raw == 0xDEADB33F) {
42 cdma_pusher.reset();
43 return;
44 }
45 if (!cdma_pusher) {
46 cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
47 }
48
49 // SubmitCommandBuffer would make the nvdec operations async, this is not currently working
50 // TODO(ameerj): RE proper async nvdec operation
51 // gpu_thread.SubmitCommandBuffer(std::move(entries));
52
53 cdma_pusher->Push(std::move(entries));
54 cdma_pusher->DispatchCalls();
55}
56
57void GPUAsynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
58 gpu_thread.SwapBuffers(framebuffer);
59}
60
61void GPUAsynch::FlushRegion(VAddr addr, u64 size) {
62 gpu_thread.FlushRegion(addr, size);
63}
64
65void GPUAsynch::InvalidateRegion(VAddr addr, u64 size) {
66 gpu_thread.InvalidateRegion(addr, size);
67}
68
69void GPUAsynch::FlushAndInvalidateRegion(VAddr addr, u64 size) {
70 gpu_thread.FlushAndInvalidateRegion(addr, size);
71}
72
73void GPUAsynch::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
74 auto& interrupt_manager = system.InterruptManager();
75 interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
76}
77
78void GPUAsynch::WaitIdle() const {
79 gpu_thread.WaitIdle();
80}
81
82void GPUAsynch::OnCommandListEnd() {
83 gpu_thread.OnCommandListEnd();
84}
85
86} // namespace VideoCommon
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
deleted file mode 100644
index a384113f4..000000000
--- a/src/video_core/gpu_asynch.h
+++ /dev/null
@@ -1,47 +0,0 @@
1// Copyright 2019 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 "video_core/gpu.h"
8#include "video_core/gpu_thread.h"
9
10namespace Core::Frontend {
11class GraphicsContext;
12}
13
14namespace VideoCore {
15class RendererBase;
16} // namespace VideoCore
17
18namespace VideoCommon {
19
20/// Implementation of GPU interface that runs the GPU asynchronously
21class GPUAsynch final : public Tegra::GPU {
22public:
23 explicit GPUAsynch(Core::System& system_, bool use_nvdec_);
24 ~GPUAsynch() override;
25
26 void Start() override;
27 void ObtainContext() override;
28 void ReleaseContext() override;
29 void PushGPUEntries(Tegra::CommandList&& entries) override;
30 void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override;
31 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
32 void FlushRegion(VAddr addr, u64 size) override;
33 void InvalidateRegion(VAddr addr, u64 size) override;
34 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
35 void WaitIdle() const override;
36
37 void OnCommandListEnd() override;
38
39protected:
40 void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override;
41
42private:
43 GPUThread::ThreadManager gpu_thread;
44 std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
45};
46
47} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
deleted file mode 100644
index 1e9d4b9b2..000000000
--- a/src/video_core/gpu_synch.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/gpu_synch.h"
6#include "video_core/renderer_base.h"
7
8namespace VideoCommon {
9
10GPUSynch::GPUSynch(Core::System& system_, bool use_nvdec_) : GPU{system_, false, use_nvdec_} {}
11
12GPUSynch::~GPUSynch() = default;
13
14void GPUSynch::Start() {}
15
16void GPUSynch::ObtainContext() {
17 renderer->Context().MakeCurrent();
18}
19
20void GPUSynch::ReleaseContext() {
21 renderer->Context().DoneCurrent();
22}
23
24void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
25 dma_pusher->Push(std::move(entries));
26 dma_pusher->DispatchCalls();
27}
28
29void GPUSynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
30 if (!use_nvdec) {
31 return;
32 }
33 // This condition fires when a video stream ends, clears all intermediary data
34 if (entries[0].raw == 0xDEADB33F) {
35 cdma_pusher.reset();
36 return;
37 }
38 if (!cdma_pusher) {
39 cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
40 }
41 cdma_pusher->Push(std::move(entries));
42 cdma_pusher->DispatchCalls();
43}
44
45void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
46 renderer->SwapBuffers(framebuffer);
47}
48
49void GPUSynch::FlushRegion(VAddr addr, u64 size) {
50 renderer->Rasterizer().FlushRegion(addr, size);
51}
52
53void GPUSynch::InvalidateRegion(VAddr addr, u64 size) {
54 renderer->Rasterizer().InvalidateRegion(addr, size);
55}
56
57void GPUSynch::FlushAndInvalidateRegion(VAddr addr, u64 size) {
58 renderer->Rasterizer().FlushAndInvalidateRegion(addr, size);
59}
60
61} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
deleted file mode 100644
index c5904b8db..000000000
--- a/src/video_core/gpu_synch.h
+++ /dev/null
@@ -1,41 +0,0 @@
1// Copyright 2019 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 "video_core/gpu.h"
8
9namespace Core::Frontend {
10class GraphicsContext;
11}
12
13namespace VideoCore {
14class RendererBase;
15} // namespace VideoCore
16
17namespace VideoCommon {
18
19/// Implementation of GPU interface that runs the GPU synchronously
20class GPUSynch final : public Tegra::GPU {
21public:
22 explicit GPUSynch(Core::System& system_, bool use_nvdec_);
23 ~GPUSynch() override;
24
25 void Start() override;
26 void ObtainContext() override;
27 void ReleaseContext() override;
28 void PushGPUEntries(Tegra::CommandList&& entries) override;
29 void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override;
30 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
31 void FlushRegion(VAddr addr, u64 size) override;
32 void InvalidateRegion(VAddr addr, u64 size) override;
33 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
34 void WaitIdle() const override {}
35
36protected:
37 void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
38 [[maybe_unused]] u32 value) const override {}
39};
40
41} // namespace VideoCommon
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index e27218b96..7e490bcc3 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/scope_exit.h"
7#include "common/thread.h" 8#include "common/thread.h"
8#include "core/core.h" 9#include "core/core.h"
9#include "core/frontend/emu_window.h" 10#include "core/frontend/emu_window.h"
@@ -21,6 +22,8 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
21 SynchState& state, Tegra::CDmaPusher& cdma_pusher) { 22 SynchState& state, Tegra::CDmaPusher& cdma_pusher) {
22 std::string name = "yuzu:GPU"; 23 std::string name = "yuzu:GPU";
23 MicroProfileOnThreadCreate(name.c_str()); 24 MicroProfileOnThreadCreate(name.c_str());
25 SCOPE_EXIT({ MicroProfileOnThreadExit(); });
26
24 Common::SetCurrentThreadName(name.c_str()); 27 Common::SetCurrentThreadName(name.c_str());
25 Common::SetCurrentThreadPriority(Common::ThreadPriority::High); 28 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
26 system.RegisterHostThread(); 29 system.RegisterHostThread();
@@ -65,7 +68,8 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
65 } 68 }
66} 69}
67 70
68ThreadManager::ThreadManager(Core::System& system_) : system{system_} {} 71ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
72 : system{system_}, is_async{is_async_} {}
69 73
70ThreadManager::~ThreadManager() { 74ThreadManager::~ThreadManager() {
71 if (!thread.joinable()) { 75 if (!thread.joinable()) {
@@ -97,19 +101,30 @@ void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
97} 101}
98 102
99void ThreadManager::FlushRegion(VAddr addr, u64 size) { 103void ThreadManager::FlushRegion(VAddr addr, u64 size) {
100 if (!Settings::IsGPULevelHigh()) { 104 if (!is_async) {
105 // Always flush with synchronous GPU mode
101 PushCommand(FlushRegionCommand(addr, size)); 106 PushCommand(FlushRegionCommand(addr, size));
102 return; 107 return;
103 } 108 }
104 if (!Settings::IsGPULevelExtreme()) { 109
105 return; 110 // Asynchronous GPU mode
106 } 111 switch (Settings::values.gpu_accuracy.GetValue()) {
107 if (system.Renderer().Rasterizer().MustFlushRegion(addr, size)) { 112 case Settings::GPUAccuracy::Normal:
113 PushCommand(FlushRegionCommand(addr, size));
114 break;
115 case Settings::GPUAccuracy::High:
116 // TODO(bunnei): Is this right? Preserving existing behavior for now
117 break;
118 case Settings::GPUAccuracy::Extreme: {
108 auto& gpu = system.GPU(); 119 auto& gpu = system.GPU();
109 u64 fence = gpu.RequestFlush(addr, size); 120 u64 fence = gpu.RequestFlush(addr, size);
110 PushCommand(GPUTickCommand()); 121 PushCommand(GPUTickCommand());
111 while (fence > gpu.CurrentFlushRequestFence()) { 122 while (fence > gpu.CurrentFlushRequestFence()) {
112 } 123 }
124 break;
125 }
126 default:
127 UNIMPLEMENTED_MSG("Unsupported gpu_accuracy {}", Settings::values.gpu_accuracy.GetValue());
113 } 128 }
114} 129}
115 130
@@ -123,7 +138,8 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
123} 138}
124 139
125void ThreadManager::WaitIdle() const { 140void ThreadManager::WaitIdle() const {
126 while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed)) { 141 while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed) &&
142 system.IsPoweredOn()) {
127 } 143 }
128} 144}
129 145
@@ -134,6 +150,12 @@ void ThreadManager::OnCommandListEnd() {
134u64 ThreadManager::PushCommand(CommandData&& command_data) { 150u64 ThreadManager::PushCommand(CommandData&& command_data) {
135 const u64 fence{++state.last_fence}; 151 const u64 fence{++state.last_fence};
136 state.queue.Push(CommandDataContainer(std::move(command_data), fence)); 152 state.queue.Push(CommandDataContainer(std::move(command_data), fence));
153
154 if (!is_async) {
155 // In synchronous GPU mode, block the caller until the command has executed
156 WaitIdle();
157 }
158
137 return fence; 159 return fence;
138} 160}
139 161
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index f1c52cd9e..2775629e7 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -10,8 +10,9 @@
10#include <optional> 10#include <optional>
11#include <thread> 11#include <thread>
12#include <variant> 12#include <variant>
13
13#include "common/threadsafe_queue.h" 14#include "common/threadsafe_queue.h"
14#include "video_core/gpu.h" 15#include "video_core/framebuffer_config.h"
15 16
16namespace Tegra { 17namespace Tegra {
17struct FramebufferConfig; 18struct FramebufferConfig;
@@ -25,6 +26,10 @@ class GraphicsContext;
25class System; 26class System;
26} // namespace Core 27} // namespace Core
27 28
29namespace VideoCore {
30class RendererBase;
31} // namespace VideoCore
32
28namespace VideoCommon::GPUThread { 33namespace VideoCommon::GPUThread {
29 34
30/// Command to signal to the GPU thread that processing has ended 35/// Command to signal to the GPU thread that processing has ended
@@ -112,7 +117,7 @@ struct SynchState final {
112/// Class used to manage the GPU thread 117/// Class used to manage the GPU thread
113class ThreadManager final { 118class ThreadManager final {
114public: 119public:
115 explicit ThreadManager(Core::System& system_); 120 explicit ThreadManager(Core::System& system_, bool is_async_);
116 ~ThreadManager(); 121 ~ThreadManager();
117 122
118 /// Creates and starts the GPU thread. 123 /// Creates and starts the GPU thread.
@@ -150,6 +155,7 @@ private:
150 Core::System& system; 155 Core::System& system;
151 std::thread thread; 156 std::thread thread;
152 std::thread::id thread_id; 157 std::thread::id thread_id;
158 const bool is_async;
153}; 159};
154 160
155} // namespace VideoCommon::GPUThread 161} // namespace VideoCommon::GPUThread
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 837800bfe..53444e945 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -7,8 +7,6 @@
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/settings.h" 9#include "core/settings.h"
10#include "video_core/gpu_asynch.h"
11#include "video_core/gpu_synch.h"
12#include "video_core/renderer_base.h" 10#include "video_core/renderer_base.h"
13#include "video_core/renderer_opengl/renderer_opengl.h" 11#include "video_core/renderer_opengl/renderer_opengl.h"
14#include "video_core/renderer_vulkan/renderer_vulkan.h" 12#include "video_core/renderer_vulkan/renderer_vulkan.h"
@@ -39,13 +37,9 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
39namespace VideoCore { 37namespace VideoCore {
40 38
41std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { 39std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
42 std::unique_ptr<Tegra::GPU> gpu;
43 const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue(); 40 const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue();
44 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { 41 std::unique_ptr<Tegra::GPU> gpu = std::make_unique<Tegra::GPU>(
45 gpu = std::make_unique<VideoCommon::GPUAsynch>(system, use_nvdec); 42 system, Settings::values.use_asynchronous_gpu_emulation.GetValue(), use_nvdec);
46 } else {
47 gpu = std::make_unique<VideoCommon::GPUSynch>(system, use_nvdec);
48 }
49 43
50 auto context = emu_window.CreateSharedContext(); 44 auto context = emu_window.CreateSharedContext();
51 const auto scope = context->Acquire(); 45 const auto scope = context->Acquire();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 9fb254986..34c2a5f8b 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -1589,14 +1589,12 @@ void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool
1589 1589
1590void Config::Reload() { 1590void Config::Reload() {
1591 ReadValues(); 1591 ReadValues();
1592 Settings::Sanitize();
1593 // To apply default value changes 1592 // To apply default value changes
1594 SaveValues(); 1593 SaveValues();
1595 Settings::Apply(Core::System::GetInstance()); 1594 Settings::Apply(Core::System::GetInstance());
1596} 1595}
1597 1596
1598void Config::Save() { 1597void Config::Save() {
1599 Settings::Sanitize();
1600 SaveValues(); 1598 SaveValues();
1601} 1599}
1602 1600
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 7aa515226..ab66d7f93 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -580,9 +580,8 @@ void GMainWindow::InitializeWidgets() {
580 if (emulation_running) { 580 if (emulation_running) {
581 return; 581 return;
582 } 582 }
583 const bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() || 583 Settings::values.use_asynchronous_gpu_emulation.SetValue(
584 Settings::values.use_multi_core.GetValue(); 584 !Settings::values.use_asynchronous_gpu_emulation.GetValue());
585 Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
586 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); 585 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
587 Settings::Apply(Core::System::GetInstance()); 586 Settings::Apply(Core::System::GetInstance());
588 }); 587 });
@@ -599,16 +598,13 @@ void GMainWindow::InitializeWidgets() {
599 return; 598 return;
600 } 599 }
601 Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue()); 600 Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue());
602 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
603 Settings::values.use_multi_core.GetValue();
604 Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
605 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
606 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); 601 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
607 Settings::Apply(Core::System::GetInstance()); 602 Settings::Apply(Core::System::GetInstance());
608 }); 603 });
609 multicore_status_button->setText(tr("MULTICORE")); 604 multicore_status_button->setText(tr("MULTICORE"));
610 multicore_status_button->setCheckable(true); 605 multicore_status_button->setCheckable(true);
611 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); 606 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
607
612 statusBar()->insertPermanentWidget(0, multicore_status_button); 608 statusBar()->insertPermanentWidget(0, multicore_status_button);
613 statusBar()->insertPermanentWidget(0, async_status_button); 609 statusBar()->insertPermanentWidget(0, async_status_button);
614 610
@@ -2533,9 +2529,6 @@ void GMainWindow::UpdateStatusBar() {
2533void GMainWindow::UpdateStatusButtons() { 2529void GMainWindow::UpdateStatusButtons() {
2534 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); 2530 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
2535 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); 2531 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
2536 Settings::values.use_asynchronous_gpu_emulation.SetValue(
2537 Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
2538 Settings::values.use_multi_core.GetValue());
2539 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); 2532 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
2540 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 2533 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
2541 Settings::RendererBackend::Vulkan); 2534 Settings::RendererBackend::Vulkan);