diff --git a/pcsx2/Gif_Unit.cpp b/pcsx2/Gif_Unit.cpp index 819c99caa8..35642861e2 100644 --- a/pcsx2/Gif_Unit.cpp +++ b/pcsx2/Gif_Unit.cpp @@ -105,6 +105,7 @@ bool Gif_HandlerAD(u8* pMem) bool Gif_HandlerAD_MTVU(u8* pMem) { + // Note: Atomic communication is with MTVU.cpp Get_GSChanges u32 reg = pMem[8]; u32* data = (u32*)pMem; @@ -125,29 +126,26 @@ bool Gif_HandlerAD_MTVU(u8* pMem) } else if (reg == 0x60) { // SIGNAL - - if (CSRreg.SIGNAL) - { // Time to ignore all subsequent drawing operations. + GUNIT_WARN("GIF Handler - SIGNAL"); + if (vu1Thread.gsInterrupts.load(std::memory_order_acquire) & VU_Thread::InterruptFlagSignal) Console.Error("GIF Handler MTVU - Double SIGNAL Not Handled"); - return 1; - } - else - { - GUNIT_WARN("GIF Handler - SIGNAL"); - vu1Thread.gsSignal = ((u64)data[1] << 32) | data[0]; - vu1Thread.gsSignalCnt++; - } + vu1Thread.gsSignal.store(((u64)data[1] << 32) | data[0], std::memory_order_relaxed); + vu1Thread.gsInterrupts.fetch_or(VU_Thread::InterruptFlagSignal, std::memory_order_release); } else if (reg == 0x61) { // FINISH GUNIT_WARN("GIF Handler - FINISH"); - vu1Thread.gsFinish = 1; + u32 old = vu1Thread.gsInterrupts.fetch_or(VU_Thread::InterruptFlagFinish, std::memory_order_relaxed); + if (old & VU_Thread::InterruptFlagFinish) + Console.Error("GIF Handler MTVU - Double FINISH Not Handled"); } else if (reg == 0x62) { // LABEL GUNIT_WARN("GIF Handler - LABEL"); - vu1Thread.gsLabel = ((u64)data[1] << 32) | data[0]; - vu1Thread.gsLabelCnt++; + if (vu1Thread.gsLabel.load(std::memory_order_acquire) & VU_Thread::InterruptFlagLabel) + Console.Error("GIF Handler MTVU - Double LABEL Not Handled"); + vu1Thread.gsLabel.store(((u64)data[1] << 32) | data[0], std::memory_order_relaxed); + vu1Thread.gsInterrupts.fetch_or(VU_Thread::InterruptFlagLabel, std::memory_order_release); } else if (reg >= 0x63 && reg != 0x7f) { diff --git a/pcsx2/MTVU.cpp b/pcsx2/MTVU.cpp index 6011e62415..40ea1641b4 100644 --- a/pcsx2/MTVU.cpp +++ b/pcsx2/MTVU.cpp @@ -62,29 +62,18 @@ void SaveStateBase::mtvuFreeze() unsigned int v = vu1Thread.vuCycles[i].load(); Freeze(v); } - u32 gsFinishInt; - u64 gsSignals; - u32 gsSignalsCnt; - gsFinishInt = vu1Thread.gsFinish.load(); - Freeze(gsFinishInt); - vu1Thread.gsFinish.store(gsFinishInt); - gsSignals = vu1Thread.gsSignal.load(); - Freeze(gsSignals); - vu1Thread.gsSignal.store(gsSignals); - gsSignalsCnt = vu1Thread.gsSignalCnt.load(); - Freeze(gsSignalsCnt); - vu1Thread.gsSignalCnt.store(gsSignalsCnt); - gsSignals = vu1Thread.gsLabel.load(); - Freeze(gsSignals); - vu1Thread.gsLabel.store(gsSignals); - gsSignalsCnt = vu1Thread.gsLabelCnt.load(); - Freeze(gsSignalsCnt); - vu1Thread.gsLabelCnt.store(gsSignalsCnt); + u32 gsInterrupts = vu1Thread.gsInterrupts.load(); + Freeze(gsInterrupts); + vu1Thread.gsInterrupts.store(gsInterrupts); + u64 gsSignal = vu1Thread.gsSignal.load(); + Freeze(gsSignal); + vu1Thread.gsSignal.store(gsSignal); + u64 gsLabel = vu1Thread.gsLabel.load(); + Freeze(gsLabel); + vu1Thread.gsLabel.store(gsLabel); Freeze(vu1Thread.vuCycleIdx); - Freeze(vu1Thread.lastLabel); - Freeze(vu1Thread.lastSignal); } VU_Thread::VU_Thread(BaseVUmicroCPU*& _vuCPU, VURegs& _vuRegs) @@ -109,8 +98,6 @@ void VU_Thread::Reset() ScopedLock lock(mtxBusy); vuCycleIdx = 0; - lastLabel = 0; - lastSignal = 0; isBusy = false; m_ato_write_pos = 0; m_write_pos = 0; @@ -120,8 +107,7 @@ void VU_Thread::Reset() memzero(vifRegs); for (size_t i = 0; i < 4; ++i) vu1Thread.vuCycles[i] = 0; - vu1Thread.gsSignal = 0; - vu1Thread.gsLabel = 0; + vu1Thread.gsInterrupts = 0; } void VU_Thread::ExecuteTaskInThread() @@ -346,17 +332,32 @@ u32 VU_Thread::Get_vuCycles() void VU_Thread::Get_GSChanges() { - u32 finishInt = gsFinish.load(std::memory_order_acquire); - u64 signalCnt = gsSignalCnt.load(std::memory_order_acquire); - u64 labelCnt = gsLabelCnt.load(std::memory_order_acquire); + // Note: Atomic communication is with Gif_Unit.cpp Gif_HandlerAD_MTVU + u32 interrupts = gsInterrupts.load(std::memory_order_relaxed); + if (!interrupts) + return; + + // We don't support stacking multiple of the same type of interrupt, so the faster we read the required data and clear the flag the less likely we'll run into issues with that + u64 signal, label; + if (interrupts & (InterruptFlagSignal | InterruptFlagLabel)) + { + // label and signal access other variables so the load must have acquire semantics + std::atomic_thread_fence(std::memory_order_acquire); + signal = gsSignal.load(std::memory_order_relaxed); + label = gsLabel.load(std::memory_order_relaxed); + // Also need release semantics on the clear + gsInterrupts.fetch_and(~interrupts, std::memory_order_release); + } + else + { + gsInterrupts.fetch_and(~interrupts, std::memory_order_relaxed); + } - if (signalCnt != lastSignal) + if (interrupts & InterruptFlagSignal) { GUNIT_WARN("SIGNAL firing"); - const u64 signal = gsSignal.load(std::memory_order_acquire); const u32 signalMsk = (u32)(signal >> 32); const u32 signalData = (u32)signal; - lastSignal = signalCnt; if (CSRreg.SIGNAL) { GUNIT_WARN("Queue SIGNAL"); @@ -374,7 +375,7 @@ void VU_Thread::Get_GSChanges() gsIrq(); } } - if (finishInt) + if (interrupts & InterruptFlagFinish) { GUNIT_WARN("Finish firing"); CSRreg.FINISH = true; @@ -382,16 +383,12 @@ void VU_Thread::Get_GSChanges() if (!gifRegs.stat.APATH) Gif_FinishIRQ(); - - gsFinish.store(0); } - if (labelCnt != lastLabel) + if (interrupts & InterruptFlagLabel) { GUNIT_WARN("LABEL firing"); - const u64 label = gsLabel.load(std::memory_order_acquire); const u32 labelMsk = (u32)(label >> 32); const u32 labelData = (u32)label; - lastLabel = labelCnt; GSSIGLBLID.LBLID = (GSSIGLBLID.LBLID & ~labelMsk) | (labelData & labelMsk); } } diff --git a/pcsx2/MTVU.h b/pcsx2/MTVU.h index 2d88bee571..380bdde2f6 100644 --- a/pcsx2/MTVU.h +++ b/pcsx2/MTVU.h @@ -44,16 +44,19 @@ class VU_Thread : public pxThread { public: __aligned16 vifStruct vif; __aligned16 VIFregisters vifRegs; - __aligned(4) Semaphore semaXGkick; - __aligned(4) std::atomic vuCycles[4]; // Used for VU cycle stealing hack - __aligned(4) u32 vuCycleIdx; // Used for VU cycle stealing hack - __aligned(4) u32 lastSignal; - __aligned(4) u32 lastLabel; - __aligned(4) std::atomic gsFinish; // Used for GS Signal, Finish etc - __aligned(4) std::atomic gsLabelCnt; // Used for GS Label command - __aligned(4) std::atomic gsSignalCnt; // Used for GS Signal command - __aligned(4) std::atomic gsLabel; // Used for GS Label command - __aligned(4) std::atomic gsSignal; // Used for GS Signal command + Semaphore semaXGkick; + std::atomic vuCycles[4]; // Used for VU cycle stealing hack + u32 vuCycleIdx; // Used for VU cycle stealing hack + + enum InterruptFlag { + InterruptFlagFinish = 1 << 0, + InterruptFlagSignal = 1 << 1, + InterruptFlagLabel = 1 << 2, + }; + + std::atomic gsInterrupts; // Used for GS Signal, Finish etc + std::atomic gsLabel; // Used for GS Label command + std::atomic gsSignal; // Used for GS Signal command VU_Thread(BaseVUmicroCPU*& _vuCPU, VURegs& _vuRegs); virtual ~VU_Thread(); diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index d76527e759..609d5d25f9 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -24,7 +24,7 @@ // the lower 16 bit value. IF the change is breaking of all compatibility with old // states, increment the upper 16 bit value, and clear the lower 16 bits to 0. -static const u32 g_SaveVersion = (0x9A14 << 16) | 0x0000; +static const u32 g_SaveVersion = (0x9A15 << 16) | 0x0000; // this function is meant to be used in the place of GSfreeze, and provides a safe layer // between the GS saving function and the MTGS's needs. :)