Somewhat functional clock timing setting
Some checks failed
eden-build / windows (msvc) (pull_request) Successful in 35m58s
eden-build / source (pull_request) Has been cancelled
eden-build / linux (pull_request) Has been cancelled
eden-build / android (pull_request) Has been cancelled
eden-license / license-header (pull_request_target) Has been cancelled

hangs when clock timing or both is set

Signed-off-by: swurl <swurl@swurl.xyz>
This commit is contained in:
swurl 2025-04-17 13:33:49 -04:00
parent 449edd5224
commit ec60d21616
Signed by: crueter
GPG key ID: A5A7629F109C8FD1
10 changed files with 64 additions and 60 deletions

View file

@ -231,11 +231,16 @@ struct Values {
CpuAccuracy::Auto, CpuAccuracy::Paranoid, CpuAccuracy::Auto, CpuAccuracy::Paranoid,
"cpu_accuracy", Category::Cpu}; "cpu_accuracy", Category::Cpu};
SwitchableSetting<u32, true> cpu_clock_rate{linkage, 1020, SwitchableSetting<u32, true> cpu_clock_rate{linkage, 1,
500, 1785, 1, 6,
"cpu_clock_rate", Category::Cpu, "cpu_clock_rate", Category::Cpu,
Specialization::Scalar}; Specialization::Scalar};
SwitchableSetting<CpuClockStrategy, true> cpu_clock_strategy{linkage, CpuClockStrategy::Clock,
CpuClockStrategy::None, CpuClockStrategy::Both,
"cpu_clock_strategy", Category::Cpu};
SwitchableSetting<bool> cpu_debug_mode{linkage, false, "cpu_debug_mode", Category::CpuDebug}; SwitchableSetting<bool> cpu_debug_mode{linkage, false, "cpu_debug_mode", Category::CpuDebug};
Setting<bool> cpuopt_page_tables{linkage, true, "cpuopt_page_tables", Category::CpuDebug}; Setting<bool> cpuopt_page_tables{linkage, true, "cpuopt_page_tables", Category::CpuDebug};

View file

@ -134,6 +134,8 @@ ENUM(CpuBackend, Dynarmic, Nce);
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
ENUM(CpuClockStrategy, None, Clock, Timing, Both)
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);

View file

