Run ahead support (+ improved save/load state performance)

Note: Run ahead currently doesn't work well with netplay, movies and rewind
This commit is contained in:
Sour 2019-12-26 12:03:38 -05:00
parent 30c7eea6fa
commit 5f2c502be9
15 changed files with 301 additions and 123 deletions

View file

@ -90,6 +90,14 @@ void Console::Release()
_movieManager.reset();
}
void Console::RunFrame()
{
uint32_t frameCount = _ppu->GetFrameCount();
while(frameCount == _ppu->GetFrameCount()) {
_cpu->Exec();
}
}
void Console::Run()
{
if(!_cpu) {
@ -98,9 +106,9 @@ void Console::Run()
auto emulationLock = _emulationLock.AcquireSafe();
auto lock = _runLock.AcquireSafe();
uint32_t previousFrameCount = 0;
_stopFlag = false;
_isRunAheadFrame = false;
PlatformUtilities::EnableHighResolutionTimer();
@ -116,29 +124,25 @@ void Console::Run()
_lastFrameTimer.Reset();
while(!_stopFlag) {
_cpu->Exec();
if(previousFrameCount != _ppu->GetFrameCount()) {
bool useRunAhead = _settings->GetEmulationConfig().RunAheadFrames > 0 && !_debugger && !_rewindManager->IsRewinding() && _settings->GetEmulationSpeed() > 0 && _settings->GetEmulationSpeed() <= 100;
if(useRunAhead) {
RunFrameWithRunAhead();
} else {
RunFrame();
_rewindManager->ProcessEndOfFrame();
WaitForLock();
if(_pauseOnNextFrame) {
_pauseOnNextFrame = false;
_paused = true;
}
if(_paused && !_stopFlag && !_debugger) {
WaitForPauseEnd();
}
previousFrameCount = _ppu->GetFrameCount();
if(_controlManager->GetSystemActionManager()->IsResetPressed()) {
Reset();
} else if(_controlManager->GetSystemActionManager()->IsPowerCyclePressed()) {
PowerCycle();
}
ProcessSystemActions();
}
WaitForLock();
if(_pauseOnNextFrame) {
_pauseOnNextFrame = false;
_paused = true;
}
if(_paused && !_stopFlag && !_debugger) {
WaitForPauseEnd();
}
}
_movieManager->Stop();
@ -148,6 +152,48 @@ void Console::Run()
PlatformUtilities::RestoreTimerResolution();
}
bool Console::ProcessSystemActions()
{
if(_controlManager->GetSystemActionManager()->IsResetPressed()) {
Reset();
return true;
} else if(_controlManager->GetSystemActionManager()->IsPowerCyclePressed()) {
PowerCycle();
return true;
}
return false;
}
void Console::RunFrameWithRunAhead()
{
stringstream runAheadState;
uint32_t frameCount = _settings->GetEmulationConfig().RunAheadFrames;
//Run a single frame and save the state (no audio/video)
_isRunAheadFrame = true;
RunFrame();
Serialize(runAheadState, 0);
while(frameCount > 1) {
//Run extra frames if the requested run ahead frame count is higher than 1
frameCount--;
RunFrame();
}
_isRunAheadFrame = false;
//Run one frame normally (with audio/video output)
RunFrame();
_rewindManager->ProcessEndOfFrame();
bool wasReset = ProcessSystemActions();
if(!wasReset) {
//Load the state we saved earlier
_isRunAheadFrame = true;
Deserialize(runAheadState, SaveStateManager::FileFormatVersion, false);
_isRunAheadFrame = false;
}
}
void Console::ProcessEndOfFrame()
{
#ifndef LIBRETRO
@ -156,25 +202,27 @@ void Console::ProcessEndOfFrame()
_cart->GetCoprocessor()->ProcessEndOfFrame();
}
_frameLimiter->ProcessFrame();
while(_frameLimiter->WaitForNextFrame()) {
if(_stopFlag || _frameDelay != GetFrameDelay() || _paused || _pauseOnNextFrame || _lockCounter > 0) {
//Need to process another event, stop sleeping
break;
if(!_isRunAheadFrame) {
_frameLimiter->ProcessFrame();
while(_frameLimiter->WaitForNextFrame()) {
if(_stopFlag || _frameDelay != GetFrameDelay() || _paused || _pauseOnNextFrame || _lockCounter > 0) {
//Need to process another event, stop sleeping
break;
}
}
}
double newFrameDelay = GetFrameDelay();
if(newFrameDelay != _frameDelay) {
_frameDelay = newFrameDelay;
_frameLimiter->SetDelay(_frameDelay);
}
double newFrameDelay = GetFrameDelay();
if(newFrameDelay != _frameDelay) {
_frameDelay = newFrameDelay;
_frameLimiter->SetDelay(_frameDelay);
}
PreferencesConfig cfg = _settings->GetPreferences();
if(cfg.ShowDebugInfo) {
double lastFrameTime = _lastFrameTimer.GetElapsedMS();
_lastFrameTimer.Reset();
_stats->DisplayStats(this, lastFrameTime);
PreferencesConfig cfg = _settings->GetPreferences();
if(cfg.ShowDebugInfo) {
double lastFrameTime = _lastFrameTimer.GetElapsedMS();
_lastFrameTimer.Reset();
_stats->DisplayStats(this, lastFrameTime);
}
}
_controlManager->UpdateInputState();
@ -554,7 +602,7 @@ void Console::WaitForLock()
}
}
void Console::Serialize(ostream &out)
void Console::Serialize(ostream &out, int compressionLevel)
{
Serializer serializer(SaveStateManager::FileFormatVersion);
serializer.Stream(_cpu.get());
@ -568,12 +616,12 @@ void Console::Serialize(ostream &out)
if(_msu1) {
serializer.Stream(_msu1.get());
}
serializer.Save(out);
serializer.Save(out, compressionLevel);
}
void Console::Deserialize(istream &in, uint32_t fileFormatVersion)
void Console::Deserialize(istream &in, uint32_t fileFormatVersion, bool compressed)
{
Serializer serializer(in, fileFormatVersion);
Serializer serializer(in, fileFormatVersion, compressed);
serializer.Stream(_cpu.get());
serializer.Stream(_memoryManager.get());
serializer.Stream(_ppu.get());
@ -731,6 +779,11 @@ bool Console::IsRunning()
return _cpu != nullptr;
}
bool Console::IsRunAheadFrame()
{
return _isRunAheadFrame;
}
template<CpuType type>
void Console::ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType)
{

View file

@ -82,6 +82,8 @@ private:
ConsoleRegion _region;
uint32_t _masterClockRate;
atomic<bool> _isRunAheadFrame;
unique_ptr<DebugStats> _stats;
unique_ptr<FrameLimiter> _frameLimiter;
Timer _lastFrameTimer;
@ -92,6 +94,10 @@ private:
void WaitForLock();
void WaitForPauseEnd();
void RunFrame();
bool ProcessSystemActions();
void RunFrameWithRunAhead();
public:
Console();
~Console();
@ -99,6 +105,7 @@ public:
void Initialize();
void Release();
void Run();
void RunSingleFrame();
void Stop(bool sendNotification);
@ -123,8 +130,8 @@ public:
void Lock();
void Unlock();
void Serialize(ostream &out);
void Deserialize(istream &in, uint32_t fileFormatVersion);
void Serialize(ostream &out, int compressionLevel = 1);
void Deserialize(istream &in, uint32_t fileFormatVersion, bool compressed = true);
shared_ptr<SoundMixer> GetSoundMixer();
shared_ptr<VideoRenderer> GetVideoRenderer();
@ -155,6 +162,8 @@ public:
thread::id GetEmulationThreadId();
bool IsRunning();
bool IsInputRecordingEnabled();
bool IsRunAheadFrame();
template<CpuType type> void ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType);
template<CpuType type> void ProcessMemoryWrite(uint32_t addr, uint8_t value, MemoryOperationType opType);

View file

@ -163,8 +163,10 @@ void ControlManager::UpdateInputState()
debugger->ProcessEvent(EventType::InputPolled);
}
for(IInputRecorder* recorder : _inputRecorders) {
recorder->RecordInput(_controlDevices);
if(!_console->IsRunAheadFrame()) {
for(IInputRecorder* recorder : _inputRecorders) {
recorder->RecordInput(_controlDevices);
}
}
//MessageManager::Log(log);

View file

@ -476,6 +476,10 @@ bool Ppu::ProcessEndOfScanline(uint16_t hClock)
(_settings->GetEmulationSpeed() == 0 || _settings->GetEmulationSpeed() > 150) &&
_frameSkipTimer.GetElapsedMS() < 10
);
if(_console->IsRunAheadFrame()) {
_skipRender = true;
}
//Update overclock timings once per frame
UpdateNmiScanline();

