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

View file

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

View file

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

View file

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

View file

@ -7,21 +7,16 @@
#include "DebugHud.h"
#include "IAudioDevice.h"
DebugStats::DebugStats(Console * console)
void DebugStats::DisplayStats(Console *console, double lastFrameTime)
{
_console = console;
}
void DebugStats::DisplayStats(double lastFrameTime)
{
AudioStatistics stats = _console->GetSoundMixer()->GetStatistics();
AudioConfig audioCfg = _console->GetSettings()->GetAudioConfig();
shared_ptr<DebugHud> hud = _console->GetDebugHud();
AudioStatistics stats = console->GetSoundMixer()->GetStatistics();
AudioConfig audioCfg = console->GetSettings()->GetAudioConfig();
shared_ptr<DebugHud> hud = console->GetDebugHud();
_frameDurations[_frameDurationIndex] = lastFrameTime;
_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, 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, 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, 0xFFFFFF, false, 1, startFrame);
@ -55,7 +50,7 @@ void DebugStats::DisplayStats(double lastFrameTime)
ss << "Last Frame: " << std::fixed << std::setprecision(2) << lastFrameTime << " ms";
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);
_lastFrameMax = std::max(lastFrameTime, _lastFrameMax);
} else {

View file

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

View file

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

View file

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