@ -8,7 +8,6 @@
#include <ratio> #include <ratio>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hardware_properties.h"
namespace Common { namespace Common {
@ -16,9 +15,7 @@ class WallClock {
public: public:
static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz
static inline u64 CPUTickFreq() { static constexpr u64 CPUTickFreq = 1'020'000; // CPU Frequency = 1020.0MHz
return Core::Hardware::BASE_CLOCK_RATE();
}
virtual ~WallClock() = default; virtual ~WallClock() = default;
@ -54,19 +51,19 @@ public:
// Cycle Timing // Cycle Timing
static inline u64 CPUTickToNS(u64 cpu_tick) { static inline u64 CPUTickToNS(u64 cpu_tick) {
return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den(); return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den;
} }
static inline u64 CPUTickToUS(u64 cpu_tick) { static inline u64 CPUTickToUS(u64 cpu_tick) {
return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den(); return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den;
} }
static inline u64 CPUTickToCNTPCT(u64 cpu_tick) { static inline u64 CPUTickToCNTPCT(u64 cpu_tick) {
return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den(); return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den;
} }
static inline u64 CPUTickToGPUTick(u64 cpu_tick) { static inline u64 CPUTickToGPUTick(u64 cpu_tick) {
return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den(); return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den;
} }
protected: protected:
@ -80,34 +77,10 @@ protected:
using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>; using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>;
// Cycle Timing // Cycle Timing
using CPUTickToNsRatio = std::ratio<std::nano::den, CPUTickFreq>;
struct CPUTickToNsRatio { using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>;
static inline std::intmax_t num = std::nano::den; using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>;
static inline std::intmax_t den() { using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>;
return CPUTickFreq();
}
};
struct CPUTickToUsRatio {
static inline std::intmax_t num = std::micro::den;
static inline std::intmax_t den() {
return CPUTickFreq();
}
};
struct CPUTickToCNTPCTRatio {
static inline std::intmax_t num = CNTFRQ;
static inline std::intmax_t den() {
return CPUTickFreq();
}
};
struct CPUTickToGPUTickRatio {
static inline std::intmax_t num = GPUTickFreq;
static inline std::intmax_t den() {
return CPUTickFreq();
}
};
}; };
std::unique_ptr<WallClock> CreateOptimalClock(); std::unique_ptr<WallClock> CreateOptimalClock();

View file

@ -1,25 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <iostream>
#include "common/settings.h"
#ifdef _WIN32 #ifdef _WIN32
#include "common/windows/timer_resolution.h" #include "common/windows/timer_resolution.h"
#endif #endif
#ifdef ARCHITECTURE_x86_64 #ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_wait.h"
#endif #endif
#include "common/microprofile.h" #include "common/microprofile.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hardware_properties.h"
namespace Core::Timing { namespace Core::Timing {
int CoreTiming::rate_multiplier = 1;
int CoreTiming::time_multiplier = 1;
constexpr s64 MAX_SLICE_LENGTH = 10000; constexpr s64 MAX_SLICE_LENGTH = 10000;
std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
@ -185,14 +188,14 @@ void CoreTiming::ResetTicks() {
u64 CoreTiming::GetClockTicks() const { u64 CoreTiming::GetClockTicks() const {
if (is_multicore) [[likely]] { if (is_multicore) [[likely]] {
return clock->GetCNTPCT(); return clock->GetCNTPCT() * rate_multiplier;
} }
return Common::WallClock::CPUTickToCNTPCT(cpu_ticks); return Common::WallClock::CPUTickToCNTPCT(cpu_ticks);
} }
u64 CoreTiming::GetGPUTicks() const { u64 CoreTiming::GetGPUTicks() const {
if (is_multicore) [[likely]] { if (is_multicore) [[likely]] {
return clock->GetGPUTick(); return clock->GetGPUTick() * rate_multiplier;
} }
return Common::WallClock::CPUTickToGPUTick(cpu_ticks); return Common::WallClock::CPUTickToGPUTick(cpu_ticks);
} }
@ -302,6 +305,7 @@ void CoreTiming::ThreadLoop() {
} }
void CoreTiming::Reset() { void CoreTiming::Reset() {
std::cout << "reset called" << std::endl;
paused = true; paused = true;
shutting_down = true; shutting_down = true;
pause_event.Set(); pause_event.Set();
@ -311,18 +315,27 @@ void CoreTiming::Reset() {
} }
timer_thread.reset(); timer_thread.reset();
has_started = false; has_started = false;
auto clock_strategy = Settings::values.cpu_clock_strategy.GetValue();
auto multiplier = Settings::values.cpu_clock_rate.GetValue();
time_multiplier = (clock_strategy == Settings::CpuClockStrategy::Timing || clock_strategy == Settings::CpuClockStrategy::Both) ? multiplier : 1;
rate_multiplier = (clock_strategy == Settings::CpuClockStrategy::Clock || clock_strategy == Settings::CpuClockStrategy::Both) ? multiplier : 1;
std::cout << "reset finished " << time_multiplier << " " << rate_multiplier << " " << (int) clock_strategy << " " << multiplier << " " << this << std::endl;
} }
std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
if (is_multicore) [[likely]] { if (is_multicore) [[likely]] {
return clock->GetTimeNS(); LOG_INFO(Core, "GLOBAL TIME MULTIPLIER {:X}", time_multiplier);
return clock->GetTimeNS() * CoreTiming::time_multiplier;
} }
return std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)}; return std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)};
} }
std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
if (is_multicore) [[likely]] { if (is_multicore) [[likely]] {
return clock->GetTimeUS(); return clock->GetTimeUS() * time_multiplier;
} }
return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)}; return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
} }

View file

@ -15,6 +15,7 @@
#include <boost/heap/fibonacci_heap.hpp> #include <boost/heap/fibonacci_heap.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/settings_enums.h"
#include "common/thread.h" #include "common/thread.h"
#include "common/wall_clock.h" #include "common/wall_clock.h"
@ -178,6 +179,10 @@ private:
/// Cycle timing /// Cycle timing
u64 cpu_ticks{}; u64 cpu_ticks{};
s64 downcount{}; s64 downcount{};
// Clock multipliers
static int rate_multiplier;
static int time_multiplier;
}; };
/// Creates a core timing event with the given name and callback. /// Creates a core timing event with the given name and callback.

View file

