rsx/lv2: Add a timeout to the queue drain logic

- Also adds an optimization to skip draining the queue if the command arg is the same.
This commit is contained in:
kd-11
2026-09-23 17:54:33 +03:00
committed by kd-11
parent 99a926d278
commit 56237b6858
+57 -23
View File
@@ -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<RsxDriverInfo>(rsxthr->driver_info);
const u32 rsx_queue_id = driverInfo.handler_queue;
if (const auto queue = idm::get_unlocked<lv2_obj, lv2_event_queue>(rsx_queue_id))
if (const auto enabled_events = static_cast<u64>(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<lv2_obj, lv2_event_queue>(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<u32>(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<u32>(a4);
render->send_event(0, SYS_RSX_EVENT_USER_CMD, 0);
break;
}