mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-04-28 21:08:04 +03:00
Merge d5114e7191
into 8ee64a84c7
This commit is contained in:
commit
ea97b2d11b
6 changed files with 120 additions and 53 deletions
|
@ -45,6 +45,7 @@ const Info<bool> MAIN_ACCURATE_CPU_CACHE{{System::Main, "Core", "AccurateCPUCach
|
||||||
const Info<bool> MAIN_DSP_HLE{{System::Main, "Core", "DSPHLE"}, true};
|
const Info<bool> MAIN_DSP_HLE{{System::Main, "Core", "DSPHLE"}, true};
|
||||||
const Info<int> MAIN_MAX_FALLBACK{{System::Main, "Core", "MaxFallback"}, 100};
|
const Info<int> MAIN_MAX_FALLBACK{{System::Main, "Core", "MaxFallback"}, 100};
|
||||||
const Info<int> MAIN_TIMING_VARIANCE{{System::Main, "Core", "TimingVariance"}, 40};
|
const Info<int> MAIN_TIMING_VARIANCE{{System::Main, "Core", "TimingVariance"}, 40};
|
||||||
|
const Info<bool> MAIN_CORRECT_TIME_DRIFT{{System::Main, "Core", "CorrectTimeDrift"}, false};
|
||||||
const Info<bool> MAIN_CPU_THREAD{{System::Main, "Core", "CPUThread"}, true};
|
const Info<bool> MAIN_CPU_THREAD{{System::Main, "Core", "CPUThread"}, true};
|
||||||
const Info<bool> MAIN_SYNC_ON_SKIP_IDLE{{System::Main, "Core", "SyncOnSkipIdle"}, true};
|
const Info<bool> MAIN_SYNC_ON_SKIP_IDLE{{System::Main, "Core", "SyncOnSkipIdle"}, true};
|
||||||
const Info<std::string> MAIN_DEFAULT_ISO{{System::Main, "Core", "DefaultISO"}, ""};
|
const Info<std::string> MAIN_DEFAULT_ISO{{System::Main, "Core", "DefaultISO"}, ""};
|
||||||
|
|
|
@ -63,6 +63,7 @@ extern const Info<bool> MAIN_ACCURATE_CPU_CACHE;
|
||||||
extern const Info<bool> MAIN_DSP_HLE;
|
extern const Info<bool> MAIN_DSP_HLE;
|
||||||
extern const Info<int> MAIN_MAX_FALLBACK;
|
extern const Info<int> MAIN_MAX_FALLBACK;
|
||||||
extern const Info<int> MAIN_TIMING_VARIANCE;
|
extern const Info<int> MAIN_TIMING_VARIANCE;
|
||||||
|
extern const Info<bool> MAIN_CORRECT_TIME_DRIFT;
|
||||||
extern const Info<bool> MAIN_CPU_THREAD;
|
extern const Info<bool> MAIN_CPU_THREAD;
|
||||||
extern const Info<bool> MAIN_SYNC_ON_SKIP_IDLE;
|
extern const Info<bool> MAIN_SYNC_ON_SKIP_IDLE;
|
||||||
extern const Info<std::string> MAIN_DEFAULT_ISO;
|
extern const Info<std::string> MAIN_DEFAULT_ISO;
|
||||||
|
|
|
@ -81,12 +81,6 @@ void CoreTimingManager::UnregisterAllEvents()
|
||||||
|
|
||||||
void CoreTimingManager::Init()
|
void CoreTimingManager::Init()
|
||||||
{
|
{
|
||||||
m_registered_config_callback_id =
|
|
||||||
CPUThreadConfigCallback::AddConfigChangedCallback([this]() { RefreshConfig(); });
|
|
||||||
RefreshConfig();
|
|
||||||
|
|
||||||
m_last_oc_factor = m_config_oc_factor;
|
|
||||||
m_globals.last_OC_factor_inverted = m_config_oc_inv_factor;
|
|
||||||
m_system.GetPPCState().downcount = CyclesToDowncount(MAX_SLICE_LENGTH);
|
m_system.GetPPCState().downcount = CyclesToDowncount(MAX_SLICE_LENGTH);
|
||||||
m_globals.slice_length = MAX_SLICE_LENGTH;
|
m_globals.slice_length = MAX_SLICE_LENGTH;
|
||||||
m_globals.global_timer = 0;
|
m_globals.global_timer = 0;
|
||||||
|
@ -103,6 +97,13 @@ void CoreTimingManager::Init()
|
||||||
|
|
||||||
m_event_fifo_id = 0;
|
m_event_fifo_id = 0;
|
||||||
m_ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback);
|
m_ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback);
|
||||||
|
|
||||||
|
m_registered_config_callback_id =
|
||||||
|
CPUThreadConfigCallback::AddConfigChangedCallback([this]() { RefreshConfig(); });
|
||||||
|
RefreshConfig();
|
||||||
|
|
||||||
|
m_last_oc_factor = m_config_oc_factor;
|
||||||
|
m_globals.last_OC_factor_inverted = m_config_oc_inv_factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTimingManager::Shutdown()
|
void CoreTimingManager::Shutdown()
|
||||||
|
@ -127,16 +128,17 @@ void CoreTimingManager::RefreshConfig()
|
||||||
|
|
||||||
m_max_variance = std::chrono::duration_cast<DT>(DT_ms(Config::Get(Config::MAIN_TIMING_VARIANCE)));
|
m_max_variance = std::chrono::duration_cast<DT>(DT_ms(Config::Get(Config::MAIN_TIMING_VARIANCE)));
|
||||||
|
|
||||||
|
m_correct_time_drift = Config::Get(Config::MAIN_CORRECT_TIME_DRIFT);
|
||||||
|
|
||||||
if (AchievementManager::GetInstance().IsHardcoreModeActive() &&
|
if (AchievementManager::GetInstance().IsHardcoreModeActive() &&
|
||||||
Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f &&
|
Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f &&
|
||||||
Config::Get(Config::MAIN_EMULATION_SPEED) > 0.0f)
|
Config::Get(Config::MAIN_EMULATION_SPEED) > 0.0f)
|
||||||
{
|
{
|
||||||
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
|
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
|
||||||
m_emulation_speed = 1.0f;
|
|
||||||
OSD::AddMessage("Minimum speed is 100% in Hardcore Mode");
|
OSD::AddMessage("Minimum speed is 100% in Hardcore Mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_emulation_speed = Config::Get(Config::MAIN_EMULATION_SPEED);
|
UpdateSpeedLimit(GetTicks(), Config::Get(Config::MAIN_EMULATION_SPEED));
|
||||||
|
|
||||||
m_use_precision_timer = Config::Get(Config::MAIN_PRECISION_FRAME_TIMING);
|
m_use_precision_timer = Config::Get(Config::MAIN_PRECISION_FRAME_TIMING);
|
||||||
}
|
}
|
||||||
|
@ -197,13 +199,14 @@ void CoreTimingManager::DoState(PointerWrap& p)
|
||||||
// The exact layout of the heap in memory is implementation defined, therefore it is platform
|
// The exact layout of the heap in memory is implementation defined, therefore it is platform
|
||||||
// and library version specific.
|
// and library version specific.
|
||||||
std::ranges::make_heap(m_event_queue, std::ranges::greater{});
|
std::ranges::make_heap(m_event_queue, std::ranges::greater{});
|
||||||
|
|
||||||
// The stave state has changed the time, so our previous Throttle targets are invalid.
|
|
||||||
// Especially when global_time goes down; So we create a fake throttle update.
|
|
||||||
ResetThrottle(m_globals.global_timer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CoreTimingManager::Resume()
|
||||||
|
{
|
||||||
|
ResetThrottle(m_globals.global_timer);
|
||||||
|
}
|
||||||
|
|
||||||
// This should only be called from the CPU thread. If you are calling
|
// This should only be called from the CPU thread. If you are calling
|
||||||
// it from any other thread, you are doing something evil
|
// it from any other thread, you are doing something evil
|
||||||
u64 CoreTimingManager::GetTicks() const
|
u64 CoreTimingManager::GetTicks() const
|
||||||
|
@ -355,20 +358,24 @@ void CoreTimingManager::Advance()
|
||||||
power_pc.CheckExternalExceptions();
|
power_pc.CheckExternalExceptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimePoint CoreTimingManager::CalculateTargetHostTimeInternal(s64 target_cycle)
|
||||||
|
{
|
||||||
|
const s64 elapsed_cycles = target_cycle - m_throttle_reference_cycle;
|
||||||
|
return m_throttle_reference_time +
|
||||||
|
Clock::duration{std::chrono::seconds{elapsed_cycles}} / m_throttle_adj_clock_per_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreTimingManager::IsSpeedUnlimited() const
|
||||||
|
{
|
||||||
|
return m_throttle_adj_clock_per_sec == 0 || Core::GetIsThrottlerTempDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
TimePoint CoreTimingManager::GetTargetHostTime(s64 target_cycle)
|
TimePoint CoreTimingManager::GetTargetHostTime(s64 target_cycle)
|
||||||
{
|
{
|
||||||
const double speed = Core::GetIsThrottlerTempDisabled() ? 0.0 : m_emulation_speed;
|
if (IsSpeedUnlimited())
|
||||||
|
|
||||||
if (speed > 0)
|
|
||||||
{
|
|
||||||
const s64 cycles = target_cycle - m_throttle_last_cycle;
|
|
||||||
return m_throttle_deadline + std::chrono::duration_cast<DT>(
|
|
||||||
DT_s(cycles) / (m_emulation_speed * m_throttle_clock_per_sec));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Clock::now();
|
return Clock::now();
|
||||||
}
|
|
||||||
|
return CalculateTargetHostTimeInternal(target_cycle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTimingManager::SleepUntil(TimePoint time_point)
|
void CoreTimingManager::SleepUntil(TimePoint time_point)
|
||||||
|
@ -399,44 +406,78 @@ void CoreTimingManager::SleepUntil(TimePoint time_point)
|
||||||
|
|
||||||
void CoreTimingManager::Throttle(const s64 target_cycle)
|
void CoreTimingManager::Throttle(const s64 target_cycle)
|
||||||
{
|
{
|
||||||
// Based on number of cycles and emulation speed, increase the target deadline
|
if (IsSpeedUnlimited())
|
||||||
const s64 cycles = target_cycle - m_throttle_last_cycle;
|
{
|
||||||
m_throttle_last_cycle = target_cycle;
|
ResetThrottle(target_cycle);
|
||||||
|
m_throttle_disable_vi_int = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const double speed = Core::GetIsThrottlerTempDisabled() ? 0.0 : m_emulation_speed;
|
// Push throttle reference values forward by exact seconds.
|
||||||
|
// This avoids drifting from cumulative rounding errors.
|
||||||
|
{
|
||||||
|
const s64 sec_adj = (target_cycle - m_throttle_reference_cycle) / m_throttle_adj_clock_per_sec;
|
||||||
|
const s64 cycle_adj = sec_adj * m_throttle_adj_clock_per_sec;
|
||||||
|
|
||||||
if (0.0 < speed)
|
m_throttle_reference_cycle += cycle_adj;
|
||||||
m_throttle_deadline +=
|
m_throttle_reference_time += std::chrono::seconds{sec_adj};
|
||||||
std::chrono::duration_cast<DT>(DT_s(cycles) / (speed * m_throttle_clock_per_sec));
|
}
|
||||||
|
|
||||||
|
TimePoint target_time = CalculateTargetHostTimeInternal(target_cycle);
|
||||||
|
|
||||||
const TimePoint time = Clock::now();
|
const TimePoint time = Clock::now();
|
||||||
const TimePoint min_deadline = time - m_max_fallback;
|
|
||||||
const TimePoint max_deadline = time + m_max_fallback;
|
|
||||||
|
|
||||||
if (m_throttle_deadline > max_deadline)
|
// "Correct Time Drift" setting prevents the timing relaxing.
|
||||||
|
const TimePoint min_target = m_correct_time_drift ? target_time : (time - m_max_fallback);
|
||||||
|
if (target_time < min_target)
|
||||||
{
|
{
|
||||||
m_throttle_deadline = max_deadline;
|
// Core is running too slow.. i.e. CPU bottleneck.
|
||||||
}
|
const DT adjustment = min_target - target_time;
|
||||||
else if (m_throttle_deadline < min_deadline)
|
DEBUG_LOG_FMT(CORE, "Core can not keep up with timings! [relaxing timings by {} us]",
|
||||||
{
|
DT_us(adjustment).count());
|
||||||
DEBUG_LOG_FMT(COMMON, "System can not to keep up with timings! [relaxing timings by {} us]",
|
|
||||||
DT_us(min_deadline - m_throttle_deadline).count());
|
m_throttle_reference_time += adjustment;
|
||||||
m_throttle_deadline = min_deadline;
|
target_time += adjustment;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimePoint vi_deadline = time - std::min(m_max_fallback, m_max_variance) / 2;
|
UpdateVISkip(time, target_time);
|
||||||
|
|
||||||
// Skip the VI interrupt if the CPU is lagging by a certain amount.
|
SleepUntil(target_time);
|
||||||
// It doesn't matter what amount of lag we skip VI at, as long as it's constant.
|
}
|
||||||
m_throttle_disable_vi_int = 0.0 < speed && m_throttle_deadline < vi_deadline;
|
|
||||||
|
|
||||||
SleepUntil(m_throttle_deadline);
|
void CoreTimingManager::UpdateSpeedLimit(s64 cycle, double new_speed)
|
||||||
|
{
|
||||||
|
m_emulation_speed = new_speed;
|
||||||
|
|
||||||
|
const u32 new_clock_per_sec =
|
||||||
|
std::lround(m_system.GetSystemTimers().GetTicksPerSecond() * new_speed);
|
||||||
|
|
||||||
|
const bool was_limited = m_throttle_adj_clock_per_sec != 0;
|
||||||
|
if (was_limited)
|
||||||
|
{
|
||||||
|
// Adjust throttle reference for graceful clock speed transition.
|
||||||
|
const s64 ticks = cycle - m_throttle_reference_cycle;
|
||||||
|
const s64 new_ticks = ticks * new_clock_per_sec / m_throttle_adj_clock_per_sec;
|
||||||
|
m_throttle_reference_cycle = cycle - new_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_throttle_adj_clock_per_sec = new_clock_per_sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTimingManager::ResetThrottle(s64 cycle)
|
void CoreTimingManager::ResetThrottle(s64 cycle)
|
||||||
{
|
{
|
||||||
m_throttle_last_cycle = cycle;
|
m_throttle_reference_cycle = cycle;
|
||||||
m_throttle_deadline = Clock::now();
|
m_throttle_reference_time = Clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreTimingManager::UpdateVISkip(TimePoint current_time, TimePoint target_time)
|
||||||
|
{
|
||||||
|
const DT vi_fallback = std::min(m_max_variance, m_max_fallback);
|
||||||
|
|
||||||
|
// Skip the VI interrupt if the CPU is lagging by a certain amount.
|
||||||
|
// It doesn't matter what amount of lag we skip VI at, as long as it's constant.
|
||||||
|
const TimePoint vi_target = current_time - vi_fallback / 2;
|
||||||
|
m_throttle_disable_vi_int = target_time < vi_target;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CoreTimingManager::GetVISkip() const
|
bool CoreTimingManager::GetVISkip() const
|
||||||
|
@ -465,12 +506,14 @@ void CoreTimingManager::AdjustEventQueueTimes(u32 new_ppc_clock, u32 old_ppc_clo
|
||||||
{
|
{
|
||||||
g_perf_metrics.AdjustClockSpeed(m_globals.global_timer, new_ppc_clock, old_ppc_clock);
|
g_perf_metrics.AdjustClockSpeed(m_globals.global_timer, new_ppc_clock, old_ppc_clock);
|
||||||
|
|
||||||
m_throttle_clock_per_sec = new_ppc_clock;
|
const s64 ticks = m_globals.global_timer;
|
||||||
|
|
||||||
|
UpdateSpeedLimit(ticks, m_emulation_speed);
|
||||||
|
|
||||||
for (Event& ev : m_event_queue)
|
for (Event& ev : m_event_queue)
|
||||||
{
|
{
|
||||||
const s64 ticks = (ev.time - m_globals.global_timer) * new_ppc_clock / old_ppc_clock;
|
const s64 ev_ticks = (ev.time - ticks) * new_ppc_clock / old_ppc_clock;
|
||||||
ev.time = m_globals.global_timer + ticks;
|
ev.time = ticks + ev_ticks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,10 @@ public:
|
||||||
void Init();
|
void Init();
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
|
// Needed when the host-time changes from the guest's perspective.
|
||||||
|
// e.g. state-load or resume-from-pause
|
||||||
|
void Resume();
|
||||||
|
|
||||||
// This should only be called from the CPU thread, if you are calling it any other thread, you are
|
// This should only be called from the CPU thread, if you are calling it any other thread, you are
|
||||||
// doing something evil
|
// doing something evil
|
||||||
u64 GetTicks() const;
|
u64 GetTicks() const;
|
||||||
|
@ -202,16 +206,21 @@ private:
|
||||||
float m_config_oc_inv_factor = 0.0f;
|
float m_config_oc_inv_factor = 0.0f;
|
||||||
bool m_config_sync_on_skip_idle = false;
|
bool m_config_sync_on_skip_idle = false;
|
||||||
|
|
||||||
s64 m_throttle_last_cycle = 0;
|
s64 m_throttle_reference_cycle = 0;
|
||||||
TimePoint m_throttle_deadline = Clock::now();
|
TimePoint m_throttle_reference_time = Clock::now();
|
||||||
s64 m_throttle_clock_per_sec = 0;
|
u32 m_throttle_adj_clock_per_sec = 0;
|
||||||
bool m_throttle_disable_vi_int = false;
|
bool m_throttle_disable_vi_int = false;
|
||||||
|
|
||||||
DT m_max_fallback = {};
|
DT m_max_fallback = {};
|
||||||
DT m_max_variance = {};
|
DT m_max_variance = {};
|
||||||
|
bool m_correct_time_drift = false;
|
||||||
double m_emulation_speed = 1.0;
|
double m_emulation_speed = 1.0;
|
||||||
|
|
||||||
|
bool IsSpeedUnlimited() const;
|
||||||
|
void UpdateSpeedLimit(s64 cycle, double new_speed);
|
||||||
void ResetThrottle(s64 cycle);
|
void ResetThrottle(s64 cycle);
|
||||||
|
TimePoint CalculateTargetHostTimeInternal(s64 target_cycle);
|
||||||
|
void UpdateVISkip(TimePoint current_time, TimePoint target_time);
|
||||||
|
|
||||||
int DowncountToCycles(int downcount) const;
|
int DowncountToCycles(int downcount) const;
|
||||||
int CyclesToDowncount(int cycles) const;
|
int CyclesToDowncount(int cycles) const;
|
||||||
|
|
|
@ -382,6 +382,7 @@ void PowerPCManager::SingleStep()
|
||||||
|
|
||||||
void PowerPCManager::RunLoop()
|
void PowerPCManager::RunLoop()
|
||||||
{
|
{
|
||||||
|
m_system.GetCoreTiming().Resume();
|
||||||
m_cpu_core_base->Run();
|
m_cpu_core_base->Run();
|
||||||
Host_UpdateDisasmDialog();
|
Host_UpdateDisasmDialog();
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,18 @@ void AdvancedPane::CreateLayout()
|
||||||
"needed.<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
|
"needed.<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
|
||||||
cpu_options_group_layout->addWidget(m_accurate_cpu_cache_checkbox);
|
cpu_options_group_layout->addWidget(m_accurate_cpu_cache_checkbox);
|
||||||
|
|
||||||
|
auto* const timing_group = new QGroupBox(tr("Timing"));
|
||||||
|
main_layout->addWidget(timing_group);
|
||||||
|
auto* timing_group_layout = new QVBoxLayout{timing_group};
|
||||||
|
auto* const correct_time_drift =
|
||||||
|
new ConfigBool{tr("Correct Time Drift"), Config::MAIN_CORRECT_TIME_DRIFT};
|
||||||
|
correct_time_drift->SetDescription(
|
||||||
|
tr("Allow the emulated console to run fast after stutters,"
|
||||||
|
"<br>pursuing accurate overall elapsed time unless paused or speed-adjusted."
|
||||||
|
"<br><br>This may be useful for internet play."
|
||||||
|
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
|
||||||
|
timing_group_layout->addWidget(correct_time_drift);
|
||||||
|
|
||||||
auto* clock_override = new QGroupBox(tr("Clock Override"));
|
auto* clock_override = new QGroupBox(tr("Clock Override"));
|
||||||
auto* clock_override_layout = new QVBoxLayout();
|
auto* clock_override_layout = new QVBoxLayout();
|
||||||
clock_override->setLayout(clock_override_layout);
|
clock_override->setLayout(clock_override_layout);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue