diff options
| author | 2017-11-25 14:56:57 +0100 | |
|---|---|---|
| committer | 2018-01-08 19:10:25 -0500 | |
| commit | 82151d407d8021fa8865cf8dd51c4d5cf0a4b702 (patch) | |
| tree | 739df280fddbecb50e1a2fa690abe8749486ea2d /src/core/core_timing.h | |
| 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/core/core_timing.h')
| -rw-r--r-- | src/core/core_timing.h | 201 |
1 files changed, 124 insertions, 77 deletions
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 92c811af6..46ddcd18b 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -1,144 +1,191 @@ | |||
| 1 | // Copyright (c) 2012- PPSSPP Project / Dolphin Project. | 1 | // Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2+ |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | /** | ||
| 8 | * This is a system to schedule events into the emulated machine's future. Time is measured | ||
| 9 | * in main CPU clock cycles. | ||
| 10 | * | ||
| 11 | * To schedule an event, you first have to register its type. This is where you pass in the | ||
| 12 | * callback. You then schedule events using the type id you get back. | ||
| 13 | * | ||
| 14 | * The int cyclesLate that the callbacks get is how many cycles late it was. | ||
| 15 | * So to schedule a new event on a regular basis: | ||
| 16 | * inside callback: | ||
| 17 | * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") | ||
| 18 | */ | ||
| 19 | |||
| 7 | #include <functional> | 20 | #include <functional> |
| 21 | #include <limits> | ||
| 8 | #include <string> | 22 | #include <string> |
| 9 | #include "common/common_types.h" | 23 | #include "common/common_types.h" |
| 24 | #include "common/logging/log.h" | ||
| 10 | 25 | ||
| 11 | // This is a system to schedule events into the emulated machine's future. Time is measured | 26 | // The timing we get from the assembly is 268,111,855.956 Hz |
| 12 | // in main CPU clock cycles. | 27 | // It is possible that this number isn't just an integer because the compiler could have |
| 13 | 28 | // optimized the multiplication by a multiply-by-constant division. | |
| 14 | // To schedule an event, you first have to register its type. This is where you pass in the | 29 | // Rounding to the nearest integer should be fine |
| 15 | // callback. You then schedule events using the type id you get back. | 30 | constexpr u64 BASE_CLOCK_RATE = 383778816; // Switch clock speed is 384MHz docked |
| 16 | 31 | constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE; | |
| 17 | // See HW/SystemTimers.cpp for the main part of Dolphin's usage of this scheduler. | ||
| 18 | |||
| 19 | // The int cycles_late that the callbacks get is how many cycles late it was. | ||
| 20 | // So to schedule a new event on a regular basis: | ||
| 21 | // inside callback: | ||
| 22 | // ScheduleEvent(periodInCycles - cycles_late, callback, "whatever") | ||
| 23 | |||
| 24 | constexpr int BASE_CLOCK_RATE = 383778816; // Switch clock speed is 384MHz docked | ||
| 25 | extern int g_clock_rate_arm11; | ||
| 26 | 32 | ||
| 27 | inline s64 msToCycles(int ms) { | 33 | inline s64 msToCycles(int ms) { |
| 28 | return (s64)g_clock_rate_arm11 / 1000 * ms; | 34 | // since ms is int there is no way to overflow |
| 35 | return BASE_CLOCK_RATE * static_cast<s64>(ms) / 1000; | ||
| 29 | } | 36 | } |
| 30 | 37 | ||
| 31 | inline s64 msToCycles(float ms) { | 38 | inline s64 msToCycles(float ms) { |
| 32 | return (s64)(g_clock_rate_arm11 * ms * (0.001f)); | 39 | return static_cast<s64>(BASE_CLOCK_RATE * (0.001f) * ms); |
| 33 | } | 40 | } |
| 34 | 41 | ||
| 35 | inline s64 msToCycles(double ms) { | 42 | inline s64 msToCycles(double ms) { |
| 36 | return (s64)(g_clock_rate_arm11 * ms * (0.001)); | 43 | return static_cast<s64>(BASE_CLOCK_RATE * (0.001) * ms); |
| 37 | } | 44 | } |
| 38 | 45 | ||
| 39 | inline s64 usToCycles(float us) { | 46 | inline s64 usToCycles(float us) { |
| 40 | return (s64)(g_clock_rate_arm11 * us * (0.000001f)); | 47 | return static_cast<s64>(BASE_CLOCK_RATE * (0.000001f) * us); |
| 41 | } | 48 | } |
| 42 | 49 | ||
| 43 | inline s64 usToCycles(int us) { | 50 | inline s64 usToCycles(int us) { |
| 44 | return (g_clock_rate_arm11 / 1000000 * (s64)us); | 51 | return (BASE_CLOCK_RATE * static_cast<s64>(us) / 1000000); |
| 45 | } | 52 | } |
| 46 | 53 | ||
| 47 | inline s64 usToCycles(s64 us) { | 54 | inline s64 usToCycles(s64 us) { |
| 48 | return (g_clock_rate_arm11 / 1000000 * us); | 55 | if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) { |
| 56 | LOG_ERROR(Core_Timing, "Integer overflow, use max value"); | ||
| 57 | return std::numeric_limits<s64>::max(); | ||
| 58 | } | ||
| 59 | if (us > MAX_VALUE_TO_MULTIPLY) { | ||
| 60 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); | ||
| 61 | return BASE_CLOCK_RATE * (us / 1000000); | ||
| 62 | } | ||
| 63 | return (BASE_CLOCK_RATE * us) / 1000000; | ||
| 49 | } | 64 | } |
| 50 | 65 | ||
| 51 | inline s64 usToCycles(u64 us) { | 66 | inline s64 usToCycles(u64 us) { |
| 52 | return (s64)(g_clock_rate_arm11 / 1000000 * us); | 67 | if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) { |
| 68 | LOG_ERROR(Core_Timing, "Integer overflow, use max value"); | ||
| 69 | return std::numeric_limits<s64>::max(); | ||
| 70 | } | ||
| 71 | if (us > MAX_VALUE_TO_MULTIPLY) { | ||
| 72 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); | ||
| 73 | return BASE_CLOCK_RATE * static_cast<s64>(us / 1000000); | ||
| 74 | } | ||
| 75 | return (BASE_CLOCK_RATE * static_cast<s64>(us)) / 1000000; | ||
| 76 | } | ||
| 77 | |||
| 78 | inline s64 nsToCycles(float ns) { | ||
| 79 | return static_cast<s64>(BASE_CLOCK_RATE * (0.000000001f) * ns); | ||
| 80 | } | ||
| 81 | |||
| 82 | inline s64 nsToCycles(int ns) { | ||
| 83 | return BASE_CLOCK_RATE * static_cast<s64>(ns) / 1000000000; | ||
| 84 | } | ||
| 85 | |||
| 86 | inline s64 nsToCycles(s64 ns) { | ||
| 87 | if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) { | ||
| 88 | LOG_ERROR(Core_Timing, "Integer overflow, use max value"); | ||
| 89 | return std::numeric_limits<s64>::max(); | ||
| 90 | } | ||
| 91 | if (ns > MAX_VALUE_TO_MULTIPLY) { | ||
| 92 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); | ||
| 93 | return BASE_CLOCK_RATE * (ns / 1000000000); | ||
| 94 | } | ||
| 95 | return (BASE_CLOCK_RATE * ns) / 1000000000; | ||
| 96 | } | ||
| 97 | |||
| 98 | inline s64 nsToCycles(u64 ns) { | ||
| 99 | if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) { | ||
| 100 | LOG_ERROR(Core_Timing, "Integer overflow, use max value"); | ||
| 101 | return std::numeric_limits<s64>::max(); | ||
| 102 | } | ||
| 103 | if (ns > MAX_VALUE_TO_MULTIPLY) { | ||
| 104 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); | ||
| 105 | return BASE_CLOCK_RATE * (static_cast<s64>(ns) / 1000000000); | ||
| 106 | } | ||
| 107 | return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000; | ||
| 108 | } | ||
| 109 | |||
| 110 | inline u64 cyclesToNs(s64 cycles) { | ||
| 111 | return cycles * 1000000000 / BASE_CLOCK_RATE; | ||
| 53 | } | 112 | } |
| 54 | 113 | ||
| 55 | inline s64 cyclesToUs(s64 cycles) { | 114 | inline s64 cyclesToUs(s64 cycles) { |
| 56 | return cycles / (g_clock_rate_arm11 / 1000000); | 115 | return cycles * 1000000 / BASE_CLOCK_RATE; |
| 57 | } | 116 | } |
| 58 | 117 | ||
| 59 | inline u64 cyclesToMs(s64 cycles) { | 118 | inline u64 cyclesToMs(s64 cycles) { |
| 60 | return cycles / (g_clock_rate_arm11 / 1000); | 119 | return cycles * 1000 / BASE_CLOCK_RATE; |
| 61 | } | 120 | } |
| 62 | 121 | ||
| 63 | namespace CoreTiming { | 122 | namespace CoreTiming { |
| 123 | |||
| 124 | /** | ||
| 125 | * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is | ||
| 126 | * required to end slice -1 and start slice 0 before the first cycle of code is executed. | ||
| 127 | */ | ||
| 64 | void Init(); | 128 | void Init(); |
| 65 | void Shutdown(); | 129 | void Shutdown(); |
| 66 | 130 | ||
| 67 | typedef void (*MHzChangeCallback)(); | ||
| 68 | typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; | 131 | typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; |
| 69 | 132 | ||
| 70 | /** | 133 | /** |
| 71 | * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time) | 134 | * This should only be called from the emu thread, if you are calling it any other thread, you are |
| 72 | * @param ticks Number of ticks to advance the CPU core | 135 | * doing something evil |
| 73 | */ | 136 | */ |
| 74 | void AddTicks(u64 ticks); | ||
| 75 | |||
| 76 | u64 GetTicks(); | 137 | u64 GetTicks(); |
| 77 | u64 GetIdleTicks(); | 138 | u64 GetIdleTicks(); |
| 78 | u64 GetGlobalTimeUs(); | 139 | void AddTicks(u64 ticks); |
| 140 | |||
| 141 | struct EventType; | ||
| 79 | 142 | ||
| 80 | /** | 143 | /** |
| 81 | * Registers an event type with the specified name and callback | 144 | * Returns the event_type identifier. if name is not unique, it will assert. |
| 82 | * @param name Name of the event type | ||
| 83 | * @param callback Function that will execute when this event fires | ||
| 84 | * @returns An identifier for the event type that was registered | ||
| 85 | */ | 145 | */ |
| 86 | int RegisterEvent(const char* name, TimedCallback callback); | 146 | EventType* RegisterEvent(const std::string& name, TimedCallback callback); |
| 87 | /// For save states. | ||
| 88 | void RestoreRegisterEvent(int event_type, const char* name, TimedCallback callback); | ||
| 89 | void UnregisterAllEvents(); | 147 | void UnregisterAllEvents(); |
| 90 | 148 | ||
| 91 | /// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk, | ||
| 92 | /// when we implement state saves. | ||
| 93 | /** | 149 | /** |
| 94 | * Schedules an event to run after the specified number of cycles, | 150 | * After the first Advance, the slice lengths and the downcount will be reduced whenever an event |
| 95 | * with an optional parameter to be passed to the callback handler. | 151 | * is scheduled earlier than the current values. |
| 96 | * This must be run ONLY from within the cpu thread. | 152 | * Scheduling from a callback will not update the downcount until the Advance() completes. |
| 97 | * @param cycles_into_future The number of cycles after which this event will be fired | ||
| 98 | * @param event_type The event type to fire, as returned from RegisterEvent | ||
| 99 | * @param userdata Optional parameter to pass to the callback when fired | ||
| 100 | */ | 153 | */ |
| 101 | void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata = 0); | 154 | void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0); |
| 102 | |||
| 103 | void ScheduleEvent_Threadsafe(s64 cycles_into_future, int event_type, u64 userdata = 0); | ||
| 104 | void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata = 0); | ||
| 105 | 155 | ||
| 106 | /** | 156 | /** |
| 107 | * Unschedules an event with the specified type and userdata | 157 | * This is to be called when outside of hle threads, such as the graphics thread, wants to |
| 108 | * @param event_type The type of event to unschedule, as returned from RegisterEvent | 158 | * schedule things to be executed on the main thread. |
| 109 | * @param userdata The userdata that identifies this event, as passed to ScheduleEvent | 159 | * Not that this doesn't change slice_length and thus events scheduled by this might be called |
| 110 | * @returns The remaining ticks until the next invocation of the event callback | 160 | * with a delay of up to MAX_SLICE_LENGTH |
| 111 | */ | 161 | */ |
| 112 | s64 UnscheduleEvent(int event_type, u64 userdata); | 162 | void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata); |
| 163 | |||
| 164 | void UnscheduleEvent(const EventType* event_type, u64 userdata); | ||
| 113 | 165 | ||
| 114 | s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata); | 166 | /// We only permit one event of each type in the queue at a time. |
| 167 | void RemoveEvent(const EventType* event_type); | ||
| 168 | void RemoveNormalAndThreadsafeEvent(const EventType* event_type); | ||
| 115 | 169 | ||
| 116 | void RemoveEvent(int event_type); | 170 | /** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends |
| 117 | void RemoveThreadsafeEvent(int event_type); | 171 | * the previous timing slice and begins the next one, you must Advance from the previous |
| 118 | void RemoveAllEvents(int event_type); | 172 | * slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an |
| 119 | bool IsScheduled(int event_type); | 173 | * Advance() is required to initialize the slice length before the first cycle of emulated |
| 120 | /// Runs any pending events and updates downcount for the next slice of cycles | 174 | * instructions is executed. |
| 175 | */ | ||
| 121 | void Advance(); | 176 | void Advance(); |
| 122 | void MoveEvents(); | 177 | void MoveEvents(); |
| 123 | void ProcessFifoWaitEvents(); | ||
| 124 | void ForceCheck(); | ||
| 125 | 178 | ||
| 126 | /// Pretend that the main CPU has executed enough cycles to reach the next event. | 179 | /// Pretend that the main CPU has executed enough cycles to reach the next event. |
| 127 | void Idle(int maxIdle = 0); | 180 | void Idle(); |
| 128 | 181 | ||
| 129 | /// Clear all pending events. This should ONLY be done on exit or state load. | 182 | /// Clear all pending events. This should ONLY be done on exit. |
| 130 | void ClearPendingEvents(); | 183 | void ClearPendingEvents(); |
| 131 | 184 | ||
| 132 | void LogPendingEvents(); | 185 | void ForceExceptionCheck(s64 cycles); |
| 133 | |||
| 134 | /// Warning: not included in save states. | ||
| 135 | void RegisterAdvanceCallback(void (*callback)(int cycles_executed)); | ||
| 136 | void RegisterMHzChangeCallback(MHzChangeCallback callback); | ||
| 137 | 186 | ||
| 138 | std::string GetScheduledEventsSummary(); | 187 | u64 GetGlobalTimeUs(); |
| 139 | 188 | ||
| 140 | void SetClockFrequencyMHz(int cpu_mhz); | 189 | int GetDowncount(); |
| 141 | int GetClockFrequencyMHz(); | ||
| 142 | extern int g_slice_length; | ||
| 143 | 190 | ||
| 144 | } // namespace | 191 | } // namespace CoreTiming |