core_timing: Allow configuring a fixed or random initial system tick value. (#7309)
* core_timing: Apply random base ticks value on startup. * core: Maintain consistent base system ticks in TAS movies. * frontend: Add setting to configure a fixed base system ticks value.
This commit is contained in:
parent
96aa1b3a08
commit
0165012ba4
|
@ -215,6 +215,8 @@ void Config::ReadValues() {
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ReadSetting("System", Settings::values.init_ticks_type);
|
||||||
|
ReadSetting("System", Settings::values.init_ticks_override);
|
||||||
ReadSetting("System", Settings::values.plugin_loader_enabled);
|
ReadSetting("System", Settings::values.plugin_loader_enabled);
|
||||||
ReadSetting("System", Settings::values.allow_plugin_loader);
|
ReadSetting("System", Settings::values.allow_plugin_loader);
|
||||||
|
|
||||||
|
|
|
@ -288,6 +288,14 @@ init_clock =
|
||||||
# Note: 3DS can only handle times later then Jan 1 2000
|
# Note: 3DS can only handle times later then Jan 1 2000
|
||||||
init_time =
|
init_time =
|
||||||
|
|
||||||
|
# The system ticks count to use when citra starts
|
||||||
|
# 0: Random (default), 1: Fixed
|
||||||
|
init_ticks_type =
|
||||||
|
|
||||||
|
# Tick count to use when init_ticks_type is set to Fixed.
|
||||||
|
# Defaults to 0.
|
||||||
|
init_ticks_override =
|
||||||
|
|
||||||
# Plugin loader state, if enabled plugins will be loaded from the SD card.
|
# Plugin loader state, if enabled plugins will be loaded from the SD card.
|
||||||
# You can also set if homebrew apps are allowed to enable the plugin loader
|
# You can also set if homebrew apps are allowed to enable the plugin loader
|
||||||
plugin_loader =
|
plugin_loader =
|
||||||
|
|
|
@ -225,6 +225,8 @@ void Config::ReadValues() {
|
||||||
std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch())
|
std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch())
|
||||||
.count();
|
.count();
|
||||||
}
|
}
|
||||||
|
ReadSetting("System", Settings::values.init_ticks_type);
|
||||||
|
ReadSetting("System", Settings::values.init_ticks_override);
|
||||||
ReadSetting("System", Settings::values.plugin_loader_enabled);
|
ReadSetting("System", Settings::values.plugin_loader_enabled);
|
||||||
ReadSetting("System", Settings::values.allow_plugin_loader);
|
ReadSetting("System", Settings::values.allow_plugin_loader);
|
||||||
|
|
||||||
|
|
|
@ -307,6 +307,14 @@ init_clock =
|
||||||
# Note: 3DS can only handle times later then Jan 1 2000
|
# Note: 3DS can only handle times later then Jan 1 2000
|
||||||
init_time =
|
init_time =
|
||||||
|
|
||||||
|
# The system ticks count to use when citra starts
|
||||||
|
# 0: Random (default), 1: Fixed
|
||||||
|
init_ticks_type =
|
||||||
|
|
||||||
|
# Tick count to use when init_ticks_type is set to Fixed.
|
||||||
|
# Defaults to 0.
|
||||||
|
init_ticks_override =
|
||||||
|
|
||||||
[Camera]
|
[Camera]
|
||||||
# Which camera engine to use for the right outer camera
|
# Which camera engine to use for the right outer camera
|
||||||
# blank (default): a dummy camera that always returns black image
|
# blank (default): a dummy camera that always returns black image
|
||||||
|
|
|
@ -687,6 +687,8 @@ void Config::ReadSystemValues() {
|
||||||
ReadBasicSetting(Settings::values.init_clock);
|
ReadBasicSetting(Settings::values.init_clock);
|
||||||
ReadBasicSetting(Settings::values.init_time);
|
ReadBasicSetting(Settings::values.init_time);
|
||||||
ReadBasicSetting(Settings::values.init_time_offset);
|
ReadBasicSetting(Settings::values.init_time_offset);
|
||||||
|
ReadBasicSetting(Settings::values.init_ticks_type);
|
||||||
|
ReadBasicSetting(Settings::values.init_ticks_override);
|
||||||
ReadBasicSetting(Settings::values.plugin_loader_enabled);
|
ReadBasicSetting(Settings::values.plugin_loader_enabled);
|
||||||
ReadBasicSetting(Settings::values.allow_plugin_loader);
|
ReadBasicSetting(Settings::values.allow_plugin_loader);
|
||||||
}
|
}
|
||||||
|
@ -1173,6 +1175,8 @@ void Config::SaveSystemValues() {
|
||||||
WriteBasicSetting(Settings::values.init_clock);
|
WriteBasicSetting(Settings::values.init_clock);
|
||||||
WriteBasicSetting(Settings::values.init_time);
|
WriteBasicSetting(Settings::values.init_time);
|
||||||
WriteBasicSetting(Settings::values.init_time_offset);
|
WriteBasicSetting(Settings::values.init_time_offset);
|
||||||
|
WriteBasicSetting(Settings::values.init_ticks_type);
|
||||||
|
WriteBasicSetting(Settings::values.init_ticks_override);
|
||||||
WriteBasicSetting(Settings::values.plugin_loader_enabled);
|
WriteBasicSetting(Settings::values.plugin_loader_enabled);
|
||||||
WriteBasicSetting(Settings::values.allow_plugin_loader);
|
WriteBasicSetting(Settings::values.allow_plugin_loader);
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,6 +230,8 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
|
||||||
&ConfigureSystem::UpdateBirthdayComboBox);
|
&ConfigureSystem::UpdateBirthdayComboBox);
|
||||||
connect(ui->combo_init_clock, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
connect(ui->combo_init_clock, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||||
&ConfigureSystem::UpdateInitTime);
|
&ConfigureSystem::UpdateInitTime);
|
||||||
|
connect(ui->combo_init_ticks_type, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||||
|
&ConfigureSystem::UpdateInitTicks);
|
||||||
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
|
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
|
||||||
&ConfigureSystem::RefreshConsoleID);
|
&ConfigureSystem::RefreshConsoleID);
|
||||||
connect(ui->button_start_download, &QPushButton::clicked, this,
|
connect(ui->button_start_download, &QPushButton::clicked, this,
|
||||||
|
@ -293,6 +295,11 @@ void ConfigureSystem::SetConfiguration() {
|
||||||
QTime time = QTime::fromMSecsSinceStartOfDay(static_cast<int>(time_offset * 1000));
|
QTime time = QTime::fromMSecsSinceStartOfDay(static_cast<int>(time_offset * 1000));
|
||||||
ui->edit_init_time_offset_time->setTime(time);
|
ui->edit_init_time_offset_time->setTime(time);
|
||||||
|
|
||||||
|
ui->combo_init_ticks_type->setCurrentIndex(
|
||||||
|
static_cast<u8>(Settings::values.init_ticks_type.GetValue()));
|
||||||
|
ui->edit_init_ticks_value->setText(
|
||||||
|
QString::number(Settings::values.init_ticks_override.GetValue()));
|
||||||
|
|
||||||
cfg = Service::CFG::GetModule(system);
|
cfg = Service::CFG::GetModule(system);
|
||||||
ReadSystemSettings();
|
ReadSystemSettings();
|
||||||
|
|
||||||
|
@ -413,6 +420,11 @@ void ConfigureSystem::ApplyConfiguration() {
|
||||||
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().toSecsSinceEpoch();
|
Settings::values.init_time = ui->edit_init_time->dateTime().toSecsSinceEpoch();
|
||||||
|
|
||||||
|
Settings::values.init_ticks_type =
|
||||||
|
static_cast<Settings::InitTicks>(ui->combo_init_ticks_type->currentIndex());
|
||||||
|
Settings::values.init_ticks_override =
|
||||||
|
static_cast<s64>(ui->edit_init_ticks_value->text().toLongLong());
|
||||||
|
|
||||||
s64 time_offset_time = ui->edit_init_time_offset_time->time().msecsSinceStartOfDay() / 1000;
|
s64 time_offset_time = ui->edit_init_time_offset_time->time().msecsSinceStartOfDay() / 1000;
|
||||||
s64 time_offset_days = ui->edit_init_time_offset_days->value() * 86400;
|
s64 time_offset_days = ui->edit_init_time_offset_days->value() * 86400;
|
||||||
|
|
||||||
|
@ -462,6 +474,7 @@ void ConfigureSystem::ConfigureTime() {
|
||||||
SetConfiguration();
|
SetConfiguration();
|
||||||
|
|
||||||
UpdateInitTime(ui->combo_init_clock->currentIndex());
|
UpdateInitTime(ui->combo_init_clock->currentIndex());
|
||||||
|
UpdateInitTicks(ui->combo_init_ticks_type->currentIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureSystem::UpdateInitTime(int init_clock) {
|
void ConfigureSystem::UpdateInitTime(int init_clock) {
|
||||||
|
@ -477,6 +490,15 @@ void ConfigureSystem::UpdateInitTime(int init_clock) {
|
||||||
ui->edit_init_time_offset_time->setVisible(!is_fixed_time && is_global);
|
ui->edit_init_time_offset_time->setVisible(!is_fixed_time && is_global);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigureSystem::UpdateInitTicks(int init_ticks_type) {
|
||||||
|
const bool is_global = Settings::IsConfiguringGlobal();
|
||||||
|
const bool is_fixed =
|
||||||
|
static_cast<Settings::InitTicks>(init_ticks_type) == Settings::InitTicks::Fixed;
|
||||||
|
|
||||||
|
ui->label_init_ticks_value->setVisible(is_fixed && is_global);
|
||||||
|
ui->edit_init_ticks_value->setVisible(is_fixed && is_global);
|
||||||
|
}
|
||||||
|
|
||||||
void ConfigureSystem::RefreshConsoleID() {
|
void ConfigureSystem::RefreshConsoleID() {
|
||||||
QMessageBox::StandardButton reply;
|
QMessageBox::StandardButton reply;
|
||||||
QString warning_text = tr("This will replace your current virtual 3DS with a new one. "
|
QString warning_text = tr("This will replace your current virtual 3DS with a new one. "
|
||||||
|
@ -512,6 +534,8 @@ void ConfigureSystem::SetupPerGameUI() {
|
||||||
ui->label_birthday->setVisible(false);
|
ui->label_birthday->setVisible(false);
|
||||||
ui->label_init_clock->setVisible(false);
|
ui->label_init_clock->setVisible(false);
|
||||||
ui->label_init_time->setVisible(false);
|
ui->label_init_time->setVisible(false);
|
||||||
|
ui->label_init_ticks_type->setVisible(false);
|
||||||
|
ui->label_init_ticks_value->setVisible(false);
|
||||||
ui->label_console_id->setVisible(false);
|
ui->label_console_id->setVisible(false);
|
||||||
ui->label_sound->setVisible(false);
|
ui->label_sound->setVisible(false);
|
||||||
ui->label_language->setVisible(false);
|
ui->label_language->setVisible(false);
|
||||||
|
@ -522,12 +546,14 @@ void ConfigureSystem::SetupPerGameUI() {
|
||||||
ui->combo_birthday->setVisible(false);
|
ui->combo_birthday->setVisible(false);
|
||||||
ui->combo_birthmonth->setVisible(false);
|
ui->combo_birthmonth->setVisible(false);
|
||||||
ui->combo_init_clock->setVisible(false);
|
ui->combo_init_clock->setVisible(false);
|
||||||
|
ui->combo_init_ticks_type->setVisible(false);
|
||||||
ui->combo_sound->setVisible(false);
|
ui->combo_sound->setVisible(false);
|
||||||
ui->combo_language->setVisible(false);
|
ui->combo_language->setVisible(false);
|
||||||
ui->combo_country->setVisible(false);
|
ui->combo_country->setVisible(false);
|
||||||
ui->label_init_time_offset->setVisible(false);
|
ui->label_init_time_offset->setVisible(false);
|
||||||
ui->edit_init_time_offset_days->setVisible(false);
|
ui->edit_init_time_offset_days->setVisible(false);
|
||||||
ui->edit_init_time_offset_time->setVisible(false);
|
ui->edit_init_time_offset_time->setVisible(false);
|
||||||
|
ui->edit_init_ticks_value->setVisible(false);
|
||||||
ui->toggle_system_setup->setVisible(false);
|
ui->toggle_system_setup->setVisible(false);
|
||||||
ui->button_regenerate_console_id->setVisible(false);
|
ui->button_regenerate_console_id->setVisible(false);
|
||||||
// Apps can change the state of the plugin loader, so plugins load
|
// Apps can change the state of the plugin loader, so plugins load
|
||||||
|
|
|
@ -43,6 +43,7 @@ private:
|
||||||
|
|
||||||
void UpdateBirthdayComboBox(int birthmonth_index);
|
void UpdateBirthdayComboBox(int birthmonth_index);
|
||||||
void UpdateInitTime(int init_clock);
|
void UpdateInitTime(int init_clock);
|
||||||
|
void UpdateInitTicks(int init_ticks_type);
|
||||||
void RefreshConsoleID();
|
void RefreshConsoleID();
|
||||||
|
|
||||||
void SetupPerGameUI();
|
void SetupPerGameUI();
|
||||||
|
|
|
@ -304,34 +304,75 @@
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="0">
|
<item row="9" column="0">
|
||||||
|
<widget class="QLabel" name="label_init_ticks_type">
|
||||||
|
<property name="text">
|
||||||
|
<string>Initial System Ticks</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="1">
|
||||||
|
<widget class="QComboBox" name="combo_init_ticks_type">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Random</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Fixed</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="0">
|
||||||
|
<widget class="QLabel" name="label_init_ticks_value">
|
||||||
|
<property name="text">
|
||||||
|
<string>Initial System Ticks Override</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="1">
|
||||||
|
<widget class="QLineEdit" name="edit_init_ticks_value">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maxLength">
|
||||||
|
<number>20</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="0">
|
||||||
<widget class="QLabel" name="label_play_coins">
|
<widget class="QLabel" name="label_play_coins">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Play Coins:</string>
|
<string>Play Coins:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="1">
|
<item row="11" column="1">
|
||||||
<widget class="QSpinBox" name="spinBox_play_coins">
|
<widget class="QSpinBox" name="spinBox_play_coins">
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>300</number>
|
<number>300</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="10" column="1">
|
<item row="12" column="1">
|
||||||
<widget class="QCheckBox" name="toggle_system_setup">
|
<widget class="QCheckBox" name="toggle_system_setup">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Run System Setup when Home Menu is launched</string>
|
<string>Run System Setup when Home Menu is launched</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="11" column="0">
|
<item row="13" column="0">
|
||||||
<widget class="QLabel" name="label_console_id">
|
<widget class="QLabel" name="label_console_id">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Console ID:</string>
|
<string>Console ID:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="11" column="1">
|
<item row="13" column="1">
|
||||||
<widget class="QPushButton" name="button_regenerate_console_id">
|
<widget class="QPushButton" name="button_regenerate_console_id">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
@ -347,35 +388,35 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="12" column="0">
|
<item row="14" column="0">
|
||||||
<widget class="QLabel" name="label_plugin_loader">
|
<widget class="QLabel" name="label_plugin_loader">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>3GX Plugin Loader:</string>
|
<string>3GX Plugin Loader:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="12" column="1">
|
<item row="14" column="1">
|
||||||
<widget class="QCheckBox" name="plugin_loader">
|
<widget class="QCheckBox" name="plugin_loader">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable 3GX plugin loader</string>
|
<string>Enable 3GX plugin loader</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="13" column="1">
|
<item row="15" column="1">
|
||||||
<widget class="QCheckBox" name="allow_plugin_loader">
|
<widget class="QCheckBox" name="allow_plugin_loader">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Allow games to change plugin loader state</string>
|
<string>Allow games to change plugin loader state</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="14" column="0">
|
<item row="16" column="0">
|
||||||
<widget class="QLabel" name="label_nus_download">
|
<widget class="QLabel" name="label_nus_download">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Download System Files from Nitendo servers</string>
|
<string>Download System Files from Nitendo servers</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="14" column="1">
|
<item row="16" column="1">
|
||||||
<widget class="QWidget" name="body_nus_download">
|
<widget class="QWidget" name="body_nus_download">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_nus_download">
|
<layout class="QHBoxLayout" name="horizontalLayout_nus_download">
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -28,6 +28,11 @@ enum class InitClock : u32 {
|
||||||
FixedTime = 1,
|
FixedTime = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class InitTicks : u32 {
|
||||||
|
Random = 0,
|
||||||
|
Fixed = 1,
|
||||||
|
};
|
||||||
|
|
||||||
enum class LayoutOption : u32 {
|
enum class LayoutOption : u32 {
|
||||||
Default,
|
Default,
|
||||||
SingleScreen,
|
SingleScreen,
|
||||||
|
@ -437,6 +442,8 @@ struct Values {
|
||||||
Setting<InitClock> init_clock{InitClock::SystemTime, "init_clock"};
|
Setting<InitClock> init_clock{InitClock::SystemTime, "init_clock"};
|
||||||
Setting<u64> init_time{946681277ULL, "init_time"};
|
Setting<u64> init_time{946681277ULL, "init_time"};
|
||||||
Setting<s64> init_time_offset{0, "init_time_offset"};
|
Setting<s64> init_time_offset{0, "init_time_offset"};
|
||||||
|
Setting<InitTicks> init_ticks_type{InitTicks::Random, "init_ticks_type"};
|
||||||
|
Setting<s64> init_ticks_override{0, "init_ticks_override"};
|
||||||
Setting<bool> plugin_loader_enabled{false, "plugin_loader"};
|
Setting<bool> plugin_loader_enabled{false, "plugin_loader"};
|
||||||
Setting<bool> allow_plugin_loader{true, "allow_plugin_loader"};
|
Setting<bool> allow_plugin_loader{true, "allow_plugin_loader"};
|
||||||
|
|
||||||
|
|
|
@ -379,7 +379,8 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
|
||||||
|
|
||||||
memory = std::make_unique<Memory::MemorySystem>(*this);
|
memory = std::make_unique<Memory::MemorySystem>(*this);
|
||||||
|
|
||||||
timing = std::make_unique<Timing>(num_cores, Settings::values.cpu_clock_percentage.GetValue());
|
timing = std::make_unique<Timing>(num_cores, Settings::values.cpu_clock_percentage.GetValue(),
|
||||||
|
movie.GetOverrideBaseTicks());
|
||||||
|
|
||||||
kernel = std::make_unique<Kernel::KernelSystem>(
|
kernel = std::make_unique<Kernel::KernelSystem>(
|
||||||
*memory, *timing, [this] { PrepareReschedule(); }, memory_mode, num_cores, n3ds_hw_caps,
|
*memory, *timing, [this] { PrepareReschedule(); }, memory_mode, num_cores, n3ds_hw_caps,
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/settings.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -19,15 +21,28 @@ 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, u32 cpu_clock_percentage) {
|
Timing::Timing(std::size_t num_cores, u32 cpu_clock_percentage, s64 override_base_ticks) {
|
||||||
|
// Generate non-zero base tick count to simulate time the system ran before launching the game.
|
||||||
|
// This accounts for games that rely on the system tick to seed randomness.
|
||||||
|
const auto base_ticks = override_base_ticks >= 0 ? override_base_ticks : GenerateBaseTicks();
|
||||||
|
|
||||||
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>(base_ticks);
|
||||||
}
|
}
|
||||||
UpdateClockSpeed(cpu_clock_percentage);
|
UpdateClockSpeed(cpu_clock_percentage);
|
||||||
current_timer = timers[0].get();
|
current_timer = timers[0].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s64 Timing::GenerateBaseTicks() {
|
||||||
|
if (Settings::values.init_ticks_type.GetValue() == Settings::InitTicks::Fixed) {
|
||||||
|
return Settings::values.init_ticks_override.GetValue();
|
||||||
|
}
|
||||||
|
// Bounded to 32 bits to make sure we don't generate too high of a counter and risk overflowing.
|
||||||
|
std::mt19937 random_gen(std::random_device{}());
|
||||||
|
return random_gen();
|
||||||
|
}
|
||||||
|
|
||||||
void Timing::UpdateClockSpeed(u32 cpu_clock_percentage) {
|
void Timing::UpdateClockSpeed(u32 cpu_clock_percentage) {
|
||||||
for (auto& timer : timers) {
|
for (auto& timer : timers) {
|
||||||
timer->cpu_clock_scale = 100.0 / cpu_clock_percentage;
|
timer->cpu_clock_scale = 100.0 / cpu_clock_percentage;
|
||||||
|
@ -146,7 +161,7 @@ std::shared_ptr<Timing::Timer> Timing::GetTimer(std::size_t cpu_id) {
|
||||||
return timers[cpu_id];
|
return timers[cpu_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
Timing::Timer::Timer() = default;
|
Timing::Timer::Timer(s64 base_ticks) : executed_ticks(base_ticks) {}
|
||||||
|
|
||||||
Timing::Timer::~Timer() {
|
Timing::Timer::~Timer() {
|
||||||
MoveEvents();
|
MoveEvents();
|
||||||
|
|
|
@ -185,7 +185,7 @@ public:
|
||||||
|
|
||||||
class Timer {
|
class Timer {
|
||||||
public:
|
public:
|
||||||
Timer();
|
Timer(s64 base_ticks = 0);
|
||||||
~Timer();
|
~Timer();
|
||||||
|
|
||||||
s64 GetMaxSliceLength() const;
|
s64 GetMaxSliceLength() const;
|
||||||
|
@ -249,7 +249,7 @@ public:
|
||||||
friend class boost::serialization::access;
|
friend class boost::serialization::access;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Timing(std::size_t num_cores, u32 cpu_clock_percentage);
|
explicit Timing(std::size_t num_cores, u32 cpu_clock_percentage, s64 override_base_ticks = -1);
|
||||||
|
|
||||||
~Timing(){};
|
~Timing(){};
|
||||||
|
|
||||||
|
@ -290,6 +290,9 @@ public:
|
||||||
event_queue_locked = false;
|
event_queue_locked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a random tick count to seed the system tick timer with.
|
||||||
|
static s64 GenerateBaseTicks();
|
||||||
|
|
||||||
private:
|
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.
|
||||||
|
|
|
@ -120,8 +120,9 @@ struct CTMHeader {
|
||||||
std::array<char, 32> author; /// Author of the movie
|
std::array<char, 32> author; /// Author of the movie
|
||||||
u32_le rerecord_count; /// Number of rerecords when making the movie
|
u32_le rerecord_count; /// Number of rerecords when making the movie
|
||||||
u64_le input_count; /// Number of inputs (button and pad states) when making the movie
|
u64_le input_count; /// Number of inputs (button and pad states) when making the movie
|
||||||
|
s64_le timing_base_ticks; /// The base system tick count to initialize core timing with.
|
||||||
|
|
||||||
std::array<u8, 164> reserved; /// Make heading 256 bytes so it has consistent size
|
std::array<u8, 156> reserved; /// Make heading 256 bytes so it has consistent size
|
||||||
};
|
};
|
||||||
static_assert(sizeof(CTMHeader) == 256, "CTMHeader should be 256 bytes");
|
static_assert(sizeof(CTMHeader) == 256, "CTMHeader should be 256 bytes");
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
@ -158,6 +159,7 @@ void Movie::serialize(Archive& ar, const unsigned int file_version) {
|
||||||
ar& recorded_input_;
|
ar& recorded_input_;
|
||||||
|
|
||||||
ar& init_time;
|
ar& init_time;
|
||||||
|
ar& base_ticks;
|
||||||
|
|
||||||
if (Archive::is_loading::value) {
|
if (Archive::is_loading::value) {
|
||||||
u64 savestate_movie_id;
|
u64 savestate_movie_id;
|
||||||
|
@ -453,6 +455,10 @@ u64 Movie::GetOverrideInitTime() const {
|
||||||
return init_time;
|
return init_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s64 Movie::GetOverrideBaseTicks() const {
|
||||||
|
return base_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
Movie::ValidationResult Movie::ValidateHeader(const CTMHeader& header) const {
|
Movie::ValidationResult Movie::ValidateHeader(const CTMHeader& header) const {
|
||||||
if (header_magic_bytes != header.filetype) {
|
if (header_magic_bytes != header.filetype) {
|
||||||
LOG_ERROR(Movie, "Playback file does not have valid header");
|
LOG_ERROR(Movie, "Playback file does not have valid header");
|
||||||
|
@ -487,6 +493,7 @@ void Movie::SaveMovie() {
|
||||||
header.filetype = header_magic_bytes;
|
header.filetype = header_magic_bytes;
|
||||||
header.program_id = program_id;
|
header.program_id = program_id;
|
||||||
header.clock_init_time = init_time;
|
header.clock_init_time = init_time;
|
||||||
|
header.timing_base_ticks = base_ticks;
|
||||||
header.id = id;
|
header.id = id;
|
||||||
|
|
||||||
std::memcpy(header.author.data(), record_movie_author.data(),
|
std::memcpy(header.author.data(), record_movie_author.data(),
|
||||||
|
@ -591,6 +598,7 @@ void Movie::PrepareForPlayback(const std::string& movie_file) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
init_time = header.value().clock_init_time;
|
init_time = header.value().clock_init_time;
|
||||||
|
base_ticks = header.value().timing_base_ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Movie::PrepareForRecording() {
|
void Movie::PrepareForRecording() {
|
||||||
|
@ -605,6 +613,8 @@ void Movie::PrepareForRecording() {
|
||||||
} else {
|
} else {
|
||||||
init_time = Settings::values.init_time.GetValue();
|
init_time = Settings::values.init_time.GetValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base_ticks = Timing::GenerateBaseTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
Movie::ValidationResult Movie::ValidateMovie(const std::string& movie_file) const {
|
Movie::ValidationResult Movie::ValidateMovie(const std::string& movie_file) const {
|
||||||
|
@ -661,6 +671,7 @@ void Movie::Shutdown() {
|
||||||
current_byte = 0;
|
current_byte = 0;
|
||||||
current_input = 0;
|
current_input = 0;
|
||||||
init_time = 0;
|
init_time = 0;
|
||||||
|
base_ticks = -1;
|
||||||
id = 0;
|
id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,9 @@ public:
|
||||||
/// Get the init time that would override the one in the settings
|
/// Get the init time that would override the one in the settings
|
||||||
u64 GetOverrideInitTime() const;
|
u64 GetOverrideInitTime() const;
|
||||||
|
|
||||||
|
/// Get the base system ticks value that would override the one generated by core timing
|
||||||
|
s64 GetOverrideBaseTicks() const;
|
||||||
|
|
||||||
struct MovieMetadata {
|
struct MovieMetadata {
|
||||||
u64 program_id;
|
u64 program_id;
|
||||||
std::string author;
|
std::string author;
|
||||||
|
@ -169,6 +172,7 @@ private:
|
||||||
std::string record_movie_author;
|
std::string record_movie_author;
|
||||||
|
|
||||||
u64 init_time; // Clock init time override for RNG consistency
|
u64 init_time; // Clock init time override for RNG consistency
|
||||||
|
s64 base_ticks = -1; // Core timing base system ticks override for RNG consistency
|
||||||
|
|
||||||
std::vector<u8> recorded_input;
|
std::vector<u8> recorded_input;
|
||||||
std::size_t current_byte = 0;
|
std::size_t current_byte = 0;
|
||||||
|
|
Reference in New Issue