summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Fernando Sahmkow2020-03-08 22:39:41 -0400
committerGravatar Fernando Sahmkow2020-06-27 11:35:42 -0400
commitab9aae28bf5daa5fa7105bb4ef41b6d0b3c9cdc1 (patch)
treeec11dc90eb2cad49237eecc98766f61b04724254 /src
parentScheduler: Set last running time on thread. (diff)
downloadyuzu-ab9aae28bf5daa5fa7105bb4ef41b6d0b3c9cdc1.tar.gz
yuzu-ab9aae28bf5daa5fa7105bb4ef41b6d0b3c9cdc1.tar.xz
yuzu-ab9aae28bf5daa5fa7105bb4ef41b6d0b3c9cdc1.zip
General: Initial Setup for Single Core.
Diffstat (limited to 'src')
-rw-r--r--src/core/core.cpp3
-rw-r--r--src/core/cpu_manager.cpp186
-rw-r--r--src/core/cpu_manager.h30
-rw-r--r--src/core/hle/kernel/kernel.cpp19
-rw-r--r--src/core/hle/kernel/kernel.h3
-rw-r--r--src/core/memory.cpp8
-rw-r--r--src/yuzu/configuration/configure_general.cpp6
-rw-r--r--src/yuzu/configuration/configure_general.ui7
8 files changed, 228 insertions, 34 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 5d4ecdce5..fd1bdcaf0 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -149,6 +149,9 @@ struct System::Impl {
149 149
150 device_memory = std::make_unique<Core::DeviceMemory>(system); 150 device_memory = std::make_unique<Core::DeviceMemory>(system);
151 151
152 kernel.SetMulticore(Settings::values.use_multi_core);
153 cpu_manager.SetMulticore(Settings::values.use_multi_core);
154
152 core_timing.Initialize([&system]() { system.RegisterHostThread(); }); 155 core_timing.Initialize([&system]() { system.RegisterHostThread(); });
153 kernel.Initialize(); 156 kernel.Initialize();
154 cpu_manager.Initialize(); 157 cpu_manager.Initialize();
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 9a261968a..e72f89808 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -26,9 +26,13 @@ void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) {
26 26
27void CpuManager::Initialize() { 27void CpuManager::Initialize() {
28 running_mode = true; 28 running_mode = true;
29 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 29 if (is_multicore) {
30 core_data[core].host_thread = 30 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
31 std::make_unique<std::thread>(ThreadStart, std::ref(*this), core); 31 core_data[core].host_thread =
32 std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
33 }
34 } else {
35 core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0);
32 } 36 }
33} 37}
34 38
@@ -41,52 +45,72 @@ void CpuManager::Shutdown() {
41 } 45 }
42} 46}
43 47
48std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
49 return std::function<void(void*)>(GuestThreadFunction);
50}
51
52std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
53 return std::function<void(void*)>(IdleThreadFunction);
54}
55
56std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
57 return std::function<void(void*)>(SuspendThreadFunction);
58}
59
44void CpuManager::GuestThreadFunction(void* cpu_manager_) { 60void CpuManager::GuestThreadFunction(void* cpu_manager_) {
45 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); 61 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
46 cpu_manager->RunGuestThread(); 62 if (cpu_manager->is_multicore) {
63 cpu_manager->MultiCoreRunGuestThread();
64 } else {
65 cpu_manager->SingleCoreRunGuestThread();
66 }
47} 67}
48 68
49void CpuManager::GuestRewindFunction(void* cpu_manager_) { 69void CpuManager::GuestRewindFunction(void* cpu_manager_) {
50 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); 70 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
51 cpu_manager->RunGuestLoop(); 71 if (cpu_manager->is_multicore) {
72 cpu_manager->MultiCoreRunGuestLoop();
73 } else {
74 cpu_manager->SingleCoreRunGuestLoop();
75 }
52} 76}
53 77
54void CpuManager::IdleThreadFunction(void* cpu_manager_) { 78void CpuManager::IdleThreadFunction(void* cpu_manager_) {
55 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); 79 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
56 cpu_manager->RunIdleThread(); 80 if (cpu_manager->is_multicore) {
81 cpu_manager->MultiCoreRunIdleThread();
82 } else {
83 cpu_manager->SingleCoreRunIdleThread();
84 }
57} 85}
58 86
59void CpuManager::SuspendThreadFunction(void* cpu_manager_) { 87void CpuManager::SuspendThreadFunction(void* cpu_manager_) {
60 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); 88 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
61 cpu_manager->RunSuspendThread(); 89 if (cpu_manager->is_multicore) {
62} 90 cpu_manager->MultiCoreRunSuspendThread();
63 91 } else {
64std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { 92 cpu_manager->SingleCoreRunSuspendThread();
65 return std::function<void(void*)>(GuestThreadFunction); 93 }
66}
67
68std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
69 return std::function<void(void*)>(IdleThreadFunction);
70}
71
72std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
73 return std::function<void(void*)>(SuspendThreadFunction);
74} 94}
75 95
76void* CpuManager::GetStartFuncParamater() { 96void* CpuManager::GetStartFuncParamater() {
77 return static_cast<void*>(this); 97 return static_cast<void*>(this);
78} 98}
79 99
80void CpuManager::RunGuestThread() { 100///////////////////////////////////////////////////////////////////////////////
101/// MultiCore ///
102///////////////////////////////////////////////////////////////////////////////
103
104void CpuManager::MultiCoreRunGuestThread() {
81 auto& kernel = system.Kernel(); 105 auto& kernel = system.Kernel();
82 { 106 {
83 auto& sched = kernel.CurrentScheduler(); 107 auto& sched = kernel.CurrentScheduler();
84 sched.OnThreadStart(); 108 sched.OnThreadStart();
85 } 109 }
86 RunGuestLoop(); 110 MultiCoreRunGuestLoop();
87} 111}
88 112
89void CpuManager::RunGuestLoop() { 113void CpuManager::MultiCoreRunGuestLoop() {
90 auto& kernel = system.Kernel(); 114 auto& kernel = system.Kernel();
91 auto* thread = kernel.CurrentScheduler().GetCurrentThread(); 115 auto* thread = kernel.CurrentScheduler().GetCurrentThread();
92 auto host_context = thread->GetHostContext(); 116 auto host_context = thread->GetHostContext();
@@ -103,7 +127,7 @@ void CpuManager::RunGuestLoop() {
103 } 127 }
104} 128}
105 129
106void CpuManager::RunIdleThread() { 130void CpuManager::MultiCoreRunIdleThread() {
107 auto& kernel = system.Kernel(); 131 auto& kernel = system.Kernel();
108 while (true) { 132 while (true) {
109 auto& physical_core = kernel.CurrentPhysicalCore(); 133 auto& physical_core = kernel.CurrentPhysicalCore();
@@ -113,7 +137,7 @@ void CpuManager::RunIdleThread() {
113 } 137 }
114} 138}
115 139
116void CpuManager::RunSuspendThread() { 140void CpuManager::MultiCoreRunSuspendThread() {
117 auto& kernel = system.Kernel(); 141 auto& kernel = system.Kernel();
118 { 142 {
119 auto& sched = kernel.CurrentScheduler(); 143 auto& sched = kernel.CurrentScheduler();
@@ -130,7 +154,7 @@ void CpuManager::RunSuspendThread() {
130 } 154 }
131} 155}
132 156
133void CpuManager::Pause(bool paused) { 157void CpuManager::MultiCorePause(bool paused) {
134 if (!paused) { 158 if (!paused) {
135 bool all_not_barrier = false; 159 bool all_not_barrier = false;
136 while (!all_not_barrier) { 160 while (!all_not_barrier) {
@@ -171,10 +195,120 @@ void CpuManager::Pause(bool paused) {
171 paused_state = paused; 195 paused_state = paused;
172} 196}
173 197
198///////////////////////////////////////////////////////////////////////////////
199/// SingleCore ///
200///////////////////////////////////////////////////////////////////////////////
201
202void CpuManager::SingleCoreRunGuestThread() {
203 auto& kernel = system.Kernel();
204 {
205 auto& sched = kernel.CurrentScheduler();
206 sched.OnThreadStart();
207 }
208 SingleCoreRunGuestLoop();
209}
210
211void CpuManager::SingleCoreRunGuestLoop() {
212 auto& kernel = system.Kernel();
213 auto* thread = kernel.CurrentScheduler().GetCurrentThread();
214 auto host_context = thread->GetHostContext();
215 host_context->SetRewindPoint(std::function<void(void*)>(GuestRewindFunction), this);
216 host_context.reset();
217 while (true) {
218 auto& physical_core = kernel.CurrentPhysicalCore();
219 while (!physical_core.IsInterrupted()) {
220 physical_core.Run();
221 preemption_count++;
222 if (preemption_count % max_cycle_runs == 0) {
223 break;
224 }
225 }
226 physical_core.ClearExclusive();
227 PreemptSingleCore();
228 auto& scheduler = physical_core.Scheduler();
229 scheduler.TryDoContextSwitch();
230 }
231}
232
233void CpuManager::SingleCoreRunIdleThread() {
234 auto& kernel = system.Kernel();
235 while (true) {
236 auto& physical_core = kernel.CurrentPhysicalCore();
237 PreemptSingleCore();
238 auto& scheduler = physical_core.Scheduler();
239 scheduler.TryDoContextSwitch();
240 }
241}
242
243void CpuManager::SingleCoreRunSuspendThread() {
244 auto& kernel = system.Kernel();
245 {
246 auto& sched = kernel.CurrentScheduler();
247 sched.OnThreadStart();
248 }
249 while (true) {
250 auto core = kernel.GetCurrentHostThreadID();
251 auto& scheduler = kernel.CurrentScheduler();
252 Kernel::Thread* current_thread = scheduler.GetCurrentThread();
253 Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
254 ASSERT(scheduler.ContextSwitchPending());
255 ASSERT(core == kernel.GetCurrentHostThreadID());
256 scheduler.TryDoContextSwitch();
257 }
258}
259
260void CpuManager::PreemptSingleCore() {
261 preemption_count = 0;
262 std::size_t old_core = current_core;
263 current_core = (current_core + 1) % Core::Hardware::NUM_CPU_CORES;
264 auto& scheduler = system.Kernel().Scheduler(old_core);
265 Kernel::Thread* current_thread = system.Kernel().Scheduler(old_core).GetCurrentThread();
266 Kernel::Thread* next_thread = system.Kernel().Scheduler(current_core).GetCurrentThread();
267 Common::Fiber::YieldTo(current_thread->GetHostContext(), next_thread->GetHostContext());
268}
269
270void CpuManager::SingleCorePause(bool paused) {
271 if (!paused) {
272 bool all_not_barrier = false;
273 while (!all_not_barrier) {
274 all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load();
275 }
276 core_data[0].enter_barrier->Set();
277 if (paused_state.load()) {
278 bool all_barrier = false;
279 while (!all_barrier) {
280 all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
281 }
282 core_data[0].exit_barrier->Set();
283 }
284 } else {
285 /// Wait until all cores are paused.
286 bool all_barrier = false;
287 while (!all_barrier) {
288 all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
289 }
290 /// Don't release the barrier
291 }
292 paused_state = paused;
293}
294
295void CpuManager::Pause(bool paused) {
296 if (is_multicore) {
297 MultiCorePause(paused);
298 } else {
299 SingleCorePause(paused);
300 }
301}
302
174void CpuManager::RunThread(std::size_t core) { 303void CpuManager::RunThread(std::size_t core) {
175 /// Initialization 304 /// Initialization
176 system.RegisterCoreThread(core); 305 system.RegisterCoreThread(core);
177 std::string name = "yuzu:CoreHostThread_" + std::to_string(core); 306 std::string name;
307 if (is_multicore) {
308 name = "yuzu:CoreCPUThread_" + std::to_string(core);
309 } else {
310 name = "yuzu:CPUThread";
311 }
178 MicroProfileOnThreadCreate(name.c_str()); 312 MicroProfileOnThreadCreate(name.c_str());
179 Common::SetCurrentThreadName(name.c_str()); 313 Common::SetCurrentThreadName(name.c_str());
180 auto& data = core_data[core]; 314 auto& data = core_data[core];
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index e83ab20f9..1e81481ec 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -30,6 +30,10 @@ public:
30 CpuManager& operator=(const CpuManager&) = delete; 30 CpuManager& operator=(const CpuManager&) = delete;
31 CpuManager& operator=(CpuManager&&) = delete; 31 CpuManager& operator=(CpuManager&&) = delete;
32 32
33 /// Sets if emulation is multicore or single core, must be set before Initialize
34 void SetMulticore(bool is_multicore) {
35 this->is_multicore = is_multicore;
36 }
33 void Initialize(); 37 void Initialize();
34 void Shutdown(); 38 void Shutdown();
35 39
@@ -40,21 +44,34 @@ public:
40 std::function<void(void*)> GetSuspendThreadStartFunc(); 44 std::function<void(void*)> GetSuspendThreadStartFunc();
41 void* GetStartFuncParamater(); 45 void* GetStartFuncParamater();
42 46
47 std::size_t CurrentCore() const {
48 return current_core;
49 }
50
43private: 51private:
44 static void GuestThreadFunction(void* cpu_manager); 52 static void GuestThreadFunction(void* cpu_manager);
45 static void GuestRewindFunction(void* cpu_manager); 53 static void GuestRewindFunction(void* cpu_manager);
46 static void IdleThreadFunction(void* cpu_manager); 54 static void IdleThreadFunction(void* cpu_manager);
47 static void SuspendThreadFunction(void* cpu_manager); 55 static void SuspendThreadFunction(void* cpu_manager);
48 56
49 void RunGuestThread(); 57 void MultiCoreRunGuestThread();
50 void RunGuestLoop(); 58 void MultiCoreRunGuestLoop();
51 void RunIdleThread(); 59 void MultiCoreRunIdleThread();
52 void RunSuspendThread(); 60 void MultiCoreRunSuspendThread();
61 void MultiCorePause(bool paused);
62
63 void SingleCoreRunGuestThread();
64 void SingleCoreRunGuestLoop();
65 void SingleCoreRunIdleThread();
66 void SingleCoreRunSuspendThread();
67 void SingleCorePause(bool paused);
53 68
54 static void ThreadStart(CpuManager& cpu_manager, std::size_t core); 69 static void ThreadStart(CpuManager& cpu_manager, std::size_t core);
55 70
56 void RunThread(std::size_t core); 71 void RunThread(std::size_t core);
57 72
73 void PreemptSingleCore();
74
58 struct CoreData { 75 struct CoreData {
59 std::shared_ptr<Common::Fiber> host_context; 76 std::shared_ptr<Common::Fiber> host_context;
60 std::unique_ptr<Common::Event> enter_barrier; 77 std::unique_ptr<Common::Event> enter_barrier;
@@ -70,6 +87,11 @@ private:
70 87
71 std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{}; 88 std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
72 89
90 bool is_multicore{};
91 std::size_t current_core{};
92 std::size_t preemption_count{};
93 static constexpr std::size_t max_cycle_runs = 5;
94
73 System& system; 95 System& system;
74}; 96};
75 97
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 721ab1e70..4a091ea38 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -113,6 +113,10 @@ struct KernelCore::Impl {
113 explicit Impl(Core::System& system, KernelCore& kernel) 113 explicit Impl(Core::System& system, KernelCore& kernel)
114 : global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {} 114 : global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {}
115 115
116 void SetMulticore(bool is_multicore) {
117 this->is_multicore = is_multicore;
118 }
119
116 void Initialize(KernelCore& kernel) { 120 void Initialize(KernelCore& kernel) {
117 Shutdown(); 121 Shutdown();
118 122
@@ -237,6 +241,9 @@ struct KernelCore::Impl {
237 241
238 void RegisterCoreThread(std::size_t core_id) { 242 void RegisterCoreThread(std::size_t core_id) {
239 std::unique_lock lock{register_thread_mutex}; 243 std::unique_lock lock{register_thread_mutex};
244 if (!is_multicore) {
245 single_core_thread_id = std::this_thread::get_id();
246 }
240 const std::thread::id this_id = std::this_thread::get_id(); 247 const std::thread::id this_id = std::this_thread::get_id();
241 const auto it = host_thread_ids.find(this_id); 248 const auto it = host_thread_ids.find(this_id);
242 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); 249 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
@@ -258,6 +265,11 @@ struct KernelCore::Impl {
258 265
259 u32 GetCurrentHostThreadID() const { 266 u32 GetCurrentHostThreadID() const {
260 const std::thread::id this_id = std::this_thread::get_id(); 267 const std::thread::id this_id = std::this_thread::get_id();
268 if (!is_multicore) {
269 if (single_core_thread_id == this_id) {
270 return static_cast<u32>(system.GetCpuManager().CurrentCore());
271 }
272 }
261 const auto it = host_thread_ids.find(this_id); 273 const auto it = host_thread_ids.find(this_id);
262 if (it == host_thread_ids.end()) { 274 if (it == host_thread_ids.end()) {
263 return Core::INVALID_HOST_THREAD_ID; 275 return Core::INVALID_HOST_THREAD_ID;
@@ -378,6 +390,9 @@ struct KernelCore::Impl {
378 390
379 std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; 391 std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
380 392
393 bool is_multicore{};
394 std::thread::id single_core_thread_id{};
395
381 // System context 396 // System context
382 Core::System& system; 397 Core::System& system;
383}; 398};
@@ -387,6 +402,10 @@ KernelCore::~KernelCore() {
387 Shutdown(); 402 Shutdown();
388} 403}
389 404
405void KernelCore::SetMulticore(bool is_multicore) {
406 impl->SetMulticore(is_multicore);
407}
408
390void KernelCore::Initialize() { 409void KernelCore::Initialize() {
391 impl->Initialize(*this); 410 impl->Initialize(*this);
392} 411}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 5d32a8329..162bbd2f8 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -65,6 +65,9 @@ public:
65 KernelCore(KernelCore&&) = delete; 65 KernelCore(KernelCore&&) = delete;
66 KernelCore& operator=(KernelCore&&) = delete; 66 KernelCore& operator=(KernelCore&&) = delete;
67 67
68 /// Sets if emulation is multicore or single core, must be set before Initialize
69 void SetMulticore(bool is_multicore);
70
68 /// Resets the kernel to a clean slate for use. 71 /// Resets the kernel to a clean slate for use.
69 void Initialize(); 72 void Initialize();
70 73
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 4cb5d05e5..7def00768 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -715,8 +715,8 @@ struct Memory::Impl {
715 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 715 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
716 break; 716 break;
717 case Common::PageType::RasterizerCachedMemory: { 717 case Common::PageType::RasterizerCachedMemory: {
718 u8* host_ptr{GetPointerFromVMA(vaddr)}; 718 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
719 system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); 719 system.GPU().InvalidateRegion(vaddr, sizeof(T));
720 T volatile* pointer = reinterpret_cast<T volatile*>(&host_ptr); 720 T volatile* pointer = reinterpret_cast<T volatile*>(&host_ptr);
721 return Common::AtomicCompareAndSwap(pointer, data, expected); 721 return Common::AtomicCompareAndSwap(pointer, data, expected);
722 break; 722 break;
@@ -745,8 +745,8 @@ struct Memory::Impl {
745 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 745 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
746 break; 746 break;
747 case Common::PageType::RasterizerCachedMemory: { 747 case Common::PageType::RasterizerCachedMemory: {
748 u8* host_ptr{GetPointerFromVMA(vaddr)}; 748 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
749 system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(u128)); 749 system.GPU().InvalidateRegion(vaddr, sizeof(u128));
750 u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&host_ptr); 750 u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&host_ptr);
751 return Common::AtomicCompareAndSwap(pointer, data, expected); 751 return Common::AtomicCompareAndSwap(pointer, data, expected);
752 break; 752 break;
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index cb95423e0..74b2ad537 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -23,6 +23,11 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
23ConfigureGeneral::~ConfigureGeneral() = default; 23ConfigureGeneral::~ConfigureGeneral() = default;
24 24
25void ConfigureGeneral::SetConfiguration() { 25void ConfigureGeneral::SetConfiguration() {
26 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
27
28 ui->use_multi_core->setEnabled(runtime_lock);
29 ui->use_multi_core->setChecked(Settings::values.use_multi_core);
30
26 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); 31 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
27 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); 32 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
28 ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); 33 ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
@@ -41,6 +46,7 @@ void ConfigureGeneral::ApplyConfiguration() {
41 46
42 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); 47 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
43 Settings::values.frame_limit = ui->frame_limit->value(); 48 Settings::values.frame_limit = ui->frame_limit->value();
49 Settings::values.use_multi_core = ui->use_multi_core->isChecked();
44} 50}
45 51
46void ConfigureGeneral::changeEvent(QEvent* event) { 52void ConfigureGeneral::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index fc3b7e65a..f872bddec 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -52,6 +52,13 @@
52 </layout> 52 </layout>
53 </item> 53 </item>
54 <item> 54 <item>
55 <widget class="QCheckBox" name="use_multi_core">
56 <property name="text">
57 <string>Emulate CPU in Multiple Cores</string>
58 </property>
59 </widget>
60 </item>
61 <item>
55 <widget class="QCheckBox" name="toggle_check_exit"> 62 <widget class="QCheckBox" name="toggle_check_exit">
56 <property name="text"> 63 <property name="text">
57 <string>Confirm exit while emulation is running</string> 64 <string>Confirm exit while emulation is running</string>