Input: Ensure input is always processed at the same moment on each frame (first cycle of NMI scanline)

Before this, DMA transfers could delay input processing for a long time (potentially multiple frames)
This commit is contained in:
Sour 2019-10-30 20:42:19 -04:00
parent faecb0b37f
commit 305ec6e446
8 changed files with 76 additions and 67 deletions

View file

@ -31,6 +31,7 @@
#include "BatteryManager.h" #include "BatteryManager.h"
#include "CheatManager.h" #include "CheatManager.h"
#include "MovieManager.h" #include "MovieManager.h"
#include "SystemActionManager.h"
#include "SpcHud.h" #include "SpcHud.h"
#include "../Utilities/Serializer.h" #include "../Utilities/Serializer.h"
#include "../Utilities/Timer.h" #include "../Utilities/Timer.h"
@ -96,14 +97,9 @@ void Console::Run()
auto emulationLock = _emulationLock.AcquireSafe(); auto emulationLock = _emulationLock.AcquireSafe();
auto lock = _runLock.AcquireSafe(); auto lock = _runLock.AcquireSafe();
DebugStats stats(this);
Timer lastFrameTimer;
_stopFlag = false;
uint32_t previousFrameCount = 0; uint32_t previousFrameCount = 0;
double frameDelay = GetFrameDelay(); _stopFlag = false;
FrameLimiter frameLimiter(frameDelay);
PlatformUtilities::EnableHighResolutionTimer(); PlatformUtilities::EnableHighResolutionTimer();
@ -113,15 +109,15 @@ void Console::Run()
_memoryManager->IncMasterClockStartup(); _memoryManager->IncMasterClockStartup();
_controlManager->UpdateInputState(); _controlManager->UpdateInputState();
_frameDelay = GetFrameDelay();
_stats.reset(new DebugStats());
_frameLimiter.reset(new FrameLimiter(_frameDelay));
_lastFrameTimer.Reset();
while(!_stopFlag) { while(!_stopFlag) {
_cpu->Exec(); _cpu->Exec();
if(previousFrameCount != _ppu->GetFrameCount()) { if(previousFrameCount != _ppu->GetFrameCount()) {
_cart->RunCoprocessors();
if(_cart->GetCoprocessor()) {
_cart->GetCoprocessor()->ProcessEndOfFrame();
}
_rewindManager->ProcessEndOfFrame(); _rewindManager->ProcessEndOfFrame();
WaitForLock(); WaitForLock();
@ -133,32 +129,14 @@ void Console::Run()
if(_paused && !_stopFlag && !_debugger) { if(_paused && !_stopFlag && !_debugger) {
WaitForPauseEnd(); WaitForPauseEnd();
if(_stopFlag) {
break;
}
} }
frameLimiter.ProcessFrame();
frameLimiter.WaitForNextFrame();
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(lastFrameTime);
}
_controlManager->UpdateInputState();
_controlManager->UpdateControlDevices();
_internalRegisters->ProcessAutoJoypadRead();
previousFrameCount = _ppu->GetFrameCount(); previousFrameCount = _ppu->GetFrameCount();
if(_controlManager->GetSystemActionManager()->IsResetPressed()) {
Reset();
} else if(_controlManager->GetSystemActionManager()->IsPowerCyclePressed()) {
PowerCycle();
}
} }
} }
@ -169,6 +147,36 @@ void Console::Run()
PlatformUtilities::RestoreTimerResolution(); PlatformUtilities::RestoreTimerResolution();
} }
void Console::ProcessEndOfFrame()
{
#ifndef LIBRETRO
_cart->RunCoprocessors();
if(_cart->GetCoprocessor()) {
_cart->GetCoprocessor()->ProcessEndOfFrame();
}
_frameLimiter->ProcessFrame();
_frameLimiter->WaitForNextFrame();
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);
}
_controlManager->UpdateInputState();
_controlManager->UpdateControlDevices();
_internalRegisters->ProcessAutoJoypadRead();
#endif
}
void Console::RunSingleFrame() void Console::RunSingleFrame()
{ {
//Used by Libretro //Used by Libretro

View file

@ -3,6 +3,7 @@
#include "CartTypes.h" #include "CartTypes.h"
#include "DebugTypes.h" #include "DebugTypes.h"
#include "ConsoleLock.h" #include "ConsoleLock.h"
#include "../Utilities/Timer.h"
#include "../Utilities/VirtualFile.h" #include "../Utilities/VirtualFile.h"
#include "../Utilities/SimpleLock.h" #include "../Utilities/SimpleLock.h"
@ -27,6 +28,9 @@ class BatteryManager;
class CheatManager; class CheatManager;
class MovieManager; class MovieManager;
class SpcHud; class SpcHud;
class FrameLimiter;
class DebugStats;
enum class MemoryOperationType; enum class MemoryOperationType;
enum class SnesMemoryType; enum class SnesMemoryType;
enum class EventType; enum class EventType;
@ -75,6 +79,11 @@ private:
ConsoleRegion _region; ConsoleRegion _region;
uint32_t _masterClockRate; uint32_t _masterClockRate;
unique_ptr<DebugStats> _stats;
unique_ptr<FrameLimiter> _frameLimiter;
Timer _lastFrameTimer;
double _frameDelay = 0;
double GetFrameDelay(); double GetFrameDelay();
void UpdateRegion(); void UpdateRegion();
void WaitForLock(); void WaitForLock();
@ -91,6 +100,8 @@ public:
void RunSingleFrame(); void RunSingleFrame();
void Stop(bool sendNotification); void Stop(bool sendNotification);
void ProcessEndOfFrame();
void Reset(); void Reset();
void PowerCycle(); void PowerCycle();

View file

@ -70,9 +70,9 @@ vector<ControllerData> ControlManager::GetPortStates()
return states; return states;
} }
shared_ptr<SystemActionManager> ControlManager::GetSystemActionManager() SystemActionManager* ControlManager::GetSystemActionManager()
{ {
return _systemActionManager; return _systemActionManager.get();
} }
shared_ptr<BaseControlDevice> ControlManager::GetControlDevice(uint8_t port) shared_ptr<BaseControlDevice> ControlManager::GetControlDevice(uint8_t port)
@ -134,7 +134,6 @@ void ControlManager::UpdateControlDevices()
} }
} }
} }
_systemActionManager->ProcessSystemActions();
} }
void ControlManager::UpdateInputState() void ControlManager::UpdateInputState()

View file

@ -51,7 +51,7 @@ public:
vector<ControllerData> GetPortStates(); vector<ControllerData> GetPortStates();
shared_ptr<SystemActionManager> GetSystemActionManager(); SystemActionManager* GetSystemActionManager();
shared_ptr<BaseControlDevice> GetControlDevice(uint8_t port); shared_ptr<BaseControlDevice> GetControlDevice(uint8_t port);
vector<shared_ptr<BaseControlDevice>> GetControlDevices(); vector<shared_ptr<BaseControlDevice>> GetControlDevices();

View file

@ -7,21 +7,16 @@
#include "DebugHud.h" #include "DebugHud.h"
#include "IAudioDevice.h" #include "IAudioDevice.h"
DebugStats::DebugStats(Console * console) void DebugStats::DisplayStats(Console *console, double lastFrameTime)
{ {
_console = console; AudioStatistics stats = console->GetSoundMixer()->GetStatistics();
} AudioConfig audioCfg = console->GetSettings()->GetAudioConfig();
shared_ptr<DebugHud> hud = console->GetDebugHud();
void DebugStats::DisplayStats(double lastFrameTime)
{
AudioStatistics stats = _console->GetSoundMixer()->GetStatistics();
AudioConfig audioCfg = _console->GetSettings()->GetAudioConfig();
shared_ptr<DebugHud> hud = _console->GetDebugHud();
_frameDurations[_frameDurationIndex] = lastFrameTime; _frameDurations[_frameDurationIndex] = lastFrameTime;
_frameDurationIndex = (_frameDurationIndex + 1) % 60; _frameDurationIndex = (_frameDurationIndex + 1) % 60;
int startFrame = _console->GetPpu()->GetFrameCount(); int startFrame = console->GetPpu()->GetFrameCount();
hud->DrawRectangle(8, 8, 115, 49, 0x40000000, true, 1, startFrame); hud->DrawRectangle(8, 8, 115, 49, 0x40000000, true, 1, startFrame);
hud->DrawRectangle(8, 8, 115, 49, 0xFFFFFF, false, 1, startFrame); hud->DrawRectangle(8, 8, 115, 49, 0xFFFFFF, false, 1, startFrame);
@ -36,7 +31,7 @@ void DebugStats::DisplayStats(double lastFrameTime)
hud->DrawString(10, 30, "Underruns: " + std::to_string(stats.BufferUnderrunEventCount), 0xFFFFFF, 0xFF000000, 1, startFrame); hud->DrawString(10, 30, "Underruns: " + std::to_string(stats.BufferUnderrunEventCount), 0xFFFFFF, 0xFF000000, 1, startFrame);
hud->DrawString(10, 39, "Buffer Size: " + std::to_string(stats.BufferSize / 1024) + "kb", 0xFFFFFF, 0xFF000000, 1, startFrame); hud->DrawString(10, 39, "Buffer Size: " + std::to_string(stats.BufferSize / 1024) + "kb", 0xFFFFFF, 0xFF000000, 1, startFrame);
hud->DrawString(10, 48, "Rate: " + std::to_string((uint32_t)(audioCfg.SampleRate * _console->GetSoundMixer()->GetRateAdjustment())) + "Hz", 0xFFFFFF, 0xFF000000, 1, startFrame); hud->DrawString(10, 48, "Rate: " + std::to_string((uint32_t)(audioCfg.SampleRate * console->GetSoundMixer()->GetRateAdjustment())) + "Hz", 0xFFFFFF, 0xFF000000, 1, startFrame);
hud->DrawRectangle(132, 8, 115, 49, 0x40000000, true, 1, startFrame); hud->DrawRectangle(132, 8, 115, 49, 0x40000000, true, 1, startFrame);
hud->DrawRectangle(132, 8, 115, 49, 0xFFFFFF, false, 1, startFrame); hud->DrawRectangle(132, 8, 115, 49, 0xFFFFFF, false, 1, startFrame);
@ -55,7 +50,7 @@ void DebugStats::DisplayStats(double lastFrameTime)
ss << "Last Frame: " << std::fixed << std::setprecision(2) << lastFrameTime << " ms"; ss << "Last Frame: " << std::fixed << std::setprecision(2) << lastFrameTime << " ms";
hud->DrawString(134, 30, ss.str(), 0xFFFFFF, 0xFF000000, 1, startFrame); hud->DrawString(134, 30, ss.str(), 0xFFFFFF, 0xFF000000, 1, startFrame);
if(_console->GetPpu()->GetFrameCount() > 60) { if(console->GetPpu()->GetFrameCount() > 60) {
_lastFrameMin = std::min(lastFrameTime, _lastFrameMin); _lastFrameMin = std::min(lastFrameTime, _lastFrameMin);
_lastFrameMax = std::max(lastFrameTime, _lastFrameMax); _lastFrameMax = std::max(lastFrameTime, _lastFrameMax);
} else { } else {

View file

@ -6,14 +6,11 @@ class Console;
class DebugStats class DebugStats
{ {
private: private:
Console *_console;
double _frameDurations[60] = {}; double _frameDurations[60] = {};
uint32_t _frameDurationIndex = 0; uint32_t _frameDurationIndex = 0;
double _lastFrameMin = 9999; double _lastFrameMin = 9999;
double _lastFrameMax = 0; double _lastFrameMax = 0;
public: public:
DebugStats(Console *console); void DisplayStats(Console *console, double lastFrameTime);
void DisplayStats(double lastFrameTime);
}; };

View file

@ -467,6 +467,8 @@ bool Ppu::ProcessEndOfScanline(uint16_t hClock)
_regs->SetNmiFlag(true); _regs->SetNmiFlag(true);
SendFrame(); SendFrame();
_console->ProcessEndOfFrame();
if(_regs->IsNmiEnabled()) { if(_regs->IsNmiEnabled()) {
_console->GetCpu()->SetNmiFlag(); _console->GetCpu()->SetNmiFlag();
} }

View file

@ -79,16 +79,13 @@ public:
return false; return false;
} }
void ProcessSystemActions() bool IsResetPressed()
{ {
if(_console) { return IsPressed(SystemActionManager::Buttons::ResetButton);
if(IsPressed(SystemActionManager::Buttons::ResetButton)) { }
_console->Reset();
} bool IsPowerCyclePressed()
if(IsPressed(SystemActionManager::Buttons::PowerButton)) { {
_console->PowerCycle(); return IsPressed(SystemActionManager::Buttons::PowerButton);
//Calling PowerCycle() causes this object to be deleted - no code must be written below this line
}
}
} }
}; };