summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/templates/build-msvc.yml2
-rw-r--r--CMakeLists.txt2
m---------externals/dynarmic0
-rw-r--r--src/audio_core/algorithm/interpolate.cpp2
-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.txt4
-rw-r--r--src/common/memory_hook.cpp11
-rw-r--r--src/common/memory_hook.h47
-rw-r--r--src/common/page_table.cpp10
-rw-r--r--src/common/page_table.h88
-rw-r--r--src/common/swap.h4
-rw-r--r--src/common/thread_worker.cpp58
-rw-r--r--src/common/thread_worker.h30
-rw-r--r--src/common/virtual_buffer.h10
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp4
-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/memory/memory_block.h14
-rw-r--r--src/core/hle/kernel/memory/page_table.cpp14
-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/kernel/svc_types.h4
-rw-r--r--src/core/hle/service/am/am.cpp6
-rw-r--r--src/core/hle/service/am/am.h2
-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.cpp38
-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/memory.cpp217
-rw-r--r--src/core/memory.h34
-rw-r--r--src/core/settings.cpp5
-rw-r--r--src/core/settings.h6
-rw-r--r--src/input_common/gcadapter/gc_adapter.h6
-rw-r--r--src/input_common/motion_input.cpp2
-rw-r--r--src/input_common/mouse/mouse_input.h2
-rw-r--r--src/input_common/sdl/sdl_impl.cpp47
-rw-r--r--src/input_common/udp/udp.cpp8
-rw-r--r--src/tests/CMakeLists.txt2
-rw-r--r--src/tests/common/fibers.cpp4
-rw-r--r--src/tests/core/arm/arm_test_common.cpp145
-rw-r--r--src/tests/core/arm/arm_test_common.h93
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/command_classes/vic.cpp2
-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/renderer_vulkan/vk_device.cpp2
-rw-r--r--src/video_core/video_core.cpp10
-rw-r--r--src/yuzu/applets/controller.cpp2
-rw-r--r--src/yuzu/applets/error.cpp6
-rw-r--r--src/yuzu/compatdb.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp3
-rw-r--r--src/yuzu/configuration/configure_cpu.ui12
-rw-r--r--src/yuzu/configuration/configure_input.cpp13
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp32
-rw-r--r--src/yuzu/configuration/configure_input_player.h12
-rw-r--r--src/yuzu/main.cpp38
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
-rw-r--r--src/yuzu_tester/yuzu.cpp2
107 files changed, 1079 insertions, 1602 deletions
diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml
index 33ff8201f..721179550 100644
--- a/.ci/templates/build-msvc.yml
+++ b/.ci/templates/build-msvc.yml
@@ -8,7 +8,7 @@ steps:
8 displayName: 'Install vulkan-sdk' 8 displayName: 'Install vulkan-sdk'
9- script: python -m pip install --upgrade pip conan 9- script: python -m pip install --upgrade pip conan
10 displayName: 'Install conan' 10 displayName: 'Install conan'
11- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd .. 11- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
12 displayName: 'Configure CMake' 12 displayName: 'Configure CMake'
13- task: MSBuild@1 13- task: MSBuild@1
14 displayName: 'Build' 14 displayName: 'Build'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eda555494..5793c5d57 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -165,7 +165,7 @@ macro(yuzu_find_packages)
165 "lz4 1.8 lz4/1.9.2" 165 "lz4 1.8 lz4/1.9.2"
166 "nlohmann_json 3.8 nlohmann_json/3.8.0" 166 "nlohmann_json 3.8 nlohmann_json/3.8.0"
167 "ZLIB 1.2 zlib/1.2.11" 167 "ZLIB 1.2 zlib/1.2.11"
168 "zstd 1.4 zstd/1.4.5" 168 "zstd 1.4 zstd/1.4.8"
169 ) 169 )
170 170
171 foreach(PACKAGE ${REQUIRED_LIBS}) 171 foreach(PACKAGE ${REQUIRED_LIBS})
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 4a9a0d07f7376b439430e686721e8176c7b56ce Subproject 3806284cbefc4115436dcdc687776a45ec31309
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp
index 699fcb84c..3b4144e21 100644
--- a/src/audio_core/algorithm/interpolate.cpp
+++ b/src/audio_core/algorithm/interpolate.cpp
@@ -218,7 +218,7 @@ void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size
218 const auto l2 = lut[lut_index + 2]; 218 const auto l2 = lut[lut_index + 2];
219 const auto l3 = lut[lut_index + 3]; 219 const auto l3 = lut[lut_index + 3];
220 220
221 const auto s0 = static_cast<s32>(input[index]); 221 const auto s0 = static_cast<s32>(input[index + 0]);
222 const auto s1 = static_cast<s32>(input[index + 1]); 222 const auto s1 = static_cast<s32>(input[index + 1]);
223 const auto s2 = static_cast<s32>(input[index + 2]); 223 const auto s2 = static_cast<s32>(input[index + 2]);
224 const auto s3 = static_cast<s32>(input[index + 3]); 224 const auto s3 = static_cast<s32>(input[index + 3]);
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..2c2bd2ee8 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -135,8 +135,6 @@ add_library(common STATIC
135 math_util.h 135 math_util.h
136 memory_detect.cpp 136 memory_detect.cpp
137 memory_detect.h 137 memory_detect.h
138 memory_hook.cpp
139 memory_hook.h
140 microprofile.cpp 138 microprofile.cpp
141 microprofile.h 139 microprofile.h
142 microprofileui.h 140 microprofileui.h
@@ -162,6 +160,8 @@ add_library(common STATIC
162 thread.cpp 160 thread.cpp
163 thread.h 161 thread.h
164 thread_queue_list.h 162 thread_queue_list.h
163 thread_worker.cpp
164 thread_worker.h
165 threadsafe_queue.h 165 threadsafe_queue.h
166 time_zone.cpp 166 time_zone.cpp
167 time_zone.h 167 time_zone.h
diff --git a/src/common/memory_hook.cpp b/src/common/memory_hook.cpp
deleted file mode 100644
index 3986986d6..000000000
--- a/src/common/memory_hook.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/memory_hook.h"
6
7namespace Common {
8
9MemoryHook::~MemoryHook() = default;
10
11} // namespace Common
diff --git a/src/common/memory_hook.h b/src/common/memory_hook.h
deleted file mode 100644
index adaa4c2c5..000000000
--- a/src/common/memory_hook.h
+++ /dev/null
@@ -1,47 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <optional>
9
10#include "common/common_types.h"
11
12namespace Common {
13
14/**
15 * Memory hooks have two purposes:
16 * 1. To allow reads and writes to a region of memory to be intercepted. This is used to implement
17 * texture forwarding and memory breakpoints for debugging.
18 * 2. To allow for the implementation of MMIO devices.
19 *
20 * A hook may be mapped to multiple regions of memory.
21 *
22 * If a std::nullopt or false is returned from a function, the read/write request is passed through
23 * to the underlying memory region.
24 */
25class MemoryHook {
26public:
27 virtual ~MemoryHook();
28
29 virtual std::optional<bool> IsValidAddress(VAddr addr) = 0;
30
31 virtual std::optional<u8> Read8(VAddr addr) = 0;
32 virtual std::optional<u16> Read16(VAddr addr) = 0;
33 virtual std::optional<u32> Read32(VAddr addr) = 0;
34 virtual std::optional<u64> Read64(VAddr addr) = 0;
35
36 virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
37
38 virtual bool Write8(VAddr addr, u8 data) = 0;
39 virtual bool Write16(VAddr addr, u16 data) = 0;
40 virtual bool Write32(VAddr addr, u32 data) = 0;
41 virtual bool Write64(VAddr addr, u64 data) = 0;
42
43 virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0;
44};
45
46using MemoryHookPointer = std::shared_ptr<MemoryHook>;
47} // namespace Common
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index bccea0894..8fd8620fd 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -10,16 +10,10 @@ PageTable::PageTable() = default;
10 10
11PageTable::~PageTable() noexcept = default; 11PageTable::~PageTable() noexcept = default;
12 12
13void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, 13void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) {
14 bool has_attribute) { 14 const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
15 const std::size_t num_page_table_entries{1ULL
16 << (address_space_width_in_bits - page_size_in_bits)};
17 pointers.resize(num_page_table_entries); 15 pointers.resize(num_page_table_entries);
18 backing_addr.resize(num_page_table_entries); 16 backing_addr.resize(num_page_table_entries);
19
20 if (has_attribute) {
21 attributes.resize(num_page_table_entries);
22 }
23} 17}
24 18
25} // namespace Common 19} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
index 9754fabf9..61c5552e0 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -4,10 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <tuple> 8#include <tuple>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/memory_hook.h"
11#include "common/virtual_buffer.h" 11#include "common/virtual_buffer.h"
12 12
13namespace Common { 13namespace Common {
@@ -20,27 +20,6 @@ enum class PageType : u8 {
20 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and 20 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
21 /// invalidation 21 /// invalidation
22 RasterizerCachedMemory, 22 RasterizerCachedMemory,
23 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
24 Special,
25 /// Page is allocated for use.
26 Allocated,
27};
28
29struct SpecialRegion {
30 enum class Type {
31 DebugHook,
32 IODevice,
33 } type;
34
35 MemoryHookPointer handler;
36
37 [[nodiscard]] bool operator<(const SpecialRegion& other) const {
38 return std::tie(type, handler) < std::tie(other.type, other.handler);
39 }
40
41 [[nodiscard]] bool operator==(const SpecialRegion& other) const {
42 return std::tie(type, handler) == std::tie(other.type, other.handler);
43 }
44}; 23};
45 24
46/** 25/**
@@ -48,6 +27,59 @@ struct SpecialRegion {
48 * mimics the way a real CPU page table works. 27 * mimics the way a real CPU page table works.
49 */ 28 */
50struct PageTable { 29struct PageTable {
30 /// Number of bits reserved for attribute tagging.
31 /// This can be at most the guaranteed alignment of the pointers in the page table.
32 static constexpr int ATTRIBUTE_BITS = 2;
33
34 /**
35 * Pair of host pointer and page type attribute.
36 * This uses the lower bits of a given pointer to store the attribute tag.
37 * Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
38 * call. In other words, they are guaranteed to be synchronized at all times.
39 */
40 class PageInfo {
41 public:
42 /// Returns the page pointer
43 [[nodiscard]] u8* Pointer() const noexcept {
44 return ExtractPointer(raw.load(std::memory_order_relaxed));
45 }
46
47 /// Returns the page type attribute
48 [[nodiscard]] PageType Type() const noexcept {
49 return ExtractType(raw.load(std::memory_order_relaxed));
50 }
51
52 /// Returns the page pointer and attribute pair, extracted from the same atomic read
53 [[nodiscard]] std::pair<u8*, PageType> PointerType() const noexcept {
54 const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
55 return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
56 }
57
58 /// Returns the raw representation of the page information.
59 /// Use ExtractPointer and ExtractType to unpack the value.
60 [[nodiscard]] uintptr_t Raw() const noexcept {
61 return raw.load(std::memory_order_relaxed);
62 }
63
64 /// Write a page pointer and type pair atomically
65 void Store(u8* pointer, PageType type) noexcept {
66 raw.store(reinterpret_cast<uintptr_t>(pointer) | static_cast<uintptr_t>(type));
67 }
68
69 /// Unpack a pointer from a page info raw representation
70 [[nodiscard]] static u8* ExtractPointer(uintptr_t raw) noexcept {
71 return reinterpret_cast<u8*>(raw & (~uintptr_t{0} << ATTRIBUTE_BITS));
72 }
73
74 /// Unpack a page type from a page info raw representation
75 [[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
76 return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
77 }
78
79 private:
80 std::atomic<uintptr_t> raw;
81 };
82
51 PageTable(); 83 PageTable();
52 ~PageTable() noexcept; 84 ~PageTable() noexcept;
53 85
@@ -58,25 +90,21 @@ struct PageTable {
58 PageTable& operator=(PageTable&&) noexcept = default; 90 PageTable& operator=(PageTable&&) noexcept = default;
59 91
60 /** 92 /**
61 * Resizes the page table to be able to accomodate enough pages within 93 * Resizes the page table to be able to accommodate enough pages within
62 * a given address space. 94 * a given address space.
63 * 95 *
64 * @param address_space_width_in_bits The address size width in bits. 96 * @param address_space_width_in_bits The address size width in bits.
65 * @param page_size_in_bits The page size in bits. 97 * @param page_size_in_bits The page size in bits.
66 * @param has_attribute Whether or not this page has any backing attributes.
67 */ 98 */
68 void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, 99 void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
69 bool has_attribute);
70 100
71 /** 101 /**
72 * Vector of memory pointers backing each page. An entry can only be non-null if the 102 * Vector of memory pointers backing each page. An entry can only be non-null if the
73 * corresponding entry in the `attributes` vector is of type `Memory`. 103 * corresponding attribute element is of type `Memory`.
74 */ 104 */
75 VirtualBuffer<u8*> pointers; 105 VirtualBuffer<PageInfo> pointers;
76 106
77 VirtualBuffer<u64> backing_addr; 107 VirtualBuffer<u64> backing_addr;
78
79 VirtualBuffer<PageType> attributes;
80}; 108};
81 109
82} // namespace Common 110} // namespace Common
diff --git a/src/common/swap.h b/src/common/swap.h
index 7665942a2..a80e191dc 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -394,7 +394,7 @@ public:
394 template <typename S, typename T2, typename F2> 394 template <typename S, typename T2, typename F2>
395 friend S operator%(const S& p, const swapped_t v); 395 friend S operator%(const S& p, const swapped_t v);
396 396
397 // Arithmetics + assignements 397 // Arithmetics + assignments
398 template <typename S, typename T2, typename F2> 398 template <typename S, typename T2, typename F2>
399 friend S operator+=(const S& p, const swapped_t v); 399 friend S operator+=(const S& p, const swapped_t v);
400 400
@@ -451,7 +451,7 @@ S operator%(const S& i, const swap_struct_t<T, F> v) {
451 return i % v.swap(); 451 return i % v.swap();
452} 452}
453 453
454// Arithmetics + assignements 454// Arithmetics + assignments
455template <typename S, typename T, typename F> 455template <typename S, typename T, typename F>
456S& operator+=(S& i, const swap_struct_t<T, F> v) { 456S& operator+=(S& i, const swap_struct_t<T, F> v) {
457 i += v.swap(); 457 i += v.swap();
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/common/virtual_buffer.h b/src/common/virtual_buffer.h
index 91d430036..fb1a6f81f 100644
--- a/src/common/virtual_buffer.h
+++ b/src/common/virtual_buffer.h
@@ -15,10 +15,12 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;
15template <typename T> 15template <typename T>
16class VirtualBuffer final { 16class VirtualBuffer final {
17public: 17public:
18 static_assert( 18 // TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
19 std::is_trivially_constructible_v<T>, 19 // using std::atomic_ref once libc++ has support for it
20 "T must be trivially constructible, as non-trivial constructors will not be executed " 20 // static_assert(
21 "with the current allocator"); 21 // std::is_trivially_constructible_v<T>,
22 // "T must be trivially constructible, as non-trivial constructors will not be executed "
23 // "with the current allocator");
22 24
23 constexpr VirtualBuffer() = default; 25 constexpr VirtualBuffer() = default;
24 explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} { 26 explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
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/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index e9c74b1a6..0831dd5d2 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -133,6 +133,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
133 config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( 133 config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
134 page_table.pointers.data()); 134 page_table.pointers.data());
135 config.absolute_offset_page_table = true; 135 config.absolute_offset_page_table = true;
136 config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
136 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; 137 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
137 config.only_detect_misalignment_via_page_table_on_page_boundary = true; 138 config.only_detect_misalignment_via_page_table_on_page_boundary = true;
138 139
@@ -180,6 +181,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
180 if (Settings::values.cpuopt_unsafe_reduce_fp_error) { 181 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
181 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; 182 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
182 } 183 }
184 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
185 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
186 }
183 } 187 }
184 188
185 return std::make_unique<Dynarmic::A32::Jit>(config); 189 return std::make_unique<Dynarmic::A32::Jit>(config);
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 7a4eb88a2..4c5ebca22 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -152,6 +152,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
152 // Memory 152 // Memory
153 config.page_table = reinterpret_cast<void**>(page_table.pointers.data()); 153 config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
154 config.page_table_address_space_bits = address_space_bits; 154 config.page_table_address_space_bits = address_space_bits;
155 config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
155 config.silently_mirror_page_table = false; 156 config.silently_mirror_page_table = false;
156 config.absolute_offset_page_table = true; 157 config.absolute_offset_page_table = true;
157 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; 158 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
@@ -211,6 +212,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
211 if (Settings::values.cpuopt_unsafe_reduce_fp_error) { 212 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
212 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; 213 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
213 } 214 }
215 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
217 }
214 } 218 }
215 219
216 return std::make_shared<Dynarmic::A64::Jit>(config); 220 return std::make_shared<Dynarmic::A64::Jit>(config);
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/memory/memory_block.h b/src/core/hle/kernel/memory/memory_block.h
index 37fe19916..83acece1e 100644
--- a/src/core/hle/kernel/memory/memory_block.h
+++ b/src/core/hle/kernel/memory/memory_block.h
@@ -73,12 +73,12 @@ enum class MemoryState : u32 {
73 ThreadLocal = 73 ThreadLocal =
74 static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, 74 static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
75 75
76 Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc | 76 Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
77 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | 77 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
78 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 78 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
79 79
80 SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc | 80 SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
81 FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 81 FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
82 82
83 SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | 83 SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
84 FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 84 FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
@@ -111,8 +111,8 @@ static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09);
111static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A); 111static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A);
112static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B); 112static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B);
113static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C); 113static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C);
114static_assert(static_cast<u32>(MemoryState::Transfered) == 0x015C3C0D); 114static_assert(static_cast<u32>(MemoryState::Transferred) == 0x015C3C0D);
115static_assert(static_cast<u32>(MemoryState::SharedTransfered) == 0x005C380E); 115static_assert(static_cast<u32>(MemoryState::SharedTransferred) == 0x005C380E);
116static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F); 116static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F);
117static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010); 117static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010);
118static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811); 118static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811);
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp
index f53a7be82..080886554 100644
--- a/src/core/hle/kernel/memory/page_table.cpp
+++ b/src/core/hle/kernel/memory/page_table.cpp
@@ -265,7 +265,7 @@ ResultCode PageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_t
265 physical_memory_usage = 0; 265 physical_memory_usage = 0;
266 memory_pool = pool; 266 memory_pool = pool;
267 267
268 page_table_impl.Resize(address_space_width, PageBits, true); 268 page_table_impl.Resize(address_space_width, PageBits);
269 269
270 return InitializeMemoryLayout(start, end); 270 return InitializeMemoryLayout(start, end);
271} 271}
@@ -1007,8 +1007,8 @@ constexpr VAddr PageTable::GetRegionAddress(MemoryState state) const {
1007 case MemoryState::Shared: 1007 case MemoryState::Shared:
1008 case MemoryState::AliasCode: 1008 case MemoryState::AliasCode:
1009 case MemoryState::AliasCodeData: 1009 case MemoryState::AliasCodeData:
1010 case MemoryState::Transfered: 1010 case MemoryState::Transferred:
1011 case MemoryState::SharedTransfered: 1011 case MemoryState::SharedTransferred:
1012 case MemoryState::SharedCode: 1012 case MemoryState::SharedCode:
1013 case MemoryState::GeneratedCode: 1013 case MemoryState::GeneratedCode:
1014 case MemoryState::CodeOut: 1014 case MemoryState::CodeOut:
@@ -1042,8 +1042,8 @@ constexpr std::size_t PageTable::GetRegionSize(MemoryState state) const {
1042 case MemoryState::Shared: 1042 case MemoryState::Shared:
1043 case MemoryState::AliasCode: 1043 case MemoryState::AliasCode:
1044 case MemoryState::AliasCodeData: 1044 case MemoryState::AliasCodeData:
1045 case MemoryState::Transfered: 1045 case MemoryState::Transferred:
1046 case MemoryState::SharedTransfered: 1046 case MemoryState::SharedTransferred:
1047 case MemoryState::SharedCode: 1047 case MemoryState::SharedCode:
1048 case MemoryState::GeneratedCode: 1048 case MemoryState::GeneratedCode:
1049 case MemoryState::CodeOut: 1049 case MemoryState::CodeOut:
@@ -1080,8 +1080,8 @@ constexpr bool PageTable::CanContain(VAddr addr, std::size_t size, MemoryState s
1080 case MemoryState::AliasCodeData: 1080 case MemoryState::AliasCodeData:
1081 case MemoryState::Stack: 1081 case MemoryState::Stack:
1082 case MemoryState::ThreadLocal: 1082 case MemoryState::ThreadLocal:
1083 case MemoryState::Transfered: 1083 case MemoryState::Transferred:
1084 case MemoryState::SharedTransfered: 1084 case MemoryState::SharedTransferred:
1085 case MemoryState::SharedCode: 1085 case MemoryState::SharedCode:
1086 case MemoryState::GeneratedCode: 1086 case MemoryState::GeneratedCode:
1087 case MemoryState::CodeOut: 1087 case MemoryState::CodeOut:
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/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 986724beb..11e1d8e2d 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -23,8 +23,8 @@ enum class MemoryState : u32 {
23 Ipc = 0x0A, 23 Ipc = 0x0A,
24 Stack = 0x0B, 24 Stack = 0x0B,
25 ThreadLocal = 0x0C, 25 ThreadLocal = 0x0C,
26 Transfered = 0x0D, 26 Transferred = 0x0D,
27 SharedTransfered = 0x0E, 27 SharedTransferred = 0x0E,
28 SharedCode = 0x0F, 28 SharedCode = 0x0F,
29 Inaccessible = 0x10, 29 Inaccessible = 0x10,
30 NonSecureIpc = 0x11, 30 NonSecureIpc = 0x11,
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index cb13210e5..c9808060a 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -560,14 +560,14 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest
560 560
561AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) { 561AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) {
562 on_new_message = 562 on_new_message =
563 Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageRecieved"); 563 Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageReceived");
564 on_operation_mode_changed = 564 on_operation_mode_changed =
565 Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged"); 565 Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged");
566} 566}
567 567
568AppletMessageQueue::~AppletMessageQueue() = default; 568AppletMessageQueue::~AppletMessageQueue() = default;
569 569
570const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetMesssageRecieveEvent() const { 570const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetMessageReceiveEvent() const {
571 return on_new_message.readable; 571 return on_new_message.readable;
572} 572}
573 573
@@ -675,7 +675,7 @@ void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
675 675
676 IPC::ResponseBuilder rb{ctx, 2, 1}; 676 IPC::ResponseBuilder rb{ctx, 2, 1};
677 rb.Push(RESULT_SUCCESS); 677 rb.Push(RESULT_SUCCESS);
678 rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent()); 678 rb.PushCopyObjects(msg_queue->GetMessageReceiveEvent());
679} 679}
680 680
681void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { 681void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b1da0d081..f51aca1af 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -55,7 +55,7 @@ public:
55 explicit AppletMessageQueue(Kernel::KernelCore& kernel); 55 explicit AppletMessageQueue(Kernel::KernelCore& kernel);
56 ~AppletMessageQueue(); 56 ~AppletMessageQueue();
57 57
58 const std::shared_ptr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const; 58 const std::shared_ptr<Kernel::ReadableEvent>& GetMessageReceiveEvent() const;
59 const std::shared_ptr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const; 59 const std::shared_ptr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const;
60 void PushMessage(AppletMessage msg); 60 void PushMessage(AppletMessage msg);
61 AppletMessage PopMessage(); 61 AppletMessage PopMessage();
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..0e6bde9f5 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,26 @@ 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 is_connect = true;
161}
162
135void BufferQueue::Disconnect() { 163void BufferQueue::Disconnect() {
136 buffers.fill({}); 164 buffers.fill({});
137 queue_sequence.clear(); 165 queue_sequence.clear();
138 buffer_wait_event.writable->Signal(); 166 buffer_wait_event.writable->Signal();
167 is_connect = false;
168 condition.notify_one();
139} 169}
140 170
141u32 BufferQueue::Query(QueryType type) { 171u32 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/memory.cpp b/src/core/memory.cpp
index 54a848936..11609682a 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -4,7 +4,6 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include <mutex>
8#include <optional> 7#include <optional>
9#include <utility> 8#include <utility>
10 9
@@ -45,44 +44,16 @@ struct Memory::Impl {
45 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); 44 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
46 } 45 }
47 46
48 void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
49 Common::MemoryHookPointer mmio_handler) {
50 UNIMPLEMENTED();
51 }
52
53 void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { 47 void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
54 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 48 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
55 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 49 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
56 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped); 50 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped);
57 } 51 }
58 52
59 void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
60 Common::MemoryHookPointer hook) {
61 UNIMPLEMENTED();
62 }
63
64 void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
65 Common::MemoryHookPointer hook) {
66 UNIMPLEMENTED();
67 }
68
69 bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { 53 bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
70 const auto& page_table = process.PageTable().PageTableImpl(); 54 const auto& page_table = process.PageTable().PageTableImpl();
71 55 const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType();
72 const u8* const page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; 56 return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
73 if (page_pointer != nullptr) {
74 return true;
75 }
76
77 if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory) {
78 return true;
79 }
80
81 if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special) {
82 return false;
83 }
84
85 return false;
86 } 57 }
87 58
88 bool IsValidVirtualAddress(VAddr vaddr) const { 59 bool IsValidVirtualAddress(VAddr vaddr) const {
@@ -100,17 +71,15 @@ struct Memory::Impl {
100 } 71 }
101 72
102 u8* GetPointer(const VAddr vaddr) const { 73 u8* GetPointer(const VAddr vaddr) const {
103 u8* const page_pointer{current_page_table->pointers[vaddr >> PAGE_BITS]}; 74 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
104 if (page_pointer) { 75 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
105 return page_pointer + vaddr; 76 return pointer + vaddr;
106 } 77 }
107 78 const auto type = Common::PageTable::PageInfo::ExtractType(raw_pointer);
108 if (current_page_table->attributes[vaddr >> PAGE_BITS] == 79 if (type == Common::PageType::RasterizerCachedMemory) {
109 Common::PageType::RasterizerCachedMemory) {
110 return GetPointerFromRasterizerCachedMemory(vaddr); 80 return GetPointerFromRasterizerCachedMemory(vaddr);
111 } 81 }
112 82 return nullptr;
113 return {};
114 } 83 }
115 84
116 u8 Read8(const VAddr addr) { 85 u8 Read8(const VAddr addr) {
@@ -222,7 +191,8 @@ struct Memory::Impl {
222 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 191 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
223 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 192 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
224 193
225 switch (page_table.attributes[page_index]) { 194 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
195 switch (type) {
226 case Common::PageType::Unmapped: { 196 case Common::PageType::Unmapped: {
227 LOG_ERROR(HW_Memory, 197 LOG_ERROR(HW_Memory,
228 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 198 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -231,10 +201,8 @@ struct Memory::Impl {
231 break; 201 break;
232 } 202 }
233 case Common::PageType::Memory: { 203 case Common::PageType::Memory: {
234 DEBUG_ASSERT(page_table.pointers[page_index]); 204 DEBUG_ASSERT(pointer);
235 205 const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
236 const u8* const src_ptr =
237 page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
238 std::memcpy(dest_buffer, src_ptr, copy_amount); 206 std::memcpy(dest_buffer, src_ptr, copy_amount);
239 break; 207 break;
240 } 208 }
@@ -268,7 +236,8 @@ struct Memory::Impl {
268 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 236 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
269 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 237 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
270 238
271 switch (page_table.attributes[page_index]) { 239 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
240 switch (type) {
272 case Common::PageType::Unmapped: { 241 case Common::PageType::Unmapped: {
273 LOG_ERROR(HW_Memory, 242 LOG_ERROR(HW_Memory,
274 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 243 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -277,10 +246,8 @@ struct Memory::Impl {
277 break; 246 break;
278 } 247 }
279 case Common::PageType::Memory: { 248 case Common::PageType::Memory: {
280 DEBUG_ASSERT(page_table.pointers[page_index]); 249 DEBUG_ASSERT(pointer);
281 250 const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
282 const u8* const src_ptr =
283 page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
284 std::memcpy(dest_buffer, src_ptr, copy_amount); 251 std::memcpy(dest_buffer, src_ptr, copy_amount);
285 break; 252 break;
286 } 253 }
@@ -320,7 +287,8 @@ struct Memory::Impl {
320 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 287 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
321 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 288 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
322 289
323 switch (page_table.attributes[page_index]) { 290 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
291 switch (type) {
324 case Common::PageType::Unmapped: { 292 case Common::PageType::Unmapped: {
325 LOG_ERROR(HW_Memory, 293 LOG_ERROR(HW_Memory,
326 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 294 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -328,10 +296,8 @@ struct Memory::Impl {
328 break; 296 break;
329 } 297 }
330 case Common::PageType::Memory: { 298 case Common::PageType::Memory: {
331 DEBUG_ASSERT(page_table.pointers[page_index]); 299 DEBUG_ASSERT(pointer);
332 300 u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
333 u8* const dest_ptr =
334 page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
335 std::memcpy(dest_ptr, src_buffer, copy_amount); 301 std::memcpy(dest_ptr, src_buffer, copy_amount);
336 break; 302 break;
337 } 303 }
@@ -364,7 +330,8 @@ struct Memory::Impl {
364 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 330 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
365 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 331 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
366 332
367 switch (page_table.attributes[page_index]) { 333 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
334 switch (type) {
368 case Common::PageType::Unmapped: { 335 case Common::PageType::Unmapped: {
369 LOG_ERROR(HW_Memory, 336 LOG_ERROR(HW_Memory,
370 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 337 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -372,10 +339,8 @@ struct Memory::Impl {
372 break; 339 break;
373 } 340 }
374 case Common::PageType::Memory: { 341 case Common::PageType::Memory: {
375 DEBUG_ASSERT(page_table.pointers[page_index]); 342 DEBUG_ASSERT(pointer);
376 343 u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
377 u8* const dest_ptr =
378 page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
379 std::memcpy(dest_ptr, src_buffer, copy_amount); 344 std::memcpy(dest_ptr, src_buffer, copy_amount);
380 break; 345 break;
381 } 346 }
@@ -414,7 +379,8 @@ struct Memory::Impl {
414 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 379 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
415 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 380 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
416 381
417 switch (page_table.attributes[page_index]) { 382 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
383 switch (type) {
418 case Common::PageType::Unmapped: { 384 case Common::PageType::Unmapped: {
419 LOG_ERROR(HW_Memory, 385 LOG_ERROR(HW_Memory,
420 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 386 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -422,10 +388,8 @@ struct Memory::Impl {
422 break; 388 break;
423 } 389 }
424 case Common::PageType::Memory: { 390 case Common::PageType::Memory: {
425 DEBUG_ASSERT(page_table.pointers[page_index]); 391 DEBUG_ASSERT(pointer);
426 392 u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
427 u8* dest_ptr =
428 page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
429 std::memset(dest_ptr, 0, copy_amount); 393 std::memset(dest_ptr, 0, copy_amount);
430 break; 394 break;
431 } 395 }
@@ -461,7 +425,8 @@ struct Memory::Impl {
461 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 425 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
462 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 426 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
463 427
464 switch (page_table.attributes[page_index]) { 428 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
429 switch (type) {
465 case Common::PageType::Unmapped: { 430 case Common::PageType::Unmapped: {
466 LOG_ERROR(HW_Memory, 431 LOG_ERROR(HW_Memory,
467 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 432 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -470,9 +435,8 @@ struct Memory::Impl {
470 break; 435 break;
471 } 436 }
472 case Common::PageType::Memory: { 437 case Common::PageType::Memory: {
473 DEBUG_ASSERT(page_table.pointers[page_index]); 438 DEBUG_ASSERT(pointer);
474 const u8* src_ptr = 439 const u8* src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
475 page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
476 WriteBlock(process, dest_addr, src_ptr, copy_amount); 440 WriteBlock(process, dest_addr, src_ptr, copy_amount);
477 break; 441 break;
478 } 442 }
@@ -498,34 +462,19 @@ struct Memory::Impl {
498 return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size); 462 return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size);
499 } 463 }
500 464
501 struct PageEntry {
502 u8* const pointer;
503 const Common::PageType attribute;
504 };
505
506 PageEntry SafePageEntry(std::size_t base) const {
507 std::lock_guard lock{rasterizer_cache_guard};
508 return {
509 .pointer = current_page_table->pointers[base],
510 .attribute = current_page_table->attributes[base],
511 };
512 }
513
514 void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { 465 void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
515 std::lock_guard lock{rasterizer_cache_guard};
516 if (vaddr == 0) { 466 if (vaddr == 0) {
517 return; 467 return;
518 } 468 }
519
520 // Iterate over a contiguous CPU address space, which corresponds to the specified GPU 469 // Iterate over a contiguous CPU address space, which corresponds to the specified GPU
521 // address space, marking the region as un/cached. The region is marked un/cached at a 470 // address space, marking the region as un/cached. The region is marked un/cached at a
522 // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size 471 // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
523 // is different). This assumes the specified GPU address region is contiguous as well. 472 // is different). This assumes the specified GPU address region is contiguous as well.
524 473
525 u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; 474 const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
526 for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { 475 for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
527 Common::PageType& page_type{current_page_table->attributes[vaddr >> PAGE_BITS]}; 476 const Common::PageType page_type{
528 477 current_page_table->pointers[vaddr >> PAGE_BITS].Type()};
529 if (cached) { 478 if (cached) {
530 // Switch page type to cached if now cached 479 // Switch page type to cached if now cached
531 switch (page_type) { 480 switch (page_type) {
@@ -534,8 +483,8 @@ struct Memory::Impl {
534 // space, for example, a system module need not have a VRAM mapping. 483 // space, for example, a system module need not have a VRAM mapping.
535 break; 484 break;
536 case Common::PageType::Memory: 485 case Common::PageType::Memory:
537 page_type = Common::PageType::RasterizerCachedMemory; 486 current_page_table->pointers[vaddr >> PAGE_BITS].Store(
538 current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; 487 nullptr, Common::PageType::RasterizerCachedMemory);
539 break; 488 break;
540 case Common::PageType::RasterizerCachedMemory: 489 case Common::PageType::RasterizerCachedMemory:
541 // There can be more than one GPU region mapped per CPU region, so it's common 490 // There can be more than one GPU region mapped per CPU region, so it's common
@@ -556,16 +505,16 @@ struct Memory::Impl {
556 // that this area is already unmarked as cached. 505 // that this area is already unmarked as cached.
557 break; 506 break;
558 case Common::PageType::RasterizerCachedMemory: { 507 case Common::PageType::RasterizerCachedMemory: {
559 u8* pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)}; 508 u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)};
560 if (pointer == nullptr) { 509 if (pointer == nullptr) {
561 // It's possible that this function has been called while updating the 510 // It's possible that this function has been called while updating the
562 // pagetable after unmapping a VMA. In that case the underlying VMA will no 511 // pagetable after unmapping a VMA. In that case the underlying VMA will no
563 // longer exist, and we should just leave the pagetable entry blank. 512 // longer exist, and we should just leave the pagetable entry blank.
564 page_type = Common::PageType::Unmapped; 513 current_page_table->pointers[vaddr >> PAGE_BITS].Store(
514 nullptr, Common::PageType::Unmapped);
565 } else { 515 } else {
566 current_page_table->pointers[vaddr >> PAGE_BITS] = 516 current_page_table->pointers[vaddr >> PAGE_BITS].Store(
567 pointer - (vaddr & ~PAGE_MASK); 517 pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);
568 page_type = Common::PageType::Memory;
569 } 518 }
570 break; 519 break;
571 } 520 }
@@ -595,7 +544,7 @@ struct Memory::Impl {
595 auto& gpu = system.GPU(); 544 auto& gpu = system.GPU();
596 for (u64 i = 0; i < size; i++) { 545 for (u64 i = 0; i < size; i++) {
597 const auto page = base + i; 546 const auto page = base + i;
598 if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) { 547 if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) {
599 gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE); 548 gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
600 } 549 }
601 } 550 }
@@ -610,20 +559,18 @@ struct Memory::Impl {
610 "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE); 559 "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
611 560
612 while (base != end) { 561 while (base != end) {
613 page_table.attributes[base] = type; 562 page_table.pointers[base].Store(nullptr, type);
614 page_table.pointers[base] = nullptr;
615 page_table.backing_addr[base] = 0; 563 page_table.backing_addr[base] = 0;
616 564
617 base += 1; 565 base += 1;
618 } 566 }
619 } else { 567 } else {
620 while (base != end) { 568 while (base != end) {
621 page_table.pointers[base] = 569 page_table.pointers[base].Store(
622 system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS); 570 system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type);
623 page_table.attributes[base] = type;
624 page_table.backing_addr[base] = target - (base << PAGE_BITS); 571 page_table.backing_addr[base] = target - (base << PAGE_BITS);
625 572
626 ASSERT_MSG(page_table.pointers[base], 573 ASSERT_MSG(page_table.pointers[base].Pointer(),
627 "memory mapping base yield a nullptr within the table"); 574 "memory mapping base yield a nullptr within the table");
628 575
629 base += 1; 576 base += 1;
@@ -646,21 +593,13 @@ struct Memory::Impl {
646 template <typename T> 593 template <typename T>
647 T Read(const VAddr vaddr) { 594 T Read(const VAddr vaddr) {
648 // Avoid adding any extra logic to this fast-path block 595 // Avoid adding any extra logic to this fast-path block
649 if (const u8* const pointer = current_page_table->pointers[vaddr >> PAGE_BITS]) { 596 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
597 if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
650 T value; 598 T value;
651 std::memcpy(&value, &pointer[vaddr], sizeof(T)); 599 std::memcpy(&value, &pointer[vaddr], sizeof(T));
652 return value; 600 return value;
653 } 601 }
654 602 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
655 // Otherwise, we need to grab the page with a lock, in case it is currently being modified
656 const auto entry = SafePageEntry(vaddr >> PAGE_BITS);
657 if (entry.pointer) {
658 T value;
659 std::memcpy(&value, &entry.pointer[vaddr], sizeof(T));
660 return value;
661 }
662
663 switch (entry.attribute) {
664 case Common::PageType::Unmapped: 603 case Common::PageType::Unmapped:
665 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); 604 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
666 return 0; 605 return 0;
@@ -692,20 +631,12 @@ struct Memory::Impl {
692 template <typename T> 631 template <typename T>
693 void Write(const VAddr vaddr, const T data) { 632 void Write(const VAddr vaddr, const T data) {
694 // Avoid adding any extra logic to this fast-path block 633 // Avoid adding any extra logic to this fast-path block
695 if (u8* const pointer = current_page_table->pointers[vaddr >> PAGE_BITS]) { 634 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
635 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
696 std::memcpy(&pointer[vaddr], &data, sizeof(T)); 636 std::memcpy(&pointer[vaddr], &data, sizeof(T));
697 return; 637 return;
698 } 638 }
699 639 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
700 // Otherwise, we need to grab the page with a lock, in case it is currently being modified
701 const auto entry = SafePageEntry(vaddr >> PAGE_BITS);
702 if (entry.pointer) {
703 // Memory was mapped, we are done
704 std::memcpy(&entry.pointer[vaddr], &data, sizeof(T));
705 return;
706 }
707
708 switch (entry.attribute) {
709 case Common::PageType::Unmapped: 640 case Common::PageType::Unmapped:
710 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 641 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
711 static_cast<u32>(data), vaddr); 642 static_cast<u32>(data), vaddr);
@@ -726,15 +657,13 @@ struct Memory::Impl {
726 657
727 template <typename T> 658 template <typename T>
728 bool WriteExclusive(const VAddr vaddr, const T data, const T expected) { 659 bool WriteExclusive(const VAddr vaddr, const T data, const T expected) {
729 u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; 660 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
730 if (page_pointer != nullptr) { 661 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
731 // NOTE: Avoid adding any extra logic to this fast-path block 662 // NOTE: Avoid adding any extra logic to this fast-path block
732 auto* pointer = reinterpret_cast<volatile T*>(&page_pointer[vaddr]); 663 const auto volatile_pointer = reinterpret_cast<volatile T*>(&pointer[vaddr]);
733 return Common::AtomicCompareAndSwap(pointer, data, expected); 664 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
734 } 665 }
735 666 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
736 const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
737 switch (type) {
738 case Common::PageType::Unmapped: 667 case Common::PageType::Unmapped:
739 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 668 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
740 static_cast<u32>(data), vaddr); 669 static_cast<u32>(data), vaddr);
@@ -755,15 +684,13 @@ struct Memory::Impl {
755 } 684 }
756 685
757 bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) { 686 bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) {
758 u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; 687 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
759 if (page_pointer != nullptr) { 688 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
760 // NOTE: Avoid adding any extra logic to this fast-path block 689 // NOTE: Avoid adding any extra logic to this fast-path block
761 auto* pointer = reinterpret_cast<volatile u64*>(&page_pointer[vaddr]); 690 const auto volatile_pointer = reinterpret_cast<volatile u64*>(&pointer[vaddr]);
762 return Common::AtomicCompareAndSwap(pointer, data, expected); 691 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
763 } 692 }
764 693 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
765 const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
766 switch (type) {
767 case Common::PageType::Unmapped: 694 case Common::PageType::Unmapped:
768 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8, 695 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8,
769 static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr); 696 static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr);
@@ -783,7 +710,6 @@ struct Memory::Impl {
783 return true; 710 return true;
784 } 711 }
785 712
786 mutable std::mutex rasterizer_cache_guard;
787 Common::PageTable* current_page_table = nullptr; 713 Common::PageTable* current_page_table = nullptr;
788 Core::System& system; 714 Core::System& system;
789}; 715};
@@ -799,25 +725,10 @@ void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size
799 impl->MapMemoryRegion(page_table, base, size, target); 725 impl->MapMemoryRegion(page_table, base, size, target);
800} 726}
801 727
802void Memory::MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
803 Common::MemoryHookPointer mmio_handler) {
804 impl->MapIoRegion(page_table, base, size, std::move(mmio_handler));
805}
806
807void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { 728void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
808 impl->UnmapRegion(page_table, base, size); 729 impl->UnmapRegion(page_table, base, size);
809} 730}
810 731
811void Memory::AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
812 Common::MemoryHookPointer hook) {
813 impl->AddDebugHook(page_table, base, size, std::move(hook));
814}
815
816void Memory::RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
817 Common::MemoryHookPointer hook) {
818 impl->RemoveDebugHook(page_table, base, size, std::move(hook));
819}
820
821bool Memory::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { 732bool Memory::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
822 return impl->IsValidVirtualAddress(process, vaddr); 733 return impl->IsValidVirtualAddress(process, vaddr);
823} 734}
diff --git a/src/core/memory.h b/src/core/memory.h
index 4a1cc63f4..705ebb23d 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -8,7 +8,6 @@
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/memory_hook.h"
12 11
13namespace Common { 12namespace Common {
14struct PageTable; 13struct PageTable;
@@ -78,17 +77,6 @@ public:
78 void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target); 77 void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target);
79 78
80 /** 79 /**
81 * Maps a region of the emulated process address space as a IO region.
82 *
83 * @param page_table The page table of the emulated process.
84 * @param base The address to start mapping at. Must be page-aligned.
85 * @param size The amount of bytes to map. Must be page-aligned.
86 * @param mmio_handler The handler that backs the mapping.
87 */
88 void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
89 Common::MemoryHookPointer mmio_handler);
90
91 /**
92 * Unmaps a region of the emulated process address space. 80 * Unmaps a region of the emulated process address space.
93 * 81 *
94 * @param page_table The page table of the emulated process. 82 * @param page_table The page table of the emulated process.
@@ -98,28 +86,6 @@ public:
98 void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size); 86 void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
99 87
100 /** 88 /**
101 * Adds a memory hook to intercept reads and writes to given region of memory.
102 *
103 * @param page_table The page table of the emulated process
104 * @param base The starting address to apply the hook to.
105 * @param size The size of the memory region to apply the hook to, in bytes.
106 * @param hook The hook to apply to the region of memory.
107 */
108 void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
109 Common::MemoryHookPointer hook);
110
111 /**
112 * Removes a memory hook from a given range of memory.
113 *
114 * @param page_table The page table of the emulated process.
115 * @param base The starting address to remove the hook from.
116 * @param size The size of the memory region to remove the hook from, in bytes.
117 * @param hook The hook to remove from the specified region of memory.
118 */
119 void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
120 Common::MemoryHookPointer hook);
121
122 /**
123 * Checks whether or not the supplied address is a valid virtual 89 * Checks whether or not the supplied address is a valid virtual
124 * address for the given process. 90 * address for the given process.
125 * 91 *
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..a324530bd 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -131,6 +131,7 @@ struct Values {
131 131
132 bool cpuopt_unsafe_unfuse_fma; 132 bool cpuopt_unsafe_unfuse_fma;
133 bool cpuopt_unsafe_reduce_fp_error; 133 bool cpuopt_unsafe_reduce_fp_error;
134 bool cpuopt_unsafe_inaccurate_nan;
134 135
135 // Renderer 136 // Renderer
136 Setting<RendererBackend> renderer_backend; 137 Setting<RendererBackend> renderer_backend;
@@ -221,7 +222,7 @@ struct Values {
221 bool disable_macro_jit; 222 bool disable_macro_jit;
222 bool extended_logging; 223 bool extended_logging;
223 224
224 // Misceallaneous 225 // Miscellaneous
225 std::string log_filter; 226 std::string log_filter;
226 bool use_dev_keys; 227 bool use_dev_keys;
227 228
@@ -257,7 +258,4 @@ void LogSettings();
257// Restore the global state of all applicable settings in the Values struct 258// Restore the global state of all applicable settings in the Values struct
258void RestoreGlobalState(bool is_powered_on); 259void RestoreGlobalState(bool is_powered_on);
259 260
260// Fixes settings that are known to cause issues with the emulator
261void Sanitize();
262
263} // namespace Settings 261} // namespace Settings
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index f1256c9da..7a6c545bd 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -120,17 +120,17 @@ private:
120 /// For use in initialization, querying devices to find the adapter 120 /// For use in initialization, querying devices to find the adapter
121 void Setup(); 121 void Setup();
122 122
123 /// Resets status of all GC controller devices to a disconected state 123 /// Resets status of all GC controller devices to a disconnected state
124 void ResetDevices(); 124 void ResetDevices();
125 125
126 /// Resets status of device connected to a disconected state 126 /// Resets status of device connected to a disconnected state
127 void ResetDevice(std::size_t port); 127 void ResetDevice(std::size_t port);
128 128
129 /// Returns true if we successfully gain access to GC Adapter 129 /// Returns true if we successfully gain access to GC Adapter
130 bool CheckDeviceAccess(); 130 bool CheckDeviceAccess();
131 131
132 /// Captures GC Adapter endpoint address 132 /// Captures GC Adapter endpoint address
133 /// Returns true if the endpoind was set correctly 133 /// Returns true if the endpoint was set correctly
134 bool GetGCEndpoint(libusb_device* device); 134 bool GetGCEndpoint(libusb_device* device);
135 135
136 /// For shutting down, clear all data, join all threads, release usb 136 /// For shutting down, clear all data, join all threads, release usb
diff --git a/src/input_common/motion_input.cpp b/src/input_common/motion_input.cpp
index f77ba535d..6a65f175e 100644
--- a/src/input_common/motion_input.cpp
+++ b/src/input_common/motion_input.cpp
@@ -129,7 +129,7 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
129 rad_gyro += ki * integral_error; 129 rad_gyro += ki * integral_error;
130 rad_gyro += kd * derivative_error; 130 rad_gyro += kd * derivative_error;
131 } else { 131 } else {
132 // Give more weight to acelerometer values to compensate for the lack of gyro 132 // Give more weight to accelerometer values to compensate for the lack of gyro
133 rad_gyro += 35.0f * kp * real_error; 133 rad_gyro += 35.0f * kp * real_error;
134 rad_gyro += 10.0f * ki * integral_error; 134 rad_gyro += 10.0f * ki * integral_error;
135 rad_gyro += 10.0f * kd * derivative_error; 135 rad_gyro += 10.0f * kd * derivative_error;
diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h
index 65e64bee7..58803c1bf 100644
--- a/src/input_common/mouse/mouse_input.h
+++ b/src/input_common/mouse/mouse_input.h
@@ -20,7 +20,7 @@ enum class MouseButton {
20 Left, 20 Left,
21 Wheel, 21 Wheel,
22 Right, 22 Right,
23 Foward, 23 Forward,
24 Backward, 24 Backward,
25 Undefined, 25 Undefined,
26}; 26};
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 0b531f698..d32eb732a 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -1030,11 +1030,44 @@ public:
1030 } 1030 }
1031 return {}; 1031 return {};
1032 } 1032 }
1033 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const { 1033 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(SDL_Event& event) {
1034 switch (event.type) { 1034 switch (event.type) {
1035 case SDL_JOYAXISMOTION: 1035 case SDL_JOYAXISMOTION:
1036 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 1036 if (!axis_memory.count(event.jaxis.which) ||
1037 !axis_memory[event.jaxis.which].count(event.jaxis.axis)) {
1038 axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value;
1039 axis_event_count[event.jaxis.which][event.jaxis.axis] = 1;
1037 break; 1040 break;
1041 } else {
1042 axis_event_count[event.jaxis.which][event.jaxis.axis]++;
1043 // The joystick and axis exist in our map if we take this branch, so no checks
1044 // needed
1045 if (std::abs(
1046 (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) /
1047 32767.0) < 0.5) {
1048 break;
1049 } else {
1050 if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 &&
1051 IsAxisAtPole(event.jaxis.value) &&
1052 IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) {
1053 // If we have exactly two events and both are near a pole, this is
1054 // likely a digital input masquerading as an analog axis; Instead of
1055 // trying to look at the direction the axis travelled, assume the first
1056 // event was press and the second was release; This should handle most
1057 // digital axes while deferring to the direction of travel for analog
1058 // axes
1059 event.jaxis.value = static_cast<Sint16>(
1060 std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis]));
1061 } else {
1062 // There are more than two events, so this is likely a true analog axis,
1063 // check the direction it travelled
1064 event.jaxis.value = static_cast<Sint16>(std::copysign(
1065 32767,
1066 event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]));
1067 }
1068 axis_memory.clear();
1069 axis_event_count.clear();
1070 }
1038 } 1071 }
1039 [[fallthrough]]; 1072 [[fallthrough]];
1040 case SDL_JOYBUTTONUP: 1073 case SDL_JOYBUTTONUP:
@@ -1043,6 +1076,16 @@ public:
1043 } 1076 }
1044 return std::nullopt; 1077 return std::nullopt;
1045 } 1078 }
1079
1080private:
1081 // Determine whether an axis value is close to an extreme or center
1082 // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per
1083 // axis, which is why the center must be considered a pole
1084 bool IsAxisAtPole(int16_t value) const {
1085 return std::abs(value) >= 32767 || std::abs(value) < 327;
1086 }
1087 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, int16_t>> axis_memory;
1088 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, uint32_t>> axis_event_count;
1046}; 1089};
1047 1090
1048class SDLMotionPoller final : public SDLPoller { 1091class SDLMotionPoller final : public SDLPoller {
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index 8686a059c..c5da27a38 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -28,14 +28,14 @@ private:
28 mutable std::mutex mutex; 28 mutable std::mutex mutex;
29}; 29};
30 30
31/// A motion device factory that creates motion devices from JC Adapter 31/// A motion device factory that creates motion devices from a UDP client
32UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_) 32UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)
33 : client(std::move(client_)) {} 33 : client(std::move(client_)) {}
34 34
35/** 35/**
36 * Creates motion device 36 * Creates motion device
37 * @param params contains parameters for creating the device: 37 * @param params contains parameters for creating the device:
38 * - "port": the nth jcpad on the adapter 38 * - "port": the UDP port number
39 */ 39 */
40std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { 40std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
41 auto ip = params.Get("ip", "127.0.0.1"); 41 auto ip = params.Get("ip", "127.0.0.1");
@@ -90,14 +90,14 @@ private:
90 mutable std::mutex mutex; 90 mutable std::mutex mutex;
91}; 91};
92 92
93/// A motion device factory that creates motion devices from JC Adapter 93/// A motion device factory that creates motion devices from a UDP client
94UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_) 94UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)
95 : client(std::move(client_)) {} 95 : client(std::move(client_)) {}
96 96
97/** 97/**
98 * Creates motion device 98 * Creates motion device
99 * @param params contains parameters for creating the device: 99 * @param params contains parameters for creating the device:
100 * - "port": the nth jcpad on the adapter 100 * - "port": the UDP port number
101 */ 101 */
102std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { 102std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
103 auto ip = params.Get("ip", "127.0.0.1"); 103 auto ip = params.Get("ip", "127.0.0.1");
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index d80b0b688..8a606b448 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -4,8 +4,6 @@ add_executable(tests
4 common/fibers.cpp 4 common/fibers.cpp
5 common/param_package.cpp 5 common/param_package.cpp
6 common/ring_buffer.cpp 6 common/ring_buffer.cpp
7 core/arm/arm_test_common.cpp
8 core/arm/arm_test_common.h
9 core/core_timing.cpp 7 core/core_timing.cpp
10 tests.cpp 8 tests.cpp
11) 9)
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp
index 4757dd2b4..d94492fc6 100644
--- a/src/tests/common/fibers.cpp
+++ b/src/tests/common/fibers.cpp
@@ -207,7 +207,7 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) {
207} 207}
208 208
209/** This test checks for fiber thread exchange configuration and validates that fibers are 209/** This test checks for fiber thread exchange configuration and validates that fibers are
210 * that a fiber has been succesfully transfered from one thread to another and that the TLS 210 * that a fiber has been successfully transferred from one thread to another and that the TLS
211 * region of the thread is kept while changing fibers. 211 * region of the thread is kept while changing fibers.
212 */ 212 */
213TEST_CASE("Fibers::InterExchange", "[common]") { 213TEST_CASE("Fibers::InterExchange", "[common]") {
@@ -299,7 +299,7 @@ static void ThreadStart3(u32 id, TestControl3& test_control) {
299} 299}
300 300
301/** This test checks for one two threads racing for starting the same fiber. 301/** This test checks for one two threads racing for starting the same fiber.
302 * It checks execution occured in an ordered manner and by no time there were 302 * It checks execution occurred in an ordered manner and by no time there were
303 * two contexts at the same time. 303 * two contexts at the same time.
304 */ 304 */
305TEST_CASE("Fibers::StartRace", "[common]") { 305TEST_CASE("Fibers::StartRace", "[common]") {
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
deleted file mode 100644
index e54674d11..000000000
--- a/src/tests/core/arm/arm_test_common.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/page_table.h"
8#include "core/core.h"
9#include "core/hle/kernel/memory/page_table.h"
10#include "core/hle/kernel/process.h"
11#include "core/memory.h"
12#include "tests/core/arm/arm_test_common.h"
13
14namespace ArmTests {
15
16TestEnvironment::TestEnvironment(bool mutable_memory_)
17 : mutable_memory(mutable_memory_),
18 test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} {
19 auto& system = Core::System::GetInstance();
20
21 auto process = Kernel::Process::Create(system, "", Kernel::Process::ProcessType::Userland);
22 page_table = &process->PageTable().PageTableImpl();
23
24 system.Memory().MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
25 system.Memory().MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
26
27 kernel.MakeCurrentProcess(process.get());
28}
29
30TestEnvironment::~TestEnvironment() {
31 auto& system = Core::System::GetInstance();
32 system.Memory().UnmapRegion(*page_table, 0x80000000, 0x80000000);
33 system.Memory().UnmapRegion(*page_table, 0x00000000, 0x80000000);
34}
35
36void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) {
37 SetMemory32(vaddr + 0, static_cast<u32>(value));
38 SetMemory32(vaddr + 4, static_cast<u32>(value >> 32));
39}
40
41void TestEnvironment::SetMemory32(VAddr vaddr, u32 value) {
42 SetMemory16(vaddr + 0, static_cast<u16>(value));
43 SetMemory16(vaddr + 2, static_cast<u16>(value >> 16));
44}
45
46void TestEnvironment::SetMemory16(VAddr vaddr, u16 value) {
47 SetMemory8(vaddr + 0, static_cast<u8>(value));
48 SetMemory8(vaddr + 1, static_cast<u8>(value >> 8));
49}
50
51void TestEnvironment::SetMemory8(VAddr vaddr, u8 value) {
52 test_memory->data[vaddr] = value;
53}
54
55std::vector<WriteRecord> TestEnvironment::GetWriteRecords() const {
56 return write_records;
57}
58
59void TestEnvironment::ClearWriteRecords() {
60 write_records.clear();
61}
62
63TestEnvironment::TestMemory::~TestMemory() {}
64
65std::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) {
66 return true;
67}
68
69std::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
70 const auto iter = data.find(addr);
71
72 if (iter == data.end()) {
73 // Some arbitrary data
74 return static_cast<u8>(addr);
75 }
76
77 return iter->second;
78}
79
80std::optional<u16> TestEnvironment::TestMemory::Read16(VAddr addr) {
81 return *Read8(addr) | static_cast<u16>(*Read8(addr + 1)) << 8;
82}
83
84std::optional<u32> TestEnvironment::TestMemory::Read32(VAddr addr) {
85 return *Read16(addr) | static_cast<u32>(*Read16(addr + 2)) << 16;
86}
87
88std::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) {
89 return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32;
90}
91
92bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) {
93 VAddr addr = src_addr;
94 u8* data = static_cast<u8*>(dest_buffer);
95
96 for (std::size_t i = 0; i < size; i++, addr++, data++) {
97 *data = *Read8(addr);
98 }
99
100 return true;
101}
102
103bool TestEnvironment::TestMemory::Write8(VAddr addr, u8 data) {
104 env->write_records.emplace_back(8, addr, data);
105 if (env->mutable_memory)
106 env->SetMemory8(addr, data);
107 return true;
108}
109
110bool TestEnvironment::TestMemory::Write16(VAddr addr, u16 data) {
111 env->write_records.emplace_back(16, addr, data);
112 if (env->mutable_memory)
113 env->SetMemory16(addr, data);
114 return true;
115}
116
117bool TestEnvironment::TestMemory::Write32(VAddr addr, u32 data) {
118 env->write_records.emplace_back(32, addr, data);
119 if (env->mutable_memory)
120 env->SetMemory32(addr, data);
121 return true;
122}
123
124bool TestEnvironment::TestMemory::Write64(VAddr addr, u64 data) {
125 env->write_records.emplace_back(64, addr, data);
126 if (env->mutable_memory)
127 env->SetMemory64(addr, data);
128 return true;
129}
130
131bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer,
132 std::size_t size) {
133 VAddr addr = dest_addr;
134 const u8* data = static_cast<const u8*>(src_buffer);
135
136 for (std::size_t i = 0; i < size; i++, addr++, data++) {
137 env->write_records.emplace_back(8, addr, *data);
138 if (env->mutable_memory)
139 env->SetMemory8(addr, *data);
140 }
141
142 return true;
143}
144
145} // namespace ArmTests
diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h
deleted file mode 100644
index d145dbfcc..000000000
--- a/src/tests/core/arm/arm_test_common.h
+++ /dev/null
@@ -1,93 +0,0 @@
1// Copyright 2016 Citra 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 <tuple>
8#include <unordered_map>
9#include <vector>
10
11#include "common/common_types.h"
12#include "common/memory_hook.h"
13#include "core/hle/kernel/kernel.h"
14
15namespace Common {
16struct PageTable;
17}
18
19namespace ArmTests {
20
21struct WriteRecord {
22 WriteRecord(std::size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {}
23 std::size_t size;
24 VAddr addr;
25 u64 data;
26 bool operator==(const WriteRecord& o) const {
27 return std::tie(size, addr, data) == std::tie(o.size, o.addr, o.data);
28 }
29};
30
31class TestEnvironment final {
32public:
33 /*
34 * Inititalise test environment
35 * @param mutable_memory If false, writes to memory can never be read back.
36 * (Memory is immutable.)
37 */
38 explicit TestEnvironment(bool mutable_memory = false);
39
40 /// Shutdown test environment
41 ~TestEnvironment();
42
43 /// Sets value at memory location vaddr.
44 void SetMemory8(VAddr vaddr, u8 value);
45 void SetMemory16(VAddr vaddr, u16 value);
46 void SetMemory32(VAddr vaddr, u32 value);
47 void SetMemory64(VAddr vaddr, u64 value);
48
49 /**
50 * Whenever Memory::Write{8,16,32,64} is called within the test environment,
51 * a new write-record is made.
52 * @returns A vector of write records made since they were last cleared.
53 */
54 std::vector<WriteRecord> GetWriteRecords() const;
55
56 /// Empties the internal write-record store.
57 void ClearWriteRecords();
58
59private:
60 friend struct TestMemory;
61 struct TestMemory final : Common::MemoryHook {
62 explicit TestMemory(TestEnvironment* env_) : env(env_) {}
63 TestEnvironment* env;
64
65 ~TestMemory() override;
66
67 std::optional<bool> IsValidAddress(VAddr addr) override;
68
69 std::optional<u8> Read8(VAddr addr) override;
70 std::optional<u16> Read16(VAddr addr) override;
71 std::optional<u32> Read32(VAddr addr) override;
72 std::optional<u64> Read64(VAddr addr) override;
73
74 bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override;
75
76 bool Write8(VAddr addr, u8 data) override;
77 bool Write16(VAddr addr, u16 data) override;
78 bool Write32(VAddr addr, u32 data) override;
79 bool Write64(VAddr addr, u64 data) override;
80
81 bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) override;
82
83 std::unordered_map<VAddr, u8> data;
84 };
85
86 bool mutable_memory;
87 std::shared_ptr<TestMemory> test_memory;
88 std::vector<WriteRecord> write_records;
89 Common::PageTable* page_table = nullptr;
90 Kernel::KernelCore kernel;
91};
92
93} // namespace ArmTests
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index f977cf12b..4bd48f706 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/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp
index aa8c9f9de..55e632346 100644
--- a/src/video_core/command_classes/vic.cpp
+++ b/src/video_core/command_classes/vic.cpp
@@ -53,7 +53,7 @@ void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) {
53 53
54void Vic::Execute() { 54void Vic::Execute() {
55 if (output_surface_luma_address == 0) { 55 if (output_surface_luma_address == 0) {
56 LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Recieved 0x{:X}", 56 LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Received 0x{:X}",
57 vic_state.output_surface.luma_offset); 57 vic_state.output_surface.luma_offset);
58 return; 58 return;
59 } 59 }
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/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index fd55ca8a8..9008530d5 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -484,7 +484,7 @@ VkFormat VKDevice::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFla
484} 484}
485 485
486void VKDevice::ReportLoss() const { 486void VKDevice::ReportLoss() const {
487 LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); 487 LOG_CRITICAL(Render_Vulkan, "Device loss occurred!");
488 488
489 // Wait for the log to flush and for Nsight Aftermath to dump the results 489 // Wait for the log to flush and for Nsight Aftermath to dump the results
490 std::this_thread::sleep_for(std::chrono::seconds{15}); 490 std::this_thread::sleep_for(std::chrono::seconds{15});
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/applets/controller.cpp b/src/yuzu/applets/controller.cpp
index a15e8ca2a..c680fd2c2 100644
--- a/src/yuzu/applets/controller.cpp
+++ b/src/yuzu/applets/controller.cpp
@@ -535,7 +535,7 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index)
535 // This emulates a delay between disconnecting and reconnecting controllers as some games 535 // This emulates a delay between disconnecting and reconnecting controllers as some games
536 // do not respond to a change in controller type if it was instantaneous. 536 // do not respond to a change in controller type if it was instantaneous.
537 using namespace std::chrono_literals; 537 using namespace std::chrono_literals;
538 std::this_thread::sleep_for(20ms); 538 std::this_thread::sleep_for(60ms);
539 539
540 UpdateController(controller_type, player_index, player_connected); 540 UpdateController(controller_type, player_index, player_connected);
541} 541}
diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp
index 53a993cf6..8ee03ddb3 100644
--- a/src/yuzu/applets/error.cpp
+++ b/src/yuzu/applets/error.cpp
@@ -19,7 +19,7 @@ QtErrorDisplay::~QtErrorDisplay() = default;
19void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const { 19void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const {
20 callback = std::move(finished); 20 callback = std::move(finished);
21 emit MainWindowDisplayError( 21 emit MainWindowDisplayError(
22 tr("An error has occured.\nPlease try again or contact the developer of the " 22 tr("An error has occurred.\nPlease try again or contact the developer of the "
23 "software.\n\nError Code: %1-%2 (0x%3)") 23 "software.\n\nError Code: %1-%2 (0x%3)")
24 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) 24 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
25 .arg(error.description, 4, 10, QChar::fromLatin1('0')) 25 .arg(error.description, 4, 10, QChar::fromLatin1('0'))
@@ -32,7 +32,7 @@ void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::secon
32 32
33 const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); 33 const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
34 emit MainWindowDisplayError( 34 emit MainWindowDisplayError(
35 tr("An error occured on %1 at %2.\nPlease try again or contact the " 35 tr("An error occurred on %1 at %2.\nPlease try again or contact the "
36 "developer of the software.\n\nError Code: %3-%4 (0x%5)") 36 "developer of the software.\n\nError Code: %3-%4 (0x%5)")
37 .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) 37 .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy")))
38 .arg(date_time.toString(QStringLiteral("h:mm:ss A"))) 38 .arg(date_time.toString(QStringLiteral("h:mm:ss A")))
@@ -46,7 +46,7 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te
46 std::function<void()> finished) const { 46 std::function<void()> finished) const {
47 callback = std::move(finished); 47 callback = std::move(finished);
48 emit MainWindowDisplayError( 48 emit MainWindowDisplayError(
49 tr("An error has occured.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") 49 tr("An error has occurred.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5")
50 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) 50 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
51 .arg(error.description, 4, 10, QChar::fromLatin1('0')) 51 .arg(error.description, 4, 10, QChar::fromLatin1('0'))
52 .arg(error.raw, 8, 16, QChar::fromLatin1('0')) 52 .arg(error.raw, 8, 16, QChar::fromLatin1('0'))
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp
index 649912557..a470056ef 100644
--- a/src/yuzu/compatdb.cpp
+++ b/src/yuzu/compatdb.cpp
@@ -72,7 +72,7 @@ void CompatDB::Submit() {
72void CompatDB::OnTestcaseSubmitted() { 72void CompatDB::OnTestcaseSubmitted() {
73 if (!testcase_watcher.result()) { 73 if (!testcase_watcher.result()) {
74 QMessageBox::critical(this, tr("Communication error"), 74 QMessageBox::critical(this, tr("Communication error"),
75 tr("An error occured while sending the Testcase")); 75 tr("An error occurred while sending the Testcase"));
76 button(NextButton)->setEnabled(true); 76 button(NextButton)->setEnabled(true);
77 button(NextButton)->setText(tr("Next")); 77 button(NextButton)->setText(tr("Next"));
78 button(CancelButton)->setVisible(true); 78 button(CancelButton)->setVisible(true);
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 9fb254986..43cd11ba0 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -764,6 +764,8 @@ void Config::ReadCpuValues() {
764 ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool(); 764 ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool();
765 Settings::values.cpuopt_unsafe_reduce_fp_error = 765 Settings::values.cpuopt_unsafe_reduce_fp_error =
766 ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool(); 766 ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool();
767 Settings::values.cpuopt_unsafe_inaccurate_nan =
768 ReadSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true).toBool();
767 } 769 }
768 770
769 qt_config->endGroup(); 771 qt_config->endGroup();
@@ -1327,6 +1329,8 @@ void Config::SaveCpuValues() {
1327 Settings::values.cpuopt_unsafe_unfuse_fma, true); 1329 Settings::values.cpuopt_unsafe_unfuse_fma, true);
1328 WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), 1330 WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"),
1329 Settings::values.cpuopt_unsafe_reduce_fp_error, true); 1331 Settings::values.cpuopt_unsafe_reduce_fp_error, true);
1332 WriteSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"),
1333 Settings::values.cpuopt_unsafe_inaccurate_nan, true);
1330 } 1334 }
1331 1335
1332 qt_config->endGroup(); 1336 qt_config->endGroup();
@@ -1589,14 +1593,12 @@ void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool
1589 1593
1590void Config::Reload() { 1594void Config::Reload() {
1591 ReadValues(); 1595 ReadValues();
1592 Settings::Sanitize();
1593 // To apply default value changes 1596 // To apply default value changes
1594 SaveValues(); 1597 SaveValues();
1595 Settings::Apply(Core::System::GetInstance()); 1598 Settings::Apply(Core::System::GetInstance());
1596} 1599}
1597 1600
1598void Config::Save() { 1601void Config::Save() {
1599 Settings::Sanitize();
1600 SaveValues(); 1602 SaveValues();
1601} 1603}
1602 1604
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index 37fcd6adc..d055cbd60 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -36,6 +36,8 @@ void ConfigureCpu::SetConfiguration() {
36 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma); 36 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma);
37 ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); 37 ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock);
38 ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error); 38 ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error);
39 ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock);
40 ui->cpuopt_unsafe_inaccurate_nan->setChecked(Settings::values.cpuopt_unsafe_inaccurate_nan);
39} 41}
40 42
41void ConfigureCpu::AccuracyUpdated(int index) { 43void ConfigureCpu::AccuracyUpdated(int index) {
@@ -61,6 +63,7 @@ void ConfigureCpu::ApplyConfiguration() {
61 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); 63 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex());
62 Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked(); 64 Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked();
63 Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked(); 65 Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked();
66 Settings::values.cpuopt_unsafe_inaccurate_nan = ui->cpuopt_unsafe_inaccurate_nan->isChecked();
64} 67}
65 68
66void ConfigureCpu::changeEvent(QEvent* event) { 69void ConfigureCpu::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index ebdd2e6e9..bcd0962e9 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -109,6 +109,18 @@
109 </property> 109 </property>
110 </widget> 110 </widget>
111 </item> 111 </item>
112 <item>
113 <widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan">
114 <property name="text">
115 <string>Inaccurate NaN handling</string>
116 </property>
117 <property name="toolTip">
118 <string>
119 &lt;div&gt;This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.&lt;/div&gt;
120 </string>
121 </property>
122 </widget>
123 </item>
112 </layout> 124 </layout>
113 </widget> 125 </widget>
114 </item> 126 </item>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index d9009091b..567a36d9b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <thread>
7 8
8#include <QSignalBlocker> 9#include <QSignalBlocker>
9#include <QTimer> 10#include <QTimer>
@@ -181,8 +182,18 @@ QList<QWidget*> ConfigureInput::GetSubTabs() const {
181} 182}
182 183
183void ConfigureInput::ApplyConfiguration() { 184void ConfigureInput::ApplyConfiguration() {
184 for (auto controller : player_controllers) { 185 for (auto* controller : player_controllers) {
185 controller->ApplyConfiguration(); 186 controller->ApplyConfiguration();
187 controller->TryDisconnectSelectedController();
188 }
189
190 // This emulates a delay between disconnecting and reconnecting controllers as some games
191 // do not respond to a change in controller type if it was instantaneous.
192 using namespace std::chrono_literals;
193 std::this_thread::sleep_for(60ms);
194
195 for (auto* controller : player_controllers) {
196 controller->TryConnectSelectedController();
186 } 197 }
187 198
188 advanced->ApplyConfiguration(); 199 advanced->ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 3c7500ee3..46ea026e4 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -4,7 +4,6 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <thread>
8#include <utility> 7#include <utility>
9#include <QGridLayout> 8#include <QGridLayout>
10#include <QInputDialog> 9#include <QInputDialog>
@@ -576,6 +575,10 @@ void ConfigureInputPlayer::ApplyConfiguration() {
576 575
577 std::transform(motions_param.begin(), motions_param.end(), motions.begin(), 576 std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
578 [](const Common::ParamPackage& param) { return param.Serialize(); }); 577 [](const Common::ParamPackage& param) { return param.Serialize(); });
578}
579
580void ConfigureInputPlayer::TryConnectSelectedController() {
581 auto& player = Settings::values.players.GetValue()[player_index];
579 582
580 const auto controller_type = 583 const auto controller_type =
581 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 584 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
@@ -588,15 +591,12 @@ void ConfigureInputPlayer::ApplyConfiguration() {
588 return; 591 return;
589 } 592 }
590 593
591 // Disconnect the controller first.
592 UpdateController(controller_type, player_index, false);
593
594 player.controller_type = controller_type; 594 player.controller_type = controller_type;
595 player.connected = player_connected; 595 player.connected = player_connected;
596 596
597 ConfigureVibration::SetVibrationDevices(player_index); 597 ConfigureVibration::SetVibrationDevices(player_index);
598 598
599 // Handheld 599 // Connect/Disconnect Handheld depending on Player 1's controller configuration.
600 if (player_index == 0) { 600 if (player_index == 0) {
601 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; 601 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
602 if (controller_type == Settings::ControllerType::Handheld) { 602 if (controller_type == Settings::ControllerType::Handheld) {
@@ -611,14 +611,26 @@ void ConfigureInputPlayer::ApplyConfiguration() {
611 return; 611 return;
612 } 612 }
613 613
614 // This emulates a delay between disconnecting and reconnecting controllers as some games
615 // do not respond to a change in controller type if it was instantaneous.
616 using namespace std::chrono_literals;
617 std::this_thread::sleep_for(20ms);
618
619 UpdateController(controller_type, player_index, player_connected); 614 UpdateController(controller_type, player_index, player_connected);
620} 615}
621 616
617void ConfigureInputPlayer::TryDisconnectSelectedController() {
618 const auto& player = Settings::values.players.GetValue()[player_index];
619
620 const auto controller_type =
621 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
622 const auto player_connected = ui->groupConnectedController->isChecked() &&
623 controller_type != Settings::ControllerType::Handheld;
624
625 // Do not do anything if the controller configuration has not changed.
626 if (player.controller_type == controller_type && player.connected == player_connected) {
627 return;
628 }
629
630 // Disconnect the controller first.
631 UpdateController(controller_type, player_index, false);
632}
633
622void ConfigureInputPlayer::showEvent(QShowEvent* event) { 634void ConfigureInputPlayer::showEvent(QShowEvent* event) {
623 if (bottom_row == nullptr) { 635 if (bottom_row == nullptr) {
624 return; 636 return;
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 9c30879a2..c4ae50de7 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -54,6 +54,18 @@ public:
54 /// Save all button configurations to settings file. 54 /// Save all button configurations to settings file.
55 void ApplyConfiguration(); 55 void ApplyConfiguration();
56 56
57 /**
58 * Attempts to connect the currently selected controller in the HID backend.
59 * This function will not do anything if it is not connected in the frontend.
60 */
61 void TryConnectSelectedController();
62
63 /**
64 * Attempts to disconnect the currently selected controller in the HID backend.
65 * This function will not do anything if the configuration has not changed.
66 */
67 void TryDisconnectSelectedController();
68
57 /// Set the connection state checkbox (used to sync state). 69 /// Set the connection state checkbox (used to sync state).
58 void ConnectPlayer(bool connected); 70 void ConnectPlayer(bool connected);
59 71
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 7aa515226..43d64b708 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -142,7 +142,7 @@ constexpr int default_mouse_timeout = 2500;
142/** 142/**
143 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there 143 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there
144 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the 144 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the
145 * user. This is 32-bits - if we have more than 32 callouts, we should retire and recyle old ones. 145 * user. This is 32-bits - if we have more than 32 callouts, we should retire and recycle old ones.
146 */ 146 */
147enum class CalloutFlag : uint32_t { 147enum class CalloutFlag : uint32_t {
148 Telemetry = 0x1, 148 Telemetry = 0x1,
@@ -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
@@ -1049,20 +1045,23 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
1049 break; 1045 break;
1050 1046
1051 default: 1047 default:
1052 if (static_cast<u32>(result) > 1048 if (result > Core::System::ResultStatus::ErrorLoader) {
1053 static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) {
1054 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); 1049 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
1055 const u16 error_id = static_cast<u16>(result) - loader_id; 1050 const u16 error_id = static_cast<u16>(result) - loader_id;
1056 const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id); 1051 const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id);
1057 LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code); 1052 LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code);
1058 QMessageBox::critical( 1053
1059 this, 1054 const auto title =
1060 tr("Error while loading ROM! ").append(QString::fromStdString(error_code)), 1055 tr("Error while loading ROM! %1", "%1 signifies a numeric error code.")
1061 QString::fromStdString(fmt::format( 1056 .arg(QString::fromStdString(error_code));
1062 "{}<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the " 1057 const auto description =
1063 "yuzu quickstart guide</a> to redump your files.<br>You can refer " 1058 tr("%1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the "
1064 "to the yuzu wiki</a> or the yuzu Discord</a> for help.", 1059 "yuzu quickstart guide</a> to redump your files.<br>You can refer "
1065 static_cast<Loader::ResultStatus>(error_id)))); 1060 "to the yuzu wiki</a> or the yuzu Discord</a> for help.",
1061 "%1 signifies a numeric error ID.")
1062 .arg(error_id);
1063
1064 QMessageBox::critical(this, title, description);
1066 } else { 1065 } else {
1067 QMessageBox::critical( 1066 QMessageBox::critical(
1068 this, tr("Error while loading ROM!"), 1067 this, tr("Error while loading ROM!"),
@@ -2533,9 +2532,6 @@ void GMainWindow::UpdateStatusBar() {
2533void GMainWindow::UpdateStatusButtons() { 2532void GMainWindow::UpdateStatusButtons() {
2534 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); 2533 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
2535 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); 2534 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()); 2535 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
2540 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 2536 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
2541 Settings::RendererBackend::Vulkan); 2537 Settings::RendererBackend::Vulkan);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 2497c71ae..39e0d35aa 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -202,7 +202,7 @@ int main(int argc, char** argv) {
202 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); 202 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
203 const u16 error_id = static_cast<u16>(load_result) - loader_id; 203 const u16 error_id = static_cast<u16>(load_result) - loader_id;
204 LOG_CRITICAL(Frontend, 204 LOG_CRITICAL(Frontend,
205 "While attempting to load the ROM requested, an error occured. Please " 205 "While attempting to load the ROM requested, an error occurred. Please "
206 "refer to the yuzu wiki for more information or the yuzu discord for " 206 "refer to the yuzu wiki for more information or the yuzu discord for "
207 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", 207 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
208 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); 208 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 6435ffabb..09cf2ad77 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -242,7 +242,7 @@ int main(int argc, char** argv) {
242 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); 242 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
243 const u16 error_id = static_cast<u16>(load_result) - loader_id; 243 const u16 error_id = static_cast<u16>(load_result) - loader_id;
244 LOG_CRITICAL(Frontend, 244 LOG_CRITICAL(Frontend,
245 "While attempting to load the ROM requested, an error occured. Please " 245 "While attempting to load the ROM requested, an error occurred. Please "
246 "refer to the yuzu wiki for more information or the yuzu discord for " 246 "refer to the yuzu wiki for more information or the yuzu discord for "
247 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", 247 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
248 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); 248 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));