Merge pull request #5025 from jroweboy/tomoscrewme

Add CPU Clock Frequency slider
This commit is contained in:
Hamish Milne 2020-03-28 12:31:41 +00:00 committed by GitHub
commit 1ff8d002a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 204 additions and 84 deletions

View file

@ -104,6 +104,8 @@ void Config::ReadValues() {
// Core // Core
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
Settings::values.cpu_clock_percentage =
sdl2_config->GetInteger("Core", "cpu_clock_percentage", 100);
// Renderer // Renderer
Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", false); Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", false);

View file

@ -91,6 +91,12 @@ udp_pad_index=
# 0: Interpreter (slow), 1 (default): JIT (fast) # 0: Interpreter (slow), 1 (default): JIT (fast)
use_cpu_jit = use_cpu_jit =
# Change the Clock Frequency of the emulated 3DS CPU.
# Underclocking can increase the performance of the game at the risk of freezing.
# Overclocking may fix lag that happens on console, but also comes with the risk of freezing.
# Range is any positive integer (but we suspect 25 - 400 is a good idea) Default is 100
cpu_clock_percentage =
[Renderer] [Renderer]
# Whether to render using GLES or OpenGL # Whether to render using GLES or OpenGL
# 0 (default): OpenGL, 1: GLES # 0 (default): OpenGL, 1: GLES

View file

@ -253,6 +253,8 @@ void Config::ReadCoreValues() {
qt_config->beginGroup(QStringLiteral("Core")); qt_config->beginGroup(QStringLiteral("Core"));
Settings::values.use_cpu_jit = ReadSetting(QStringLiteral("use_cpu_jit"), true).toBool(); Settings::values.use_cpu_jit = ReadSetting(QStringLiteral("use_cpu_jit"), true).toBool();
Settings::values.cpu_clock_percentage =
ReadSetting(QStringLiteral("cpu_clock_percentage"), 100).toInt();
qt_config->endGroup(); qt_config->endGroup();
} }
@ -737,6 +739,8 @@ void Config::SaveCoreValues() {
qt_config->beginGroup(QStringLiteral("Core")); qt_config->beginGroup(QStringLiteral("Core"));
WriteSetting(QStringLiteral("use_cpu_jit"), Settings::values.use_cpu_jit, true); WriteSetting(QStringLiteral("use_cpu_jit"), Settings::values.use_cpu_jit, true);
WriteSetting(QStringLiteral("cpu_clock_percentage"), Settings::values.cpu_clock_percentage,
100);
qt_config->endGroup(); qt_config->endGroup();
} }

View file

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>345</width> <width>345</width>
<height>357</height> <height>358</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -68,6 +68,13 @@
<string>Emulation</string> <string>Emulation</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QCheckBox" name="toggle_frame_limit">
<property name="text">
<string>Limit Speed Percent</string>
</property>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
@ -119,13 +126,6 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QCheckBox" name="toggle_frame_limit">
<property name="text">
<string>Limit Speed Percent</string>
</property>
</widget>
</item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QSpinBox" name="frame_limit"> <widget class="QSpinBox" name="frame_limit">
<property name="suffix"> <property name="suffix">

View file