View file

@ -44,6 +44,10 @@ void RewindManager::ClearBuffer()
void RewindManager::ProcessNotification(ConsoleNotificationType type, void * parameter)
{
if(_console->IsRunAheadFrame()) {
return;
}
if(type == ConsoleNotificationType::PpuFrameDone) {
_hasHistory = _history.size() >= 2;
if(_settings->GetRewindBufferSize() > 0) {

View file

@ -273,6 +273,8 @@ struct EmulationConfig
ConsoleRegion Region = ConsoleRegion::Auto;
uint32_t RunAheadFrames = 0;
bool EnableRandomPowerOnState = false;
bool EnableStrictBoardMappings = false;

View file

@ -93,7 +93,7 @@ void SoundMixer::PlayAudioBuffer(int16_t* samples, uint32_t sampleCount)
}
shared_ptr<RewindManager> rewindManager = _console->GetRewindManager();
if(rewindManager && rewindManager->SendAudio(out, count)) {
if(!_console->IsRunAheadFrame() && rewindManager && rewindManager->SendAudio(out, count)) {
bool isRecording = _waveRecorder || _console->GetVideoRenderer()->IsRecording();
if(isRecording) {
if(_waveRecorder) {

View file

@ -41,11 +41,9 @@ public:
{
if(_needReset) {
SetBit(SystemActionManager::Buttons::ResetButton);
_needReset = false;
}
if(_needPowerCycle) {
SetBit(SystemActionManager::Buttons::PowerButton);
_needPowerCycle = false;
}
}
@ -81,11 +79,13 @@ public:
bool IsResetPressed()
{
_needReset = false;
return IsPressed(SystemActionManager::Buttons::ResetButton);
}
bool IsPowerCyclePressed()
{
_needPowerCycle = false;
return IsPressed(SystemActionManager::Buttons::PowerButton);
}
};

View file

@ -158,6 +158,10 @@ uint32_t VideoDecoder::GetFrameCount()
void VideoDecoder::UpdateFrameSync(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber, bool forRewind)
{
if(_console->IsRunAheadFrame()) {
return;
}
if(_frameChanged) {
//Last frame isn't done decoding yet - sometimes Signal() introduces a 25-30ms delay
while(_frameChanged) {
@ -177,6 +181,10 @@ void VideoDecoder::UpdateFrameSync(uint16_t *ppuOutputBuffer, uint16_t width, ui
void VideoDecoder::UpdateFrame(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber)
{
if(_console->IsRunAheadFrame()) {
return;
}
if(_frameChanged) {
//Last frame isn't done decoding yet - sometimes Signal() introduces a 25-30ms delay
while(_frameChanged) {

View file

@ -15,6 +15,8 @@ namespace Mesen.GUI.Config
[MinMax(0, 5000)] public UInt32 RewindSpeed = 100;
public ConsoleRegion Region = ConsoleRegion.Auto;
[MinMax(0, 10)] public UInt32 RunAheadFrames = 0;
[MarshalAs(UnmanagedType.I1)] public bool EnableRandomPowerOnState = false;
[MarshalAs(UnmanagedType.I1)] public bool EnableStrictBoardMappings = false;

View file

@ -282,7 +282,9 @@
<Control ID="lblTurboSpeed">Fast Forward Speed:</Control>
<Control ID="lblRewindSpeedHint">% (0 = Maximum speed)</Control>
<Control ID="lblRewindSpeed">Rewind Speed:</Control>
<Control ID="lblRunAhead">Run Ahead:</Control>
<Control ID="lblRunAheadFrames">frames (reduces input lag, increases CPU usage)</Control>
<Control ID="tpgAdvanced">Advanced</Control>
<Control ID="lblDeveloperSettings">Recommended settings for developers (homebrew / ROM hacking)</Control>
<Control ID="lblMiscSettings">Miscellaneous Settings</Control>

View file

@ -31,6 +31,10 @@
this.tabMain = new System.Windows.Forms.TabControl();
this.tpgGeneral = new System.Windows.Forms.TabPage();
this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel();
this.flowLayoutPanel5 = new System.Windows.Forms.FlowLayoutPanel();
this.nudRunAheadFrames = new Mesen.GUI.Controls.MesenNumericUpDown();
this.lblRunAheadFrames = new System.Windows.Forms.Label();
this.lblRunAhead = new System.Windows.Forms.Label();
this.label1 = new System.Windows.Forms.Label();
this.flowLayoutPanel9 = new System.Windows.Forms.FlowLayoutPanel();
this.nudTurboSpeed = new Mesen.GUI.Controls.MesenNumericUpDown();
@ -50,6 +54,7 @@
this.chkEnableRandomPowerOnState = new Mesen.GUI.Controls.ctrlRiskyOption();
this.cboRamPowerOnState = new System.Windows.Forms.ComboBox();
this.lblRamPowerOnState = new System.Windows.Forms.Label();
this.chkEnableStrictBoardMappings = new Mesen.GUI.Controls.ctrlRiskyOption();
this.tpgOverclocking = new System.Windows.Forms.TabPage();
this.picHint = new System.Windows.Forms.PictureBox();
this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel();
@ -63,10 +68,10 @@
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.nudGsuClockSpeed = new Mesen.GUI.Controls.MesenNumericUpDown();
this.lblGsuClockSpeed = new System.Windows.Forms.Label();
this.chkEnableStrictBoardMappings = new Mesen.GUI.Controls.ctrlRiskyOption();
this.tabMain.SuspendLayout();
this.tpgGeneral.SuspendLayout();
this.tableLayoutPanel4.SuspendLayout();
this.flowLayoutPanel5.SuspendLayout();
this.flowLayoutPanel9.SuspendLayout();
this.flowLayoutPanel6.SuspendLayout();
this.flowLayoutPanel10.SuspendLayout();
@ -83,7 +88,7 @@
// baseConfigPanel
//
this.baseConfigPanel.Location = new System.Drawing.Point(0, 290);
this.baseConfigPanel.Size = new System.Drawing.Size(414, 29);
this.baseConfigPanel.Size = new System.Drawing.Size(445, 29);
this.baseConfigPanel.TabIndex = 4;
//
// tabMain
@ -95,7 +100,7 @@
this.tabMain.Location = new System.Drawing.Point(0, 0);
this.tabMain.Name = "tabMain";
this.tabMain.SelectedIndex = 0;
this.tabMain.Size = new System.Drawing.Size(414, 290);
this.tabMain.Size = new System.Drawing.Size(445, 290);
this.tabMain.TabIndex = 2;
//
// tpgGeneral
@ -104,7 +109,7 @@
this.tpgGeneral.Location = new System.Drawing.Point(4, 22);
this.tpgGeneral.Name = "tpgGeneral";
this.tpgGeneral.Padding = new System.Windows.Forms.Padding(3);
this.tpgGeneral.Size = new System.Drawing.Size(406, 264);
this.tpgGeneral.Size = new System.Drawing.Size(437, 264);
this.tpgGeneral.TabIndex = 2;
this.tpgGeneral.Text = "General";
this.tpgGeneral.UseVisualStyleBackColor = true;
@ -115,6 +120,8 @@
this.tableLayoutPanel4.ColumnCount = 2;
this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel4.Controls.Add(this.flowLayoutPanel5, 1, 4);
this.tableLayoutPanel4.Controls.Add(this.lblRunAhead, 0, 4);
this.tableLayoutPanel4.Controls.Add(this.label1, 0, 3);
this.tableLayoutPanel4.Controls.Add(this.flowLayoutPanel9, 1, 1);
this.tableLayoutPanel4.Controls.Add(this.lblTurboSpeed, 0, 1);
@ -126,15 +133,79 @@
this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel4.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel4.Name = "tableLayoutPanel4";
this.tableLayoutPanel4.RowCount = 5;
this.tableLayoutPanel4.RowCount = 6;
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel4.Size = new System.Drawing.Size(400, 258);
this.tableLayoutPanel4.Size = new System.Drawing.Size(431, 258);
this.tableLayoutPanel4.TabIndex = 0;
//
// flowLayoutPanel5
//
this.flowLayoutPanel5.AutoSize = true;
this.flowLayoutPanel5.Controls.Add(this.nudRunAheadFrames);
this.flowLayoutPanel5.Controls.Add(this.lblRunAheadFrames);
this.flowLayoutPanel5.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel5.Location = new System.Drawing.Point(111, 108);
this.flowLayoutPanel5.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel5.Name = "flowLayoutPanel5";
this.flowLayoutPanel5.Size = new System.Drawing.Size(320, 27);
this.flowLayoutPanel5.TabIndex = 20;
//
// nudRunAheadFrames
//
this.nudRunAheadFrames.DecimalPlaces = 0;
this.nudRunAheadFrames.Increment = new decimal(new int[] {
1,
0,
0,
0});
this.nudRunAheadFrames.IsHex = false;
this.nudRunAheadFrames.Location = new System.Drawing.Point(3, 3);
this.nudRunAheadFrames.Maximum = new decimal(new int[] {
10,
0,
0,
0});
this.nudRunAheadFrames.MaximumSize = new System.Drawing.Size(10000, 20);
this.nudRunAheadFrames.Minimum = new decimal(new int[] {
0,
0,
0,
0});
this.nudRunAheadFrames.MinimumSize = new System.Drawing.Size(0, 21);
this.nudRunAheadFrames.Name = "nudRunAheadFrames";
this.nudRunAheadFrames.Size = new System.Drawing.Size(48, 21);
this.nudRunAheadFrames.TabIndex = 1;
this.nudRunAheadFrames.Value = new decimal(new int[] {
0,
0,
0,
0});
//
// lblRunAheadFrames
//
this.lblRunAheadFrames.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblRunAheadFrames.AutoSize = true;
this.lblRunAheadFrames.Location = new System.Drawing.Point(57, 7);
this.lblRunAheadFrames.Name = "lblRunAheadFrames";
this.lblRunAheadFrames.Size = new System.Drawing.Size(236, 13);
this.lblRunAheadFrames.TabIndex = 2;
this.lblRunAheadFrames.Text = "frames (reduces input lag, increases CPU usage)";
//
// lblRunAhead
//
this.lblRunAhead.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblRunAhead.AutoSize = true;
this.lblRunAhead.Location = new System.Drawing.Point(3, 115);
this.lblRunAhead.Name = "lblRunAhead";
this.lblRunAhead.Size = new System.Drawing.Size(64, 13);
this.lblRunAhead.TabIndex = 19;
this.lblRunAhead.Text = "Run Ahead:";
//
// label1
//
this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left;
@ -154,7 +225,7 @@
this.flowLayoutPanel9.Location = new System.Drawing.Point(111, 27);
this.flowLayoutPanel9.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel9.Name = "flowLayoutPanel9";
this.flowLayoutPanel9.Size = new System.Drawing.Size(289, 27);
this.flowLayoutPanel9.Size = new System.Drawing.Size(320, 27);
this.flowLayoutPanel9.TabIndex = 14;
//
// nudTurboSpeed
@ -217,7 +288,7 @@
this.flowLayoutPanel6.Location = new System.Drawing.Point(111, 0);
this.flowLayoutPanel6.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel6.Name = "flowLayoutPanel6";
this.flowLayoutPanel6.Size = new System.Drawing.Size(289, 27);
this.flowLayoutPanel6.Size = new System.Drawing.Size(320, 27);
this.flowLayoutPanel6.TabIndex = 11;
//
// nudEmulationSpeed
@ -290,7 +361,7 @@
this.flowLayoutPanel10.Location = new System.Drawing.Point(111, 54);
this.flowLayoutPanel10.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel10.Name = "flowLayoutPanel10";
this.flowLayoutPanel10.Size = new System.Drawing.Size(289, 27);
this.flowLayoutPanel10.Size = new System.Drawing.Size(320, 27);
this.flowLayoutPanel10.TabIndex = 16;
//
// nudRewindSpeed
@ -349,7 +420,7 @@
this.tpgAdvanced.Location = new System.Drawing.Point(4, 22);
this.tpgAdvanced.Name = "tpgAdvanced";
this.tpgAdvanced.Padding = new System.Windows.Forms.Padding(3);
this.tpgAdvanced.Size = new System.Drawing.Size(406, 264);
this.tpgAdvanced.Size = new System.Drawing.Size(437, 264);
this.tpgAdvanced.TabIndex = 3;
this.tpgAdvanced.Text = "Advanced";
this.tpgAdvanced.UseVisualStyleBackColor = true;
@ -371,7 +442,7 @@
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel2.Size = new System.Drawing.Size(400, 258);
this.tableLayoutPanel2.Size = new System.Drawing.Size(431, 258);
this.tableLayoutPanel2.TabIndex = 5;
//
// chkEnableRandomPowerOnState
@ -403,13 +474,23 @@
this.lblRamPowerOnState.TabIndex = 0;
this.lblRamPowerOnState.Text = "Default power on state for RAM:";
//
// chkEnableStrictBoardMappings
//
this.chkEnableStrictBoardMappings.Checked = false;
this.tableLayoutPanel2.SetColumnSpan(this.chkEnableStrictBoardMappings, 2);
this.chkEnableStrictBoardMappings.Location = new System.Drawing.Point(0, 51);
this.chkEnableStrictBoardMappings.Name = "chkEnableStrictBoardMappings";
this.chkEnableStrictBoardMappings.Size = new System.Drawing.Size(400, 24);
this.chkEnableStrictBoardMappings.TabIndex = 7;
this.chkEnableStrictBoardMappings.Text = "Use strict board mappings (breaks some romhacks)";
//
// tpgOverclocking
//
this.tpgOverclocking.Controls.Add(this.picHint);
this.tpgOverclocking.Controls.Add(this.tableLayoutPanel3);
this.tpgOverclocking.Location = new System.Drawing.Point(4, 22);
this.tpgOverclocking.Name = "tpgOverclocking";
this.tpgOverclocking.Size = new System.Drawing.Size(406, 264);
this.tpgOverclocking.Size = new System.Drawing.Size(437, 264);
this.tpgOverclocking.TabIndex = 4;
this.tpgOverclocking.Text = "Overclocking";
this.tpgOverclocking.UseVisualStyleBackColor = true;
@ -442,7 +523,7 @@
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel3.Size = new System.Drawing.Size(406, 264);
this.tableLayoutPanel3.Size = new System.Drawing.Size(437, 264);
this.tableLayoutPanel3.TabIndex = 1;
//
// lblOverclockHint
@ -461,7 +542,7 @@
this.grpPpuTiming.Dock = System.Windows.Forms.DockStyle.Fill;
this.grpPpuTiming.Location = new System.Drawing.Point(3, 57);
this.grpPpuTiming.Name = "grpPpuTiming";
this.grpPpuTiming.Size = new System.Drawing.Size(400, 71);
this.grpPpuTiming.Size = new System.Drawing.Size(431, 71);
this.grpPpuTiming.TabIndex = 7;
this.grpPpuTiming.TabStop = false;
this.grpPpuTiming.Text = "PPU Vertical Blank Configuration";
@ -482,7 +563,7 @@
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel5.Size = new System.Drawing.Size(394, 52);
this.tableLayoutPanel5.Size = new System.Drawing.Size(425, 52);
this.tableLayoutPanel5.TabIndex = 0;
//
// nudExtraScanlinesAfterNmi
@ -582,7 +663,7 @@
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 1;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(406, 25);
this.tableLayoutPanel1.Size = new System.Drawing.Size(437, 25);
this.tableLayoutPanel1.TabIndex = 8;
//
// nudGsuClockSpeed
@ -628,21 +709,11 @@
this.lblGsuClockSpeed.TabIndex = 0;
this.lblGsuClockSpeed.Text = "Super FX clock speed (%):";
//
// chkEnableStrictBoardMappings
//
this.chkEnableStrictBoardMappings.Checked = false;
this.tableLayoutPanel2.SetColumnSpan(this.chkEnableStrictBoardMappings, 2);
this.chkEnableStrictBoardMappings.Location = new System.Drawing.Point(0, 51);
this.chkEnableStrictBoardMappings.Name = "chkEnableStrictBoardMappings";
this.chkEnableStrictBoardMappings.Size = new System.Drawing.Size(400, 24);
this.chkEnableStrictBoardMappings.TabIndex = 7;
this.chkEnableStrictBoardMappings.Text = "Use strict board mappings (breaks some romhacks)";
//
// frmEmulationConfig
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(414, 319);
this.ClientSize = new System.Drawing.Size(445, 319);
this.Controls.Add(this.tabMain);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
@ -657,6 +728,8 @@
this.tpgGeneral.PerformLayout();
this.tableLayoutPanel4.ResumeLayout(false);
this.tableLayoutPanel4.PerformLayout();
this.flowLayoutPanel5.ResumeLayout(false);
this.flowLayoutPanel5.PerformLayout();
this.flowLayoutPanel9.ResumeLayout(false);
this.flowLayoutPanel9.PerformLayout();
this.flowLayoutPanel6.ResumeLayout(false);
@ -718,5 +791,9 @@
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
private Controls.ctrlRiskyOption chkEnableRandomPowerOnState;
private Controls.ctrlRiskyOption chkEnableStrictBoardMappings;
}
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel5;
private Controls.MesenNumericUpDown nudRunAheadFrames;
private System.Windows.Forms.Label lblRunAheadFrames;
private System.Windows.Forms.Label lblRunAhead;
}
}

View file

@ -26,6 +26,7 @@ namespace Mesen.GUI.Forms.Config
AddBinding(nameof(EmulationConfig.TurboSpeed), nudTurboSpeed);
AddBinding(nameof(EmulationConfig.RewindSpeed), nudRewindSpeed);
AddBinding(nameof(EmulationConfig.Region), cboRegion);
AddBinding(nameof(EmulationConfig.RunAheadFrames), nudRunAheadFrames);
AddBinding(nameof(EmulationConfig.RamPowerOnState), cboRamPowerOnState);
AddBinding(nameof(EmulationConfig.EnableRandomPowerOnState), chkEnableRandomPowerOnState);

View file

@ -8,49 +8,60 @@ Serializer::Serializer(uint32_t version)
{
_version = version;
_block.Data = vector<uint8_t>(0x50000);
_block.Position = 0;
_block.reset(new BlockData());
_block->Data = vector<uint8_t>(0x50000);
_block->Position = 0;
_saving = true;
}
Serializer::Serializer(istream &file, uint32_t version)
Serializer::Serializer(istream &file, uint32_t version, bool compressed)
{
_version = version;
_block.Position = 0;
_block.reset(new BlockData());
_block->Position = 0;
_saving = false;
uint32_t decompressedSize;
file.read((char*)&decompressedSize, sizeof(decompressedSize));
if(compressed) {
uint32_t decompressedSize;
file.read((char*)&decompressedSize, sizeof(decompressedSize));
uint32_t compressedSize;
file.read((char*)&compressedSize, sizeof(compressedSize));
uint32_t compressedSize;
file.read((char*)&compressedSize, sizeof(compressedSize));
vector<uint8_t> compressedData(compressedSize, 0);
file.read((char*)compressedData.data(), compressedSize);
vector<uint8_t> compressedData(compressedSize, 0);
file.read((char*)compressedData.data(), compressedSize);
_block.Data = vector<uint8_t>(decompressedSize, 0);
_block->Data = vector<uint8_t>(decompressedSize, 0);
unsigned long decompSize = decompressedSize;
uncompress(_block.Data.data(), &decompSize, compressedData.data(), (unsigned long)compressedData.size());
unsigned long decompSize = decompressedSize;
uncompress(_block->Data.data(), &decompSize, compressedData.data(), (unsigned long)compressedData.size());
} else {
file.seekg(0, std::ios::end);
uint32_t size = (uint32_t)file.tellg();
file.seekg(0, std::ios::beg);
_block->Data = vector<uint8_t>(size, 0);
file.read((char*)_block->Data.data(), size);
}
}
void Serializer::EnsureCapacity(uint32_t typeSize)
{
//Make sure the current block/stream is large enough to fit the next write
uint32_t oldSize = (uint32_t)_block.Data.size();
uint32_t oldSize = (uint32_t)_block->Data.size();
if(oldSize == 0) {
oldSize = typeSize * 2;
}
uint32_t sizeRequired = _block.Position + typeSize;
uint32_t sizeRequired = _block->Position + typeSize;
uint32_t newSize = oldSize;
while(newSize < sizeRequired) {
newSize *= 2;
}
_block.Data.resize(newSize);
_block->Data.resize(newSize);
}
void Serializer::RecursiveStream()
@ -59,18 +70,18 @@ void Serializer::RecursiveStream()
void Serializer::StreamStartBlock()
{
BlockData block;
block.Position = 0;
unique_ptr<BlockData> block(new BlockData());
block->Position = 0;
if(!_saving) {
VectorInfo<uint8_t> vectorInfo = { &block.Data };
VectorInfo<uint8_t> vectorInfo = { &block->Data };
InternalStream(vectorInfo);
} else {
block.Data = vector<uint8_t>(0x100);
block->Data = vector<uint8_t>(0x100);
}
_blocks.push_back(_block);
_block = block;
_blocks.push_back(std::move(_block));
_block = std::move(block);
}
void Serializer::StreamEndBlock()
@ -79,28 +90,32 @@ void Serializer::StreamEndBlock()
throw std::runtime_error("Invalid call to end block");
}
BlockData block = _block;
unique_ptr<BlockData> block = std::move(_block);
_block = _blocks.back();
_block = std::move(_blocks.back());
_blocks.pop_back();
if(_saving) {
ArrayInfo<uint8_t> arrayInfo { block.Data.data(), block.Position };
ArrayInfo<uint8_t> arrayInfo { block->Data.data(), block->Position };
InternalStream(arrayInfo);
}
}
void Serializer::Save(ostream& file, int compressionLevel)
{
unsigned long compressedSize = compressBound((unsigned long)_block.Position);
uint8_t* compressedData = new uint8_t[compressedSize];
compress2(compressedData, &compressedSize, (unsigned char*)_block.Data.data(), (unsigned long)_block.Position, compressionLevel);
if(compressionLevel == 0) {
file.write((char*)_block->Data.data(), _block->Position);
} else {
unsigned long compressedSize = compressBound((unsigned long)_block->Position);
uint8_t* compressedData = new uint8_t[compressedSize];
compress2(compressedData, &compressedSize, (unsigned char*)_block->Data.data(), (unsigned long)_block->Position, compressionLevel);
uint32_t size = (uint32_t)compressedSize;
file.write((char*)&_block.Position, sizeof(uint32_t));
file.write((char*)&size, sizeof(uint32_t));
file.write((char*)compressedData, compressedSize);
delete[] compressedData;
uint32_t size = (uint32_t)compressedSize;
file.write((char*)&_block->Position, sizeof(uint32_t));
file.write((char*)&size, sizeof(uint32_t));
file.write((char*)compressedData, compressedSize);
delete[] compressedData;
}
}
void Serializer::WriteEmptyBlock(ostream* file)

View file

@ -34,9 +34,8 @@ struct BlockData
class Serializer
{
private:
vector<BlockData> _blocks;
BlockData _block;
vector<unique_ptr<BlockData>> _blocks;
unique_ptr<BlockData> _block;
uint32_t _version = 0;
bool _saving = false;
@ -60,7 +59,7 @@ private:
public:
Serializer(uint32_t version);
Serializer(istream &file, uint32_t version);
Serializer(istream &file, uint32_t version, bool compressed = true);
uint32_t GetVersion() { return _version; }
bool IsSaving() { return _saving; }
@ -87,15 +86,15 @@ void Serializer::StreamElement(T &value, T defaultValue)
EnsureCapacity(typeSize);
for(int i = 0; i < typeSize; i++) {
_block.Data[_block.Position++] = bytes[i];
_block->Data[_block->Position++] = bytes[i];
}
} else {
if(_block.Position + sizeof(T) <= _block.Data.size()) {
memcpy(&value, _block.Data.data() + _block.Position, sizeof(T));
_block.Position += sizeof(T);
if(_block->Position + sizeof(T) <= _block->Data.size()) {
memcpy(&value, _block->Data.data() + _block->Position, sizeof(T));
_block->Position += sizeof(T);
} else {
value = defaultValue;
_block.Position = (uint32_t)_block.Data.size();
_block->Position = (uint32_t)_block->Data.size();
}
}
}
@ -115,11 +114,11 @@ void Serializer::InternalStream(ArrayInfo<T> &info)
EnsureCapacity(info.ElementCount * sizeof(T));
if(_saving) {
memcpy(_block.Data.data() + _block.Position, info.Array, info.ElementCount * sizeof(T));
memcpy(_block->Data.data() + _block->Position, info.Array, info.ElementCount * sizeof(T));
} else {
memcpy(info.Array, _block.Data.data() + _block.Position, info.ElementCount * sizeof(T));
memcpy(info.Array, _block->Data.data() + _block->Position, info.ElementCount * sizeof(T));
}
_block.Position += info.ElementCount * sizeof(T);
_block->Position += info.ElementCount * sizeof(T);
}
template<typename T>