MTVU: Allow stacking of LABEL

This commit is contained in:
TellowKrinkle 2020-12-28 00:19:02 -06:00 committed by tellowkrinkle
parent 3c2f7983a5
commit 4ffc0d2247
2 changed files with 24 additions and 19 deletions

View File

@ -142,9 +142,19 @@ bool Gif_HandlerAD_MTVU(u8* pMem)
else if (reg == 0x62)
{ // LABEL
GUNIT_WARN("GIF Handler - LABEL");
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);
// It's okay to coalesce label updates
u32 labelData = data[0];
u32 labelMsk = data[1];
u64 existing = 0;
u64 wanted = ((u64)labelMsk << 32) | labelData;
while (!vu1Thread.gsLabel.compare_exchange_weak(existing, wanted, std::memory_order_relaxed))
{
u32 existingData = (u32)existing;
u32 existingMsk = (u32)(existing >> 32);
u32 wantedData = (existingData & ~labelMsk) | (labelData & labelMsk);
u32 wantedMsk = existingMsk | labelMsk;
wanted = ((u64)wantedMsk << 32) | wantedData;
}
vu1Thread.gsInterrupts.fetch_or(VU_Thread::InterruptFlagLabel, std::memory_order_release);
}
else if (reg >= 0x63 && reg != 0x7f)

View File

@ -336,25 +336,14 @@ void VU_Thread::Get_GSChanges()
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 (interrupts & InterruptFlagSignal)
{
std::atomic_thread_fence(std::memory_order_acquire);
const u64 signal = gsSignal.load(std::memory_order_relaxed);
// If load of signal was moved after clearing the flag, the other thread could write a new value before we load without noticing the double signal
// Prevent that with release semantics
gsInterrupts.fetch_and(~InterruptFlagSignal, std::memory_order_release);
GUNIT_WARN("SIGNAL firing");
const u32 signalMsk = (u32)(signal >> 32);
const u32 signalData = (u32)signal;
@ -377,6 +366,7 @@ void VU_Thread::Get_GSChanges()
}
if (interrupts & InterruptFlagFinish)
{
gsInterrupts.fetch_and(~InterruptFlagFinish, std::memory_order_relaxed);
GUNIT_WARN("Finish firing");
CSRreg.FINISH = true;
gifUnit.gsFINISH.gsFINISHFired = false;
@ -386,6 +376,11 @@ void VU_Thread::Get_GSChanges()
}
if (interrupts & InterruptFlagLabel)
{
gsInterrupts.fetch_and(~InterruptFlagLabel, std::memory_order_acquire);
// If other thread updates gsLabel for a second interrupt, that's okay. Worst case we think there's a label interrupt but gsLabel is 0
// We do not want the exchange of gsLabel to move ahead of clearing the flag, or the other thread could add more work before we clear the flag, resulting in an update with the flag unset
// acquire semantics should supply that guarantee
const u64 label = gsLabel.exchange(0, std::memory_order_relaxed);
GUNIT_WARN("LABEL firing");
const u32 labelMsk = (u32)(label >> 32);
const u32 labelData = (u32)label;