cpu_manager.cpp 12 KB

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