Debugger: Allow multiple PPU viewer windows to refresh at different scanline/cycle timings

This commit is contained in:
Sour 2018-04-10 17:05:54 -04:00
parent 1809c5becc
commit 1e47897ca6
10 changed files with 88 additions and 56 deletions

View file

@ -435,6 +435,8 @@ void Console::Run()
UpdateNesModel(true); UpdateNesModel(true);
_running = true;
bool crashed = false; bool crashed = false;
try { try {
while(true) { while(true) {
@ -511,6 +513,10 @@ void Console::Run()
MessageManager::DisplayMessage("Error", "GameCrash", ex.what()); MessageManager::DisplayMessage("Error", "GameCrash", ex.what());
} }
_running = false;
MessageManager::SendNotification(ConsoleNotificationType::BeforeEmulationStop);
if(!crashed) { if(!crashed) {
SaveStateManager::SaveRecentGame(GetMapperInfo().RomName, _romFilepath, _patchFilename); SaveStateManager::SaveRecentGame(GetMapperInfo().RomName, _romFilepath, _patchFilename);
} }
@ -559,12 +565,12 @@ void Console::Run()
bool Console::IsRunning() bool Console::IsRunning()
{ {
return !Instance->_stopLock.IsFree(); return !Instance->_stopLock.IsFree() && Instance->_running;
} }
bool Console::IsPaused() bool Console::IsPaused()
{ {
return _runLock.IsFree() || !_pauseLock.IsFree(); return _runLock.IsFree() || !_pauseLock.IsFree() || !_running;
} }
void Console::UpdateNesModel(bool sendNotification) void Console::UpdateNesModel(bool sendNotification)

View file

@ -57,6 +57,7 @@ class Console
string _patchFilename; string _patchFilename;
bool _stop = false; bool _stop = false;
bool _running = false;
bool _disableOcNextFrame = false; bool _disableOcNextFrame = false;

View file

@ -79,9 +79,6 @@ Debugger::Debugger(shared_ptr<Console> console, shared_ptr<CPU> cpu, shared_ptr<
_ppuScrollX = 0; _ppuScrollX = 0;
_ppuScrollY = 0; _ppuScrollY = 0;
_ppuViewerScanline = 241;
_ppuViewerCycle = 0;
_flags = 0; _flags = 0;
_runToCycle = 0; _runToCycle = 0;
@ -104,6 +101,8 @@ Debugger::Debugger(shared_ptr<Console> console, shared_ptr<CPU> cpu, shared_ptr<
_hasScript = false; _hasScript = false;
_nextScriptId = 0; _nextScriptId = 0;
UpdatePpuCyclesToProcess();
Debugger::Instance = this; Debugger::Instance = this;
} }
@ -468,21 +467,27 @@ void Debugger::ProcessStepConditions(uint16_t addr)
void Debugger::PrivateProcessPpuCycle() void Debugger::PrivateProcessPpuCycle()
{ {
if(PPU::GetCurrentCycle() == (uint32_t)_ppuViewerCycle && PPU::GetCurrentScanline() == _ppuViewerScanline) { if(_proccessPpuCycle[PPU::GetCurrentCycle()]) {
MessageManager::SendNotification(ConsoleNotificationType::PpuViewerDisplayFrame); int32_t currentCycle = (PPU::GetCurrentCycle() << 9) + PPU::GetCurrentScanline();
} for(auto updateCycle : _ppuViewerUpdateCycle) {
if(PPU::GetCurrentCycle() == 0) { if(updateCycle.second == currentCycle) {
if(_breakOnScanline == PPU::GetCurrentScanline()) { MessageManager::SendNotification(ConsoleNotificationType::PpuViewerDisplayFrame, (void*)(uint64_t)updateCycle.first);
Step(1); }
SleepUntilResume(BreakSource::Pause);
} }
if(PPU::GetCurrentScanline() == 241) {
ProcessEvent(EventType::EndFrame); if(PPU::GetCurrentCycle() == 0) {
} else if(PPU::GetCurrentScanline() == -1) { if(_breakOnScanline == PPU::GetCurrentScanline()) {
ProcessEvent(EventType::StartFrame); Step(1);
SleepUntilResume(BreakSource::Pause);
}
if(PPU::GetCurrentScanline() == 241) {
ProcessEvent(EventType::EndFrame);
} else if(PPU::GetCurrentScanline() == -1) {
ProcessEvent(EventType::StartFrame);
}
} }
} }
OperationInfo operationInfo { 0, 0, MemoryOperationType::DummyRead }; OperationInfo operationInfo { 0, 0, MemoryOperationType::DummyRead };
if(_hasBreakpoint[BreakpointType::Global]) { if(_hasBreakpoint[BreakpointType::Global]) {
@ -1128,10 +1133,27 @@ void Debugger::GetPpuAbsoluteAddressAndType(uint32_t relativeAddr, PpuAddressTyp
info->Type = PpuAddressType::None; info->Type = PpuAddressType::None;
} }
void Debugger::SetPpuViewerScanlineCycle(int32_t scanline, int32_t cycle) void Debugger::UpdatePpuCyclesToProcess()
{ {
_ppuViewerScanline = scanline; memset(_proccessPpuCycle, 0, sizeof(_proccessPpuCycle));
_ppuViewerCycle = cycle; for(auto updateCycle : _ppuViewerUpdateCycle) {
_proccessPpuCycle[updateCycle.second] = true;
}
_proccessPpuCycle[0] = true;
}
void Debugger::SetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle)
{
DebugBreakHelper helper(this);
_ppuViewerUpdateCycle[ppuViewerId] = (cycle << 9) + scanline;
UpdatePpuCyclesToProcess();
}
void Debugger::ClearPpuViewerSettings(int32_t ppuViewer)
{
DebugBreakHelper helper(this);
_ppuViewerUpdateCycle.erase(ppuViewer);
UpdatePpuCyclesToProcess();
} }
void Debugger::SetLastFramePpuScroll(uint16_t addr, uint8_t xScroll, bool updateHorizontalScrollOnly) void Debugger::SetLastFramePpuScroll(uint16_t addr, uint8_t xScroll, bool updateHorizontalScrollOnly)

View file

@ -107,8 +107,8 @@ private:
atomic<int32_t> _breakOnScanline; atomic<int32_t> _breakOnScanline;
int32_t _ppuViewerScanline; bool _proccessPpuCycle[341];
int32_t _ppuViewerCycle; std::unordered_map<int, int> _ppuViewerUpdateCycle;
uint16_t _ppuScrollX; uint16_t _ppuScrollX;
uint16_t _ppuScrollY; uint16_t _ppuScrollY;
@ -142,6 +142,8 @@ private:
void AddDebugEvent(DebugEventType type, uint16_t address = -1, uint8_t value = 0, int16_t breakpointId = -1, int8_t ppuLatch = -1); void AddDebugEvent(DebugEventType type, uint16_t address = -1, uint8_t value = 0, int16_t breakpointId = -1, int8_t ppuLatch = -1);
void UpdatePpuCyclesToProcess();
public: public:
Debugger(shared_ptr<Console> console, shared_ptr<CPU> cpu, shared_ptr<PPU> ppu, shared_ptr<APU> apu, shared_ptr<MemoryManager> memoryManager, shared_ptr<BaseMapper> mapper); Debugger(shared_ptr<Console> console, shared_ptr<CPU> cpu, shared_ptr<PPU> ppu, shared_ptr<APU> apu, shared_ptr<MemoryManager> memoryManager, shared_ptr<BaseMapper> mapper);
~Debugger(); ~Debugger();
@ -186,7 +188,9 @@ public:
shared_ptr<CodeDataLogger> GetCodeDataLogger(); shared_ptr<CodeDataLogger> GetCodeDataLogger();
void SetNextStatement(uint16_t addr); void SetNextStatement(uint16_t addr);
void SetPpuViewerScanlineCycle(int32_t scanline, int32_t cycle);
void SetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle);
void ClearPpuViewerSettings(int32_t ppuViewer);
bool IsExecutionStopped(); bool IsExecutionStopped();

View file

@ -22,6 +22,7 @@ enum class ConsoleNotificationType
ExecuteShortcut = 16, ExecuteShortcut = 16,
EmulationStopped = 17, EmulationStopped = 17,
EventViewerDisplayFrame = 18, EventViewerDisplayFrame = 18,
BeforeEmulationStop = 19,
}; };
class INotificationListener class INotificationListener

View file

@ -711,24 +711,7 @@ void MessageManager::UnregisterNotificationListener(INotificationListener* notif
void MessageManager::SendNotification(ConsoleNotificationType type, void* parameter) void MessageManager::SendNotification(ConsoleNotificationType type, void* parameter)
{ {
auto lock = _notificationLock.AcquireSafe(); auto lock = _notificationLock.AcquireSafe();
for(INotificationListener *notificationListener : MessageManager::_notificationListeners) {
//Iterate on a copy to prevent issues if a notification causes a listener to unregister itself notificationListener->ProcessNotification(type, parameter);
vector<INotificationListener*> listeners = MessageManager::_notificationListeners;
vector<INotificationListener*> processedListeners;
for(size_t i = 0, len = listeners.size(); i < len; i++) {
INotificationListener* notificationListener = listeners[i];
if(std::find(processedListeners.begin(), processedListeners.end(), notificationListener) == processedListeners.end()) {
//Only send notification if it hasn't been processed already
notificationListener->ProcessNotification(type, parameter);
}
processedListeners.push_back(notificationListener);
if(len != MessageManager::_notificationListeners.size()) {
//Vector changed, start from the beginning again (can occur when sending a notification caused listeners to unregister themselves)
i = 0;
len = MessageManager::_notificationListeners.size();
listeners = MessageManager::_notificationListeners;
}
} }
} }

View file

@ -21,10 +21,16 @@ namespace Mesen.GUI.Debugger
private TabPage _selectedTab; private TabPage _selectedTab;
private bool _refreshing = false; private bool _refreshing = false;
private static int _nextPpuViewerId = 0;
private int _ppuViewerId = 0;
public frmPpuViewer() public frmPpuViewer()
{ {
InitializeComponent(); InitializeComponent();
_ppuViewerId = _nextPpuViewerId;
_nextPpuViewerId++;
this._selectedTab = this.tpgNametableViewer; this._selectedTab = this.tpgNametableViewer;
this.mnuAutoRefresh.Checked = ConfigManager.Config.DebugInfo.PpuAutoRefresh; this.mnuAutoRefresh.Checked = ConfigManager.Config.DebugInfo.PpuAutoRefresh;
this.mnuRefreshOnBreak.Checked = ConfigManager.Config.DebugInfo.PpuRefreshOnBreak; this.mnuRefreshOnBreak.Checked = ConfigManager.Config.DebugInfo.PpuRefreshOnBreak;
@ -49,7 +55,7 @@ namespace Mesen.GUI.Debugger
this.nudScanline.Value = ConfigManager.Config.DebugInfo.PpuDisplayScanline; this.nudScanline.Value = ConfigManager.Config.DebugInfo.PpuDisplayScanline;
this.nudCycle.Value = ConfigManager.Config.DebugInfo.PpuDisplayCycle; this.nudCycle.Value = ConfigManager.Config.DebugInfo.PpuDisplayCycle;
InteropEmu.DebugSetPpuViewerScanlineCycle((int)this.nudScanline.Value, (int)this.nudCycle.Value); InteropEmu.DebugSetPpuViewerScanlineCycle(_ppuViewerId, (int)this.nudScanline.Value, (int)this.nudCycle.Value);
this.ctrlNametableViewer.GetData(); this.ctrlNametableViewer.GetData();
this.ctrlChrViewer.GetData(); this.ctrlChrViewer.GetData();
@ -67,6 +73,7 @@ namespace Mesen.GUI.Debugger
{ {
base.OnFormClosing(e); base.OnFormClosing(e);
this._notifListener.OnNotification -= this._notifListener_OnNotification; this._notifListener.OnNotification -= this._notifListener_OnNotification;
InteropEmu.DebugClearPpuViewerSettings(_ppuViewerId);
} }
private void _notifListener_OnNotification(InteropEmu.NotificationEventArgs e) private void _notifListener_OnNotification(InteropEmu.NotificationEventArgs e)
@ -81,17 +88,19 @@ namespace Mesen.GUI.Debugger
break; break;
case InteropEmu.ConsoleNotificationType.PpuViewerDisplayFrame: case InteropEmu.ConsoleNotificationType.PpuViewerDisplayFrame:
if(ConfigManager.Config.DebugInfo.PpuAutoRefresh && !_refreshing && (DateTime.Now - _lastUpdate).Milliseconds > 66) { if(e.Parameter.ToInt32() == _ppuViewerId) {
//Update at 15 fps most if(ConfigManager.Config.DebugInfo.PpuAutoRefresh && !_refreshing && (DateTime.Now - _lastUpdate).Milliseconds > 66) {
this.GetData(); //Update at 15 fps most
this.BeginInvoke((MethodInvoker)(() => this.RefreshViewers())); this.GetData();
_lastUpdate = DateTime.Now; this.BeginInvoke((MethodInvoker)(() => this.RefreshViewers()));
_lastUpdate = DateTime.Now;
}
} }
break; break;
case InteropEmu.ConsoleNotificationType.GameLoaded: case InteropEmu.ConsoleNotificationType.GameLoaded:
//Configuration is lost when debugger is restarted (when switching game or power cycling) //Configuration is lost when debugger is restarted (when switching game or power cycling)
InteropEmu.DebugSetPpuViewerScanlineCycle(ConfigManager.Config.DebugInfo.PpuDisplayScanline, ConfigManager.Config.DebugInfo.PpuDisplayCycle); InteropEmu.DebugSetPpuViewerScanlineCycle(_ppuViewerId, ConfigManager.Config.DebugInfo.PpuDisplayScanline, ConfigManager.Config.DebugInfo.PpuDisplayCycle);
break; break;
} }
} }
@ -152,7 +161,7 @@ namespace Mesen.GUI.Debugger
scanline = Math.Min(260, Math.Max(-1, scanline)); scanline = Math.Min(260, Math.Max(-1, scanline));
cycle = Math.Min(340, Math.Max(0, cycle)); cycle = Math.Min(340, Math.Max(0, cycle));
InteropEmu.DebugSetPpuViewerScanlineCycle(scanline, cycle); InteropEmu.DebugSetPpuViewerScanlineCycle(_ppuViewerId, scanline, cycle);
ConfigManager.Config.DebugInfo.PpuDisplayScanline = scanline; ConfigManager.Config.DebugInfo.PpuDisplayScanline = scanline;
ConfigManager.Config.DebugInfo.PpuDisplayCycle = cycle; ConfigManager.Config.DebugInfo.PpuDisplayCycle = cycle;
ConfigManager.ApplyChanges(); ConfigManager.ApplyChanges();

