Core: Added runahead support (1 to 10 frames)

This commit is contained in:
Sour 2019-12-24 13:46:10 -05:00
parent fd02e406b0
commit c7675bcd49
24 changed files with 1143 additions and 964 deletions

View file

@ -720,6 +720,17 @@ void Console::RunSlaveCpu()
}
}
void Console::RunFrame()
{
uint32_t frameCount = _ppu->GetFrameCount();
while(_ppu->GetFrameCount() == frameCount) {
_cpu->Exec();
if(_slave) {
RunSlaveCpu();
}
}
}
void Console::Run()
{
Timer clockTimer;
@ -729,7 +740,6 @@ void Console::Run()
double targetTime;
double lastFrameMin = 9999;
double lastFrameMax = 0;
uint32_t lastFrameNumber = -1;
double lastDelay = GetFrameDelay();
_runLock.Acquire();
@ -753,131 +763,135 @@ void Console::Run()
bool crashed = false;
try {
while(true) {
_cpu->Exec();
if(_slave) {
RunSlaveCpu();
stringstream runAheadState;
bool useRunAhead = _settings->GetRunAheadFrames() > 0 && !_debugger && !_rewindManager->IsRewinding();
if(useRunAhead) {
RunFrameWithRunAhead(runAheadState);
} else {
RunFrame();
}
if(_ppu->GetFrameCount() != lastFrameNumber) {
_soundMixer->ProcessEndOfFrame();
_soundMixer->ProcessEndOfFrame();
if(_slave) {
_slave->_soundMixer->ProcessEndOfFrame();
}
if(_historyViewer) {
_historyViewer->ProcessEndOfFrame();
}
_rewindManager->ProcessEndOfFrame();
_settings->DisableOverclocking(_disableOcNextFrame || IsNsf());
_disableOcNextFrame = false;
//Update model (ntsc/pal) and get delay for next frame
UpdateNesModel(true);
double delay = GetFrameDelay();
if(_resetRunTimers || delay != lastDelay || (clockTimer.GetElapsedMS() - targetTime) > 300) {
//Reset the timers, this can happen in 3 scenarios:
//1) Target frame rate changed
//2) The console was reset/power cycled or the emulation was paused (with or without the debugger)
//3) As a satefy net, if we overshoot our target by over 300 milliseconds, the timer is reset, too.
// This can happen when something slows the emulator down severely (or when breaking execution in VS when debugging Mesen itself, etc.)
clockTimer.Reset();
targetTime = 0;
_resetRunTimers = false;
lastDelay = delay;
}
targetTime += delay;
bool displayDebugInfo = _settings->CheckFlag(EmulationFlags::DisplayDebugInfo);
if(displayDebugInfo) {
double lastFrameTime = lastFrameTimer.GetElapsedMS();
lastFrameTimer.Reset();
frameDurations[frameDurationIndex] = lastFrameTime;
frameDurationIndex = (frameDurationIndex + 1) % 60;
DisplayDebugInformation(lastFrameTime, lastFrameMin, lastFrameMax, frameDurations);
if(_slave) {
_slave->_soundMixer->ProcessEndOfFrame();
_slave->DisplayDebugInformation(lastFrameTime, lastFrameMin, lastFrameMax, frameDurations);
}
}
if(_historyViewer) {
_historyViewer->ProcessEndOfFrame();
}
_rewindManager->ProcessEndOfFrame();
_settings->DisableOverclocking(_disableOcNextFrame || IsNsf());
_disableOcNextFrame = false;
//Update model (ntsc/pal) and get delay for next frame
UpdateNesModel(true);
double delay = GetFrameDelay();
if(_resetRunTimers || delay != lastDelay || (clockTimer.GetElapsedMS() - targetTime) > 300) {
//Reset the timers, this can happen in 3 scenarios:
//1) Target frame rate changed
//2) The console was reset/power cycled or the emulation was paused (with or without the debugger)
//3) As a satefy net, if we overshoot our target by over 300 milliseconds, the timer is reset, too.
// This can happen when something slows the emulator down severely (or when breaking execution in VS when debugging Mesen itself, etc.)
clockTimer.Reset();
//When sleeping for a long time (e.g <= 25% speed), sleep in small chunks and check to see if we need to stop sleeping between each sleep call
while(targetTime - clockTimer.GetElapsedMS() > 50) {
clockTimer.WaitUntil(clockTimer.GetElapsedMS() + 40);
if(delay != GetFrameDelay() || _stop || _settings->NeedsPause() || _pauseCounter > 0) {
targetTime = 0;
_resetRunTimers = false;
lastDelay = delay;
}
targetTime += delay;
bool displayDebugInfo = _settings->CheckFlag(EmulationFlags::DisplayDebugInfo);
if(displayDebugInfo) {
double lastFrameTime = lastFrameTimer.GetElapsedMS();
lastFrameTimer.Reset();
frameDurations[frameDurationIndex] = lastFrameTime;
frameDurationIndex = (frameDurationIndex + 1) % 60;
DisplayDebugInformation(lastFrameTime, lastFrameMin, lastFrameMax, frameDurations);
if(_slave) {
_slave->DisplayDebugInformation(lastFrameTime, lastFrameMin, lastFrameMax, frameDurations);
}
}
//When sleeping for a long time (e.g <= 25% speed), sleep in small chunks and check to see if we need to stop sleeping between each sleep call
while(targetTime - clockTimer.GetElapsedMS() > 50) {
clockTimer.WaitUntil(clockTimer.GetElapsedMS() + 40);
if(delay != GetFrameDelay() || _stop || _settings->NeedsPause() || _pauseCounter > 0) {
targetTime = 0;
break;
}
}
//Sleep until we're ready to start the next frame
clockTimer.WaitUntil(targetTime);
if(_pauseCounter > 0) {
//Need to temporarely pause the emu (to save/load a state, etc.)
_runLock.Release();
//Spin wait until we are allowed to start again
while(_pauseCounter > 0) { }
_runLock.Acquire();
}
if(_pauseOnNextFrameRequested) {
//Used by "Run Single Frame" option
_settings->SetFlags(EmulationFlags::Paused);
_pauseOnNextFrameRequested = false;
}
bool pausedRequired = _settings->NeedsPause();
if(pausedRequired && !_stop && !_settings->CheckFlag(EmulationFlags::DebuggerWindowEnabled)) {
_notificationManager->SendNotification(ConsoleNotificationType::GamePaused);
//Prevent audio from looping endlessly while game is paused
_soundMixer->StopAudio();
if(_slave) {
_slave->_soundMixer->StopAudio();
}
_runLock.Release();
PlatformUtilities::EnableScreensaver();
PlatformUtilities::RestoreTimerResolution();
while(pausedRequired && !_stop && !_settings->CheckFlag(EmulationFlags::DebuggerWindowEnabled)) {
//Sleep until emulation is resumed
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(30));
pausedRequired = _settings->NeedsPause();
_paused = true;
}
_paused = false;
PlatformUtilities::DisableScreensaver();
_runLock.Acquire();
_notificationManager->SendNotification(ConsoleNotificationType::GameResumed);
lastFrameTimer.Reset();
//Reset the timer to avoid speed up after a pause
_resetRunTimers = true;
}
if(_settings->CheckFlag(EmulationFlags::UseHighResolutionTimer)) {
PlatformUtilities::EnableHighResolutionTimer();
} else {
PlatformUtilities::RestoreTimerResolution();
}
_systemActionManager->ProcessSystemActions();
lastFrameNumber = _ppu->GetFrameCount();
if(_stop) {
_stop = false;
break;
}
}
//Sleep until we're ready to start the next frame
clockTimer.WaitUntil(targetTime);
if(useRunAhead) {
_settings->SetRunAheadFrameFlag(true);
LoadState(runAheadState);
_settings->SetRunAheadFrameFlag(false);
}
if(_pauseCounter > 0) {
//Need to temporarely pause the emu (to save/load a state, etc.)
_runLock.Release();
//Spin wait until we are allowed to start again
while(_pauseCounter > 0) { }
_runLock.Acquire();
}
if(_pauseOnNextFrameRequested) {
//Used by "Run Single Frame" option
_settings->SetFlags(EmulationFlags::Paused);
_pauseOnNextFrameRequested = false;
}
bool pausedRequired = _settings->NeedsPause();
if(pausedRequired && !_stop && !_settings->CheckFlag(EmulationFlags::DebuggerWindowEnabled)) {
_notificationManager->SendNotification(ConsoleNotificationType::GamePaused);
//Prevent audio from looping endlessly while game is paused
_soundMixer->StopAudio();
if(_slave) {
_slave->_soundMixer->StopAudio();
}
_runLock.Release();
PlatformUtilities::EnableScreensaver();
PlatformUtilities::RestoreTimerResolution();
while(pausedRequired && !_stop && !_settings->CheckFlag(EmulationFlags::DebuggerWindowEnabled)) {
//Sleep until emulation is resumed
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(30));
pausedRequired = _settings->NeedsPause();
_paused = true;
}
_paused = false;
PlatformUtilities::DisableScreensaver();
_runLock.Acquire();
_notificationManager->SendNotification(ConsoleNotificationType::GameResumed);
lastFrameTimer.Reset();
//Reset the timer to avoid speed up after a pause
_resetRunTimers = true;
}
if(_settings->CheckFlag(EmulationFlags::UseHighResolutionTimer)) {
PlatformUtilities::EnableHighResolutionTimer();
} else {
PlatformUtilities::RestoreTimerResolution();
}
_systemActionManager->ProcessSystemActions();
if(_stop) {
_stop = false;
break;
}
}
} catch(const std::runtime_error &ex) {
crashed = true;
@ -926,6 +940,26 @@ void Console::Run()
_notificationManager->SendNotification(ConsoleNotificationType::EmulationStopped);
}
void Console::RunFrameWithRunAhead(std::stringstream& runAheadState)
{
uint32_t runAheadFrames = _settings->GetRunAheadFrames();
_settings->SetRunAheadFrameFlag(true);
//Run a single frame and save the state (no audio/video)
RunFrame();
SaveState(runAheadState);
while(runAheadFrames > 1) {
//Run extra frames if the requested run ahead frame count is higher than 1
runAheadFrames--;
RunFrame();
}
_apu->EndFrame();
_settings->SetRunAheadFrameFlag(false);
//Run one frame normally (with audio/video output)
RunFrame();
_apu->EndFrame();
}
void Console::ResetRunTimers()
{
_resetRunTimers = true;
@ -1023,6 +1057,9 @@ double Console::GetFrameDelay()
void Console::SaveState(ostream &saveStream)
{
if(_initialized) {
//Send any unprocessed sound to the SoundMixer - needed for rewind
_apu->EndFrame();
_cpu->SaveSnapshot(&saveStream);
_ppu->SaveSnapshot(&saveStream);
_memoryManager->SaveSnapshot(&saveStream);
@ -1050,6 +1087,9 @@ void Console::LoadState(istream &loadStream)
void Console::LoadState(istream &loadStream, uint32_t stateVersion)
{
if(_initialized) {
//Send any unprocessed sound to the SoundMixer - needed for rewind
_apu->EndFrame();
_cpu->LoadSnapshot(&loadStream, stateVersion);
_ppu->LoadSnapshot(&loadStream, stateVersion);
_memoryManager->LoadSnapshot(&loadStream, stateVersion);
@ -1080,9 +1120,6 @@ void Console::LoadState(istream &loadStream, uint32_t stateVersion)
void Console::LoadState(uint8_t *buffer, uint32_t bufferSize)
{
//Send any unprocessed sound to the SoundMixer - needed for rewind
_apu->EndFrame();
stringstream stream;
stream.write((char*)buffer, bufferSize);
stream.seekg(0, ios::beg);

View file

@ -100,6 +100,8 @@ private:
bool _initialized = false;
std::thread::id _emulationThreadId;
void RunFrameWithRunAhead(std::stringstream& runAheadState);
void LoadHdPack(VirtualFile &romFile, VirtualFile &patchFile);
void UpdateNesModel(bool sendNotification);
@ -156,6 +158,7 @@ public:
void RunSingleFrame();
void RunSlaveCpu();
void RunFrame();
bool UpdateHdPackMode();
shared_ptr<SystemActionManager> GetSystemActionManager();

View file

@ -293,8 +293,10 @@ void ControlManager::UpdateInputState()
debugger->ProcessEvent(EventType::InputPolled);
}
for(IInputRecorder* recorder : _inputRecorders) {
recorder->RecordInput(_controlDevices);
if(!_console->GetSettings()->IsRunAheadFrame()) {
for(IInputRecorder* recorder : _inputRecorders) {
recorder->RecordInput(_controlDevices);
}
}
//Used by VS System games

View file

@ -655,6 +655,9 @@ private:
uint32_t _sampleRate = 48000;
AudioFilterSettings _audioFilterSettings;
uint32_t _runAheadFrames = 0;
bool _isRunAheadFrame = false;
NesModel _model = NesModel::Auto;
PpuModel _ppuModel = PpuModel::Ppu2C02;
@ -992,6 +995,26 @@ public:
return value;
}
void SetRunAheadFrames(uint32_t frameCount)
{
_runAheadFrames = frameCount;
}
uint32_t GetRunAheadFrames()
{
return _runAheadFrames;
}
void SetRunAheadFrameFlag(bool disabled)
{
_isRunAheadFrame = disabled;
}
bool IsRunAheadFrame()
{
return _isRunAheadFrame;
}
//0: No limit, Number: % of default speed (50/60fps)
void SetEmulationSpeed(uint32_t emulationSpeed, bool displaySpeed = false)
{

View file

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

View file

@ -164,7 +164,7 @@ void SoundMixer::PlayAudioBuffer(uint32_t time)
_crossFeedFilter.ApplyFilter(_outputBuffer, sampleCount, filterSettings.CrossFadeRatio);
}
if(rewindManager && rewindManager->SendAudio(_outputBuffer, (uint32_t)sampleCount, _sampleRate)) {
if(!_settings->IsRunAheadFrame() && rewindManager && rewindManager->SendAudio(_outputBuffer, (uint32_t)sampleCount, _sampleRate)) {
bool isRecording = _waveRecorder || _console->GetVideoRenderer()->IsRecording();
if(isRecording) {
shared_ptr<WaveRecorder> recorder = _waveRecorder;

View file

@ -41,11 +41,9 @@ public:
{
if(_needReset) {
SetBit(SystemActionManager::Buttons::ResetButton);
_needReset = false;
}
if(_needPowerCycle) {
SetBit(SystemActionManager::Buttons::PowerButton);
_needPowerCycle = false;
}
}
@ -72,6 +70,7 @@ public:
shared_ptr<Console> console = _console;
if(console) {
if(IsPressed(SystemActionManager::Buttons::ResetButton)) {
_needReset = false;
console->ResetComponents(true);
console->GetControlManager()->UpdateInputState();
}

View file

@ -21,6 +21,7 @@
VideoDecoder::VideoDecoder(shared_ptr<Console> console)
{
_console = console;
_settings = _console->GetSettings();
_frameChanged = false;
_stopFlag = false;
UpdateVideoFilter();
@ -158,6 +159,10 @@ uint32_t VideoDecoder::GetFrameCount()
void VideoDecoder::UpdateFrameSync(void *ppuOutputBuffer, HdScreenInfo *hdScreenInfo)
{
if(_settings->IsRunAheadFrame()) {
return;
}
_frameNumber = _console->GetFrameCount();
_hdScreenInfo = hdScreenInfo;
_ppuOutputBuffer = (uint16_t*)ppuOutputBuffer;
@ -167,6 +172,10 @@ void VideoDecoder::UpdateFrameSync(void *ppuOutputBuffer, HdScreenInfo *hdScreen
void VideoDecoder::UpdateFrame(void *ppuOutputBuffer, HdScreenInfo *hdScreenInfo)
{
if(_settings->IsRunAheadFrame()) {
return;
}
if(_frameChanged) {
//Last frame isn't done decoding yet - sometimes Signal() introduces a 25-30ms delay
while(_frameChanged) {

View file

@ -27,7 +27,7 @@ class VideoDecoder
{
private:
shared_ptr<Console> _console;
EmulationSettings* _settings;
uint16_t *_ppuOutputBuffer = nullptr;
HdScreenInfo *_hdScreenInfo = nullptr;
bool _hdFilterEnabled = false;

View file

@ -38,6 +38,7 @@ namespace Mesen.GUI.Config
[MinMax(0, 5000)] public UInt32 EmulationSpeed = 100;
[MinMax(0, 5000)] public UInt32 TurboSpeed = 300;
[MinMax(0, 5000)] public UInt32 RewindSpeed = 100;
[MinMax(0, 10)] public UInt32 RunAheadFrames = 0;
public EmulationInfo()
{
@ -49,6 +50,7 @@ namespace Mesen.GUI.Config
InteropEmu.SetEmulationSpeed(emulationInfo.EmulationSpeed);
InteropEmu.SetTurboRewindSpeed(emulationInfo.TurboSpeed, emulationInfo.RewindSpeed);
InteropEmu.SetRunAheadFrames(emulationInfo.RunAheadFrames);
InteropEmu.SetFlag(EmulationFlags.Mmc3IrqAltBehavior, emulationInfo.UseAlternativeMmc3Irq);
InteropEmu.SetFlag(EmulationFlags.AllowInvalidInput, emulationInfo.AllowInvalidInput);

View file

@ -308,6 +308,8 @@
<Control ID="lblEmulationSpeed">Velocitat d'emulació:</Control>
<Control ID="lblRewindSpeedHint">% (0 = Velocitat màxima)</Control>
<Control ID="lblRewindSpeed">Velocitat del rebobinat:</Control>
<Control ID="lblRunAhead">Run Ahead:</Control>
<Control ID="lblRunAheadFrames">frames (reduces input lag, increases CPU usage)</Control>
<Control ID="tpgAdvanced">Avançat</Control>
<Control ID="lblDeveloperSettings">Recommended settings for developers (homebrew / ROM hacking)</Control>

View file

@ -308,6 +308,8 @@
<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>

View file

@ -307,6 +307,8 @@
<Control ID="lblEmulationSpeed">Velocidad de emulación:</Control>
<Control ID="lblRewindSpeedHint">% (0 = Velocidad máxima)</Control>
<Control ID="lblRewindSpeed">Velocidad de rebobinado:</Control>
<Control ID="lblRunAhead">Run Ahead:</Control>
<Control ID="lblRunAheadFrames">frames (reduces input lag, increases CPU usage)</Control>
<Control ID="tpgAdvanced">Avanzado</Control>
<Control ID="lblDeveloperSettings">Configuración recomendada para desarrolladores (homebrew / ROM hacking)</Control>

View file

@ -308,6 +308,8 @@
<Control ID="lblTurboSpeed">Vitesse d'avance rapide :</Control>
<Control ID="lblRewindSpeedHint">% (0 = Vitesse maximale)</Control>
<Control ID="lblRewindSpeed">Vitesse du rembobinage :</Control>
<Control ID="lblRunAhead">Run Ahead:</Control>
<Control ID="lblRunAheadFrames">images (réduit l'input lag, au coût de la performance)</Control>
<Control ID="tpgAdvanced">Avancé</Control>
<Control ID="lblDeveloperSettings">Options recommandées pour les développeurs (homebrew / ROM hacking)</Control>

View file

@ -308,6 +308,8 @@
<Control ID="lblTurboSpeed">Velocità avanti veloce:</Control>
<Control ID="lblRewindSpeedHint">% (0 = Velocità massima)</Control>
<Control ID="lblRewindSpeed">Velocità Riavvolgimento:</Control>
<Control ID="lblRunAhead">Run Ahead:</Control>
<Control ID="lblRunAheadFrames">frames (reduces input lag, increases CPU usage)</Control>
<Control ID="tpgAdvanced">Avanzato</Control>
<Control ID="lblDeveloperSettings">Opzioni raccomandate per sviluppatori (homebrew / ROM hacking)</Control>

View file

@ -307,6 +307,8 @@
<Control ID="lblTurboSpeed">早送りの速度:</Control>
<Control ID="lblRewindSpeedHint">% (0 = 最高速度)</Control>
<Control ID="lblRewindSpeed">巻き戻しの速度:</Control>
<Control ID="lblRunAhead">Run Ahead:</Control>
<Control ID="lblRunAheadFrames">フレーム (入力遅延低下)</Control>
<Control ID="tpgAdvanced">詳細設定</Control>
<Control ID="lblDeveloperSettings">開発者向け設定 (自作ソフト / ROM hacking)</Control>

View file

@ -308,6 +308,8 @@
<Control ID="lblTurboSpeed">Velocidade de avanço rápido:</Control>
<Control ID="lblRewindSpeedHint">% (0 = Velocidade máxima)</Control>
<Control ID="lblRewindSpeed">Velocidade de rebobinamento:</Control>
<Control ID="lblRunAhead">Run Ahead:</Control>
<Control ID="lblRunAheadFrames">frames (reduces input lag, increases CPU usage)</Control>
<Control ID="tpgAdvanced">Avançado</Control>
<Control ID="lblDeveloperSettings">Configurações recomendadas para programadores (homebrew / ROM hacking)</Control>

View file

@ -307,6 +307,8 @@
<Control ID="lblTurboSpeed">Перемотка:</Control>
<Control ID="lblRewindSpeedHint">% (0 = Максимальная скорость)</Control>
<Control ID="lblRewindSpeed">Скорость перемотки:</Control>
<Control ID="lblRunAhead">Run Ahead:</Control>
<Control ID="lblRunAheadFrames">frames (reduces input lag, increases CPU usage)</Control>
<Control ID="tpgAdvanced">Расширенные</Control>
<Control ID="lblDeveloperSettings">Рекомендованные настройки для разработчиков (homebrew / ROM hacking)</Control>

View file

@ -307,6 +307,8 @@
<Control ID="lblTurboSpeed">Перемотка:</Control>
<Control ID="lblRewindSpeedHint">% (0 = Максимальна швидкiсть)</Control>
<Control ID="lblRewindSpeed">Швидкість перемотування:</Control>
<Control ID="lblRunAhead">Run Ahead:</Control>
<Control ID="lblRunAheadFrames">frames (reduces input lag, increases CPU usage)</Control>
<Control ID="tpgAdvanced">Розширені</Control>
<Control ID="lblDeveloperSettings">Recommended settings for developers (homebrew / ROM hacking)</Control>

View file

@ -334,6 +334,8 @@
<Control ID="lblTurboSpeed">快进速度:</Control>
<Control ID="lblRewindSpeedHint">% (0 = 最快)</Control>
<Control ID="lblRewindSpeed">快退速度:</Control>
<Control ID="lblRunAhead">Run Ahead:</Control>
<Control ID="lblRunAheadFrames">frames (reduces input lag, increases CPU usage)</Control>
<Control ID="tpgAdvanced">高级</Control>
<Control ID="lblDeveloperSettings">开发者推荐设置 (自作/修改 ROM)</Control>

File diff suppressed because it is too large Load diff

View file

@ -27,6 +27,7 @@ namespace Mesen.GUI.Forms.Config
AddBinding("EmulationSpeed", nudEmulationSpeed);
AddBinding("TurboSpeed", nudTurboSpeed);
AddBinding("RewindSpeed", nudRewindSpeed);
AddBinding("RunAheadFrames", nudRunAheadFrames);
AddBinding("UseAlternativeMmc3Irq", chkUseAlternativeMmc3Irq);
AddBinding("AllowInvalidInput", chkAllowInvalidInput);

View file

@ -31,6 +31,7 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void HistoryViewerRelease();
[DllImport(DLLPath)] public static extern void HistoryViewerRun();
[DllImport(DLLPath)] public static extern void HistoryViewerStop();
[DllImport(DLLPath)] public static extern UInt32 HistoryViewerGetHistoryLength();
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool HistoryViewerSaveMovie([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string movieFile, UInt32 startPosition, UInt32 endPosition);
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool HistoryViewerCreateSaveState([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string outfileFile, UInt32 position);
@ -192,6 +193,7 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void SetSampleRate(UInt32 sampleRate);
[DllImport(DLLPath)] public static extern void SetAudioLatency(UInt32 msLatency);
[DllImport(DLLPath)] public static extern void SetAudioFilterSettings(AudioFilterSettings settings);
[DllImport(DLLPath)] public static extern void SetRunAheadFrames(UInt32 frameCount);
[DllImport(DLLPath)] public static extern NesModel GetNesModel();
[DllImport(DLLPath)] public static extern void SetNesModel(NesModel model);

View file

@ -627,6 +627,7 @@ namespace InteropEmu {
DllExport void __stdcall SetSampleRate(uint32_t sampleRate) { _settings->SetSampleRate(sampleRate); }
DllExport void __stdcall SetAudioLatency(uint32_t msLatency) { _settings->SetAudioLatency(msLatency); }
DllExport void __stdcall SetAudioFilterSettings(AudioFilterSettings settings) { _settings->SetAudioFilterSettings(settings); }
DllExport void __stdcall SetRunAheadFrames(uint32_t frameCount) { _settings->SetRunAheadFrames(frameCount); }
DllExport NesModel __stdcall GetNesModel() { return _console->GetModel(); }
DllExport void __stdcall SetNesModel(uint32_t model) { _settings->SetNesModel((NesModel)model); }