From 5f2c502be9908b80ec359d96b09688f6917b8fda Mon Sep 17 00:00:00 2001 From: Sour Date: Thu, 26 Dec 2019 12:03:38 -0500 Subject: [PATCH] Run ahead support (+ improved save/load state performance) Note: Run ahead currently doesn't work well with netplay, movies and rewind --- Core/Console.cpp | 137 ++++++++++++------ Core/Console.h | 13 +- Core/ControlManager.cpp | 6 +- Core/Ppu.cpp | 4 + Core/RewindManager.cpp | 4 + Core/SettingTypes.h | 2 + Core/SoundMixer.cpp | 2 +- Core/SystemActionManager.h | 4 +- Core/VideoDecoder.cpp | 8 + UI/Config/EmulationConfig.cs | 2 + UI/Dependencies/resources.en.xml | 4 +- .../Config/frmEmulationConfig.Designer.cs | 133 +++++++++++++---- UI/Forms/Config/frmEmulationConfig.cs | 1 + Utilities/Serializer.cpp | 81 ++++++----- Utilities/Serializer.h | 23 ++- 15 files changed, 301 insertions(+), 123 deletions(-) diff --git a/Core/Console.cpp b/Core/Console.cpp index 1f75037..2b70ca7 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -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 void Console::ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType) { diff --git a/Core/Console.h b/Core/Console.h index d3906c8..d635d74 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -82,6 +82,8 @@ private: ConsoleRegion _region; uint32_t _masterClockRate; + atomic _isRunAheadFrame; + unique_ptr _stats; unique_ptr _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 GetSoundMixer(); shared_ptr GetVideoRenderer(); @@ -155,6 +162,8 @@ public: thread::id GetEmulationThreadId(); bool IsRunning(); + bool IsInputRecordingEnabled(); + bool IsRunAheadFrame(); template void ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType); template void ProcessMemoryWrite(uint32_t addr, uint8_t value, MemoryOperationType opType); diff --git a/Core/ControlManager.cpp b/Core/ControlManager.cpp index 5cf2e7f..8aa15b0 100644 --- a/Core/ControlManager.cpp +++ b/Core/ControlManager.cpp @@ -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); diff --git a/Core/Ppu.cpp b/Core/Ppu.cpp index 7d4fa28..77f6657 100644 --- a/Core/Ppu.cpp +++ b/Core/Ppu.cpp @@ -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(); diff --git a/Core/RewindManager.cpp b/Core/RewindManager.cpp index 9760734..27e8c66 100644 --- a/Core/RewindManager.cpp +++ b/Core/RewindManager.cpp @@ -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) { diff --git a/Core/SettingTypes.h b/Core/SettingTypes.h index 70e3b3f..e58f933 100644 --- a/Core/SettingTypes.h +++ b/Core/SettingTypes.h @@ -273,6 +273,8 @@ struct EmulationConfig ConsoleRegion Region = ConsoleRegion::Auto; + uint32_t RunAheadFrames = 0; + bool EnableRandomPowerOnState = false; bool EnableStrictBoardMappings = false; diff --git a/Core/SoundMixer.cpp b/Core/SoundMixer.cpp index 185992f..3320c8b 100644 --- a/Core/SoundMixer.cpp +++ b/Core/SoundMixer.cpp @@ -93,7 +93,7 @@ void SoundMixer::PlayAudioBuffer(int16_t* samples, uint32_t sampleCount) } shared_ptr 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) { diff --git a/Core/SystemActionManager.h b/Core/SystemActionManager.h index 6cd33e2..c5e42cc 100644 --- a/Core/SystemActionManager.h +++ b/Core/SystemActionManager.h @@ -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); } }; \ No newline at end of file diff --git a/Core/VideoDecoder.cpp b/Core/VideoDecoder.cpp index 8186f3d..60efb1a 100644 --- a/Core/VideoDecoder.cpp +++ b/Core/VideoDecoder.cpp @@ -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) { diff --git a/UI/Config/EmulationConfig.cs b/UI/Config/EmulationConfig.cs index a84f110..45ed1ff 100644 --- a/UI/Config/EmulationConfig.cs +++ b/UI/Config/EmulationConfig.cs @@ -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; diff --git a/UI/Dependencies/resources.en.xml b/UI/Dependencies/resources.en.xml index e08fec4..f3a8374 100644 --- a/UI/Dependencies/resources.en.xml +++ b/UI/Dependencies/resources.en.xml @@ -282,7 +282,9 @@ Fast Forward Speed: % (0 = Maximum speed) Rewind Speed: - + Run Ahead: + frames (reduces input lag, increases CPU usage) + Advanced Recommended settings for developers (homebrew / ROM hacking) Miscellaneous Settings diff --git a/UI/Forms/Config/frmEmulationConfig.Designer.cs b/UI/Forms/Config/frmEmulationConfig.Designer.cs index 7370354..9c54a5c 100644 --- a/UI/Forms/Config/frmEmulationConfig.Designer.cs +++ b/UI/Forms/Config/frmEmulationConfig.Designer.cs @@ -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; + } } \ No newline at end of file diff --git a/UI/Forms/Config/frmEmulationConfig.cs b/UI/Forms/Config/frmEmulationConfig.cs index e297441..d64a440 100644 --- a/UI/Forms/Config/frmEmulationConfig.cs +++ b/UI/Forms/Config/frmEmulationConfig.cs @@ -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); diff --git a/Utilities/Serializer.cpp b/Utilities/Serializer.cpp index 26ae2da..0a20fe3 100644 --- a/Utilities/Serializer.cpp +++ b/Utilities/Serializer.cpp @@ -8,49 +8,60 @@ Serializer::Serializer(uint32_t version) { _version = version; - _block.Data = vector(0x50000); - _block.Position = 0; + _block.reset(new BlockData()); + _block->Data = vector(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 compressedData(compressedSize, 0); - file.read((char*)compressedData.data(), compressedSize); + vector compressedData(compressedSize, 0); + file.read((char*)compressedData.data(), compressedSize); - _block.Data = vector(decompressedSize, 0); + _block->Data = vector(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(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 block(new BlockData()); + block->Position = 0; if(!_saving) { - VectorInfo vectorInfo = { &block.Data }; + VectorInfo vectorInfo = { &block->Data }; InternalStream(vectorInfo); } else { - block.Data = vector(0x100); + block->Data = vector(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 block = std::move(_block); - _block = _blocks.back(); + _block = std::move(_blocks.back()); _blocks.pop_back(); if(_saving) { - ArrayInfo arrayInfo { block.Data.data(), block.Position }; + ArrayInfo 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) diff --git a/Utilities/Serializer.h b/Utilities/Serializer.h index 8f1661a..07cd518 100644 --- a/Utilities/Serializer.h +++ b/Utilities/Serializer.h @@ -34,9 +34,8 @@ struct BlockData class Serializer { private: - vector _blocks; - - BlockData _block; + vector> _blocks; + unique_ptr _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 &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