View file

@ -608,8 +608,8 @@ namespace Mesen.GUI.Forms
})); }));
break; break;
case InteropEmu.ConsoleNotificationType.EmulationStopped: case InteropEmu.ConsoleNotificationType.BeforeEmulationStop:
this.BeginInvoke((Action)(() => { this.Invoke((Action)(() => {
DebugWindowManager.CloseAll(); DebugWindowManager.CloseAll();
})); }));
break; break;
@ -777,7 +777,7 @@ namespace Mesen.GUI.Forms
case EmulatorShortcut.Pause: PauseEmu(); break; case EmulatorShortcut.Pause: PauseEmu(); break;
case EmulatorShortcut.Reset: this.ResetEmu(); break; case EmulatorShortcut.Reset: this.ResetEmu(); break;
case EmulatorShortcut.PowerCycle: this.PowerCycleEmu(); break; case EmulatorShortcut.PowerCycle: this.PowerCycleEmu(); break;
case EmulatorShortcut.PowerOff: InteropEmu.Stop(); break; case EmulatorShortcut.PowerOff: Task.Run(() => InteropEmu.Stop()); break;
case EmulatorShortcut.Exit: this.Close(); break; case EmulatorShortcut.Exit: this.Close(); break;
case EmulatorShortcut.ToggleCheats: ToggleCheats(); break; case EmulatorShortcut.ToggleCheats: ToggleCheats(); break;

