SPU: Task-based SPURS limiter

This commit is contained in:
Elad Ashkenazi 2024-09-21 17:19:45 +03:00 committed by Elad
parent c4334f5142
commit ad42a2b89a
3 changed files with 77 additions and 3 deletions

View File

@ -1837,6 +1837,11 @@ void spu_thread::cpu_task()
}
}
if (spurs_addr && group->max_run != 6)
{
group->spurs_running++;
}
if (jit)
{
while (true)
@ -1884,6 +1889,14 @@ void spu_thread::cpu_task()
allow_interrupts_in_cpu_work = false;
}
if (spurs_addr && group->max_run != 6)
{
if (group->spurs_running.exchange(0))
{
group->spurs_running.notify_all();
}
}
}
void spu_thread::cpu_work()
@ -5469,12 +5482,71 @@ s64 spu_thread::get_ch_value(u32 ch)
case SPU_RdEventStat:
{
const bool is_spurs_task_wait = pc == 0x11a8 && group->max_run != 6 && spurs_addr;
if (is_spurs_task_wait)
{
const u32 prev_running = group->spurs_running.fetch_op([](u32& x)
{
if (x)
{
x--;
return true;
}
return false;
}).first;
if (prev_running == group->max_run)
{
group->spurs_running.notify_all();
}
}
const u32 mask1 = ch_events.load().mask;
auto events = get_events(mask1, false, true);
const auto wait_spurs_task = [&]
{
if (is_spurs_task_wait)
{
// Wait for other threads to complete their tasks (temporarily)
if (!is_stopped())
{
const u32 prev_running = group->spurs_running.fetch_op([max = group->max_run](u32& x)
{
if (x < max)
{
x++;
return true;
}
return false;
}).first;
if (prev_running >= group->max_run)
{
const u64 before = get_system_time();
thread_ctrl::wait_on(group->spurs_running, prev_running, 10000);
// Check for missed waiting (due to thread-state notification or value change)
const u32 new_running = group->spurs_running;
if (!is_stopped() && new_running >= group->max_run && get_system_time() - before < 1500)
{
thread_ctrl::wait_on(group->spurs_running, new_running, 10000);
}
group->spurs_running++;
}
}
}
};
if (events.count)
{
wait_spurs_task();
return events.events & mask1;
}
@ -5490,7 +5562,7 @@ s64 spu_thread::get_ch_value(u32 ch)
const u32 old_raddr = raddr;
// Does not need to safe-access reservation if LR is the only event masked
// Because it's either an access violation or a livelock if an invalid memory is passed
// Because it's either an access violation or a live-lock if an invalid memory is passed
if (raddr && mask1 > SPU_EVENT_LR)
{
auto area = vm::get(vm::any, raddr);
@ -5504,7 +5576,7 @@ s64 spu_thread::get_ch_value(u32 ch)
}
else if (area)
{
// Ensure possesion over reservation memory so it won't be deallocated
// Ensure possession over reservation memory so it won't be de-allocated
auto [base_addr, shm_] = area->peek(raddr);
if (shm_)
@ -5690,6 +5762,7 @@ s64 spu_thread::get_ch_value(u32 ch)
thread_ctrl::wait_on(state, old, 100);
}
wait_spurs_task();
wakeup_delay();
if (is_paused(state - cpu_flag::suspend))

View File

@ -1057,7 +1057,7 @@ error_code sys_spu_thread_group_start(ppu_thread& ppu, u32 id)
default: return CELL_ESTAT;
}
const u32 max_threads = group->max_run;
const u32 max_threads = group->max_num;
group->join_state = 0;
group->exit_status = 0;

View File

@ -289,6 +289,7 @@ struct lv2_spu_group
atomic_t<s32> exit_status; // SPU Thread Group Exit Status
atomic_t<u32> join_state; // flags used to detect exit cause and signal
atomic_t<u32> running = 0; // Number of running threads
atomic_t<u32> spurs_running = 0;
atomic_t<u32> stop_count = 0;
atomic_t<u32> wait_term_count = 0;
u32 waiter_spu_index = -1; // Index of SPU executing a waiting syscall