mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-04-07 15:36:18 +00:00
sys_rsxaudio: Initial implementation (#11907)
This commit is contained in:
parent
0ac90ac395
commit
d1e468fefb
33 changed files with 4148 additions and 546 deletions
|
|
@ -1,129 +1,202 @@
|
|||
#include "Utilities/simple_ringbuf.h"
|
||||
|
||||
simple_ringbuf::simple_ringbuf(u32 size)
|
||||
simple_ringbuf::simple_ringbuf(u64 size)
|
||||
{
|
||||
set_buf_size(size);
|
||||
}
|
||||
|
||||
simple_ringbuf::~simple_ringbuf()
|
||||
{
|
||||
rw_ptr.load(); // Sync
|
||||
}
|
||||
|
||||
simple_ringbuf::simple_ringbuf(const simple_ringbuf& other)
|
||||
{
|
||||
ctr_state old = other.rw_ptr.load();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
buf = other.buf;
|
||||
rw_ptr = old;
|
||||
|
||||
const ctr_state current = other.rw_ptr.load();
|
||||
if (old == current)
|
||||
{
|
||||
break;
|
||||
}
|
||||
old = current;
|
||||
}
|
||||
}
|
||||
|
||||
simple_ringbuf& simple_ringbuf::operator=(const simple_ringbuf& other)
|
||||
{
|
||||
if (this == &other) return *this;
|
||||
|
||||
ctr_state old = other.rw_ptr.load();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
buf = other.buf;
|
||||
rw_ptr = old;
|
||||
|
||||
const ctr_state current = other.rw_ptr.load();
|
||||
if (old == current)
|
||||
{
|
||||
break;
|
||||
}
|
||||
old = current;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
simple_ringbuf::simple_ringbuf(simple_ringbuf&& other)
|
||||
{
|
||||
rw_ptr = other.rw_ptr.load();
|
||||
buf_size = other.buf_size;
|
||||
const ctr_state other_rw_ptr = other.rw_ptr.load();
|
||||
buf = std::move(other.buf);
|
||||
initialized = other.initialized.observe();
|
||||
rw_ptr = other_rw_ptr;
|
||||
|
||||
other.buf_size = 0;
|
||||
other.rw_ptr = 0;
|
||||
other.initialized = false;
|
||||
other.rw_ptr.store({});
|
||||
}
|
||||
|
||||
simple_ringbuf& simple_ringbuf::operator=(simple_ringbuf&& other)
|
||||
{
|
||||
if (this == &other) return *this;
|
||||
|
||||
rw_ptr = other.rw_ptr.load();
|
||||
buf_size = other.buf_size;
|
||||
const ctr_state other_rw_ptr = other.rw_ptr.load();
|
||||
buf = std::move(other.buf);
|
||||
initialized = other.initialized.observe();
|
||||
rw_ptr = other_rw_ptr;
|
||||
|
||||
other.buf_size = 0;
|
||||
other.rw_ptr = 0;
|
||||
other.initialized = false;
|
||||
other.rw_ptr.store({});
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::get_free_size() const
|
||||
u64 simple_ringbuf::get_free_size() const
|
||||
{
|
||||
const u64 _rw_ptr = rw_ptr;
|
||||
const u32 rd = static_cast<u32>(_rw_ptr);
|
||||
const u32 wr = static_cast<u32>(_rw_ptr >> 32);
|
||||
|
||||
return wr >= rd ? buf_size - 1 - (wr - rd) : rd - wr - 1U;
|
||||
return get_free_size(rw_ptr);
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::get_used_size() const
|
||||
u64 simple_ringbuf::get_used_size() const
|
||||
{
|
||||
return buf_size - 1 - get_free_size();
|
||||
return get_used_size(rw_ptr);
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::get_total_size() const
|
||||
u64 simple_ringbuf::get_total_size() const
|
||||
{
|
||||
return buf_size;
|
||||
rw_ptr.load(); // Sync
|
||||
return buf.size() - 1;
|
||||
}
|
||||
|
||||
void simple_ringbuf::set_buf_size(u32 size)
|
||||
u64 simple_ringbuf::get_free_size(ctr_state val) const
|
||||
{
|
||||
ensure(size);
|
||||
const u64 buf_size = buf.size();
|
||||
const u64 rd = val.read_ptr % buf_size;
|
||||
const u64 wr = val.write_ptr % buf_size;
|
||||
|
||||
this->buf_size = size + 1;
|
||||
buf = std::make_unique<u8[]>(this->buf_size);
|
||||
flush();
|
||||
initialized = true;
|
||||
return (wr >= rd ? buf_size + rd - wr : rd - wr) - 1;
|
||||
}
|
||||
|
||||
void simple_ringbuf::flush()
|
||||
u64 simple_ringbuf::get_used_size(ctr_state val) const
|
||||
{
|
||||
rw_ptr.atomic_op([&](u64 &val)
|
||||
const u64 buf_size = buf.size();
|
||||
const u64 rd = val.read_ptr % buf_size;
|
||||
const u64 wr = val.write_ptr % buf_size;
|
||||
|
||||
return wr >= rd ? wr - rd : buf_size + wr - rd;
|
||||
}
|
||||
|
||||
void simple_ringbuf::set_buf_size(u64 size)
|
||||
{
|
||||
ensure(size != umax);
|
||||
|
||||
buf.resize(size + 1);
|
||||
rw_ptr.store({});
|
||||
}
|
||||
|
||||
void simple_ringbuf::writer_flush(u64 cnt)
|
||||
{
|
||||
rw_ptr.atomic_op([&](ctr_state& val)
|
||||
{
|
||||
val = static_cast<u32>(val >> 32) | (val & 0xFFFFFFFF'00000000);
|
||||
const u64 used = get_used_size(val);
|
||||
if (used == 0) return;
|
||||
|
||||
val.write_ptr += buf.size() - std::min<u64>(used, cnt);
|
||||
});
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::push(const void *data, u32 size)
|
||||
void simple_ringbuf::reader_flush(u64 cnt)
|
||||
{
|
||||
ensure(data != nullptr && initialized.observe());
|
||||
|
||||
const u32 old = static_cast<u32>(rw_ptr.load() >> 32);
|
||||
const u32 to_push = std::min(size, get_free_size());
|
||||
auto b_data = static_cast<const u8*>(data);
|
||||
|
||||
if (!to_push) return 0;
|
||||
|
||||
if (old + to_push > buf_size)
|
||||
rw_ptr.atomic_op([&](ctr_state& val)
|
||||
{
|
||||
const auto first_write_sz = buf_size - old;
|
||||
memcpy(&buf[old], b_data, first_write_sz);
|
||||
memcpy(&buf[0], b_data + first_write_sz, to_push - first_write_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&buf[old], b_data, to_push);
|
||||
}
|
||||
|
||||
rw_ptr.atomic_op([&](u64 &val)
|
||||
{
|
||||
val = static_cast<u64>((old + to_push) % buf_size) << 32 | static_cast<u32>(val);
|
||||
val.read_ptr += std::min(get_used_size(val), cnt);
|
||||
});
|
||||
|
||||
return to_push;
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::pop(void *data, u32 size)
|
||||
u64 simple_ringbuf::push(const void* data, u64 size, bool force)
|
||||
{
|
||||
ensure(data != nullptr && initialized.observe());
|
||||
ensure(data != nullptr);
|
||||
|
||||
const u32 old = static_cast<u32>(rw_ptr.load());
|
||||
const u32 to_pop = std::min(size, get_used_size());
|
||||
u8 *b_data = static_cast<u8*>(data);
|
||||
|
||||
if (!to_pop) return 0;
|
||||
|
||||
if (old + to_pop > buf_size)
|
||||
return rw_ptr.atomic_op([&](ctr_state& val) -> u64
|
||||
{
|
||||
const auto first_read_sz = buf_size - old;
|
||||
memcpy(b_data, &buf[old], first_read_sz);
|
||||
memcpy(b_data + first_read_sz, &buf[0], to_pop - first_read_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(b_data, &buf[old], to_pop);
|
||||
}
|
||||
const u64 buf_size = buf.size();
|
||||
const u64 old = val.write_ptr % buf_size;
|
||||
const u64 free_size = get_free_size(val);
|
||||
const u64 to_push = std::min(size, free_size);
|
||||
const auto b_data = static_cast<const u8*>(data);
|
||||
|
||||
rw_ptr.atomic_op([&](u64 &val)
|
||||
{
|
||||
val = (old + to_pop) % buf_size | (val & 0xFFFFFFFF'00000000);
|
||||
if (!to_push || (!force && free_size < size))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (old + to_push > buf_size)
|
||||
{
|
||||
const auto first_write_sz = buf_size - old;
|
||||
memcpy(&buf[old], b_data, first_write_sz);
|
||||
memcpy(&buf[0], b_data + first_write_sz, to_push - first_write_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&buf[old], b_data, to_push);
|
||||
}
|
||||
|
||||
val.write_ptr += to_push;
|
||||
|
||||
return to_push;
|
||||
});
|
||||
}
|
||||
|
||||
u64 simple_ringbuf::pop(void* data, u64 size, bool force)
|
||||
{
|
||||
ensure(data != nullptr);
|
||||
|
||||
return rw_ptr.atomic_op([&](ctr_state& val) -> u64
|
||||
{
|
||||
const u64 buf_size = buf.size();
|
||||
const u64 old = val.read_ptr % buf_size;
|
||||
const u64 used_size = get_used_size(val);
|
||||
const u64 to_pop = std::min(size, used_size);
|
||||
const auto b_data = static_cast<u8*>(data);
|
||||
|
||||
if (!to_pop || (!force && used_size < size))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (old + to_pop > buf_size)
|
||||
{
|
||||
const auto first_read_sz = buf_size - old;
|
||||
memcpy(b_data, &buf[old], first_read_sz);
|
||||
memcpy(b_data + first_read_sz, &buf[0], to_pop - first_read_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(b_data, &buf[old], to_pop);
|
||||
}
|
||||
|
||||
val.read_ptr += to_pop;
|
||||
|
||||
return to_pop;
|
||||
});
|
||||
|
||||
return to_pop;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,37 +2,53 @@
|
|||
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include <vector>
|
||||
|
||||
// Single reader/writer simple ringbuffer.
|
||||
// Counters are 32-bit.
|
||||
class simple_ringbuf
|
||||
{
|
||||
private:
|
||||
|
||||
atomic_t<u64> rw_ptr = 0;
|
||||
u32 buf_size = 0;
|
||||
std::unique_ptr<u8[]> buf{};
|
||||
atomic_t<bool> initialized = false;
|
||||
|
||||
public:
|
||||
|
||||
simple_ringbuf() {};
|
||||
simple_ringbuf(u32 size);
|
||||
simple_ringbuf(u64 size = 0);
|
||||
virtual ~simple_ringbuf();
|
||||
|
||||
simple_ringbuf(const simple_ringbuf&) = delete;
|
||||
simple_ringbuf& operator=(const simple_ringbuf&) = delete;
|
||||
|
||||
simple_ringbuf(simple_ringbuf&& other);
|
||||
simple_ringbuf& operator=(simple_ringbuf&& other);
|
||||
|
||||
u32 get_free_size() const;
|
||||
u32 get_used_size() const;
|
||||
u32 get_total_size() const;
|
||||
simple_ringbuf(const simple_ringbuf& other);
|
||||
simple_ringbuf& operator=(const simple_ringbuf& other);
|
||||
|
||||
// Thread unsafe functions.
|
||||
void set_buf_size(u32 size);
|
||||
void flush(); // Could be safely called from reader.
|
||||
simple_ringbuf(simple_ringbuf&& other);
|
||||
simple_ringbuf& operator=(simple_ringbuf&& other);
|
||||
void set_buf_size(u64 size);
|
||||
|
||||
u32 push(const void *data, u32 size);
|
||||
u32 pop(void *data, u32 size);
|
||||
// Helper functions
|
||||
u64 get_free_size() const;
|
||||
u64 get_used_size() const;
|
||||
u64 get_total_size() const;
|
||||
|
||||
// Writer functions
|
||||
u64 push(const void* data, u64 size, bool force = false);
|
||||
void writer_flush(u64 cnt = umax);
|
||||
|
||||
// Reader functions
|
||||
u64 pop(void* data, u64 size, bool force = false);
|
||||
void reader_flush(u64 cnt = umax);
|
||||
|
||||
private:
|
||||
|
||||
struct ctr_state
|
||||
{
|
||||
alignas(sizeof(u64) * 2)
|
||||
u64 read_ptr = 0;
|
||||
u64 write_ptr = 0;
|
||||
|
||||
auto operator<=>(const ctr_state& other) const = default;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ctr_state) == sizeof(u64) * 2);
|
||||
|
||||
atomic_t<ctr_state> rw_ptr{};
|
||||
std::vector<u8> buf{};
|
||||
|
||||
u64 get_free_size(ctr_state val) const;
|
||||
u64 get_used_size(ctr_state val) const;
|
||||
};
|
||||
|
|
|
|||
159
Utilities/transactional_storage.h
Normal file
159
Utilities/transactional_storage.h
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
#include "util/types.hpp"
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
// Thread-safe object pool with garbage collection
|
||||
class universal_pool
|
||||
{
|
||||
public:
|
||||
|
||||
universal_pool(u32 gc_interval = 10000) : gc_interval(gc_interval)
|
||||
{
|
||||
}
|
||||
|
||||
~universal_pool()
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
storage.clear();
|
||||
}
|
||||
|
||||
universal_pool(const universal_pool&) = delete;
|
||||
universal_pool& operator=(const universal_pool&) = delete;
|
||||
|
||||
void set_gc_interval(u32 new_val)
|
||||
{
|
||||
gc_interval = new_val;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
requires (std::invocable<F&> && std::is_same_v<std::invoke_result_t<F&>, std::shared_ptr<void>>)
|
||||
void add_op(F func)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
if (std::shared_ptr<void> new_val = std::invoke(func); new_val)
|
||||
{
|
||||
storage.push_back(new_val);
|
||||
}
|
||||
delete_unused();
|
||||
}
|
||||
|
||||
void force_gc()
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
delete_unused();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void delete_unused()
|
||||
{
|
||||
const u32 gc_int = gc_interval.observe();
|
||||
|
||||
if (u64 crnt_time = get_system_time(); gc_int == 0 || crnt_time > gc_last_time + gc_int)
|
||||
{
|
||||
gc_last_time = crnt_time;
|
||||
storage.erase
|
||||
(
|
||||
std::remove_if(storage.begin(), storage.end(), [](auto& obj) { return obj.use_count() <= 1; }),
|
||||
storage.end()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
shared_mutex mutex{};
|
||||
std::vector<std::shared_ptr<void>> storage{};
|
||||
u64 gc_last_time = get_system_time();
|
||||
atomic_t<u32> gc_interval = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class transactional_storage
|
||||
{
|
||||
public:
|
||||
|
||||
transactional_storage(std::shared_ptr<universal_pool> pool, std::shared_ptr<T> obj = std::make_shared<T>())
|
||||
{
|
||||
ensure(pool && obj);
|
||||
|
||||
this->pool = pool;
|
||||
add(obj);
|
||||
}
|
||||
|
||||
transactional_storage(const transactional_storage&) = delete;
|
||||
transactional_storage& operator=(const transactional_storage&) = delete;
|
||||
|
||||
transactional_storage(transactional_storage&& other)
|
||||
{
|
||||
pool = std::move(other.pool);
|
||||
|
||||
std::unique_lock lock_other{other.current_mutex};
|
||||
const std::shared_ptr<T> other_current = other.current;
|
||||
other.current = nullptr;
|
||||
lock_other.unlock();
|
||||
|
||||
std::lock_guard lock{current_mutex};
|
||||
current = other_current;
|
||||
}
|
||||
|
||||
transactional_storage& operator=(transactional_storage&& other)
|
||||
{
|
||||
if (this == &other) return *this;
|
||||
|
||||
pool = std::move(other.pool);
|
||||
|
||||
std::unique_lock lock_other{other.current_mutex};
|
||||
const std::shared_ptr<T> other_current = other.current;
|
||||
other.current = nullptr;
|
||||
lock_other.unlock();
|
||||
|
||||
std::lock_guard lock{current_mutex};
|
||||
current = other_current;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::shared_ptr<const T> get_current()
|
||||
{
|
||||
reader_lock lock(current_mutex);
|
||||
return current;
|
||||
}
|
||||
|
||||
void add(std::shared_ptr<T> obj)
|
||||
{
|
||||
if (!obj)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pool->add_op([&]() -> std::shared_ptr<void>
|
||||
{
|
||||
{
|
||||
std::lock_guard lock{current_mutex};
|
||||
current = obj;
|
||||
}
|
||||
return std::move(obj);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
requires (std::invocable<F&> && std::is_same_v<std::invoke_result_t<F&>, std::shared_ptr<T>>)
|
||||
void add_op(F func)
|
||||
{
|
||||
pool->add_op([&]() -> std::shared_ptr<void>
|
||||
{
|
||||
std::shared_ptr<T> obj = std::invoke(func);
|
||||
if (obj)
|
||||
{
|
||||
std::lock_guard lock{current_mutex};
|
||||
current = obj;
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
shared_mutex current_mutex{};
|
||||
std::shared_ptr<T> current{};
|
||||
std::shared_ptr<universal_pool> pool{};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue