System: Implement runahead
This commit is contained in:
parent
689b62e065
commit
e01d66d18e
|
@ -117,6 +117,9 @@ void AnalogController::SetAxisState(s32 axis_code, float value)
|
|||
|
||||
void AnalogController::SetAxisState(Axis axis, u8 value)
|
||||
{
|
||||
if (value != m_axis_state[static_cast<u8>(axis)])
|
||||
System::SetRunaheadReplayFlag();
|
||||
|
||||
m_axis_state[static_cast<u8>(axis)] = value;
|
||||
}
|
||||
|
||||
|
@ -131,10 +134,22 @@ void AnalogController::SetButtonState(Button button, bool pressed)
|
|||
return;
|
||||
}
|
||||
|
||||
const u16 bit = u16(1) << static_cast<u8>(button);
|
||||
|
||||
if (pressed)
|
||||
m_button_state &= ~(u16(1) << static_cast<u8>(button));
|
||||
{
|
||||
if (m_button_state & bit)
|
||||
System::SetRunaheadReplayFlag();
|
||||
|
||||
m_button_state &= ~(bit);
|
||||
}
|
||||
else
|
||||
m_button_state |= u16(1) << static_cast<u8>(button);
|
||||
{
|
||||
if (!(m_button_state & bit))
|
||||
System::SetRunaheadReplayFlag();
|
||||
|
||||
m_button_state |= bit;
|
||||
}
|
||||
}
|
||||
|
||||
void AnalogController::SetButtonState(s32 button_code, bool pressed)
|
||||
|
|
|
@ -83,6 +83,9 @@ void AnalogJoystick::SetAxisState(s32 axis_code, float value)
|
|||
|
||||
void AnalogJoystick::SetAxisState(Axis axis, u8 value)
|
||||
{
|
||||
if (m_axis_state[static_cast<u8>(axis)] != value)
|
||||
System::SetRunaheadReplayFlag();
|
||||
|
||||
m_axis_state[static_cast<u8>(axis)] = value;
|
||||
}
|
||||
|
||||
|
@ -96,10 +99,22 @@ void AnalogJoystick::SetButtonState(Button button, bool pressed)
|
|||
return;
|
||||
}
|
||||
|
||||
const u16 bit = u16(1) << static_cast<u8>(button);
|
||||
|
||||
if (pressed)
|
||||
m_button_state &= ~(u16(1) << static_cast<u8>(button));
|
||||
{
|
||||
if (m_button_state & bit)
|
||||
System::SetRunaheadReplayFlag();
|
||||
|
||||
m_button_state &= ~bit;
|
||||
}
|
||||
else
|
||||
m_button_state |= (u16(1) << static_cast<u8>(button));
|
||||
{
|
||||
if (!(m_button_state & bit))
|
||||
System::SetRunaheadReplayFlag();
|
||||
|
||||
m_button_state |= bit;
|
||||
}
|
||||
}
|
||||
|
||||
void AnalogJoystick::SetButtonState(s32 button_code, bool pressed)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "host_interface.h"
|
||||
#include "system.h"
|
||||
|
||||
DigitalController::DigitalController() = default;
|
||||
|
||||
|
@ -45,10 +46,21 @@ void DigitalController::SetAxisState(s32 axis_code, float value) {}
|
|||
|
||||
void DigitalController::SetButtonState(Button button, bool pressed)
|
||||
{
|
||||
const u16 bit = u16(1) << static_cast<u8>(button);
|
||||
if (pressed)
|
||||
m_button_state &= ~(u16(1) << static_cast<u8>(button));
|
||||
{
|
||||
if (m_button_state & bit)
|
||||
System::SetRunaheadReplayFlag();
|
||||
|
||||
m_button_state &= ~bit;
|
||||
}
|
||||
else
|
||||
m_button_state |= u16(1) << static_cast<u8>(button);
|
||||
{
|
||||
if (!(m_button_state & bit))
|
||||
System::SetRunaheadReplayFlag();
|
||||
|
||||
m_button_state |= bit;
|
||||
}
|
||||
}
|
||||
|
||||
void DigitalController::SetButtonState(s32 button_code, bool pressed)
|
||||
|
|
|
@ -75,6 +75,12 @@ bool GPU_SW::Initialize(HostDisplay* host_display)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GPU_SW::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display)
|
||||
{
|
||||
// ignore the host texture for software mode, since we want to save vram here
|
||||
return GPU::DoState(sw, nullptr, update_display);
|
||||
}
|
||||
|
||||
void GPU_SW::Reset(bool clear_vram)
|
||||
{
|
||||
GPU::Reset(clear_vram);
|
||||
|
|
|
@ -18,6 +18,7 @@ public:
|
|||
bool IsHardwareRenderer() const override;
|
||||
|
||||
bool Initialize(HostDisplay* host_display) override;
|
||||
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
|
||||
void Reset(bool clear_vram) override;
|
||||
void UpdateSettings() override;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "host_display.h"
|
||||
#include "pgxp.h"
|
||||
#include "save_state_version.h"
|
||||
#include "spu.h"
|
||||
#include "system.h"
|
||||
#include "texture_replacements.h"
|
||||
#include <cmath>
|
||||
|
@ -78,6 +79,9 @@ void HostInterface::CreateAudioStream()
|
|||
}
|
||||
|
||||
m_audio_stream->SetOutputVolume(GetAudioOutputVolume());
|
||||
|
||||
if (System::IsValid())
|
||||
g_spu.SetAudioStream(m_audio_stream.get());
|
||||
}
|
||||
|
||||
s32 HostInterface::GetAudioOutputVolume() const
|
||||
|
@ -491,6 +495,8 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
|||
si.SetBoolValue("Main", "RewindEnable", false);
|
||||
si.SetFloatValue("Main", "RewindFrequency", 10.0f);
|
||||
si.SetIntValue("Main", "RewindSaveSlots", 10);
|
||||
si.SetBoolValue("Main", "RunaheadEnable", false);
|
||||
si.SetFloatValue("Main", "RunaheadFrames", 1);
|
||||
|
||||
si.SetStringValue("CPU", "ExecutionMode", Settings::GetCPUExecutionModeName(Settings::DEFAULT_CPU_EXECUTION_MODE));
|
||||
si.SetBoolValue("CPU", "RecompilerMemoryExceptions", false);
|
||||
|
@ -660,7 +666,8 @@ void HostInterface::FixIncompatibleSettings(bool display_osd_messages)
|
|||
#endif
|
||||
|
||||
// rewinding causes issues with mmap fastmem, so just use LUT
|
||||
if (g_settings.rewind_enable && g_settings.IsUsingFastmem() && g_settings.cpu_fastmem_mode == CPUFastmemMode::MMap)
|
||||
if ((g_settings.rewind_enable || g_settings.runahead_enable) && g_settings.IsUsingFastmem() &&
|
||||
g_settings.cpu_fastmem_mode == CPUFastmemMode::MMap)
|
||||
{
|
||||
Log_WarningPrintf("Disabling mmap fastmem due to rewind being enabled");
|
||||
g_settings.cpu_fastmem_mode = CPUFastmemMode::LUT;
|
||||
|
@ -768,7 +775,8 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||
g_settings.display_active_end_offset != old_settings.display_active_end_offset ||
|
||||
g_settings.display_line_start_offset != old_settings.display_line_start_offset ||
|
||||
g_settings.display_line_end_offset != old_settings.display_line_end_offset ||
|
||||
g_settings.rewind_enable != old_settings.rewind_enable)
|
||||
g_settings.rewind_enable != old_settings.rewind_enable ||
|
||||
g_settings.runahead_enable != old_settings.runahead_enable)
|
||||
{
|
||||
if (g_settings.IsUsingCodeCache())
|
||||
CPU::CodeCache::Reinitialize();
|
||||
|
@ -808,7 +816,9 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||
|
||||
if (g_settings.rewind_enable != old_settings.rewind_enable ||
|
||||
g_settings.rewind_save_frequency != old_settings.rewind_save_frequency ||
|
||||
g_settings.rewind_save_slots != old_settings.rewind_save_slots)
|
||||
g_settings.rewind_save_slots != old_settings.rewind_save_slots ||
|
||||
g_settings.runahead_enable != old_settings.runahead_enable ||
|
||||
g_settings.runahead_frames != old_settings.runahead_frames)
|
||||
{
|
||||
System::UpdateMemorySaveStateSettings();
|
||||
}
|
||||
|
|
|
@ -126,6 +126,8 @@ void Settings::Load(SettingsInterface& si)
|
|||
rewind_enable = si.GetBoolValue("Main", "RewindEnable", false);
|
||||
rewind_save_frequency = si.GetFloatValue("Main", "RewindFrequency", 10.0f);
|
||||
rewind_save_slots = static_cast<u32>(si.GetIntValue("Main", "RewindSaveSlots", 10));
|
||||
runahead_enable = si.GetBoolValue("Main", "RunaheadEnable", false);
|
||||
runahead_frames = static_cast<u32>(si.GetIntValue("Main", "RunaheadFrames", 1));
|
||||
|
||||
cpu_execution_mode =
|
||||
ParseCPUExecutionMode(
|
||||
|
@ -301,6 +303,8 @@ void Settings::Save(SettingsInterface& si) const
|
|||
si.SetBoolValue("Main", "RewindEnable", rewind_enable);
|
||||
si.SetFloatValue("Main", "RewindFrequency", rewind_save_frequency);
|
||||
si.SetIntValue("Main", "RewindSaveSlots", rewind_save_slots);
|
||||
si.SetBoolValue("Main", "RunaheadEnable", runahead_enable);
|
||||
si.SetFloatValue("Main", "RunaheadFrames", runahead_frames);
|
||||
|
||||
si.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode));
|
||||
si.SetBoolValue("CPU", "OverclockEnable", cpu_overclock_enable);
|
||||
|
|
|
@ -95,8 +95,10 @@ struct Settings
|
|||
bool disable_all_enhancements = false;
|
||||
|
||||
bool rewind_enable = false;
|
||||
bool runahead_enable = false;
|
||||
float rewind_save_frequency = 10.0f;
|
||||
u32 rewind_save_slots = 10;
|
||||
u32 runahead_frames = 1;
|
||||
|
||||
GPURenderer gpu_renderer = GPURenderer::Software;
|
||||
std::string gpu_adapter;
|
||||
|
|
|
@ -31,6 +31,7 @@ void SPU::Initialize()
|
|||
"SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
|
||||
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<SPU*>(param)->ExecuteTransfer(ticks); }, this,
|
||||
false);
|
||||
m_audio_stream = g_host_interface->GetAudioStream();
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
@ -49,6 +50,7 @@ void SPU::Shutdown()
|
|||
m_tick_event.reset();
|
||||
m_transfer_event.reset();
|
||||
m_dump_writer.reset();
|
||||
m_audio_stream = nullptr;
|
||||
}
|
||||
|
||||
void SPU::Reset()
|
||||
|
@ -172,7 +174,6 @@ bool SPU::DoState(StateWrapper& sw)
|
|||
|
||||
if (sw.IsReading())
|
||||
{
|
||||
g_host_interface->GetAudioStream()->EmptyBuffers();
|
||||
UpdateEventInterval();
|
||||
UpdateTransferEvent();
|
||||
}
|
||||
|
@ -1731,10 +1732,9 @@ void SPU::Execute(TickCount ticks)
|
|||
|
||||
while (remaining_frames > 0)
|
||||
{
|
||||
AudioStream* const output_stream = g_host_interface->GetAudioStream();
|
||||
s16* output_frame_start;
|
||||
u32 output_frame_space = remaining_frames;
|
||||
output_stream->BeginWrite(&output_frame_start, &output_frame_space);
|
||||
m_audio_stream->BeginWrite(&output_frame_start, &output_frame_space);
|
||||
|
||||
s16* output_frame = output_frame_start;
|
||||
const u32 frames_in_this_batch = std::min(remaining_frames, output_frame_space);
|
||||
|
@ -1837,7 +1837,7 @@ void SPU::Execute(TickCount ticks)
|
|||
if (m_dump_writer)
|
||||
m_dump_writer->WriteFrames(output_frame_start, frames_in_this_batch);
|
||||
|
||||
output_stream->EndWrite(frames_in_this_batch);
|
||||
m_audio_stream->EndWrite(frames_in_this_batch);
|
||||
remaining_frames -= frames_in_this_batch;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,9 @@ public:
|
|||
const std::array<u8, RAM_SIZE>& GetRAM() const { return m_ram; }
|
||||
std::array<u8, RAM_SIZE>& GetRAM() { return m_ram; }
|
||||
|
||||
/// Change output stream - used for runahead.
|
||||
ALWAYS_INLINE void SetAudioStream(AudioStream* stream) { m_audio_stream = stream; }
|
||||
|
||||
private:
|
||||
static constexpr u32 SPU_BASE = 0x1F801C00;
|
||||
static constexpr u32 NUM_VOICES = 24;
|
||||
|
@ -373,6 +376,7 @@ private:
|
|||
std::unique_ptr<TimingEvent> m_tick_event;
|
||||
std::unique_ptr<TimingEvent> m_transfer_event;
|
||||
std::unique_ptr<Common::WAVWriter> m_dump_writer;
|
||||
AudioStream* m_audio_stream = nullptr;
|
||||
TickCount m_ticks_carry = 0;
|
||||
TickCount m_cpu_ticks_per_spu_tick = 0;
|
||||
TickCount m_cpu_tick_divider = 0;
|
||||
|
|
|
@ -61,6 +61,9 @@ struct MemorySaveState
|
|||
std::unique_ptr<GrowableMemoryByteStream> state_stream;
|
||||
};
|
||||
|
||||
static bool SaveMemoryState(MemorySaveState* mss);
|
||||
static bool LoadMemoryState(const MemorySaveState& mss);
|
||||
|
||||
static bool LoadEXE(const char* filename);
|
||||
static bool SetExpansionROM(const char* filename);
|
||||
|
||||
|
@ -70,10 +73,16 @@ static std::unique_ptr<CDImage> OpenCDImage(const char* path, bool force_preload
|
|||
static bool DoLoadState(ByteStream* stream, bool force_software_renderer, bool update_display);
|
||||
static bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display);
|
||||
static void DoRunFrame();
|
||||
static void DoRewind();
|
||||
static void DoMemorySaveStates();
|
||||
static bool CreateGPU(GPURenderer renderer);
|
||||
|
||||
static bool SaveRewindState();
|
||||
static void DoRewind();
|
||||
|
||||
static void SaveRunaheadState();
|
||||
static void DoRunahead();
|
||||
|
||||
static void DoMemorySaveStates();
|
||||
|
||||
static bool Initialize(bool force_software_renderer);
|
||||
|
||||
static void UpdateRunningGame(const char* path, CDImage* image);
|
||||
|
@ -126,6 +135,11 @@ static s32 s_rewind_save_frequency = -1;
|
|||
static s32 s_rewind_save_counter = -1;
|
||||
static bool s_rewinding_first_save = false;
|
||||
|
||||
static std::deque<MemorySaveState> s_runahead_states;
|
||||
static std::unique_ptr<AudioStream> s_runahead_audio_stream;
|
||||
static bool s_runahead_replay_pending = false;
|
||||
static u32 s_runahead_frames = 0;
|
||||
|
||||
State GetState()
|
||||
{
|
||||
return s_state;
|
||||
|
@ -835,6 +849,7 @@ void Shutdown()
|
|||
return;
|
||||
|
||||
ClearMemorySaveStates();
|
||||
s_runahead_audio_stream.reset();
|
||||
|
||||
g_texture_replacements.Shutdown();
|
||||
|
||||
|
@ -1150,6 +1165,7 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer, bool update_di
|
|||
if (s_state == State::Starting)
|
||||
s_state = State::Running;
|
||||
|
||||
g_host_interface->GetAudioStream()->EmptyBuffers();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1301,6 +1317,9 @@ void RunFrame()
|
|||
return;
|
||||
}
|
||||
|
||||
if (s_runahead_frames > 0)
|
||||
DoRunahead();
|
||||
|
||||
DoRunFrame();
|
||||
|
||||
if (s_memory_saves_enabled)
|
||||
|
@ -1960,6 +1979,7 @@ void CalculateRewindMemoryUsage(u32 num_saves, u64* ram_usage, u64* vram_usage)
|
|||
void ClearMemorySaveStates()
|
||||
{
|
||||
s_rewind_states.clear();
|
||||
s_runahead_states.clear();
|
||||
}
|
||||
|
||||
void UpdateMemorySaveStateSettings()
|
||||
|
@ -1984,6 +2004,60 @@ void UpdateMemorySaveStateSettings()
|
|||
s_rewind_save_frequency = -1;
|
||||
s_rewind_save_counter = -1;
|
||||
}
|
||||
|
||||
s_rewind_load_frequency = -1;
|
||||
s_rewind_load_counter = -1;
|
||||
|
||||
s_runahead_frames = g_settings.runahead_enable ? g_settings.runahead_frames : 0;
|
||||
s_runahead_replay_pending = false;
|
||||
if (s_runahead_frames > 0)
|
||||
{
|
||||
Log_InfoPrintf("Runahead is active with %u frames", s_runahead_frames);
|
||||
|
||||
if (!s_runahead_audio_stream)
|
||||
{
|
||||
// doesn't matter if it's not resampled here since it eats everything anyway, nom nom nom.
|
||||
s_runahead_audio_stream = AudioStream::CreateNullAudioStream();
|
||||
s_runahead_audio_stream->Reconfigure(HostInterface::AUDIO_SAMPLE_RATE, HostInterface::AUDIO_SAMPLE_RATE,
|
||||
HostInterface::AUDIO_CHANNELS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s_runahead_audio_stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadMemoryState(const MemorySaveState& mss)
|
||||
{
|
||||
mss.state_stream->SeekAbsolute(0);
|
||||
|
||||
StateWrapper sw(mss.state_stream.get(), StateWrapper::Mode::Read, SAVE_STATE_VERSION);
|
||||
HostDisplayTexture* host_texture = mss.vram_texture.get();
|
||||
if (!DoState(sw, &host_texture, true))
|
||||
{
|
||||
g_host_interface->ReportError("Failed to load memory save state, resetting.");
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveMemoryState(MemorySaveState* mss)
|
||||
{
|
||||
mss->state_stream = std::make_unique<GrowableMemoryByteStream>(nullptr, MAX_SAVE_STATE_SIZE);
|
||||
|
||||
HostDisplayTexture* host_texture = nullptr;
|
||||
StateWrapper sw(mss->state_stream.get(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
|
||||
if (!DoState(sw, &host_texture, false))
|
||||
{
|
||||
Log_ErrorPrint("Failed to create rewind state.");
|
||||
return false;
|
||||
}
|
||||
|
||||
mss->vram_texture.reset(host_texture);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveRewindState()
|
||||
|
@ -1995,17 +2069,9 @@ bool SaveRewindState()
|
|||
s_rewind_states.pop_front();
|
||||
|
||||
MemorySaveState mss;
|
||||
mss.state_stream = std::make_unique<GrowableMemoryByteStream>(nullptr, MAX_SAVE_STATE_SIZE);
|
||||
|
||||
HostDisplayTexture* host_texture = nullptr;
|
||||
StateWrapper sw(mss.state_stream.get(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
|
||||
if (!DoState(sw, &host_texture, false))
|
||||
{
|
||||
Log_ErrorPrint("Failed to create rewind state.");
|
||||
if (!SaveMemoryState(&mss))
|
||||
return false;
|
||||
}
|
||||
|
||||
mss.vram_texture.reset(host_texture);
|
||||
s_rewind_states.push_back(std::move(mss));
|
||||
|
||||
Log_DevPrintf("Saved rewind state (%u bytes, took %.4f ms)", s_rewind_states.back().state_stream->GetSize(),
|
||||
|
@ -2027,17 +2093,8 @@ bool LoadRewindState(u32 skip_saves /*= 0*/, bool consume_state /*=true */)
|
|||
|
||||
Common::Timer load_timer;
|
||||
|
||||
const MemorySaveState& mss = s_rewind_states.back();
|
||||
mss.state_stream->SeekAbsolute(0);
|
||||
|
||||
StateWrapper sw(mss.state_stream.get(), StateWrapper::Mode::Read, SAVE_STATE_VERSION);
|
||||
HostDisplayTexture* host_texture = mss.vram_texture.get();
|
||||
if (!DoState(sw, &host_texture, true))
|
||||
{
|
||||
g_host_interface->ReportError("Failed to load rewind state from memory, resetting.");
|
||||
Reset();
|
||||
if (!LoadMemoryState(s_rewind_states.back()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (consume_state)
|
||||
s_rewind_states.pop_back();
|
||||
|
@ -2082,6 +2139,68 @@ void DoRewind()
|
|||
}
|
||||
}
|
||||
|
||||
void SaveRunaheadState()
|
||||
{
|
||||
if (s_runahead_states.size() >= s_runahead_frames)
|
||||
s_runahead_states.pop_front();
|
||||
|
||||
MemorySaveState mss;
|
||||
if (!SaveMemoryState(&mss))
|
||||
{
|
||||
Log_ErrorPrint("Failed to save runahead state.");
|
||||
return;
|
||||
}
|
||||
|
||||
s_runahead_states.push_back(std::move(mss));
|
||||
}
|
||||
|
||||
void DoRunahead()
|
||||
{
|
||||
Common::Timer timer;
|
||||
Log_DevPrintf("runahead starting at frame %u", s_frame_number);
|
||||
|
||||
if (s_runahead_replay_pending)
|
||||
{
|
||||
// we need to replay and catch up - load the state,
|
||||
s_runahead_replay_pending = false;
|
||||
if (!LoadMemoryState(s_runahead_states.front()))
|
||||
return;
|
||||
|
||||
// and throw away all the states, forcing us to catch up below
|
||||
// TODO: can we leave one frame here and run, avoiding the extra save?
|
||||
s_runahead_states.clear();
|
||||
Log_VerbosePrintf("Rewound to frame %u, took %.2f ms", s_frame_number, timer.GetTimeMilliseconds());
|
||||
}
|
||||
|
||||
// run the frames with no audio
|
||||
s32 frames_to_run = static_cast<s32>(s_runahead_frames) - static_cast<s32>(s_runahead_states.size());
|
||||
if (frames_to_run > 0)
|
||||
{
|
||||
Common::Timer timer2;
|
||||
const s32 temp = frames_to_run;
|
||||
|
||||
g_spu.SetAudioStream(s_runahead_audio_stream.get());
|
||||
|
||||
while (frames_to_run > 0)
|
||||
{
|
||||
DoRunFrame();
|
||||
SaveRunaheadState();
|
||||
frames_to_run--;
|
||||
}
|
||||
|
||||
g_spu.SetAudioStream(g_host_interface->GetAudioStream());
|
||||
|
||||
Log_VerbosePrintf("Running %d frames to catch up took %.2f ms", temp, timer2.GetTimeMilliseconds());
|
||||
}
|
||||
else
|
||||
{
|
||||
// save this frame
|
||||
SaveRunaheadState();
|
||||
}
|
||||
|
||||
Log_DevPrintf("runahead ending at frame %u, took %.2f ms", s_frame_number, timer.GetTimeMilliseconds());
|
||||
}
|
||||
|
||||
void DoMemorySaveStates()
|
||||
{
|
||||
if (s_rewind_save_counter >= 0)
|
||||
|
@ -2096,6 +2215,15 @@ void DoMemorySaveStates()
|
|||
s_rewind_save_counter--;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_runahead_frames > 0)
|
||||
SaveRunaheadState();
|
||||
}
|
||||
|
||||
void SetRunaheadReplayFlag()
|
||||
{
|
||||
Log_DevPrintf("Runahead rewind pending...");
|
||||
s_runahead_replay_pending = true;
|
||||
}
|
||||
|
||||
} // namespace System
|
|
@ -234,8 +234,8 @@ void SetCheatList(std::unique_ptr<CheatList> cheats);
|
|||
void CalculateRewindMemoryUsage(u32 num_saves, u64* ram_usage, u64* vram_usage);
|
||||
void ClearMemorySaveStates();
|
||||
void UpdateMemorySaveStateSettings();
|
||||
bool SaveRewindState();
|
||||
bool LoadRewindState(u32 skip_saves = 0, bool consume_state = true);
|
||||
void SetRewinding(bool enabled);
|
||||
void SetRunaheadReplayFlag();
|
||||
|
||||
} // namespace System
|
||||
|
|
|
@ -18,6 +18,8 @@ EmulationSettingsWidget::EmulationSettingsWidget(QtHostInterface* host_interface
|
|||
SettingWidgetBinder::BindWidgetToFloatSetting(m_host_interface, m_ui.rewindSaveFrequency, "Main", "RewindFrequency",
|
||||
10.0f);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(m_host_interface, m_ui.rewindSaveSlots, "Main", "RewindSaveSlots", 10);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.runaheadEnable, "Main", "RunaheadEnable", false);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(m_host_interface, m_ui.runaheadFrames, "Main", "RunaheadFrames", 1);
|
||||
|
||||
QtUtils::FillComboBoxWithEmulationSpeeds(m_ui.emulationSpeed);
|
||||
const int emulation_speed_index =
|
||||
|
@ -46,6 +48,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(QtHostInterface* host_interface
|
|||
&EmulationSettingsWidget::updateRewindSummaryLabel);
|
||||
connect(m_ui.rewindSaveSlots, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
&EmulationSettingsWidget::updateRewindSummaryLabel);
|
||||
connect(m_ui.runaheadEnable, &QCheckBox::stateChanged, this, &EmulationSettingsWidget::updateRunaheadFields);
|
||||
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.emulationSpeed, tr("Emulation Speed"), "100%",
|
||||
|
@ -67,6 +70,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(QtHostInterface* host_interface
|
|||
"should disable this option."));
|
||||
|
||||
updateRewindSummaryLabel();
|
||||
updateRunaheadFields();
|
||||
}
|
||||
|
||||
EmulationSettingsWidget::~EmulationSettingsWidget() = default;
|
||||
|
@ -124,3 +128,8 @@ void EmulationSettingsWidget::updateRewindSummaryLabel()
|
|||
m_ui.rewindSaveSlots->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void EmulationSettingsWidget::updateRunaheadFields()
|
||||
{
|
||||
m_ui.runaheadFrames->setEnabled(m_ui.runaheadEnable->isChecked());
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ private Q_SLOTS:
|
|||
void onFastForwardSpeedIndexChanged(int index);
|
||||
void onTurboSpeedIndexChanged(int index);
|
||||
void updateRewindSummaryLabel();
|
||||
void updateRunaheadFields();
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -138,6 +138,42 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Runahead</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="runaheadEnable">
|
||||
<property name="text">
|
||||
<string>Enable Runahead</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Runahead Frames:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="runaheadFrames">
|
||||
<property name="suffix">
|
||||
<string> Frames</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>20</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
|
|
Loading…
Reference in New Issue