@ -217,6 +217,17 @@ static const std::array<const char*, 187> country_names = {
QT_TRANSLATE_NOOP("ConfigureSystem", "Bermuda"), // 180-186 QT_TRANSLATE_NOOP("ConfigureSystem", "Bermuda"), // 180-186
}; };
// The QSlider doesn't have an easy way to set a custom step amount,
// so we can just convert from the sliders range (0 - 79) to the expected
// settings range (5 - 400) with simple math.
static constexpr int SliderToSettings(int value) {
return 5 * value + 5;
}
static constexpr int SettingsToSlider(int value) {
return (value - 5) / 5;
}
ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) {
ui->setupUi(this); ui->setupUi(this);
connect(ui->combo_birthmonth, connect(ui->combo_birthmonth,
@ -233,6 +244,10 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
} }
} }
connect(ui->slider_clock_speed, &QSlider::valueChanged, [&](int value) {
ui->clock_display_label->setText(QStringLiteral("%1%").arg(SliderToSettings(value)));
});
ConfigureTime(); ConfigureTime();
} }
@ -258,6 +273,10 @@ void ConfigureSystem::SetConfiguration() {
ui->label_disable_info->hide(); ui->label_disable_info->hide();
} }
ui->slider_clock_speed->setValue(SettingsToSlider(Settings::values.cpu_clock_percentage));
ui->clock_display_label->setText(
QStringLiteral("%1%").arg(Settings::values.cpu_clock_percentage));
} }
void ConfigureSystem::ReadSystemSettings() { void ConfigureSystem::ReadSystemSettings() {
@ -299,10 +318,7 @@ void ConfigureSystem::ReadSystemSettings() {
} }
void ConfigureSystem::ApplyConfiguration() { void ConfigureSystem::ApplyConfiguration() {
if (!enabled) { if (enabled) {
return;
}
bool modified = false; bool modified = false;
// apply username // apply username
@ -358,6 +374,9 @@ void ConfigureSystem::ApplyConfiguration() {
Settings::values.init_clock = Settings::values.init_clock =
static_cast<Settings::InitClock>(ui->combo_init_clock->currentIndex()); static_cast<Settings::InitClock>(ui->combo_init_clock->currentIndex());
Settings::values.init_time = ui->edit_init_time->dateTime().toTime_t(); Settings::values.init_time = ui->edit_init_time->dateTime().toTime_t();
}
Settings::values.cpu_clock_percentage = SliderToSettings(ui->slider_clock_speed->value());
Settings::Apply(); Settings::Apply();
} }

View file

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>360</width> <width>471</width>
<height>377</height> <height>555</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -228,8 +228,7 @@
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="4" column="1">
<widget class="QComboBox" name="combo_country"> <widget class="QComboBox" name="combo_country"/>
</widget>
</item> </item>
<item row="5" column="0"> <item row="5" column="0">
<widget class="QLabel" name="label_init_clock"> <widget class="QLabel" name="label_init_clock">
@ -306,6 +305,63 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Advanced</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>CPU Clock Speed</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QSlider" name="slider_clock_speed">
<property name="toolTip">
<string>&lt;html&gt;&lt;body&gt;Changes the emulated CPU clock frequency.&lt;br&gt;Underclocking can increase performance but may cause the game to freeze.&lt;br&gt;Overclocking may reduce in game lag but also might cause freezes&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>79</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="pageStep">
<number>15</number>
</property>
<property name="value">
<number>25</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="clock_display_label">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2"/>
</item>
<item> <item>
<widget class="QLabel" name="label_disable_info"> <widget class="QLabel" name="label_disable_info">
<property name="text"> <property name="text">
@ -316,6 +372,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="label_cpu_clock_info">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;CPU Clock Speed Information&lt;br/&gt;Underclocking can increase performance but may cause the game to freeze.&lt;br/&gt;Overclocking may reduce in game lag but also might cause freezes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">

View file

@ -258,7 +258,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
memory = std::make_unique<Memory::MemorySystem>(); memory = std::make_unique<Memory::MemorySystem>();
timing = std::make_unique<Timing>(num_cores); timing = std::make_unique<Timing>(num_cores, Settings::values.cpu_clock_percentage);
kernel = std::make_unique<Kernel::KernelSystem>( kernel = std::make_unique<Kernel::KernelSystem>(
*memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores, n3ds_mode); *memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores, n3ds_mode);

View file

@ -20,14 +20,20 @@ bool Timing::Event::operator<(const Timing::Event& right) const {
return std::tie(time, fifo_order) < std::tie(right.time, right.fifo_order); return std::tie(time, fifo_order) < std::tie(right.time, right.fifo_order);
} }
Timing::Timing(std::size_t num_cores) { Timing::Timing(std::size_t num_cores, u32 cpu_clock_percentage) {
timers.resize(num_cores); timers.resize(num_cores);
for (std::size_t i = 0; i < num_cores; ++i) { for (std::size_t i = 0; i < num_cores; ++i) {
timers[i] = std::make_shared<Timer>(); timers[i] = std::make_shared<Timer>(100.0 / cpu_clock_percentage);
} }
current_timer = timers[0]; current_timer = timers[0];
} }
void Timing::UpdateClockSpeed(u32 cpu_clock_percentage) {
for (auto& timer : timers) {
timer->cpu_clock_scale = 100.0 / cpu_clock_percentage;
}
}
TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback callback) { TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback callback) {
// check for existing type with same name. // check for existing type with same name.
// we want event type names to remain unique so that we can use them for serialization. // we want event type names to remain unique so that we can use them for serialization.
@ -117,6 +123,8 @@ std::shared_ptr<Timing::Timer> Timing::GetTimer(std::size_t cpu_id) {
return timers[cpu_id]; return timers[cpu_id];
} }
Timing::Timer::Timer(double cpu_clock_scale_) : cpu_clock_scale(cpu_clock_scale_) {}
Timing::Timer::~Timer() { Timing::Timer::~Timer() {
MoveEvents(); MoveEvents();
} }
@ -130,7 +138,7 @@ u64 Timing::Timer::GetTicks() const {
} }
void Timing::Timer::AddTicks(u64 ticks) { void Timing::Timer::AddTicks(u64 ticks) {
downcount -= ticks; downcount -= static_cast<u64>(ticks * cpu_clock_scale);
} }
u64 Timing::Timer::GetIdleTicks() const { u64 Timing::Timer::GetIdleTicks() const {

View file

@ -148,6 +148,7 @@ public:
class Timer { class Timer {
public: public:
Timer(double cpu_clock_scale);
~Timer(); ~Timer();
s64 GetMaxSliceLength() const; s64 GetMaxSliceLength() const;
@ -190,10 +191,13 @@ public:
s64 slice_length = MAX_SLICE_LENGTH; s64 slice_length = MAX_SLICE_LENGTH;
s64 downcount = MAX_SLICE_LENGTH; s64 downcount = MAX_SLICE_LENGTH;
s64 executed_ticks = 0; s64 executed_ticks = 0;
u64 idled_cycles; u64 idled_cycles = 0;
// Stores a scaling for the internal clockspeed. Changing this number results in
// under/overclocking the guest cpu
double cpu_clock_scale = 1.0;
}; };
explicit Timing(std::size_t num_cores); explicit Timing(std::size_t num_cores, u32 cpu_clock_percentage);
~Timing(){}; ~Timing(){};
@ -220,6 +224,11 @@ public:
global_timer += ticks; global_timer += ticks;
} }
/**
* Updates the value of the cpu clock scaling to the new percentage.
*/
void UpdateClockSpeed(u32 cpu_clock_percentage);
std::chrono::microseconds GetGlobalTimeUs() const; std::chrono::microseconds GetGlobalTimeUs() const;
std::shared_ptr<Timer> GetTimer(std::size_t cpu_id); std::shared_ptr<Timer> GetTimer(std::size_t cpu_id);
@ -229,10 +238,14 @@ private:
// unordered_map stores each element separately as a linked list node so pointers to // unordered_map stores each element separately as a linked list node so pointers to
// elements remain stable regardless of rehashes/resizing. // elements remain stable regardless of rehashes/resizing.
std::unordered_map<std::string, TimingEventType> event_types; std::unordered_map<std::string, TimingEventType> event_types = {};
std::vector<std::shared_ptr<Timer>> timers; std::vector<std::shared_ptr<Timer>> timers;
std::shared_ptr<Timer> current_timer; std::shared_ptr<Timer> current_timer;
// Stores a scaling for the internal clockspeed. Changing this number results in
// under/overclocking the guest cpu
double cpu_clock_scale = 1.0;
}; };
} // namespace Core } // namespace Core

View file

@ -44,6 +44,7 @@ void Apply() {
auto& system = Core::System::GetInstance(); auto& system = Core::System::GetInstance();
if (system.IsPoweredOn()) { if (system.IsPoweredOn()) {
system.CoreTiming().UpdateClockSpeed(values.cpu_clock_percentage);
Core::DSP().SetSink(values.sink_id, values.audio_device_id); Core::DSP().SetSink(values.sink_id, values.audio_device_id);
Core::DSP().EnableStretching(values.enable_audio_stretching); Core::DSP().EnableStretching(values.enable_audio_stretching);

View file

@ -128,6 +128,7 @@ struct Values {
// Core // Core
bool use_cpu_jit; bool use_cpu_jit;
int cpu_clock_percentage;
// Data Storage // Data Storage
bool use_virtual_sd; bool use_virtual_sd;

View file

@ -15,7 +15,7 @@ static Memory::PageTable* page_table = nullptr;
TestEnvironment::TestEnvironment(bool mutable_memory_) TestEnvironment::TestEnvironment(bool mutable_memory_)
: mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
timing = std::make_unique<Core::Timing>(1); timing = std::make_unique<Core::Timing>(1, 100);
memory = std::make_unique<Memory::MemorySystem>(); memory = std::make_unique<Memory::MemorySystem>();
kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, [] {}, 0, 1, 0); kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, [] {}, 0, 1, 0);

View file

@ -43,7 +43,7 @@ static void AdvanceAndCheck(Core::Timing& timing, u32 idx, int downcount, int ex
} }
TEST_CASE("CoreTiming[BasicOrder]", "[core]") { TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
Core::Timing timing(1); Core::Timing timing(1, 100);
Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>);
Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>);
@ -90,7 +90,7 @@ void FifoCallback(u64 userdata, s64 cycles_late) {
TEST_CASE("CoreTiming[SharedSlot]", "[core]") { TEST_CASE("CoreTiming[SharedSlot]", "[core]") {
using namespace SharedSlotTest; using namespace SharedSlotTest;
Core::Timing timing(1); Core::Timing timing(1, 100);
Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", FifoCallback<0>); Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", FifoCallback<0>);
Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", FifoCallback<1>); Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", FifoCallback<1>);
@ -118,7 +118,7 @@ TEST_CASE("CoreTiming[SharedSlot]", "[core]") {
} }
TEST_CASE("CoreTiming[PredictableLateness]", "[core]") { TEST_CASE("CoreTiming[PredictableLateness]", "[core]") {
Core::Timing timing(1); Core::Timing timing(1, 100);
Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>);
Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>);
@ -149,7 +149,7 @@ static void RescheduleCallback(Core::Timing& timing, u64 userdata, s64 cycles_la
TEST_CASE("CoreTiming[ChainScheduling]", "[core]") { TEST_CASE("CoreTiming[ChainScheduling]", "[core]") {
using namespace ChainSchedulingTest; using namespace ChainSchedulingTest;
Core::Timing timing(1); Core::Timing timing(1, 100);
Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>);
Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>);

View file

@ -21,7 +21,7 @@ static std::shared_ptr<Object> MakeObject(Kernel::KernelSystem& kernel) {
} }
TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") {
Core::Timing timing(1); Core::Timing timing(1, 100);
Memory::MemorySystem memory; Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0); Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0);
auto [server, client] = kernel.CreateSessionPair(); auto [server, client] = kernel.CreateSessionPair();
@ -233,7 +233,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel
} }
TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
Core::Timing timing(1); Core::Timing timing(1, 100);
Memory::MemorySystem memory; Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0); Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0);
auto [server, client] = kernel.CreateSessionPair(); auto [server, client] = kernel.CreateSessionPair();

View file

@ -11,7 +11,7 @@
#include "core/memory.h" #include "core/memory.h"
TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") {
Core::Timing timing(1); Core::Timing timing(1, 100);
Memory::MemorySystem memory; Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0); Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0);
SECTION("these regions should not be mapped on an empty process") { SECTION("these regions should not be mapped on an empty process") {