Skip to content

Commit

Permalink
SPU: Use waitable atomic for SPU channels interface
Browse files Browse the repository at this point in the history
  • Loading branch information
elad335 committed Jun 26, 2020
1 parent 5a8eb9d commit ab02ac4
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 58 deletions.
2 changes: 1 addition & 1 deletion rpcs3/Emu/Cell/RawSPUThread.cpp
Expand Up @@ -121,7 +121,7 @@ bool spu_thread::read_reg(const u32 addr, u32& value)

case SPU_Out_MBox_offs:
{
value = ch_out_mbox.pop(*this);
value = ch_out_mbox.pop();
return true;
}

Expand Down
52 changes: 17 additions & 35 deletions rpcs3/Emu/Cell/SPUThread.cpp
Expand Up @@ -1166,11 +1166,11 @@ void spu_thread::push_snr(u32 number, u32 value)
// Check corresponding SNR register settings
if ((snr_config >> number) & 1)
{
channel->push_or(*this, value);
channel->push_or(value);
}
else
{
channel->push(*this, value);
channel->push(value);
}
}

Expand Down Expand Up @@ -2360,19 +2360,8 @@ s64 spu_thread::get_ch_value(u32 ch)
busy_wait();
}

u32 out = 0;

while (!channel.try_pop(out))
{
if (is_stopped())
{
return -1;
}

thread_ctrl::wait();
}

check_state();
const s64 out = channel.pop_wait(*this);
static_cast<void>(test_stopped());
return out;
};

Expand Down Expand Up @@ -2420,9 +2409,8 @@ s64 spu_thread::get_ch_value(u32 ch)

case MFC_RdTagStat:
{
if (ch_tag_stat.get_count())
if (u32 out; ch_tag_stat.try_read(out))
{
u32 out = ch_tag_stat.get_value();
ch_tag_stat.set_value(0, false);
return out;
}
Expand All @@ -2448,9 +2436,8 @@ s64 spu_thread::get_ch_value(u32 ch)

case MFC_RdAtomicStat:
{
if (ch_atomic_stat.get_count())
if (u32 out; ch_atomic_stat.try_read(out))
{
u32 out = ch_atomic_stat.get_value();
ch_atomic_stat.set_value(0, false);
return out;
}
Expand All @@ -2461,9 +2448,8 @@ s64 spu_thread::get_ch_value(u32 ch)

case MFC_RdListStallStat:
{
if (ch_stall_stat.get_count())
if (u32 out; ch_stall_stat.try_read(out))
{
u32 out = ch_stall_stat.get_value();
ch_stall_stat.set_value(0, false);
return out;
}
Expand Down Expand Up @@ -2570,16 +2556,14 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
{
if (offset >= RAW_SPU_BASE_ADDR)
{
while (!ch_out_intr_mbox.try_push(value))
if (ch_out_intr_mbox.get_count())
{
state += cpu_flag::wait;
}

if (is_stopped())
{
return false;
}

thread_ctrl::wait();
if (!ch_out_intr_mbox.push_wait(*this, value))
{
return false;
}

int_ctrl[2].set(SPU_INT2_STAT_MAILBOX_INT);
Expand Down Expand Up @@ -2710,16 +2694,14 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)

case SPU_WrOutMbox:
{
while (!ch_out_mbox.try_push(value))
if (ch_out_mbox.get_count())
{
state += cpu_flag::wait;
}

if (is_stopped())
{
return false;
}

thread_ctrl::wait();
if (!ch_out_mbox.push_wait(*this, value))
{
return false;
}

check_state();
Expand Down
98 changes: 77 additions & 21 deletions rpcs3/Emu/Cell/SPUThread.h
Expand Up @@ -170,23 +170,20 @@ struct spu_channel
// Returns true on success
bool try_push(u32 value)
{
const u64 old = data.fetch_op([value](u64& data)
return data.fetch_op([value](u64& data)
{
if (data & bit_count) [[unlikely]]
{
data |= bit_wait;
}
else
if (!(data & bit_count)) [[likely]]
{
data = bit_count | value;
return true;
}
});

return !(old & bit_count);
return false;
}).second;
}

// Push performing bitwise OR with previous value, may require notification
void push_or(cpu_thread& spu, u32 value)
void push_or(u32 value)
{
const u64 old = data.fetch_op([value](u64& data)
{
Expand All @@ -196,7 +193,7 @@ struct spu_channel

if (old & bit_wait)
{
spu.notify();
data.notify_one();
}
}

Expand All @@ -206,31 +203,28 @@ struct spu_channel
}

// Push unconditionally (overwriting previous value), may require notification
void push(cpu_thread& spu, u32 value)
void push(u32 value)
{
if (data.exchange(bit_count | value) & bit_wait)
{
spu.notify();
data.notify_one();
}
}

// Returns true on success
bool try_pop(u32& out)
{
const u64 old = data.fetch_op([&](u64& data)
return data.fetch_op([&](u64& data)
{
if (data & bit_count) [[likely]]
{
out = static_cast<u32>(data);
data = 0;
return true;
}
else
{
data |= bit_wait;
}
});

return (old & bit_count) != 0;
return false;
}).second;
}

// Reading without modification
Expand All @@ -248,19 +242,81 @@ struct spu_channel
}

// Pop unconditionally (loading last value), may require notification
u32 pop(cpu_thread& spu)
u32 pop()
{
// Value is not cleared and may be read again
const u64 old = data.fetch_and(~(bit_count | bit_wait));

if (old & bit_wait)
{
spu.notify();
data.notify_one();
}

return static_cast<u32>(old);
}

s64 pop_wait(cpu_thread& spu)
{
while (true)
{
const u64 old = data.fetch_op([&](u64& data)
{
if (data & bit_count) [[likely]]
{
data = 0;
}
else
{
data = bit_wait;
}
});

if (old & bit_count)
{
return static_cast<u32>(old);
}

if (spu.is_stopped())
{
return -1;
}

data.wait(bit_wait);
}
}

bool push_wait(cpu_thread& spu, u32 value)
{
while (true)
{
const u64 val = data.atomic_op([&](u64& data)
{
if (data & bit_count) [[unlikely]]
{
data |= bit_wait;
}
else
{
data = bit_count | value;
}

return data;
});

if (!(val & bit_wait))
{
return true;
}

if (spu.is_stopped())
{
return false;
}

data.wait(val);
}
}

void set_value(u32 value, bool count = true)
{
data.release(u64{count} << off_count | value);
Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/Cell/lv2/sys_spu.cpp
Expand Up @@ -2203,7 +2203,7 @@ error_code raw_spu_read_puint_mb(u32 id, vm::ptr<u32> value)
return CELL_ESRCH;
}

*value = thread->ch_out_intr_mbox.pop(*thread);
*value = thread->ch_out_intr_mbox.pop();

return CELL_OK;
}
Expand Down

0 comments on commit ab02ac4

Please sign in to comment.