@ -5,22 +5,15 @@
#pragma once #pragma once
#include <array> #include <array>
#include <iostream>
#include "common/bit_util.h" #include "common/bit_util.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/settings.h"
#include "common/logging/log.h"
namespace Core { namespace Core {
namespace Hardware { namespace Hardware {
inline u64 BASE_CLOCK_RATE() { constexpr u64 BASE_CLOCK_RATE = 1'020'000; // CPU Frequency = 1020.0 MHz
LOG_DEBUG(Core, "Settings reported clock rate={:08X}", Settings::values.cpu_clock_rate.GetValue());
// std::cout << Settings::values.cpu_clock_rate.GetValue() << std::endl;
return Settings::values.cpu_clock_rate.GetValue() * 1'000'000;
}
constexpr u64 CNTFREQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz constexpr u64 CNTFREQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores

View file

@ -80,10 +80,6 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa
void Controller::SetClockSpeed(u32 mhz) { void Controller::SetClockSpeed(u32 mhz) {
LOG_DEBUG(Service_APM, "called, mhz={:08X}", mhz); LOG_DEBUG(Service_APM, "called, mhz={:08X}", mhz);
// TODO: needs to be verified
// Settings::values.cpu_clock_rate.SetGlobal(false);
// Settings::values.cpu_clock_rate.SetValue(mhz);
} }
} // namespace Service::APM } // namespace Service::APM

View file

@ -9,7 +9,7 @@
#include "core/hle/service/vi/display_list.h" #include "core/hle/service/vi/display_list.h"
#include "core/hle/service/vi/vsync_manager.h" #include "core/hle/service/vi/vsync_manager.h"
constexpr auto FrameNs = std::chrono::nanoseconds{1000000000 / 60}; constexpr auto FrameNs = std::chrono::nanoseconds{1'000'000'000 / 60};
namespace Service::VI { namespace Service::VI {

View file

@ -74,7 +74,8 @@ void ConfigureCpu::Setup(const ConfigurationShared::Builder& builder) {
} else if (setting->Id() == Settings::values.cpu_backend.Id()) { } else if (setting->Id() == Settings::values.cpu_backend.Id()) {
backend_layout->addWidget(widget); backend_layout->addWidget(widget);
backend_combobox = widget->combobox; backend_combobox = widget->combobox;
} else if (setting->Id() == Settings::values.cpu_clock_rate.Id()) { } else if (setting->Id() == Settings::values.cpu_clock_rate.Id()
|| setting->Id() == Settings::values.cpu_clock_strategy.Id()) {
clock_layout->addWidget(widget); clock_layout->addWidget(widget);
} else { } else {
// Presently, all other settings here are unsafe checkboxes // Presently, all other settings here are unsafe checkboxes

View file

@ -77,8 +77,14 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
tr("This setting controls the accuracy of the emulated CPU.\nDon't change this unless " tr("This setting controls the accuracy of the emulated CPU.\nDon't change this unless "
"you know what you are doing.")); "you know what you are doing."));
INSERT(Settings, cpu_backend, tr("Backend:"), QStringLiteral()); INSERT(Settings, cpu_backend, tr("Backend:"), QStringLiteral());
INSERT(Settings, cpu_clock_rate, tr("CPU Clock Rate (MHz):"), tr("Increasing CPU clock rate may " INSERT(Settings, cpu_clock_rate, tr("CPU Clock Multiplier:"), tr("Increasing CPU clock speed may "
"improve performance, but may also reduce stability.")); "improve performance, but may also reduce stability."));
INSERT(Settings, cpu_clock_strategy, tr("Clock Strategy:"),
tr("Some games use clock rate for timings, others use clock timing, and some even use both.\n"
"Generally, this setting will increase a game's default FPS, but may cause unintended\n"
"speedups or gameplay issues; for this reason, dedicated mods that unlock VSync and\n"
"enable dynamic FPS are preferred over this setting.\nOnly change if you know what you're "
"doing."));
// Cpu Debug // Cpu Debug
@ -356,6 +362,16 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
PAIR(CpuAccuracy, Unsafe, tr("Unsafe")), PAIR(CpuAccuracy, Unsafe, tr("Unsafe")),
PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")), PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")),
}}); }});
translations->insert(
{Settings::EnumMetadata<Settings::CpuClockStrategy>::Index(),
{
PAIR(CpuClockStrategy, None, tr("None")),
PAIR(CpuClockStrategy, Clock, tr("Clock Rate")),
PAIR(CpuClockStrategy, Timing, tr("Clock Timing")),
PAIR(CpuClockStrategy, Both, tr("Both")),
}});
translations->insert({Settings::EnumMetadata<Settings::CpuBackend>::Index(), translations->insert({Settings::EnumMetadata<Settings::CpuBackend>::Index(),
{ {
PAIR(CpuBackend, Dynarmic, tr("Dynarmic")), PAIR(CpuBackend, Dynarmic, tr("Dynarmic")),