diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index ed5ebd8e69..2904f07c75 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -142,32 +142,64 @@ bool rsx::thread::send_event(u64 data1, u64 event_flags, u64 data3) return true; } -void _sys_rsx_drain_event_queue(rsx::thread* rsxthr) +void _sys_rsx_drain_event_queue(rsx::thread* rsxthr, u64 event_flags = umax, u64 wait_timeout_ms = 1000ull) { const auto& driverInfo = *vm::_ptr(rsxthr->driver_info); const u32 rsx_queue_id = driverInfo.handler_queue; - if (const auto queue = idm::get_unlocked(rsx_queue_id)) + if (const auto enabled_events = static_cast(driverInfo.handlers) | (0xffff'ffffull << 32); + !(enabled_events & event_flags)) { - while (true) - { - // First check if the queue is empty. This is the most likely scenario. - { - std::lock_guard lock(queue->mutex); - if (!queue->exists || (queue->events.empty() && queue->pq)) - { - break; - } - } + // sys_rsx.trace("Event flag not set."); + return; + } - // Emulator still running? - if (Emu.IsStopped()) + const auto queue = idm::get_unlocked(rsx_queue_id); + if (!queue) + { + sys_rsx.error("Failed to get RSX event queue."); + return; + } + + u64 start_ts = get_system_time(); + while (true) + { + // First check if the queue is empty. This is the most likely scenario. + { + std::lock_guard lock(queue->mutex); + if (!queue->exists || (queue->events.empty() && queue->pq)) { break; } + } - // Wait - thread_ctrl::wait_for(100); + // Emulator still running? + if (Emu.IsStopped()) + { + break; + } + + // Wait + thread_ctrl::wait_for(100); + + // Check for timeout + if (wait_timeout_ms == umax) + { + continue; + } + + // If paused, reset the timeout + if (Emu.IsPaused()) + { + start_ts = get_system_time(); + continue; + } + + const auto elapsed_ms = (get_system_time() - start_ts) / 1000ull; + if (elapsed_ms > wait_timeout_ms) + { + sys_rsx.error("RSX queue appears to be stuck. We have been waiting for %llums already. Aborting wait...", elapsed_ms); + break; } } } @@ -964,14 +996,16 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 case 0xFEF: // hack: user command { // NOTE: Hardware tests show that back-to-back user_cmd events execute in-order. - // However, data is being stored in the userCmdParam value which can get clobbered if we leave the values enqueued. - // As an optimization, instead of draining the queue after sending the event, we do it before. That way, back-to-back calls are correctly ordered, but single events don't bother waiting. - _sys_rsx_drain_event_queue(render); + // The invocation is non-blocking but also very quick to fire. + const auto intr_cause = static_cast(a4); + if (intr_cause != driverInfo.userCmdParam) + { + // Drain the event queue to make sure any previous callbacks have fired. + // The userCmdParam object is shared and we do not want to clobber it. + _sys_rsx_drain_event_queue(render, SYS_RSX_EVENT_USER_CMD); + driverInfo.userCmdParam = intr_cause; + } - // '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 - driverInfo.userCmdParam = static_cast(a4); render->send_event(0, SYS_RSX_EVENT_USER_CMD, 0); break; }