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:
parent
faecb0b37f
commit
305ec6e446
8 changed files with 76 additions and 67 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -467,6 +467,8 @@ bool Ppu::ProcessEndOfScanline(uint16_t hClock)
|
|||
_regs->SetNmiFlag(true);
|
||||
SendFrame();
|
||||
|
||||
_console->ProcessEndOfFrame();
|
||||
|
||||
if(_regs->IsNmiEnabled()) {
|
||||
_console->GetCpu()->SetNmiFlag();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
Loading…
Add table
Reference in a new issue