diff options
| author | 2017-11-25 14:56:57 +0100 | |
|---|---|---|
| committer | 2018-01-08 19:10:25 -0500 | |
| commit | 82151d407d8021fa8865cf8dd51c4d5cf0a4b702 (patch) | |
| tree | 739df280fddbecb50e1a2fa690abe8749486ea2d /src/tests/core | |
| parent | IPC: Make DuplicateSession return the Domain instead of the Session if the re... (diff) | |
| download | yuzu-82151d407d8021fa8865cf8dd51c4d5cf0a4b702.tar.gz yuzu-82151d407d8021fa8865cf8dd51c4d5cf0a4b702.tar.xz yuzu-82151d407d8021fa8865cf8dd51c4d5cf0a4b702.zip | |
CoreTiming: Reworked CoreTiming (cherry-picked from Citra #3119)
* CoreTiming: New CoreTiming; Add Test for CoreTiming
Diffstat (limited to 'src/tests/core')
| -rw-r--r-- | src/tests/core/core_timing.cpp | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp new file mode 100644 index 000000000..fcaa30990 --- /dev/null +++ b/src/tests/core/core_timing.cpp | |||
| @@ -0,0 +1,237 @@ | |||
| 1 | // Copyright 2016 Dolphin Emulator Project / 2017 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <catch.hpp> | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <bitset> | ||
| 9 | #include <string> | ||
| 10 | #include "common/file_util.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/core_timing.h" | ||
| 13 | |||
| 14 | // Numbers are chosen randomly to make sure the correct one is given. | ||
| 15 | static constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; | ||
| 16 | static constexpr int MAX_SLICE_LENGTH = 20000; // Copied from CoreTiming internals | ||
| 17 | |||
| 18 | static std::bitset<CB_IDS.size()> callbacks_ran_flags; | ||
| 19 | static u64 expected_callback = 0; | ||
| 20 | static s64 lateness = 0; | ||
| 21 | |||
| 22 | template <unsigned int IDX> | ||
| 23 | void CallbackTemplate(u64 userdata, s64 cycles_late) { | ||
| 24 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); | ||
| 25 | callbacks_ran_flags.set(IDX); | ||
| 26 | REQUIRE(CB_IDS[IDX] == userdata); | ||
| 27 | REQUIRE(CB_IDS[IDX] == expected_callback); | ||
| 28 | REQUIRE(lateness == cycles_late); | ||
| 29 | } | ||
| 30 | |||
| 31 | class ScopeInit final { | ||
| 32 | public: | ||
| 33 | ScopeInit() { | ||
| 34 | CoreTiming::Init(); | ||
| 35 | } | ||
| 36 | ~ScopeInit() { | ||
| 37 | CoreTiming::Shutdown(); | ||
| 38 | } | ||
| 39 | }; | ||
| 40 | |||
| 41 | static void AdvanceAndCheck(u32 idx, int downcount, int expected_lateness = 0, | ||
| 42 | int cpu_downcount = 0) { | ||
| 43 | callbacks_ran_flags = 0; | ||
| 44 | expected_callback = CB_IDS[idx]; | ||
| 45 | lateness = expected_lateness; | ||
| 46 | |||
| 47 | CoreTiming::AddTicks(CoreTiming::GetDowncount() - | ||
| 48 | cpu_downcount); // Pretend we executed X cycles of instructions. | ||
| 49 | CoreTiming::Advance(); | ||
| 50 | |||
| 51 | REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); | ||
| 52 | REQUIRE(downcount == CoreTiming::GetDowncount()); | ||
| 53 | } | ||
| 54 | |||
| 55 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { | ||
| 56 | ScopeInit guard; | ||
| 57 | |||
| 58 | CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>); | ||
| 59 | CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>); | ||
| 60 | CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>); | ||
| 61 | CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>); | ||
| 62 | CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>); | ||
| 63 | |||
| 64 | // Enter slice 0 | ||
| 65 | CoreTiming::Advance(); | ||
| 66 | |||
| 67 | // D -> B -> C -> A -> E | ||
| 68 | CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]); | ||
| 69 | REQUIRE(1000 == CoreTiming::GetDowncount()); | ||
| 70 | CoreTiming::ScheduleEvent(500, cb_b, CB_IDS[1]); | ||
| 71 | REQUIRE(500 == CoreTiming::GetDowncount()); | ||
| 72 | CoreTiming::ScheduleEvent(800, cb_c, CB_IDS[2]); | ||
| 73 | REQUIRE(500 == CoreTiming::GetDowncount()); | ||
| 74 | CoreTiming::ScheduleEvent(100, cb_d, CB_IDS[3]); | ||
| 75 | REQUIRE(100 == CoreTiming::GetDowncount()); | ||
| 76 | CoreTiming::ScheduleEvent(1200, cb_e, CB_IDS[4]); | ||
| 77 | REQUIRE(100 == CoreTiming::GetDowncount()); | ||
| 78 | |||
| 79 | AdvanceAndCheck(3, 400); | ||
| 80 | AdvanceAndCheck(1, 300); | ||
| 81 | AdvanceAndCheck(2, 200); | ||
| 82 | AdvanceAndCheck(0, 200); | ||
| 83 | AdvanceAndCheck(4, MAX_SLICE_LENGTH); | ||
| 84 | } | ||
| 85 | |||
| 86 | TEST_CASE("CoreTiming[Threadsave]", "[core]") { | ||
| 87 | ScopeInit guard; | ||
| 88 | |||
| 89 | CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>); | ||
| 90 | CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>); | ||
| 91 | CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>); | ||
| 92 | CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>); | ||
| 93 | CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>); | ||
| 94 | |||
| 95 | // Enter slice 0 | ||
| 96 | CoreTiming::Advance(); | ||
| 97 | |||
| 98 | // D -> B -> C -> A -> E | ||
| 99 | CoreTiming::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]); | ||
| 100 | // Manually force since ScheduleEventThreadsafe doesn't call it | ||
| 101 | CoreTiming::ForceExceptionCheck(1000); | ||
| 102 | REQUIRE(1000 == CoreTiming::GetDowncount()); | ||
| 103 | CoreTiming::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]); | ||
| 104 | // Manually force since ScheduleEventThreadsafe doesn't call it | ||
| 105 | CoreTiming::ForceExceptionCheck(500); | ||
| 106 | REQUIRE(500 == CoreTiming::GetDowncount()); | ||
| 107 | CoreTiming::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]); | ||
| 108 | // Manually force since ScheduleEventThreadsafe doesn't call it | ||
| 109 | CoreTiming::ForceExceptionCheck(800); | ||
| 110 | REQUIRE(500 == CoreTiming::GetDowncount()); | ||
| 111 | CoreTiming::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]); | ||
| 112 | // Manually force since ScheduleEventThreadsafe doesn't call it | ||
| 113 | CoreTiming::ForceExceptionCheck(100); | ||
| 114 | REQUIRE(100 == CoreTiming::GetDowncount()); | ||
| 115 | CoreTiming::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]); | ||
| 116 | // Manually force since ScheduleEventThreadsafe doesn't call it | ||
| 117 | CoreTiming::ForceExceptionCheck(1200); | ||
| 118 | REQUIRE(100 == CoreTiming::GetDowncount()); | ||
| 119 | |||
| 120 | AdvanceAndCheck(3, 400); | ||
| 121 | AdvanceAndCheck(1, 300); | ||
| 122 | AdvanceAndCheck(2, 200); | ||
| 123 | AdvanceAndCheck(0, 200); | ||
| 124 | AdvanceAndCheck(4, MAX_SLICE_LENGTH); | ||
| 125 | } | ||
| 126 | |||
| 127 | namespace SharedSlotTest { | ||
| 128 | static unsigned int counter = 0; | ||
| 129 | |||
| 130 | template <unsigned int ID> | ||
| 131 | void FifoCallback(u64 userdata, s64 cycles_late) { | ||
| 132 | static_assert(ID < CB_IDS.size(), "ID out of range"); | ||
| 133 | callbacks_ran_flags.set(ID); | ||
| 134 | REQUIRE(CB_IDS[ID] == userdata); | ||
| 135 | REQUIRE(ID == counter); | ||
| 136 | REQUIRE(lateness == cycles_late); | ||
| 137 | ++counter; | ||
| 138 | } | ||
| 139 | } // namespace SharedSlotTest | ||
| 140 | |||
| 141 | TEST_CASE("CoreTiming[SharedSlot]", "[core]") { | ||
| 142 | using namespace SharedSlotTest; | ||
| 143 | |||
| 144 | ScopeInit guard; | ||
| 145 | |||
| 146 | CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", FifoCallback<0>); | ||
| 147 | CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", FifoCallback<1>); | ||
| 148 | CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", FifoCallback<2>); | ||
| 149 | CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", FifoCallback<3>); | ||
| 150 | CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", FifoCallback<4>); | ||
| 151 | |||
| 152 | CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]); | ||
| 153 | CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]); | ||
| 154 | CoreTiming::ScheduleEvent(1000, cb_c, CB_IDS[2]); | ||
| 155 | CoreTiming::ScheduleEvent(1000, cb_d, CB_IDS[3]); | ||
| 156 | CoreTiming::ScheduleEvent(1000, cb_e, CB_IDS[4]); | ||
| 157 | |||
| 158 | // Enter slice 0 | ||
| 159 | CoreTiming::Advance(); | ||
| 160 | REQUIRE(1000 == CoreTiming::GetDowncount()); | ||
| 161 | |||
| 162 | callbacks_ran_flags = 0; | ||
| 163 | counter = 0; | ||
| 164 | lateness = 0; | ||
| 165 | CoreTiming::AddTicks(CoreTiming::GetDowncount()); | ||
| 166 | CoreTiming::Advance(); | ||
| 167 | REQUIRE(MAX_SLICE_LENGTH == CoreTiming::GetDowncount()); | ||
| 168 | REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong()); | ||
| 169 | } | ||
| 170 | |||
| 171 | TEST_CASE("CoreTiming[PredictableLateness]", "[core]") { | ||
| 172 | ScopeInit guard; | ||
| 173 | |||
| 174 | CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>); | ||
| 175 | CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>); | ||
| 176 | |||
| 177 | // Enter slice 0 | ||
| 178 | CoreTiming::Advance(); | ||
| 179 | |||
| 180 | CoreTiming::ScheduleEvent(100, cb_a, CB_IDS[0]); | ||
| 181 | CoreTiming::ScheduleEvent(200, cb_b, CB_IDS[1]); | ||
| 182 | |||
| 183 | AdvanceAndCheck(0, 90, 10, -10); // (100 - 10) | ||
| 184 | AdvanceAndCheck(1, MAX_SLICE_LENGTH, 50, -50); | ||
| 185 | } | ||
| 186 | |||
| 187 | namespace ChainSchedulingTest { | ||
| 188 | static int reschedules = 0; | ||
| 189 | |||
| 190 | static void RescheduleCallback(u64 userdata, s64 cycles_late) { | ||
| 191 | --reschedules; | ||
| 192 | REQUIRE(reschedules >= 0); | ||
| 193 | REQUIRE(lateness == cycles_late); | ||
| 194 | |||
| 195 | if (reschedules > 0) | ||
| 196 | CoreTiming::ScheduleEvent(1000, reinterpret_cast<CoreTiming::EventType*>(userdata), | ||
| 197 | userdata); | ||
| 198 | } | ||
| 199 | } // namespace ChainSchedulingTest | ||
| 200 | |||
| 201 | TEST_CASE("CoreTiming[ChainScheduling]", "[core]") { | ||
| 202 | using namespace ChainSchedulingTest; | ||
| 203 | |||
| 204 | ScopeInit guard; | ||
| 205 | |||
| 206 | CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>); | ||
| 207 | CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>); | ||
| 208 | CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>); | ||
| 209 | CoreTiming::EventType* cb_rs = | ||
| 210 | CoreTiming::RegisterEvent("callbackReschedule", RescheduleCallback); | ||
| 211 | |||
| 212 | // Enter slice 0 | ||
| 213 | CoreTiming::Advance(); | ||
| 214 | |||
| 215 | CoreTiming::ScheduleEvent(800, cb_a, CB_IDS[0]); | ||
| 216 | CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]); | ||
| 217 | CoreTiming::ScheduleEvent(2200, cb_c, CB_IDS[2]); | ||
| 218 | CoreTiming::ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs)); | ||
| 219 | REQUIRE(800 == CoreTiming::GetDowncount()); | ||
| 220 | |||
| 221 | reschedules = 3; | ||
| 222 | AdvanceAndCheck(0, 200); // cb_a | ||
| 223 | AdvanceAndCheck(1, 1000); // cb_b, cb_rs | ||
| 224 | REQUIRE(2 == reschedules); | ||
| 225 | |||
| 226 | CoreTiming::AddTicks(CoreTiming::GetDowncount()); | ||
| 227 | CoreTiming::Advance(); // cb_rs | ||
| 228 | REQUIRE(1 == reschedules); | ||
| 229 | REQUIRE(200 == CoreTiming::GetDowncount()); | ||
| 230 | |||
| 231 | AdvanceAndCheck(2, 800); // cb_c | ||
| 232 | |||
| 233 | CoreTiming::AddTicks(CoreTiming::GetDowncount()); | ||
| 234 | CoreTiming::Advance(); // cb_rs | ||
| 235 | REQUIRE(0 == reschedules); | ||
| 236 | REQUIRE(MAX_SLICE_LENGTH == CoreTiming::GetDowncount()); | ||
| 237 | } | ||