summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorPierre Bourdon <delroth@gmail.com>2018-08-27 02:34:09 +0200
committerPierre Bourdon <delroth@gmail.com>2018-10-27 15:14:59 +0200
commit64e04eb38c934418fcb78226ec4fe820b06bde33 (patch)
tree1797995c8819184f95773a02713b6ad638eef369 /Source/Core
parenteadb4a66a5db5939df89e71a64edf436737c2cbb (diff)
SystemTimers: export performance index from the throttler callback
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/HW/SystemTimers.cpp176
-rw-r--r--Source/Core/Core/HW/SystemTimers.h11
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