summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings.h13
-rw-r--r--src/common/thread_worker.cpp58
-rw-r--r--src/common/thread_worker.h102
-rw-r--r--src/common/unique_function.h62
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp18
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp18
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/unique_function.cpp108
-rw-r--r--src/tests/video_core/buffer_base.cpp2
-rw-r--r--src/video_core/buffer_cache/buffer_base.h19
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h238
-rw-r--r--src/video_core/dma_pusher.cpp10
-rw-r--r--src/video_core/fence_manager.h7
-rw-r--r--src/video_core/gpu.cpp4
-rw-r--r--src/video_core/rasterizer_interface.h3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h1
-rw-r--r--src/video_core/texture_cache/types.h4
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/configuration/config.cpp23
-rw-r--r--src/yuzu/configuration/configure.ui50
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp18
-rw-r--r--src/yuzu/configuration/configure_cpu.h1
-rw-r--r--src/yuzu/configuration/configure_cpu.ui14
-rw-r--r--src/yuzu/configuration/configure_cpu_debug.h1
-rw-r--r--src/yuzu/configuration/configure_cpu_debug.ui94
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui63
-rw-r--r--src/yuzu/configuration/configure_debug_tab.cpp38
-rw-r--r--src/yuzu/configuration/configure_debug_tab.h32
-rw-r--r--src/yuzu/configuration/configure_debug_tab.ui52
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui18
-rw-r--r--src/yuzu/main.cpp10
40 files changed, 800 insertions, 322 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index a6fa9a85d..e03fffd8d 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -180,7 +180,6 @@ add_library(common STATIC
180 thread.cpp 180 thread.cpp
181 thread.h 181 thread.h
182 thread_queue_list.h 182 thread_queue_list.h
183 thread_worker.cpp
184 thread_worker.h 183 thread_worker.h
185 threadsafe_queue.h 184 threadsafe_queue.h
186 time_zone.cpp 185 time_zone.cpp
@@ -188,6 +187,7 @@ add_library(common STATIC
188 tiny_mt.h 187 tiny_mt.h
189 tree.h 188 tree.h
190 uint128.h 189 uint128.h
190 unique_function.h
191 uuid.cpp 191 uuid.cpp
192 uuid.h 192 uuid.h
193 vector_math.h 193 vector_math.h
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 0061e29cc..e1973af85 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -93,7 +93,7 @@ bool IsGPULevelHigh() {
93} 93}
94 94
95bool IsFastmemEnabled() { 95bool IsFastmemEnabled() {
96 if (values.cpu_accuracy.GetValue() == CPUAccuracy::DebugMode) { 96 if (values.cpu_debug_mode) {
97 return static_cast<bool>(values.cpuopt_fastmem); 97 return static_cast<bool>(values.cpuopt_fastmem);
98 } 98 }
99 return true; 99 return true;
diff --git a/src/common/settings.h b/src/common/settings.h
index bf83186f5..71d0f864f 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -31,9 +31,9 @@ enum class GPUAccuracy : u32 {
31}; 31};
32 32
33enum class CPUAccuracy : u32 { 33enum class CPUAccuracy : u32 {
34 Accurate = 0, 34 Auto = 0,
35 Unsafe = 1, 35 Accurate = 1,
36 DebugMode = 2, 36 Unsafe = 2,
37}; 37};
38 38
39/** The BasicSetting class is a simple resource manager. It defines a label and default value 39/** The BasicSetting class is a simple resource manager. It defines a label and default value
@@ -284,7 +284,10 @@ struct Values {
284 Setting<bool> use_multi_core{true, "use_multi_core"}; 284 Setting<bool> use_multi_core{true, "use_multi_core"};
285 285
286 // Cpu 286 // Cpu
287 Setting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Accurate, "cpu_accuracy"}; 287 Setting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, "cpu_accuracy"};
288 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
289 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
290 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
288 291
289 BasicSetting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"}; 292 BasicSetting<bool> cpuopt_page_tables{true, "cpuopt_page_tables"};
290 BasicSetting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"}; 293 BasicSetting<bool> cpuopt_block_linking{true, "cpuopt_block_linking"};
@@ -327,7 +330,7 @@ struct Values {
327 Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; 330 Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"};
328 Setting<bool> accelerate_astc{true, "accelerate_astc"}; 331 Setting<bool> accelerate_astc{true, "accelerate_astc"};
329 Setting<bool> use_vsync{true, "use_vsync"}; 332 Setting<bool> use_vsync{true, "use_vsync"};
330 Setting<bool> disable_fps_limit{false, "disable_fps_limit"}; 333 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
331 Setting<bool> use_assembly_shaders{false, "use_assembly_shaders"}; 334 Setting<bool> use_assembly_shaders{false, "use_assembly_shaders"};
332 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 335 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
333 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 336 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
diff --git a/src/common/thread_worker.cpp b/src/common/thread_worker.cpp
deleted file mode 100644
index 8f9bf447a..000000000
--- a/src/common/thread_worker.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/thread.h"
6#include "common/thread_worker.h"
7
8namespace Common {
9
10ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) {
11 for (std::size_t i = 0; i < num_workers; ++i)
12 threads.emplace_back([this, thread_name{std::string{name}}] {
13 Common::SetCurrentThreadName(thread_name.c_str());
14
15 // Wait for first request
16 {
17 std::unique_lock lock{queue_mutex};
18 condition.wait(lock, [this] { return stop || !requests.empty(); });
19 }
20
21 while (true) {
22 std::function<void()> task;
23
24 {
25 std::unique_lock lock{queue_mutex};
26 condition.wait(lock, [this] { return stop || !requests.empty(); });
27 if (stop || requests.empty()) {
28 return;
29 }
30 task = std::move(requests.front());
31 requests.pop();
32 }
33
34 task();
35 }
36 });
37}
38
39ThreadWorker::~ThreadWorker() {
40 {
41 std::unique_lock lock{queue_mutex};
42 stop = true;
43 }
44 condition.notify_all();
45 for (std::thread& thread : threads) {
46 thread.join();
47 }
48}
49
50void ThreadWorker::QueueWork(std::function<void()>&& work) {
51 {
52 std::unique_lock lock{queue_mutex};
53 requests.emplace(work);
54 }
55 condition.notify_one();
56}
57
58} // namespace Common
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h
index f1859971f..8272985ff 100644
--- a/src/common/thread_worker.h
+++ b/src/common/thread_worker.h
@@ -7,24 +7,110 @@
7#include <atomic> 7#include <atomic>
8#include <functional> 8#include <functional>
9#include <mutex> 9#include <mutex>
10#include <stop_token>
10#include <string> 11#include <string>
12#include <thread>
13#include <type_traits>
11#include <vector> 14#include <vector>
12#include <queue> 15#include <queue>
13 16
17#include "common/thread.h"
18#include "common/unique_function.h"
19
14namespace Common { 20namespace Common {
15 21
16class ThreadWorker final { 22template <class StateType = void>
23class StatefulThreadWorker {
24 static constexpr bool with_state = !std::is_same_v<StateType, void>;
25
26 struct DummyCallable {
27 int operator()() const noexcept {
28 return 0;
29 }
30 };
31
32 using Task =
33 std::conditional_t<with_state, UniqueFunction<void, StateType*>, UniqueFunction<void>>;
34 using StateMaker = std::conditional_t<with_state, std::function<StateType()>, DummyCallable>;
35
17public: 36public:
18 explicit ThreadWorker(std::size_t num_workers, const std::string& name); 37 explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {})
19 ~ThreadWorker(); 38 : workers_queued{num_workers}, thread_name{std::move(name)} {
20 void QueueWork(std::function<void()>&& work); 39 const auto lambda = [this, func](std::stop_token stop_token) {
40 Common::SetCurrentThreadName(thread_name.c_str());
41 {
42 std::conditional_t<with_state, StateType, int> state{func()};
43 while (!stop_token.stop_requested()) {
44 Task task;
45 {
46 std::unique_lock lock{queue_mutex};
47 if (requests.empty()) {
48 wait_condition.notify_all();
49 }
50 condition.wait(lock, stop_token, [this] { return !requests.empty(); });
51 if (stop_token.stop_requested()) {
52 break;
53 }
54 task = std::move(requests.front());
55 requests.pop();
56 }
57 if constexpr (with_state) {
58 task(&state);
59 } else {
60 task();
61 }
62 ++work_done;
63 }
64 }
65 ++workers_stopped;
66 wait_condition.notify_all();
67 };
68 threads.reserve(num_workers);
69 for (size_t i = 0; i < num_workers; ++i) {
70 threads.emplace_back(lambda);
71 }
72 }
73
74 StatefulThreadWorker& operator=(const StatefulThreadWorker&) = delete;
75 StatefulThreadWorker(const StatefulThreadWorker&) = delete;
76
77 StatefulThreadWorker& operator=(StatefulThreadWorker&&) = delete;
78 StatefulThreadWorker(StatefulThreadWorker&&) = delete;
79
80 void QueueWork(Task work) {
81 {
82 std::unique_lock lock{queue_mutex};
83 requests.emplace(std::move(work));
84 ++work_scheduled;
85 }
86 condition.notify_one();
87 }
88
89 void WaitForRequests(std::stop_token stop_token = {}) {
90 std::stop_callback callback(stop_token, [this] {
91 for (auto& thread : threads) {
92 thread.request_stop();
93 }
94 });
95 std::unique_lock lock{queue_mutex};
96 wait_condition.wait(lock, [this] {
97 return workers_stopped >= workers_queued || work_done >= work_scheduled;
98 });
99 }
21 100
22private: 101private:
23 std::vector<std::thread> threads; 102 std::queue<Task> requests;
24 std::queue<std::function<void()>> requests;
25 std::mutex queue_mutex; 103 std::mutex queue_mutex;
26 std::condition_variable condition; 104 std::condition_variable_any condition;
27 std::atomic_bool stop{}; 105 std::condition_variable wait_condition;
106 std::atomic<size_t> work_scheduled{};
107 std::atomic<size_t> work_done{};
108 std::atomic<size_t> workers_stopped{};
109 std::atomic<size_t> workers_queued{};
110 std::string thread_name;
111 std::vector<std::jthread> threads;
28}; 112};
29 113
114using ThreadWorker = StatefulThreadWorker<>;
115
30} // namespace Common 116} // namespace Common
diff --git a/src/common/unique_function.h b/src/common/unique_function.h
new file mode 100644
index 000000000..ca0559071
--- /dev/null
+++ b/src/common/unique_function.h
@@ -0,0 +1,62 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <utility>
9
10namespace Common {
11
12/// General purpose function wrapper similar to std::function.
13/// Unlike std::function, the captured values don't have to be copyable.
14/// This class can be moved but not copied.
15template <typename ResultType, typename... Args>
16class UniqueFunction {
17 class CallableBase {
18 public:
19 virtual ~CallableBase() = default;
20 virtual ResultType operator()(Args&&...) = 0;
21 };
22
23 template <typename Functor>
24 class Callable final : public CallableBase {
25 public:
26 Callable(Functor&& functor_) : functor{std::move(functor_)} {}
27 ~Callable() override = default;
28
29 ResultType operator()(Args&&... args) override {
30 return functor(std::forward<Args>(args)...);
31 }
32
33 private:
34 Functor functor;
35 };
36
37public:
38 UniqueFunction() = default;
39
40 template <typename Functor>
41 UniqueFunction(Functor&& functor)
42 : callable{std::make_unique<Callable<Functor>>(std::move(functor))} {}
43
44 UniqueFunction& operator=(UniqueFunction&& rhs) noexcept = default;
45 UniqueFunction(UniqueFunction&& rhs) noexcept = default;
46
47 UniqueFunction& operator=(const UniqueFunction&) = delete;
48 UniqueFunction(const UniqueFunction&) = delete;
49
50 ResultType operator()(Args&&... args) const {
51 return (*callable)(std::forward<Args>(args)...);
52 }
53
54 explicit operator bool() const noexcept {
55 return static_cast<bool>(callable);
56 }
57
58private:
59 std::unique_ptr<CallableBase> callable;
60};
61
62} // namespace Common
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 77a44f862..b0d89c539 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -150,7 +150,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
150 config.far_code_offset = 400_MiB; 150 config.far_code_offset = 400_MiB;
151 151
152 // Safe optimizations 152 // Safe optimizations
153 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) { 153 if (Settings::values.cpu_debug_mode) {
154 if (!Settings::values.cpuopt_page_tables) { 154 if (!Settings::values.cpuopt_page_tables) {
155 config.page_table = nullptr; 155 config.page_table = nullptr;
156 } 156 }
@@ -183,20 +183,28 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
183 // Unsafe optimizations 183 // Unsafe optimizations
184 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 184 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
185 config.unsafe_optimizations = true; 185 config.unsafe_optimizations = true;
186 if (Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()) { 186 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
187 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 187 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
188 } 188 }
189 if (Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()) { 189 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
190 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; 190 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
191 } 191 }
192 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr.GetValue()) { 192 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
193 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 193 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
194 } 194 }
195 if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) { 195 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
196 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 196 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
197 } 197 }
198 } 198 }
199 199
200 // Curated optimizations
201 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
202 config.unsafe_optimizations = true;
203 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
204 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
205 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
206 }
207
200 return std::make_unique<Dynarmic::A32::Jit>(config); 208 return std::make_unique<Dynarmic::A32::Jit>(config);
201} 209}
202 210
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 75332e348..bf27ffe71 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -190,7 +190,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
190 config.far_code_offset = 400_MiB; 190 config.far_code_offset = 400_MiB;
191 191
192 // Safe optimizations 192 // Safe optimizations
193 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) { 193 if (Settings::values.cpu_debug_mode) {
194 if (!Settings::values.cpuopt_page_tables) { 194 if (!Settings::values.cpuopt_page_tables) {
195 config.page_table = nullptr; 195 config.page_table = nullptr;
196 } 196 }
@@ -223,20 +223,28 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
223 // Unsafe optimizations 223 // Unsafe optimizations
224 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 224 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
225 config.unsafe_optimizations = true; 225 config.unsafe_optimizations = true;
226 if (Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()) { 226 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
227 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 227 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
228 } 228 }
229 if (Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()) { 229 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
230 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; 230 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
231 } 231 }
232 if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) { 232 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
233 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 233 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
234 } 234 }
235 if (Settings::values.cpuopt_unsafe_fastmem_check.GetValue()) { 235 if (Settings::values.cpuopt_unsafe_fastmem_check) {
236 config.fastmem_address_space_bits = 64; 236 config.fastmem_address_space_bits = 64;
237 } 237 }
238 } 238 }
239 239
240 // Curated optimizations
241 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
242 config.unsafe_optimizations = true;
243 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
244 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
245 config.fastmem_address_space_bits = 64;
246 }
247
240 return std::make_shared<Dynarmic::A64::Jit>(config); 248 return std::make_shared<Dynarmic::A64::Jit>(config);
241} 249}
242 250
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 96bc30cac..c4c012f3d 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -5,6 +5,7 @@ add_executable(tests
5 common/host_memory.cpp 5 common/host_memory.cpp
6 common/param_package.cpp 6 common/param_package.cpp
7 common/ring_buffer.cpp 7 common/ring_buffer.cpp
8 common/unique_function.cpp
8 core/core_timing.cpp 9 core/core_timing.cpp
9 core/network/network.cpp 10 core/network/network.cpp
10 tests.cpp 11 tests.cpp
diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp
new file mode 100644
index 000000000..ac9912738
--- /dev/null
+++ b/src/tests/common/unique_function.cpp
@@ -0,0 +1,108 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6
7#include <catch2/catch.hpp>
8
9#include "common/unique_function.h"
10
11namespace {
12struct Noisy {
13 Noisy() : state{"Default constructed"} {}
14 Noisy(Noisy&& rhs) noexcept : state{"Move constructed"} {
15 rhs.state = "Moved away";
16 }
17 Noisy& operator=(Noisy&& rhs) noexcept {
18 state = "Move assigned";
19 rhs.state = "Moved away";
20 }
21 Noisy(const Noisy&) : state{"Copied constructed"} {}
22 Noisy& operator=(const Noisy&) {
23 state = "Copied assigned";
24 }
25
26 std::string state;
27};
28} // Anonymous namespace
29
30TEST_CASE("UniqueFunction", "[common]") {
31 SECTION("Capture reference") {
32 int value = 0;
33 Common::UniqueFunction<void> func = [&value] { value = 5; };
34 func();
35 REQUIRE(value == 5);
36 }
37 SECTION("Capture pointer") {
38 int value = 0;
39 int* pointer = &value;
40 Common::UniqueFunction<void> func = [pointer] { *pointer = 5; };
41 func();
42 REQUIRE(value == 5);
43 }
44 SECTION("Move object") {
45 Noisy noisy;
46 REQUIRE(noisy.state == "Default constructed");
47
48 Common::UniqueFunction<void> func = [noisy = std::move(noisy)] {
49 REQUIRE(noisy.state == "Move constructed");
50 };
51 REQUIRE(noisy.state == "Moved away");
52 func();
53 }
54 SECTION("Move construct function") {
55 int value = 0;
56 Common::UniqueFunction<void> func = [&value] { value = 5; };
57 Common::UniqueFunction<void> new_func = std::move(func);
58 new_func();
59 REQUIRE(value == 5);
60 }
61 SECTION("Move assign function") {
62 int value = 0;
63 Common::UniqueFunction<void> func = [&value] { value = 5; };
64 Common::UniqueFunction<void> new_func;
65 new_func = std::move(func);
66 new_func();
67 REQUIRE(value == 5);
68 }
69 SECTION("Default construct then assign function") {
70 int value = 0;
71 Common::UniqueFunction<void> func;
72 func = [&value] { value = 5; };
73 func();
74 REQUIRE(value == 5);
75 }
76 SECTION("Pass arguments") {
77 int result = 0;
78 Common::UniqueFunction<void, int, int> func = [&result](int a, int b) { result = a + b; };
79 func(5, 4);
80 REQUIRE(result == 9);
81 }
82 SECTION("Pass arguments and return value") {
83 Common::UniqueFunction<int, int, int> func = [](int a, int b) { return a + b; };
84 REQUIRE(func(5, 4) == 9);
85 }
86 SECTION("Destructor") {
87 int num_destroyed = 0;
88 struct Foo {
89 Foo(int* num_) : num{num_} {}
90 Foo(Foo&& rhs) : num{std::exchange(rhs.num, nullptr)} {}
91 Foo(const Foo&) = delete;
92
93 ~Foo() {
94 if (num) {
95 ++*num;
96 }
97 }
98
99 int* num = nullptr;
100 };
101 Foo object{&num_destroyed};
102 {
103 Common::UniqueFunction<void> func = [object = std::move(object)] {};
104 REQUIRE(num_destroyed == 0);
105 }
106 REQUIRE(num_destroyed == 1);
107 }
108}
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
index edced69bb..9f5a54de4 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -536,7 +536,7 @@ TEST_CASE("BufferBase: Cached write downloads") {
536 REQUIRE(rasterizer.Count() == 63); 536 REQUIRE(rasterizer.Count() == 63);
537 buffer.MarkRegionAsGpuModified(c + PAGE, PAGE); 537 buffer.MarkRegionAsGpuModified(c + PAGE, PAGE);
538 int num = 0; 538 int num = 0;
539 buffer.ForEachDownloadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); 539 buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
540 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); 540 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
541 REQUIRE(num == 0); 541 REQUIRE(num == 0);
542 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); 542 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index b121d36a3..c3318095c 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -226,19 +226,24 @@ public:
226 /// Call 'func' for each CPU modified range and unmark those pages as CPU modified 226 /// Call 'func' for each CPU modified range and unmark those pages as CPU modified
227 template <typename Func> 227 template <typename Func>
228 void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) { 228 void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) {
229 ForEachModifiedRange<Type::CPU>(query_cpu_range, size, func); 229 ForEachModifiedRange<Type::CPU>(query_cpu_range, size, true, func);
230 } 230 }
231 231
232 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified 232 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified
233 template <typename Func> 233 template <typename Func>
234 void ForEachDownloadRange(VAddr query_cpu_range, u64 size, Func&& func) { 234 void ForEachDownloadRange(VAddr query_cpu_range, u64 size, bool clear, Func&& func) {
235 ForEachModifiedRange<Type::GPU>(query_cpu_range, size, func); 235 ForEachModifiedRange<Type::GPU>(query_cpu_range, size, clear, func);
236 }
237
238 template <typename Func>
239 void ForEachDownloadRangeAndClear(VAddr query_cpu_range, u64 size, Func&& func) {
240 ForEachModifiedRange<Type::GPU>(query_cpu_range, size, true, func);
236 } 241 }
237 242
238 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified 243 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified
239 template <typename Func> 244 template <typename Func>
240 void ForEachDownloadRange(Func&& func) { 245 void ForEachDownloadRange(Func&& func) {
241 ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), func); 246 ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), true, func);
242 } 247 }
243 248
244 /// Mark buffer as picked 249 /// Mark buffer as picked
@@ -415,7 +420,7 @@ private:
415 * @param func Function to call for each turned off region 420 * @param func Function to call for each turned off region
416 */ 421 */
417 template <Type type, typename Func> 422 template <Type type, typename Func>
418 void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) { 423 void ForEachModifiedRange(VAddr query_cpu_range, s64 size, bool clear, Func&& func) {
419 static_assert(type != Type::Untracked); 424 static_assert(type != Type::Untracked);
420 425
421 const s64 difference = query_cpu_range - cpu_addr; 426 const s64 difference = query_cpu_range - cpu_addr;
@@ -467,7 +472,9 @@ private:
467 bits = (bits << left_offset) >> left_offset; 472 bits = (bits << left_offset) >> left_offset;
468 473
469 const u64 current_word = state_words[word_index] & bits; 474 const u64 current_word = state_words[word_index] & bits;
470 state_words[word_index] &= ~bits; 475 if (clear) {
476 state_words[word_index] &= ~bits;
477 }
471 478
472 if constexpr (type == Type::CPU) { 479 if constexpr (type == Type::CPU) {
473 const u64 current_bits = untracked_words[word_index] & bits; 480 const u64 current_bits = untracked_words[word_index] & bits;
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index cad7f902d..502feddba 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -15,6 +15,7 @@
15#include <vector> 15#include <vector>
16 16
17#include <boost/container/small_vector.hpp> 17#include <boost/container/small_vector.hpp>
18#include <boost/icl/interval_set.hpp>
18 19
19#include "common/common_types.h" 20#include "common/common_types.h"
20#include "common/div_ceil.h" 21#include "common/div_ceil.h"
@@ -77,6 +78,9 @@ class BufferCache {
77 using Runtime = typename P::Runtime; 78 using Runtime = typename P::Runtime;
78 using Buffer = typename P::Buffer; 79 using Buffer = typename P::Buffer;
79 80
81 using IntervalSet = boost::icl::interval_set<VAddr>;
82 using IntervalType = typename IntervalSet::interval_type;
83
80 struct Empty {}; 84 struct Empty {};
81 85
82 struct OverlapResult { 86 struct OverlapResult {
@@ -148,11 +152,14 @@ public:
148 /// Return true when there are uncommitted buffers to be downloaded 152 /// Return true when there are uncommitted buffers to be downloaded
149 [[nodiscard]] bool HasUncommittedFlushes() const noexcept; 153 [[nodiscard]] bool HasUncommittedFlushes() const noexcept;
150 154
155 void AccumulateFlushes();
156
151 /// Return true when the caller should wait for async downloads 157 /// Return true when the caller should wait for async downloads
152 [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept; 158 [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
153 159
154 /// Commit asynchronous downloads 160 /// Commit asynchronous downloads
155 void CommitAsyncFlushes(); 161 void CommitAsyncFlushes();
162 void CommitAsyncFlushesHigh();
156 163
157 /// Pop asynchronous downloads 164 /// Pop asynchronous downloads
158 void PopAsyncFlushes(); 165 void PopAsyncFlushes();
@@ -160,6 +167,9 @@ public:
160 /// Return true when a CPU region is modified from the GPU 167 /// Return true when a CPU region is modified from the GPU
161 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); 168 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
162 169
170 /// Return true when a CPU region is modified from the CPU
171 [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
172
163 std::mutex mutex; 173 std::mutex mutex;
164 174
165private: 175private:
@@ -272,8 +282,6 @@ private:
272 282
273 void DeleteBuffer(BufferId buffer_id); 283 void DeleteBuffer(BufferId buffer_id);
274 284
275 void ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id);
276
277 void NotifyBufferDeletion(); 285 void NotifyBufferDeletion();
278 286
279 [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const; 287 [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const;
@@ -327,9 +335,9 @@ private:
327 335
328 std::vector<BufferId> cached_write_buffer_ids; 336 std::vector<BufferId> cached_write_buffer_ids;
329 337
330 // TODO: This data structure is not optimal and it should be reworked 338 IntervalSet uncommitted_ranges;
331 std::vector<BufferId> uncommitted_downloads; 339 IntervalSet common_ranges;
332 std::deque<std::vector<BufferId>> committed_downloads; 340 std::deque<IntervalSet> committed_ranges;
333 341
334 size_t immediate_buffer_capacity = 0; 342 size_t immediate_buffer_capacity = 0;
335 std::unique_ptr<u8[]> immediate_buffer_alloc; 343 std::unique_ptr<u8[]> immediate_buffer_alloc;
@@ -352,6 +360,7 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
352 // Ensure the first slot is used for the null buffer 360 // Ensure the first slot is used for the null buffer
353 void(slot_buffers.insert(runtime, NullBufferParams{})); 361 void(slot_buffers.insert(runtime, NullBufferParams{}));
354 deletion_iterator = slot_buffers.end(); 362 deletion_iterator = slot_buffers.end();
363 common_ranges.clear();
355} 364}
356 365
357template <class P> 366template <class P>
@@ -547,29 +556,30 @@ void BufferCache<P>::FlushCachedWrites() {
547 556
548template <class P> 557template <class P>
549bool BufferCache<P>::HasUncommittedFlushes() const noexcept { 558bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
550 return !uncommitted_downloads.empty(); 559 return !uncommitted_ranges.empty() || !committed_ranges.empty();
551} 560}
552 561
553template <class P> 562template <class P>
554bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { 563void BufferCache<P>::AccumulateFlushes() {
555 return !committed_downloads.empty() && !committed_downloads.front().empty(); 564 if (Settings::values.gpu_accuracy.GetValue() != Settings::GPUAccuracy::High) {
565 uncommitted_ranges.clear();
566 return;
567 }
568 if (uncommitted_ranges.empty()) {
569 return;
570 }
571 committed_ranges.emplace_back(std::move(uncommitted_ranges));
556} 572}
557 573
558template <class P> 574template <class P>
559void BufferCache<P>::CommitAsyncFlushes() { 575bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
560 // This is intentionally passing the value by copy 576 return false;
561 committed_downloads.push_front(uncommitted_downloads);
562 uncommitted_downloads.clear();
563} 577}
564 578
565template <class P> 579template <class P>
566void BufferCache<P>::PopAsyncFlushes() { 580void BufferCache<P>::CommitAsyncFlushesHigh() {
567 if (committed_downloads.empty()) { 581 AccumulateFlushes();
568 return; 582 if (committed_ranges.empty()) {
569 }
570 auto scope_exit_pop_download = detail::ScopeExit([this] { committed_downloads.pop_back(); });
571 const std::span<const BufferId> download_ids = committed_downloads.back();
572 if (download_ids.empty()) {
573 return; 583 return;
574 } 584 }
575 MICROPROFILE_SCOPE(GPU_DownloadMemory); 585 MICROPROFILE_SCOPE(GPU_DownloadMemory);
@@ -577,20 +587,66 @@ void BufferCache<P>::PopAsyncFlushes() {
577 boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads; 587 boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads;
578 u64 total_size_bytes = 0; 588 u64 total_size_bytes = 0;
579 u64 largest_copy = 0; 589 u64 largest_copy = 0;
580 for (const BufferId buffer_id : download_ids) { 590 for (const IntervalSet& intervals : committed_ranges) {
581 slot_buffers[buffer_id].ForEachDownloadRange([&](u64 range_offset, u64 range_size) { 591 for (auto& interval : intervals) {
582 downloads.push_back({ 592 const std::size_t size = interval.upper() - interval.lower();
583 BufferCopy{ 593 const VAddr cpu_addr = interval.lower();
584 .src_offset = range_offset, 594 ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
585 .dst_offset = total_size_bytes, 595 boost::container::small_vector<BufferCopy, 1> copies;
586 .size = range_size, 596 buffer.ForEachDownloadRangeAndClear(
587 }, 597 cpu_addr, size, [&](u64 range_offset, u64 range_size) {
588 buffer_id, 598 const VAddr buffer_addr = buffer.CpuAddr();
599 const auto add_download = [&](VAddr start, VAddr end) {
600 const u64 new_offset = start - buffer_addr;
601 const u64 new_size = end - start;
602 downloads.push_back({
603 BufferCopy{
604 .src_offset = new_offset,
605 .dst_offset = total_size_bytes,
606 .size = new_size,
607 },
608 buffer_id,
609 });
610 // Align up to avoid cache conflicts
611 constexpr u64 align = 256ULL;
612 constexpr u64 mask = ~(align - 1ULL);
613 total_size_bytes += (new_size + align - 1) & mask;
614 largest_copy = std::max(largest_copy, new_size);
615 };
616
617 const VAddr start_address = buffer_addr + range_offset;
618 const VAddr end_address = start_address + range_size;
619 const IntervalType search_interval{cpu_addr, 1};
620 auto it = common_ranges.lower_bound(search_interval);
621 if (it == common_ranges.end()) {
622 it = common_ranges.begin();
623 }
624 while (it != common_ranges.end()) {
625 VAddr inter_addr_end = it->upper();
626 VAddr inter_addr = it->lower();
627 if (inter_addr >= end_address) {
628 break;
629 }
630 if (inter_addr_end <= start_address) {
631 it++;
632 continue;
633 }
634 if (inter_addr_end > end_address) {
635 inter_addr_end = end_address;
636 }
637 if (inter_addr < start_address) {
638 inter_addr = start_address;
639 }
640 add_download(inter_addr, inter_addr_end);
641 it++;
642 }
643 const IntervalType subtract_interval{start_address, end_address};
644 common_ranges.subtract(subtract_interval);
645 });
589 }); 646 });
590 total_size_bytes += range_size; 647 }
591 largest_copy = std::max(largest_copy, range_size);
592 });
593 } 648 }
649 committed_ranges.clear();
594 if (downloads.empty()) { 650 if (downloads.empty()) {
595 return; 651 return;
596 } 652 }
@@ -623,6 +679,19 @@ void BufferCache<P>::PopAsyncFlushes() {
623} 679}
624 680
625template <class P> 681template <class P>
682void BufferCache<P>::CommitAsyncFlushes() {
683 if (Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High) {
684 CommitAsyncFlushesHigh();
685 } else {
686 uncommitted_ranges.clear();
687 committed_ranges.clear();
688 }
689}
690
691template <class P>
692void BufferCache<P>::PopAsyncFlushes() {}
693
694template <class P>
626bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { 695bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
627 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); 696 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
628 for (u64 page = addr >> PAGE_BITS; page < page_end;) { 697 for (u64 page = addr >> PAGE_BITS; page < page_end;) {
@@ -642,6 +711,25 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
642} 711}
643 712
644template <class P> 713template <class P>
714bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
715 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
716 for (u64 page = addr >> PAGE_BITS; page < page_end;) {
717 const BufferId image_id = page_table[page];
718 if (!image_id) {
719 ++page;
720 continue;
721 }
722 Buffer& buffer = slot_buffers[image_id];
723 if (buffer.IsRegionCpuModified(addr, size)) {
724 return true;
725 }
726 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
727 page = Common::DivCeil(end_addr, PAGE_SIZE);
728 }
729 return false;
730}
731
732template <class P>
645void BufferCache<P>::BindHostIndexBuffer() { 733void BufferCache<P>::BindHostIndexBuffer() {
646 Buffer& buffer = slot_buffers[index_buffer.buffer_id]; 734 Buffer& buffer = slot_buffers[index_buffer.buffer_id];
647 TouchBuffer(buffer); 735 TouchBuffer(buffer);
@@ -1010,16 +1098,16 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 s
1010 Buffer& buffer = slot_buffers[buffer_id]; 1098 Buffer& buffer = slot_buffers[buffer_id];
1011 buffer.MarkRegionAsGpuModified(cpu_addr, size); 1099 buffer.MarkRegionAsGpuModified(cpu_addr, size);
1012 1100
1013 const bool is_accuracy_high = Settings::IsGPULevelHigh(); 1101 const IntervalType base_interval{cpu_addr, cpu_addr + size};
1102 common_ranges.add(base_interval);
1103
1104 const bool is_accuracy_high =
1105 Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High;
1014 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); 1106 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
1015 if (!is_accuracy_high || !is_async) { 1107 if (!is_async && !is_accuracy_high) {
1016 return;
1017 }
1018 if (std::ranges::find(uncommitted_downloads, buffer_id) != uncommitted_downloads.end()) {
1019 // Already inserted
1020 return; 1108 return;
1021 } 1109 }
1022 uncommitted_downloads.push_back(buffer_id); 1110 uncommitted_ranges.add(base_interval);
1023} 1111}
1024 1112
1025template <class P> 1113template <class P>
@@ -1103,7 +1191,6 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
1103 if (!copies.empty()) { 1191 if (!copies.empty()) {
1104 runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies); 1192 runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies);
1105 } 1193 }
1106 ReplaceBufferDownloads(overlap_id, new_buffer_id);
1107 DeleteBuffer(overlap_id); 1194 DeleteBuffer(overlap_id);
1108} 1195}
1109 1196
@@ -1244,14 +1331,51 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
1244 boost::container::small_vector<BufferCopy, 1> copies; 1331 boost::container::small_vector<BufferCopy, 1> copies;
1245 u64 total_size_bytes = 0; 1332 u64 total_size_bytes = 0;
1246 u64 largest_copy = 0; 1333 u64 largest_copy = 0;
1247 buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) { 1334 buffer.ForEachDownloadRangeAndClear(cpu_addr, size, [&](u64 range_offset, u64 range_size) {
1248 copies.push_back(BufferCopy{ 1335 const VAddr buffer_addr = buffer.CpuAddr();
1249 .src_offset = range_offset, 1336 const auto add_download = [&](VAddr start, VAddr end) {
1250 .dst_offset = total_size_bytes, 1337 const u64 new_offset = start - buffer_addr;
1251 .size = range_size, 1338 const u64 new_size = end - start;
1252 }); 1339 copies.push_back(BufferCopy{
1253 total_size_bytes += range_size; 1340 .src_offset = new_offset,
1254 largest_copy = std::max(largest_copy, range_size); 1341 .dst_offset = total_size_bytes,
1342 .size = new_size,
1343 });
1344 // Align up to avoid cache conflicts
1345 constexpr u64 align = 256ULL;
1346 constexpr u64 mask = ~(align - 1ULL);
1347 total_size_bytes += (new_size + align - 1) & mask;
1348 largest_copy = std::max(largest_copy, new_size);
1349 };
1350
1351 const VAddr start_address = buffer_addr + range_offset;
1352 const VAddr end_address = start_address + range_size;
1353 const IntervalType search_interval{start_address - range_size, 1};
1354 auto it = common_ranges.lower_bound(search_interval);
1355 if (it == common_ranges.end()) {
1356 it = common_ranges.begin();
1357 }
1358 while (it != common_ranges.end()) {
1359 VAddr inter_addr_end = it->upper();
1360 VAddr inter_addr = it->lower();
1361 if (inter_addr >= end_address) {
1362 break;
1363 }
1364 if (inter_addr_end <= start_address) {
1365 it++;
1366 continue;
1367 }
1368 if (inter_addr_end > end_address) {
1369 inter_addr_end = end_address;
1370 }
1371 if (inter_addr < start_address) {
1372 inter_addr = start_address;
1373 }
1374 add_download(inter_addr, inter_addr_end);
1375 it++;
1376 }
1377 const IntervalType subtract_interval{start_address, end_address};
1378 common_ranges.subtract(subtract_interval);
1255 }); 1379 });
1256 if (total_size_bytes == 0) { 1380 if (total_size_bytes == 0) {
1257 return; 1381 return;
@@ -1316,18 +1440,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id) {
1316} 1440}
1317 1441
1318template <class P> 1442template <class P>
1319void BufferCache<P>::ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id) {
1320 const auto replace = [old_buffer_id, new_buffer_id](std::vector<BufferId>& buffers) {
1321 std::ranges::replace(buffers, old_buffer_id, new_buffer_id);
1322 if (auto it = std::ranges::find(buffers, new_buffer_id); it != buffers.end()) {
1323 buffers.erase(std::remove(it + 1, buffers.end(), new_buffer_id), buffers.end());
1324 }
1325 };
1326 replace(uncommitted_downloads);
1327 std::ranges::for_each(committed_downloads, replace);
1328}
1329
1330template <class P>
1331void BufferCache<P>::NotifyBufferDeletion() { 1443void BufferCache<P>::NotifyBufferDeletion() {
1332 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { 1444 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
1333 dirty_uniform_buffers.fill(~u32{0}); 1445 dirty_uniform_buffers.fill(~u32{0});
@@ -1349,15 +1461,9 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
1349 if (!cpu_addr || size == 0) { 1461 if (!cpu_addr || size == 0) {
1350 return NULL_BINDING; 1462 return NULL_BINDING;
1351 } 1463 }
1352 // HACK(Rodrigo): This is the number of bytes bound in host beyond the guest API's range.
1353 // It exists due to some games like Astral Chain operate out of bounds.
1354 // Binding the whole map range would be technically correct, but games have large maps that make
1355 // this approach unaffordable for now.
1356 static constexpr u32 arbitrary_extra_bytes = 0xc000;
1357 const u32 bytes_to_map_end = static_cast<u32>(gpu_memory.BytesToMapEnd(gpu_addr));
1358 const Binding binding{ 1464 const Binding binding{
1359 .cpu_addr = *cpu_addr, 1465 .cpu_addr = *cpu_addr,
1360 .size = std::min(size + arbitrary_extra_bytes, bytes_to_map_end), 1466 .size = size,
1361 .buffer_id = BufferId{}, 1467 .buffer_id = BufferId{},
1362 }; 1468 };
1363 return binding; 1469 return binding;
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 8b33c04ab..8d28bd884 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/cityhash.h" 5#include "common/cityhash.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "common/settings.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/memory.h" 9#include "core/memory.h"
9#include "video_core/dma_pusher.h" 10#include "video_core/dma_pusher.h"
@@ -76,8 +77,13 @@ bool DmaPusher::Step() {
76 77
77 // Push buffer non-empty, read a word 78 // Push buffer non-empty, read a word
78 command_headers.resize(command_list_header.size); 79 command_headers.resize(command_list_header.size);
79 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), 80 if (Settings::IsGPULevelHigh()) {
80 command_list_header.size * sizeof(u32)); 81 gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(),
82 command_list_header.size * sizeof(u32));
83 } else {
84 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(),
85 command_list_header.size * sizeof(u32));
86 }
81 } 87 }
82 for (std::size_t index = 0; index < command_headers.size();) { 88 for (std::size_t index = 0; index < command_headers.size();) {
83 const CommandHeader& command_header = command_headers[index]; 89 const CommandHeader& command_header = command_headers[index];
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index f055b61e9..34dc6c596 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -8,6 +8,7 @@
8#include <queue> 8#include <queue>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/settings.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "video_core/delayed_destruction_ring.h" 13#include "video_core/delayed_destruction_ring.h"
13#include "video_core/gpu.h" 14#include "video_core/gpu.h"
@@ -53,6 +54,12 @@ public:
53 delayed_destruction_ring.Tick(); 54 delayed_destruction_ring.Tick();
54 } 55 }
55 56
57 // Unlike other fences, this one doesn't
58 void SignalOrdering() {
59 std::scoped_lock lock{buffer_cache.mutex};
60 buffer_cache.AccumulateFlushes();
61 }
62
56 void SignalSemaphore(GPUVAddr addr, u32 value) { 63 void SignalSemaphore(GPUVAddr addr, u32 value) {
57 TryReleasePendingFences(); 64 TryReleasePendingFences();
58 const bool should_flush = ShouldFlush(); 65 const bool should_flush = ShouldFlush();
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 35cc561be..f317ddc2b 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -268,11 +268,13 @@ void GPU::CallPullerMethod(const MethodCall& method_call) {
268 case BufferMethods::SemaphoreAddressHigh: 268 case BufferMethods::SemaphoreAddressHigh:
269 case BufferMethods::SemaphoreAddressLow: 269 case BufferMethods::SemaphoreAddressLow:
270 case BufferMethods::SemaphoreSequence: 270 case BufferMethods::SemaphoreSequence:
271 case BufferMethods::RefCnt:
272 case BufferMethods::UnkCacheFlush: 271 case BufferMethods::UnkCacheFlush:
273 case BufferMethods::WrcacheFlush: 272 case BufferMethods::WrcacheFlush:
274 case BufferMethods::FenceValue: 273 case BufferMethods::FenceValue:
275 break; 274 break;
275 case BufferMethods::RefCnt:
276 rasterizer->SignalReference();
277 break;
276 case BufferMethods::FenceAction: 278 case BufferMethods::FenceAction:
277 ProcessFenceActionMethod(); 279 ProcessFenceActionMethod();
278 break; 280 break;
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 0cec4225b..67aef6000 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -63,6 +63,9 @@ public:
63 /// Signal a GPU based syncpoint as a fence 63 /// Signal a GPU based syncpoint as a fence
64 virtual void SignalSyncPoint(u32 value) = 0; 64 virtual void SignalSyncPoint(u32 value) = 0;
65 65
66 /// Signal a GPU based reference as point
67 virtual void SignalReference() = 0;
68
66 /// Release all pending fences. 69 /// Release all pending fences.
67 virtual void ReleaseFences() = 0; 70 virtual void ReleaseFences() = 0;
68 71
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 07ad0e205..a4ed8f68f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -634,6 +634,13 @@ void RasterizerOpenGL::SignalSyncPoint(u32 value) {
634 fence_manager.SignalSyncPoint(value); 634 fence_manager.SignalSyncPoint(value);
635} 635}
636 636
637void RasterizerOpenGL::SignalReference() {
638 if (!gpu.IsAsync()) {
639 return;
640 }
641 fence_manager.SignalOrdering();
642}
643
637void RasterizerOpenGL::ReleaseFences() { 644void RasterizerOpenGL::ReleaseFences() {
638 if (!gpu.IsAsync()) { 645 if (!gpu.IsAsync()) {
639 return; 646 return;
@@ -650,6 +657,7 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
650 657
651void RasterizerOpenGL::WaitForIdle() { 658void RasterizerOpenGL::WaitForIdle() {
652 glMemoryBarrier(GL_ALL_BARRIER_BITS); 659 glMemoryBarrier(GL_ALL_BARRIER_BITS);
660 SignalReference();
653} 661}
654 662
655void RasterizerOpenGL::FragmentBarrier() { 663void RasterizerOpenGL::FragmentBarrier() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 482efed7a..d8df71962 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -83,6 +83,7 @@ public:
83 void ModifyGPUMemory(GPUVAddr addr, u64 size) override; 83 void ModifyGPUMemory(GPUVAddr addr, u64 size) override;
84 void SignalSemaphore(GPUVAddr addr, u32 value) override; 84 void SignalSemaphore(GPUVAddr addr, u32 value) override;
85 void SignalSyncPoint(u32 value) override; 85 void SignalSyncPoint(u32 value) override;
86 void SignalReference() override;
86 void ReleaseFences() override; 87 void ReleaseFences() override;
87 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 88 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
88 void WaitForIdle() override; 89 void WaitForIdle() override;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index bd4d649cc..9ea4b6653 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -580,6 +580,13 @@ void RasterizerVulkan::SignalSyncPoint(u32 value) {
580 fence_manager.SignalSyncPoint(value); 580 fence_manager.SignalSyncPoint(value);
581} 581}
582 582
583void RasterizerVulkan::SignalReference() {
584 if (!gpu.IsAsync()) {
585 return;
586 }
587 fence_manager.SignalOrdering();
588}
589
583void RasterizerVulkan::ReleaseFences() { 590void RasterizerVulkan::ReleaseFences() {
584 if (!gpu.IsAsync()) { 591 if (!gpu.IsAsync()) {
585 return; 592 return;
@@ -612,6 +619,7 @@ void RasterizerVulkan::WaitForIdle() {
612 cmdbuf.SetEvent(event, flags); 619 cmdbuf.SetEvent(event, flags);
613 cmdbuf.WaitEvents(event, flags, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, {}, {}, {}); 620 cmdbuf.WaitEvents(event, flags, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, {}, {}, {});
614 }); 621 });
622 SignalReference();
615} 623}
616 624
617void RasterizerVulkan::FragmentBarrier() { 625void RasterizerVulkan::FragmentBarrier() {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 41459c5c5..5450ccfb5 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -75,6 +75,7 @@ public:
75 void ModifyGPUMemory(GPUVAddr addr, u64 size) override; 75 void ModifyGPUMemory(GPUVAddr addr, u64 size) override;
76 void SignalSemaphore(GPUVAddr addr, u32 value) override; 76 void SignalSemaphore(GPUVAddr addr, u32 value) override;
77 void SignalSyncPoint(u32 value) override; 77 void SignalSyncPoint(u32 value) override;
78 void SignalReference() override;
78 void ReleaseFences() override; 79 void ReleaseFences() override;
79 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 80 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
80 void WaitForIdle() override; 81 void WaitForIdle() override;
diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h
index 9fbdc1ac6..47a11cb2f 100644
--- a/src/video_core/texture_cache/types.h
+++ b/src/video_core/texture_cache/types.h
@@ -133,8 +133,8 @@ struct BufferImageCopy {
133}; 133};
134 134
135struct BufferCopy { 135struct BufferCopy {
136 size_t src_offset; 136 u64 src_offset;
137 size_t dst_offset; 137 u64 dst_offset;
138 size_t size; 138 size_t size;
139}; 139};
140 140
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 634fe66a5..f870b33b1 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -52,6 +52,9 @@ add_executable(yuzu
52 configuration/configure_debug_controller.cpp 52 configuration/configure_debug_controller.cpp
53 configuration/configure_debug_controller.h 53 configuration/configure_debug_controller.h
54 configuration/configure_debug_controller.ui 54 configuration/configure_debug_controller.ui
55 configuration/configure_debug_tab.cpp
56 configuration/configure_debug_tab.h
57 configuration/configure_debug_tab.ui
55 configuration/configure_dialog.cpp 58 configuration/configure_dialog.cpp
56 configuration/configure_dialog.h 59 configuration/configure_dialog.h
57 configuration/configure_filesystem.cpp 60 configuration/configure_filesystem.cpp
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 1a0f75373..8c71ad5c1 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -793,7 +793,13 @@ void Config::ReadPathValues() {
793void Config::ReadCpuValues() { 793void Config::ReadCpuValues() {
794 qt_config->beginGroup(QStringLiteral("Cpu")); 794 qt_config->beginGroup(QStringLiteral("Cpu"));
795 795
796 ReadGlobalSetting(Settings::values.cpu_accuracy); 796 ReadBasicSetting(Settings::values.cpu_accuracy_first_time);
797 if (Settings::values.cpu_accuracy_first_time) {
798 Settings::values.cpu_accuracy.SetValue(Settings::values.cpu_accuracy.GetDefault());
799 Settings::values.cpu_accuracy_first_time.SetValue(false);
800 } else {
801 ReadGlobalSetting(Settings::values.cpu_accuracy);
802 }
797 803
798 ReadGlobalSetting(Settings::values.cpuopt_unsafe_unfuse_fma); 804 ReadGlobalSetting(Settings::values.cpuopt_unsafe_unfuse_fma);
799 ReadGlobalSetting(Settings::values.cpuopt_unsafe_reduce_fp_error); 805 ReadGlobalSetting(Settings::values.cpuopt_unsafe_reduce_fp_error);
@@ -802,6 +808,7 @@ void Config::ReadCpuValues() {
802 ReadGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); 808 ReadGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check);
803 809
804 if (global) { 810 if (global) {
811 ReadBasicSetting(Settings::values.cpu_debug_mode);
805 ReadBasicSetting(Settings::values.cpuopt_page_tables); 812 ReadBasicSetting(Settings::values.cpuopt_page_tables);
806 ReadBasicSetting(Settings::values.cpuopt_block_linking); 813 ReadBasicSetting(Settings::values.cpuopt_block_linking);
807 ReadBasicSetting(Settings::values.cpuopt_return_stack_buffer); 814 ReadBasicSetting(Settings::values.cpuopt_return_stack_buffer);
@@ -820,7 +827,6 @@ void Config::ReadRendererValues() {
820 qt_config->beginGroup(QStringLiteral("Renderer")); 827 qt_config->beginGroup(QStringLiteral("Renderer"));
821 828
822 ReadGlobalSetting(Settings::values.renderer_backend); 829 ReadGlobalSetting(Settings::values.renderer_backend);
823 ReadBasicSetting(Settings::values.renderer_debug);
824 ReadGlobalSetting(Settings::values.vulkan_device); 830 ReadGlobalSetting(Settings::values.vulkan_device);
825 ReadGlobalSetting(Settings::values.fullscreen_mode); 831 ReadGlobalSetting(Settings::values.fullscreen_mode);
826 ReadGlobalSetting(Settings::values.aspect_ratio); 832 ReadGlobalSetting(Settings::values.aspect_ratio);
@@ -833,7 +839,6 @@ void Config::ReadRendererValues() {
833 ReadGlobalSetting(Settings::values.use_nvdec_emulation); 839 ReadGlobalSetting(Settings::values.use_nvdec_emulation);
834 ReadGlobalSetting(Settings::values.accelerate_astc); 840 ReadGlobalSetting(Settings::values.accelerate_astc);
835 ReadGlobalSetting(Settings::values.use_vsync); 841 ReadGlobalSetting(Settings::values.use_vsync);
836 ReadGlobalSetting(Settings::values.disable_fps_limit);
837 ReadGlobalSetting(Settings::values.use_assembly_shaders); 842 ReadGlobalSetting(Settings::values.use_assembly_shaders);
838 ReadGlobalSetting(Settings::values.use_asynchronous_shaders); 843 ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
839 ReadGlobalSetting(Settings::values.use_fast_gpu_time); 844 ReadGlobalSetting(Settings::values.use_fast_gpu_time);
@@ -842,6 +847,10 @@ void Config::ReadRendererValues() {
842 ReadGlobalSetting(Settings::values.bg_green); 847 ReadGlobalSetting(Settings::values.bg_green);
843 ReadGlobalSetting(Settings::values.bg_blue); 848 ReadGlobalSetting(Settings::values.bg_blue);
844 849
850 if (global) {
851 ReadBasicSetting(Settings::values.renderer_debug);
852 }
853
845 qt_config->endGroup(); 854 qt_config->endGroup();
846} 855}
847 856
@@ -1309,6 +1318,7 @@ void Config::SavePathValues() {
1309void Config::SaveCpuValues() { 1318void Config::SaveCpuValues() {
1310 qt_config->beginGroup(QStringLiteral("Cpu")); 1319 qt_config->beginGroup(QStringLiteral("Cpu"));
1311 1320
1321 WriteBasicSetting(Settings::values.cpu_accuracy_first_time);
1312 WriteSetting(QStringLiteral("cpu_accuracy"), 1322 WriteSetting(QStringLiteral("cpu_accuracy"),
1313 static_cast<u32>(Settings::values.cpu_accuracy.GetValue(global)), 1323 static_cast<u32>(Settings::values.cpu_accuracy.GetValue(global)),
1314 static_cast<u32>(Settings::values.cpu_accuracy.GetDefault()), 1324 static_cast<u32>(Settings::values.cpu_accuracy.GetDefault()),
@@ -1321,6 +1331,7 @@ void Config::SaveCpuValues() {
1321 WriteGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); 1331 WriteGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check);
1322 1332
1323 if (global) { 1333 if (global) {
1334 WriteBasicSetting(Settings::values.cpu_debug_mode);
1324 WriteBasicSetting(Settings::values.cpuopt_page_tables); 1335 WriteBasicSetting(Settings::values.cpuopt_page_tables);
1325 WriteBasicSetting(Settings::values.cpuopt_block_linking); 1336 WriteBasicSetting(Settings::values.cpuopt_block_linking);
1326 WriteBasicSetting(Settings::values.cpuopt_return_stack_buffer); 1337 WriteBasicSetting(Settings::values.cpuopt_return_stack_buffer);
@@ -1342,7 +1353,6 @@ void Config::SaveRendererValues() {
1342 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)), 1353 static_cast<u32>(Settings::values.renderer_backend.GetValue(global)),
1343 static_cast<u32>(Settings::values.renderer_backend.GetDefault()), 1354 static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
1344 Settings::values.renderer_backend.UsingGlobal()); 1355 Settings::values.renderer_backend.UsingGlobal());
1345 WriteBasicSetting(Settings::values.renderer_debug);
1346 WriteGlobalSetting(Settings::values.vulkan_device); 1356 WriteGlobalSetting(Settings::values.vulkan_device);
1347 WriteGlobalSetting(Settings::values.fullscreen_mode); 1357 WriteGlobalSetting(Settings::values.fullscreen_mode);
1348 WriteGlobalSetting(Settings::values.aspect_ratio); 1358 WriteGlobalSetting(Settings::values.aspect_ratio);
@@ -1358,7 +1368,6 @@ void Config::SaveRendererValues() {
1358 WriteGlobalSetting(Settings::values.use_nvdec_emulation); 1368 WriteGlobalSetting(Settings::values.use_nvdec_emulation);
1359 WriteGlobalSetting(Settings::values.accelerate_astc); 1369 WriteGlobalSetting(Settings::values.accelerate_astc);
1360 WriteGlobalSetting(Settings::values.use_vsync); 1370 WriteGlobalSetting(Settings::values.use_vsync);
1361 WriteGlobalSetting(Settings::values.disable_fps_limit);
1362 WriteGlobalSetting(Settings::values.use_assembly_shaders); 1371 WriteGlobalSetting(Settings::values.use_assembly_shaders);
1363 WriteGlobalSetting(Settings::values.use_asynchronous_shaders); 1372 WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
1364 WriteGlobalSetting(Settings::values.use_fast_gpu_time); 1373 WriteGlobalSetting(Settings::values.use_fast_gpu_time);
@@ -1367,6 +1376,10 @@ void Config::SaveRendererValues() {
1367 WriteGlobalSetting(Settings::values.bg_green); 1376 WriteGlobalSetting(Settings::values.bg_green);
1368 WriteGlobalSetting(Settings::values.bg_blue); 1377 WriteGlobalSetting(Settings::values.bg_blue);
1369 1378
1379 if (global) {
1380 WriteBasicSetting(Settings::values.renderer_debug);
1381 }
1382
1370 qt_config->endGroup(); 1383 qt_config->endGroup();
1371} 1384}
1372 1385
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index f92c3aff3..fca9aed5f 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -41,7 +41,7 @@
41 <item> 41 <item>
42 <widget class="QTabWidget" name="tabWidget"> 42 <widget class="QTabWidget" name="tabWidget">
43 <property name="currentIndex"> 43 <property name="currentIndex">
44 <number>0</number> 44 <number>11</number>
45 </property> 45 </property>
46 <widget class="ConfigureGeneral" name="generalTab"> 46 <widget class="ConfigureGeneral" name="generalTab">
47 <property name="accessibleName"> 47 <property name="accessibleName">
@@ -107,14 +107,6 @@
107 <string>CPU</string> 107 <string>CPU</string>
108 </attribute> 108 </attribute>
109 </widget> 109 </widget>
110 <widget class="ConfigureCpuDebug" name="cpuDebugTab">
111 <property name="accessibleName">
112 <string>Debug</string>
113 </property>
114 <attribute name="title">
115 <string>Debug</string>
116 </attribute>
117 </widget>
118 <widget class="ConfigureGraphics" name="graphicsTab"> 110 <widget class="ConfigureGraphics" name="graphicsTab">
119 <property name="accessibleName"> 111 <property name="accessibleName">
120 <string>Graphics</string> 112 <string>Graphics</string>
@@ -139,7 +131,7 @@
139 <string>Audio</string> 131 <string>Audio</string>
140 </attribute> 132 </attribute>
141 </widget> 133 </widget>
142 <widget class="ConfigureDebug" name="debugTab"> 134 <widget class="ConfigureDebugTab" name="debugTab">
143 <property name="accessibleName"> 135 <property name="accessibleName">
144 <string>Debug</string> 136 <string>Debug</string>
145 </property> 137 </property>
@@ -208,24 +200,12 @@
208 <container>1</container> 200 <container>1</container>
209 </customwidget> 201 </customwidget>
210 <customwidget> 202 <customwidget>
211 <class>ConfigureDebug</class>
212 <extends>QWidget</extends>
213 <header>configuration/configure_debug.h</header>
214 <container>1</container>
215 </customwidget>
216 <customwidget>
217 <class>ConfigureCpu</class> 203 <class>ConfigureCpu</class>
218 <extends>QWidget</extends> 204 <extends>QWidget</extends>
219 <header>configuration/configure_cpu.h</header> 205 <header>configuration/configure_cpu.h</header>
220 <container>1</container> 206 <container>1</container>
221 </customwidget> 207 </customwidget>
222 <customwidget> 208 <customwidget>
223 <class>ConfigureCpuDebug</class>
224 <extends>QWidget</extends>
225 <header>configuration/configure_cpu_debug.h</header>
226 <container>1</container>
227 </customwidget>
228 <customwidget>
229 <class>ConfigureGraphics</class> 209 <class>ConfigureGraphics</class>
230 <extends>QWidget</extends> 210 <extends>QWidget</extends>
231 <header>configuration/configure_graphics.h</header> 211 <header>configuration/configure_graphics.h</header>
@@ -267,6 +247,12 @@
267 <header>configuration/configure_service.h</header> 247 <header>configuration/configure_service.h</header>
268 <container>1</container> 248 <container>1</container>
269 </customwidget> 249 </customwidget>
250 <customwidget>
251 <class>ConfigureDebugTab</class>
252 <extends>QWidget</extends>
253 <header>configuration/configure_debug_tab.h</header>
254 <container>1</container>
255 </customwidget>
270 </customwidgets> 256 </customwidgets>
271 <resources/> 257 <resources/>
272 <connections> 258 <connections>
@@ -275,12 +261,32 @@
275 <signal>accepted()</signal> 261 <signal>accepted()</signal>
276 <receiver>ConfigureDialog</receiver> 262 <receiver>ConfigureDialog</receiver>
277 <slot>accept()</slot> 263 <slot>accept()</slot>
264 <hints>
265 <hint type="sourcelabel">
266 <x>20</x>
267 <y>20</y>
268 </hint>
269 <hint type="destinationlabel">
270 <x>20</x>
271 <y>20</y>
272 </hint>
273 </hints>
278 </connection> 274 </connection>
279 <connection> 275 <connection>
280 <sender>buttonBox</sender> 276 <sender>buttonBox</sender>
281 <signal>rejected()</signal> 277 <signal>rejected()</signal>
282 <receiver>ConfigureDialog</receiver> 278 <receiver>ConfigureDialog</receiver>
283 <slot>reject()</slot> 279 <slot>reject()</slot>
280 <hints>
281 <hint type="sourcelabel">
282 <x>20</x>
283 <y>20</y>
284 </hint>
285 <hint type="destinationlabel">
286 <x>20</x>
287 <y>20</y>
288 </hint>
289 </hints>
284 </connection> 290 </connection>
285 </connections> 291 </connections>
286</ui> 292</ui>
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index 13db2ba98..8d7171487 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -20,8 +20,6 @@ ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::Config
20 20
21 SetConfiguration(); 21 SetConfiguration();
22 22
23 connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this,
24 &ConfigureCpu::AccuracyUpdated);
25 connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this, 23 connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this,
26 &ConfigureCpu::UpdateGroup); 24 &ConfigureCpu::UpdateGroup);
27} 25}
@@ -58,20 +56,6 @@ void ConfigureCpu::SetConfiguration() {
58 UpdateGroup(ui->accuracy->currentIndex()); 56 UpdateGroup(ui->accuracy->currentIndex());
59} 57}
60 58
61void ConfigureCpu::AccuracyUpdated(int index) {
62 if (Settings::IsConfiguringGlobal() &&
63 static_cast<Settings::CPUAccuracy>(index) == Settings::CPUAccuracy::DebugMode) {
64 const auto result = QMessageBox::warning(this, tr("Setting CPU to Debug Mode"),
65 tr("CPU Debug Mode is only intended for developer "
66 "use. Are you sure you want to enable this?"),
67 QMessageBox::Yes | QMessageBox::No);
68 if (result == QMessageBox::No) {
69 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate));
70 UpdateGroup(static_cast<int>(Settings::CPUAccuracy::Accurate));
71 }
72 }
73}
74
75void ConfigureCpu::UpdateGroup(int index) { 59void ConfigureCpu::UpdateGroup(int index) {
76 if (!Settings::IsConfiguringGlobal()) { 60 if (!Settings::IsConfiguringGlobal()) {
77 index -= ConfigurationShared::USE_GLOBAL_OFFSET; 61 index -= ConfigurationShared::USE_GLOBAL_OFFSET;
@@ -134,8 +118,6 @@ void ConfigureCpu::SetupPerGameUI() {
134 ConfigurationShared::SetColoredComboBox( 118 ConfigurationShared::SetColoredComboBox(
135 ui->accuracy, ui->widget_accuracy, 119 ui->accuracy, ui->widget_accuracy,
136 static_cast<u32>(Settings::values.cpu_accuracy.GetValue(true))); 120 static_cast<u32>(Settings::values.cpu_accuracy.GetValue(true)));
137 ui->accuracy->removeItem(static_cast<u32>(Settings::CPUAccuracy::DebugMode) +
138 ConfigurationShared::USE_GLOBAL_OFFSET);
139 121
140 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_unfuse_fma, 122 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_unfuse_fma,
141 Settings::values.cpuopt_unsafe_unfuse_fma, 123 Settings::values.cpuopt_unsafe_unfuse_fma,
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h
index b2b5f1671..154931482 100644
--- a/src/yuzu/configuration/configure_cpu.h
+++ b/src/yuzu/configuration/configure_cpu.h
@@ -29,7 +29,6 @@ private:
29 void changeEvent(QEvent* event) override; 29 void changeEvent(QEvent* event) override;
30 void RetranslateUI(); 30 void RetranslateUI();
31 31
32 void AccuracyUpdated(int index);
33 void UpdateGroup(int index); 32 void UpdateGroup(int index);
34 33
35 void SetConfiguration(); 34 void SetConfiguration();
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index 0e296d4e5..5b9457faf 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>448</width>
10 <height>321</height> 10 <height>433</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -17,7 +17,7 @@
17 <item> 17 <item>
18 <layout class="QVBoxLayout"> 18 <layout class="QVBoxLayout">
19 <item> 19 <item>
20 <widget class="QGroupBox"> 20 <widget class="QGroupBox" name="groupBox">
21 <property name="title"> 21 <property name="title">
22 <string>General</string> 22 <string>General</string>
23 </property> 23 </property>
@@ -36,17 +36,17 @@
36 <widget class="QComboBox" name="accuracy"> 36 <widget class="QComboBox" name="accuracy">
37 <item> 37 <item>
38 <property name="text"> 38 <property name="text">
39 <string>Accurate</string> 39 <string>Auto</string>
40 </property> 40 </property>
41 </item> 41 </item>
42 <item> 42 <item>
43 <property name="text"> 43 <property name="text">
44 <string>Unsafe</string> 44 <string>Accurate</string>
45 </property> 45 </property>
46 </item> 46 </item>
47 <item> 47 <item>
48 <property name="text"> 48 <property name="text">
49 <string>Enable Debug Mode</string> 49 <string>Unsafe</string>
50 </property> 50 </property>
51 </item> 51 </item>
52 </widget> 52 </widget>
@@ -57,7 +57,7 @@
57 <item> 57 <item>
58 <widget class="QLabel" name="label_recommended_accuracy"> 58 <widget class="QLabel" name="label_recommended_accuracy">
59 <property name="text"> 59 <property name="text">
60 <string>We recommend setting accuracy to &quot;Accurate&quot;.</string> 60 <string>We recommend setting accuracy to &quot;Auto&quot;.</string>
61 </property> 61 </property>
62 <property name="wordWrap"> 62 <property name="wordWrap">
63 <bool>false</bool> 63 <bool>false</bool>
diff --git a/src/yuzu/configuration/configure_cpu_debug.h b/src/yuzu/configuration/configure_cpu_debug.h
index 10de55099..1b0d8050c 100644
--- a/src/yuzu/configuration/configure_cpu_debug.h
+++ b/src/yuzu/configuration/configure_cpu_debug.h
@@ -6,7 +6,6 @@
6 6
7#include <memory> 7#include <memory>
8#include <QWidget> 8#include <QWidget>
9#include "common/settings.h"
10 9
11namespace Ui { 10namespace Ui {
12class ConfigureCpuDebug; 11class ConfigureCpuDebug;
diff --git a/src/yuzu/configuration/configure_cpu_debug.ui b/src/yuzu/configuration/configure_cpu_debug.ui
index c43f89a5a..abf469b55 100644
--- a/src/yuzu/configuration/configure_cpu_debug.ui
+++ b/src/yuzu/configuration/configure_cpu_debug.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>592</width>
10 <height>321</height> 10 <height>503</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -17,140 +17,132 @@
17 <item> 17 <item>
18 <layout class="QVBoxLayout"> 18 <layout class="QVBoxLayout">
19 <item> 19 <item>
20 <widget class="QGroupBox"> 20 <widget class="QGroupBox" name="groupBox">
21 <property name="title"> 21 <property name="title">
22 <string>Toggle CPU Optimizations</string> 22 <string>Toggle CPU Optimizations</string>
23 </property> 23 </property>
24 <layout class="QVBoxLayout"> 24 <layout class="QVBoxLayout">
25 <item> 25 <item>
26 <widget class="QLabel"> 26 <widget class="QLabel" name="label">
27 <property name="wordWrap">
28 <bool>1</bool>
29 </property>
30 <property name="text"> 27 <property name="text">
31 <string> 28 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;For debugging only.&lt;/span&gt;&lt;br/&gt;If you're not sure what these do, keep all of these enabled. &lt;br/&gt;These settings, when disabled, only take effect when CPU Debugging is enabled. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
32 &lt;div&gt; 29 </property>
33 &lt;b&gt;For debugging only.&lt;/b&gt; 30 <property name="wordWrap">
34 &lt;br&gt; 31 <bool>false</bool>
35 If you're not sure what these do, keep all of these enabled.
36 &lt;br&gt;
37 These settings, when disabled, only take effect when CPU Accuracy is "Debug Mode".
38 &lt;/div&gt;
39 </string>
40 </property> 32 </property>
41 </widget> 33 </widget>
42 </item> 34 </item>
43 <item> 35 <item>
44 <widget class="QCheckBox" name="cpuopt_page_tables"> 36 <widget class="QCheckBox" name="cpuopt_page_tables">
45 <property name="text">
46 <string>Enable inline page tables</string>
47 </property>
48 <property name="toolTip"> 37 <property name="toolTip">
49 <string> 38 <string>
50 &lt;div style="white-space: nowrap"&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt; 39 &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
51 &lt;div style="white-space: nowrap"&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt; 40 &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
52 &lt;div style="white-space: nowrap"&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt; 41 &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
53 </string> 42 </string>
54 </property> 43 </property>
44 <property name="text">
45 <string>Enable inline page tables</string>
46 </property>
55 </widget> 47 </widget>
56 </item> 48 </item>
57 <item> 49 <item>
58 <widget class="QCheckBox" name="cpuopt_block_linking"> 50 <widget class="QCheckBox" name="cpuopt_block_linking">
59 <property name="text">
60 <string>Enable block linking</string>
61 </property>
62 <property name="toolTip"> 51 <property name="toolTip">
63 <string> 52 <string>
64 &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt; 53 &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
65 </string> 54 </string>
66 </property> 55 </property>
56 <property name="text">
57 <string>Enable block linking</string>
58 </property>
67 </widget> 59 </widget>
68 </item> 60 </item>
69 <item> 61 <item>
70 <widget class="QCheckBox" name="cpuopt_return_stack_buffer"> 62 <widget class="QCheckBox" name="cpuopt_return_stack_buffer">
71 <property name="text">
72 <string>Enable return stack buffer</string>
73 </property>
74 <property name="toolTip"> 63 <property name="toolTip">
75 <string> 64 <string>
76 &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt; 65 &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
77 </string> 66 </string>
78 </property> 67 </property>
68 <property name="text">
69 <string>Enable return stack buffer</string>
70 </property>
79 </widget> 71 </widget>
80 </item> 72 </item>
81 <item> 73 <item>
82 <widget class="QCheckBox" name="cpuopt_fast_dispatcher"> 74 <widget class="QCheckBox" name="cpuopt_fast_dispatcher">
83 <property name="text">
84 <string>Enable fast dispatcher</string>
85 </property>
86 <property name="toolTip"> 75 <property name="toolTip">
87 <string> 76 <string>
88 &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt; 77 &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
89 </string> 78 </string>
90 </property> 79 </property>
80 <property name="text">
81 <string>Enable fast dispatcher</string>
82 </property>
91 </widget> 83 </widget>
92 </item> 84 </item>
93 <item> 85 <item>
94 <widget class="QCheckBox" name="cpuopt_context_elimination"> 86 <widget class="QCheckBox" name="cpuopt_context_elimination">
95 <property name="text">
96 <string>Enable context elimination</string>
97 </property>
98 <property name="toolTip"> 87 <property name="toolTip">
99 <string> 88 <string>
100 &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt; 89 &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
101 </string> 90 </string>
102 </property> 91 </property>
92 <property name="text">
93 <string>Enable context elimination</string>
94 </property>
103 </widget> 95 </widget>
104 </item> 96 </item>
105 <item> 97 <item>
106 <widget class="QCheckBox" name="cpuopt_const_prop"> 98 <widget class="QCheckBox" name="cpuopt_const_prop">
107 <property name="text">
108 <string>Enable constant propagation</string>
109 </property>
110 <property name="toolTip"> 99 <property name="toolTip">
111 <string> 100 <string>
112 &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt; 101 &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
113 </string> 102 </string>
114 </property> 103 </property>
104 <property name="text">
105 <string>Enable constant propagation</string>
106 </property>
115 </widget> 107 </widget>
116 </item> 108 </item>
117 <item> 109 <item>
118 <widget class="QCheckBox" name="cpuopt_misc_ir"> 110 <widget class="QCheckBox" name="cpuopt_misc_ir">
119 <property name="text">
120 <string>Enable miscellaneous optimizations</string>
121 </property>
122 <property name="toolTip"> 111 <property name="toolTip">
123 <string> 112 <string>
124 &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt; 113 &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
125 </string> 114 </string>
126 </property> 115 </property>
116 <property name="text">
117 <string>Enable miscellaneous optimizations</string>
118 </property>
127 </widget> 119 </widget>
128 </item> 120 </item>
129 <item> 121 <item>
130 <widget class="QCheckBox" name="cpuopt_reduce_misalign_checks"> 122 <widget class="QCheckBox" name="cpuopt_reduce_misalign_checks">
131 <property name="text">
132 <string>Enable misalignment check reduction</string>
133 </property>
134 <property name="toolTip"> 123 <property name="toolTip">
135 <string> 124 <string>
136 &lt;div style="white-space: nowrap"&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt; 125 &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
137 &lt;div style="white-space: nowrap"&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt; 126 &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
138 </string> 127 </string>
139 </property> 128 </property>
129 <property name="text">
130 <string>Enable misalignment check reduction</string>
131 </property>
140 </widget> 132 </widget>
141 </item> 133 </item>
142 <item> 134 <item>
143 <widget class="QCheckBox" name="cpuopt_fastmem"> 135 <widget class="QCheckBox" name="cpuopt_fastmem">
144 <property name="text">
145 <string>Enable Host MMU Emulation</string>
146 </property>
147 <property name="toolTip"> 136 <property name="toolTip">
148 <string> 137 <string>
149 &lt;div style="white-space: nowrap"&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt; 138 &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
150 &lt;div style="white-space: nowrap"&gt;Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU.&lt;/div&gt; 139 &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU.&lt;/div&gt;
151 &lt;div style="white-space: nowrap"&gt;Disabling this forces all memory accesses to use Software MMU Emulation.&lt;/div&gt; 140 &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to use Software MMU Emulation.&lt;/div&gt;
152 </string> 141 </string>
153 </property> 142 </property>
143 <property name="text">
144 <string>Enable Host MMU Emulation</string>
145 </property>
154 </widget> 146 </widget>
155 </item> 147 </item>
156 </layout> 148 </layout>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index cbe45a305..8fceb3878 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -43,6 +43,8 @@ void ConfigureDebug::SetConfiguration() {
43 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue()); 43 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
44 ui->enable_graphics_debugging->setEnabled(runtime_lock); 44 ui->enable_graphics_debugging->setEnabled(runtime_lock);
45 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); 45 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
46 ui->enable_cpu_debugging->setEnabled(runtime_lock);
47 ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
46 ui->disable_macro_jit->setEnabled(runtime_lock); 48 ui->disable_macro_jit->setEnabled(runtime_lock);
47 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue()); 49 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
48 ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue()); 50 ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
@@ -58,6 +60,7 @@ void ConfigureDebug::ApplyConfiguration() {
58 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); 60 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
59 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); 61 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
60 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); 62 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
63 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
61 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 64 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
62 Settings::values.extended_logging = ui->extended_logging->isChecked(); 65 Settings::values.extended_logging = ui->extended_logging->isChecked();
63 Debugger::ToggleConsole(); 66 Debugger::ToggleConsole();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index c8087542f..1260ad6f0 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>400</width>
10 <height>486</height> 10 <height>777</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -192,34 +192,41 @@
192 </property> 192 </property>
193 </widget> 193 </widget>
194 </item> 194 </item>
195 <item>
196 <widget class="QCheckBox" name="use_debug_asserts">
197 <property name="text">
198 <string>Enable Debug Asserts</string>
199 </property>
200 </widget>
201 </item>
202 <item>
203 <widget class="QCheckBox" name="use_auto_stub">
204 <property name="text">
205 <string>Enable Auto-Stub</string>
206 </property>
207 </widget>
208 </item>
209 <item> 195 <item>
210 <widget class="QLabel" name="label_5"> 196 <widget class="QCheckBox" name="enable_cpu_debugging">
211 <property name="font"> 197 <property name="text">
212 <font> 198 <string>Enable CPU Debugging</string>
213 <italic>true</italic> 199 </property>
214 </font> 200 </widget>
215 </property> 201 </item>
216 <property name="text"> 202 <item>
217 <string>This will be reset automatically when yuzu closes.</string> 203 <widget class="QCheckBox" name="use_debug_asserts">
218 </property> 204 <property name="text">
219 <property name="indent"> 205 <string>Enable Debug Asserts</string>
220 <number>20</number> 206 </property>
221 </property> 207 </widget>
222 </widget> 208 </item>
209 <item>
210 <widget class="QCheckBox" name="use_auto_stub">
211 <property name="text">
212 <string>Enable Auto-Stub</string>
213 </property>
214 </widget>
215 </item>
216 <item>
217 <widget class="QLabel" name="label_5">
218 <property name="font">
219 <font>
220 <italic>true</italic>
221 </font>
222 </property>
223 <property name="text">
224 <string>This will be reset automatically when yuzu closes.</string>
225 </property>
226 <property name="indent">
227 <number>20</number>
228 </property>
229 </widget>
223 </item> 230 </item>
224 </layout> 231 </layout>
225 </widget> 232 </widget>
diff --git a/src/yuzu/configuration/configure_debug_tab.cpp b/src/yuzu/configuration/configure_debug_tab.cpp
new file mode 100644
index 000000000..67d369249
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_tab.cpp
@@ -0,0 +1,38 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "ui_configure_debug_tab.h"
6#include "yuzu/configuration/configure_debug_tab.h"
7
8ConfigureDebugTab::ConfigureDebugTab(QWidget* parent)
9 : QWidget(parent), ui(new Ui::ConfigureDebugTab) {
10 ui->setupUi(this);
11
12 SetConfiguration();
13}
14
15ConfigureDebugTab::~ConfigureDebugTab() = default;
16
17void ConfigureDebugTab::ApplyConfiguration() {
18 ui->debugTab->ApplyConfiguration();
19 ui->cpuDebugTab->ApplyConfiguration();
20}
21
22void ConfigureDebugTab::SetCurrentIndex(int index) {
23 ui->tabWidget->setCurrentIndex(index);
24}
25
26void ConfigureDebugTab::changeEvent(QEvent* event) {
27 if (event->type() == QEvent::LanguageChange) {
28 RetranslateUI();
29 }
30
31 QWidget::changeEvent(event);
32}
33
34void ConfigureDebugTab::RetranslateUI() {
35 ui->retranslateUi(this);
36}
37
38void ConfigureDebugTab::SetConfiguration() {}
diff --git a/src/yuzu/configuration/configure_debug_tab.h b/src/yuzu/configuration/configure_debug_tab.h
new file mode 100644
index 000000000..0a96d43d0
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_tab.h
@@ -0,0 +1,32 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QWidget>
9
10namespace Ui {
11class ConfigureDebugTab;
12}
13
14class ConfigureDebugTab : public QWidget {
15 Q_OBJECT
16
17public:
18 explicit ConfigureDebugTab(QWidget* parent = nullptr);
19 ~ConfigureDebugTab() override;
20
21 void ApplyConfiguration();
22
23 void SetCurrentIndex(int index);
24
25private:
26 void changeEvent(QEvent* event) override;
27 void RetranslateUI();
28
29 void SetConfiguration();
30
31 std::unique_ptr<Ui::ConfigureDebugTab> ui;
32};
diff --git a/src/yuzu/configuration/configure_debug_tab.ui b/src/yuzu/configuration/configure_debug_tab.ui
new file mode 100644
index 000000000..7dc6dd704
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_tab.ui
@@ -0,0 +1,52 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureDebugTab</class>
4 <widget class="QWidget" name="ConfigureDebugTab">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>320</width>
10 <height>240</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QTabWidget" name="tabWidget">
19 <property name="currentIndex">
20 <number>1</number>
21 </property>
22 <widget class="ConfigureDebug" name="debugTab">
23 <attribute name="title">
24 <string>General</string>
25 </attribute>
26 </widget>
27 <widget class="ConfigureCpuDebug" name="cpuDebugTab">
28 <attribute name="title">
29 <string>CPU</string>
30 </attribute>
31 </widget>
32 </widget>
33 </item>
34 </layout>
35 </widget>
36 <customwidgets>
37 <customwidget>
38 <class>ConfigureDebug</class>
39 <extends>QWidget</extends>
40 <header>configuration/configure_debug.h</header>
41 <container>1</container>
42 </customwidget>
43 <customwidget>
44 <class>ConfigureCpuDebug</class>
45 <extends>QWidget</extends>
46 <header>configuration/configure_cpu_debug.h</header>
47 <container>1</container>
48 </customwidget>
49 </customwidgets>
50 <resources/>
51 <connections/>
52</ui>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 371bc01b1..bc009b6b3 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -8,6 +8,7 @@
8#include <QListWidgetItem> 8#include <QListWidgetItem>
9#include <QPushButton> 9#include <QPushButton>
10#include <QSignalBlocker> 10#include <QSignalBlocker>
11#include <QTabWidget>
11#include "common/settings.h" 12#include "common/settings.h"
12#include "core/core.h" 13#include "core/core.h"
13#include "ui_configure.h" 14#include "ui_configure.h"
@@ -32,6 +33,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
32 SetConfiguration(); 33 SetConfiguration();
33 PopulateSelectionList(); 34 PopulateSelectionList();
34 35
36 connect(ui->tabWidget, &QTabWidget::currentChanged, this,
37 [this]() { ui->debugTab->SetCurrentIndex(0); });
35 connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged); 38 connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged);
36 connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, 39 connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
37 &ConfigureDialog::UpdateVisibleTabs); 40 &ConfigureDialog::UpdateVisibleTabs);
@@ -59,7 +62,6 @@ void ConfigureDialog::ApplyConfiguration() {
59 ui->inputTab->ApplyConfiguration(); 62 ui->inputTab->ApplyConfiguration();
60 ui->hotkeysTab->ApplyConfiguration(registry); 63 ui->hotkeysTab->ApplyConfiguration(registry);
61 ui->cpuTab->ApplyConfiguration(); 64 ui->cpuTab->ApplyConfiguration();
62 ui->cpuDebugTab->ApplyConfiguration();
63 ui->graphicsTab->ApplyConfiguration(); 65 ui->graphicsTab->ApplyConfiguration();
64 ui->graphicsAdvancedTab->ApplyConfiguration(); 66 ui->graphicsAdvancedTab->ApplyConfiguration();
65 ui->audioTab->ApplyConfiguration(); 67 ui->audioTab->ApplyConfiguration();
@@ -102,7 +104,7 @@ void ConfigureDialog::PopulateSelectionList() {
102 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ 104 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
103 {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}}, 105 {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
104 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, 106 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
105 {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}}, 107 {tr("CPU"), {ui->cpuTab}},
106 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, 108 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
107 {tr("Audio"), {ui->audioTab}}, 109 {tr("Audio"), {ui->audioTab}},
108 {tr("Controls"), ui->inputTab->GetSubTabs()}}, 110 {tr("Controls"), ui->inputTab->GetSubTabs()}},
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 8d13c9857..a9e611125 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -28,7 +28,6 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
29 29
30 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); 30 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
31 ui->disable_fps_limit->setChecked(Settings::values.disable_fps_limit.GetValue());
32 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); 31 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
33 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 32 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
34 ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue()); 33 ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue());
@@ -59,8 +58,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
59 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, 58 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
60 ui->anisotropic_filtering_combobox); 59 ui->anisotropic_filtering_combobox);
61 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); 60 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync);
62 ConfigurationShared::ApplyPerGameSetting(&Settings::values.disable_fps_limit,
63 ui->disable_fps_limit, disable_fps_limit);
64 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, 61 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders,
65 ui->use_assembly_shaders, use_assembly_shaders); 62 ui->use_assembly_shaders, use_assembly_shaders);
66 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, 63 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
@@ -103,7 +100,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
103 if (Settings::IsConfiguringGlobal()) { 100 if (Settings::IsConfiguringGlobal()) {
104 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); 101 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
105 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); 102 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
106 ui->disable_fps_limit->setEnabled(Settings::values.disable_fps_limit.UsingGlobal());
107 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); 103 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
108 ui->use_asynchronous_shaders->setEnabled( 104 ui->use_asynchronous_shaders->setEnabled(
109 Settings::values.use_asynchronous_shaders.UsingGlobal()); 105 Settings::values.use_asynchronous_shaders.UsingGlobal());
@@ -116,8 +112,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
116 } 112 }
117 113
118 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync); 114 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync);
119 ConfigurationShared::SetColoredTristate(ui->disable_fps_limit,
120 Settings::values.disable_fps_limit, disable_fps_limit);
121 ConfigurationShared::SetColoredTristate( 115 ConfigurationShared::SetColoredTristate(
122 ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders); 116 ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders);
123 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, 117 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index 6ac5f20ec..9148aacf2 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -35,7 +35,6 @@ private:
35 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; 35 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
36 36
37 ConfigurationShared::CheckState use_vsync; 37 ConfigurationShared::CheckState use_vsync;
38 ConfigurationShared::CheckState disable_fps_limit;
39 ConfigurationShared::CheckState use_assembly_shaders; 38 ConfigurationShared::CheckState use_assembly_shaders;
40 ConfigurationShared::CheckState use_asynchronous_shaders; 39 ConfigurationShared::CheckState use_asynchronous_shaders;
41 ConfigurationShared::CheckState use_fast_gpu_time; 40 ConfigurationShared::CheckState use_fast_gpu_time;
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 18c43629e..ad0840355 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -77,24 +77,6 @@
77 </widget> 77 </widget>
78 </item> 78 </item>
79 <item> 79 <item>
80 <widget class="QCheckBox" name="disable_fps_limit">
81 <property name="enabled">
82 <bool>true</bool>
83 </property>
84 <property name="toolTip">
85 <string>
86 &lt;html&gt;&lt;head/&gt;&lt;body&gt;
87 &lt;p&gt;Presents guest frames as they become available, disabling the FPS limit in most titles.&lt;/p&gt;
88 &lt;p&gt;NOTE: Will cause instabilities.&lt;/p&gt;
89 &lt;/body&gt;&lt;/html&gt;
90 </string>
91 </property>
92 <property name="text">
93 <string>Disable framerate limit (experimental)</string>
94 </property>
95 </widget>
96 </item>
97 <item>
98 <widget class="QCheckBox" name="use_assembly_shaders"> 80 <widget class="QCheckBox" name="use_assembly_shaders">
99 <property name="toolTip"> 81 <property name="toolTip">
100 <string>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</string> 82 <string>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</string>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 5ed3b90b8..cb9c01154 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1355,6 +1355,9 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
1355 1355
1356 ConfigureVibration::SetAllVibrationDevices(); 1356 ConfigureVibration::SetAllVibrationDevices();
1357 1357
1358 // Disable fps limit toggle when booting a new title
1359 Settings::values.disable_fps_limit.SetValue(false);
1360
1358 // Save configurations 1361 // Save configurations
1359 UpdateUISettings(); 1362 UpdateUISettings();
1360 game_list->SaveInterfaceLayout(); 1363 game_list->SaveInterfaceLayout();
@@ -2913,7 +2916,12 @@ void GMainWindow::UpdateStatusBar() {
2913 } else { 2916 } else {
2914 emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); 2917 emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
2915 } 2918 }
2916 game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); 2919 if (Settings::values.disable_fps_limit) {
2920 game_fps_label->setText(
2921 tr("Game: %1 FPS (Limit off)").arg(results.average_game_fps, 0, 'f', 0));
2922 } else {
2923 game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0));
2924 }
2917 emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); 2925 emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
2918 2926
2919 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); 2927 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue());