diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 542443c37e..3a0bcd40d2 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1536,14 +1536,14 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe // If we fail due to being busy, wait a bit and try again. while (static_cast(sending_error) == CELL_EBUSY) { + thread_ctrl::wait_for(1000); + sending_error = sys_event_port_send(pf_port_id, data1, data2, data3); + if (cpu->is_stopped()) { sending_error = {}; break; } - - thread_ctrl::wait_for(1000); - sending_error = sys_event_port_send(pf_port_id, data1, data2, data3); } if (sending_error) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index b11a2e4b91..a3d0bfc68f 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include "Utilities/JIT.h" #include "Utilities/date_time.h" #include "Emu/Memory/vm.h" @@ -1704,7 +1704,7 @@ void spu_thread::serialize_common(utils::serial& ar) , ch_out_mbox.data, ch_out_intr_mbox.data, snr_config, ch_snr1.data, ch_snr2.data, ch_events.raw().all, interrupts_enabled , run_ctrl, exit_status.data, status_npc.raw().status); - if (GET_SERIALIZATION_VERSION(spu) != 1u) + if (GET_SERIALIZATION_VERSION(spu) != 1) { ar(ch_dec_start_timestamp, ch_dec_value, is_dec_frozen); } diff --git a/rpcs3/Emu/Cell/lv2/sys_event.cpp b/rpcs3/Emu/Cell/lv2/sys_event.cpp index d9d6619b56..29f2c5f6d3 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event.cpp @@ -239,7 +239,7 @@ error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode) const auto queue = idm::withdraw(equeue_id, [&](lv2_event_queue& queue) -> CellError { - qlock.lock(queue.mutex); + qlock = std::unique_lock{queue.mutex}; if (!mode && !queue.sq.empty()) { @@ -456,8 +456,14 @@ error_code sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ptrmutex); + + if (std::find(queue->sq.begin(), queue->sq.end(), &ppu) == queue->sq.end()) + { + break; + } + + ppu.state += cpu_flag::again; return {}; } @@ -686,7 +692,8 @@ error_code sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3) { if (port.ret == CELL_EAGAIN) { - return CELL_OK; + // Not really an error code exposed to games (thread has raised cpu_flag::again) + return not_an_error(CELL_EAGAIN); } if (port.ret == CELL_EBUSY) diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index 43132c5e56..bc047d91d4 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -37,7 +37,7 @@ u64 rsxTimeStamp() return get_timebased_time(); } -void rsx::thread::send_event(u64 data1, u64 event_flags, u64 data3) const +bool rsx::thread::send_event(u64 data1, u64 event_flags, u64 data3) { // Filter event bits, send them only if they are masked by gcm // Except the upper 32-bits, they are reserved for unmapped io events and execute unconditionally @@ -46,7 +46,7 @@ void rsx::thread::send_event(u64 data1, u64 event_flags, u64 data3) const if (!event_flags) { // Nothing to do - return; + return true; } auto error = sys_event_port_send(rsx_event_port, data1, event_flags, data3); @@ -76,35 +76,19 @@ void rsx::thread::send_event(u64 data1, u64 event_flags, u64 data3) const error = sys_event_port_send(rsx_event_port, data1, event_flags, data3); } - if (!Emu.IsPaused() && error && error + 0u != CELL_ENOTCONN) + if (error + 0u == CELL_EAGAIN) + { + // Thread has aborted when sending event (VBLANK duplicates are allowed) + ensure((unsent_gcm_events.fetch_or(event_flags) & event_flags & ~(SYS_RSX_EVENT_VBLANK | SYS_RSX_EVENT_SECOND_VBLANK_BASE | SYS_RSX_EVENT_SECOND_VBLANK_BASE * 2)) == 0); + return false; + } + + if (error && error + 0u != CELL_ENOTCONN) { fmt::throw_exception("rsx::thread::send_event() Failed to send event! (error=%x)", +error); } -} -// Returns true on success of receiving the event -void signal_gcm_intr_thread_offline(lv2_event_queue& q) -{ - const auto render = rsx::get_current_renderer(); - const auto cpu = cpu_thread::get_current(); - - static shared_mutex s_dummy; - - std::scoped_lock lock_rsx(render ? render->sys_rsx_mtx : s_dummy, q.mutex); - - if (std::find(q.sq.begin(), q.sq.end(), cpu) == q.sq.end()) - { - return; - } - - cpu->state += cpu_flag::again; - - if (!vm::check_addr(render->driver_info) || vm::_ref(render->driver_info).handler_queue != q.id) - { - return; - } - - render->gcm_intr_thread_offline = true; + return true; } error_code sys_rsx_device_open(cpu_thread& cpu) @@ -534,7 +518,10 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 driverInfo.head[a3].flipFlags |= 0x40000000 | (1 << a4); render->on_frame_end(static_cast(a4)); - render->send_event(0, SYS_RSX_EVENT_QUEUE_BASE << a3, 0); + if (!render->send_event(0, SYS_RSX_EVENT_QUEUE_BASE << a3, 0)) + { + break; + } if (g_cfg.video.frame_limit == frame_limit_type::infinite) { @@ -780,19 +767,6 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 case 0xFEC: // hack: flip event notification { - reader_lock lock(render->sys_rsx_mtx); - - if (render->gcm_intr_thread_offline) - { - if (auto cpu = get_current_cpu_thread()) - { - cpu->state += cpu_flag::exit; - cpu->state += cpu_flag::again; - } - - break; - } - // we only ever use head 1 for now driverInfo.head[1].flipFlags |= 0x80000000; driverInfo.head[1].lastFlipTime = rsxTimeStamp(); // should rsxthread set this? @@ -817,13 +791,6 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 // NOTE: There currently seem to only be 2 active heads on PS3 ensure(a3 < 2); - reader_lock lock(render->sys_rsx_mtx); - - if (render->gcm_intr_thread_offline) - { - break; - } - // todo: this is wrong and should be 'second' vblank handler and freq, but since currently everything is reported as being 59.94, this should be fine driverInfo.head[a3].lastSecondVTime.atomic_op([&](be_t& time) { @@ -852,19 +819,6 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 case 0xFEF: // hack: user command { - reader_lock lock(render->sys_rsx_mtx); - - if (render->gcm_intr_thread_offline) - { - if (auto cpu = get_current_cpu_thread()) - { - cpu->state += cpu_flag::exit; - cpu->state += cpu_flag::again; - } - - break; - } - // 'custom' invalid package id for now // as i think we need custom lv1 interrupts to handle this accurately // this also should probly be set by rsxthread diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 4353e19720..cf373445c7 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -453,6 +453,11 @@ namespace rsx ar(device_addr, label_addr, main_mem_size, local_mem_size, rsx_event_port, driver_info); ar(in_begin_end, zcull_stats_enabled, zcull_rendering_enabled, zcull_pixel_cnt_enabled); ar(display_buffers, display_buffers_count, current_display_buffer); + + if (ar.is_writing() || GET_SERIALIZATION_VERSION(rsx) != 1u) + { + ar(unsent_gcm_events); + } } thread::thread(utils::serial* _ar) @@ -756,6 +761,14 @@ namespace rsx vblank_count = 0; + if (u64 event_flags = unsent_gcm_events.exchange(0)) + { + if (!send_event(0, event_flags, 0)) + { + return; + } + } + g_fxo->init("VBlank Thread", [this]() { // See sys_timer_usleep for details @@ -772,7 +785,7 @@ namespace rsx u64 local_vblank_count = 0; // TODO: exit condition - while (!is_stopped()) + while (!is_stopped() && !unsent_gcm_events) { // Get current time const u64 current = get_system_time(); @@ -3407,6 +3420,13 @@ namespace rsx if (!isHLE) { sys_rsx_context_attribute(0x55555555, 0xFEC, buffer, 0, 0, 0); + + if (unsent_gcm_events) + { + // TODO: A proper fix + return; + } + continue; } diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 5050ddba67..4b0a070b92 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -584,9 +584,10 @@ namespace rsx u32 local_mem_size{0}; u32 rsx_event_port{0}; u32 driver_info{0}; - bool gcm_intr_thread_offline = false; // Hack for savestates - void send_event(u64, u64, u64) const; + atomic_t unsent_gcm_events = 0; // Unsent event bits when aborting RSX/VBLANK thread (will be sent on savestate load) + + bool send_event(u64, u64, u64); bool m_rtts_dirty = true; std::array m_textures_dirty; diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 5a34cfd111..e9d60fe9e4 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -67,8 +67,8 @@ u64 g_rtm_tx_limit2 = 0; struct serial_ver_t { bool used = false; - u32 current_version = 0; - std::set compatible_versions; + s32 current_version = 0; + std::set compatible_versions; }; static std::array s_serial_versions; @@ -83,7 +83,7 @@ static std::array s_serial_versions; ::s_serial_versions[identifier].used = true;\ }\ \ - extern u32 get_##name##_serialization_version()\ + extern s32 get_##name##_serialization_version()\ {\ return ::s_serial_versions[identifier].current_version;\ } @@ -101,7 +101,7 @@ SERIALIZATION_VER(lv2_config, 9, 1) namespace rsx { - SERIALIZATION_VER(rsx, 10, 1) + SERIALIZATION_VER(rsx, 10, 1, 2) } namespace np @@ -111,7 +111,7 @@ namespace np #ifdef _MSC_VER // Compiler bug, lambda function body does seem to inherit used namespace atleast for function decleration -SERIALIZATION_VER(rsx, 10, 1) +SERIALIZATION_VER(rsx, 10, 1, 2) SERIALIZATION_VER(sceNp, 11, 1) #endif diff --git a/rpcs3/util/types.hpp b/rpcs3/util/types.hpp index 5052970e18..0dc9d3b879 100644 --- a/rpcs3/util/types.hpp +++ b/rpcs3/util/types.hpp @@ -1118,7 +1118,7 @@ extern bool serialize(utils::serial& ar, T& obj); #define GET_SERIALIZATION_VERSION(name) []()\ {\ - extern u32 get_##name##_serialization_version();\ + extern s32 get_##name##_serialization_version();\ return get_##name##_serialization_version();\ }()