View file

@ -253,7 +253,8 @@ namespace Mesen.GUI
} }
[DllImport(DLLPath)] public static extern void DebugGetAbsoluteAddressAndType(UInt32 relativeAddr, ref AddressTypeInfo addressTypeInfo); [DllImport(DLLPath)] public static extern void DebugGetAbsoluteAddressAndType(UInt32 relativeAddr, ref AddressTypeInfo addressTypeInfo);
[DllImport(DLLPath)] public static extern void DebugSetPpuViewerScanlineCycle(Int32 scanline, Int32 cycle); [DllImport(DLLPath)] public static extern void DebugSetPpuViewerScanlineCycle(Int32 ppuViewerId, Int32 scanline, Int32 cycle);
[DllImport(DLLPath)] public static extern void DebugClearPpuViewerSettings(Int32 ppuViewerId);
[DllImport(DLLPath)] public static extern void DebugSetFreezeState(UInt16 address, [MarshalAs(UnmanagedType.I1)]bool frozen); [DllImport(DLLPath)] public static extern void DebugSetFreezeState(UInt16 address, [MarshalAs(UnmanagedType.I1)]bool frozen);
@ -876,6 +877,7 @@ namespace Mesen.GUI
ExecuteShortcut = 16, ExecuteShortcut = 16,
EmulationStopped = 17, EmulationStopped = 17,
EventViewerDisplayFrame = 18, EventViewerDisplayFrame = 18,
BeforeEmulationStop = 19,
} }
public enum ControllerType public enum ControllerType
@ -1049,7 +1051,10 @@ namespace Mesen.GUI
public void Dispose() public void Dispose()
{ {
InteropEmu.UnregisterNotificationCallback(_notificationListener); Task.Run(() => {
//Unregister the callback in another thread, to prevent deadlocks
InteropEmu.UnregisterNotificationCallback(_notificationListener);
});
} }
public void ProcessNotification(int type, IntPtr parameter) public void ProcessNotification(int type, IntPtr parameter)

View file

@ -51,7 +51,8 @@ extern "C"
DllExport void __stdcall DebugBreakOnScanline(int32_t scanline) { GetDebugger()->BreakOnScanline(scanline); } DllExport void __stdcall DebugBreakOnScanline(int32_t scanline) { GetDebugger()->BreakOnScanline(scanline); }
DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); } DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); }
DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(scanline, cycle); } DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(ppuViewerId, scanline, cycle); }
DllExport void __stdcall DebugClearPpuViewerSettings(int32_t ppuViewerId) { return GetDebugger()->ClearPpuViewerSettings(ppuViewerId); }
DllExport void __stdcall DebugSetNextStatement(uint16_t addr) { GetDebugger()->SetNextStatement(addr); } DllExport void __stdcall DebugSetNextStatement(uint16_t addr) { GetDebugger()->SetNextStatement(addr); }
DllExport void __stdcall DebugSetMemoryState(DebugMemoryType type, uint8_t *buffer) { GetDebugger()->GetMemoryDumper()->SetMemoryState(type, buffer); } DllExport void __stdcall DebugSetMemoryState(DebugMemoryType type, uint8_t *buffer) { GetDebugger()->GetMemoryDumper()->SetMemoryState(type, buffer); }