summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/core.cpp32
-rw-r--r--src/core/core.h4
-rw-r--r--src/core/hle/ipc.h4
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp147
-rw-r--r--src/core/hle/kernel/address_arbiter.h74
-rw-r--r--src/core/hle/kernel/client_session.cpp14
-rw-r--r--src/core/hle/kernel/client_session.h9
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp8
-rw-r--r--src/core/hle/kernel/hle_ipc.h10
-rw-r--r--src/core/hle/kernel/kernel.cpp30
-rw-r--r--src/core/hle/kernel/kernel.h26
-rw-r--r--src/core/hle/kernel/server_session.cpp28
-rw-r--r--src/core/hle/kernel/server_session.h53
-rw-r--r--src/core/hle/kernel/svc.cpp40
-rw-r--r--src/core/hle/kernel/thread.cpp2
-rw-r--r--src/core/hle/kernel/vm_manager.cpp47
-rw-r--r--src/core/hle/kernel/vm_manager.h24
-rw-r--r--src/core/hle/result.h1
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp1
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h3
-rw-r--r--src/core/hle/service/audio/audout_u.cpp17
-rw-r--r--src/core/hle/service/audio/audren_u.cpp3
-rw-r--r--src/core/hle/service/audio/errors.h15
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp14
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp2
-rw-r--r--src/core/hle/service/sm/controller.cpp2
-rw-r--r--src/core/memory.cpp14
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp2
-rw-r--r--src/tests/core/arm/arm_test_common.cpp6
-rw-r--r--src/video_core/CMakeLists.txt8
-rw-r--r--src/video_core/engines/fermi_2d.cpp7
-rw-r--r--src/video_core/engines/fermi_2d.h2
-rw-r--r--src/video_core/engines/kepler_compute.cpp3
-rw-r--r--src/video_core/engines/kepler_compute.h3
-rw-r--r--src/video_core/engines/kepler_memory.cpp2
-rw-r--r--src/video_core/engines/kepler_memory.h1
-rw-r--r--src/video_core/engines/maxwell_dma.cpp5
-rw-r--r--src/video_core/engines/maxwell_dma.h1
-rw-r--r--src/video_core/engines/shader_bytecode.h1
-rw-r--r--src/video_core/gpu.cpp5
-rw-r--r--src/video_core/gpu.h55
-rw-r--r--src/video_core/gpu_asynch.cpp37
-rw-r--r--src/video_core/gpu_asynch.h37
-rw-r--r--src/video_core/gpu_synch.cpp37
-rw-r--r--src/video_core/gpu_synch.h29
-rw-r--r--src/video_core/gpu_thread.cpp152
-rw-r--r--src/video_core/gpu_thread.h136
-rw-r--r--src/video_core/renderer_base.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp115
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp27
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp2
-rw-r--r--src/video_core/surface.cpp2
-rw-r--r--src/video_core/textures/astc.cpp80
-rw-r--r--src/video_core/textures/astc.h2
-rw-r--r--src/video_core/textures/convert.cpp92
-rw-r--r--src/video_core/textures/convert.h18
-rw-r--r--src/video_core/textures/decoders.cpp6
-rw-r--r--src/video_core/textures/decoders.h18
-rw-r--r--src/yuzu/applets/web_browser.cpp2
-rw-r--r--src/yuzu/bootmanager.cpp10
-rw-r--r--src/yuzu/compatdb.cpp4
-rw-r--r--src/yuzu/configuration/config.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics.ui7
-rw-r--r--src/yuzu/debugger/wait_tree.cpp8
-rw-r--r--src/yuzu/debugger/wait_tree.h3
-rw-r--r--src/yuzu/main.cpp15
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h4
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
76 files changed, 1140 insertions, 454 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 988356c65..8ccb2d5f0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -217,6 +217,7 @@ add_library(core STATIC
217 hle/service/audio/audren_u.h 217 hle/service/audio/audren_u.h
218 hle/service/audio/codecctl.cpp 218 hle/service/audio/codecctl.cpp
219 hle/service/audio/codecctl.h 219 hle/service/audio/codecctl.h
220 hle/service/audio/errors.h
220 hle/service/audio/hwopus.cpp 221 hle/service/audio/hwopus.cpp
221 hle/service/audio/hwopus.h 222 hle/service/audio/hwopus.h
222 hle/service/bcat/bcat.cpp 223 hle/service/bcat/bcat.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index ab7181a05..eba2177d1 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -36,7 +36,8 @@
36#include "frontend/applets/software_keyboard.h" 36#include "frontend/applets/software_keyboard.h"
37#include "frontend/applets/web_browser.h" 37#include "frontend/applets/web_browser.h"
38#include "video_core/debug_utils/debug_utils.h" 38#include "video_core/debug_utils/debug_utils.h"
39#include "video_core/gpu.h" 39#include "video_core/gpu_asynch.h"
40#include "video_core/gpu_synch.h"
40#include "video_core/renderer_base.h" 41#include "video_core/renderer_base.h"
41#include "video_core/video_core.h" 42#include "video_core/video_core.h"
42 43
@@ -78,6 +79,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
78 return vfs->OpenFile(path, FileSys::Mode::Read); 79 return vfs->OpenFile(path, FileSys::Mode::Read);
79} 80}
80struct System::Impl { 81struct System::Impl {
82 explicit Impl(System& system) : kernel{system} {}
81 83
82 Cpu& CurrentCpuCore() { 84 Cpu& CurrentCpuCore() {
83 return cpu_core_manager.GetCurrentCore(); 85 return cpu_core_manager.GetCurrentCore();
@@ -95,7 +97,7 @@ struct System::Impl {
95 LOG_DEBUG(HW_Memory, "initialized OK"); 97 LOG_DEBUG(HW_Memory, "initialized OK");
96 98
97 core_timing.Initialize(); 99 core_timing.Initialize();
98 kernel.Initialize(core_timing); 100 kernel.Initialize();
99 101
100 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( 102 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
101 std::chrono::system_clock::now().time_since_epoch()); 103 std::chrono::system_clock::now().time_since_epoch());
@@ -128,10 +130,16 @@ struct System::Impl {
128 return ResultStatus::ErrorVideoCore; 130 return ResultStatus::ErrorVideoCore;
129 } 131 }
130 132
131 gpu_core = std::make_unique<Tegra::GPU>(system, renderer->Rasterizer()); 133 is_powered_on = true;
134
135 if (Settings::values.use_asynchronous_gpu_emulation) {
136 gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer);
137 } else {
138 gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer);
139 }
132 140
133 cpu_core_manager.Initialize(system); 141 cpu_core_manager.Initialize(system);
134 is_powered_on = true; 142
135 LOG_DEBUG(Core, "Initialized OK"); 143 LOG_DEBUG(Core, "Initialized OK");
136 144
137 // Reset counters and set time origin to current frame 145 // Reset counters and set time origin to current frame
@@ -182,13 +190,13 @@ struct System::Impl {
182 190
183 void Shutdown() { 191 void Shutdown() {
184 // Log last frame performance stats 192 // Log last frame performance stats
185 auto perf_results = GetAndResetPerfStats(); 193 const auto perf_results = GetAndResetPerfStats();
186 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed", 194 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
187 perf_results.emulation_speed * 100.0); 195 perf_results.emulation_speed * 100.0);
188 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate", 196 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
189 perf_results.game_fps); 197 perf_results.game_fps);
190 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", 198 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
191 perf_results.frametime * 1000.0); 199 perf_results.frametime * 1000.0);
192 200
193 is_powered_on = false; 201 is_powered_on = false;
194 202
@@ -265,7 +273,7 @@ struct System::Impl {
265 Core::FrameLimiter frame_limiter; 273 Core::FrameLimiter frame_limiter;
266}; 274};
267 275
268System::System() : impl{std::make_unique<Impl>()} {} 276System::System() : impl{std::make_unique<Impl>(*this)} {}
269System::~System() = default; 277System::~System() = default;
270 278
271Cpu& System::CurrentCpuCore() { 279Cpu& System::CurrentCpuCore() {
diff --git a/src/core/core.h b/src/core/core.h
index d720013f7..ba76a41d8 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -293,10 +293,6 @@ inline ARM_Interface& CurrentArmInterface() {
293 return System::GetInstance().CurrentArmInterface(); 293 return System::GetInstance().CurrentArmInterface();
294} 294}
295 295
296inline TelemetrySession& Telemetry() {
297 return System::GetInstance().TelemetrySession();
298}
299
300inline Kernel::Process* CurrentProcess() { 296inline Kernel::Process* CurrentProcess() {
301 return System::GetInstance().CurrentProcess(); 297 return System::GetInstance().CurrentProcess();
302} 298}
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index ed84197b3..455d1f346 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -4,10 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/bit_field.h"
8#include "common/common_funcs.h"
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "common/swap.h" 10#include "common/swap.h"
9#include "core/hle/kernel/errors.h"
10#include "core/memory.h"
11 11
12namespace IPC { 12namespace IPC {
13 13
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index a250d088d..9780a7849 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -9,6 +9,7 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/core_cpu.h" 11#include "core/core_cpu.h"
12#include "core/hle/kernel/address_arbiter.h"
12#include "core/hle/kernel/errors.h" 13#include "core/hle/kernel/errors.h"
13#include "core/hle/kernel/object.h" 14#include "core/hle/kernel/object.h"
14#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
@@ -17,58 +18,16 @@
17#include "core/hle/result.h" 18#include "core/hle/result.h"
18#include "core/memory.h" 19#include "core/memory.h"
19 20
20namespace Kernel::AddressArbiter { 21namespace Kernel {
21 22namespace {
22// Performs actual address waiting logic.
23static ResultCode WaitForAddress(VAddr address, s64 timeout) {
24 SharedPtr<Thread> current_thread = GetCurrentThread();
25 current_thread->SetArbiterWaitAddress(address);
26 current_thread->SetStatus(ThreadStatus::WaitArb);
27 current_thread->InvalidateWakeupCallback();
28
29 current_thread->WakeAfterDelay(timeout);
30
31 Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
32 return RESULT_TIMEOUT;
33}
34
35// Gets the threads waiting on an address.
36static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
37 const auto RetrieveWaitingThreads = [](std::size_t core_index,
38 std::vector<SharedPtr<Thread>>& waiting_threads,
39 VAddr arb_addr) {
40 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
41 const auto& thread_list = scheduler.GetThreadList();
42
43 for (const auto& thread : thread_list) {
44 if (thread->GetArbiterWaitAddress() == arb_addr)
45 waiting_threads.push_back(thread);
46 }
47 };
48
49 // Retrieve all threads that are waiting for this address.
50 std::vector<SharedPtr<Thread>> threads;
51 RetrieveWaitingThreads(0, threads, address);
52 RetrieveWaitingThreads(1, threads, address);
53 RetrieveWaitingThreads(2, threads, address);
54 RetrieveWaitingThreads(3, threads, address);
55
56 // Sort them by priority, such that the highest priority ones come first.
57 std::sort(threads.begin(), threads.end(),
58 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
59 return lhs->GetPriority() < rhs->GetPriority();
60 });
61
62 return threads;
63}
64
65// Wake up num_to_wake (or all) threads in a vector. 23// Wake up num_to_wake (or all) threads in a vector.
66static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { 24void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
67 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process 25 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
68 // them all. 26 // them all.
69 std::size_t last = waiting_threads.size(); 27 std::size_t last = waiting_threads.size();
70 if (num_to_wake > 0) 28 if (num_to_wake > 0) {
71 last = num_to_wake; 29 last = num_to_wake;
30 }
72 31
73 // Signal the waiting threads. 32 // Signal the waiting threads.
74 for (std::size_t i = 0; i < last; i++) { 33 for (std::size_t i = 0; i < last; i++) {
@@ -78,42 +37,41 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
78 waiting_threads[i]->ResumeFromWait(); 37 waiting_threads[i]->ResumeFromWait();
79 } 38 }
80} 39}
40} // Anonymous namespace
81 41
82// Signals an address being waited on. 42AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
83ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { 43AddressArbiter::~AddressArbiter() = default;
84 std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
85 44
45ResultCode AddressArbiter::SignalToAddress(VAddr address, s32 num_to_wake) {
46 const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
86 WakeThreads(waiting_threads, num_to_wake); 47 WakeThreads(waiting_threads, num_to_wake);
87 return RESULT_SUCCESS; 48 return RESULT_SUCCESS;
88} 49}
89 50
90// Signals an address being waited on and increments its value if equal to the value argument. 51ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
91ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { 52 s32 num_to_wake) {
92 // Ensure that we can write to the address. 53 // Ensure that we can write to the address.
93 if (!Memory::IsValidVirtualAddress(address)) { 54 if (!Memory::IsValidVirtualAddress(address)) {
94 return ERR_INVALID_ADDRESS_STATE; 55 return ERR_INVALID_ADDRESS_STATE;
95 } 56 }
96 57
97 if (static_cast<s32>(Memory::Read32(address)) == value) { 58 if (static_cast<s32>(Memory::Read32(address)) != value) {
98 Memory::Write32(address, static_cast<u32>(value + 1));
99 } else {
100 return ERR_INVALID_STATE; 59 return ERR_INVALID_STATE;
101 } 60 }
102 61
62 Memory::Write32(address, static_cast<u32>(value + 1));
103 return SignalToAddress(address, num_to_wake); 63 return SignalToAddress(address, num_to_wake);
104} 64}
105 65
106// Signals an address being waited on and modifies its value based on waiting thread count if equal 66ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
107// to the value argument. 67 s32 num_to_wake) {
108ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
109 s32 num_to_wake) {
110 // Ensure that we can write to the address. 68 // Ensure that we can write to the address.
111 if (!Memory::IsValidVirtualAddress(address)) { 69 if (!Memory::IsValidVirtualAddress(address)) {
112 return ERR_INVALID_ADDRESS_STATE; 70 return ERR_INVALID_ADDRESS_STATE;
113 } 71 }
114 72
115 // Get threads waiting on the address. 73 // Get threads waiting on the address.
116 std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); 74 const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
117 75
118 // Determine the modified value depending on the waiting count. 76 // Determine the modified value depending on the waiting count.
119 s32 updated_value; 77 s32 updated_value;
@@ -125,31 +83,31 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
125 updated_value = value; 83 updated_value = value;
126 } 84 }
127 85
128 if (static_cast<s32>(Memory::Read32(address)) == value) { 86 if (static_cast<s32>(Memory::Read32(address)) != value) {
129 Memory::Write32(address, static_cast<u32>(updated_value));
130 } else {
131 return ERR_INVALID_STATE; 87 return ERR_INVALID_STATE;
132 } 88 }
133 89
90 Memory::Write32(address, static_cast<u32>(updated_value));
134 WakeThreads(waiting_threads, num_to_wake); 91 WakeThreads(waiting_threads, num_to_wake);
135 return RESULT_SUCCESS; 92 return RESULT_SUCCESS;
136} 93}
137 94
138// Waits on an address if the value passed is less than the argument value, optionally decrementing. 95ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
139ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { 96 bool should_decrement) {
140 // Ensure that we can read the address. 97 // Ensure that we can read the address.
141 if (!Memory::IsValidVirtualAddress(address)) { 98 if (!Memory::IsValidVirtualAddress(address)) {
142 return ERR_INVALID_ADDRESS_STATE; 99 return ERR_INVALID_ADDRESS_STATE;
143 } 100 }
144 101
145 s32 cur_value = static_cast<s32>(Memory::Read32(address)); 102 const s32 cur_value = static_cast<s32>(Memory::Read32(address));
146 if (cur_value < value) { 103 if (cur_value >= value) {
147 if (should_decrement) {
148 Memory::Write32(address, static_cast<u32>(cur_value - 1));
149 }
150 } else {
151 return ERR_INVALID_STATE; 104 return ERR_INVALID_STATE;
152 } 105 }
106
107 if (should_decrement) {
108 Memory::Write32(address, static_cast<u32>(cur_value - 1));
109 }
110
153 // Short-circuit without rescheduling, if timeout is zero. 111 // Short-circuit without rescheduling, if timeout is zero.
154 if (timeout == 0) { 112 if (timeout == 0) {
155 return RESULT_TIMEOUT; 113 return RESULT_TIMEOUT;
@@ -158,8 +116,7 @@ ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool
158 return WaitForAddress(address, timeout); 116 return WaitForAddress(address, timeout);
159} 117}
160 118
161// Waits on an address if the value passed is equal to the argument value. 119ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
162ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
163 // Ensure that we can read the address. 120 // Ensure that we can read the address.
164 if (!Memory::IsValidVirtualAddress(address)) { 121 if (!Memory::IsValidVirtualAddress(address)) {
165 return ERR_INVALID_ADDRESS_STATE; 122 return ERR_INVALID_ADDRESS_STATE;
@@ -175,4 +132,46 @@ ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
175 132
176 return WaitForAddress(address, timeout); 133 return WaitForAddress(address, timeout);
177} 134}
178} // namespace Kernel::AddressArbiter 135
136ResultCode AddressArbiter::WaitForAddress(VAddr address, s64 timeout) {
137 SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
138 current_thread->SetArbiterWaitAddress(address);
139 current_thread->SetStatus(ThreadStatus::WaitArb);
140 current_thread->InvalidateWakeupCallback();
141
142 current_thread->WakeAfterDelay(timeout);
143
144 system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
145 return RESULT_TIMEOUT;
146}
147
148std::vector<SharedPtr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) const {
149 const auto RetrieveWaitingThreads = [this](std::size_t core_index,
150 std::vector<SharedPtr<Thread>>& waiting_threads,
151 VAddr arb_addr) {
152 const auto& scheduler = system.Scheduler(core_index);
153 const auto& thread_list = scheduler.GetThreadList();
154
155 for (const auto& thread : thread_list) {
156 if (thread->GetArbiterWaitAddress() == arb_addr) {
157 waiting_threads.push_back(thread);
158 }
159 }
160 };
161
162 // Retrieve all threads that are waiting for this address.
163 std::vector<SharedPtr<Thread>> threads;
164 RetrieveWaitingThreads(0, threads, address);
165 RetrieveWaitingThreads(1, threads, address);
166 RetrieveWaitingThreads(2, threads, address);
167 RetrieveWaitingThreads(3, threads, address);
168
169 // Sort them by priority, such that the highest priority ones come first.
170 std::sort(threads.begin(), threads.end(),
171 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
172 return lhs->GetPriority() < rhs->GetPriority();
173 });
174
175 return threads;
176}
177} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index b58f21bec..e0c36f2e3 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -5,28 +5,68 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/address_arbiter.h"
8 9
9union ResultCode; 10union ResultCode;
10 11
11namespace Kernel::AddressArbiter { 12namespace Core {
13class System;
14}
12 15
13enum class ArbitrationType { 16namespace Kernel {
14 WaitIfLessThan = 0,
15 DecrementAndWaitIfLessThan = 1,
16 WaitIfEqual = 2,
17};
18 17
19enum class SignalType { 18class Thread;
20 Signal = 0, 19
21 IncrementAndSignalIfEqual = 1, 20class AddressArbiter {
22 ModifyByWaitingCountAndSignalIfEqual = 2, 21public:
23}; 22 enum class ArbitrationType {
23 WaitIfLessThan = 0,
24 DecrementAndWaitIfLessThan = 1,
25 WaitIfEqual = 2,
26 };
27
28 enum class SignalType {
29 Signal = 0,
30 IncrementAndSignalIfEqual = 1,
31 ModifyByWaitingCountAndSignalIfEqual = 2,
32 };
33
34 explicit AddressArbiter(Core::System& system);
35 ~AddressArbiter();
36
37 AddressArbiter(const AddressArbiter&) = delete;
38 AddressArbiter& operator=(const AddressArbiter&) = delete;
39
40 AddressArbiter(AddressArbiter&&) = default;
41 AddressArbiter& operator=(AddressArbiter&&) = delete;
24 42
25ResultCode SignalToAddress(VAddr address, s32 num_to_wake); 43 /// Signals an address being waited on.
26ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); 44 ResultCode SignalToAddress(VAddr address, s32 num_to_wake);
27ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
28 45
29ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement); 46 /// Signals an address being waited on and increments its value if equal to the value argument.
30ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); 47 ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
48
49 /// Signals an address being waited on and modifies its value based on waiting thread count if
50 /// equal to the value argument.
51 ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
52 s32 num_to_wake);
53
54 /// Waits on an address if the value passed is less than the argument value,
55 /// optionally decrementing.
56 ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
57 bool should_decrement);
58
59 /// Waits on an address if the value passed is equal to the argument value.
60 ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
61
62private:
63 // Waits on the given address with a timeout in nanoseconds
64 ResultCode WaitForAddress(VAddr address, s64 timeout);
65
66 // Gets the threads waiting on an address.
67 std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
68
69 Core::System& system;
70};
31 71
32} // namespace Kernel::AddressArbiter 72} // namespace Kernel
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 704e82824..c17baa50a 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -17,21 +17,11 @@ ClientSession::~ClientSession() {
17 // This destructor will be called automatically when the last ClientSession handle is closed by 17 // This destructor will be called automatically when the last ClientSession handle is closed by
18 // the emulated application. 18 // the emulated application.
19 19
20 // Local references to ServerSession and SessionRequestHandler are necessary to guarantee they 20 // A local reference to the ServerSession is necessary to guarantee it
21 // will be kept alive until after ClientDisconnected() returns. 21 // will be kept alive until after ClientDisconnected() returns.
22 SharedPtr<ServerSession> server = parent->server; 22 SharedPtr<ServerSession> server = parent->server;
23 if (server) { 23 if (server) {
24 std::shared_ptr<SessionRequestHandler> hle_handler = server->hle_handler; 24 server->ClientDisconnected();
25 if (hle_handler)
26 hle_handler->ClientDisconnected(server);
27
28 // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
29 // their WaitSynchronization result to 0xC920181A.
30
31 // Clean up the list of client threads with pending requests, they are unneeded now that the
32 // client endpoint is closed.
33 server->pending_requesting_threads.clear();
34 server->currently_handling = nullptr;
35 } 25 }
36 26
37 parent->client = nullptr; 27 parent->client = nullptr;
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index 4c18de69c..b1f39aad7 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -36,14 +36,15 @@ public:
36 36
37 ResultCode SendSyncRequest(SharedPtr<Thread> thread); 37 ResultCode SendSyncRequest(SharedPtr<Thread> thread);
38 38
39 std::string name; ///< Name of client port (optional) 39private:
40 explicit ClientSession(KernelCore& kernel);
41 ~ClientSession() override;
40 42
41 /// The parent session, which links to the server endpoint. 43 /// The parent session, which links to the server endpoint.
42 std::shared_ptr<Session> parent; 44 std::shared_ptr<Session> parent;
43 45
44private: 46 /// Name of the client session (optional)
45 explicit ClientSession(KernelCore& kernel); 47 std::string name;
46 ~ClientSession() override;
47}; 48};
48 49
49} // namespace Kernel 50} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 5dd855db8..cbffff017 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -264,11 +264,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
264 // Write the domain objects to the command buffer, these go after the raw untranslated data. 264 // Write the domain objects to the command buffer, these go after the raw untranslated data.
265 // TODO(Subv): This completely ignores C buffers. 265 // TODO(Subv): This completely ignores C buffers.
266 std::size_t domain_offset = size - domain_message_header->num_objects; 266 std::size_t domain_offset = size - domain_message_header->num_objects;
267 auto& request_handlers = server_session->domain_request_handlers;
268 267
269 for (auto& object : domain_objects) { 268 for (const auto& object : domain_objects) {
270 request_handlers.emplace_back(object); 269 server_session->AppendDomainRequestHandler(object);
271 dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size()); 270 dst_cmdbuf[domain_offset++] =
271 static_cast<u32_le>(server_session->NumDomainRequestHandlers());
272 } 272 }
273 } 273 }
274 274
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index cb1c5aff3..0107acea4 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -15,6 +15,8 @@
15#include "core/hle/ipc.h" 15#include "core/hle/ipc.h"
16#include "core/hle/kernel/object.h" 16#include "core/hle/kernel/object.h"
17 17
18union ResultCode;
19
18namespace Service { 20namespace Service {
19class ServiceFrameworkBase; 21class ServiceFrameworkBase;
20} 22}
@@ -208,14 +210,12 @@ public:
208 210
209 template <typename T> 211 template <typename T>
210 SharedPtr<T> GetCopyObject(std::size_t index) { 212 SharedPtr<T> GetCopyObject(std::size_t index) {
211 ASSERT(index < copy_objects.size()); 213 return DynamicObjectCast<T>(copy_objects.at(index));
212 return DynamicObjectCast<T>(copy_objects[index]);
213 } 214 }
214 215
215 template <typename T> 216 template <typename T>
216 SharedPtr<T> GetMoveObject(std::size_t index) { 217 SharedPtr<T> GetMoveObject(std::size_t index) {
217 ASSERT(index < move_objects.size()); 218 return DynamicObjectCast<T>(move_objects.at(index));
218 return DynamicObjectCast<T>(move_objects[index]);
219 } 219 }
220 220
221 void AddMoveObject(SharedPtr<Object> object) { 221 void AddMoveObject(SharedPtr<Object> object) {
@@ -232,7 +232,7 @@ public:
232 232
233 template <typename T> 233 template <typename T>
234 std::shared_ptr<T> GetDomainRequestHandler(std::size_t index) const { 234 std::shared_ptr<T> GetDomainRequestHandler(std::size_t index) const {
235 return std::static_pointer_cast<T>(domain_request_handlers[index]); 235 return std::static_pointer_cast<T>(domain_request_handlers.at(index));
236 } 236 }
237 237
238 void SetDomainRequestHandlers( 238 void SetDomainRequestHandlers(
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index dd749eed4..04ea9349e 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -12,6 +12,7 @@
12 12
13#include "core/core.h" 13#include "core/core.h"
14#include "core/core_timing.h" 14#include "core/core_timing.h"
15#include "core/hle/kernel/address_arbiter.h"
15#include "core/hle/kernel/client_port.h" 16#include "core/hle/kernel/client_port.h"
16#include "core/hle/kernel/handle_table.h" 17#include "core/hle/kernel/handle_table.h"
17#include "core/hle/kernel/kernel.h" 18#include "core/hle/kernel/kernel.h"
@@ -86,11 +87,13 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
86} 87}
87 88
88struct KernelCore::Impl { 89struct KernelCore::Impl {
89 void Initialize(KernelCore& kernel, Core::Timing::CoreTiming& core_timing) { 90 explicit Impl(Core::System& system) : address_arbiter{system}, system{system} {}
91
92 void Initialize(KernelCore& kernel) {
90 Shutdown(); 93 Shutdown();
91 94
92 InitializeSystemResourceLimit(kernel); 95 InitializeSystemResourceLimit(kernel);
93 InitializeThreads(core_timing); 96 InitializeThreads();
94 } 97 }
95 98
96 void Shutdown() { 99 void Shutdown() {
@@ -122,9 +125,9 @@ struct KernelCore::Impl {
122 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess()); 125 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
123 } 126 }
124 127
125 void InitializeThreads(Core::Timing::CoreTiming& core_timing) { 128 void InitializeThreads() {
126 thread_wakeup_event_type = 129 thread_wakeup_event_type =
127 core_timing.RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); 130 system.CoreTiming().RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
128 } 131 }
129 132
130 std::atomic<u32> next_object_id{0}; 133 std::atomic<u32> next_object_id{0};
@@ -135,6 +138,8 @@ struct KernelCore::Impl {
135 std::vector<SharedPtr<Process>> process_list; 138 std::vector<SharedPtr<Process>> process_list;
136 Process* current_process = nullptr; 139 Process* current_process = nullptr;
137 140
141 Kernel::AddressArbiter address_arbiter;
142
138 SharedPtr<ResourceLimit> system_resource_limit; 143 SharedPtr<ResourceLimit> system_resource_limit;
139 144
140 Core::Timing::EventType* thread_wakeup_event_type = nullptr; 145 Core::Timing::EventType* thread_wakeup_event_type = nullptr;
@@ -145,15 +150,18 @@ struct KernelCore::Impl {
145 /// Map of named ports managed by the kernel, which can be retrieved using 150 /// Map of named ports managed by the kernel, which can be retrieved using
146 /// the ConnectToPort SVC. 151 /// the ConnectToPort SVC.
147 NamedPortTable named_ports; 152 NamedPortTable named_ports;
153
154 // System context
155 Core::System& system;
148}; 156};
149 157
150KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {} 158KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
151KernelCore::~KernelCore() { 159KernelCore::~KernelCore() {
152 Shutdown(); 160 Shutdown();
153} 161}
154 162
155void KernelCore::Initialize(Core::Timing::CoreTiming& core_timing) { 163void KernelCore::Initialize() {
156 impl->Initialize(*this, core_timing); 164 impl->Initialize(*this);
157} 165}
158 166
159void KernelCore::Shutdown() { 167void KernelCore::Shutdown() {
@@ -184,6 +192,14 @@ const Process* KernelCore::CurrentProcess() const {
184 return impl->current_process; 192 return impl->current_process;
185} 193}
186 194
195AddressArbiter& KernelCore::AddressArbiter() {
196 return impl->address_arbiter;
197}
198
199const AddressArbiter& KernelCore::AddressArbiter() const {
200 return impl->address_arbiter;
201}
202
187void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) { 203void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
188 impl->named_ports.emplace(std::move(name), std::move(port)); 204 impl->named_ports.emplace(std::move(name), std::move(port));
189} 205}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 154bced42..4d292aca9 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -11,6 +11,10 @@
11template <typename T> 11template <typename T>
12class ResultVal; 12class ResultVal;
13 13
14namespace Core {
15class System;
16}
17
14namespace Core::Timing { 18namespace Core::Timing {
15class CoreTiming; 19class CoreTiming;
16struct EventType; 20struct EventType;
@@ -18,6 +22,7 @@ struct EventType;
18 22
19namespace Kernel { 23namespace Kernel {
20 24
25class AddressArbiter;
21class ClientPort; 26class ClientPort;
22class HandleTable; 27class HandleTable;
23class Process; 28class Process;
@@ -30,7 +35,14 @@ private:
30 using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>; 35 using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>;
31 36
32public: 37public:
33 KernelCore(); 38 /// Constructs an instance of the kernel using the given System
39 /// instance as a context for any necessary system-related state,
40 /// such as threads, CPU core state, etc.
41 ///
42 /// @post After execution of the constructor, the provided System
43 /// object *must* outlive the kernel instance itself.
44 ///
45 explicit KernelCore(Core::System& system);
34 ~KernelCore(); 46 ~KernelCore();
35 47
36 KernelCore(const KernelCore&) = delete; 48 KernelCore(const KernelCore&) = delete;
@@ -40,11 +52,7 @@ public:
40 KernelCore& operator=(KernelCore&&) = delete; 52 KernelCore& operator=(KernelCore&&) = delete;
41 53
42 /// Resets the kernel to a clean slate for use. 54 /// Resets the kernel to a clean slate for use.
43 /// 55 void Initialize();
44 /// @param core_timing CoreTiming instance used to create any necessary
45 /// kernel-specific callback events.
46 ///
47 void Initialize(Core::Timing::CoreTiming& core_timing);
48 56
49 /// Clears all resources in use by the kernel instance. 57 /// Clears all resources in use by the kernel instance.
50 void Shutdown(); 58 void Shutdown();
@@ -67,6 +75,12 @@ public:
67 /// Retrieves a const pointer to the current process. 75 /// Retrieves a const pointer to the current process.
68 const Process* CurrentProcess() const; 76 const Process* CurrentProcess() const;
69 77
78 /// Provides a reference to the kernel's address arbiter.
79 Kernel::AddressArbiter& AddressArbiter();
80
81 /// Provides a const reference to the kernel's address arbiter.
82 const Kernel::AddressArbiter& AddressArbiter() const;
83
70 /// Adds a port to the named port table 84 /// Adds a port to the named port table
71 void AddNamedPort(std::string name, SharedPtr<ClientPort> port); 85 void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
72 86
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 027434f92..3452a9c0c 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -63,6 +63,34 @@ void ServerSession::Acquire(Thread* thread) {
63 pending_requesting_threads.pop_back(); 63 pending_requesting_threads.pop_back();
64} 64}
65 65
66void ServerSession::ClientDisconnected() {
67 // We keep a shared pointer to the hle handler to keep it alive throughout
68 // the call to ClientDisconnected, as ClientDisconnected invalidates the
69 // hle_handler member itself during the course of the function executing.
70 std::shared_ptr<SessionRequestHandler> handler = hle_handler;
71 if (handler) {
72 // Note that after this returns, this server session's hle_handler is
73 // invalidated (set to null).
74 handler->ClientDisconnected(this);
75 }
76
77 // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
78 // their WaitSynchronization result to 0xC920181A.
79
80 // Clean up the list of client threads with pending requests, they are unneeded now that the
81 // client endpoint is closed.
82 pending_requesting_threads.clear();
83 currently_handling = nullptr;
84}
85
86void ServerSession::AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler) {
87 domain_request_handlers.push_back(std::move(handler));
88}
89
90std::size_t ServerSession::NumDomainRequestHandlers() const {
91 return domain_request_handlers.size();
92}
93
66ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { 94ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
67 auto* const domain_message_header = context.GetDomainMessageHeader(); 95 auto* const domain_message_header = context.GetDomainMessageHeader();
68 if (domain_message_header) { 96 if (domain_message_header) {
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index e0e9d64c8..aea4ccfeb 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -46,6 +46,14 @@ public:
46 return HANDLE_TYPE; 46 return HANDLE_TYPE;
47 } 47 }
48 48
49 Session* GetParent() {
50 return parent.get();
51 }
52
53 const Session* GetParent() const {
54 return parent.get();
55 }
56
49 using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>; 57 using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>;
50 58
51 /** 59 /**
@@ -78,23 +86,16 @@ public:
78 86
79 void Acquire(Thread* thread) override; 87 void Acquire(Thread* thread) override;
80 88
81 std::string name; ///< The name of this session (optional) 89 /// Called when a client disconnection occurs.
82 std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint. 90 void ClientDisconnected();
83 std::shared_ptr<SessionRequestHandler>
84 hle_handler; ///< This session's HLE request handler (applicable when not a domain)
85 91
86 /// This is the list of domain request handlers (after conversion to a domain) 92 /// Adds a new domain request handler to the collection of request handlers within
87 std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers; 93 /// this ServerSession instance.
88 94 void AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler);
89 /// List of threads that are pending a response after a sync request. This list is processed in
90 /// a LIFO manner, thus, the last request will be dispatched first.
91 /// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
92 std::vector<SharedPtr<Thread>> pending_requesting_threads;
93 95
94 /// Thread whose request is currently being handled. A request is considered "handled" when a 96 /// Retrieves the total number of domain request handlers that have been
95 /// response is sent via svcReplyAndReceive. 97 /// appended to this ServerSession instance.
96 /// TODO(Subv): Find a better name for this. 98 std::size_t NumDomainRequestHandlers() const;
97 SharedPtr<Thread> currently_handling;
98 99
99 /// Returns true if the session has been converted to a domain, otherwise False 100 /// Returns true if the session has been converted to a domain, otherwise False
100 bool IsDomain() const { 101 bool IsDomain() const {
@@ -129,8 +130,30 @@ private:
129 /// object handle. 130 /// object handle.
130 ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context); 131 ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
131 132
133 /// The parent session, which links to the client endpoint.
134 std::shared_ptr<Session> parent;
135
136 /// This session's HLE request handler (applicable when not a domain)
137 std::shared_ptr<SessionRequestHandler> hle_handler;
138
139 /// This is the list of domain request handlers (after conversion to a domain)
140 std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
141
142 /// List of threads that are pending a response after a sync request. This list is processed in
143 /// a LIFO manner, thus, the last request will be dispatched first.
144 /// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
145 std::vector<SharedPtr<Thread>> pending_requesting_threads;
146
147 /// Thread whose request is currently being handled. A request is considered "handled" when a
148 /// response is sent via svcReplyAndReceive.
149 /// TODO(Subv): Find a better name for this.
150 SharedPtr<Thread> currently_handling;
151
132 /// When set to True, converts the session to a domain at the end of the command 152 /// When set to True, converts the session to a domain at the end of the command
133 bool convert_to_domain{}; 153 bool convert_to_domain{};
154
155 /// The name of this session (optional)
156 std::string name;
134}; 157};
135 158
136} // namespace Kernel 159} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index c5d399bab..7f5c0cc86 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -20,6 +20,7 @@
20#include "core/hle/kernel/address_arbiter.h" 20#include "core/hle/kernel/address_arbiter.h"
21#include "core/hle/kernel/client_port.h" 21#include "core/hle/kernel/client_port.h"
22#include "core/hle/kernel/client_session.h" 22#include "core/hle/kernel/client_session.h"
23#include "core/hle/kernel/errors.h"
23#include "core/hle/kernel/handle_table.h" 24#include "core/hle/kernel/handle_table.h"
24#include "core/hle/kernel/kernel.h" 25#include "core/hle/kernel/kernel.h"
25#include "core/hle/kernel/mutex.h" 26#include "core/hle/kernel/mutex.h"
@@ -47,23 +48,6 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) {
47 return address + size > address; 48 return address + size > address;
48} 49}
49 50
50// Checks if a given address range lies within a larger address range.
51constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
52 VAddr address_range_end) {
53 const VAddr end_address = address + size - 1;
54 return address_range_begin <= address && end_address <= address_range_end - 1;
55}
56
57bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
58 return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
59 vm.GetAddressSpaceEndAddress());
60}
61
62bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
63 return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
64 vm.GetNewMapRegionEndAddress());
65}
66
67// 8 GiB 51// 8 GiB
68constexpr u64 MAIN_MEMORY_SIZE = 0x200000000; 52constexpr u64 MAIN_MEMORY_SIZE = 0x200000000;
69 53
@@ -105,14 +89,14 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
105 return ERR_INVALID_ADDRESS_STATE; 89 return ERR_INVALID_ADDRESS_STATE;
106 } 90 }
107 91
108 if (!IsInsideAddressSpace(vm_manager, src_addr, size)) { 92 if (!vm_manager.IsWithinAddressSpace(src_addr, size)) {
109 LOG_ERROR(Kernel_SVC, 93 LOG_ERROR(Kernel_SVC,
110 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", 94 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
111 src_addr, size); 95 src_addr, size);
112 return ERR_INVALID_ADDRESS_STATE; 96 return ERR_INVALID_ADDRESS_STATE;
113 } 97 }
114 98
115 if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) { 99 if (!vm_manager.IsWithinNewMapRegion(dst_addr, size)) {
116 LOG_ERROR(Kernel_SVC, 100 LOG_ERROR(Kernel_SVC,
117 "Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}", 101 "Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}",
118 dst_addr, size); 102 dst_addr, size);
@@ -238,7 +222,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
238 auto* const current_process = Core::CurrentProcess(); 222 auto* const current_process = Core::CurrentProcess();
239 auto& vm_manager = current_process->VMManager(); 223 auto& vm_manager = current_process->VMManager();
240 224
241 if (!IsInsideAddressSpace(vm_manager, addr, size)) { 225 if (!vm_manager.IsWithinAddressSpace(addr, size)) {
242 LOG_ERROR(Kernel_SVC, 226 LOG_ERROR(Kernel_SVC,
243 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, 227 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
244 size); 228 size);
@@ -299,7 +283,7 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr
299 } 283 }
300 284
301 auto& vm_manager = Core::CurrentProcess()->VMManager(); 285 auto& vm_manager = Core::CurrentProcess()->VMManager();
302 if (!IsInsideAddressSpace(vm_manager, address, size)) { 286 if (!vm_manager.IsWithinAddressSpace(address, size)) {
303 LOG_ERROR(Kernel_SVC, 287 LOG_ERROR(Kernel_SVC,
304 "Given address (0x{:016X}) is outside the bounds of the address space.", address); 288 "Given address (0x{:016X}) is outside the bounds of the address space.", address);
305 return ERR_INVALID_ADDRESS_STATE; 289 return ERR_INVALID_ADDRESS_STATE;
@@ -1495,13 +1479,14 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout
1495 return ERR_INVALID_ADDRESS; 1479 return ERR_INVALID_ADDRESS;
1496 } 1480 }
1497 1481
1482 auto& address_arbiter = Core::System::GetInstance().Kernel().AddressArbiter();
1498 switch (static_cast<AddressArbiter::ArbitrationType>(type)) { 1483 switch (static_cast<AddressArbiter::ArbitrationType>(type)) {
1499 case AddressArbiter::ArbitrationType::WaitIfLessThan: 1484 case AddressArbiter::ArbitrationType::WaitIfLessThan:
1500 return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false); 1485 return address_arbiter.WaitForAddressIfLessThan(address, value, timeout, false);
1501 case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan: 1486 case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan:
1502 return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, true); 1487 return address_arbiter.WaitForAddressIfLessThan(address, value, timeout, true);
1503 case AddressArbiter::ArbitrationType::WaitIfEqual: 1488 case AddressArbiter::ArbitrationType::WaitIfEqual:
1504 return AddressArbiter::WaitForAddressIfEqual(address, value, timeout); 1489 return address_arbiter.WaitForAddressIfEqual(address, value, timeout);
1505 default: 1490 default:
1506 LOG_ERROR(Kernel_SVC, 1491 LOG_ERROR(Kernel_SVC,
1507 "Invalid arbitration type, expected WaitIfLessThan, DecrementAndWaitIfLessThan " 1492 "Invalid arbitration type, expected WaitIfLessThan, DecrementAndWaitIfLessThan "
@@ -1526,13 +1511,14 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
1526 return ERR_INVALID_ADDRESS; 1511 return ERR_INVALID_ADDRESS;
1527 } 1512 }
1528 1513
1514 auto& address_arbiter = Core::System::GetInstance().Kernel().AddressArbiter();
1529 switch (static_cast<AddressArbiter::SignalType>(type)) { 1515 switch (static_cast<AddressArbiter::SignalType>(type)) {
1530 case AddressArbiter::SignalType::Signal: 1516 case AddressArbiter::SignalType::Signal:
1531 return AddressArbiter::SignalToAddress(address, num_to_wake); 1517 return address_arbiter.SignalToAddress(address, num_to_wake);
1532 case AddressArbiter::SignalType::IncrementAndSignalIfEqual: 1518 case AddressArbiter::SignalType::IncrementAndSignalIfEqual:
1533 return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake); 1519 return address_arbiter.IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
1534 case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual: 1520 case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual:
1535 return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, 1521 return address_arbiter.ModifyByWaitingCountAndSignalToAddressIfEqual(address, value,
1536 num_to_wake); 1522 num_to_wake);
1537 default: 1523 default:
1538 LOG_ERROR(Kernel_SVC, 1524 LOG_ERROR(Kernel_SVC,
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 6661e2130..eb54d6651 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -184,8 +184,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
184 return ERR_INVALID_PROCESSOR_ID; 184 return ERR_INVALID_PROCESSOR_ID;
185 } 185 }
186 186
187 // TODO(yuriks): Other checks, returning 0xD9001BEA
188
189 if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) { 187 if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
190 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); 188 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
191 // TODO (bunnei): Find the correct error code to use here 189 // TODO (bunnei): Find the correct error code to use here
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 10ad94aa6..05c59af34 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -17,8 +17,8 @@
17#include "core/memory_setup.h" 17#include "core/memory_setup.h"
18 18
19namespace Kernel { 19namespace Kernel {
20 20namespace {
21static const char* GetMemoryStateName(MemoryState state) { 21const char* GetMemoryStateName(MemoryState state) {
22 static constexpr const char* names[] = { 22 static constexpr const char* names[] = {
23 "Unmapped", "Io", 23 "Unmapped", "Io",
24 "Normal", "CodeStatic", 24 "Normal", "CodeStatic",
@@ -35,6 +35,14 @@ static const char* GetMemoryStateName(MemoryState state) {
35 return names[ToSvcMemoryState(state)]; 35 return names[ToSvcMemoryState(state)];
36} 36}
37 37
38// Checks if a given address range lies within a larger address range.
39constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
40 VAddr address_range_end) {
41 const VAddr end_address = address + size - 1;
42 return address_range_begin <= address && end_address <= address_range_end - 1;
43}
44} // Anonymous namespace
45
38bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { 46bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
39 ASSERT(base + size == next.base); 47 ASSERT(base + size == next.base);
40 if (permissions != next.permissions || state != next.state || attribute != next.attribute || 48 if (permissions != next.permissions || state != next.state || attribute != next.attribute ||
@@ -249,8 +257,7 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
249} 257}
250 258
251ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 259ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
252 if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() || 260 if (!IsWithinHeapRegion(target, size)) {
253 target + size < target) {
254 return ERR_INVALID_ADDRESS; 261 return ERR_INVALID_ADDRESS;
255 } 262 }
256 263
@@ -285,8 +292,7 @@ ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission p
285} 292}
286 293
287ResultCode VMManager::HeapFree(VAddr target, u64 size) { 294ResultCode VMManager::HeapFree(VAddr target, u64 size) {
288 if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() || 295 if (!IsWithinHeapRegion(target, size)) {
289 target + size < target) {
290 return ERR_INVALID_ADDRESS; 296 return ERR_INVALID_ADDRESS;
291 } 297 }
292 298
@@ -706,6 +712,11 @@ u64 VMManager::GetAddressSpaceWidth() const {
706 return address_space_width; 712 return address_space_width;
707} 713}
708 714
715bool VMManager::IsWithinAddressSpace(VAddr address, u64 size) const {
716 return IsInsideAddressRange(address, size, GetAddressSpaceBaseAddress(),
717 GetAddressSpaceEndAddress());
718}
719
709VAddr VMManager::GetASLRRegionBaseAddress() const { 720VAddr VMManager::GetASLRRegionBaseAddress() const {
710 return aslr_region_base; 721 return aslr_region_base;
711} 722}
@@ -750,6 +761,11 @@ u64 VMManager::GetCodeRegionSize() const {
750 return code_region_end - code_region_base; 761 return code_region_end - code_region_base;
751} 762}
752 763
764bool VMManager::IsWithinCodeRegion(VAddr address, u64 size) const {
765 return IsInsideAddressRange(address, size, GetCodeRegionBaseAddress(),
766 GetCodeRegionEndAddress());
767}
768
753VAddr VMManager::GetHeapRegionBaseAddress() const { 769VAddr VMManager::GetHeapRegionBaseAddress() const {
754 return heap_region_base; 770 return heap_region_base;
755} 771}
@@ -762,6 +778,11 @@ u64 VMManager::GetHeapRegionSize() const {
762 return heap_region_end - heap_region_base; 778 return heap_region_end - heap_region_base;
763} 779}
764 780
781bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const {
782 return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(),
783 GetHeapRegionEndAddress());
784}
785
765VAddr VMManager::GetMapRegionBaseAddress() const { 786VAddr VMManager::GetMapRegionBaseAddress() const {
766 return map_region_base; 787 return map_region_base;
767} 788}
@@ -774,6 +795,10 @@ u64 VMManager::GetMapRegionSize() const {
774 return map_region_end - map_region_base; 795 return map_region_end - map_region_base;
775} 796}
776 797
798bool VMManager::IsWithinMapRegion(VAddr address, u64 size) const {
799 return IsInsideAddressRange(address, size, GetMapRegionBaseAddress(), GetMapRegionEndAddress());
800}
801
777VAddr VMManager::GetNewMapRegionBaseAddress() const { 802VAddr VMManager::GetNewMapRegionBaseAddress() const {
778 return new_map_region_base; 803 return new_map_region_base;
779} 804}
@@ -786,6 +811,11 @@ u64 VMManager::GetNewMapRegionSize() const {
786 return new_map_region_end - new_map_region_base; 811 return new_map_region_end - new_map_region_base;
787} 812}
788 813
814bool VMManager::IsWithinNewMapRegion(VAddr address, u64 size) const {
815 return IsInsideAddressRange(address, size, GetNewMapRegionBaseAddress(),
816 GetNewMapRegionEndAddress());
817}
818
789VAddr VMManager::GetTLSIORegionBaseAddress() const { 819VAddr VMManager::GetTLSIORegionBaseAddress() const {
790 return tls_io_region_base; 820 return tls_io_region_base;
791} 821}
@@ -798,4 +828,9 @@ u64 VMManager::GetTLSIORegionSize() const {
798 return tls_io_region_end - tls_io_region_base; 828 return tls_io_region_end - tls_io_region_base;
799} 829}
800 830
831bool VMManager::IsWithinTLSIORegion(VAddr address, u64 size) const {
832 return IsInsideAddressRange(address, size, GetTLSIORegionBaseAddress(),
833 GetTLSIORegionEndAddress());
834}
835
801} // namespace Kernel 836} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 6091533bc..88e0b3c02 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -432,18 +432,21 @@ public:
432 /// Gets the address space width in bits. 432 /// Gets the address space width in bits.
433 u64 GetAddressSpaceWidth() const; 433 u64 GetAddressSpaceWidth() const;
434 434
435 /// Determines whether or not the given address range lies within the address space.
436 bool IsWithinAddressSpace(VAddr address, u64 size) const;
437
435 /// Gets the base address of the ASLR region. 438 /// Gets the base address of the ASLR region.
436 VAddr GetASLRRegionBaseAddress() const; 439 VAddr GetASLRRegionBaseAddress() const;
437 440
438 /// Gets the end address of the ASLR region. 441 /// Gets the end address of the ASLR region.
439 VAddr GetASLRRegionEndAddress() const; 442 VAddr GetASLRRegionEndAddress() const;
440 443
441 /// Determines whether or not the specified address range is within the ASLR region.
442 bool IsWithinASLRRegion(VAddr address, u64 size) const;
443
444 /// Gets the size of the ASLR region 444 /// Gets the size of the ASLR region
445 u64 GetASLRRegionSize() const; 445 u64 GetASLRRegionSize() const;
446 446
447 /// Determines whether or not the specified address range is within the ASLR region.
448 bool IsWithinASLRRegion(VAddr address, u64 size) const;
449
447 /// Gets the base address of the code region. 450 /// Gets the base address of the code region.
448 VAddr GetCodeRegionBaseAddress() const; 451 VAddr GetCodeRegionBaseAddress() const;
449 452
@@ -453,6 +456,9 @@ public:
453 /// Gets the total size of the code region in bytes. 456 /// Gets the total size of the code region in bytes.
454 u64 GetCodeRegionSize() const; 457 u64 GetCodeRegionSize() const;
455 458
459 /// Determines whether or not the specified range is within the code region.
460 bool IsWithinCodeRegion(VAddr address, u64 size) const;
461
456 /// Gets the base address of the heap region. 462 /// Gets the base address of the heap region.
457 VAddr GetHeapRegionBaseAddress() const; 463 VAddr GetHeapRegionBaseAddress() const;
458 464
@@ -462,6 +468,9 @@ public:
462 /// Gets the total size of the heap region in bytes. 468 /// Gets the total size of the heap region in bytes.
463 u64 GetHeapRegionSize() const; 469 u64 GetHeapRegionSize() const;
464 470
471 /// Determines whether or not the specified range is within the heap region.
472 bool IsWithinHeapRegion(VAddr address, u64 size) const;
473
465 /// Gets the base address of the map region. 474 /// Gets the base address of the map region.
466 VAddr GetMapRegionBaseAddress() const; 475 VAddr GetMapRegionBaseAddress() const;
467 476
@@ -471,6 +480,9 @@ public:
471 /// Gets the total size of the map region in bytes. 480 /// Gets the total size of the map region in bytes.
472 u64 GetMapRegionSize() const; 481 u64 GetMapRegionSize() const;
473 482
483 /// Determines whether or not the specified range is within the map region.
484 bool IsWithinMapRegion(VAddr address, u64 size) const;
485
474 /// Gets the base address of the new map region. 486 /// Gets the base address of the new map region.
475 VAddr GetNewMapRegionBaseAddress() const; 487 VAddr GetNewMapRegionBaseAddress() const;
476 488
@@ -480,6 +492,9 @@ public:
480 /// Gets the total size of the new map region in bytes. 492 /// Gets the total size of the new map region in bytes.
481 u64 GetNewMapRegionSize() const; 493 u64 GetNewMapRegionSize() const;
482 494
495 /// Determines whether or not the given address range is within the new map region
496 bool IsWithinNewMapRegion(VAddr address, u64 size) const;
497
483 /// Gets the base address of the TLS IO region. 498 /// Gets the base address of the TLS IO region.
484 VAddr GetTLSIORegionBaseAddress() const; 499 VAddr GetTLSIORegionBaseAddress() const;
485 500
@@ -489,6 +504,9 @@ public:
489 /// Gets the total size of the TLS IO region in bytes. 504 /// Gets the total size of the TLS IO region in bytes.
490 u64 GetTLSIORegionSize() const; 505 u64 GetTLSIORegionSize() const;
491 506
507 /// Determines if the given address range is within the TLS IO region.
508 bool IsWithinTLSIORegion(VAddr address, u64 size) const;
509
492 /// Each VMManager has its own page table, which is set as the main one when the owning process 510 /// Each VMManager has its own page table, which is set as the main one when the owning process
493 /// is scheduled. 511 /// is scheduled.
494 Memory::PageTable page_table; 512 Memory::PageTable page_table;
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index bfb77cc31..1ed144481 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -8,7 +8,6 @@
8#include <utility> 8#include <utility>
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_funcs.h"
12#include "common/common_types.h" 11#include "common/common_types.h"
13 12
14// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes 13// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index f255f74b5..8c5bd6059 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -7,6 +7,7 @@
7#include "common/string_util.h" 7#include "common/string_util.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/frontend/applets/software_keyboard.h" 9#include "core/frontend/applets/software_keyboard.h"
10#include "core/hle/result.h"
10#include "core/hle/service/am/am.h" 11#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applets/software_keyboard.h" 12#include "core/hle/service/am/applets/software_keyboard.h"
12 13
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h
index efd5753a1..b93a30d28 100644
--- a/src/core/hle/service/am/applets/software_keyboard.h
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -9,10 +9,13 @@
9#include <vector> 9#include <vector>
10 10
11#include "common/common_funcs.h" 11#include "common/common_funcs.h"
12#include "common/common_types.h"
12#include "common/swap.h" 13#include "common/swap.h"
13#include "core/hle/service/am/am.h" 14#include "core/hle/service/am/am.h"
14#include "core/hle/service/am/applets/applets.h" 15#include "core/hle/service/am/applets/applets.h"
15 16
17union ResultCode;
18
16namespace Service::AM::Applets { 19namespace Service::AM::Applets {
17 20
18enum class KeysetDisable : u32 { 21enum class KeysetDisable : u32 {
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 6831c0735..21f5e64c7 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -18,17 +18,11 @@
18#include "core/hle/kernel/readable_event.h" 18#include "core/hle/kernel/readable_event.h"
19#include "core/hle/kernel/writable_event.h" 19#include "core/hle/kernel/writable_event.h"
20#include "core/hle/service/audio/audout_u.h" 20#include "core/hle/service/audio/audout_u.h"
21#include "core/hle/service/audio/errors.h"
21#include "core/memory.h" 22#include "core/memory.h"
22 23
23namespace Service::Audio { 24namespace Service::Audio {
24 25
25namespace ErrCodes {
26enum {
27 ErrorUnknown = 2,
28 BufferCountExceeded = 8,
29};
30}
31
32constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; 26constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
33constexpr int DefaultSampleRate{48000}; 27constexpr int DefaultSampleRate{48000};
34 28
@@ -100,7 +94,7 @@ private:
100 94
101 if (stream->IsPlaying()) { 95 if (stream->IsPlaying()) {
102 IPC::ResponseBuilder rb{ctx, 2}; 96 IPC::ResponseBuilder rb{ctx, 2};
103 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown)); 97 rb.Push(ERR_OPERATION_FAILED);
104 return; 98 return;
105 } 99 }
106 100
@@ -113,7 +107,9 @@ private:
113 void StopAudioOut(Kernel::HLERequestContext& ctx) { 107 void StopAudioOut(Kernel::HLERequestContext& ctx) {
114 LOG_DEBUG(Service_Audio, "called"); 108 LOG_DEBUG(Service_Audio, "called");
115 109
116 audio_core.StopStream(stream); 110 if (stream->IsPlaying()) {
111 audio_core.StopStream(stream);
112 }
117 113
118 IPC::ResponseBuilder rb{ctx, 2}; 114 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(RESULT_SUCCESS); 115 rb.Push(RESULT_SUCCESS);
@@ -143,7 +139,8 @@ private:
143 139
144 if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { 140 if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
145 IPC::ResponseBuilder rb{ctx, 2}; 141 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded)); 142 rb.Push(ERR_BUFFER_COUNT_EXCEEDED);
143 return;
147 } 144 }
148 145
149 IPC::ResponseBuilder rb{ctx, 2}; 146 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index ea8f9d0bb..c9de10a24 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -17,6 +17,7 @@
17#include "core/hle/kernel/readable_event.h" 17#include "core/hle/kernel/readable_event.h"
18#include "core/hle/kernel/writable_event.h" 18#include "core/hle/kernel/writable_event.h"
19#include "core/hle/service/audio/audren_u.h" 19#include "core/hle/service/audio/audren_u.h"
20#include "core/hle/service/audio/errors.h"
20 21
21namespace Service::Audio { 22namespace Service::Audio {
22 23
@@ -146,7 +147,7 @@ private:
146 // code in this case. 147 // code in this case.
147 148
148 IPC::ResponseBuilder rb{ctx, 2}; 149 IPC::ResponseBuilder rb{ctx, 2};
149 rb.Push(ResultCode{ErrorModule::Audio, 201}); 150 rb.Push(ERR_NOT_SUPPORTED);
150 } 151 }
151 152
152 Kernel::EventPair system_event; 153 Kernel::EventPair system_event;
diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h
new file mode 100644
index 000000000..6f8c09bcf
--- /dev/null
+++ b/src/core/hle/service/audio/errors.h
@@ -0,0 +1,15 @@
1// Copyright 2019 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 "core/hle/result.h"
8
9namespace Service::Audio {
10
11constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::Audio, 2};
12constexpr ResultCode ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8};
13constexpr ResultCode ERR_NOT_SUPPORTED{ErrorModule::Audio, 513};
14
15} // namespace Service::Audio
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index dbe7ee6e8..20c7c39aa 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -36,7 +36,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
36 36
37 auto& instance = Core::System::GetInstance(); 37 auto& instance = Core::System::GetInstance();
38 instance.GetPerfStats().EndGameFrame(); 38 instance.GetPerfStats().EndGameFrame();
39 instance.Renderer().SwapBuffers(framebuffer); 39 instance.GPU().SwapBuffers(framebuffer);
40} 40}
41 41
42} // namespace Service::Nvidia::Devices 42} // namespace Service::Nvidia::Devices
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 466db7ccd..a34b9e753 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -178,7 +178,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
178 auto& gpu = system_instance.GPU(); 178 auto& gpu = system_instance.GPU();
179 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset); 179 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
180 ASSERT(cpu_addr); 180 ASSERT(cpu_addr);
181 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size); 181 gpu.FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
182 182
183 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); 183 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
184 184
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 0a650f36c..8ce7bc7a5 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -136,16 +136,6 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
136 return 0; 136 return 0;
137} 137}
138 138
139static void PushGPUEntries(Tegra::CommandList&& entries) {
140 if (entries.empty()) {
141 return;
142 }
143
144 auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()};
145 dma_pusher.Push(std::move(entries));
146 dma_pusher.DispatchCalls();
147}
148
149u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { 139u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
150 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 140 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
151 UNIMPLEMENTED(); 141 UNIMPLEMENTED();
@@ -163,7 +153,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
163 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], 153 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
164 params.num_entries * sizeof(Tegra::CommandListHeader)); 154 params.num_entries * sizeof(Tegra::CommandListHeader));
165 155
166 PushGPUEntries(std::move(entries)); 156 Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
167 157
168 params.fence_out.id = 0; 158 params.fence_out.id = 0;
169 params.fence_out.value = 0; 159 params.fence_out.value = 0;
@@ -184,7 +174,7 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
184 Memory::ReadBlock(params.address, entries.data(), 174 Memory::ReadBlock(params.address, entries.data(),
185 params.num_entries * sizeof(Tegra::CommandListHeader)); 175 params.num_entries * sizeof(Tegra::CommandListHeader));
186 176
187 PushGPUEntries(std::move(entries)); 177 Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
188 178
189 params.fence_out.id = 0; 179 params.fence_out.id = 0;
190 params.fence_out.value = 0; 180 params.fence_out.value = 0;
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 56f31e2ac..fc496b654 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -186,7 +186,7 @@ void NVFlinger::Compose() {
186 186
187 // There was no queued buffer to draw, render previous frame 187 // There was no queued buffer to draw, render previous frame
188 system_instance.GetPerfStats().EndGameFrame(); 188 system_instance.GetPerfStats().EndGameFrame();
189 system_instance.Renderer().SwapBuffers({}); 189 system_instance.GPU().SwapBuffers({});
190 continue; 190 continue;
191 } 191 }
192 192
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index 74da4d5e6..e9ee73710 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -30,7 +30,7 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
30 30
31 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; 31 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
32 rb.Push(RESULT_SUCCESS); 32 rb.Push(RESULT_SUCCESS);
33 Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client}; 33 Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->GetParent()->client};
34 rb.PushMoveObjects(session); 34 rb.PushMoveObjects(session);
35 35
36 LOG_DEBUG(Service, "session={}", session->GetObjectId()); 36 LOG_DEBUG(Service, "session={}", session->GetObjectId());
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index f809567b6..6591c45d2 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -171,9 +171,6 @@ T Read(const VAddr vaddr) {
171 return value; 171 return value;
172 } 172 }
173 173
174 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
175 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
176
177 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 174 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
178 switch (type) { 175 switch (type) {
179 case PageType::Unmapped: 176 case PageType::Unmapped:
@@ -204,9 +201,6 @@ void Write(const VAddr vaddr, const T data) {
204 return; 201 return;
205 } 202 }
206 203
207 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
208 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
209
210 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 204 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
211 switch (type) { 205 switch (type) {
212 case PageType::Unmapped: 206 case PageType::Unmapped:
@@ -362,16 +356,16 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
362 const VAddr overlap_end = std::min(end, region_end); 356 const VAddr overlap_end = std::min(end, region_end);
363 const VAddr overlap_size = overlap_end - overlap_start; 357 const VAddr overlap_size = overlap_end - overlap_start;
364 358
365 auto& rasterizer = system_instance.Renderer().Rasterizer(); 359 auto& gpu = system_instance.GPU();
366 switch (mode) { 360 switch (mode) {
367 case FlushMode::Flush: 361 case FlushMode::Flush:
368 rasterizer.FlushRegion(overlap_start, overlap_size); 362 gpu.FlushRegion(overlap_start, overlap_size);
369 break; 363 break;
370 case FlushMode::Invalidate: 364 case FlushMode::Invalidate:
371 rasterizer.InvalidateRegion(overlap_start, overlap_size); 365 gpu.InvalidateRegion(overlap_start, overlap_size);
372 break; 366 break;
373 case FlushMode::FlushAndInvalidate: 367 case FlushMode::FlushAndInvalidate:
374 rasterizer.FlushAndInvalidateRegion(overlap_start, overlap_size); 368 gpu.FlushAndInvalidateRegion(overlap_start, overlap_size);
375 break; 369 break;
376 } 370 }
377 }; 371 };
diff --git a/src/core/settings.h b/src/core/settings.h
index 7e76e0466..cdfb2f742 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -393,6 +393,7 @@ struct Values {
393 u16 frame_limit; 393 u16 frame_limit;
394 bool use_disk_shader_cache; 394 bool use_disk_shader_cache;
395 bool use_accurate_gpu_emulation; 395 bool use_accurate_gpu_emulation;
396 bool use_asynchronous_gpu_emulation;
396 397
397 float bg_red; 398 float bg_red;
398 float bg_green; 399 float bg_green;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 58dfcc4df..e1db06811 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -162,6 +162,8 @@ TelemetrySession::TelemetrySession() {
162 Settings::values.use_disk_shader_cache); 162 Settings::values.use_disk_shader_cache);
163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation", 163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
164 Settings::values.use_accurate_gpu_emulation); 164 Settings::values.use_accurate_gpu_emulation);
165 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAsynchronousGpuEmulation",
166 Settings::values.use_asynchronous_gpu_emulation);
165 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", 167 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
166 Settings::values.use_docked_mode); 168 Settings::values.use_docked_mode);
167} 169}
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 9b8a44fa1..ea27ef90d 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -13,11 +13,11 @@
13namespace ArmTests { 13namespace ArmTests {
14 14
15TestEnvironment::TestEnvironment(bool mutable_memory_) 15TestEnvironment::TestEnvironment(bool mutable_memory_)
16 : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { 16 : mutable_memory(mutable_memory_),
17 17 test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} {
18 auto process = Kernel::Process::Create(kernel, ""); 18 auto process = Kernel::Process::Create(kernel, "");
19 kernel.MakeCurrentProcess(process.get()); 19 kernel.MakeCurrentProcess(process.get());
20 page_table = &Core::CurrentProcess()->VMManager().page_table; 20 page_table = &process->VMManager().page_table;
21 21
22 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); 22 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
23 page_table->special_regions.clear(); 23 page_table->special_regions.clear();
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 3e9d2b3be..57f31cd58 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -17,6 +17,12 @@ add_library(video_core STATIC
17 engines/shader_header.h 17 engines/shader_header.h
18 gpu.cpp 18 gpu.cpp
19 gpu.h 19 gpu.h
20 gpu_asynch.cpp
21 gpu_asynch.h
22 gpu_synch.cpp
23 gpu_synch.h
24 gpu_thread.cpp
25 gpu_thread.h
20 macro_interpreter.cpp 26 macro_interpreter.cpp
21 macro_interpreter.h 27 macro_interpreter.h
22 memory_manager.cpp 28 memory_manager.cpp
@@ -94,6 +100,8 @@ add_library(video_core STATIC
94 surface.h 100 surface.h
95 textures/astc.cpp 101 textures/astc.cpp
96 textures/astc.h 102 textures/astc.h
103 textures/convert.cpp
104 textures/convert.h
97 textures/decoders.cpp 105 textures/decoders.cpp
98 textures/decoders.h 106 textures/decoders.h
99 textures/texture.h 107 textures/texture.h
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 540dcc52c..03b7ee5d8 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -2,12 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h" 5#include "common/assert.h"
6#include "core/memory.h" 6#include "common/logging/log.h"
7#include "common/math_util.h"
7#include "video_core/engines/fermi_2d.h" 8#include "video_core/engines/fermi_2d.h"
8#include "video_core/engines/maxwell_3d.h"
9#include "video_core/rasterizer_interface.h" 9#include "video_core/rasterizer_interface.h"
10#include "video_core/textures/decoders.h"
11 10
12namespace Tegra::Engines { 11namespace Tegra::Engines {
13 12
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index c69f74cc5..80523e320 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -5,7 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/assert.h" 8#include <cstddef>
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 4ca856b6b..b1d950460 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -2,9 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h"
5#include "common/logging/log.h" 6#include "common/logging/log.h"
6#include "core/core.h"
7#include "core/memory.h"
8#include "video_core/engines/kepler_compute.h" 7#include "video_core/engines/kepler_compute.h"
9#include "video_core/memory_manager.h" 8#include "video_core/memory_manager.h"
10 9
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index df0a32e0f..6575afd0f 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -5,8 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/assert.h" 8#include <cstddef>
9#include "common/bit_field.h"
10#include "common/common_funcs.h" 9#include "common/common_funcs.h"
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "video_core/gpu.h" 11#include "video_core/gpu.h"
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 4f6126116..aae2a4019 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -48,7 +48,7 @@ void KeplerMemory::ProcessData(u32 data) {
48 // We have to invalidate the destination region to evict any outdated surfaces from the cache. 48 // We have to invalidate the destination region to evict any outdated surfaces from the cache.
49 // We do this before actually writing the new data because the destination address might contain 49 // We do this before actually writing the new data because the destination address might contain
50 // a dirty surface that will have to be written back to memory. 50 // a dirty surface that will have to be written back to memory.
51 rasterizer.InvalidateRegion(*dest_address, sizeof(u32)); 51 Core::System::GetInstance().GPU().InvalidateRegion(*dest_address, sizeof(u32));
52 52
53 Memory::Write32(*dest_address, data); 53 Memory::Write32(*dest_address, data);
54 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); 54 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index f680c2ad9..9181e9d80 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <cstddef>
8#include "common/bit_field.h" 9#include "common/bit_field.h"
9#include "common/common_funcs.h" 10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 529a14ec7..9dfea5999 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h"
6#include "core/core.h" 7#include "core/core.h"
7#include "core/memory.h" 8#include "core/memory.h"
8#include "video_core/engines/maxwell_3d.h" 9#include "video_core/engines/maxwell_3d.h"
@@ -91,12 +92,12 @@ void MaxwellDMA::HandleCopy() {
91 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { 92 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
92 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated 93 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
93 // copying. 94 // copying.
94 rasterizer.FlushRegion(*source_cpu, src_size); 95 Core::System::GetInstance().GPU().FlushRegion(*source_cpu, src_size);
95 96
96 // We have to invalidate the destination region to evict any outdated surfaces from the 97 // We have to invalidate the destination region to evict any outdated surfaces from the
97 // cache. We do this before actually writing the new data because the destination address 98 // cache. We do this before actually writing the new data because the destination address
98 // might contain a dirty surface that will have to be written back to memory. 99 // might contain a dirty surface that will have to be written back to memory.
99 rasterizer.InvalidateRegion(*dest_cpu, dst_size); 100 Core::System::GetInstance().GPU().InvalidateRegion(*dest_cpu, dst_size);
100 }; 101 };
101 102
102 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 103 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index cf75aeb12..34c369320 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <cstddef>
8#include "common/bit_field.h" 9#include "common/bit_field.h"
9#include "common/common_funcs.h" 10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 252592edd..c7eb15b6a 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -6,7 +6,6 @@
6 6
7#include <bitset> 7#include <bitset>
8#include <optional> 8#include <optional>
9#include <string>
10#include <tuple> 9#include <tuple>
11#include <vector> 10#include <vector>
12 11
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index ac30d1a89..08abf8ac9 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -12,7 +12,7 @@
12#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/engines/maxwell_dma.h" 13#include "video_core/engines/maxwell_dma.h"
14#include "video_core/gpu.h" 14#include "video_core/gpu.h"
15#include "video_core/rasterizer_interface.h" 15#include "video_core/renderer_base.h"
16 16
17namespace Tegra { 17namespace Tegra {
18 18
@@ -28,7 +28,8 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
28 UNREACHABLE(); 28 UNREACHABLE();
29} 29}
30 30
31GPU::GPU(Core::System& system, VideoCore::RasterizerInterface& rasterizer) { 31GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{renderer} {
32 auto& rasterizer{renderer.Rasterizer()};
32 memory_manager = std::make_unique<Tegra::MemoryManager>(); 33 memory_manager = std::make_unique<Tegra::MemoryManager>();
33 dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); 34 dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
34 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); 35 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 6313702f2..14a421cc1 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -16,8 +16,8 @@ class System;
16} 16}
17 17
18namespace VideoCore { 18namespace VideoCore {
19class RasterizerInterface; 19class RendererBase;
20} 20} // namespace VideoCore
21 21
22namespace Tegra { 22namespace Tegra {
23 23
@@ -119,9 +119,10 @@ enum class EngineID {
119 MAXWELL_DMA_COPY_A = 0xB0B5, 119 MAXWELL_DMA_COPY_A = 0xB0B5,
120}; 120};
121 121
122class GPU final { 122class GPU {
123public: 123public:
124 explicit GPU(Core::System& system, VideoCore::RasterizerInterface& rasterizer); 124 explicit GPU(Core::System& system, VideoCore::RendererBase& renderer);
125
125 ~GPU(); 126 ~GPU();
126 127
127 struct MethodCall { 128 struct MethodCall {
@@ -200,8 +201,42 @@ public:
200 }; 201 };
201 } regs{}; 202 } regs{};
202 203
204 /// Push GPU command entries to be processed
205 virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
206
207 /// Swap buffers (render frame)
208 virtual void SwapBuffers(
209 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0;
210
211 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
212 virtual void FlushRegion(VAddr addr, u64 size) = 0;
213
214 /// Notify rasterizer that any caches of the specified region should be invalidated
215 virtual void InvalidateRegion(VAddr addr, u64 size) = 0;
216
217 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
218 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
219
203private: 220private:
221 void ProcessBindMethod(const MethodCall& method_call);
222 void ProcessSemaphoreTriggerMethod();
223 void ProcessSemaphoreRelease();
224 void ProcessSemaphoreAcquire();
225
226 /// Calls a GPU puller method.
227 void CallPullerMethod(const MethodCall& method_call);
228
229 /// Calls a GPU engine method.
230 void CallEngineMethod(const MethodCall& method_call);
231
232 /// Determines where the method should be executed.
233 bool ExecuteMethodOnEngine(const MethodCall& method_call);
234
235protected:
204 std::unique_ptr<Tegra::DmaPusher> dma_pusher; 236 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
237 VideoCore::RendererBase& renderer;
238
239private:
205 std::unique_ptr<Tegra::MemoryManager> memory_manager; 240 std::unique_ptr<Tegra::MemoryManager> memory_manager;
206 241
207 /// Mapping of command subchannels to their bound engine ids. 242 /// Mapping of command subchannels to their bound engine ids.
@@ -217,18 +252,6 @@ private:
217 std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; 252 std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
218 /// Inline memory engine 253 /// Inline memory engine
219 std::unique_ptr<Engines::KeplerMemory> kepler_memory; 254 std::unique_ptr<Engines::KeplerMemory> kepler_memory;
220
221 void ProcessBindMethod(const MethodCall& method_call);
222 void ProcessSemaphoreTriggerMethod();
223 void ProcessSemaphoreRelease();
224 void ProcessSemaphoreAcquire();
225
226 // Calls a GPU puller method.
227 void CallPullerMethod(const MethodCall& method_call);
228 // Calls a GPU engine method.
229 void CallEngineMethod(const MethodCall& method_call);
230 // Determines where the method should be executed.
231 bool ExecuteMethodOnEngine(const MethodCall& method_call);
232}; 255};
233 256
234#define ASSERT_REG_POSITION(field_name, position) \ 257#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
new file mode 100644
index 000000000..ad0a747e3
--- /dev/null
+++ b/src/video_core/gpu_asynch.cpp
@@ -0,0 +1,37 @@
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_asynch.h"
6#include "video_core/gpu_thread.h"
7#include "video_core/renderer_base.h"
8
9namespace VideoCommon {
10
11GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
12 : Tegra::GPU(system, renderer), gpu_thread{renderer, *dma_pusher} {}
13
14GPUAsynch::~GPUAsynch() = default;
15
16void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
17 gpu_thread.SubmitList(std::move(entries));
18}
19
20void GPUAsynch::SwapBuffers(
21 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
22 gpu_thread.SwapBuffers(std::move(framebuffer));
23}
24
25void GPUAsynch::FlushRegion(VAddr addr, u64 size) {
26 gpu_thread.FlushRegion(addr, size);
27}
28
29void GPUAsynch::InvalidateRegion(VAddr addr, u64 size) {
30 gpu_thread.InvalidateRegion(addr, size);
31}
32
33void GPUAsynch::FlushAndInvalidateRegion(VAddr addr, u64 size) {
34 gpu_thread.FlushAndInvalidateRegion(addr, size);
35}
36
37} // namespace VideoCommon
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
new file mode 100644
index 000000000..58046f3e9
--- /dev/null
+++ b/src/video_core/gpu_asynch.h
@@ -0,0 +1,37 @@
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 VideoCore {
11class RendererBase;
12} // namespace VideoCore
13
14namespace VideoCommon {
15
16namespace GPUThread {
17class ThreadManager;
18} // namespace GPUThread
19
20/// Implementation of GPU interface that runs the GPU asynchronously
21class GPUAsynch : public Tegra::GPU {
22public:
23 explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer);
24 ~GPUAsynch();
25
26 void PushGPUEntries(Tegra::CommandList&& entries) override;
27 void SwapBuffers(
28 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
29 void FlushRegion(VAddr addr, u64 size) override;
30 void InvalidateRegion(VAddr addr, u64 size) override;
31 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
32
33private:
34 GPUThread::ThreadManager gpu_thread;
35};
36
37} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
new file mode 100644
index 000000000..4c00b96c7
--- /dev/null
+++ b/src/video_core/gpu_synch.cpp
@@ -0,0 +1,37 @@
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, VideoCore::RendererBase& renderer)
11 : Tegra::GPU(system, renderer) {}
12
13GPUSynch::~GPUSynch() = default;
14
15void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
16 dma_pusher->Push(std::move(entries));
17 dma_pusher->DispatchCalls();
18}
19
20void GPUSynch::SwapBuffers(
21 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
22 renderer.SwapBuffers(std::move(framebuffer));
23}
24
25void GPUSynch::FlushRegion(VAddr addr, u64 size) {
26 renderer.Rasterizer().FlushRegion(addr, size);
27}
28
29void GPUSynch::InvalidateRegion(VAddr addr, u64 size) {
30 renderer.Rasterizer().InvalidateRegion(addr, size);
31}
32
33void GPUSynch::FlushAndInvalidateRegion(VAddr addr, u64 size) {
34 renderer.Rasterizer().FlushAndInvalidateRegion(addr, size);
35}
36
37} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
new file mode 100644
index 000000000..658f683e2
--- /dev/null
+++ b/src/video_core/gpu_synch.h
@@ -0,0 +1,29 @@
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 VideoCore {
10class RendererBase;
11} // namespace VideoCore
12
13namespace VideoCommon {
14
15/// Implementation of GPU interface that runs the GPU synchronously
16class GPUSynch : public Tegra::GPU {
17public:
18 explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer);
19 ~GPUSynch();
20
21 void PushGPUEntries(Tegra::CommandList&& entries) override;
22 void SwapBuffers(
23 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
24 void FlushRegion(VAddr addr, u64 size) override;
25 void InvalidateRegion(VAddr addr, u64 size) override;
26 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
27};
28
29} // namespace VideoCommon
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
new file mode 100644
index 000000000..c5bdd2a17
--- /dev/null
+++ b/src/video_core/gpu_thread.cpp
@@ -0,0 +1,152 @@
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 "common/assert.h"
6#include "common/microprofile.h"
7#include "core/frontend/scope_acquire_window_context.h"
8#include "core/settings.h"
9#include "video_core/dma_pusher.h"
10#include "video_core/gpu.h"
11#include "video_core/gpu_thread.h"
12#include "video_core/renderer_base.h"
13
14namespace VideoCommon::GPUThread {
15
16/// Executes a single GPU thread command
17static void ExecuteCommand(CommandData* command, VideoCore::RendererBase& renderer,
18 Tegra::DmaPusher& dma_pusher) {
19 if (const auto submit_list = std::get_if<SubmitListCommand>(command)) {
20 dma_pusher.Push(std::move(submit_list->entries));
21 dma_pusher.DispatchCalls();
22 } else if (const auto data = std::get_if<SwapBuffersCommand>(command)) {
23 renderer.SwapBuffers(data->framebuffer);
24 } else if (const auto data = std::get_if<FlushRegionCommand>(command)) {
25 renderer.Rasterizer().FlushRegion(data->addr, data->size);
26 } else if (const auto data = std::get_if<InvalidateRegionCommand>(command)) {
27 renderer.Rasterizer().InvalidateRegion(data->addr, data->size);
28 } else if (const auto data = std::get_if<FlushAndInvalidateRegionCommand>(command)) {
29 renderer.Rasterizer().FlushAndInvalidateRegion(data->addr, data->size);
30 } else {
31 UNREACHABLE();
32 }
33}
34
35/// Runs the GPU thread
36static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher,
37 SynchState& state) {
38
39 MicroProfileOnThreadCreate("GpuThread");
40
41 auto WaitForWakeup = [&]() {
42 std::unique_lock<std::mutex> lock{state.signal_mutex};
43 state.signal_condition.wait(lock, [&] { return !state.is_idle || !state.is_running; });
44 };
45
46 // Wait for first GPU command before acquiring the window context
47 WaitForWakeup();
48
49 // If emulation was stopped during disk shader loading, abort before trying to acquire context
50 if (!state.is_running) {
51 return;
52 }
53
54 Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()};
55
56 while (state.is_running) {
57 if (!state.is_running) {
58 return;
59 }
60
61 {
62 // Thread has been woken up, so make the previous write queue the next read queue
63 std::lock_guard<std::mutex> lock{state.signal_mutex};
64 std::swap(state.push_queue, state.pop_queue);
65 }
66
67 // Execute all of the GPU commands
68 while (!state.pop_queue->empty()) {
69 ExecuteCommand(&state.pop_queue->front(), renderer, dma_pusher);
70 state.pop_queue->pop();
71 }
72
73 state.UpdateIdleState();
74
75 // Signal that the GPU thread has finished processing commands
76 if (state.is_idle) {
77 state.idle_condition.notify_one();
78 }
79
80 // Wait for CPU thread to send more GPU commands
81 WaitForWakeup();
82 }
83}
84
85ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher)
86 : renderer{renderer}, dma_pusher{dma_pusher}, thread{RunThread, std::ref(renderer),
87 std::ref(dma_pusher), std::ref(state)},
88 thread_id{thread.get_id()} {}
89
90ThreadManager::~ThreadManager() {
91 {
92 // Notify GPU thread that a shutdown is pending
93 std::lock_guard<std::mutex> lock{state.signal_mutex};
94 state.is_running = false;
95 }
96
97 state.signal_condition.notify_one();
98 thread.join();
99}
100
101void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
102 if (entries.empty()) {
103 return;
104 }
105
106 PushCommand(SubmitListCommand(std::move(entries)), false, false);
107}
108
109void ThreadManager::SwapBuffers(
110 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
111 PushCommand(SwapBuffersCommand(std::move(framebuffer)), true, false);
112}
113
114void ThreadManager::FlushRegion(VAddr addr, u64 size) {
115 // Block the CPU when using accurate emulation
116 PushCommand(FlushRegionCommand(addr, size), Settings::values.use_accurate_gpu_emulation, false);
117}
118
119void ThreadManager::InvalidateRegion(VAddr addr, u64 size) {
120 PushCommand(InvalidateRegionCommand(addr, size), true, true);
121}
122
123void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
124 InvalidateRegion(addr, size);
125}
126
127void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu) {
128 {
129 std::lock_guard<std::mutex> lock{state.signal_mutex};
130
131 if ((allow_on_cpu && state.is_idle) || IsGpuThread()) {
132 // Execute the command synchronously on the current thread
133 ExecuteCommand(&command_data, renderer, dma_pusher);
134 return;
135 }
136
137 // Push the command to the GPU thread
138 state.UpdateIdleState();
139 state.push_queue->emplace(command_data);
140 }
141
142 // Signal the GPU thread that commands are pending
143 state.signal_condition.notify_one();
144
145 if (wait_for_idle) {
146 // Wait for the GPU to be idle (all commands to be executed)
147 std::unique_lock<std::mutex> lock{state.idle_mutex};
148 state.idle_condition.wait(lock, [this] { return static_cast<bool>(state.is_idle); });
149 }
150}
151
152} // namespace VideoCommon::GPUThread
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
new file mode 100644
index 000000000..2ad8214cc
--- /dev/null
+++ b/src/video_core/gpu_thread.h
@@ -0,0 +1,136 @@
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 <array>
8#include <atomic>
9#include <condition_variable>
10#include <memory>
11#include <mutex>
12#include <optional>
13#include <thread>
14#include <variant>
15
16namespace Tegra {
17struct FramebufferConfig;
18class DmaPusher;
19} // namespace Tegra
20
21namespace VideoCore {
22class RendererBase;
23} // namespace VideoCore
24
25namespace VideoCommon::GPUThread {
26
27/// Command to signal to the GPU thread that a command list is ready for processing
28struct SubmitListCommand final {
29 explicit SubmitListCommand(Tegra::CommandList&& entries) : entries{std::move(entries)} {}
30
31 Tegra::CommandList entries;
32};
33
34/// Command to signal to the GPU thread that a swap buffers is pending
35struct SwapBuffersCommand final {
36 explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer)
37 : framebuffer{std::move(framebuffer)} {}
38
39 std::optional<const Tegra::FramebufferConfig> framebuffer;
40};
41
42/// Command to signal to the GPU thread to flush a region
43struct FlushRegionCommand final {
44 explicit constexpr FlushRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {}
45
46 const VAddr addr;
47 const u64 size;
48};
49
50/// Command to signal to the GPU thread to invalidate a region
51struct InvalidateRegionCommand final {
52 explicit constexpr InvalidateRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {}
53
54 const VAddr addr;
55 const u64 size;
56};
57
58/// Command to signal to the GPU thread to flush and invalidate a region
59struct FlushAndInvalidateRegionCommand final {
60 explicit constexpr FlushAndInvalidateRegionCommand(VAddr addr, u64 size)
61 : addr{addr}, size{size} {}
62
63 const VAddr addr;
64 const u64 size;
65};
66
67using CommandData = std::variant<SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
68 InvalidateRegionCommand, FlushAndInvalidateRegionCommand>;
69
70/// Struct used to synchronize the GPU thread
71struct SynchState final {
72 std::atomic<bool> is_running{true};
73 std::atomic<bool> is_idle{true};
74 std::condition_variable signal_condition;
75 std::mutex signal_mutex;
76 std::condition_variable idle_condition;
77 std::mutex idle_mutex;
78
79 // We use two queues for sending commands to the GPU thread, one for writing (push_queue) to and
80 // one for reading from (pop_queue). These are swapped whenever the current pop_queue becomes
81 // empty. This allows for efficient thread-safe access, as it does not require any copies.
82
83 using CommandQueue = std::queue<CommandData>;
84 std::array<CommandQueue, 2> command_queues;
85 CommandQueue* push_queue{&command_queues[0]};
86 CommandQueue* pop_queue{&command_queues[1]};
87
88 void UpdateIdleState() {
89 std::lock_guard<std::mutex> lock{idle_mutex};
90 is_idle = command_queues[0].empty() && command_queues[1].empty();
91 }
92};
93
94/// Class used to manage the GPU thread
95class ThreadManager final {
96public:
97 explicit ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher);
98 ~ThreadManager();
99
100 /// Push GPU command entries to be processed
101 void SubmitList(Tegra::CommandList&& entries);
102
103 /// Swap buffers (render frame)
104 void SwapBuffers(
105 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer);
106
107 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
108 void FlushRegion(VAddr addr, u64 size);
109
110 /// Notify rasterizer that any caches of the specified region should be invalidated
111 void InvalidateRegion(VAddr addr, u64 size);
112
113 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
114 void FlushAndInvalidateRegion(VAddr addr, u64 size);
115
116 /// Waits the caller until the GPU thread is idle, used for synchronization
117 void WaitForIdle();
118
119private:
120 /// Pushes a command to be executed by the GPU thread
121 void PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu);
122
123 /// Returns true if this is called by the GPU thread
124 bool IsGpuThread() const {
125 return std::this_thread::get_id() == thread_id;
126 }
127
128private:
129 SynchState state;
130 std::thread thread;
131 std::thread::id thread_id;
132 VideoCore::RendererBase& renderer;
133 Tegra::DmaPusher& dma_pusher;
134};
135
136} // namespace VideoCommon::GPUThread
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 94223f45f..919d1f2d4 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
5#include "core/frontend/emu_window.h" 6#include "core/frontend/emu_window.h"
6#include "core/settings.h" 7#include "core/settings.h"
7#include "video_core/renderer_base.h" 8#include "video_core/renderer_base.h"
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 321d9dd3d..168288088 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -749,11 +749,7 @@ void RasterizerOpenGL::FlushAll() {}
749 749
750void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { 750void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
751 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 751 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
752 752 res_cache.FlushRegion(addr, size);
753 if (Settings::values.use_accurate_gpu_emulation) {
754 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
755 res_cache.FlushRegion(addr, size);
756 }
757} 753}
758 754
759void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 755void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index b5a9722f9..876698b37 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -21,7 +21,7 @@
21#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 21#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
22#include "video_core/renderer_opengl/utils.h" 22#include "video_core/renderer_opengl/utils.h"
23#include "video_core/surface.h" 23#include "video_core/surface.h"
24#include "video_core/textures/astc.h" 24#include "video_core/textures/convert.h"
25#include "video_core/textures/decoders.h" 25#include "video_core/textures/decoders.h"
26 26
27namespace OpenGL { 27namespace OpenGL {
@@ -597,103 +597,6 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
597 } 597 }
598} 598}
599 599
600static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bool reverse) {
601 union S8Z24 {
602 BitField<0, 24, u32> z24;
603 BitField<24, 8, u32> s8;
604 };
605 static_assert(sizeof(S8Z24) == 4, "S8Z24 is incorrect size");
606
607 union Z24S8 {
608 BitField<0, 8, u32> s8;
609 BitField<8, 24, u32> z24;
610 };
611 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size");
612
613 S8Z24 s8z24_pixel{};
614 Z24S8 z24s8_pixel{};
615 constexpr auto bpp{GetBytesPerPixel(PixelFormat::S8Z24)};
616 for (std::size_t y = 0; y < height; ++y) {
617 for (std::size_t x = 0; x < width; ++x) {
618 const std::size_t offset{bpp * (y * width + x)};
619 if (reverse) {
620 std::memcpy(&z24s8_pixel, &data[offset], sizeof(Z24S8));
621 s8z24_pixel.s8.Assign(z24s8_pixel.s8);
622 s8z24_pixel.z24.Assign(z24s8_pixel.z24);
623 std::memcpy(&data[offset], &s8z24_pixel, sizeof(S8Z24));
624 } else {
625 std::memcpy(&s8z24_pixel, &data[offset], sizeof(S8Z24));
626 z24s8_pixel.s8.Assign(s8z24_pixel.s8);
627 z24s8_pixel.z24.Assign(s8z24_pixel.z24);
628 std::memcpy(&data[offset], &z24s8_pixel, sizeof(Z24S8));
629 }
630 }
631 }
632}
633
634/**
635 * Helper function to perform software conversion (as needed) when loading a buffer from Switch
636 * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
637 * typical desktop GPUs.
638 */
639static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
640 u32 width, u32 height, u32 depth) {
641 switch (pixel_format) {
642 case PixelFormat::ASTC_2D_4X4:
643 case PixelFormat::ASTC_2D_8X8:
644 case PixelFormat::ASTC_2D_8X5:
645 case PixelFormat::ASTC_2D_5X4:
646 case PixelFormat::ASTC_2D_5X5:
647 case PixelFormat::ASTC_2D_4X4_SRGB:
648 case PixelFormat::ASTC_2D_8X8_SRGB:
649 case PixelFormat::ASTC_2D_8X5_SRGB:
650 case PixelFormat::ASTC_2D_5X4_SRGB:
651 case PixelFormat::ASTC_2D_5X5_SRGB:
652 case PixelFormat::ASTC_2D_10X8:
653 case PixelFormat::ASTC_2D_10X8_SRGB: {
654 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
655 u32 block_width{};
656 u32 block_height{};
657 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format);
658 data =
659 Tegra::Texture::ASTC::Decompress(data, width, height, depth, block_width, block_height);
660 break;
661 }
662 case PixelFormat::S8Z24:
663 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
664 ConvertS8Z24ToZ24S8(data, width, height, false);
665 break;
666 }
667}
668
669/**
670 * Helper function to perform software conversion (as needed) when flushing a buffer from OpenGL to
671 * Switch memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or
672 * with typical desktop GPUs.
673 */
674static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
675 u32 width, u32 height) {
676 switch (pixel_format) {
677 case PixelFormat::ASTC_2D_4X4:
678 case PixelFormat::ASTC_2D_8X8:
679 case PixelFormat::ASTC_2D_4X4_SRGB:
680 case PixelFormat::ASTC_2D_8X8_SRGB:
681 case PixelFormat::ASTC_2D_5X5:
682 case PixelFormat::ASTC_2D_5X5_SRGB:
683 case PixelFormat::ASTC_2D_10X8:
684 case PixelFormat::ASTC_2D_10X8_SRGB: {
685 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
686 static_cast<u32>(pixel_format));
687 UNREACHABLE();
688 break;
689 }
690 case PixelFormat::S8Z24:
691 // Convert the Z24S8 depth format to S8Z24, as OpenGL does not support S8Z24.
692 ConvertS8Z24ToZ24S8(data, width, height, true);
693 break;
694 }
695}
696
697MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64)); 600MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64));
698void CachedSurface::LoadGLBuffer() { 601void CachedSurface::LoadGLBuffer() {
699 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 602 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
@@ -722,8 +625,16 @@ void CachedSurface::LoadGLBuffer() {
722 } 625 }
723 } 626 }
724 for (u32 i = 0; i < params.max_mip_level; i++) { 627 for (u32 i = 0; i < params.max_mip_level; i++) {
725 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i), 628 const u32 width = params.MipWidth(i);
726 params.MipHeight(i), params.MipDepth(i)); 629 const u32 height = params.MipHeight(i);
630 const u32 depth = params.MipDepth(i);
631 if (VideoCore::Surface::IsPixelFormatASTC(params.pixel_format)) {
632 // Reserve size for RGBA8 conversion
633 constexpr std::size_t rgba_bpp = 4;
634 gl_buffer[i].resize(std::max(gl_buffer[i].size(), width * height * depth * rgba_bpp));
635 }
636 Tegra::Texture::ConvertFromGuestToHost(gl_buffer[i].data(), params.pixel_format, width,
637 height, depth, true, true);
727 } 638 }
728} 639}
729 640
@@ -746,8 +657,8 @@ void CachedSurface::FlushGLBuffer() {
746 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, 657 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type,
747 static_cast<GLsizei>(gl_buffer[0].size()), gl_buffer[0].data()); 658 static_cast<GLsizei>(gl_buffer[0].size()), gl_buffer[0].data());
748 glPixelStorei(GL_PACK_ROW_LENGTH, 0); 659 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
749 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer[0], params.pixel_format, params.width, 660 Tegra::Texture::ConvertFromHostToGuest(gl_buffer[0].data(), params.pixel_format, params.width,
750 params.height); 661 params.height, params.depth, true, true);
751 const u8* const texture_src_data = Memory::GetPointer(params.addr); 662 const u8* const texture_src_data = Memory::GetPointer(params.addr);
752 ASSERT(texture_src_data); 663 ASSERT(texture_src_data);
753 if (params.is_tiled) { 664 if (params.is_tiled) {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index e60b2eb44..8b510b6ae 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -244,6 +244,21 @@ void RendererOpenGL::InitOpenGLObjects() {
244 LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); 244 LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture);
245} 245}
246 246
247void RendererOpenGL::AddTelemetryFields() {
248 const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))};
249 const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))};
250 const char* const gpu_model{reinterpret_cast<char const*>(glGetString(GL_RENDERER))};
251
252 LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version);
253 LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor);
254 LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
255
256 auto& telemetry_session = system.TelemetrySession();
257 telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_Vendor", gpu_vendor);
258 telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model);
259 telemetry_session.AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version);
260}
261
247void RendererOpenGL::CreateRasterizer() { 262void RendererOpenGL::CreateRasterizer() {
248 if (rasterizer) { 263 if (rasterizer) {
249 return; 264 return;
@@ -466,17 +481,7 @@ bool RendererOpenGL::Init() {
466 glDebugMessageCallback(DebugHandler, nullptr); 481 glDebugMessageCallback(DebugHandler, nullptr);
467 } 482 }
468 483
469 const char* gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))}; 484 AddTelemetryFields();
470 const char* gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))};
471 const char* gpu_model{reinterpret_cast<char const*>(glGetString(GL_RENDERER))};
472
473 LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version);
474 LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor);
475 LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
476
477 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Vendor", gpu_vendor);
478 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model);
479 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version);
480 485
481 if (!GLAD_GL_VERSION_4_3) { 486 if (!GLAD_GL_VERSION_4_3) {
482 return false; 487 return false;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index c168fa89e..6cbf9d2cb 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -60,6 +60,7 @@ public:
60 60
61private: 61private:
62 void InitOpenGLObjects(); 62 void InitOpenGLObjects();
63 void AddTelemetryFields();
63 void CreateRasterizer(); 64 void CreateRasterizer();
64 65
65 void ConfigureFramebufferTexture(TextureInfo& texture, 66 void ConfigureFramebufferTexture(TextureInfo& texture,
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 18b7b94a1..4a33a6c84 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -8,7 +8,7 @@
8#include <tuple> 8#include <tuple>
9 9
10#include "common/alignment.h" 10#include "common/alignment.h"
11#include "core/core.h" 11#include "common/assert.h"
12#include "core/memory.h" 12#include "core/memory.h"
13#include "video_core/renderer_vulkan/declarations.h" 13#include "video_core/renderer_vulkan/declarations.h"
14#include "video_core/renderer_vulkan/vk_buffer_cache.h" 14#include "video_core/renderer_vulkan/vk_buffer_cache.h"
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 044ba116a..a7ac26d71 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -89,8 +89,6 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
89 89
90PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { 90PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
91 switch (format) { 91 switch (format) {
92 // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the
93 // gamma.
94 case Tegra::RenderTargetFormat::RGBA8_SRGB: 92 case Tegra::RenderTargetFormat::RGBA8_SRGB:
95 return PixelFormat::RGBA8_SRGB; 93 return PixelFormat::RGBA8_SRGB;
96 case Tegra::RenderTargetFormat::RGBA8_UNORM: 94 case Tegra::RenderTargetFormat::RGBA8_UNORM:
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index bc50a4876..b508d64e9 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -23,28 +23,12 @@
23 23
24#include "video_core/textures/astc.h" 24#include "video_core/textures/astc.h"
25 25
26class BitStream { 26class InputBitStream {
27public: 27public:
28 explicit BitStream(unsigned char* ptr, int nBits = 0, int start_offset = 0) 28 explicit InputBitStream(const unsigned char* ptr, int nBits = 0, int start_offset = 0)
29 : m_NumBits(nBits), m_CurByte(ptr), m_NextBit(start_offset % 8) {} 29 : m_NumBits(nBits), m_CurByte(ptr), m_NextBit(start_offset % 8) {}
30 30
31 ~BitStream() = default; 31 ~InputBitStream() = default;
32
33 int GetBitsWritten() const {
34 return m_BitsWritten;
35 }
36
37 void WriteBitsR(unsigned int val, unsigned int nBits) {
38 for (unsigned int i = 0; i < nBits; i++) {
39 WriteBit((val >> (nBits - i - 1)) & 1);
40 }
41 }
42
43 void WriteBits(unsigned int val, unsigned int nBits) {
44 for (unsigned int i = 0; i < nBits; i++) {
45 WriteBit((val >> i) & 1);
46 }
47 }
48 32
49 int GetBitsRead() const { 33 int GetBitsRead() const {
50 return m_BitsRead; 34 return m_BitsRead;
@@ -71,6 +55,38 @@ public:
71 } 55 }
72 56
73private: 57private:
58 const int m_NumBits;
59 const unsigned char* m_CurByte;
60 int m_NextBit = 0;
61 int m_BitsRead = 0;
62
63 bool done = false;
64};
65
66class OutputBitStream {
67public:
68 explicit OutputBitStream(unsigned char* ptr, int nBits = 0, int start_offset = 0)
69 : m_NumBits(nBits), m_CurByte(ptr), m_NextBit(start_offset % 8) {}
70
71 ~OutputBitStream() = default;
72
73 int GetBitsWritten() const {
74 return m_BitsWritten;
75 }
76
77 void WriteBitsR(unsigned int val, unsigned int nBits) {
78 for (unsigned int i = 0; i < nBits; i++) {
79 WriteBit((val >> (nBits - i - 1)) & 1);
80 }
81 }
82
83 void WriteBits(unsigned int val, unsigned int nBits) {
84 for (unsigned int i = 0; i < nBits; i++) {
85 WriteBit((val >> i) & 1);
86 }
87 }
88
89private:
74 void WriteBit(int b) { 90 void WriteBit(int b) {
75 91
76 if (done) 92 if (done)
@@ -238,8 +254,8 @@ public:
238 // Fills result with the values that are encoded in the given 254 // Fills result with the values that are encoded in the given
239 // bitstream. We must know beforehand what the maximum possible 255 // bitstream. We must know beforehand what the maximum possible
240 // value is, and how many values we're decoding. 256 // value is, and how many values we're decoding.
241 static void DecodeIntegerSequence(std::vector<IntegerEncodedValue>& result, BitStream& bits, 257 static void DecodeIntegerSequence(std::vector<IntegerEncodedValue>& result,
242 uint32_t maxRange, uint32_t nValues) { 258 InputBitStream& bits, uint32_t maxRange, uint32_t nValues) {
243 // Determine encoding parameters 259 // Determine encoding parameters
244 IntegerEncodedValue val = IntegerEncodedValue::CreateEncoding(maxRange); 260 IntegerEncodedValue val = IntegerEncodedValue::CreateEncoding(maxRange);
245 261
@@ -267,7 +283,7 @@ public:
267 } 283 }
268 284
269private: 285private:
270 static void DecodeTritBlock(BitStream& bits, std::vector<IntegerEncodedValue>& result, 286 static void DecodeTritBlock(InputBitStream& bits, std::vector<IntegerEncodedValue>& result,
271 uint32_t nBitsPerValue) { 287 uint32_t nBitsPerValue) {
272 // Implement the algorithm in section C.2.12 288 // Implement the algorithm in section C.2.12
273 uint32_t m[5]; 289 uint32_t m[5];
@@ -327,7 +343,7 @@ private:
327 } 343 }
328 } 344 }
329 345
330 static void DecodeQuintBlock(BitStream& bits, std::vector<IntegerEncodedValue>& result, 346 static void DecodeQuintBlock(InputBitStream& bits, std::vector<IntegerEncodedValue>& result,
331 uint32_t nBitsPerValue) { 347 uint32_t nBitsPerValue) {
332 // Implement the algorithm in section C.2.12 348 // Implement the algorithm in section C.2.12
333 uint32_t m[3]; 349 uint32_t m[3];
@@ -406,7 +422,7 @@ struct TexelWeightParams {
406 } 422 }
407}; 423};
408 424
409static TexelWeightParams DecodeBlockInfo(BitStream& strm) { 425static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
410 TexelWeightParams params; 426 TexelWeightParams params;
411 427
412 // Read the entire block mode all at once 428 // Read the entire block mode all at once
@@ -605,7 +621,7 @@ static TexelWeightParams DecodeBlockInfo(BitStream& strm) {
605 return params; 621 return params;
606} 622}
607 623
608static void FillVoidExtentLDR(BitStream& strm, uint32_t* const outBuf, uint32_t blockWidth, 624static void FillVoidExtentLDR(InputBitStream& strm, uint32_t* const outBuf, uint32_t blockWidth,
609 uint32_t blockHeight) { 625 uint32_t blockHeight) {
610 // Don't actually care about the void extent, just read the bits... 626 // Don't actually care about the void extent, just read the bits...
611 for (int i = 0; i < 4; ++i) { 627 for (int i = 0; i < 4; ++i) {
@@ -821,7 +837,7 @@ static void DecodeColorValues(uint32_t* out, uint8_t* data, const uint32_t* mode
821 837
822 // We now have enough to decode our integer sequence. 838 // We now have enough to decode our integer sequence.
823 std::vector<IntegerEncodedValue> decodedColorValues; 839 std::vector<IntegerEncodedValue> decodedColorValues;
824 BitStream colorStream(data); 840 InputBitStream colorStream(data);
825 IntegerEncodedValue::DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues); 841 IntegerEncodedValue::DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues);
826 842
827 // Once we have the decoded values, we need to dequantize them to the 0-255 range 843 // Once we have the decoded values, we need to dequantize them to the 0-255 range
@@ -1365,9 +1381,9 @@ static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const uint32_t*& colorValue
1365#undef READ_INT_VALUES 1381#undef READ_INT_VALUES
1366} 1382}
1367 1383
1368static void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth, 1384static void DecompressBlock(const uint8_t inBuf[16], const uint32_t blockWidth,
1369 const uint32_t blockHeight, uint32_t* outBuf) { 1385 const uint32_t blockHeight, uint32_t* outBuf) {
1370 BitStream strm(inBuf); 1386 InputBitStream strm(inBuf);
1371 TexelWeightParams weightParams = DecodeBlockInfo(strm); 1387 TexelWeightParams weightParams = DecodeBlockInfo(strm);
1372 1388
1373 // Was there an error? 1389 // Was there an error?
@@ -1421,7 +1437,7 @@ static void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth,
1421 // Define color data. 1437 // Define color data.
1422 uint8_t colorEndpointData[16]; 1438 uint8_t colorEndpointData[16];
1423 memset(colorEndpointData, 0, sizeof(colorEndpointData)); 1439 memset(colorEndpointData, 0, sizeof(colorEndpointData));
1424 BitStream colorEndpointStream(colorEndpointData, 16 * 8, 0); 1440 OutputBitStream colorEndpointStream(colorEndpointData, 16 * 8, 0);
1425 1441
1426 // Read extra config data... 1442 // Read extra config data...
1427 uint32_t baseCEM = 0; 1443 uint32_t baseCEM = 0;
@@ -1549,7 +1565,7 @@ static void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth,
1549 memset(texelWeightData + clearByteStart, 0, 16 - clearByteStart); 1565 memset(texelWeightData + clearByteStart, 0, 16 - clearByteStart);
1550 1566
1551 std::vector<IntegerEncodedValue> texelWeightValues; 1567 std::vector<IntegerEncodedValue> texelWeightValues;
1552 BitStream weightStream(texelWeightData); 1568 InputBitStream weightStream(texelWeightData);
1553 1569
1554 IntegerEncodedValue::DecodeIntegerSequence(texelWeightValues, weightStream, 1570 IntegerEncodedValue::DecodeIntegerSequence(texelWeightValues, weightStream,
1555 weightParams.m_MaxWeight, 1571 weightParams.m_MaxWeight,
@@ -1597,7 +1613,7 @@ static void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth,
1597 1613
1598namespace Tegra::Texture::ASTC { 1614namespace Tegra::Texture::ASTC {
1599 1615
1600std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 1616std::vector<uint8_t> Decompress(const uint8_t* data, uint32_t width, uint32_t height,
1601 uint32_t depth, uint32_t block_width, uint32_t block_height) { 1617 uint32_t depth, uint32_t block_width, uint32_t block_height) {
1602 uint32_t blockIdx = 0; 1618 uint32_t blockIdx = 0;
1603 std::vector<uint8_t> outData(height * width * depth * 4); 1619 std::vector<uint8_t> outData(height * width * depth * 4);
@@ -1605,7 +1621,7 @@ std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint
1605 for (uint32_t j = 0; j < height; j += block_height) { 1621 for (uint32_t j = 0; j < height; j += block_height) {
1606 for (uint32_t i = 0; i < width; i += block_width) { 1622 for (uint32_t i = 0; i < width; i += block_width) {
1607 1623
1608 uint8_t* blockPtr = data.data() + blockIdx * 16; 1624 const uint8_t* blockPtr = data + blockIdx * 16;
1609 1625
1610 // Blocks can be at most 12x12 1626 // Blocks can be at most 12x12
1611 uint32_t uncompData[144]; 1627 uint32_t uncompData[144];
diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h
index d419dd025..991cdba72 100644
--- a/src/video_core/textures/astc.h
+++ b/src/video_core/textures/astc.h
@@ -9,7 +9,7 @@
9 9
10namespace Tegra::Texture::ASTC { 10namespace Tegra::Texture::ASTC {
11 11
12std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 12std::vector<uint8_t> Decompress(const uint8_t* data, uint32_t width, uint32_t height,
13 uint32_t depth, uint32_t block_width, uint32_t block_height); 13 uint32_t depth, uint32_t block_width, uint32_t block_height);
14 14
15} // namespace Tegra::Texture::ASTC 15} // namespace Tegra::Texture::ASTC
diff --git a/src/video_core/textures/convert.cpp b/src/video_core/textures/convert.cpp
new file mode 100644
index 000000000..5e439f036
--- /dev/null
+++ b/src/video_core/textures/convert.cpp
@@ -0,0 +1,92 @@
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 <algorithm>
6#include <cstring>
7#include <tuple>
8#include <vector>
9
10#include "common/assert.h"
11#include "common/common_types.h"
12#include "common/logging/log.h"
13#include "video_core/textures/astc.h"
14#include "video_core/textures/convert.h"
15
16namespace Tegra::Texture {
17
18using VideoCore::Surface::PixelFormat;
19
20template <bool reverse>
21void SwapS8Z24ToZ24S8(u8* data, u32 width, u32 height) {
22 union S8Z24 {
23 BitField<0, 24, u32> z24;
24 BitField<24, 8, u32> s8;
25 };
26 static_assert(sizeof(S8Z24) == 4, "S8Z24 is incorrect size");
27
28 union Z24S8 {
29 BitField<0, 8, u32> s8;
30 BitField<8, 24, u32> z24;
31 };
32 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size");
33
34 S8Z24 s8z24_pixel{};
35 Z24S8 z24s8_pixel{};
36 constexpr auto bpp{
37 VideoCore::Surface::GetBytesPerPixel(VideoCore::Surface::PixelFormat::S8Z24)};
38 for (std::size_t y = 0; y < height; ++y) {
39 for (std::size_t x = 0; x < width; ++x) {
40 const std::size_t offset{bpp * (y * width + x)};
41 if constexpr (reverse) {
42 std::memcpy(&z24s8_pixel, &data[offset], sizeof(Z24S8));
43 s8z24_pixel.s8.Assign(z24s8_pixel.s8);
44 s8z24_pixel.z24.Assign(z24s8_pixel.z24);
45 std::memcpy(&data[offset], &s8z24_pixel, sizeof(S8Z24));
46 } else {
47 std::memcpy(&s8z24_pixel, &data[offset], sizeof(S8Z24));
48 z24s8_pixel.s8.Assign(s8z24_pixel.s8);
49 z24s8_pixel.z24.Assign(s8z24_pixel.z24);
50 std::memcpy(&data[offset], &z24s8_pixel, sizeof(Z24S8));
51 }
52 }
53 }
54}
55
56static void ConvertS8Z24ToZ24S8(u8* data, u32 width, u32 height) {
57 SwapS8Z24ToZ24S8<false>(data, width, height);
58}
59
60static void ConvertZ24S8ToS8Z24(u8* data, u32 width, u32 height) {
61 SwapS8Z24ToZ24S8<true>(data, width, height);
62}
63
64void ConvertFromGuestToHost(u8* data, PixelFormat pixel_format, u32 width, u32 height, u32 depth,
65 bool convert_astc, bool convert_s8z24) {
66 if (convert_astc && IsPixelFormatASTC(pixel_format)) {
67 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
68 u32 block_width{};
69 u32 block_height{};
70 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format);
71 const std::vector<u8> rgba8_data =
72 Tegra::Texture::ASTC::Decompress(data, width, height, depth, block_width, block_height);
73 std::copy(rgba8_data.begin(), rgba8_data.end(), data);
74
75 } else if (convert_s8z24 && pixel_format == PixelFormat::S8Z24) {
76 Tegra::Texture::ConvertS8Z24ToZ24S8(data, width, height);
77 }
78}
79
80void ConvertFromHostToGuest(u8* data, PixelFormat pixel_format, u32 width, u32 height, u32 depth,
81 bool convert_astc, bool convert_s8z24) {
82 if (convert_astc && IsPixelFormatASTC(pixel_format)) {
83 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
84 static_cast<u32>(pixel_format));
85 UNREACHABLE();
86
87 } else if (convert_s8z24 && pixel_format == PixelFormat::S8Z24) {
88 Tegra::Texture::ConvertZ24S8ToS8Z24(data, width, height);
89 }
90}
91
92} // namespace Tegra::Texture \ No newline at end of file
diff --git a/src/video_core/textures/convert.h b/src/video_core/textures/convert.h
new file mode 100644
index 000000000..07cd8b5da
--- /dev/null
+++ b/src/video_core/textures/convert.h
@@ -0,0 +1,18 @@
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 "common/common_types.h"
8#include "video_core/surface.h"
9
10namespace Tegra::Texture {
11
12void ConvertFromGuestToHost(u8* data, VideoCore::Surface::PixelFormat pixel_format, u32 width,
13 u32 height, u32 depth, bool convert_astc, bool convert_s8z24);
14
15void ConvertFromHostToGuest(u8* data, VideoCore::Surface::PixelFormat pixel_format, u32 width,
16 u32 height, u32 depth, bool convert_astc, bool convert_s8z24);
17
18} // namespace Tegra::Texture \ No newline at end of file
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 5db75de22..cad7340f5 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -103,8 +103,8 @@ void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const
103 const u32 swizzle_offset{y_address + table[(xb / fast_swizzle_align) % 4]}; 103 const u32 swizzle_offset{y_address + table[(xb / fast_swizzle_align) % 4]};
104 const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel; 104 const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
105 const u32 pixel_index{out_x + pixel_base}; 105 const u32 pixel_index{out_x + pixel_base};
106 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 106 data_ptrs[unswizzle ? 1 : 0] = swizzled_data + swizzle_offset;
107 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 107 data_ptrs[unswizzle ? 0 : 1] = unswizzled_data + pixel_index;
108 std::memcpy(data_ptrs[0], data_ptrs[1], fast_swizzle_align); 108 std::memcpy(data_ptrs[0], data_ptrs[1], fast_swizzle_align);
109 } 109 }
110 pixel_base += stride_x; 110 pixel_base += stride_x;
@@ -154,7 +154,7 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool
154 for (u32 xb = 0; xb < blocks_on_x; xb++) { 154 for (u32 xb = 0; xb < blocks_on_x; xb++) {
155 const u32 x_start = xb * block_x_elements; 155 const u32 x_start = xb * block_x_elements;
156 const u32 x_end = std::min(width, x_start + block_x_elements); 156 const u32 x_end = std::min(width, x_start + block_x_elements);
157 if (fast) { 157 if constexpr (fast) {
158 FastProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start, 158 FastProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start,
159 z_start, x_end, y_end, z_end, tile_offset, xy_block_size, 159 z_start, x_end, y_end, z_end, tile_offset, xy_block_size,
160 layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel); 160 layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel);
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 85b7e9f7b..65df86890 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -16,16 +16,13 @@ inline std::size_t GetGOBSize() {
16 return 512; 16 return 512;
17} 17}
18 18
19/** 19/// Unswizzles a swizzled texture without changing its format.
20 * Unswizzles a swizzled texture without changing its format.
21 */
22void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y, 20void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
23 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 21 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
24 u32 block_height = TICEntry::DefaultBlockHeight, 22 u32 block_height = TICEntry::DefaultBlockHeight,
25 u32 block_depth = TICEntry::DefaultBlockHeight, u32 width_spacing = 0); 23 u32 block_depth = TICEntry::DefaultBlockHeight, u32 width_spacing = 0);
26/** 24
27 * Unswizzles a swizzled texture without changing its format. 25/// Unswizzles a swizzled texture without changing its format.
28 */
29std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, 26std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
30 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 27 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
31 u32 block_height = TICEntry::DefaultBlockHeight, 28 u32 block_height = TICEntry::DefaultBlockHeight,
@@ -37,15 +34,11 @@ void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
37 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, 34 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
38 bool unswizzle, u32 block_height, u32 block_depth, u32 width_spacing); 35 bool unswizzle, u32 block_height, u32 block_depth, u32 width_spacing);
39 36
40/** 37/// Decodes an unswizzled texture into a A8R8G8B8 texture.
41 * Decodes an unswizzled texture into a A8R8G8B8 texture.
42 */
43std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, 38std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
44 u32 height); 39 u32 height);
45 40
46/** 41/// This function calculates the correct size of a texture depending if it's tiled or not.
47 * This function calculates the correct size of a texture depending if it's tiled or not.
48 */
49std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 42std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
50 u32 block_height, u32 block_depth); 43 u32 block_height, u32 block_depth);
51 44
@@ -53,6 +46,7 @@ std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height
53void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 46void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
54 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 47 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
55 u32 block_height); 48 u32 block_height);
49
56/// Copies a tiled subrectangle into a linear surface. 50/// Copies a tiled subrectangle into a linear surface.
57void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, 51void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
58 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 52 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/web_browser.cpp
index 6a9138d53..979b9ec14 100644
--- a/src/yuzu/applets/web_browser.cpp
+++ b/src/yuzu/applets/web_browser.cpp
@@ -56,6 +56,8 @@ constexpr char NX_SHIM_INJECT_SCRIPT[] = R"(
56 window.nx.endApplet = function() { 56 window.nx.endApplet = function() {
57 applet_done = true; 57 applet_done = true;
58 }; 58 };
59
60 window.onkeypress = function(e) { if (e.keyCode === 13) { applet_done = true; } };
59)"; 61)";
60 62
61QString GetNXShimInjectionScript() { 63QString GetNXShimInjectionScript() {
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 73b04b749..3b070bfbb 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -20,10 +20,7 @@
20EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} 20EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
21 21
22void EmuThread::run() { 22void EmuThread::run() {
23 if (!Settings::values.use_multi_core) { 23 render_window->MakeCurrent();
24 // Single core mode must acquire OpenGL context for entire emulation session
25 render_window->MakeCurrent();
26 }
27 24
28 MicroProfileOnThreadCreate("EmuThread"); 25 MicroProfileOnThreadCreate("EmuThread");
29 26
@@ -38,6 +35,11 @@ void EmuThread::run() {
38 35
39 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); 36 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
40 37
38 if (Settings::values.use_asynchronous_gpu_emulation) {
39 // Release OpenGL context for the GPU thread
40 render_window->DoneCurrent();
41 }
42
41 // holds whether the cpu was running during the last iteration, 43 // holds whether the cpu was running during the last iteration,
42 // so that the DebugModeLeft signal can be emitted before the 44 // so that the DebugModeLeft signal can be emitted before the
43 // next execution step 45 // next execution step
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp
index c09a06520..c8b0a5ec0 100644
--- a/src/yuzu/compatdb.cpp
+++ b/src/yuzu/compatdb.cpp
@@ -53,8 +53,8 @@ void CompatDB::Submit() {
53 case CompatDBPage::Final: 53 case CompatDBPage::Final:
54 back(); 54 back();
55 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); 55 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
56 Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility", 56 Core::System::GetInstance().TelemetrySession().AddField(
57 compatibility->checkedId()); 57 Telemetry::FieldType::UserFeedback, "Compatibility", compatibility->checkedId());
58 58
59 button(NextButton)->setEnabled(false); 59 button(NextButton)->setEnabled(false);
60 button(NextButton)->setText(tr("Submitting")); 60 button(NextButton)->setText(tr("Submitting"));
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index e9546dadf..74dc6bb28 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -374,6 +374,8 @@ void Config::ReadValues() {
374 qt_config->value("use_disk_shader_cache", false).toBool(); 374 qt_config->value("use_disk_shader_cache", false).toBool();
375 Settings::values.use_accurate_gpu_emulation = 375 Settings::values.use_accurate_gpu_emulation =
376 qt_config->value("use_accurate_gpu_emulation", false).toBool(); 376 qt_config->value("use_accurate_gpu_emulation", false).toBool();
377 Settings::values.use_asynchronous_gpu_emulation =
378 qt_config->value("use_asynchronous_gpu_emulation", false).toBool();
377 379
378 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); 380 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
379 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); 381 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
@@ -633,6 +635,8 @@ void Config::SaveValues() {
633 qt_config->setValue("frame_limit", Settings::values.frame_limit); 635 qt_config->setValue("frame_limit", Settings::values.frame_limit);
634 qt_config->setValue("use_disk_shader_cache", Settings::values.use_disk_shader_cache); 636 qt_config->setValue("use_disk_shader_cache", Settings::values.use_disk_shader_cache);
635 qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation); 637 qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation);
638 qt_config->setValue("use_asynchronous_gpu_emulation",
639 Settings::values.use_asynchronous_gpu_emulation);
636 640
637 // Cast to double because Qt's written float values are not human-readable 641 // Cast to double because Qt's written float values are not human-readable
638 qt_config->setValue("bg_red", (double)Settings::values.bg_red); 642 qt_config->setValue("bg_red", (double)Settings::values.bg_red);
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 0f5dd534b..dd1d67488 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -75,6 +75,8 @@ void ConfigureGraphics::setConfiguration() {
75 ui->frame_limit->setValue(Settings::values.frame_limit); 75 ui->frame_limit->setValue(Settings::values.frame_limit);
76 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); 76 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
77 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); 77 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
78 ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn());
79 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
78 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 80 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
79 Settings::values.bg_blue)); 81 Settings::values.bg_blue));
80} 82}
@@ -86,6 +88,8 @@ void ConfigureGraphics::applyConfiguration() {
86 Settings::values.frame_limit = ui->frame_limit->value(); 88 Settings::values.frame_limit = ui->frame_limit->value();
87 Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); 89 Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
88 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); 90 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
91 Settings::values.use_asynchronous_gpu_emulation =
92 ui->use_asynchronous_gpu_emulation->isChecked();
89 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 93 Settings::values.bg_red = static_cast<float>(bg_color.redF());
90 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 94 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
91 Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); 95 Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 824f5810a..c6767e0ca 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -64,6 +64,13 @@
64 </widget> 64 </widget>
65 </item> 65 </item>
66 <item> 66 <item>
67 <widget class="QCheckBox" name="use_asynchronous_gpu_emulation">
68 <property name="text">
69 <string>Use asynchronous GPU emulation</string>
70 </property>
71 </widget>
72 </item>
73 <item>
67 <layout class="QHBoxLayout" name="horizontalLayout"> 74 <layout class="QHBoxLayout" name="horizontalLayout">
68 <item> 75 <item>
69 <widget class="QLabel" name="label"> 76 <widget class="QLabel" name="label">
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index f50225d5f..06ad74ffe 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -81,9 +81,8 @@ QString WaitTreeText::GetText() const {
81 return text; 81 return text;
82} 82}
83 83
84WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) { 84WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table)
85 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 85 : mutex_address(mutex_address) {
86
87 mutex_value = Memory::Read32(mutex_address); 86 mutex_value = Memory::Read32(mutex_address);
88 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); 87 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask);
89 owner = handle_table.Get<Kernel::Thread>(owner_handle); 88 owner = handle_table.Get<Kernel::Thread>(owner_handle);
@@ -316,7 +315,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
316 315
317 const VAddr mutex_wait_address = thread.GetMutexWaitAddress(); 316 const VAddr mutex_wait_address = thread.GetMutexWaitAddress();
318 if (mutex_wait_address != 0) { 317 if (mutex_wait_address != 0) {
319 list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address)); 318 const auto& handle_table = thread.GetOwnerProcess()->GetHandleTable();
319 list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address, handle_table));
320 } else { 320 } else {
321 list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); 321 list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
322 } 322 }
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index 365c3dbfe..62886609d 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -17,6 +17,7 @@
17class EmuThread; 17class EmuThread;
18 18
19namespace Kernel { 19namespace Kernel {
20class HandleTable;
20class ReadableEvent; 21class ReadableEvent;
21class WaitObject; 22class WaitObject;
22class Thread; 23class Thread;
@@ -72,7 +73,7 @@ public:
72class WaitTreeMutexInfo : public WaitTreeExpandableItem { 73class WaitTreeMutexInfo : public WaitTreeExpandableItem {
73 Q_OBJECT 74 Q_OBJECT
74public: 75public:
75 explicit WaitTreeMutexInfo(VAddr mutex_address); 76 explicit WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table);
76 ~WaitTreeMutexInfo() override; 77 ~WaitTreeMutexInfo() override;
77 78
78 QString GetText() const override; 79 QString GetText() const override;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 5ab7896d4..41ba3c4c6 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -11,6 +11,7 @@
11#include "applets/profile_select.h" 11#include "applets/profile_select.h"
12#include "applets/software_keyboard.h" 12#include "applets/software_keyboard.h"
13#include "applets/web_browser.h" 13#include "applets/web_browser.h"
14#include "configuration/configure_input.h"
14#include "configuration/configure_per_general.h" 15#include "configuration/configure_per_general.h"
15#include "core/file_sys/vfs.h" 16#include "core/file_sys/vfs.h"
16#include "core/file_sys/vfs_real.h" 17#include "core/file_sys/vfs_real.h"
@@ -339,6 +340,11 @@ void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view
339 .arg(QString::fromStdString(std::to_string(key_code)))); 340 .arg(QString::fromStdString(std::to_string(key_code))));
340 }; 341 };
341 342
343 QMessageBox::information(
344 this, tr("Exit"),
345 tr("To exit the web application, use the game provided controls to select exit, select the "
346 "'Exit Web Applet' option in the menu bar, or press the 'Enter' key."));
347
342 bool running_exit_check = false; 348 bool running_exit_check = false;
343 while (!finished) { 349 while (!finished) {
344 QApplication::processEvents(); 350 QApplication::processEvents();
@@ -522,6 +528,7 @@ void GMainWindow::InitializeHotkeys() {
522 Qt::ApplicationShortcut); 528 Qt::ApplicationShortcut);
523 hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot", 529 hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot",
524 QKeySequence(QKeySequence::Print)); 530 QKeySequence(QKeySequence::Print));
531 hotkey_registry.RegisterHotkey("Main Window", "Change Docked Mode", QKeySequence(Qt::Key_F10));
525 532
526 hotkey_registry.LoadHotkeys(); 533 hotkey_registry.LoadHotkeys();
527 534
@@ -591,6 +598,12 @@ void GMainWindow::InitializeHotkeys() {
591 OnCaptureScreenshot(); 598 OnCaptureScreenshot();
592 } 599 }
593 }); 600 });
601 connect(hotkey_registry.GetHotkey("Main Window", "Change Docked Mode", this),
602 &QShortcut::activated, this, [&] {
603 Settings::values.use_docked_mode = !Settings::values.use_docked_mode;
604 OnDockedModeChanged(!Settings::values.use_docked_mode,
605 Settings::values.use_docked_mode);
606 });
594} 607}
595 608
596void GMainWindow::SetDefaultUIGeometry() { 609void GMainWindow::SetDefaultUIGeometry() {
@@ -849,7 +862,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
849 } 862 }
850 game_path = filename; 863 game_path = filename;
851 864
852 Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "Qt"); 865 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "Qt");
853 return true; 866 return true;
854} 867}
855 868
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index ff05b3179..ca880dc65 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -354,6 +354,8 @@ void Config::ReadValues() {
354 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); 354 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false);
355 Settings::values.use_accurate_gpu_emulation = 355 Settings::values.use_accurate_gpu_emulation =
356 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); 356 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
357 Settings::values.use_asynchronous_gpu_emulation =
358 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false);
357 359
358 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); 360 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
359 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); 361 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index a81986f8e..6538af098 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -118,6 +118,10 @@ use_disk_shader_cache =
118# 0 (default): Off (fast), 1 : On (slow) 118# 0 (default): Off (fast), 1 : On (slow)
119use_accurate_gpu_emulation = 119use_accurate_gpu_emulation =
120 120
121# Whether to use asynchronous GPU emulation
122# 0 : Off (slow), 1 (default): On (fast)
123use_asynchronous_gpu_emulation =
124
121# The clear color for the renderer. What shows up on the sides of the bottom screen. 125# The clear color for the renderer. What shows up on the sides of the bottom screen.
122# Must be in range of 0.0-1.0. Defaults to 1.0 for all. 126# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
123bg_red = 127bg_red =
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c34b5467f..c6c66a787 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -216,7 +216,7 @@ int main(int argc, char** argv) {
216 } 216 }
217 } 217 }
218 218
219 Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); 219 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
220 220
221 system.Renderer().Rasterizer().LoadDiskResources(); 221 system.Renderer().Rasterizer().LoadDiskResources();
222 222