diff options
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 | ||
| 95 | bool IsFastmemEnabled() { | 95 | bool 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 | ||
| 33 | enum class CPUAccuracy : u32 { | 33 | enum 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 | |||
| 8 | namespace Common { | ||
| 9 | |||
| 10 | ThreadWorker::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 | |||
| 39 | ThreadWorker::~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 | |||
| 50 | void 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 | |||
| 14 | namespace Common { | 20 | namespace Common { |
| 15 | 21 | ||
| 16 | class ThreadWorker final { | 22 | template <class StateType = void> |
| 23 | class 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 | |||
| 17 | public: | 36 | public: |
| 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 | ||
| 22 | private: | 101 | private: |
| 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 | ||
| 114 | using 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 | |||
| 10 | namespace 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. | ||
| 15 | template <typename ResultType, typename... Args> | ||
| 16 | class 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 | |||
| 37 | public: | ||
| 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 | |||
| 58 | private: | ||
| 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 | |||
| 11 | namespace { | ||
| 12 | struct 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 | |||
| 30 | TEST_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 | ||
| 165 | private: | 175 | private: |
| @@ -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 | ||
| 357 | template <class P> | 366 | template <class P> |
| @@ -547,29 +556,30 @@ void BufferCache<P>::FlushCachedWrites() { | |||
| 547 | 556 | ||
| 548 | template <class P> | 557 | template <class P> |
| 549 | bool BufferCache<P>::HasUncommittedFlushes() const noexcept { | 558 | bool BufferCache<P>::HasUncommittedFlushes() const noexcept { |
| 550 | return !uncommitted_downloads.empty(); | 559 | return !uncommitted_ranges.empty() || !committed_ranges.empty(); |
| 551 | } | 560 | } |
| 552 | 561 | ||
| 553 | template <class P> | 562 | template <class P> |
| 554 | bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { | 563 | void 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 | ||
| 558 | template <class P> | 574 | template <class P> |
| 559 | void BufferCache<P>::CommitAsyncFlushes() { | 575 | bool 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 | ||
| 565 | template <class P> | 579 | template <class P> |
| 566 | void BufferCache<P>::PopAsyncFlushes() { | 580 | void 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 | ||
| 625 | template <class P> | 681 | template <class P> |
| 682 | void 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 | |||
| 691 | template <class P> | ||
| 692 | void BufferCache<P>::PopAsyncFlushes() {} | ||
| 693 | |||
| 694 | template <class P> | ||
| 626 | bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { | 695 | bool 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 | ||
| 644 | template <class P> | 713 | template <class P> |
| 714 | bool 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 | |||
| 732 | template <class P> | ||
| 645 | void BufferCache<P>::BindHostIndexBuffer() { | 733 | void 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 | ||
| 1025 | template <class P> | 1113 | template <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 | ||
| 1318 | template <class P> | 1442 | template <class P> |
| 1319 | void 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 | |||
| 1330 | template <class P> | ||
| 1331 | void BufferCache<P>::NotifyBufferDeletion() { | 1443 | void 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 | ||
| 637 | void RasterizerOpenGL::SignalReference() { | ||
| 638 | if (!gpu.IsAsync()) { | ||
| 639 | return; | ||
| 640 | } | ||
| 641 | fence_manager.SignalOrdering(); | ||
| 642 | } | ||
| 643 | |||
| 637 | void RasterizerOpenGL::ReleaseFences() { | 644 | void 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 | ||
| 651 | void RasterizerOpenGL::WaitForIdle() { | 658 | void RasterizerOpenGL::WaitForIdle() { |
| 652 | glMemoryBarrier(GL_ALL_BARRIER_BITS); | 659 | glMemoryBarrier(GL_ALL_BARRIER_BITS); |
| 660 | SignalReference(); | ||
| 653 | } | 661 | } |
| 654 | 662 | ||
| 655 | void RasterizerOpenGL::FragmentBarrier() { | 663 | void 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 | ||
| 583 | void RasterizerVulkan::SignalReference() { | ||
| 584 | if (!gpu.IsAsync()) { | ||
| 585 | return; | ||
| 586 | } | ||
| 587 | fence_manager.SignalOrdering(); | ||
| 588 | } | ||
| 589 | |||
| 583 | void RasterizerVulkan::ReleaseFences() { | 590 | void 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 | ||
| 617 | void RasterizerVulkan::FragmentBarrier() { | 625 | void 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 | ||
| 135 | struct BufferCopy { | 135 | struct 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() { | |||
| 793 | void Config::ReadCpuValues() { | 793 | void 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() { | |||
| 1309 | void Config::SaveCpuValues() { | 1318 | void 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 | ||
| 61 | void 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 | |||
| 75 | void ConfigureCpu::UpdateGroup(int index) { | 59 | void 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 "Accurate".</string> | 60 | <string>We recommend setting accuracy to "Auto".</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 | ||
| 11 | namespace Ui { | 10 | namespace Ui { |
| 12 | class ConfigureCpuDebug; | 11 | class 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><html><head/><body><p><span style=" font-weight:600;">For debugging only.</span><br/>If you're not sure what these do, keep all of these enabled. <br/>These settings, when disabled, only take effect when CPU Debugging is enabled. </p></body></html></string> |
| 32 | <div> | 29 | </property> |
| 33 | <b>For debugging only.</b> | 30 | <property name="wordWrap"> |
| 34 | <br> | 31 | <bool>false</bool> |
| 35 | If you're not sure what these do, keep all of these enabled. | ||
| 36 | <br> | ||
| 37 | These settings, when disabled, only take effect when CPU Accuracy is "Debug Mode". | ||
| 38 | </div> | ||
| 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 | <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> | 39 | <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> |
| 51 | <div style="white-space: nowrap">Enabling it inlines accesses to PageTable::pointers into emitted code.</div> | 40 | <div style="white-space: nowrap">Enabling it inlines accesses to PageTable::pointers into emitted code.</div> |
| 52 | <div style="white-space: nowrap">Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.</div> | 41 | <div style="white-space: nowrap">Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.</div> |
| 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 | <div>This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.</div> | 53 | <div>This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.</div> |
| 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 | <div>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.</div> | 65 | <div>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.</div> |
| 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 | <div>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.</div> | 77 | <div>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.</div> |
| 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 | <div>Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.</div> | 89 | <div>Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.</div> |
| 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 | <div>Enables IR optimizations that involve constant propagation.</div> | 101 | <div>Enables IR optimizations that involve constant propagation.</div> |
| 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 | <div>Enables miscellaneous IR optimizations.</div> | 113 | <div>Enables miscellaneous IR optimizations.</div> |
| 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 | <div style="white-space: nowrap">When enabled, a misalignment is only triggered when an access crosses a page boundary.</div> | 125 | <div style="white-space: nowrap">When enabled, a misalignment is only triggered when an access crosses a page boundary.</div> |
| 137 | <div style="white-space: nowrap">When disabled, a misalignment is triggered on all misaligned accesses.</div> | 126 | <div style="white-space: nowrap">When disabled, a misalignment is triggered on all misaligned accesses.</div> |
| 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 | <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> | 138 | <div style="white-space: nowrap">This optimization speeds up memory accesses by the guest program.</div> |
| 150 | <div style="white-space: nowrap">Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU.</div> | 139 | <div style="white-space: nowrap">Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU.</div> |
| 151 | <div style="white-space: nowrap">Disabling this forces all memory accesses to use Software MMU Emulation.</div> | 140 | <div style="white-space: nowrap">Disabling this forces all memory accesses to use Software MMU Emulation.</div> |
| 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 | |||
| 8 | ConfigureDebugTab::ConfigureDebugTab(QWidget* parent) | ||
| 9 | : QWidget(parent), ui(new Ui::ConfigureDebugTab) { | ||
| 10 | ui->setupUi(this); | ||
| 11 | |||
| 12 | SetConfiguration(); | ||
| 13 | } | ||
| 14 | |||
| 15 | ConfigureDebugTab::~ConfigureDebugTab() = default; | ||
| 16 | |||
| 17 | void ConfigureDebugTab::ApplyConfiguration() { | ||
| 18 | ui->debugTab->ApplyConfiguration(); | ||
| 19 | ui->cpuDebugTab->ApplyConfiguration(); | ||
| 20 | } | ||
| 21 | |||
| 22 | void ConfigureDebugTab::SetCurrentIndex(int index) { | ||
| 23 | ui->tabWidget->setCurrentIndex(index); | ||
| 24 | } | ||
| 25 | |||
| 26 | void ConfigureDebugTab::changeEvent(QEvent* event) { | ||
| 27 | if (event->type() == QEvent::LanguageChange) { | ||
| 28 | RetranslateUI(); | ||
| 29 | } | ||
| 30 | |||
| 31 | QWidget::changeEvent(event); | ||
| 32 | } | ||
| 33 | |||
| 34 | void ConfigureDebugTab::RetranslateUI() { | ||
| 35 | ui->retranslateUi(this); | ||
| 36 | } | ||
| 37 | |||
| 38 | void 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 | |||
| 10 | namespace Ui { | ||
| 11 | class ConfigureDebugTab; | ||
| 12 | } | ||
| 13 | |||
| 14 | class ConfigureDebugTab : public QWidget { | ||
| 15 | Q_OBJECT | ||
| 16 | |||
| 17 | public: | ||
| 18 | explicit ConfigureDebugTab(QWidget* parent = nullptr); | ||
| 19 | ~ConfigureDebugTab() override; | ||
| 20 | |||
| 21 | void ApplyConfiguration(); | ||
| 22 | |||
| 23 | void SetCurrentIndex(int index); | ||
| 24 | |||
| 25 | private: | ||
| 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 | <html><head/><body> | ||
| 87 | <p>Presents guest frames as they become available, disabling the FPS limit in most titles.</p> | ||
| 88 | <p>NOTE: Will cause instabilities.</p> | ||
| 89 | </body></html> | ||
| 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()); |