cpu_manager.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/fiber.h"
  5. #include "common/microprofile.h"
  6. #include "common/thread.h"
  7. #include "core/arm/exclusive_monitor.h"
  8. #include "core/core.h"
  9. #include "core/core_timing.h"
  10. #include "core/cpu_manager.h"
  11. #include "core/frontend/emu_window.h"
  12. #include "core/gdbstub/gdbstub.h"
  13. #include "core/hle/kernel/kernel.h"
  14. #include "core/hle/kernel/physical_core.h"
  15. #include "core/hle/kernel/scheduler.h"
  16. #include "core/hle/kernel/thread.h"
  17. namespace Core {
  18. CpuManager::CpuManager(System& system) : system{system} {}
  19. CpuManager::~CpuManager() = default;
  20. void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) {
  21. if (!cpu_manager.is_async_gpu && !cpu_manager.is_multicore) {
  22. cpu_manager.render_window->MakeCurrent();
  23. }
  24. cpu_manager.RunThread(core);
  25. if (!cpu_manager.is_async_gpu && !cpu_manager.is_multicore) {
  26. cpu_manager.render_window->DoneCurrent();
  27. }
  28. }
  29. void CpuManager::SetRenderWindow(Core::Frontend::EmuWindow& render_window) {
  30. this->render_window = &render_window;
  31. }
  32. void CpuManager::Initialize() {
  33. running_mode = true;
  34. if (is_multicore) {
  35. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  36. core_data[core].host_thread =
  37. std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
  38. }
  39. } else {
  40. core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0);
  41. }
  42. }
  43. void CpuManager::Shutdown() {
  44. running_mode = false;
  45. Pause(false);
  46. if (is_multicore) {
  47. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  48. core_data[core].host_thread->join();
  49. core_data[core].host_thread.reset();
  50. }
  51. } else {
  52. core_data[0].host_thread->join();
  53. core_data[0].host_thread.reset();
  54. }
  55. }
  56. std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
  57. return std::function<void(void*)>(GuestThreadFunction);
  58. }
  59. std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
  60. return std::function<void(void*)>(IdleThreadFunction);
  61. }
  62. std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
  63. return std::function<void(void*)>(SuspendThreadFunction);
  64. }
  65. void CpuManager::GuestThreadFunction(void* cpu_manager_) {
  66. CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
  67. if (cpu_manager->is_multicore) {
  68. cpu_manager->MultiCoreRunGuestThread();
  69. } else {
  70. cpu_manager->SingleCoreRunGuestThread();
  71. }
  72. }
  73. void CpuManager::GuestRewindFunction(void* cpu_manager_) {
  74. CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
  75. if (cpu_manager->is_multicore) {
  76. cpu_manager->MultiCoreRunGuestLoop();
  77. } else {
  78. cpu_manager->SingleCoreRunGuestLoop();
  79. }
  80. }
  81. void CpuManager::IdleThreadFunction(void* cpu_manager_) {
  82. CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
  83. if (cpu_manager->is_multicore) {
  84. cpu_manager->MultiCoreRunIdleThread();
  85. } else {
  86. cpu_manager->SingleCoreRunIdleThread();
  87. }
  88. }
  89. void CpuManager::SuspendThreadFunction(void* cpu_manager_) {
  90. CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
  91. if (cpu_manager->is_multicore) {
  92. cpu_manager->MultiCoreRunSuspendThread();
  93. } else {
  94. cpu_manager->SingleCoreRunSuspendThread();
  95. }
  96. }
  97. void* CpuManager::GetStartFuncParamater() {
  98. return static_cast<void*>(this);
  99. }
  100. ///////////////////////////////////////////////////////////////////////////////
  101. /// MultiCore ///
  102. ///////////////////////////////////////////////////////////////////////////////
  103. void CpuManager::MultiCoreRunGuestThread() {
  104. auto& kernel = system.Kernel();
  105. {
  106. auto& sched = kernel.CurrentScheduler();
  107. sched.OnThreadStart();
  108. }
  109. MultiCoreRunGuestLoop();
  110. }
  111. void CpuManager::MultiCoreRunGuestLoop() {
  112. auto& kernel = system.Kernel();
  113. auto* thread = kernel.CurrentScheduler().GetCurrentThread();
  114. auto host_context = thread->GetHostContext();
  115. host_context->SetRewindPoint(std::function<void(void*)>(GuestRewindFunction), this);
  116. host_context.reset();
  117. while (true) {
  118. auto& physical_core = kernel.CurrentPhysicalCore();
  119. system.EnterDynarmicProfile();
  120. while (!physical_core.IsInterrupted()) {
  121. physical_core.Run();
  122. }
  123. system.ExitDynarmicProfile();
  124. physical_core.ClearExclusive();
  125. auto& scheduler = physical_core.Scheduler();
  126. scheduler.TryDoContextSwitch();
  127. }
  128. }
  129. void CpuManager::MultiCoreRunIdleThread() {
  130. auto& kernel = system.Kernel();
  131. while (true) {
  132. auto& physical_core = kernel.CurrentPhysicalCore();
  133. physical_core.Idle();
  134. auto& scheduler = physical_core.Scheduler();
  135. scheduler.TryDoContextSwitch();
  136. }
  137. }
  138. void CpuManager::MultiCoreRunSuspendThread() {
  139. auto& kernel = system.Kernel();
  140. {
  141. auto& sched = kernel.CurrentScheduler();
  142. sched.OnThreadStart();
  143. }
  144. while (true) {
  145. auto core = kernel.GetCurrentHostThreadID();
  146. auto& scheduler = kernel.CurrentScheduler();
  147. Kernel::Thread* current_thread = scheduler.GetCurrentThread();
  148. Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
  149. ASSERT(scheduler.ContextSwitchPending());
  150. ASSERT(core == kernel.GetCurrentHostThreadID());
  151. scheduler.TryDoContextSwitch();
  152. }
  153. }
  154. void CpuManager::MultiCorePause(bool paused) {
  155. if (!paused) {
  156. bool all_not_barrier = false;
  157. while (!all_not_barrier) {
  158. all_not_barrier = true;
  159. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  160. all_not_barrier &=
  161. !core_data[core].is_running.load() && core_data[core].initialized.load();
  162. }
  163. }
  164. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  165. core_data[core].enter_barrier->Set();
  166. }
  167. if (paused_state.load()) {
  168. bool all_barrier = false;
  169. while (!all_barrier) {
  170. all_barrier = true;
  171. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  172. all_barrier &=
  173. core_data[core].is_paused.load() && core_data[core].initialized.load();
  174. }
  175. }
  176. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  177. core_data[core].exit_barrier->Set();
  178. }
  179. }
  180. } else {
  181. /// Wait until all cores are paused.
  182. bool all_barrier = false;
  183. while (!all_barrier) {
  184. all_barrier = true;
  185. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  186. all_barrier &=
  187. core_data[core].is_paused.load() && core_data[core].initialized.load();
  188. }
  189. }
  190. /// Don't release the barrier
  191. }
  192. paused_state = paused;
  193. }
  194. ///////////////////////////////////////////////////////////////////////////////
  195. /// SingleCore ///
  196. ///////////////////////////////////////////////////////////////////////////////
  197. void CpuManager::SingleCoreRunGuestThread() {
  198. auto& kernel = system.Kernel();
  199. {
  200. auto& sched = kernel.CurrentScheduler();
  201. sched.OnThreadStart();
  202. }
  203. SingleCoreRunGuestLoop();
  204. }
  205. void CpuManager::SingleCoreRunGuestLoop() {
  206. auto& kernel = system.Kernel();
  207. auto* thread = kernel.CurrentScheduler().GetCurrentThread();
  208. auto host_context = thread->GetHostContext();
  209. host_context->SetRewindPoint(std::function<void(void*)>(GuestRewindFunction), this);
  210. host_context.reset();
  211. while (true) {
  212. auto& physical_core = kernel.CurrentPhysicalCore();
  213. system.EnterDynarmicProfile();
  214. while (!physical_core.IsInterrupted()) {
  215. physical_core.Run();
  216. preemption_count++;
  217. if (preemption_count % max_cycle_runs == 0) {
  218. break;
  219. }
  220. }
  221. physical_core.ClearExclusive();
  222. system.ExitDynarmicProfile();
  223. PreemptSingleCore();
  224. auto& scheduler = kernel.Scheduler(current_core);
  225. scheduler.TryDoContextSwitch();
  226. }
  227. }
  228. void CpuManager::SingleCoreRunIdleThread() {
  229. auto& kernel = system.Kernel();
  230. while (true) {
  231. auto& physical_core = kernel.CurrentPhysicalCore();
  232. PreemptSingleCore();
  233. auto& scheduler = physical_core.Scheduler();
  234. scheduler.TryDoContextSwitch();
  235. }
  236. }
  237. void CpuManager::SingleCoreRunSuspendThread() {
  238. auto& kernel = system.Kernel();
  239. {
  240. auto& sched = kernel.CurrentScheduler();
  241. sched.OnThreadStart();
  242. }
  243. while (true) {
  244. auto core = kernel.GetCurrentHostThreadID();
  245. auto& scheduler = kernel.CurrentScheduler();
  246. Kernel::Thread* current_thread = scheduler.GetCurrentThread();
  247. Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
  248. ASSERT(scheduler.ContextSwitchPending());
  249. ASSERT(core == kernel.GetCurrentHostThreadID());
  250. scheduler.TryDoContextSwitch();
  251. }
  252. }
  253. void CpuManager::PreemptSingleCore() {
  254. preemption_count = 0;
  255. std::size_t old_core = current_core;
  256. current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
  257. auto& scheduler = system.Kernel().Scheduler(old_core);
  258. Kernel::Thread* current_thread = scheduler.GetCurrentThread();
  259. scheduler.Unload();
  260. auto& next_scheduler = system.Kernel().Scheduler(current_core);
  261. Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
  262. /// May have changed scheduler
  263. auto& current_scheduler = system.Kernel().Scheduler(current_core);
  264. current_scheduler.Reload();
  265. }
  266. void CpuManager::SingleCorePause(bool paused) {
  267. if (!paused) {
  268. bool all_not_barrier = false;
  269. while (!all_not_barrier) {
  270. all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load();
  271. }
  272. core_data[0].enter_barrier->Set();
  273. if (paused_state.load()) {
  274. bool all_barrier = false;
  275. while (!all_barrier) {
  276. all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
  277. }
  278. core_data[0].exit_barrier->Set();
  279. }
  280. } else {
  281. /// Wait until all cores are paused.
  282. bool all_barrier = false;
  283. while (!all_barrier) {
  284. all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
  285. }
  286. /// Don't release the barrier
  287. }
  288. paused_state = paused;
  289. }
  290. void CpuManager::Pause(bool paused) {
  291. if (is_multicore) {
  292. MultiCorePause(paused);
  293. } else {
  294. SingleCorePause(paused);
  295. }
  296. }
  297. void CpuManager::RunThread(std::size_t core) {
  298. /// Initialization
  299. system.RegisterCoreThread(core);
  300. std::string name;
  301. if (is_multicore) {
  302. name = "yuzu:CoreCPUThread_" + std::to_string(core);
  303. } else {
  304. name = "yuzu:CPUThread";
  305. }
  306. MicroProfileOnThreadCreate(name.c_str());
  307. Common::SetCurrentThreadName(name.c_str());
  308. auto& data = core_data[core];
  309. data.enter_barrier = std::make_unique<Common::Event>();
  310. data.exit_barrier = std::make_unique<Common::Event>();
  311. data.host_context = Common::Fiber::ThreadToFiber();
  312. data.is_running = false;
  313. data.initialized = true;
  314. /// Running
  315. while (running_mode) {
  316. data.is_running = false;
  317. data.enter_barrier->Wait();
  318. auto& scheduler = system.Kernel().CurrentScheduler();
  319. Kernel::Thread* current_thread = scheduler.GetCurrentThread();
  320. data.is_running = true;
  321. Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext());
  322. data.is_running = false;
  323. data.is_paused = true;
  324. data.exit_barrier->Wait();
  325. data.is_paused = false;
  326. }
  327. /// Time to cleanup
  328. data.host_context->Exit();
  329. data.enter_barrier.reset();
  330. data.exit_barrier.reset();
  331. data.initialized = false;
  332. }
  333. } // namespace Core