mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-11 01:08:39 +12:00
SPU: Implement "double" SNR storage
This commit is contained in:
parent
dc80d000aa
commit
d0e9108800
2 changed files with 82 additions and 65 deletions
|
@ -1695,7 +1695,7 @@ void spu_thread::push_snr(u32 number, u32 value)
|
||||||
|
|
||||||
// Prepare some data
|
// Prepare some data
|
||||||
const u32 event_bit = SPU_EVENT_S1 >> (number & 1);
|
const u32 event_bit = SPU_EVENT_S1 >> (number & 1);
|
||||||
const u32 bitor_bit = (snr_config >> number) & 1;
|
const bool bitor_bit = !!((snr_config >> number) & 1);
|
||||||
|
|
||||||
// Redundant, g_use_rtm is checked inside tx_start now.
|
// Redundant, g_use_rtm is checked inside tx_start now.
|
||||||
if (g_use_rtm)
|
if (g_use_rtm)
|
||||||
|
@ -1705,10 +1705,16 @@ void spu_thread::push_snr(u32 number, u32 value)
|
||||||
|
|
||||||
const bool ok = utils::tx_start([&]
|
const bool ok = utils::tx_start([&]
|
||||||
{
|
{
|
||||||
channel_notify = (channel->data.raw() & spu_channel::bit_wait) != 0;
|
channel_notify = (channel->data.raw() == spu_channel::bit_wait);
|
||||||
thread_notify = (channel->data.raw() & spu_channel::bit_count) == 0;
|
thread_notify = (channel->data.raw() & spu_channel::bit_count) == 0;
|
||||||
|
|
||||||
if (bitor_bit)
|
if (channel_notify)
|
||||||
|
{
|
||||||
|
ensure(channel->jostling_value.raw() == spu_channel::bit_wait);
|
||||||
|
channel->jostling_value.raw() = value;
|
||||||
|
channel->data.raw() = 0;
|
||||||
|
}
|
||||||
|
else if (bitor_bit)
|
||||||
{
|
{
|
||||||
channel->data.raw() &= ~spu_channel::bit_wait;
|
channel->data.raw() &= ~spu_channel::bit_wait;
|
||||||
channel->data.raw() |= spu_channel::bit_count | value;
|
channel->data.raw() |= spu_channel::bit_count | value;
|
||||||
|
@ -1752,16 +1758,8 @@ void spu_thread::push_snr(u32 number, u32 value)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check corresponding SNR register settings
|
// Check corresponding SNR register settings
|
||||||
if (bitor_bit)
|
if (channel->push(value, bitor_bit))
|
||||||
{
|
set_events(event_bit);
|
||||||
if (channel->push_or(value))
|
|
||||||
set_events(event_bit);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (channel->push(value))
|
|
||||||
set_events(event_bit);
|
|
||||||
}
|
|
||||||
|
|
||||||
ch_events.atomic_op([](ch_events_t& ev)
|
ch_events.atomic_op([](ch_events_t& ev)
|
||||||
{
|
{
|
||||||
|
|
|
@ -166,13 +166,13 @@ enum : u32
|
||||||
SPU_FAKE_BASE_ADDR = 0xE8000000,
|
SPU_FAKE_BASE_ADDR = 0xE8000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct spu_channel
|
struct alignas(16) spu_channel
|
||||||
{
|
{
|
||||||
// Low 32 bits contain value
|
// Low 32 bits contain value
|
||||||
atomic_t<u64> data;
|
atomic_t<u64> data;
|
||||||
|
|
||||||
// Pending value to be inserted when it is possible at pop()
|
// Pending value to be inserted when it is possible in pop() or pop_wait()
|
||||||
atomic_t<u32> jostling_value;
|
atomic_t<u64> jostling_value;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr u32 off_wait = 32;
|
static constexpr u32 off_wait = 32;
|
||||||
|
@ -195,39 +195,49 @@ public:
|
||||||
}).second;
|
}).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push performing bitwise OR with previous value, may require notification
|
// Push unconditionally, may require notification
|
||||||
bool push_or(u32 value)
|
// Performing bitwise OR with previous value if specified, otherwise overwiting it
|
||||||
|
bool push(u32 value, bool to_or = false)
|
||||||
{
|
{
|
||||||
const u64 old = data.fetch_op([value](u64& data)
|
while (true)
|
||||||
{
|
{
|
||||||
data &= ~bit_wait;
|
const auto [old, pushed_to_data] = data.fetch_op([&](u64& data)
|
||||||
data |= bit_count | value;
|
{
|
||||||
});
|
if (data == bit_wait)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to_or)
|
||||||
|
{
|
||||||
|
data |= bit_count | value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data = bit_count | value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!pushed_to_data)
|
||||||
|
{
|
||||||
|
// Insert the pending value in special storage for waiting SPUs, leave no time in which the channel has data
|
||||||
|
if (!jostling_value.compare_and_swap_test(bit_wait, value))
|
||||||
|
{
|
||||||
|
// Other thread has inserted a value through jostling_value, retry
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn off waiting bit manually (must succeed because waiting bit can only be resetted by the thread pushed to jostling_value)
|
||||||
|
ensure(this->data.bit_test_reset(off_wait));
|
||||||
|
}
|
||||||
|
|
||||||
if (old & bit_wait)
|
|
||||||
{
|
|
||||||
data.notify_one();
|
data.notify_one();
|
||||||
|
|
||||||
|
// Return true if count has changed from 0 to 1, this condition is considered satisfied even if we pushed a value directly to the special storage for waiting SPUs
|
||||||
|
return !pushed_to_data || (old & bit_count) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (old & bit_count) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool push_and(u32 value)
|
|
||||||
{
|
|
||||||
return (data.fetch_and(~u64{value}) & value) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push unconditionally (overwriting previous value), may require notification
|
|
||||||
bool push(u32 value)
|
|
||||||
{
|
|
||||||
const u64 old = data.exchange(bit_count | value);
|
|
||||||
|
|
||||||
if (old & bit_wait)
|
|
||||||
{
|
|
||||||
data.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (old & bit_count) == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true on success
|
// Returns true on success
|
||||||
|
@ -250,10 +260,10 @@ public:
|
||||||
bool try_read(u32& out) const
|
bool try_read(u32& out) const
|
||||||
{
|
{
|
||||||
const u64 old = data.load();
|
const u64 old = data.load();
|
||||||
|
out = static_cast<u32>(old);
|
||||||
|
|
||||||
if (old & bit_count) [[likely]]
|
if (old & bit_count) [[likely]]
|
||||||
{
|
{
|
||||||
out = static_cast<u32>(old);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +282,7 @@ public:
|
||||||
if ((data & mask) == mask)
|
if ((data & mask) == mask)
|
||||||
{
|
{
|
||||||
// Insert the pending value, leave no time in which the channel has no data
|
// Insert the pending value, leave no time in which the channel has no data
|
||||||
data = bit_count | jostling_value;
|
data = bit_count | static_cast<u32>(jostling_value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,41 +300,50 @@ public:
|
||||||
// Waiting for channel pop state availability, actually popping if specified
|
// Waiting for channel pop state availability, actually popping if specified
|
||||||
s64 pop_wait(cpu_thread& spu, bool pop = true)
|
s64 pop_wait(cpu_thread& spu, bool pop = true)
|
||||||
{
|
{
|
||||||
while (true)
|
u64 old = data.fetch_op([&](u64& data)
|
||||||
{
|
{
|
||||||
const u64 old = data.fetch_op([&](u64& data)
|
if (data & bit_count) [[likely]]
|
||||||
{
|
{
|
||||||
if (data & bit_count) [[likely]]
|
if (pop)
|
||||||
{
|
{
|
||||||
if (pop)
|
data = 0;
|
||||||
{
|
return true;
|
||||||
data = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data = bit_wait;
|
return false;
|
||||||
return true;
|
}
|
||||||
}).first;
|
|
||||||
|
|
||||||
if (old & bit_count)
|
data = bit_wait;
|
||||||
|
jostling_value.release(bit_wait);
|
||||||
|
return true;
|
||||||
|
}).first;
|
||||||
|
|
||||||
|
if (old & bit_count)
|
||||||
|
{
|
||||||
|
return static_cast<u32>(old);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
thread_ctrl::wait_on(data, bit_wait);
|
||||||
|
old = data;
|
||||||
|
|
||||||
|
if (!(old & bit_wait))
|
||||||
{
|
{
|
||||||
return static_cast<u32>(old);
|
return static_cast<u32>(jostling_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spu.is_stopped())
|
if (spu.is_stopped())
|
||||||
{
|
{
|
||||||
if (u64 old2 = data.exchange(0); old2 & bit_count)
|
// Abort waiting and test if a value has been received
|
||||||
|
if (u64 v = jostling_value.exchange(0); !(v & bit_wait))
|
||||||
{
|
{
|
||||||
return static_cast<u32>(old2);
|
return static_cast<u32>(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ensure(data.bit_test_reset(off_wait));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_ctrl::wait_on(data, bit_wait);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue