mirror of
https://github.com/PabloMK7/citra.git
synced 2025-09-10 12:50:04 +00:00
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
14 changed files with 150 additions and 17 deletions
|
@ -379,7 +379,8 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
|
|||
|
||||
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>(
|
||||
*memory, *timing, [this] { PrepareReschedule(); }, memory_mode, num_cores, n3ds_hw_caps,
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <tuple>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core_timing.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
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) {
|
||||
for (auto& timer : timers) {
|
||||
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];
|
||||
}
|
||||
|
||||
Timing::Timer::Timer() = default;
|
||||
Timing::Timer::Timer(s64 base_ticks) : executed_ticks(base_ticks) {}
|
||||
|
||||
Timing::Timer::~Timer() {
|
||||
MoveEvents();
|
||||
|
|
|
@ -185,7 +185,7 @@ public:
|
|||
|
||||
class Timer {
|
||||
public:
|
||||
Timer();
|
||||
Timer(s64 base_ticks = 0);
|
||||
~Timer();
|
||||
|
||||
s64 GetMaxSliceLength() const;
|
||||
|
@ -249,7 +249,7 @@ public:
|
|||
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(){};
|
||||
|
||||
|
@ -290,6 +290,9 @@ public:
|
|||
event_queue_locked = false;
|
||||
}
|
||||
|
||||
/// Generates a random tick count to seed the system tick timer with.
|
||||
static s64 GenerateBaseTicks();
|
||||
|
||||
private:
|
||||
// unordered_map stores each element separately as a linked list node so pointers to
|
||||
// elements remain stable regardless of rehashes/resizing.
|
||||
|
|
|
@ -120,8 +120,9 @@ struct CTMHeader {
|
|||
std::array<char, 32> author; /// Author of 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
|
||||
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");
|
||||
#pragma pack(pop)
|
||||
|
@ -158,6 +159,7 @@ void Movie::serialize(Archive& ar, const unsigned int file_version) {
|
|||
ar& recorded_input_;
|
||||
|
||||
ar& init_time;
|
||||
ar& base_ticks;
|
||||
|
||||
if (Archive::is_loading::value) {
|
||||
u64 savestate_movie_id;
|
||||
|
@ -453,6 +455,10 @@ u64 Movie::GetOverrideInitTime() const {
|
|||
return init_time;
|
||||
}
|
||||
|
||||
s64 Movie::GetOverrideBaseTicks() const {
|
||||
return base_ticks;
|
||||
}
|
||||
|
||||
Movie::ValidationResult Movie::ValidateHeader(const CTMHeader& header) const {
|
||||
if (header_magic_bytes != header.filetype) {
|
||||
LOG_ERROR(Movie, "Playback file does not have valid header");
|
||||
|
@ -487,6 +493,7 @@ void Movie::SaveMovie() {
|
|||
header.filetype = header_magic_bytes;
|
||||
header.program_id = program_id;
|
||||
header.clock_init_time = init_time;
|
||||
header.timing_base_ticks = base_ticks;
|
||||
header.id = id;
|
||||
|
||||
std::memcpy(header.author.data(), record_movie_author.data(),
|
||||
|
@ -591,6 +598,7 @@ void Movie::PrepareForPlayback(const std::string& movie_file) {
|
|||
return;
|
||||
|
||||
init_time = header.value().clock_init_time;
|
||||
base_ticks = header.value().timing_base_ticks;
|
||||
}
|
||||
|
||||
void Movie::PrepareForRecording() {
|
||||
|
@ -605,6 +613,8 @@ void Movie::PrepareForRecording() {
|
|||
} else {
|
||||
init_time = Settings::values.init_time.GetValue();
|
||||
}
|
||||
|
||||
base_ticks = Timing::GenerateBaseTicks();
|
||||
}
|
||||
|
||||
Movie::ValidationResult Movie::ValidateMovie(const std::string& movie_file) const {
|
||||
|
@ -661,6 +671,7 @@ void Movie::Shutdown() {
|
|||
current_byte = 0;
|
||||
current_input = 0;
|
||||
init_time = 0;
|
||||
base_ticks = -1;
|
||||
id = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,9 @@ public:
|
|||
/// Get the init time that would override the one in the settings
|
||||
u64 GetOverrideInitTime() const;
|
||||
|
||||
/// Get the base system ticks value that would override the one generated by core timing
|
||||
s64 GetOverrideBaseTicks() const;
|
||||
|
||||
struct MovieMetadata {
|
||||
u64 program_id;
|
||||
std::string author;
|
||||
|
@ -168,7 +171,8 @@ private:
|
|||
std::string record_movie_file;
|
||||
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::size_t current_byte = 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue