CoreTimingTest.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. // Copyright 2016 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <gtest/gtest.h>
  4. #include <array>
  5. #include <bitset>
  6. #include <string>
  7. #include "Common/Config/Config.h"
  8. #include "Common/FileUtil.h"
  9. #include "Core/Config/MainSettings.h"
  10. #include "Core/ConfigManager.h"
  11. #include "Core/Core.h"
  12. #include "Core/CoreTiming.h"
  13. #include "Core/PowerPC/PowerPC.h"
  14. #include "Core/System.h"
  15. #include "UICommon/UICommon.h"
  16. // Numbers are chosen randomly to make sure the correct one is given.
  17. static constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
  18. static constexpr int MAX_SLICE_LENGTH = 20000; // Copied from CoreTiming internals
  19. static std::bitset<CB_IDS.size()> s_callbacks_ran_flags;
  20. static u64 s_expected_callback = 0;
  21. static s64 s_lateness = 0;
  22. template <unsigned int IDX>
  23. void CallbackTemplate(Core::System& system, u64 userdata, s64 lateness)
  24. {
  25. static_assert(IDX < CB_IDS.size(), "IDX out of range");
  26. s_callbacks_ran_flags.set(IDX);
  27. EXPECT_EQ(CB_IDS[IDX], userdata);
  28. EXPECT_EQ(CB_IDS[IDX], s_expected_callback);
  29. EXPECT_EQ(s_lateness, lateness);
  30. }
  31. class ScopeInit final
  32. {
  33. public:
  34. explicit ScopeInit(Core::System& system) : m_system(system), m_profile_path(File::CreateTempDir())
  35. {
  36. if (!UserDirectoryExists())
  37. {
  38. return;
  39. }
  40. Core::DeclareAsCPUThread();
  41. UICommon::SetUserDirectory(m_profile_path);
  42. Config::Init();
  43. SConfig::Init();
  44. system.GetPowerPC().Init(PowerPC::CPUCore::Interpreter);
  45. auto& core_timing = system.GetCoreTiming();
  46. core_timing.Init();
  47. }
  48. ~ScopeInit()
  49. {
  50. if (!UserDirectoryExists())
  51. {
  52. return;
  53. }
  54. auto& core_timing = m_system.GetCoreTiming();
  55. core_timing.Shutdown();
  56. m_system.GetPowerPC().Shutdown();
  57. SConfig::Shutdown();
  58. Config::Shutdown();
  59. Core::UndeclareAsCPUThread();
  60. File::DeleteDirRecursively(m_profile_path);
  61. }
  62. bool UserDirectoryExists() const { return !m_profile_path.empty(); }
  63. private:
  64. Core::System& m_system;
  65. std::string m_profile_path;
  66. };
  67. static void AdvanceAndCheck(Core::System& system, u32 idx, int downcount, int expected_lateness = 0,
  68. int cpu_downcount = 0)
  69. {
  70. s_callbacks_ran_flags = 0;
  71. s_expected_callback = CB_IDS[idx];
  72. s_lateness = expected_lateness;
  73. auto& ppc_state = system.GetPPCState();
  74. ppc_state.downcount = cpu_downcount; // Pretend we executed X cycles of instructions.
  75. auto& core_timing = system.GetCoreTiming();
  76. core_timing.Advance();
  77. EXPECT_EQ(decltype(s_callbacks_ran_flags)().set(idx), s_callbacks_ran_flags);
  78. EXPECT_EQ(downcount, ppc_state.downcount);
  79. }
  80. TEST(CoreTiming, BasicOrder)
  81. {
  82. auto& system = Core::System::GetInstance();
  83. ScopeInit guard(system);
  84. ASSERT_TRUE(guard.UserDirectoryExists());
  85. auto& core_timing = system.GetCoreTiming();
  86. auto& ppc_state = system.GetPPCState();
  87. CoreTiming::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
  88. CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
  89. CoreTiming::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
  90. CoreTiming::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>);
  91. CoreTiming::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>);
  92. // Enter slice 0
  93. core_timing.Advance();
  94. // D -> B -> C -> A -> E
  95. core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
  96. EXPECT_EQ(1000, ppc_state.downcount);
  97. core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]);
  98. EXPECT_EQ(500, ppc_state.downcount);
  99. core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]);
  100. EXPECT_EQ(500, ppc_state.downcount);
  101. core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]);
  102. EXPECT_EQ(100, ppc_state.downcount);
  103. core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]);
  104. EXPECT_EQ(100, ppc_state.downcount);
  105. AdvanceAndCheck(system, 3, 400);
  106. AdvanceAndCheck(system, 1, 300);
  107. AdvanceAndCheck(system, 2, 200);
  108. AdvanceAndCheck(system, 0, 200);
  109. AdvanceAndCheck(system, 4, MAX_SLICE_LENGTH);
  110. }
  111. namespace SharedSlotTest
  112. {
  113. static unsigned int s_counter = 0;
  114. template <unsigned int ID>
  115. void FifoCallback(Core::System& system, u64 userdata, s64 lateness)
  116. {
  117. static_assert(ID < CB_IDS.size(), "ID out of range");
  118. s_callbacks_ran_flags.set(ID);
  119. EXPECT_EQ(CB_IDS[ID], userdata);
  120. EXPECT_EQ(ID, s_counter);
  121. EXPECT_EQ(s_lateness, lateness);
  122. ++s_counter;
  123. }
  124. } // namespace SharedSlotTest
  125. TEST(CoreTiming, SharedSlot)
  126. {
  127. using namespace SharedSlotTest;
  128. auto& system = Core::System::GetInstance();
  129. ScopeInit guard(system);
  130. ASSERT_TRUE(guard.UserDirectoryExists());
  131. auto& core_timing = system.GetCoreTiming();
  132. auto& ppc_state = system.GetPPCState();
  133. CoreTiming::EventType* cb_a = core_timing.RegisterEvent("callbackA", FifoCallback<0>);
  134. CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", FifoCallback<1>);
  135. CoreTiming::EventType* cb_c = core_timing.RegisterEvent("callbackC", FifoCallback<2>);
  136. CoreTiming::EventType* cb_d = core_timing.RegisterEvent("callbackD", FifoCallback<3>);
  137. CoreTiming::EventType* cb_e = core_timing.RegisterEvent("callbackE", FifoCallback<4>);
  138. core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
  139. core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]);
  140. core_timing.ScheduleEvent(1000, cb_c, CB_IDS[2]);
  141. core_timing.ScheduleEvent(1000, cb_d, CB_IDS[3]);
  142. core_timing.ScheduleEvent(1000, cb_e, CB_IDS[4]);
  143. // Enter slice 0
  144. core_timing.Advance();
  145. EXPECT_EQ(1000, ppc_state.downcount);
  146. s_callbacks_ran_flags = 0;
  147. s_counter = 0;
  148. s_lateness = 0;
  149. ppc_state.downcount = 0;
  150. core_timing.Advance();
  151. EXPECT_EQ(MAX_SLICE_LENGTH, ppc_state.downcount);
  152. EXPECT_EQ(0x1FULL, s_callbacks_ran_flags.to_ullong());
  153. }
  154. TEST(CoreTiming, PredictableLateness)
  155. {
  156. auto& system = Core::System::GetInstance();
  157. ScopeInit guard(system);
  158. ASSERT_TRUE(guard.UserDirectoryExists());
  159. auto& core_timing = system.GetCoreTiming();
  160. CoreTiming::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
  161. CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
  162. // Enter slice 0
  163. core_timing.Advance();
  164. core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]);
  165. core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]);
  166. AdvanceAndCheck(system, 0, 90, 10, -10); // (100 - 10)
  167. AdvanceAndCheck(system, 1, MAX_SLICE_LENGTH, 50, -50);
  168. }
  169. namespace ChainSchedulingTest
  170. {
  171. static int s_reschedules = 0;
  172. static void RescheduleCallback(Core::System& system, u64 userdata, s64 lateness)
  173. {
  174. --s_reschedules;
  175. EXPECT_TRUE(s_reschedules >= 0);
  176. EXPECT_EQ(s_lateness, lateness);
  177. if (s_reschedules > 0)
  178. {
  179. system.GetCoreTiming().ScheduleEvent(1000, reinterpret_cast<CoreTiming::EventType*>(userdata),
  180. userdata);
  181. }
  182. }
  183. } // namespace ChainSchedulingTest
  184. TEST(CoreTiming, ChainScheduling)
  185. {
  186. using namespace ChainSchedulingTest;
  187. auto& system = Core::System::GetInstance();
  188. ScopeInit guard(system);
  189. ASSERT_TRUE(guard.UserDirectoryExists());
  190. auto& core_timing = system.GetCoreTiming();
  191. auto& ppc_state = system.GetPPCState();
  192. CoreTiming::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
  193. CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
  194. CoreTiming::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
  195. CoreTiming::EventType* cb_rs =
  196. core_timing.RegisterEvent("callbackReschedule", RescheduleCallback);
  197. // Enter slice 0
  198. core_timing.Advance();
  199. core_timing.ScheduleEvent(800, cb_a, CB_IDS[0]);
  200. core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]);
  201. core_timing.ScheduleEvent(2200, cb_c, CB_IDS[2]);
  202. core_timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
  203. EXPECT_EQ(800, ppc_state.downcount);
  204. s_reschedules = 3;
  205. AdvanceAndCheck(system, 0, 200); // cb_a
  206. AdvanceAndCheck(system, 1, 1000); // cb_b, cb_rs
  207. EXPECT_EQ(2, s_reschedules);
  208. ppc_state.downcount = 0;
  209. core_timing.Advance(); // cb_rs
  210. EXPECT_EQ(1, s_reschedules);
  211. EXPECT_EQ(200, ppc_state.downcount);
  212. AdvanceAndCheck(system, 2, 800); // cb_c
  213. ppc_state.downcount = 0;
  214. core_timing.Advance(); // cb_rs
  215. EXPECT_EQ(0, s_reschedules);
  216. EXPECT_EQ(MAX_SLICE_LENGTH, ppc_state.downcount);
  217. }
  218. namespace ScheduleIntoPastTest
  219. {
  220. static CoreTiming::EventType* s_cb_next = nullptr;
  221. static void ChainCallback(Core::System& system, u64 userdata, s64 lateness)
  222. {
  223. EXPECT_EQ(CB_IDS[0] + 1, userdata);
  224. EXPECT_EQ(0, lateness);
  225. system.GetCoreTiming().ScheduleEvent(-1000, s_cb_next, userdata - 1);
  226. }
  227. } // namespace ScheduleIntoPastTest
  228. // This can happen when scheduling from outside the CPU Thread.
  229. // Also, if the callback is very late, it may reschedule itself for the next period which
  230. // is also in the past.
  231. TEST(CoreTiming, ScheduleIntoPast)
  232. {
  233. using namespace ScheduleIntoPastTest;
  234. auto& system = Core::System::GetInstance();
  235. ScopeInit guard(system);
  236. ASSERT_TRUE(guard.UserDirectoryExists());
  237. auto& core_timing = system.GetCoreTiming();
  238. auto& ppc_state = system.GetPPCState();
  239. s_cb_next = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
  240. CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
  241. CoreTiming::EventType* cb_chain = core_timing.RegisterEvent("callbackChain", ChainCallback);
  242. // Enter slice 0
  243. core_timing.Advance();
  244. core_timing.ScheduleEvent(1000, cb_chain, CB_IDS[0] + 1);
  245. EXPECT_EQ(1000, ppc_state.downcount);
  246. AdvanceAndCheck(system, 0, MAX_SLICE_LENGTH, 1000); // Run cb_chain into late cb_a
  247. // Schedule late from wrong thread
  248. // The problem with scheduling CPU events from outside the CPU Thread is that g_global_timer
  249. // is not reliable outside the CPU Thread. It's possible for the other thread to sample the
  250. // global timer right before the timer is updated by Advance() then submit a new event using
  251. // the stale value, i.e. effectively half-way through the previous slice.
  252. // NOTE: We're only testing that the scheduler doesn't break, not whether this makes sense.
  253. Core::UndeclareAsCPUThread();
  254. auto& core_timing_globals = core_timing.GetGlobals();
  255. core_timing_globals.global_timer -= 1000;
  256. core_timing.ScheduleEvent(0, cb_b, CB_IDS[1], CoreTiming::FromThread::NON_CPU);
  257. core_timing_globals.global_timer += 1000;
  258. Core::DeclareAsCPUThread();
  259. AdvanceAndCheck(system, 1, MAX_SLICE_LENGTH, MAX_SLICE_LENGTH + 1000);
  260. // Schedule directly into the past from the CPU.
  261. // This shouldn't happen in practice, but it's best if we don't mess up the slice length and
  262. // downcount if we do.
  263. core_timing.ScheduleEvent(-1000, s_cb_next, CB_IDS[0]);
  264. EXPECT_EQ(0, ppc_state.downcount);
  265. AdvanceAndCheck(system, 0, MAX_SLICE_LENGTH, 1000);
  266. }
  267. TEST(CoreTiming, Overclocking)
  268. {
  269. auto& system = Core::System::GetInstance();
  270. ScopeInit guard(system);
  271. ASSERT_TRUE(guard.UserDirectoryExists());
  272. auto& core_timing = system.GetCoreTiming();
  273. auto& ppc_state = system.GetPPCState();
  274. CoreTiming::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
  275. CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
  276. CoreTiming::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
  277. CoreTiming::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>);
  278. CoreTiming::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>);
  279. // Overclock
  280. Config::SetCurrent(Config::MAIN_OVERCLOCK_ENABLE, true);
  281. Config::SetCurrent(Config::MAIN_OVERCLOCK, 2.0f);
  282. // Enter slice 0
  283. // Updates s_last_OC_factor.
  284. core_timing.Advance();
  285. core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]);
  286. core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]);
  287. core_timing.ScheduleEvent(400, cb_c, CB_IDS[2]);
  288. core_timing.ScheduleEvent(800, cb_d, CB_IDS[3]);
  289. core_timing.ScheduleEvent(1600, cb_e, CB_IDS[4]);
  290. EXPECT_EQ(200, ppc_state.downcount);
  291. AdvanceAndCheck(system, 0, 200); // (200 - 100) * 2
  292. AdvanceAndCheck(system, 1, 400); // (400 - 200) * 2
  293. AdvanceAndCheck(system, 2, 800); // (800 - 400) * 2
  294. AdvanceAndCheck(system, 3, 1600); // (1600 - 800) * 2
  295. AdvanceAndCheck(system, 4, MAX_SLICE_LENGTH * 2);
  296. // Underclock
  297. Config::SetCurrent(Config::MAIN_OVERCLOCK, 0.5f);
  298. core_timing.Advance();
  299. core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]);
  300. core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]);
  301. core_timing.ScheduleEvent(400, cb_c, CB_IDS[2]);
  302. core_timing.ScheduleEvent(800, cb_d, CB_IDS[3]);
  303. core_timing.ScheduleEvent(1600, cb_e, CB_IDS[4]);
  304. EXPECT_EQ(50, ppc_state.downcount);
  305. AdvanceAndCheck(system, 0, 50); // (200 - 100) / 2
  306. AdvanceAndCheck(system, 1, 100); // (400 - 200) / 2
  307. AdvanceAndCheck(system, 2, 200); // (800 - 400) / 2
  308. AdvanceAndCheck(system, 3, 400); // (1600 - 800) / 2
  309. AdvanceAndCheck(system, 4, MAX_SLICE_LENGTH / 2);
  310. // Try switching the clock mid-emulation
  311. Config::SetCurrent(Config::MAIN_OVERCLOCK, 1.0f);
  312. core_timing.Advance();
  313. core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]);
  314. core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]);
  315. core_timing.ScheduleEvent(400, cb_c, CB_IDS[2]);
  316. core_timing.ScheduleEvent(800, cb_d, CB_IDS[3]);
  317. core_timing.ScheduleEvent(1600, cb_e, CB_IDS[4]);
  318. EXPECT_EQ(100, ppc_state.downcount);
  319. AdvanceAndCheck(system, 0, 100); // (200 - 100)
  320. Config::SetCurrent(Config::MAIN_OVERCLOCK, 2.0f);
  321. AdvanceAndCheck(system, 1, 400); // (400 - 200) * 2
  322. AdvanceAndCheck(system, 2, 800); // (800 - 400) * 2
  323. Config::SetCurrent(Config::MAIN_OVERCLOCK, 0.1f);
  324. AdvanceAndCheck(system, 3, 80); // (1600 - 800) / 10
  325. Config::SetCurrent(Config::MAIN_OVERCLOCK, 1.0f);
  326. AdvanceAndCheck(system, 4, MAX_SLICE_LENGTH);
  327. }