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

View file

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

View file

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

View file

@ -107,8 +107,8 @@ private:
atomic<int32_t> _breakOnScanline;
int32_t _ppuViewerScanline;
int32_t _ppuViewerCycle;
bool _proccessPpuCycle[341];
std::unordered_map<int, int> _ppuViewerUpdateCycle;
uint16_t _ppuScrollX;
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 UpdatePpuCyclesToProcess();
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();
@ -186,7 +188,9 @@ public:
shared_ptr<CodeDataLogger> GetCodeDataLogger();
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();

View file

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

View file

@ -711,24 +711,7 @@ void MessageManager::UnregisterNotificationListener(INotificationListener* notif
void MessageManager::SendNotification(ConsoleNotificationType type, void* parameter)
{
auto lock = _notificationLock.AcquireSafe();
//Iterate on a copy to prevent issues if a notification causes a listener to unregister itself
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;
}
for(INotificationListener *notificationListener : MessageManager::_notificationListeners) {
notificationListener->ProcessNotification(type, parameter);
}
}

View file

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

View file

@ -608,8 +608,8 @@ namespace Mesen.GUI.Forms
}));
break;
case InteropEmu.ConsoleNotificationType.EmulationStopped:
this.BeginInvoke((Action)(() => {
case InteropEmu.ConsoleNotificationType.BeforeEmulationStop:
this.Invoke((Action)(() => {
DebugWindowManager.CloseAll();
}));
break;
@ -777,7 +777,7 @@ namespace Mesen.GUI.Forms
case EmulatorShortcut.Pause: PauseEmu(); break;
case EmulatorShortcut.Reset: this.ResetEmu(); 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.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 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);
@ -876,6 +877,7 @@ namespace Mesen.GUI
ExecuteShortcut = 16,
EmulationStopped = 17,
EventViewerDisplayFrame = 18,
BeforeEmulationStop = 19,
}
public enum ControllerType
@ -1049,7 +1051,10 @@ namespace Mesen.GUI
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)

View file

@ -51,7 +51,8 @@ extern "C"
DllExport void __stdcall DebugBreakOnScanline(int32_t scanline) { GetDebugger()->BreakOnScanline(scanline); }
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 DebugSetMemoryState(DebugMemoryType type, uint8_t *buffer) { GetDebugger()->GetMemoryDumper()->SetMemoryState(type, buffer); }