summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------externals/dynarmic0
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/color.h4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp13
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp13
-rw-r--r--src/core/core_timing.cpp12
-rw-r--r--src/core/core_timing_util.cpp1
-rw-r--r--src/core/core_timing_util.h1
-rw-r--r--src/core/cpu_manager.cpp29
-rw-r--r--src/core/crypto/key_manager.cpp153
-rw-r--r--src/core/crypto/key_manager.h4
-rw-r--r--src/core/file_sys/vfs_real.cpp80
-rw-r--r--src/core/settings.h6
-rw-r--r--src/video_core/engines/fermi_2d.cpp10
-rw-r--r--src/video_core/engines/fermi_2d.h9
-rw-r--r--src/video_core/engines/kepler_compute.cpp17
-rw-r--r--src/video_core/engines/kepler_compute.h16
-rw-r--r--src/video_core/engines/maxwell_3d.cpp35
-rw-r--r--src/video_core/engines/maxwell_3d.h21
-rw-r--r--src/video_core/fence_manager.h5
-rw-r--r--src/video_core/gpu.cpp31
-rw-r--r--src/video_core/gpu.h13
-rw-r--r--src/video_core/gpu_asynch.cpp10
-rw-r--r--src/video_core/gpu_asynch.h4
-rw-r--r--src/video_core/gpu_synch.cpp8
-rw-r--r--src/video_core/gpu_synch.h6
-rw-r--r--src/video_core/macro/macro_hle.cpp6
-rw-r--r--src/video_core/macro/macro_interpreter.cpp1
-rw-r--r--src/video_core/memory_manager.cpp12
-rw-r--r--src/video_core/memory_manager.h7
-rw-r--r--src/video_core/renderer_base.cpp4
-rw-r--r--src/video_core/renderer_base.h17
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.h1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp11
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h11
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp6
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h5
-rw-r--r--src/video_core/video_core.cpp32
-rw-r--r--src/web_service/CMakeLists.txt1
-rw-r--r--src/web_service/telemetry_json.cpp4
-rw-r--r--src/web_service/verify_login.cpp2
-rw-r--r--src/web_service/web_backend.cpp56
-rw-r--r--src/web_service/web_backend.h20
-rw-r--r--src/web_service/web_result.h (renamed from src/common/web_result.h)4
-rw-r--r--src/yuzu/configuration/config.cpp10
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp17
-rw-r--r--src/yuzu/configuration/configure_cpu.h1
-rw-r--r--src/yuzu/configuration/configure_cpu.ui52
52 files changed, 490 insertions, 326 deletions
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 82417da7803e2cf18efc28a1cd3f3d0a4b6045a Subproject 0e1112b7df77ae55a62a51622940d5c8f9e8c84
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 78c3bfb3b..5d54516eb 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -172,7 +172,6 @@ add_library(common STATIC
172 virtual_buffer.h 172 virtual_buffer.h
173 wall_clock.cpp 173 wall_clock.cpp
174 wall_clock.h 174 wall_clock.h
175 web_result.h
176 zstd_compression.cpp 175 zstd_compression.cpp
177 zstd_compression.h 176 zstd_compression.h
178) 177)
diff --git a/src/common/color.h b/src/common/color.h
index 381d6332e..bbcac858e 100644
--- a/src/common/color.h
+++ b/src/common/color.h
@@ -10,7 +10,7 @@
10#include "common/swap.h" 10#include "common/swap.h"
11#include "common/vector_math.h" 11#include "common/vector_math.h"
12 12
13namespace Color { 13namespace Common::Color {
14 14
15/// Convert a 1-bit color component to 8 bit 15/// Convert a 1-bit color component to 8 bit
16[[nodiscard]] constexpr u8 Convert1To8(u8 value) { 16[[nodiscard]] constexpr u8 Convert1To8(u8 value) {
@@ -268,4 +268,4 @@ inline void EncodeX24S8(u8 stencil, u8* bytes) {
268 bytes[3] = stencil; 268 bytes[3] = stencil;
269} 269}
270 270
271} // namespace Color 271} // namespace Common::Color
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 443ca72eb..b5f28a86e 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -143,7 +143,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
143 config.wall_clock_cntpct = uses_wall_clock; 143 config.wall_clock_cntpct = uses_wall_clock;
144 144
145 // Safe optimizations 145 // Safe optimizations
146 if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { 146 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
147 if (!Settings::values.cpuopt_page_tables) { 147 if (!Settings::values.cpuopt_page_tables) {
148 config.page_table = nullptr; 148 config.page_table = nullptr;
149 } 149 }
@@ -170,6 +170,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
170 } 170 }
171 } 171 }
172 172
173 // Unsafe optimizations
174 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
175 config.unsafe_optimizations = true;
176 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
177 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
178 }
179 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
180 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
181 }
182 }
183
173 return std::make_unique<Dynarmic::A32::Jit>(config); 184 return std::make_unique<Dynarmic::A32::Jit>(config);
174} 185}
175 186
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index a63a04a25..ce9968724 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -195,7 +195,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
195 config.wall_clock_cntpct = uses_wall_clock; 195 config.wall_clock_cntpct = uses_wall_clock;
196 196
197 // Safe optimizations 197 // Safe optimizations
198 if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { 198 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
199 if (!Settings::values.cpuopt_page_tables) { 199 if (!Settings::values.cpuopt_page_tables) {
200 config.page_table = nullptr; 200 config.page_table = nullptr;
201 } 201 }
@@ -222,6 +222,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
222 } 222 }
223 } 223 }
224 224
225 // Unsafe optimizations
226 if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) {
227 config.unsafe_optimizations = true;
228 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
229 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
230 }
231 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
232 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
233 }
234 }
235
225 return std::make_shared<Dynarmic::A64::Jit>(config); 236 return std::make_shared<Dynarmic::A64::Jit>(config);
226} 237}
227 238
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 71af26ec5..e6c8461a5 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -7,14 +7,14 @@
7#include <string> 7#include <string>
8#include <tuple> 8#include <tuple>
9 9
10#include "common/assert.h"
11#include "common/microprofile.h" 10#include "common/microprofile.h"
12#include "core/core_timing.h" 11#include "core/core_timing.h"
13#include "core/core_timing_util.h" 12#include "core/core_timing_util.h"
13#include "core/hardware_properties.h"
14 14
15namespace Core::Timing { 15namespace Core::Timing {
16 16
17constexpr u64 MAX_SLICE_LENGTH = 4000; 17constexpr s64 MAX_SLICE_LENGTH = 4000;
18 18
19std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { 19std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
20 return std::make_shared<EventType>(std::move(callback), std::move(name)); 20 return std::make_shared<EventType>(std::move(callback), std::move(name));
@@ -37,10 +37,8 @@ struct CoreTiming::Event {
37 } 37 }
38}; 38};
39 39
40CoreTiming::CoreTiming() { 40CoreTiming::CoreTiming()
41 clock = 41 : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
42 Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ);
43}
44 42
45CoreTiming::~CoreTiming() = default; 43CoreTiming::~CoreTiming() = default;
46 44
@@ -136,7 +134,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
136 134
137void CoreTiming::AddTicks(u64 ticks) { 135void CoreTiming::AddTicks(u64 ticks) {
138 this->ticks += ticks; 136 this->ticks += ticks;
139 downcount -= ticks; 137 downcount -= static_cast<s64>(ticks);
140} 138}
141 139
142void CoreTiming::Idle() { 140void CoreTiming::Idle() {
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
index aefc63663..8ce8e602e 100644
--- a/src/core/core_timing_util.cpp
+++ b/src/core/core_timing_util.cpp
@@ -8,6 +8,7 @@
8#include <limits> 8#include <limits>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/uint128.h" 10#include "common/uint128.h"
11#include "core/hardware_properties.h"
11 12
12namespace Core::Timing { 13namespace Core::Timing {
13 14
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index 2ed979e14..e4a046bf9 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -6,7 +6,6 @@
6 6
7#include <chrono> 7#include <chrono>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hardware_properties.h"
10 9
11namespace Core::Timing { 10namespace Core::Timing {
12 11
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 358943429..ef0bae556 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -41,9 +41,9 @@ void CpuManager::Shutdown() {
41 running_mode = false; 41 running_mode = false;
42 Pause(false); 42 Pause(false);
43 if (is_multicore) { 43 if (is_multicore) {
44 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 44 for (auto& data : core_data) {
45 core_data[core].host_thread->join(); 45 data.host_thread->join();
46 core_data[core].host_thread.reset(); 46 data.host_thread.reset();
47 } 47 }
48 } else { 48 } else {
49 core_data[0].host_thread->join(); 49 core_data[0].host_thread->join();
@@ -166,25 +166,23 @@ void CpuManager::MultiCorePause(bool paused) {
166 bool all_not_barrier = false; 166 bool all_not_barrier = false;
167 while (!all_not_barrier) { 167 while (!all_not_barrier) {
168 all_not_barrier = true; 168 all_not_barrier = true;
169 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 169 for (const auto& data : core_data) {
170 all_not_barrier &= 170 all_not_barrier &= !data.is_running.load() && data.initialized.load();
171 !core_data[core].is_running.load() && core_data[core].initialized.load();
172 } 171 }
173 } 172 }
174 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 173 for (auto& data : core_data) {
175 core_data[core].enter_barrier->Set(); 174 data.enter_barrier->Set();
176 } 175 }
177 if (paused_state.load()) { 176 if (paused_state.load()) {
178 bool all_barrier = false; 177 bool all_barrier = false;
179 while (!all_barrier) { 178 while (!all_barrier) {
180 all_barrier = true; 179 all_barrier = true;
181 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 180 for (const auto& data : core_data) {
182 all_barrier &= 181 all_barrier &= data.is_paused.load() && data.initialized.load();
183 core_data[core].is_paused.load() && core_data[core].initialized.load();
184 } 182 }
185 } 183 }
186 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 184 for (auto& data : core_data) {
187 core_data[core].exit_barrier->Set(); 185 data.exit_barrier->Set();
188 } 186 }
189 } 187 }
190 } else { 188 } else {
@@ -192,9 +190,8 @@ void CpuManager::MultiCorePause(bool paused) {
192 bool all_barrier = false; 190 bool all_barrier = false;
193 while (!all_barrier) { 191 while (!all_barrier) {
194 all_barrier = true; 192 all_barrier = true;
195 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 193 for (const auto& data : core_data) {
196 all_barrier &= 194 all_barrier &= data.is_paused.load() && data.initialized.load();
197 core_data[core].is_paused.load() && core_data[core].initialized.load();
198 } 195 }
199 } 196 }
200 /// Don't release the barrier 197 /// Don't release the barrier
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 8783d1ac2..dc591c730 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -36,6 +36,7 @@
36#include "core/settings.h" 36#include "core/settings.h"
37 37
38namespace Core::Crypto { 38namespace Core::Crypto {
39namespace {
39 40
40constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; 41constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
41constexpr u64 FULL_TICKET_SIZE = 0x400; 42constexpr u64 FULL_TICKET_SIZE = 0x400;
@@ -49,7 +50,72 @@ constexpr std::array eticket_source_hashes{
49}; 50};
50// clang-format on 51// clang-format on
51 52
52const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ 53constexpr std::array<std::pair<std::string_view, KeyIndex<S128KeyType>>, 30> s128_file_id{{
54 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
55 {"eticket_rsa_kek_source",
56 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
57 {"eticket_rsa_kekek_source",
58 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
59 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
60 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
61 {"rsa_oaep_kek_generation_source",
62 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
63 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
64 {"aes_kek_generation_source",
65 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
66 {"aes_key_generation_source",
67 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
68 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
69 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
70 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
71 {"key_area_key_application_source",
72 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
73 static_cast<u64>(KeyAreaKeyType::Application)}},
74 {"key_area_key_ocean_source",
75 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
76 static_cast<u64>(KeyAreaKeyType::Ocean)}},
77 {"key_area_key_system_source",
78 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
79 static_cast<u64>(KeyAreaKeyType::System)}},
80 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
81 {"keyblob_mac_key_source",
82 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
83 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
84 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
85 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
86 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
87 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
88 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
89 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
90 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
91 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
92 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
93 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
94 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
95 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
96}};
97
98auto Find128ByName(std::string_view name) {
99 return std::find_if(s128_file_id.begin(), s128_file_id.end(),
100 [&name](const auto& pair) { return pair.first == name; });
101}
102
103constexpr std::array<std::pair<std::string_view, KeyIndex<S256KeyType>>, 6> s256_file_id{{
104 {"header_key", {S256KeyType::Header, 0, 0}},
105 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
106 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
107 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
108 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
109 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
110}};
111
112auto Find256ByName(std::string_view name) {
113 return std::find_if(s256_file_id.begin(), s256_file_id.end(),
114 [&name](const auto& pair) { return pair.first == name; });
115}
116
117using KeyArray = std::array<std::pair<std::pair<S128KeyType, u64>, std::string_view>, 7>;
118constexpr KeyArray KEYS_VARIABLE_LENGTH{{
53 {{S128KeyType::Master, 0}, "master_key_"}, 119 {{S128KeyType::Master, 0}, "master_key_"},
54 {{S128KeyType::Package1, 0}, "package1_key_"}, 120 {{S128KeyType::Package1, 0}, "package1_key_"},
55 {{S128KeyType::Package2, 0}, "package2_key_"}, 121 {{S128KeyType::Package2, 0}, "package2_key_"},
@@ -57,14 +123,13 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
57 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"}, 123 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
58 {{S128KeyType::Keyblob, 0}, "keyblob_key_"}, 124 {{S128KeyType::Keyblob, 0}, "keyblob_key_"},
59 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, 125 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
60}; 126}};
61 127
62namespace {
63template <std::size_t Size> 128template <std::size_t Size>
64bool IsAllZeroArray(const std::array<u8, Size>& array) { 129bool IsAllZeroArray(const std::array<u8, Size>& array) {
65 return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; }); 130 return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; });
66} 131}
67} // namespace 132} // Anonymous namespace
68 133
69u64 GetSignatureTypeDataSize(SignatureType type) { 134u64 GetSignatureTypeDataSize(SignatureType type) {
70 switch (type) { 135 switch (type) {
@@ -564,13 +629,13 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
564 s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key; 629 s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
565 } else { 630 } else {
566 out[0] = Common::ToLower(out[0]); 631 out[0] = Common::ToLower(out[0]);
567 if (s128_file_id.find(out[0]) != s128_file_id.end()) { 632 if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
568 const auto index = s128_file_id.at(out[0]); 633 const auto& index = iter128->second;
569 Key128 key = Common::HexStringToArray<16>(out[1]); 634 const Key128 key = Common::HexStringToArray<16>(out[1]);
570 s128_keys[{index.type, index.field1, index.field2}] = key; 635 s128_keys[{index.type, index.field1, index.field2}] = key;
571 } else if (s256_file_id.find(out[0]) != s256_file_id.end()) { 636 } else if (const auto iter256 = Find256ByName(out[0]); iter256 != s256_file_id.end()) {
572 const auto index = s256_file_id.at(out[0]); 637 const auto& index = iter256->second;
573 Key256 key = Common::HexStringToArray<32>(out[1]); 638 const Key256 key = Common::HexStringToArray<32>(out[1]);
574 s256_keys[{index.type, index.field1, index.field2}] = key; 639 s256_keys[{index.type, index.field1, index.field2}] = key;
575 } else if (out[0].compare(0, 8, "keyblob_") == 0 && 640 } else if (out[0].compare(0, 8, "keyblob_") == 0 &&
576 out[0].compare(0, 9, "keyblob_k") != 0) { 641 out[0].compare(0, 9, "keyblob_k") != 0) {
@@ -742,8 +807,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
742 } 807 }
743 808
744 const auto iter2 = std::find_if( 809 const auto iter2 = std::find_if(
745 s128_file_id.begin(), s128_file_id.end(), 810 s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const auto& elem) {
746 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
747 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == 811 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
748 std::tie(id, field1, field2); 812 std::tie(id, field1, field2);
749 }); 813 });
@@ -753,9 +817,11 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
753 817
754 // Variable cases 818 // Variable cases
755 if (id == S128KeyType::KeyArea) { 819 if (id == S128KeyType::KeyArea) {
756 static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}", 820 static constexpr std::array<const char*, 3> kak_names = {
757 "key_area_key_ocean_{:02X}", 821 "key_area_key_application_{:02X}",
758 "key_area_key_system_{:02X}"}; 822 "key_area_key_ocean_{:02X}",
823 "key_area_key_system_{:02X}",
824 };
759 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key); 825 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
760 } else if (id == S128KeyType::Master) { 826 } else if (id == S128KeyType::Master) {
761 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key); 827 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
@@ -781,8 +847,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
781 return; 847 return;
782 } 848 }
783 const auto iter = std::find_if( 849 const auto iter = std::find_if(
784 s256_file_id.begin(), s256_file_id.end(), 850 s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const auto& elem) {
785 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
786 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == 851 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
787 std::tie(id, field1, field2); 852 std::tie(id, field1, field2);
788 }); 853 });
@@ -1245,58 +1310,4 @@ bool KeyManager::AddTicketPersonalized(Ticket raw) {
1245 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); 1310 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
1246 return true; 1311 return true;
1247} 1312}
1248
1249const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
1250 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
1251 {"eticket_rsa_kek_source",
1252 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
1253 {"eticket_rsa_kekek_source",
1254 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
1255 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
1256 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
1257 {"rsa_oaep_kek_generation_source",
1258 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
1259 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
1260 {"aes_kek_generation_source",
1261 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
1262 {"aes_key_generation_source",
1263 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
1264 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
1265 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
1266 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
1267 {"key_area_key_application_source",
1268 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1269 static_cast<u64>(KeyAreaKeyType::Application)}},
1270 {"key_area_key_ocean_source",
1271 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1272 static_cast<u64>(KeyAreaKeyType::Ocean)}},
1273 {"key_area_key_system_source",
1274 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1275 static_cast<u64>(KeyAreaKeyType::System)}},
1276 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
1277 {"keyblob_mac_key_source",
1278 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
1279 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
1280 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
1281 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
1282 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
1283 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
1284 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
1285 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
1286 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
1287 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
1288 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
1289 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
1290 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
1291 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
1292};
1293
1294const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
1295 {"header_key", {S256KeyType::Header, 0, 0}},
1296 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
1297 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
1298 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
1299 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
1300 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
1301};
1302} // namespace Core::Crypto 1313} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index bdca3770a..321b75323 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -10,7 +10,6 @@
10#include <string> 10#include <string>
11 11
12#include <variant> 12#include <variant>
13#include <boost/container/flat_map.hpp>
14#include <fmt/format.h> 13#include <fmt/format.h>
15#include "common/common_funcs.h" 14#include "common/common_funcs.h"
16#include "common/common_types.h" 15#include "common/common_types.h"
@@ -293,9 +292,6 @@ private:
293 292
294 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); 293 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
295 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); 294 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
296
297 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
298 static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
299}; 295};
300 296
301Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); 297Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 1dbf632c1..488687ba9 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -72,8 +72,10 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
72 72
73VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { 73VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
74 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 74 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
75 if (cache.find(path) != cache.end()) { 75
76 auto weak = cache[path]; 76 if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) {
77 const auto& weak = weak_iter->second;
78
77 if (!weak.expired()) { 79 if (!weak.expired()) {
78 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); 80 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
79 } 81 }
@@ -84,7 +86,7 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
84 } 86 }
85 87
86 auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str()); 88 auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str());
87 cache[path] = backing; 89 cache.insert_or_assign(path, backing);
88 90
89 // Cannot use make_shared as RealVfsFile constructor is private 91 // Cannot use make_shared as RealVfsFile constructor is private
90 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); 92 return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
@@ -116,11 +118,12 @@ VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_
116VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { 118VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
117 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); 119 const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
118 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); 120 const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
121 const auto cached_file_iter = cache.find(old_path);
119 122
120 if (cache.find(old_path) != cache.end()) { 123 if (cached_file_iter != cache.cend()) {
121 auto file = cache[old_path].lock(); 124 auto file = cached_file_iter->second.lock();
122 125
123 if (!cache[old_path].expired()) { 126 if (!cached_file_iter->second.expired()) {
124 file->Close(); 127 file->Close();
125 } 128 }
126 129
@@ -131,7 +134,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
131 134
132 cache.erase(old_path); 135 cache.erase(old_path);
133 file->Open(new_path, "r+b"); 136 file->Open(new_path, "r+b");
134 cache[new_path] = file; 137 cache.insert_or_assign(new_path, std::move(file));
135 } else { 138 } else {
136 UNREACHABLE(); 139 UNREACHABLE();
137 return nullptr; 140 return nullptr;
@@ -142,12 +145,15 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
142 145
143bool RealVfsFilesystem::DeleteFile(std::string_view path_) { 146bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
144 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 147 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
145 if (cache.find(path) != cache.end()) { 148 const auto cached_iter = cache.find(path);
146 if (!cache[path].expired()) { 149
147 cache[path].lock()->Close(); 150 if (cached_iter != cache.cend()) {
151 if (!cached_iter->second.expired()) {
152 cached_iter->second.lock()->Close();
148 } 153 }
149 cache.erase(path); 154 cache.erase(path);
150 } 155 }
156
151 return FS::Delete(path); 157 return FS::Delete(path);
152} 158}
153 159
@@ -192,21 +198,25 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
192 } 198 }
193 199
194 for (auto& kv : cache) { 200 for (auto& kv : cache) {
195 // Path in cache starts with old_path 201 // If the path in the cache doesn't start with old_path, then bail on this file.
196 if (kv.first.rfind(old_path, 0) == 0) { 202 if (kv.first.rfind(old_path, 0) != 0) {
197 const auto file_old_path = 203 continue;
198 FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault); 204 }
199 const auto file_new_path = 205
200 FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), 206 const auto file_old_path =
201 FS::DirectorySeparator::PlatformDefault); 207 FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault);
202 auto cached = cache[file_old_path]; 208 auto file_new_path = FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
203 if (!cached.expired()) { 209 FS::DirectorySeparator::PlatformDefault);
204 auto file = cached.lock(); 210 const auto& cached = cache[file_old_path];
205 file->Open(file_new_path, "r+b"); 211
206 cache.erase(file_old_path); 212 if (cached.expired()) {
207 cache[file_new_path] = file; 213 continue;
208 }
209 } 214 }
215
216 auto file = cached.lock();
217 file->Open(file_new_path, "r+b");
218 cache.erase(file_old_path);
219 cache.insert_or_assign(std::move(file_new_path), std::move(file));
210 } 220 }
211 221
212 return OpenDirectory(new_path, Mode::ReadWrite); 222 return OpenDirectory(new_path, Mode::ReadWrite);
@@ -214,15 +224,21 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
214 224
215bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { 225bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
216 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 226 const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
227
217 for (auto& kv : cache) { 228 for (auto& kv : cache) {
218 // Path in cache starts with old_path 229 // If the path in the cache doesn't start with path, then bail on this file.
219 if (kv.first.rfind(path, 0) == 0) { 230 if (kv.first.rfind(path, 0) != 0) {
220 if (!cache[kv.first].expired()) { 231 continue;
221 cache[kv.first].lock()->Close();
222 }
223 cache.erase(kv.first);
224 } 232 }
233
234 const auto& entry = cache[kv.first];
235 if (!entry.expired()) {
236 entry.lock()->Close();
237 }
238
239 cache.erase(kv.first);
225 } 240 }
241
226 return FS::DeleteDirRecursively(path); 242 return FS::DeleteDirRecursively(path);
227} 243}
228 244
@@ -260,14 +276,14 @@ bool RealVfsFile::IsReadable() const {
260} 276}
261 277
262std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { 278std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
263 if (!backing->Seek(offset, SEEK_SET)) { 279 if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
264 return 0; 280 return 0;
265 } 281 }
266 return backing->ReadBytes(data, length); 282 return backing->ReadBytes(data, length);
267} 283}
268 284
269std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { 285std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
270 if (!backing->Seek(offset, SEEK_SET)) { 286 if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) {
271 return 0; 287 return 0;
272 } 288 }
273 return backing->WriteBytes(data, length); 289 return backing->WriteBytes(data, length);
diff --git a/src/core/settings.h b/src/core/settings.h
index bb145f193..3681b5e9d 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -359,7 +359,8 @@ enum class GPUAccuracy : u32 {
359 359
360enum class CPUAccuracy { 360enum class CPUAccuracy {
361 Accurate = 0, 361 Accurate = 0,
362 DebugMode = 1, 362 Unsafe = 1,
363 DebugMode = 2,
363}; 364};
364 365
365extern bool configuring_global; 366extern bool configuring_global;
@@ -419,6 +420,9 @@ struct Values {
419 bool cpuopt_misc_ir; 420 bool cpuopt_misc_ir;
420 bool cpuopt_reduce_misalign_checks; 421 bool cpuopt_reduce_misalign_checks;
421 422
423 bool cpuopt_unsafe_unfuse_fma;
424 bool cpuopt_unsafe_reduce_fp_error;
425
422 // Renderer 426 // Renderer
423 Setting<RendererBackend> renderer_backend; 427 Setting<RendererBackend> renderer_backend;
424 bool renderer_debug; 428 bool renderer_debug;
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index ff10ff40d..6e50661a3 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -10,7 +10,13 @@
10 10
11namespace Tegra::Engines { 11namespace Tegra::Engines {
12 12
13Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {} 13Fermi2D::Fermi2D() = default;
14
15Fermi2D::~Fermi2D() = default;
16
17void Fermi2D::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
18 rasterizer = &rasterizer_;
19}
14 20
15void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 21void Fermi2D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
16 ASSERT_MSG(method < Regs::NUM_REGS, 22 ASSERT_MSG(method < Regs::NUM_REGS,
@@ -87,7 +93,7 @@ void Fermi2D::HandleSurfaceCopy() {
87 copy_config.src_rect = src_rect; 93 copy_config.src_rect = src_rect;
88 copy_config.dst_rect = dst_rect; 94 copy_config.dst_rect = dst_rect;
89 95
90 if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) { 96 if (!rasterizer->AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) {
91 UNIMPLEMENTED(); 97 UNIMPLEMENTED();
92 } 98 }
93} 99}
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 8f37d053f..213abfaae 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -34,8 +34,11 @@ namespace Tegra::Engines {
34 34
35class Fermi2D final : public EngineInterface { 35class Fermi2D final : public EngineInterface {
36public: 36public:
37 explicit Fermi2D(VideoCore::RasterizerInterface& rasterizer); 37 explicit Fermi2D();
38 ~Fermi2D() = default; 38 ~Fermi2D();
39
40 /// Binds a rasterizer to this engine.
41 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
39 42
40 /// Write the value to the register identified by method. 43 /// Write the value to the register identified by method.
41 void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; 44 void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
@@ -149,7 +152,7 @@ public:
149 }; 152 };
150 153
151private: 154private:
152 VideoCore::RasterizerInterface& rasterizer; 155 VideoCore::RasterizerInterface* rasterizer;
153 156
154 /// Performs the copy from the source surface to the destination surface as configured in the 157 /// Performs the copy from the source surface to the destination surface as configured in the
155 /// registers. 158 /// registers.
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index a82b06a38..898370739 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -16,14 +16,15 @@
16 16
17namespace Tegra::Engines { 17namespace Tegra::Engines {
18 18
19KeplerCompute::KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 19KeplerCompute::KeplerCompute(Core::System& system_, MemoryManager& memory_manager_)
20 MemoryManager& memory_manager) 20 : system{system_}, memory_manager{memory_manager_}, upload_state{memory_manager, regs.upload} {}
21 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, upload_state{
22 memory_manager,
23 regs.upload} {}
24 21
25KeplerCompute::~KeplerCompute() = default; 22KeplerCompute::~KeplerCompute() = default;
26 23
24void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
25 rasterizer = &rasterizer_;
26}
27
27void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 28void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
28 ASSERT_MSG(method < Regs::NUM_REGS, 29 ASSERT_MSG(method < Regs::NUM_REGS,
29 "Invalid KeplerCompute register, increase the size of the Regs structure"); 30 "Invalid KeplerCompute register, increase the size of the Regs structure");
@@ -104,11 +105,11 @@ SamplerDescriptor KeplerCompute::AccessSampler(u32 handle) const {
104} 105}
105 106
106VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() { 107VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() {
107 return rasterizer.AccessGuestDriverProfile(); 108 return rasterizer->AccessGuestDriverProfile();
108} 109}
109 110
110const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const { 111const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const {
111 return rasterizer.AccessGuestDriverProfile(); 112 return rasterizer->AccessGuestDriverProfile();
112} 113}
113 114
114void KeplerCompute::ProcessLaunch() { 115void KeplerCompute::ProcessLaunch() {
@@ -119,7 +120,7 @@ void KeplerCompute::ProcessLaunch() {
119 const GPUVAddr code_addr = regs.code_loc.Address() + launch_description.program_start; 120 const GPUVAddr code_addr = regs.code_loc.Address() + launch_description.program_start;
120 LOG_TRACE(HW_GPU, "Compute invocation launched at address 0x{:016x}", code_addr); 121 LOG_TRACE(HW_GPU, "Compute invocation launched at address 0x{:016x}", code_addr);
121 122
122 rasterizer.DispatchCompute(code_addr); 123 rasterizer->DispatchCompute(code_addr);
123} 124}
124 125
125Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const { 126Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index b7f668d88..7f2500aab 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -42,10 +42,12 @@ namespace Tegra::Engines {
42 42
43class KeplerCompute final : public ConstBufferEngineInterface, public EngineInterface { 43class KeplerCompute final : public ConstBufferEngineInterface, public EngineInterface {
44public: 44public:
45 explicit KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 45 explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager);
46 MemoryManager& memory_manager);
47 ~KeplerCompute(); 46 ~KeplerCompute();
48 47
48 /// Binds a rasterizer to this engine.
49 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
50
49 static constexpr std::size_t NumConstBuffers = 8; 51 static constexpr std::size_t NumConstBuffers = 8;
50 52
51 struct Regs { 53 struct Regs {
@@ -230,11 +232,6 @@ public:
230 const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; 232 const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override;
231 233
232private: 234private:
233 Core::System& system;
234 VideoCore::RasterizerInterface& rasterizer;
235 MemoryManager& memory_manager;
236 Upload::State upload_state;
237
238 void ProcessLaunch(); 235 void ProcessLaunch();
239 236
240 /// Retrieves information about a specific TIC entry from the TIC buffer. 237 /// Retrieves information about a specific TIC entry from the TIC buffer.
@@ -242,6 +239,11 @@ private:
242 239
243 /// Retrieves information about a specific TSC entry from the TSC buffer. 240 /// Retrieves information about a specific TSC entry from the TSC buffer.
244 Texture::TSCEntry GetTSCEntry(u32 tsc_index) const; 241 Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
242
243 Core::System& system;
244 MemoryManager& memory_manager;
245 VideoCore::RasterizerInterface* rasterizer = nullptr;
246 Upload::State upload_state;
245}; 247};
246 248
247#define ASSERT_REG_POSITION(field_name, position) \ 249#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index c01436295..33854445f 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -22,14 +22,19 @@ using VideoCore::QueryType;
22/// First register id that is actually a Macro call. 22/// First register id that is actually a Macro call.
23constexpr u32 MacroRegistersStart = 0xE00; 23constexpr u32 MacroRegistersStart = 0xE00;
24 24
25Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 25Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
26 MemoryManager& memory_manager) 26 : system{system_}, memory_manager{memory_manager_}, macro_engine{GetMacroEngine(*this)},
27 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, 27 upload_state{memory_manager, regs.upload} {
28 macro_engine{GetMacroEngine(*this)}, upload_state{memory_manager, regs.upload} {
29 dirty.flags.flip(); 28 dirty.flags.flip();
30 InitializeRegisterDefaults(); 29 InitializeRegisterDefaults();
31} 30}
32 31
32Maxwell3D::~Maxwell3D() = default;
33
34void Maxwell3D::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
35 rasterizer = &rasterizer_;
36}
37
33void Maxwell3D::InitializeRegisterDefaults() { 38void Maxwell3D::InitializeRegisterDefaults() {
34 // Initializes registers to their default values - what games expect them to be at boot. This is 39 // Initializes registers to their default values - what games expect them to be at boot. This is
35 // for certain registers that may not be explicitly set by games. 40 // for certain registers that may not be explicitly set by games.
@@ -192,7 +197,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
192 197
193 switch (method) { 198 switch (method) {
194 case MAXWELL3D_REG_INDEX(wait_for_idle): { 199 case MAXWELL3D_REG_INDEX(wait_for_idle): {
195 rasterizer.WaitForIdle(); 200 rasterizer->WaitForIdle();
196 break; 201 break;
197 } 202 }
198 case MAXWELL3D_REG_INDEX(shadow_ram_control): { 203 case MAXWELL3D_REG_INDEX(shadow_ram_control): {
@@ -402,7 +407,7 @@ void Maxwell3D::FlushMMEInlineDraw() {
402 407
403 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; 408 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
404 if (ShouldExecute()) { 409 if (ShouldExecute()) {
405 rasterizer.Draw(is_indexed, true); 410 rasterizer->Draw(is_indexed, true);
406 } 411 }
407 412
408 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if 413 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
@@ -465,7 +470,7 @@ void Maxwell3D::ProcessQueryGet() {
465 switch (regs.query.query_get.operation) { 470 switch (regs.query.query_get.operation) {
466 case Regs::QueryOperation::Release: 471 case Regs::QueryOperation::Release:
467 if (regs.query.query_get.fence == 1) { 472 if (regs.query.query_get.fence == 1) {
468 rasterizer.SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence); 473 rasterizer->SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence);
469 } else { 474 } else {
470 StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0); 475 StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0);
471 } 476 }
@@ -533,7 +538,7 @@ void Maxwell3D::ProcessQueryCondition() {
533void Maxwell3D::ProcessCounterReset() { 538void Maxwell3D::ProcessCounterReset() {
534 switch (regs.counter_reset) { 539 switch (regs.counter_reset) {
535 case Regs::CounterReset::SampleCnt: 540 case Regs::CounterReset::SampleCnt:
536 rasterizer.ResetCounter(QueryType::SamplesPassed); 541 rasterizer->ResetCounter(QueryType::SamplesPassed);
537 break; 542 break;
538 default: 543 default:
539 LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", 544 LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}",
@@ -547,7 +552,7 @@ void Maxwell3D::ProcessSyncPoint() {
547 const u32 increment = regs.sync_info.increment.Value(); 552 const u32 increment = regs.sync_info.increment.Value();
548 [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value(); 553 [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value();
549 if (increment) { 554 if (increment) {
550 rasterizer.SignalSyncPoint(sync_point); 555 rasterizer->SignalSyncPoint(sync_point);
551 } 556 }
552} 557}
553 558
@@ -570,7 +575,7 @@ void Maxwell3D::DrawArrays() {
570 575
571 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; 576 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
572 if (ShouldExecute()) { 577 if (ShouldExecute()) {
573 rasterizer.Draw(is_indexed, false); 578 rasterizer->Draw(is_indexed, false);
574 } 579 }
575 580
576 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if 581 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
@@ -590,8 +595,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
590 return 0; 595 return 0;
591 case Regs::QuerySelect::SamplesPassed: 596 case Regs::QuerySelect::SamplesPassed:
592 // Deferred. 597 // Deferred.
593 rasterizer.Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed, 598 rasterizer->Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed,
594 system.GPU().GetTicks()); 599 system.GPU().GetTicks());
595 return {}; 600 return {};
596 default: 601 default:
597 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}", 602 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}",
@@ -718,7 +723,7 @@ void Maxwell3D::ProcessClearBuffers() {
718 regs.clear_buffers.R == regs.clear_buffers.B && 723 regs.clear_buffers.R == regs.clear_buffers.B &&
719 regs.clear_buffers.R == regs.clear_buffers.A); 724 regs.clear_buffers.R == regs.clear_buffers.A);
720 725
721 rasterizer.Clear(); 726 rasterizer->Clear();
722} 727}
723 728
724u32 Maxwell3D::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const { 729u32 Maxwell3D::AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const {
@@ -752,11 +757,11 @@ SamplerDescriptor Maxwell3D::AccessSampler(u32 handle) const {
752} 757}
753 758
754VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() { 759VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() {
755 return rasterizer.AccessGuestDriverProfile(); 760 return rasterizer->AccessGuestDriverProfile();
756} 761}
757 762
758const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const { 763const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const {
759 return rasterizer.AccessGuestDriverProfile(); 764 return rasterizer->AccessGuestDriverProfile();
760} 765}
761 766
762} // namespace Tegra::Engines 767} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index c97eeb792..bc289c55d 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -51,9 +51,11 @@ namespace Tegra::Engines {
51 51
52class Maxwell3D final : public ConstBufferEngineInterface, public EngineInterface { 52class Maxwell3D final : public ConstBufferEngineInterface, public EngineInterface {
53public: 53public:
54 explicit Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 54 explicit Maxwell3D(Core::System& system, MemoryManager& memory_manager);
55 MemoryManager& memory_manager); 55 ~Maxwell3D();
56 ~Maxwell3D() = default; 56
57 /// Binds a rasterizer to this engine.
58 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
57 59
58 /// Register structure of the Maxwell3D engine. 60 /// Register structure of the Maxwell3D engine.
59 /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. 61 /// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
@@ -1418,12 +1420,12 @@ public:
1418 return execute_on; 1420 return execute_on;
1419 } 1421 }
1420 1422
1421 VideoCore::RasterizerInterface& GetRasterizer() { 1423 VideoCore::RasterizerInterface& Rasterizer() {
1422 return rasterizer; 1424 return *rasterizer;
1423 } 1425 }
1424 1426
1425 const VideoCore::RasterizerInterface& GetRasterizer() const { 1427 const VideoCore::RasterizerInterface& Rasterizer() const {
1426 return rasterizer; 1428 return *rasterizer;
1427 } 1429 }
1428 1430
1429 /// Notify a memory write has happened. 1431 /// Notify a memory write has happened.
@@ -1460,11 +1462,10 @@ private:
1460 void InitializeRegisterDefaults(); 1462 void InitializeRegisterDefaults();
1461 1463
1462 Core::System& system; 1464 Core::System& system;
1463
1464 VideoCore::RasterizerInterface& rasterizer;
1465
1466 MemoryManager& memory_manager; 1465 MemoryManager& memory_manager;
1467 1466
1467 VideoCore::RasterizerInterface* rasterizer = nullptr;
1468
1468 /// Start offsets of each macro in macro_memory 1469 /// Start offsets of each macro in macro_memory
1469 std::array<u32, 0x80> macro_positions = {}; 1470 std::array<u32, 0x80> macro_positions = {};
1470 1471
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index 8b2a6a42c..06cc12d5a 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -5,15 +5,10 @@
5#pragma once 5#pragma once
6 6
7#include <algorithm> 7#include <algorithm>
8#include <array>
9#include <memory>
10#include <queue> 8#include <queue>
11 9
12#include "common/assert.h"
13#include "common/common_types.h" 10#include "common/common_types.h"
14#include "core/core.h" 11#include "core/core.h"
15#include "core/memory.h"
16#include "core/settings.h"
17#include "video_core/gpu.h" 12#include "video_core/gpu.h"
18#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
19#include "video_core/rasterizer_interface.h" 14#include "video_core/rasterizer_interface.h"
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 512578c8b..acb6e6d46 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -27,21 +27,28 @@ namespace Tegra {
27 27
28MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); 28MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
29 29
30GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async) 30GPU::GPU(Core::System& system_, bool is_async_)
31 : system{system}, renderer{std::move(renderer_)}, is_async{is_async} { 31 : system{system_}, dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)},
32 auto& rasterizer{renderer->Rasterizer()}; 32 memory_manager{std::make_unique<Tegra::MemoryManager>(system)},
33 memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); 33 maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)},
34 dma_pusher = std::make_unique<Tegra::DmaPusher>(system, *this); 34 fermi_2d{std::make_unique<Engines::Fermi2D>()},
35 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); 35 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
36 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer); 36 maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)},
37 kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager); 37 kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)},
38 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager); 38 shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_} {}
39 kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);
40 shader_notify = std::make_unique<VideoCore::ShaderNotify>();
41}
42 39
43GPU::~GPU() = default; 40GPU::~GPU() = default;
44 41
42void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) {
43 renderer = std::move(renderer_);
44
45 VideoCore::RasterizerInterface& rasterizer = renderer->Rasterizer();
46 memory_manager->BindRasterizer(rasterizer);
47 maxwell_3d->BindRasterizer(rasterizer);
48 fermi_2d->BindRasterizer(rasterizer);
49 kepler_compute->BindRasterizer(rasterizer);
50}
51
45Engines::Maxwell3D& GPU::Maxwell3D() { 52Engines::Maxwell3D& GPU::Maxwell3D() {
46 return *maxwell_3d; 53 return *maxwell_3d;
47} 54}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index ebfc7b0c7..c7d11deb2 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -142,11 +142,6 @@ class MemoryManager;
142 142
143class GPU { 143class GPU {
144public: 144public:
145 explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
146 bool is_async);
147
148 virtual ~GPU();
149
150 struct MethodCall { 145 struct MethodCall {
151 u32 method{}; 146 u32 method{};
152 u32 argument{}; 147 u32 argument{};
@@ -162,6 +157,12 @@ public:
162 method_count(method_count) {} 157 method_count(method_count) {}
163 }; 158 };
164 159
160 explicit GPU(Core::System& system, bool is_async);
161 virtual ~GPU();
162
163 /// Binds a renderer to the GPU.
164 void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer);
165
165 /// Calls a GPU method. 166 /// Calls a GPU method.
166 void CallMethod(const MethodCall& method_call); 167 void CallMethod(const MethodCall& method_call);
167 168
@@ -345,8 +346,8 @@ private:
345 bool ExecuteMethodOnEngine(u32 method); 346 bool ExecuteMethodOnEngine(u32 method);
346 347
347protected: 348protected:
348 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
349 Core::System& system; 349 Core::System& system;
350 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
350 std::unique_ptr<VideoCore::RendererBase> renderer; 351 std::unique_ptr<VideoCore::RendererBase> renderer;
351 352
352private: 353private:
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 7b855f63e..70a3d5738 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -10,16 +10,14 @@
10 10
11namespace VideoCommon { 11namespace VideoCommon {
12 12
13GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, 13GPUAsynch::GPUAsynch(Core::System& system) : GPU{system, true}, gpu_thread{system} {}
14 std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
15 : GPU(system, std::move(renderer_), true), gpu_thread{system},
16 cpu_context(renderer->GetRenderWindow().CreateSharedContext()),
17 gpu_context(std::move(context)) {}
18 14
19GPUAsynch::~GPUAsynch() = default; 15GPUAsynch::~GPUAsynch() = default;
20 16
21void GPUAsynch::Start() { 17void GPUAsynch::Start() {
22 gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher); 18 gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher);
19 cpu_context = renderer->GetRenderWindow().CreateSharedContext();
20 cpu_context->MakeCurrent();
23} 21}
24 22
25void GPUAsynch::ObtainContext() { 23void GPUAsynch::ObtainContext() {
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 15e9f1d38..f89c855a5 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -20,8 +20,7 @@ namespace VideoCommon {
20/// Implementation of GPU interface that runs the GPU asynchronously 20/// Implementation of GPU interface that runs the GPU asynchronously
21class GPUAsynch final : public Tegra::GPU { 21class GPUAsynch final : public Tegra::GPU {
22public: 22public:
23 explicit GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, 23 explicit GPUAsynch(Core::System& system);
24 std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
25 ~GPUAsynch() override; 24 ~GPUAsynch() override;
26 25
27 void Start() override; 26 void Start() override;
@@ -42,7 +41,6 @@ protected:
42private: 41private:
43 GPUThread::ThreadManager gpu_thread; 42 GPUThread::ThreadManager gpu_thread;
44 std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; 43 std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
45 std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context;
46}; 44};
47 45
48} // namespace VideoCommon 46} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index aaeb9811d..1ca47ddef 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -7,20 +7,18 @@
7 7
8namespace VideoCommon { 8namespace VideoCommon {
9 9
10GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, 10GPUSynch::GPUSynch(Core::System& system) : GPU{system, false} {}
11 std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
12 : GPU(system, std::move(renderer), false), context{std::move(context)} {}
13 11
14GPUSynch::~GPUSynch() = default; 12GPUSynch::~GPUSynch() = default;
15 13
16void GPUSynch::Start() {} 14void GPUSynch::Start() {}
17 15
18void GPUSynch::ObtainContext() { 16void GPUSynch::ObtainContext() {
19 context->MakeCurrent(); 17 renderer->Context().MakeCurrent();
20} 18}
21 19
22void GPUSynch::ReleaseContext() { 20void GPUSynch::ReleaseContext() {
23 context->DoneCurrent(); 21 renderer->Context().DoneCurrent();
24} 22}
25 23
26void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { 24void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 762c20aa5..297258cb1 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -19,8 +19,7 @@ namespace VideoCommon {
19/// Implementation of GPU interface that runs the GPU synchronously 19/// Implementation of GPU interface that runs the GPU synchronously
20class GPUSynch final : public Tegra::GPU { 20class GPUSynch final : public Tegra::GPU {
21public: 21public:
22 explicit GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, 22 explicit GPUSynch(Core::System& system);
23 std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
24 ~GPUSynch() override; 23 ~GPUSynch() override;
25 24
26 void Start() override; 25 void Start() override;
@@ -36,9 +35,6 @@ public:
36protected: 35protected:
37 void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id, 36 void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
38 [[maybe_unused]] u32 value) const override {} 37 [[maybe_unused]] u32 value) const override {}
39
40private:
41 std::unique_ptr<Core::Frontend::GraphicsContext> context;
42}; 38};
43 39
44} // namespace VideoCommon 40} // namespace VideoCommon
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 0c9ff59a4..df00b57df 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -24,7 +24,7 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
24 maxwell3d.regs.index_array.first = parameters[4]; 24 maxwell3d.regs.index_array.first = parameters[4];
25 25
26 if (maxwell3d.ShouldExecute()) { 26 if (maxwell3d.ShouldExecute()) {
27 maxwell3d.GetRasterizer().Draw(true, true); 27 maxwell3d.Rasterizer().Draw(true, true);
28 } 28 }
29 maxwell3d.regs.index_array.count = 0; 29 maxwell3d.regs.index_array.count = 0;
30 maxwell3d.mme_draw.instance_count = 0; 30 maxwell3d.mme_draw.instance_count = 0;
@@ -42,7 +42,7 @@ void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
42 maxwell3d.mme_draw.instance_count = count; 42 maxwell3d.mme_draw.instance_count = count;
43 43
44 if (maxwell3d.ShouldExecute()) { 44 if (maxwell3d.ShouldExecute()) {
45 maxwell3d.GetRasterizer().Draw(false, true); 45 maxwell3d.Rasterizer().Draw(false, true);
46 } 46 }
47 maxwell3d.regs.vertex_buffer.count = 0; 47 maxwell3d.regs.vertex_buffer.count = 0;
48 maxwell3d.mme_draw.instance_count = 0; 48 maxwell3d.mme_draw.instance_count = 0;
@@ -65,7 +65,7 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
65 maxwell3d.regs.draw.topology.Assign( 65 maxwell3d.regs.draw.topology.Assign(
66 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); 66 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
67 if (maxwell3d.ShouldExecute()) { 67 if (maxwell3d.ShouldExecute()) {
68 maxwell3d.GetRasterizer().Draw(true, true); 68 maxwell3d.Rasterizer().Draw(true, true);
69 } 69 }
70 maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base? 70 maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base?
71 maxwell3d.regs.index_array.count = 0; 71 maxwell3d.regs.index_array.count = 0;
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index aa5256419..bd01fd1f2 100644
--- a/src/video_core/macro/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -34,7 +34,6 @@ void MacroInterpreterImpl::Execute(const std::vector<u32>& parameters, u32 metho
34 this->parameters = std::make_unique<u32[]>(num_parameters); 34 this->parameters = std::make_unique<u32[]>(num_parameters);
35 } 35 }
36 std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32)); 36 std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32));
37 this->num_parameters = num_parameters;
38 37
39 // Execute the code until we hit an exit condition. 38 // Execute the code until we hit an exit condition.
40 bool keep_executing = true; 39 bool keep_executing = true;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 844164645..c217f5bb2 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -14,11 +14,15 @@
14 14
15namespace Tegra { 15namespace Tegra {
16 16
17MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer) 17MemoryManager::MemoryManager(Core::System& system_)
18 : system{system}, rasterizer{rasterizer}, page_table(page_table_size) {} 18 : system{system_}, page_table(page_table_size) {}
19 19
20MemoryManager::~MemoryManager() = default; 20MemoryManager::~MemoryManager() = default;
21 21
22void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface& rasterizer_) {
23 rasterizer = &rasterizer_;
24}
25
22GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) { 26GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
23 u64 remaining_size{size}; 27 u64 remaining_size{size};
24 for (u64 offset{}; offset < size; offset += page_size) { 28 for (u64 offset{}; offset < size; offset += page_size) {
@@ -217,7 +221,7 @@ void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::siz
217 221
218 // Flush must happen on the rasterizer interface, such that memory is always synchronous 222 // Flush must happen on the rasterizer interface, such that memory is always synchronous
219 // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu. 223 // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
220 rasterizer.FlushRegion(src_addr, copy_amount); 224 rasterizer->FlushRegion(src_addr, copy_amount);
221 system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); 225 system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
222 } 226 }
223 227
@@ -266,7 +270,7 @@ void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, s
266 270
267 // Invalidate must happen on the rasterizer interface, such that memory is always 271 // Invalidate must happen on the rasterizer interface, such that memory is always
268 // synchronous when it is written (even when in asynchronous GPU mode). 272 // synchronous when it is written (even when in asynchronous GPU mode).
269 rasterizer.InvalidateRegion(dest_addr, copy_amount); 273 rasterizer->InvalidateRegion(dest_addr, copy_amount);
270 system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); 274 system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
271 } 275 }
272 276
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 681bd9588..8953fcb53 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -68,9 +68,12 @@ static_assert(sizeof(PageEntry) == 4, "PageEntry is too large");
68 68
69class MemoryManager final { 69class MemoryManager final {
70public: 70public:
71 explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer); 71 explicit MemoryManager(Core::System& system);
72 ~MemoryManager(); 72 ~MemoryManager();
73 73
74 /// Binds a renderer to the memory manager.
75 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
76
74 std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const; 77 std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
75 78
76 template <typename T> 79 template <typename T>
@@ -141,7 +144,7 @@ private:
141 144
142 Core::System& system; 145 Core::System& system;
143 146
144 VideoCore::RasterizerInterface& rasterizer; 147 VideoCore::RasterizerInterface* rasterizer = nullptr;
145 148
146 std::vector<PageEntry> page_table; 149 std::vector<PageEntry> page_table;
147}; 150};
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index dfb06e87e..a93a1732c 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -9,7 +9,9 @@
9 9
10namespace VideoCore { 10namespace VideoCore {
11 11
12RendererBase::RendererBase(Core::Frontend::EmuWindow& window) : render_window{window} { 12RendererBase::RendererBase(Core::Frontend::EmuWindow& window_,
13 std::unique_ptr<Core::Frontend::GraphicsContext> context_)
14 : render_window{window_}, context{std::move(context_)} {
13 RefreshBaseSettings(); 15 RefreshBaseSettings();
14} 16}
15 17
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 1d85219b6..649074acd 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -15,7 +15,8 @@
15 15
16namespace Core::Frontend { 16namespace Core::Frontend {
17class EmuWindow; 17class EmuWindow;
18} 18class GraphicsContext;
19} // namespace Core::Frontend
19 20
20namespace VideoCore { 21namespace VideoCore {
21 22
@@ -25,14 +26,15 @@ struct RendererSettings {
25 26
26 // Screenshot 27 // Screenshot
27 std::atomic<bool> screenshot_requested{false}; 28 std::atomic<bool> screenshot_requested{false};
28 void* screenshot_bits; 29 void* screenshot_bits{};
29 std::function<void()> screenshot_complete_callback; 30 std::function<void()> screenshot_complete_callback;
30 Layout::FramebufferLayout screenshot_framebuffer_layout; 31 Layout::FramebufferLayout screenshot_framebuffer_layout;
31}; 32};
32 33
33class RendererBase : NonCopyable { 34class RendererBase : NonCopyable {
34public: 35public:
35 explicit RendererBase(Core::Frontend::EmuWindow& window); 36 explicit RendererBase(Core::Frontend::EmuWindow& window,
37 std::unique_ptr<Core::Frontend::GraphicsContext> context);
36 virtual ~RendererBase(); 38 virtual ~RendererBase();
37 39
38 /// Initialize the renderer 40 /// Initialize the renderer
@@ -68,6 +70,14 @@ public:
68 return *rasterizer; 70 return *rasterizer;
69 } 71 }
70 72
73 Core::Frontend::GraphicsContext& Context() {
74 return *context;
75 }
76
77 const Core::Frontend::GraphicsContext& Context() const {
78 return *context;
79 }
80
71 Core::Frontend::EmuWindow& GetRenderWindow() { 81 Core::Frontend::EmuWindow& GetRenderWindow() {
72 return render_window; 82 return render_window;
73 } 83 }
@@ -94,6 +104,7 @@ public:
94protected: 104protected:
95 Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle. 105 Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
96 std::unique_ptr<RasterizerInterface> rasterizer; 106 std::unique_ptr<RasterizerInterface> rasterizer;
107 std::unique_ptr<Core::Frontend::GraphicsContext> context;
97 f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer 108 f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
98 int m_current_frame = 0; ///< Current frame, should be set by the renderer 109 int m_current_frame = 0; ///< Current frame, should be set by the renderer
99 110
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
index ec5421afa..3d2588dd2 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -4,16 +4,17 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6 6
7#include <glad/glad.h>
8
7#include "video_core/renderer_opengl/gl_buffer_cache.h" 9#include "video_core/renderer_opengl/gl_buffer_cache.h"
8#include "video_core/renderer_opengl/gl_fence_manager.h" 10#include "video_core/renderer_opengl/gl_fence_manager.h"
9 11
10namespace OpenGL { 12namespace OpenGL {
11 13
12GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed) 14GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed) : FenceBase(payload, is_stubbed) {}
13 : VideoCommon::FenceBase(payload, is_stubbed), sync_object{} {}
14 15
15GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed) 16GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed)
16 : VideoCommon::FenceBase(address, payload, is_stubbed), sync_object{} {} 17 : FenceBase(address, payload, is_stubbed) {}
17 18
18GLInnerFence::~GLInnerFence() = default; 19GLInnerFence::~GLInnerFence() = default;
19 20
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
index c917b3343..1686cf5c8 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.h
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <glad/glad.h>
9 8
10#include "common/common_types.h" 9#include "common/common_types.h"
11#include "video_core/fence_manager.h" 10#include "video_core/fence_manager.h"
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 52fbab3c1..40c0877c1 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -214,20 +214,20 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
214 // Skip games without title id 214 // Skip games without title id
215 const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; 215 const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
216 if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) { 216 if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
217 return {}; 217 return std::nullopt;
218 } 218 }
219 219
220 Common::FS::IOFile file(GetTransferablePath(), "rb"); 220 Common::FS::IOFile file(GetTransferablePath(), "rb");
221 if (!file.IsOpen()) { 221 if (!file.IsOpen()) {
222 LOG_INFO(Render_OpenGL, "No transferable shader cache found"); 222 LOG_INFO(Render_OpenGL, "No transferable shader cache found");
223 is_usable = true; 223 is_usable = true;
224 return {}; 224 return std::nullopt;
225 } 225 }
226 226
227 u32 version{}; 227 u32 version{};
228 if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) { 228 if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) {
229 LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it"); 229 LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it");
230 return {}; 230 return std::nullopt;
231 } 231 }
232 232
233 if (version < NativeVersion) { 233 if (version < NativeVersion) {
@@ -235,12 +235,12 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
235 file.Close(); 235 file.Close();
236 InvalidateTransferable(); 236 InvalidateTransferable();
237 is_usable = true; 237 is_usable = true;
238 return {}; 238 return std::nullopt;
239 } 239 }
240 if (version > NativeVersion) { 240 if (version > NativeVersion) {
241 LOG_WARNING(Render_OpenGL, "Transferable shader cache was generated with a newer version " 241 LOG_WARNING(Render_OpenGL, "Transferable shader cache was generated with a newer version "
242 "of the emulator, skipping"); 242 "of the emulator, skipping");
243 return {}; 243 return std::nullopt;
244 } 244 }
245 245
246 // Version is valid, load the shaders 246 // Version is valid, load the shaders
@@ -249,7 +249,7 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran
249 ShaderDiskCacheEntry& entry = entries.emplace_back(); 249 ShaderDiskCacheEntry& entry = entries.emplace_back();
250 if (!entry.Load(file)) { 250 if (!entry.Load(file)) {
251 LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping"); 251 LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping");
252 return {}; 252 return std::nullopt;
253 } 253 }
254 } 254 }
255 255
@@ -290,12 +290,12 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
290 ShaderCacheVersionHash file_hash{}; 290 ShaderCacheVersionHash file_hash{};
291 if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) { 291 if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) {
292 precompiled_cache_virtual_file_offset = 0; 292 precompiled_cache_virtual_file_offset = 0;
293 return {}; 293 return std::nullopt;
294 } 294 }
295 if (GetShaderCacheVersionHash() != file_hash) { 295 if (GetShaderCacheVersionHash() != file_hash) {
296 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); 296 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator");
297 precompiled_cache_virtual_file_offset = 0; 297 precompiled_cache_virtual_file_offset = 0;
298 return {}; 298 return std::nullopt;
299 } 299 }
300 300
301 std::vector<ShaderDiskCachePrecompiled> entries; 301 std::vector<ShaderDiskCachePrecompiled> entries;
@@ -305,15 +305,16 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
305 if (!LoadObjectFromPrecompiled(entry.unique_identifier) || 305 if (!LoadObjectFromPrecompiled(entry.unique_identifier) ||
306 !LoadObjectFromPrecompiled(entry.binary_format) || 306 !LoadObjectFromPrecompiled(entry.binary_format) ||
307 !LoadObjectFromPrecompiled(binary_size)) { 307 !LoadObjectFromPrecompiled(binary_size)) {
308 return {}; 308 return std::nullopt;
309 } 309 }
310 310
311 entry.binary.resize(binary_size); 311 entry.binary.resize(binary_size);
312 if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) { 312 if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) {
313 return {}; 313 return std::nullopt;
314 } 314 }
315 } 315 }
316 return entries; 316
317 return std::move(entries);
317} 318}
318 319
319void ShaderDiskCacheOpenGL::InvalidateTransferable() { 320void ShaderDiskCacheOpenGL::InvalidateTransferable() {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 0a7bc9e2b..f403f388a 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -403,7 +403,7 @@ void CachedSurface::DecorateSurfaceName() {
403 LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), params.TargetName()); 403 LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), params.TargetName());
404} 404}
405 405
406void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, std::string prefix) { 406void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix) {
407 LabelGLObject(GL_TEXTURE, main_view.handle, gpu_addr, prefix); 407 LabelGLObject(GL_TEXTURE, main_view.handle, gpu_addr, prefix);
408} 408}
409 409
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index bfc4ddf5d..de8f18489 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -90,7 +90,7 @@ public:
90 Tegra::Texture::SwizzleSource z_source, 90 Tegra::Texture::SwizzleSource z_source,
91 Tegra::Texture::SwizzleSource w_source); 91 Tegra::Texture::SwizzleSource w_source);
92 92
93 void DecorateViewName(GPUVAddr gpu_addr, std::string prefix); 93 void DecorateViewName(GPUVAddr gpu_addr, const std::string& prefix);
94 94
95 void MarkAsModified(u64 tick) { 95 void MarkAsModified(u64 tick) {
96 surface.MarkAsModified(true, tick); 96 surface.MarkAsModified(true, tick);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 14bbc3a1c..c39663db7 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -313,10 +313,11 @@ public:
313 } 313 }
314}; 314};
315 315
316RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, 316RendererOpenGL::RendererOpenGL(Core::System& system_, Core::Frontend::EmuWindow& emu_window_,
317 Core::Frontend::GraphicsContext& context) 317 Tegra::GPU& gpu_,
318 : RendererBase{emu_window}, emu_window{emu_window}, system{system}, context{context}, 318 std::unique_ptr<Core::Frontend::GraphicsContext> context_)
319 program_manager{device}, has_debug_tool{HasDebugTool()} {} 319 : RendererBase{emu_window_, std::move(context_)}, system{system_},
320 emu_window{emu_window_}, gpu{gpu_}, program_manager{device}, has_debug_tool{HasDebugTool()} {}
320 321
321RendererOpenGL::~RendererOpenGL() = default; 322RendererOpenGL::~RendererOpenGL() = default;
322 323
@@ -384,7 +385,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
384 if (has_debug_tool) { 385 if (has_debug_tool) {
385 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 386 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
386 Present(0); 387 Present(0);
387 context.SwapBuffers(); 388 context->SwapBuffers();
388 } 389 }
389} 390}
390 391
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 8b18d32e6..52ea76b7d 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -56,8 +56,9 @@ class FrameMailbox;
56 56
57class RendererOpenGL final : public VideoCore::RendererBase { 57class RendererOpenGL final : public VideoCore::RendererBase {
58public: 58public:
59 explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, 59 explicit RendererOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
60 Core::Frontend::GraphicsContext& context); 60 Tegra::GPU& gpu,
61 std::unique_ptr<Core::Frontend::GraphicsContext> context);
61 ~RendererOpenGL() override; 62 ~RendererOpenGL() override;
62 63
63 bool Init() override; 64 bool Init() override;
@@ -93,9 +94,9 @@ private:
93 94
94 bool Present(int timeout_ms); 95 bool Present(int timeout_ms);
95 96
96 Core::Frontend::EmuWindow& emu_window;
97 Core::System& system; 97 Core::System& system;
98 Core::Frontend::GraphicsContext& context; 98 Core::Frontend::EmuWindow& emu_window;
99 Tegra::GPU& gpu;
99 const Device device; 100 const Device device;
100 101
101 StateTracker state_tracker{system}; 102 StateTracker state_tracker{system};
@@ -120,7 +121,7 @@ private:
120 std::vector<u8> gl_framebuffer_data; 121 std::vector<u8> gl_framebuffer_data;
121 122
122 /// Used for transforming the framebuffer orientation 123 /// Used for transforming the framebuffer orientation
123 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags; 124 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{};
124 Common::Rectangle<int> framebuffer_crop_rect; 125 Common::Rectangle<int> framebuffer_crop_rect;
125 126
126 /// Frame presentation mailbox 127 /// Frame presentation mailbox
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 6e49699d0..ae46e0444 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -237,8 +237,10 @@ std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_ext
237 237
238} // Anonymous namespace 238} // Anonymous namespace
239 239
240RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system) 240RendererVulkan::RendererVulkan(Core::System& system_, Core::Frontend::EmuWindow& emu_window,
241 : RendererBase(window), system{system} {} 241 Tegra::GPU& gpu_,
242 std::unique_ptr<Core::Frontend::GraphicsContext> context)
243 : RendererBase{emu_window, std::move(context)}, system{system_}, gpu{gpu_} {}
242 244
243RendererVulkan::~RendererVulkan() { 245RendererVulkan::~RendererVulkan() {
244 ShutDown(); 246 ShutDown();
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 522b5bff8..13debbbc0 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -38,7 +38,9 @@ struct VKScreenInfo {
38 38
39class RendererVulkan final : public VideoCore::RendererBase { 39class RendererVulkan final : public VideoCore::RendererBase {
40public: 40public:
41 explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system); 41 explicit RendererVulkan(Core::System& system, Core::Frontend::EmuWindow& emu_window,
42 Tegra::GPU& gpu,
43 std::unique_ptr<Core::Frontend::GraphicsContext> context);
42 ~RendererVulkan() override; 44 ~RendererVulkan() override;
43 45
44 bool Init() override; 46 bool Init() override;
@@ -58,6 +60,7 @@ private:
58 void Report() const; 60 void Report() const;
59 61
60 Core::System& system; 62 Core::System& system;
63 Tegra::GPU& gpu;
61 64
62 Common::DynamicLibrary library; 65 Common::DynamicLibrary library;
63 vk::InstanceDispatch dld; 66 vk::InstanceDispatch dld;
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 45f360bdd..4e3a092c7 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.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 <memory> 5#include <memory>
6
6#include "common/logging/log.h" 7#include "common/logging/log.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/settings.h" 9#include "core/settings.h"
@@ -16,37 +17,46 @@
16#include "video_core/video_core.h" 17#include "video_core/video_core.h"
17 18
18namespace { 19namespace {
19std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, 20
20 Core::System& system, 21std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
21 Core::Frontend::GraphicsContext& context) { 22 Core::System& system, Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
23 std::unique_ptr<Core::Frontend::GraphicsContext> context) {
22 switch (Settings::values.renderer_backend.GetValue()) { 24 switch (Settings::values.renderer_backend.GetValue()) {
23 case Settings::RendererBackend::OpenGL: 25 case Settings::RendererBackend::OpenGL:
24 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context); 26 return std::make_unique<OpenGL::RendererOpenGL>(system, emu_window, gpu,
27 std::move(context));
25#ifdef HAS_VULKAN 28#ifdef HAS_VULKAN
26 case Settings::RendererBackend::Vulkan: 29 case Settings::RendererBackend::Vulkan:
27 return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); 30 return std::make_unique<Vulkan::RendererVulkan>(system, emu_window, gpu,
31 std::move(context));
28#endif 32#endif
29 default: 33 default:
30 return nullptr; 34 return nullptr;
31 } 35 }
32} 36}
37
33} // Anonymous namespace 38} // Anonymous namespace
34 39
35namespace VideoCore { 40namespace VideoCore {
36 41
37std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { 42std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
43 std::unique_ptr<Tegra::GPU> gpu;
44 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
45 gpu = std::make_unique<VideoCommon::GPUAsynch>(system);
46 } else {
47 gpu = std::make_unique<VideoCommon::GPUSynch>(system);
48 }
49
38 auto context = emu_window.CreateSharedContext(); 50 auto context = emu_window.CreateSharedContext();
39 const auto scope = context->Acquire(); 51 const auto scope = context->Acquire();
40 auto renderer = CreateRenderer(emu_window, system, *context); 52
53 auto renderer = CreateRenderer(system, emu_window, *gpu, std::move(context));
41 if (!renderer->Init()) { 54 if (!renderer->Init()) {
42 return nullptr; 55 return nullptr;
43 } 56 }
44 57
45 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { 58 gpu->BindRenderer(std::move(renderer));
46 return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer), 59 return gpu;
47 std::move(context));
48 }
49 return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context));
50} 60}
51 61
52u16 GetResolutionScaleFactor(const RendererBase& renderer) { 62u16 GetResolutionScaleFactor(const RendererBase& renderer) {
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
index 06ab7c59d..7e484b906 100644
--- a/src/web_service/CMakeLists.txt
+++ b/src/web_service/CMakeLists.txt
@@ -5,6 +5,7 @@ add_library(web_service STATIC
5 verify_login.h 5 verify_login.h
6 web_backend.cpp 6 web_backend.cpp
7 web_backend.h 7 web_backend.h
8 web_result.h
8) 9)
9 10
10create_target_directory_groups(web_service) 11create_target_directory_groups(web_service)
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index c89a3a0db..6215c914f 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -4,9 +4,9 @@
4 4
5#include <nlohmann/json.hpp> 5#include <nlohmann/json.hpp>
6#include "common/detached_tasks.h" 6#include "common/detached_tasks.h"
7#include "common/web_result.h"
8#include "web_service/telemetry_json.h" 7#include "web_service/telemetry_json.h"
9#include "web_service/web_backend.h" 8#include "web_service/web_backend.h"
9#include "web_service/web_result.h"
10 10
11namespace WebService { 11namespace WebService {
12 12
@@ -125,7 +125,7 @@ bool TelemetryJson::SubmitTestcase() {
125 Client client(impl->host, impl->username, impl->token); 125 Client client(impl->host, impl->username, impl->token);
126 auto value = client.PostJson("/gamedb/testcase", content, false); 126 auto value = client.PostJson("/gamedb/testcase", content, false);
127 127
128 return value.result_code == Common::WebResult::Code::Success; 128 return value.result_code == WebResult::Code::Success;
129} 129}
130 130
131} // namespace WebService 131} // namespace WebService
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp
index bfaa5b70a..ceb55ca6b 100644
--- a/src/web_service/verify_login.cpp
+++ b/src/web_service/verify_login.cpp
@@ -3,9 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <nlohmann/json.hpp> 5#include <nlohmann/json.hpp>
6#include "common/web_result.h"
7#include "web_service/verify_login.h" 6#include "web_service/verify_login.h"
8#include "web_service/web_backend.h" 7#include "web_service/web_backend.h"
8#include "web_service/web_result.h"
9 9
10namespace WebService { 10namespace WebService {
11 11
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 09d1651ac..74e287045 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -6,13 +6,14 @@
6#include <cstdlib> 6#include <cstdlib>
7#include <mutex> 7#include <mutex>
8#include <string> 8#include <string>
9
9#include <LUrlParser.h> 10#include <LUrlParser.h>
10#include <fmt/format.h> 11#include <fmt/format.h>
11#include <httplib.h> 12#include <httplib.h>
12#include "common/common_types.h" 13
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "common/web_result.h"
15#include "web_service/web_backend.h" 15#include "web_service/web_backend.h"
16#include "web_service/web_result.h"
16 17
17namespace WebService { 18namespace WebService {
18 19
@@ -33,17 +34,16 @@ struct Client::Impl {
33 } 34 }
34 35
35 /// A generic function handles POST, GET and DELETE request together 36 /// A generic function handles POST, GET and DELETE request together
36 Common::WebResult GenericRequest(const std::string& method, const std::string& path, 37 WebResult GenericRequest(const std::string& method, const std::string& path,
37 const std::string& data, bool allow_anonymous, 38 const std::string& data, bool allow_anonymous,
38 const std::string& accept) { 39 const std::string& accept) {
39 if (jwt.empty()) { 40 if (jwt.empty()) {
40 UpdateJWT(); 41 UpdateJWT();
41 } 42 }
42 43
43 if (jwt.empty() && !allow_anonymous) { 44 if (jwt.empty() && !allow_anonymous) {
44 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); 45 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
45 return Common::WebResult{Common::WebResult::Code::CredentialsMissing, 46 return WebResult{WebResult::Code::CredentialsMissing, "Credentials needed", ""};
46 "Credentials needed", ""};
47 } 47 }
48 48
49 auto result = GenericRequest(method, path, data, accept, jwt); 49 auto result = GenericRequest(method, path, data, accept, jwt);
@@ -62,10 +62,10 @@ struct Client::Impl {
62 * username + token is used if jwt is empty but username and token are 62 * username + token is used if jwt is empty but username and token are
63 * not empty anonymous if all of jwt, username and token are empty 63 * not empty anonymous if all of jwt, username and token are empty
64 */ 64 */
65 Common::WebResult GenericRequest(const std::string& method, const std::string& path, 65 WebResult GenericRequest(const std::string& method, const std::string& path,
66 const std::string& data, const std::string& accept, 66 const std::string& data, const std::string& accept,
67 const std::string& jwt = "", const std::string& username = "", 67 const std::string& jwt = "", const std::string& username = "",
68 const std::string& token = "") { 68 const std::string& token = "") {
69 if (cli == nullptr) { 69 if (cli == nullptr) {
70 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); 70 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
71 int port; 71 int port;
@@ -81,12 +81,12 @@ struct Client::Impl {
81 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port); 81 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port);
82 } else { 82 } else {
83 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme); 83 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
84 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme", ""}; 84 return WebResult{WebResult::Code::InvalidURL, "Bad URL scheme", ""};
85 } 85 }
86 } 86 }
87 if (cli == nullptr) { 87 if (cli == nullptr) {
88 LOG_ERROR(WebService, "Invalid URL {}", host + path); 88 LOG_ERROR(WebService, "Invalid URL {}", host + path);
89 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL", ""}; 89 return WebResult{WebResult::Code::InvalidURL, "Invalid URL", ""};
90 } 90 }
91 cli->set_timeout_sec(TIMEOUT_SECONDS); 91 cli->set_timeout_sec(TIMEOUT_SECONDS);
92 92
@@ -106,7 +106,7 @@ struct Client::Impl {
106 std::string(API_VERSION.begin(), API_VERSION.end())); 106 std::string(API_VERSION.begin(), API_VERSION.end()));
107 if (method != "GET") { 107 if (method != "GET") {
108 params.emplace(std::string("Content-Type"), std::string("application/json")); 108 params.emplace(std::string("Content-Type"), std::string("application/json"));
109 }; 109 }
110 110
111 httplib::Request request; 111 httplib::Request request;
112 request.method = method; 112 request.method = method;
@@ -118,29 +118,28 @@ struct Client::Impl {
118 118
119 if (!cli->send(request, response)) { 119 if (!cli->send(request, response)) {
120 LOG_ERROR(WebService, "{} to {} returned null", method, host + path); 120 LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
121 return Common::WebResult{Common::WebResult::Code::LibError, "Null response", ""}; 121 return WebResult{WebResult::Code::LibError, "Null response", ""};
122 } 122 }
123 123
124 if (response.status >= 400) { 124 if (response.status >= 400) {
125 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, 125 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
126 response.status); 126 response.status);
127 return Common::WebResult{Common::WebResult::Code::HttpError, 127 return WebResult{WebResult::Code::HttpError, std::to_string(response.status), ""};
128 std::to_string(response.status), ""};
129 } 128 }
130 129
131 auto content_type = response.headers.find("content-type"); 130 auto content_type = response.headers.find("content-type");
132 131
133 if (content_type == response.headers.end()) { 132 if (content_type == response.headers.end()) {
134 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); 133 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
135 return Common::WebResult{Common::WebResult::Code::WrongContent, "", ""}; 134 return WebResult{WebResult::Code::WrongContent, "", ""};
136 } 135 }
137 136
138 if (content_type->second.find(accept) == std::string::npos) { 137 if (content_type->second.find(accept) == std::string::npos) {
139 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, 138 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
140 content_type->second); 139 content_type->second);
141 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content", ""}; 140 return WebResult{WebResult::Code::WrongContent, "Wrong content", ""};
142 } 141 }
143 return Common::WebResult{Common::WebResult::Code::Success, "", response.body}; 142 return WebResult{WebResult::Code::Success, "", response.body};
144 } 143 }
145 144
146 // Retrieve a new JWT from given username and token 145 // Retrieve a new JWT from given username and token
@@ -150,7 +149,7 @@ struct Client::Impl {
150 } 149 }
151 150
152 auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token); 151 auto result = GenericRequest("POST", "/jwt/internal", "", "text/html", "", username, token);
153 if (result.result_code != Common::WebResult::Code::Success) { 152 if (result.result_code != WebResult::Code::Success) {
154 LOG_ERROR(WebService, "UpdateJWT failed"); 153 LOG_ERROR(WebService, "UpdateJWT failed");
155 } else { 154 } else {
156 std::lock_guard lock{jwt_cache.mutex}; 155 std::lock_guard lock{jwt_cache.mutex};
@@ -180,29 +179,28 @@ Client::Client(std::string host, std::string username, std::string token)
180 179
181Client::~Client() = default; 180Client::~Client() = default;
182 181
183Common::WebResult Client::PostJson(const std::string& path, const std::string& data, 182WebResult Client::PostJson(const std::string& path, const std::string& data, bool allow_anonymous) {
184 bool allow_anonymous) {
185 return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json"); 183 return impl->GenericRequest("POST", path, data, allow_anonymous, "application/json");
186} 184}
187 185
188Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) { 186WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
189 return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json"); 187 return impl->GenericRequest("GET", path, "", allow_anonymous, "application/json");
190} 188}
191 189
192Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data, 190WebResult Client::DeleteJson(const std::string& path, const std::string& data,
193 bool allow_anonymous) { 191 bool allow_anonymous) {
194 return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json"); 192 return impl->GenericRequest("DELETE", path, data, allow_anonymous, "application/json");
195} 193}
196 194
197Common::WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) { 195WebResult Client::GetPlain(const std::string& path, bool allow_anonymous) {
198 return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain"); 196 return impl->GenericRequest("GET", path, "", allow_anonymous, "text/plain");
199} 197}
200 198
201Common::WebResult Client::GetImage(const std::string& path, bool allow_anonymous) { 199WebResult Client::GetImage(const std::string& path, bool allow_anonymous) {
202 return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png"); 200 return impl->GenericRequest("GET", path, "", allow_anonymous, "image/png");
203} 201}
204 202
205Common::WebResult Client::GetExternalJWT(const std::string& audience) { 203WebResult Client::GetExternalJWT(const std::string& audience) {
206 return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false, 204 return impl->GenericRequest("POST", fmt::format("/jwt/external/{}", audience), "", false,
207 "text/html"); 205 "text/html");
208} 206}
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index 04121f17e..81f58583c 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -7,12 +7,10 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9 9
10namespace Common {
11struct WebResult;
12}
13
14namespace WebService { 10namespace WebService {
15 11
12struct WebResult;
13
16class Client { 14class Client {
17public: 15public:
18 Client(std::string host, std::string username, std::string token); 16 Client(std::string host, std::string username, std::string token);
@@ -25,8 +23,7 @@ public:
25 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 23 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
26 * @return the result of the request. 24 * @return the result of the request.
27 */ 25 */
28 Common::WebResult PostJson(const std::string& path, const std::string& data, 26 WebResult PostJson(const std::string& path, const std::string& data, bool allow_anonymous);
29 bool allow_anonymous);
30 27
31 /** 28 /**
32 * Gets JSON from the specified path. 29 * Gets JSON from the specified path.
@@ -34,7 +31,7 @@ public:
34 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 31 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
35 * @return the result of the request. 32 * @return the result of the request.
36 */ 33 */
37 Common::WebResult GetJson(const std::string& path, bool allow_anonymous); 34 WebResult GetJson(const std::string& path, bool allow_anonymous);
38 35
39 /** 36 /**
40 * Deletes JSON to the specified path. 37 * Deletes JSON to the specified path.
@@ -43,8 +40,7 @@ public:
43 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 40 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
44 * @return the result of the request. 41 * @return the result of the request.
45 */ 42 */
46 Common::WebResult DeleteJson(const std::string& path, const std::string& data, 43 WebResult DeleteJson(const std::string& path, const std::string& data, bool allow_anonymous);
47 bool allow_anonymous);
48 44
49 /** 45 /**
50 * Gets a plain string from the specified path. 46 * Gets a plain string from the specified path.
@@ -52,7 +48,7 @@ public:
52 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 48 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
53 * @return the result of the request. 49 * @return the result of the request.
54 */ 50 */
55 Common::WebResult GetPlain(const std::string& path, bool allow_anonymous); 51 WebResult GetPlain(const std::string& path, bool allow_anonymous);
56 52
57 /** 53 /**
58 * Gets an PNG image from the specified path. 54 * Gets an PNG image from the specified path.
@@ -60,14 +56,14 @@ public:
60 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 56 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
61 * @return the result of the request. 57 * @return the result of the request.
62 */ 58 */
63 Common::WebResult GetImage(const std::string& path, bool allow_anonymous); 59 WebResult GetImage(const std::string& path, bool allow_anonymous);
64 60
65 /** 61 /**
66 * Requests an external JWT for the specific audience provided. 62 * Requests an external JWT for the specific audience provided.
67 * @param audience the audience of the JWT requested. 63 * @param audience the audience of the JWT requested.
68 * @return the result of the request. 64 * @return the result of the request.
69 */ 65 */
70 Common::WebResult GetExternalJWT(const std::string& audience); 66 WebResult GetExternalJWT(const std::string& audience);
71 67
72private: 68private:
73 struct Impl; 69 struct Impl;
diff --git a/src/common/web_result.h b/src/web_service/web_result.h
index 8bfa2141d..3aeeb5288 100644
--- a/src/common/web_result.h
+++ b/src/web_service/web_result.h
@@ -7,7 +7,7 @@
7#include <string> 7#include <string>
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10namespace Common { 10namespace WebService {
11struct WebResult { 11struct WebResult {
12 enum class Code : u32 { 12 enum class Code : u32 {
13 Success, 13 Success,
@@ -22,4 +22,4 @@ struct WebResult {
22 std::string result_string; 22 std::string result_string;
23 std::string returned_data; 23 std::string returned_data;
24}; 24};
25} // namespace Common 25} // namespace WebService
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index a372190cc..7af974d8d 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -635,6 +635,11 @@ void Config::ReadCpuValues() {
635 ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool(); 635 ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool();
636 Settings::values.cpuopt_reduce_misalign_checks = 636 Settings::values.cpuopt_reduce_misalign_checks =
637 ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool(); 637 ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool();
638
639 Settings::values.cpuopt_unsafe_unfuse_fma =
640 ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool();
641 Settings::values.cpuopt_unsafe_reduce_fp_error =
642 ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool();
638 } 643 }
639 644
640 qt_config->endGroup(); 645 qt_config->endGroup();
@@ -1132,6 +1137,11 @@ void Config::SaveCpuValues() {
1132 WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true); 1137 WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true);
1133 WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), 1138 WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"),
1134 Settings::values.cpuopt_reduce_misalign_checks, true); 1139 Settings::values.cpuopt_reduce_misalign_checks, true);
1140
1141 WriteSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"),
1142 Settings::values.cpuopt_unsafe_unfuse_fma, true);
1143 WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"),
1144 Settings::values.cpuopt_unsafe_reduce_fp_error, true);
1135 } 1145 }
1136 1146
1137 qt_config->endGroup(); 1147 qt_config->endGroup();
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index 7493e5ffb..37fcd6adc 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -19,6 +19,8 @@ ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::Config
19 19
20 connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this, 20 connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this,
21 &ConfigureCpu::AccuracyUpdated); 21 &ConfigureCpu::AccuracyUpdated);
22 connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this,
23 &ConfigureCpu::UpdateGroup);
22} 24}
23 25
24ConfigureCpu::~ConfigureCpu() = default; 26ConfigureCpu::~ConfigureCpu() = default;
@@ -28,6 +30,12 @@ void ConfigureCpu::SetConfiguration() {
28 30
29 ui->accuracy->setEnabled(runtime_lock); 31 ui->accuracy->setEnabled(runtime_lock);
30 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy)); 32 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy));
33 UpdateGroup(static_cast<int>(Settings::values.cpu_accuracy));
34
35 ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock);
36 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma);
37 ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock);
38 ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error);
31} 39}
32 40
33void ConfigureCpu::AccuracyUpdated(int index) { 41void ConfigureCpu::AccuracyUpdated(int index) {
@@ -38,14 +46,21 @@ void ConfigureCpu::AccuracyUpdated(int index) {
38 QMessageBox::Yes | QMessageBox::No); 46 QMessageBox::Yes | QMessageBox::No);
39 if (result == QMessageBox::No) { 47 if (result == QMessageBox::No) {
40 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate)); 48 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate));
41 return; 49 UpdateGroup(static_cast<int>(Settings::CPUAccuracy::Accurate));
42 } 50 }
43 } 51 }
44} 52}
45 53
54void ConfigureCpu::UpdateGroup(int index) {
55 ui->unsafe_group->setVisible(static_cast<Settings::CPUAccuracy>(index) ==
56 Settings::CPUAccuracy::Unsafe);
57}
58
46void ConfigureCpu::ApplyConfiguration() { 59void ConfigureCpu::ApplyConfiguration() {
47 Settings::values.cpu_accuracy = 60 Settings::values.cpu_accuracy =
48 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); 61 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex());
62 Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked();
63 Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked();
49} 64}
50 65
51void ConfigureCpu::changeEvent(QEvent* event) { 66void ConfigureCpu::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h
index e4741d3a4..3c5683d81 100644
--- a/src/yuzu/configuration/configure_cpu.h
+++ b/src/yuzu/configuration/configure_cpu.h
@@ -26,6 +26,7 @@ private:
26 void RetranslateUI(); 26 void RetranslateUI();
27 27
28 void AccuracyUpdated(int index); 28 void AccuracyUpdated(int index);
29 void UpdateGroup(int index);
29 30
30 void SetConfiguration(); 31 void SetConfiguration();
31 32
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index bf6ea79bb..ebdd2e6e9 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -40,6 +40,11 @@
40 </item> 40 </item>
41 <item> 41 <item>
42 <property name="text"> 42 <property name="text">
43 <string>Unsafe</string>
44 </property>
45 </item>
46 <item>
47 <property name="text">
43 <string>Enable Debug Mode</string> 48 <string>Enable Debug Mode</string>
44 </property> 49 </property>
45 </item> 50 </item>
@@ -63,6 +68,53 @@
63 </layout> 68 </layout>
64 </item> 69 </item>
65 <item> 70 <item>
71 <layout class="QVBoxLayout">
72 <item>
73 <widget class="QGroupBox" name="unsafe_group">
74 <property name="title">
75 <string>Unsafe CPU Optimization Settings</string>
76 </property>
77 <layout class="QVBoxLayout">
78 <item>
79 <widget class="QLabel">
80 <property name="wordWrap">
81 <bool>1</bool>
82 </property>
83 <property name="text">
84 <string>These settings reduce accuracy for speed.</string>
85 </property>
86 </widget>
87 </item>
88 <item>
89 <widget class="QCheckBox" name="cpuopt_unsafe_unfuse_fma">
90 <property name="text">
91 <string>Unfuse FMA (improve performance on CPUs without FMA)</string>
92 </property>
93 <property name="toolTip">
94 <string>
95 &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
96 </string>
97 </property>
98 </widget>
99 </item>
100 <item>
101 <widget class="QCheckBox" name="cpuopt_unsafe_reduce_fp_error">
102 <property name="text">
103 <string>Faster FRSQRTE and FRECPE</string>
104 </property>
105 <property name="toolTip">
106 <string>
107 &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
108 </string>
109 </property>
110 </widget>
111 </item>
112 </layout>
113 </widget>
114 </item>
115 </layout>
116 </item>
117 <item>
66 <spacer name="verticalSpacer"> 118 <spacer name="verticalSpacer">
67 <property name="orientation"> 119 <property name="orientation">
68 <enum>Qt::Vertical</enum> 120 <enum>Qt::Vertical</enum>