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:
parent
30c7eea6fa
commit
5f2c502be9
15 changed files with 301 additions and 123 deletions
137
Core/Console.cpp
137
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<CpuType type>
|
||||
void Console::ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -273,6 +273,8 @@ struct EmulationConfig
|
|||
|
||||
ConsoleRegion Region = ConsoleRegion::Auto;
|
||||
|
||||
uint32_t RunAheadFrames = 0;
|
||||
|
||||
bool EnableRandomPowerOnState = false;
|
||||
bool EnableStrictBoardMappings = false;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
133
UI/Forms/Config/frmEmulationConfig.Designer.cs
generated
133
UI/Forms/Config/frmEmulationConfig.Designer.cs
generated
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue