This commit is contained in:
Branimir Karadžić
2025-11-19 19:49:32 -08:00
committed by GitHub
parent 4aaf864875
commit 54941d2a3c
2 changed files with 112 additions and 23 deletions

View File

@@ -21,11 +21,6 @@ namespace bx
{ {
} }
inline uint32_t RingBufferControl::available() const
{
return distance(m_read, m_current);
}
inline bool RingBufferControl::isEmpty() const inline bool RingBufferControl::isEmpty() const
{ {
return m_read == m_write; return m_read == m_write;
@@ -119,7 +114,7 @@ namespace bx
} }
inline SpScRingBufferControl::SpScRingBufferControl(uint32_t _size) inline SpScRingBufferControl::SpScRingBufferControl(uint32_t _size)
: m_size(_size) : m_size(max(_size, 2) )
, m_current(0) , m_current(0)
, m_write(0) , m_write(0)
, m_read(0) , m_read(0)
@@ -130,11 +125,45 @@ namespace bx
{ {
} }
inline uint32_t SpScRingBufferControl::available() const inline bool SpScRingBufferControl::isEmpty() const
{
return m_read == m_write;
}
inline uint32_t SpScRingBufferControl::getSize() const
{
return m_size;
}
inline uint32_t SpScRingBufferControl::getNumEmpty() const
{
return m_size - distance(m_read, m_write) - 1;
}
inline uint32_t SpScRingBufferControl::getNumUsed() const
{ {
return distance(m_read, m_current); return distance(m_read, m_current);
} }
inline uint32_t SpScRingBufferControl::getNumReserved() const
{
return distance(m_current, m_write);
}
inline void SpScRingBufferControl::resize(int32_t _size)
{
_size = 0 > _size
// can shrink only by number of empty slots.
? bx::max(_size, -int32_t(getNumEmpty() ) )
: _size
;
m_size += _size;
m_current += m_current >= m_write ? _size : 0;
m_read += m_read >= m_write ? _size : 0;
}
inline uint32_t SpScRingBufferControl::consume(uint32_t _size) inline uint32_t SpScRingBufferControl::consume(uint32_t _size)
{ {
const uint32_t maxSize = distance(m_read, m_current); const uint32_t maxSize = distance(m_read, m_current);
@@ -147,13 +176,13 @@ namespace bx
return size; return size;
} }
inline uint32_t SpScRingBufferControl::reserve(uint32_t _size) inline uint32_t SpScRingBufferControl::reserve(uint32_t _size, bool _mustSucceed)
{ {
const uint32_t dist = distance(m_write, m_read)-1; const uint32_t dist = distance(m_write, m_read)-1;
const uint32_t maxSize = uint32_sels(dist, m_size-1, dist); const uint32_t maxSize = uint32_sels(dist, m_size-1, dist);
const uint32_t sizeNoSign = uint32_and(_size, 0x7fffffff); const uint32_t sizeNoSign = uint32_and(_size, 0x7fffffff);
const uint32_t test = uint32_sub(sizeNoSign, maxSize); const uint32_t test = uint32_sub(sizeNoSign, maxSize);
const uint32_t size = uint32_sels(test, _size, maxSize); const uint32_t size = uint32_sels(test, _size, _mustSucceed ? 0 : maxSize);
const uint32_t advance = uint32_add(m_write, size); const uint32_t advance = uint32_add(m_write, size);
const uint32_t write = uint32_mod(advance, m_size); const uint32_t write = uint32_mod(advance, m_size);
m_write = write; m_write = write;
@@ -200,7 +229,7 @@ namespace bx
, m_size(_size) , m_size(_size)
, m_buffer(_buffer) , m_buffer(_buffer)
{ {
BX_ASSERT(_control.available() >= _size, "%d >= %d", _control.available(), _size); BX_ASSERT(_control.getNumUsed() >= _size, "%d >= %d", _control.getNumUsed(), _size);
} }
template <typename ControlT> template <typename ControlT>

View File

@@ -37,12 +37,6 @@ namespace bx
/// Destructor. /// Destructor.
~RingBufferControl(); ~RingBufferControl();
/// Returns number of used slots.
///
/// @returns Number of used slots.
///
uint32_t available() const;
/// Returns 'true' if ring buffer is empty. /// Returns 'true' if ring buffer is empty.
/// ///
/// @returns Returns 'true' if ring buffer is empty. /// @returns Returns 'true' if ring buffer is empty.
@@ -122,6 +116,14 @@ namespace bx
uint32_t m_read; //!< Read head. uint32_t m_read; //!< Read head.
}; };
/// Lock-less single producer, single consumer ring buffer control structure. Tracking "read",
/// "write", and "current" head.
///
/// This is not container, and data control represents is user defined. Read/write/current are
/// just indices.
///
/// @notice One slot is always reseved. When creating ring buffer of N slots, N-1 slots can be
/// used.
/// ///
class SpScRingBufferControl class SpScRingBufferControl
{ {
@@ -131,34 +133,92 @@ namespace bx
); );
public: public:
/// Constructor.
///
/// @param[in] _size Maximum number of slots.
/// ///
SpScRingBufferControl(uint32_t _size); SpScRingBufferControl(uint32_t _size);
/// /// Destructor.
~SpScRingBufferControl(); ~SpScRingBufferControl();
/// Returns 'true' if ring buffer is empty.
/// ///
uint32_t available() const; /// @returns Returns 'true' if ring buffer is empty.
///
bool isEmpty() const;
/// Returns total size of ring buffer.
///
/// @returns Total size of ring buffer.
///
uint32_t getSize() const;
/// Returns number of empty slots.
///
/// @returns Number of empty slots.
///
uint32_t getNumEmpty() const;
/// Returns number of used slots.
///
/// @returns Number of used slots.
///
uint32_t getNumUsed() const;
/// Returns number of reserved slots.
///
/// @returns Number of reserved slots.
///
uint32_t getNumReserved() const;
/// Resize ring buffer. Resize happens at write head, read and current head will be moved
/// forward or backward if write head is behind them.
///
/// @param[in] _size Amount to resize. Value can be positive when growing size or negative
/// when shrinking size of buffer.
///
void resize(int32_t _size);
/// Consume slots, makes slots free to be reserved. Moves "read" head forward.
///
/// @returns Number of reserved slots reserved.
/// ///
uint32_t consume(uint32_t _size); // consumer only uint32_t consume(uint32_t _size); // consumer only
/// Reserve slots, makes slots non-free, but ready to be used yet. Moves "write" head forward.
/// ///
uint32_t reserve(uint32_t _size); // producer only /// @param[in] _size Number of slots.
/// @param[in] _mustSucceed If argument is true it will not reseve any slots unless `_size`
/// of slots is reseved.
///
/// @returns Number of reserved slots reserved.
///
uint32_t reserve(uint32_t _size, bool _mustSucceed = false); // producer only
/// Commit slots, makes slots used, and ready to be consumed. Moves "current" head forward.
///
/// @param[in] _size Number of commited slots.
/// ///
uint32_t commit(uint32_t _size); // producer only uint32_t commit(uint32_t _size); // producer only
/// Calculate distance between two slots. Function takes wrapping into account.
///
/// @param[in] _from From.
/// @param[in] _to To.
///
/// @returns Distance between slots.
/// ///
uint32_t distance(uint32_t _from, uint32_t _to) const; // both uint32_t distance(uint32_t _from, uint32_t _to) const; // both
/// Invalidate ring buffer.
/// ///
void reset(); void reset();
const uint32_t m_size; uint32_t m_size; //!< Size of ring buffer.
uint32_t m_current; uint32_t m_current; //!< Currently operated area start.
uint32_t m_write; uint32_t m_write; //!< Write head.
uint32_t m_read; uint32_t m_read; //!< Read head.
}; };
/// ///