diff options
| author | Pierre Bourdon <delroth@gmail.com> | 2018-08-27 02:34:09 +0200 |
|---|---|---|
| committer | Pierre Bourdon <delroth@gmail.com> | 2018-10-27 15:14:59 +0200 |
| commit | 64e04eb38c934418fcb78226ec4fe820b06bde33 (patch) | |
| tree | 1797995c8819184f95773a02713b6ad638eef369 /Source/Core | |
| parent | eadb4a66a5db5939df89e71a64edf436737c2cbb (diff) | |
SystemTimers: export performance index from the throttler callback
Diffstat (limited to 'Source/Core')
| -rw-r--r-- | Source/Core/Core/HW/SystemTimers.cpp | 176 | ||||
| -rw-r--r-- | Source/Core/Core/HW/SystemTimers.h | 11 |
2 files changed, 124 insertions, 63 deletions
diff --git a/Source/Core/Core/HW/SystemTimers.cpp b/Source/Core/Core/HW/SystemTimers.cpp index 5d4fe2cf5a..d12b413f00 100644 --- a/Source/Core/Core/HW/SystemTimers.cpp +++ b/Source/Core/Core/HW/SystemTimers.cpp @@ -45,6 +45,7 @@ IPC_HLE_PERIOD: For the Wii Remote this is the call schedule: #include "Core/HW/SystemTimers.h" +#include <cfloat> #include <cmath> #include <cstdlib> @@ -68,36 +69,43 @@ IPC_HLE_PERIOD: For the Wii Remote this is the call schedule: namespace SystemTimers { -static CoreTiming::EventType* et_Dec; -static CoreTiming::EventType* et_VI; -static CoreTiming::EventType* et_AudioDMA; -static CoreTiming::EventType* et_DSP; -static CoreTiming::EventType* et_IPC_HLE; +namespace +{ +CoreTiming::EventType* et_Dec; +CoreTiming::EventType* et_VI; +CoreTiming::EventType* et_AudioDMA; +CoreTiming::EventType* et_DSP; +CoreTiming::EventType* et_IPC_HLE; // PatchEngine updates every 1/60th of a second by default -static CoreTiming::EventType* et_PatchEngine; -static CoreTiming::EventType* et_Throttle; +CoreTiming::EventType* et_PatchEngine; +CoreTiming::EventType* et_Throttle; -static u32 s_cpu_core_clock = 486000000u; // 486 mhz (its not 485, stop bugging me!) +u32 s_cpu_core_clock = 486000000u; // 486 mhz (its not 485, stop bugging me!) // These two are badly educated guesses. // Feel free to experiment. Set them in Init below. // This is a fixed value, don't change it -static int s_audio_dma_period; +int s_audio_dma_period; // This is completely arbitrary. If we find that we need lower latency, // we can just increase this number. -static int s_ipc_hle_period; +int s_ipc_hle_period; // Custom RTC -static s64 s_localtime_rtc_offset = 0; +s64 s_localtime_rtc_offset = 0; -u32 GetTicksPerSecond() -{ - return s_cpu_core_clock; -} +// For each emulated milliseconds, what was the real time timestamp (excluding sleep time). This is +// a "special" ring buffer where we only need to read the first and last value. +std::array<u64, 1000> s_emu_to_real_time_ring_buffer; +size_t s_emu_to_real_time_index; +std::mutex s_emu_to_real_time_mutex; + +// How much time was spent sleeping since the emulator started. Note: this does not need to be reset +// at initialization (or ever), since only the "derivative" of that value really matters. +u64 s_time_spent_sleeping; // DSP/CPU timeslicing. -static void DSPCallback(u64 userdata, s64 cyclesLate) +void DSPCallback(u64 userdata, s64 cyclesLate) { // splits up the cycle budget in case lle is used // for hle, just gives all of the slice to hle @@ -105,14 +113,14 @@ static void DSPCallback(u64 userdata, s64 cyclesLate) CoreTiming::ScheduleEvent(DSP::GetDSPEmulator()->DSP_UpdateRate() - cyclesLate, et_DSP); } -static void AudioDMACallback(u64 userdata, s64 cyclesLate) +void AudioDMACallback(u64 userdata, s64 cyclesLate) { int period = s_cpu_core_clock / (AudioInterface::GetAIDSampleRate() * 4 / 32); DSP::UpdateAudioDMA(); // Push audio to speakers. CoreTiming::ScheduleEvent(period - cyclesLate, et_AudioDMA); } -static void IPC_HLE_UpdateCallback(u64 userdata, s64 cyclesLate) +void IPC_HLE_UpdateCallback(u64 userdata, s64 cyclesLate) { if (SConfig::GetInstance().bWii) { @@ -121,56 +129,19 @@ static void IPC_HLE_UpdateCallback(u64 userdata, s64 cyclesLate) } } -static void VICallback(u64 userdata, s64 cyclesLate) +void VICallback(u64 userdata, s64 cyclesLate) { VideoInterface::Update(CoreTiming::GetTicks() - cyclesLate); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerHalfLine() - cyclesLate, et_VI); } -static void DecrementerCallback(u64 userdata, s64 cyclesLate) +void DecrementerCallback(u64 userdata, s64 cyclesLate) { PowerPC::ppcState.spr[SPR_DEC] = 0xFFFFFFFF; PowerPC::ppcState.Exceptions |= EXCEPTION_DECREMENTER; } -void DecrementerSet() -{ - u32 decValue = PowerPC::ppcState.spr[SPR_DEC]; - - CoreTiming::RemoveEvent(et_Dec); - if ((decValue & 0x80000000) == 0) - { - CoreTiming::SetFakeDecStartTicks(CoreTiming::GetTicks()); - CoreTiming::SetFakeDecStartValue(decValue); - - CoreTiming::ScheduleEvent(decValue * TIMER_RATIO, et_Dec); - } -} - -u32 GetFakeDecrementer() -{ - return (CoreTiming::GetFakeDecStartValue() - - (u32)((CoreTiming::GetTicks() - CoreTiming::GetFakeDecStartTicks()) / TIMER_RATIO)); -} - -void TimeBaseSet() -{ - CoreTiming::SetFakeTBStartTicks(CoreTiming::GetTicks()); - CoreTiming::SetFakeTBStartValue(PowerPC::ReadFullTimeBaseValue()); -} - -u64 GetFakeTimeBase() -{ - return CoreTiming::GetFakeTBStartValue() + - ((CoreTiming::GetTicks() - CoreTiming::GetFakeTBStartTicks()) / TIMER_RATIO); -} - -s64 GetLocalTimeRTCOffset() -{ - return s_localtime_rtc_offset; -} - -static void PatchEngineCallback(u64 userdata, s64 cycles_late) +void PatchEngineCallback(u64 userdata, s64 cycles_late) { // We have 2 periods, a 1000 cycle error period and the VI period. // We have to carefully combine these together so that we stay on the VI period without drifting. @@ -195,7 +166,7 @@ static void PatchEngineCallback(u64 userdata, s64 cycles_late) CoreTiming::ScheduleEvent(next_schedule, et_PatchEngine, cycles_pruned); } -static void ThrottleCallback(u64 last_time, s64 cyclesLate) +void ThrottleCallback(u64 last_time, s64 cyclesLate) { // Allow the GPU thread to sleep. Setting this flag here limits the wakeups to 1 kHz. Fifo::GpuMaySleep(); @@ -206,6 +177,14 @@ static void ThrottleCallback(u64 last_time, s64 cyclesLate) const SConfig& config = SConfig::GetInstance(); bool frame_limiter = config.m_EmulationSpeed > 0.0f && !Core::GetIsThrottlerTempDisabled(); u32 next_event = GetTicksPerSecond() / 1000; + + { + std::lock_guard<std::mutex> lk(s_emu_to_real_time_mutex); + s_emu_to_real_time_ring_buffer[s_emu_to_real_time_index] = time - s_time_spent_sleeping; + s_emu_to_real_time_index = + (s_emu_to_real_time_index + 1) % s_emu_to_real_time_ring_buffer.size(); + } + if (frame_limiter) { if (config.m_EmulationSpeed != 1.0f) @@ -213,15 +192,86 @@ static void ThrottleCallback(u64 last_time, s64 cyclesLate) const s64 max_fallback = config.iTimingVariance * 1000; if (abs(diff) > max_fallback) { - DEBUG_LOG(COMMON, "system too %s, %d ms skipped", diff < 0 ? "slow" : "fast", + DEBUG_LOG(COMMON, "system too %s, %ld ms skipped", diff < 0 ? "slow" : "fast", abs(diff) - max_fallback); last_time = time - max_fallback; } - else if ((diff / 1000) > 0) + else if (diff > 1000) + { Common::SleepCurrentThread(diff / 1000); + s_time_spent_sleeping += Common::Timer::GetTimeUs() - time; + } } CoreTiming::ScheduleEvent(next_event - cyclesLate, et_Throttle, last_time + 1000); } +} // namespace + +u32 GetTicksPerSecond() +{ + return s_cpu_core_clock; +} + +void DecrementerSet() +{ + u32 decValue = PowerPC::ppcState.spr[SPR_DEC]; + + CoreTiming::RemoveEvent(et_Dec); + if ((decValue & 0x80000000) == 0) + { + CoreTiming::SetFakeDecStartTicks(CoreTiming::GetTicks()); + CoreTiming::SetFakeDecStartValue(decValue); + + CoreTiming::ScheduleEvent(decValue * TIMER_RATIO, et_Dec); + } +} + +u32 GetFakeDecrementer() +{ + return (CoreTiming::GetFakeDecStartValue() - + (u32)((CoreTiming::GetTicks() - CoreTiming::GetFakeDecStartTicks()) / TIMER_RATIO)); +} + +void TimeBaseSet() +{ + CoreTiming::SetFakeTBStartTicks(CoreTiming::GetTicks()); + CoreTiming::SetFakeTBStartValue(PowerPC::ReadFullTimeBaseValue()); +} + +u64 GetFakeTimeBase() +{ + return CoreTiming::GetFakeTBStartValue() + + ((CoreTiming::GetTicks() - CoreTiming::GetFakeTBStartTicks()) / TIMER_RATIO); +} + +s64 GetLocalTimeRTCOffset() +{ + return s_localtime_rtc_offset; +} + +double GetEstimatedEmulationPerformance() +{ + u64 ts_now, ts_before; // In microseconds + { + std::lock_guard<std::mutex> lk(s_emu_to_real_time_mutex); + size_t index_now = s_emu_to_real_time_index == 0 ? s_emu_to_real_time_ring_buffer.size() : + s_emu_to_real_time_index - 1; + size_t index_before = s_emu_to_real_time_index; + + ts_now = s_emu_to_real_time_ring_buffer[index_now]; + ts_before = s_emu_to_real_time_ring_buffer[index_before]; + } + + if (ts_before == 0) + { + // Not enough data yet to estimate. We could technically provide an estimate based on a shorter + // time horizon, but it's not really worth it. + return 1.0; + } + + u64 delta_us = ts_now - ts_before; + double emulated_us = s_emu_to_real_time_ring_buffer.size() * 1000.0; // For each emulated ms. + return delta_us == 0 ? DBL_MAX : emulated_us / delta_us; +} // split from Init to break a circular dependency between VideoInterface::Init and // SystemTimers::Init @@ -288,6 +338,8 @@ void Init() if (SConfig::GetInstance().bWii) CoreTiming::ScheduleEvent(s_ipc_hle_period, et_IPC_HLE); + + s_emu_to_real_time_ring_buffer.fill(0); } void Shutdown() @@ -296,4 +348,4 @@ void Shutdown() s_localtime_rtc_offset = 0; } -} // namespace +} // namespace SystemTimers diff --git a/Source/Core/Core/HW/SystemTimers.h b/Source/Core/Core/HW/SystemTimers.h index 5ab65c809b..8340047b72 100644 --- a/Source/Core/Core/HW/SystemTimers.h +++ b/Source/Core/Core/HW/SystemTimers.h @@ -53,4 +53,13 @@ void TimeBaseSet(); u64 GetFakeTimeBase(); // Custom RTC s64 GetLocalTimeRTCOffset(); -} + +// Returns an estimate of how fast/slow the emulation is running (excluding throttling induced sleep +// time). The estimate is computed over the last 1s of emulated time. Example values: +// +// - 0.5: the emulator is running at 50% speed (falling behind). +// - 1.0: the emulator is running at 100% speed. +// - 2.0: the emulator is running at 200% speed (or 100% speed but sleeping half of the time). +double GetEstimatedEmulationPerformance(); + +} // namespace SystemTimers |
