diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 568c04376d..2d82278336 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1248,15 +1248,79 @@ void spu_thread::push_snr(u32 number, u32 value) // Get channel const auto channel = number & 1 ? &ch_snr2 : &ch_snr1; - // Check corresponding SNR register settings - if ((snr_config >> number) & 1) + // Prepare some data + const u32 event_bit = SPU_EVENT_S1 >> (number & 1); + const u32 bitor_bit = (snr_config >> number) & 1; + + if (g_use_rtm) { - channel->push_or(value); + bool channel_notify = false; + bool thread_notify = false; + + const bool ok = utils::tx_start([&] + { + channel_notify = (channel->data.raw() & spu_channel::bit_wait) != 0; + thread_notify = (channel->data.raw() & spu_channel::bit_count) == 0; + + if (bitor_bit) + { + channel->data.raw() &= ~spu_channel::bit_wait; + channel->data.raw() |= spu_channel::bit_count | value; + } + else + { + channel->data.raw() = spu_channel::bit_count | value; + } + + if (thread_notify) + { + ch_events.raw().events |= event_bit; + + if (ch_events.raw().mask & event_bit) + { + ch_events.raw().count = 1; + thread_notify = ch_events.raw().waiting != 0; + } + else + { + thread_notify = false; + } + } + }); + + if (ok) + { + if (channel_notify) + channel->data.notify_one(); + if (thread_notify) + this->notify(); + + return; + } + } + + // Lock event channel in case it needs event notification + ch_events.atomic_op([](ch_events_t& ev) + { + ev.locks++; + }); + + // Check corresponding SNR register settings + if (bitor_bit) + { + if (channel->push_or(value)) + set_events(event_bit); } else { - channel->push(value); + if (channel->push(value)) + set_events(event_bit); } + + ch_events.atomic_op([](ch_events_t& ev) + { + ev.locks--; + }); } void spu_thread::do_dma_transfer(const spu_mfc_cmd& args) @@ -1389,7 +1453,7 @@ void spu_thread::do_dma_transfer(const spu_mfc_cmd& args) cpu->set_events(SPU_EVENT_LR); cpu->raddr = 0; } - + switch (size0) { case 1: @@ -2465,6 +2529,7 @@ spu_thread::ch_events_t spu_thread::get_events(u32 mask_hint, bool waiting, bool fmt::throw_exception("SPU Events not implemented (mask=0x%x)" HERE, mask1); } +retry: u32 collect = 0; // Check reservation status and set SPU_EVENT_LR if lost @@ -2490,7 +2555,7 @@ spu_thread::ch_events_t spu_thread::get_events(u32 mask_hint, bool waiting, bool set_events(collect); } - return ch_events.fetch_op([&](ch_events_t& events) + auto [res, ok] = ch_events.fetch_op([&](ch_events_t& events) { if (!reading) return false; @@ -2499,7 +2564,15 @@ spu_thread::ch_events_t spu_thread::get_events(u32 mask_hint, bool waiting, bool events.count = false; return true; - }).first; + }); + + if (reading && res.locks && mask_hint & (SPU_EVENT_S1 | SPU_EVENT_S2)) + { + busy_wait(100); + goto retry; + } + + return res; } void spu_thread::set_events(u32 bits) @@ -2520,8 +2593,7 @@ void spu_thread::set_events(u32 bits) return false; })) { - // Preserved for external events implementation - //notify(); + notify(); } } diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 460030293c..28893826b3 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -75,7 +75,7 @@ enum : u32 SPU_EVENT_SN = 0x2, // MFC List Command stall-and-notify event SPU_EVENT_TG = 0x1, // MFC Tag Group status update event - SPU_EVENT_IMPLEMENTED = SPU_EVENT_LR | SPU_EVENT_TM | SPU_EVENT_SN, // Mask of implemented events + SPU_EVENT_IMPLEMENTED = SPU_EVENT_LR | SPU_EVENT_TM | SPU_EVENT_SN | SPU_EVENT_S1 | SPU_EVENT_S2, // Mask of implemented events SPU_EVENT_INTR_IMPLEMENTED = SPU_EVENT_SN, SPU_EVENT_INTR_TEST = SPU_EVENT_INTR_IMPLEMENTED, @@ -187,7 +187,7 @@ public: } // Push performing bitwise OR with previous value, may require notification - void push_or(u32 value) + bool push_or(u32 value) { const u64 old = data.fetch_op([value](u64& data) { @@ -199,6 +199,8 @@ public: { data.notify_one(); } + + return (old & bit_count) == 0; } bool push_and(u32 value) @@ -207,12 +209,16 @@ public: } // Push unconditionally (overwriting previous value), may require notification - void push(u32 value) + bool push(u32 value) { - if (data.exchange(bit_count | value) & bit_wait) + const u64 old = data.exchange(bit_count | value); + + if (old & bit_wait) { data.notify_one(); } + + return (old & bit_count) == 0; } // Returns true on success @@ -694,6 +700,7 @@ public: { u64 all; bf_t events; + bf_t locks; bf_t waiting; bf_t count; bf_t mask;