From e0955cd1d4c4e6ef16b3c43cce96a79d8abbb75b Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 11 Mar 2023 23:07:29 +0100 Subject: [PATCH] make cross-instance pause work without breaking local wifi connections --- src/frontend/qt_sdl/IPC.cpp | 83 ++++++++++++++++++++++-------------- src/frontend/qt_sdl/IPC.h | 4 +- src/frontend/qt_sdl/main.cpp | 21 ++++----- src/frontend/qt_sdl/main.h | 6 ++- 4 files changed, 69 insertions(+), 45 deletions(-) diff --git a/src/frontend/qt_sdl/IPC.cpp b/src/frontend/qt_sdl/IPC.cpp index d216bc5e..fdc4683b 100644 --- a/src/frontend/qt_sdl/IPC.cpp +++ b/src/frontend/qt_sdl/IPC.cpp @@ -35,13 +35,10 @@ #include "IPC.h" #include "Config.h" -//#include "Input.h" +#include "main.h" -namespace Input -{ -void ExtHotkeyPress(int id); -} +extern EmuThread* emuThread; namespace IPC @@ -55,6 +52,7 @@ struct BufferHeader u16 NumInstances; // total number of instances present u16 InstanceBitmask; // bitmask of all instances present u16 ConnectedBitmask; // bitmask of which instances are ready to send/receive MP packets + u16 ActiveBitmask; // bitmask of which instances are currently active (ie. not halted externally) u32 CommandWriteOffset; u32 MPPacketWriteOffset; u32 MPReplyWriteOffset; @@ -94,8 +92,6 @@ const u32 kMPPacketEnd = (2 * (kBufferSize / 3)); const u32 kMPReplyStart = kMPPacketEnd; const u32 kMPReplyEnd = kBufferSize; -bool CmdRecvFlags[Cmd_MAX]; - int MPRecvTimeout; int MPLastHostID; @@ -294,8 +290,6 @@ bool Init() } Buffer->unlock(); - memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags)); - MPLastHostID = -1; MPRecvTimeout = 25; @@ -312,6 +306,8 @@ bool InitSema() SemPoolInit(); if (!SemInit(InstanceID)) return false; if (!SemInit(16+InstanceID)) return false; + + return true; } void DeInit() @@ -322,7 +318,8 @@ void DeInit() u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; header->ConnectedBitmask &= ~(1 << InstanceID); - header->InstanceBitmask &= ~(1<ActiveBitmask &= ~(1 << InstanceID); + header->InstanceBitmask &= ~(1 << InstanceID); header->NumInstances--; Buffer->unlock(); @@ -365,6 +362,15 @@ void MPEnd() Buffer->unlock(); } +void SetActive(bool active) +{ + Buffer->lock(); + BufferHeader* header = (BufferHeader*)Buffer->data(); + if (active) header->ActiveBitmask |= (1 << InstanceID); + else header->ActiveBitmask &= ~(1 << InstanceID); + Buffer->unlock(); +} + template void FIFORead(void* buf, int len) { @@ -454,8 +460,6 @@ template void FIFOWrite(void* buf, int len) void ProcessCommands() { - memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags)); - Buffer->lock(); u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; @@ -468,7 +472,9 @@ void ProcessCommands() FIFORead<0>(&cmdheader, sizeof(cmdheader)); - if ((cmdheader.Magic != 0x4D434C4D) || (cmdheader.Length > kMaxCommandSize)) + if ((cmdheader.Magic != 0x4D434C4D) || + (cmdheader.Length > kMaxCommandSize) || + (cmdheader.Command >= Cmd_MAX)) { printf("IPC: !!! COMMAND BUFFER IS FUCKED. RESETTING\n"); CommandReadOffset = header->CommandWriteOffset; @@ -476,25 +482,27 @@ void ProcessCommands() return; } + if (!(cmdheader.Recipients & (1<= kCommandEnd) + CommandReadOffset += kCommandStart - kCommandEnd; + + continue; + } + + // handle this command + if (cmdheader.Length) FIFORead<0>(cmddata, cmdheader.Length); - if (!(cmdheader.Recipients & (1<= Cmd_MAX) - continue; - - // handle this command - /*switch (cmdheader.Command) + switch (cmdheader.Command) { case Cmd_Pause: - Input::ExtHotkeyPress(HK_Pause); + emuThread->IPCPause(cmddata[0] != 0); break; - }*/ - CmdRecvFlags[cmdheader.Command] = true; - // TODO: store the command data, for future commands that will need it - // TODO: also what if, say, we get multiple pause commands before CommandReceived() is called? + } } Buffer->unlock(); @@ -544,10 +552,10 @@ bool SendCommand(u16 recipients, u16 command, u16 len, void* cmddata) return true; } -bool CommandReceived(u16 command) +bool SendCommandU8(u16 recipients, u16 command, u8 arg) { - if (command >= Cmd_MAX) return false; - return CmdRecvFlags[command]; + u8 data = arg; + return SendCommand(recipients, command, 1, &data); } @@ -689,6 +697,8 @@ int SendMPAck(u8* packet, int len, u64 timestamp) int RecvMPHostPacket(u8* packet, u64* timestamp) { + bool block = true; + if (MPLastHostID != -1) { // check if the host is still connected @@ -697,26 +707,32 @@ int RecvMPHostPacket(u8* packet, u64* timestamp) u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; u16 curinstmask = header->ConnectedBitmask; + u16 actinstmask = header->ActiveBitmask; Buffer->unlock(); if (!(curinstmask & (1 << MPLastHostID))) return -1; + + if (!(actinstmask & (1 << MPLastHostID))) + block = false; } - return RecvMPPacketGeneric(packet, true, timestamp); + return RecvMPPacketGeneric(packet, block, timestamp); } u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask) { u16 ret = 0; u16 myinstmask = (1 << InstanceID); - u16 curinstmask; + u16 curinstmask, actinstmask; + int timeout = MPRecvTimeout; { Buffer->lock(); u8* data = (u8*)Buffer->data(); BufferHeader* header = (BufferHeader*)&data[0]; curinstmask = header->ConnectedBitmask; + actinstmask = header->ActiveBitmask; Buffer->unlock(); } @@ -724,9 +740,12 @@ u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask) if ((myinstmask & curinstmask) == curinstmask) return 0; + if ((myinstmask & actinstmask) == actinstmask) + timeout = 0; + for (;;) { - if (!SemWait(16+InstanceID, MPRecvTimeout)) + if (!SemWait(16+InstanceID, timeout)) { // no more replies available return ret; diff --git a/src/frontend/qt_sdl/IPC.h b/src/frontend/qt_sdl/IPC.h index 18a1ed09..eca43837 100644 --- a/src/frontend/qt_sdl/IPC.h +++ b/src/frontend/qt_sdl/IPC.h @@ -42,9 +42,11 @@ void SetMPRecvTimeout(int timeout); void MPBegin(); void MPEnd(); +void SetActive(bool active); + void ProcessCommands(); bool SendCommand(u16 recipients, u16 command, u16 len, void* data); -bool CommandReceived(u16 command); +bool SendCommandU8(u16 recipients, u16 command, u8 arg); int SendMPPacket(u8* data, int len, u64 timestamp); int RecvMPPacket(u8* data, u64* timestamp); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 82961132..9182adaf 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -197,7 +197,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled())); - connect(this, SIGNAL(windowIPCPause()), mainWindow, SLOT(onIPCPause())); + connect(this, SIGNAL(windowIPCPause(bool)), mainWindow, SLOT(onIPCPause(bool))); static_cast(mainWindow->panel)->transferLayout(this); } @@ -363,8 +363,6 @@ void EmuThread::run() { IPC::ProcessCommands(); - if (IPC::CommandReceived(IPC::Cmd_Pause)) emit windowIPCPause(); - Input::Process(); if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); @@ -678,6 +676,7 @@ void EmuThread::emuRun() // checkme emit windowEmuStart(); AudioInOut::Enable(); + IPC::SetActive(true); } void EmuThread::initContext() @@ -702,6 +701,7 @@ void EmuThread::emuPause() while (EmuStatus != emuStatus_Paused); AudioInOut::Disable(); + IPC::SetActive(false); } void EmuThread::emuUnpause() @@ -714,6 +714,7 @@ void EmuThread::emuUnpause() EmuRunning = PrevEmuStatus; AudioInOut::Enable(); + IPC::SetActive(true); } void EmuThread::emuStop() @@ -722,12 +723,13 @@ void EmuThread::emuStop() EmuPauseStack = EmuPauseStackRunning; AudioInOut::Disable(); + IPC::SetActive(false); } void EmuThread::emuFrameStep() { - if (EmuPauseStack < EmuPauseStackPauseThreshold) emit windowEmuPause(); - EmuRunning = emuStatus_FrameStep; + if (EmuRunning != 3) IPC::SetActive(false); + EmuRunning = 3; } bool EmuThread::emuIsRunning() @@ -2714,19 +2716,18 @@ void MainWindow::onPause(bool checked) pausedManually = false; } - IPC::SendCommand(0xFFFF, IPC::Cmd_Pause, 0, nullptr); + IPC::SendCommandU8(0xFFFF, IPC::Cmd_Pause, (u8)checked); } -void MainWindow::onIPCPause() +void MainWindow::onIPCPause(bool pause) { // for IPC, using the normal way to trigger a pause (actPause->trigger()) // isn't viable, because it would lead to broadcasting more IPC 'pause' messages // so we have to replicate it this way - actPause->toggle(); // changes visual state, without triggering onPause() - bool checked = actPause->isChecked(); + actPause->setChecked(pause); // changes visual state, without triggering onPause() - if (checked) + if (pause) { emuThread->emuPause(); OSD::AddMessage(0, "Paused"); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index f1adaeec..107b5714 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -68,6 +68,8 @@ public: void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix); + void IPCPause(bool pause) { emit windowIPCPause(pause); } + signals: void windowUpdate(); void windowTitleChange(QString title); @@ -78,7 +80,7 @@ signals: void windowEmuReset(); void windowEmuFrameStep(); - void windowIPCPause(); + void windowIPCPause(bool pause); void windowLimitFPSChange(); @@ -298,7 +300,7 @@ private slots: void onQuit(); void onPause(bool checked); - void onIPCPause(); + void onIPCPause(bool pause); void onReset(); void onStop(); void onFrameStep();