diff options
Diffstat (limited to 'Runtime/Containers')
-rw-r--r-- | Runtime/Containers/ConstantString.cpp | 123 | ||||
-rw-r--r-- | Runtime/Containers/ConstantString.h | 82 | ||||
-rw-r--r-- | Runtime/Containers/ConstantStringManager.cpp | 91 | ||||
-rw-r--r-- | Runtime/Containers/ConstantStringManager.h | 38 | ||||
-rw-r--r-- | Runtime/Containers/ConstantStringSerialization.h | 18 | ||||
-rw-r--r-- | Runtime/Containers/ExtendedRingbuffer.h | 146 | ||||
-rw-r--r-- | Runtime/Containers/GrowingRingbuffer.h | 121 | ||||
-rw-r--r-- | Runtime/Containers/Ringbuffer.h | 94 | ||||
-rw-r--r-- | Runtime/Containers/TransactionalRingbuffer.h | 37 |
9 files changed, 750 insertions, 0 deletions
diff --git a/Runtime/Containers/ConstantString.cpp b/Runtime/Containers/ConstantString.cpp new file mode 100644 index 0000000..9cda5af --- /dev/null +++ b/Runtime/Containers/ConstantString.cpp @@ -0,0 +1,123 @@ +#include "UnityPrefix.h" +#include "ConstantString.h" +#include "ConstantStringManager.h" +#include "Runtime/Threads/AtomicOps.h" + +///@TODO: Use 24 bits for refcount +///@TODO: Handle ref count overflow + +void ConstantString::create_empty () +{ + cleanup(); + m_Buffer = GetConstantStringManager().GetEmptyString(); + Assert(!owns_string()); +} + +// The header compressed the label and refcount into a 32 bits. +// Refcount lives on the 0xFFFF, label lives on the higher bits. +// We use atomic ops for thread safety on refcounting when deleting ConstantStrings. +struct AllocatedStringHeader +{ + volatile int refCountAndLabel; +}; + +MemLabelId GetLabel (const AllocatedStringHeader& header) +{ + int intLabel = (header.refCountAndLabel & (0x0000FFFF)) << 16; + ProfilerAllocationHeader* root = GET_ALLOC_HEADER(&GetConstantStringManager(), kMemString); + MemLabelId label( (MemLabelIdentifier)intLabel, root); + return label; +} + +void SetLabel (AllocatedStringHeader& header, MemLabelId label) +{ + int labelInt = label.label; + Assert(labelInt < 0xFFFF); + labelInt <<= 16; + + header.refCountAndLabel &= 0x0000FFFF; + header.refCountAndLabel |= labelInt; +} + +int GetRefCount (int refCountAndLabel) +{ + return refCountAndLabel & 0xFFFF; +} + + +inline static AllocatedStringHeader* GetHeader (const char* ptr) +{ + return reinterpret_cast<AllocatedStringHeader*> (const_cast<char*> (ptr) - sizeof(AllocatedStringHeader)); +} + +void ConstantString::operator = (const ConstantString& input) +{ + assign(input); +} + +void ConstantString::assign (const ConstantString& input) +{ + cleanup(); + m_Buffer = input.m_Buffer; + if (owns_string()) + { + AtomicIncrement(&GetHeader (get_char_ptr_fast())->refCountAndLabel); + } +} + + +void ConstantString::assign (const char* str, MemLabelId label) +{ + cleanup(); + const char* constantString = GetConstantStringManager().GetConstantString(str); + // Own Strings + if (constantString == NULL) + { + label.SetRootHeader(GET_ALLOC_HEADER(&GetConstantStringManager(), kMemString)); + size_t length = strlen(str); + + char* allocated = (char*)UNITY_MALLOC (label, length + 1 + sizeof(AllocatedStringHeader)); + char* allocatedString = allocated + sizeof(AllocatedStringHeader); + + AllocatedStringHeader& header = *GetHeader (allocatedString); + header.refCountAndLabel = 1; + SetLabel(header, label); + + Assert(GetRefCount(header.refCountAndLabel) == 1); + memcpy(allocatedString, str, length); + allocatedString[length] = 0; + + + m_Buffer = reinterpret_cast<char*> (reinterpret_cast<size_t> (allocatedString) | 1); + Assert(owns_string()); + } + else + { + m_Buffer = constantString; + Assert(!owns_string()); + } +} + +void ConstantString::cleanup () +{ + if (owns_string()) + { + AllocatedStringHeader* header = GetHeader(get_char_ptr_fast ()); + + int newRefCount = AtomicDecrement (&header->refCountAndLabel); + newRefCount = GetRefCount(newRefCount); + + if (newRefCount == 0) + { + MemLabelId label = GetLabel(*header); + UNITY_FREE(label, header); + } + } + + m_Buffer = NULL; +} + +ConstantString::~ConstantString () +{ + cleanup (); +} diff --git a/Runtime/Containers/ConstantString.h b/Runtime/Containers/ConstantString.h new file mode 100644 index 0000000..5705147 --- /dev/null +++ b/Runtime/Containers/ConstantString.h @@ -0,0 +1,82 @@ +#pragma once + +/// Constant strings use the ConstantStringManager to reuse commonly used strings. +/// Eg. GameObject names and asset names are often exactly the same. +/// When a string is not available in ConstantStringManager, it is allocated on the heap and refcounted. +/// operator = is used to assign refcounted strings. (operator = is always dirty cheap) +/// ConstantString uses only 1 ptr in the struct directly, thus the simplest case (A shared constant string) has no allocations & only 1 pointer for storage. + + +/// The ConstantStringManager is initialized at load time with all common strings and +/// stays completely constant during runtime, thus it is thread safe. ConstantString looks up all strings there first and if it is in it, it will use those. +/// Eg. when loading an asset bundle we can still reduce memory usage. +/// When serializing strings on targets that dont need to worry about backwards compatibility or asset bundle compatibility, we can simply store an index to the ConstantStringManager and look it up by index on load. +/// Thus also reducing size on disk too. + +struct ConstantString +{ + ConstantString (const char* str, MemLabelId label) + : m_Buffer (NULL) + { + assign (str, label); + } + + ConstantString () + : m_Buffer (NULL) + { + create_empty(); + } + + ConstantString (const ConstantString& input) + : m_Buffer (NULL) + { + assign(input); + } + + ~ConstantString (); + + void assign (const char* str, MemLabelId label); + void assign (const ConstantString& input); + + void operator = (const ConstantString& input); + + const char* c_str() const { return get_char_ptr_fast (); } + bool empty () const { return m_Buffer[0] == 0; } + + friend bool operator == (const ConstantString& lhs, const ConstantString& rhs) + { + if (lhs.owns_string () || rhs.owns_string()) + return strcmp(lhs.c_str(), rhs.c_str()) == 0; + else + return lhs.m_Buffer == rhs.m_Buffer; + } + + friend bool operator == (const ConstantString& lhs, const char* rhs) + { + return strcmp(lhs.c_str(), rhs) == 0; + } + + friend bool operator == (const char* rhs, const ConstantString& lhs) + { + return strcmp(lhs.c_str(), rhs) == 0; + } + + friend bool operator != (const ConstantString& lhs, const char* rhs) + { + return strcmp(lhs.c_str(), rhs) != 0; + } + + friend bool operator != (const char* rhs, const ConstantString& lhs) + { + return strcmp(lhs.c_str(), rhs) != 0; + } + + private: + + inline bool owns_string () const { return reinterpret_cast<size_t> (m_Buffer) & 1; } + inline const char* get_char_ptr_fast () const { return reinterpret_cast<const char*> (reinterpret_cast<size_t> (m_Buffer) & ~1); } + void cleanup (); + void create_empty (); + + const char* m_Buffer; +};
\ No newline at end of file diff --git a/Runtime/Containers/ConstantStringManager.cpp b/Runtime/Containers/ConstantStringManager.cpp new file mode 100644 index 0000000..157d98d --- /dev/null +++ b/Runtime/Containers/ConstantStringManager.cpp @@ -0,0 +1,91 @@ +#include "UnityPrefix.h" +#include "ConstantStringManager.h" +#include "Runtime/Utilities/InitializeAndCleanup.h" + +static ConstantStringManager* gConstantStringManager = NULL; + +void ConstantStringManager::ProfileCommonString (const char* str) +{ +#if PROFILE_COMMON_STRINGS + SET_ALLOC_OWNER(this); + m_CommonStringMutex.Lock(); + m_CommonStringCounter[str]++; + m_CommonStringMutex.Unlock(); +#endif +} + +void ConstantStringManager::DumpCommonStrings () +{ +#if PROFILE_COMMON_STRINGS + for(CommonStringCounter::iterator i=m_CommonStringCounter.begin();i != m_CommonStringCounter.end();++i) + { + printf_console("%d\t -> '%s'\n", i->second, i->first.c_str()); + } +#endif +} + +ConstantStringManager::ConstantStringManager () +{ + AddConstantString(""); + AddConstantString("TextMesh"); +} + +ConstantStringManager::~ConstantStringManager () +{ + for (int i=0;i<m_Strings.size();i++) + { + UNITY_FREE(kMemStaticString, (void*)m_Strings[i]); + } +} + +const char* ConstantStringManager::GetEmptyString() +{ + return m_Strings[0]; +} + +const char* ConstantStringManager::GetConstantString(const char* str) +{ + ProfileCommonString (str); + + for (int i=0;i<m_Strings.size();i++) + { + if (strcmp(m_Strings[i], str) == 0) + return m_Strings[i]; + } + + return NULL; +} + +void ConstantStringManager::AddConstantString (const char* string ) +{ + SET_ALLOC_OWNER(gConstantStringManager); + int length = strlen(string); + char* newString = (char*)UNITY_MALLOC(kMemStaticString, length+1); + memcpy(newString, string, length+1); + m_Strings.push_back(newString); +} + +void ConstantStringManager::AddConstantStrings (const char** strings, size_t size ) +{ + m_Strings.resize_uninitialized(size + m_Strings.size()); + for (int i=0;i<size;++i) + AddConstantString(strings[i]); +} + +void ConstantStringManager::StaticInitialize () +{ + gConstantStringManager = UNITY_NEW_AS_ROOT(ConstantStringManager, kMemString, "SharedStrings", ""); +} + +void ConstantStringManager::StaticCleanup () +{ + // gConstantStringManager->DumpCommonStrings (); + UNITY_DELETE(gConstantStringManager, kMemString); + gConstantStringManager = NULL; +} +static RegisterRuntimeInitializeAndCleanup s_ConstantStringManagerCallbacks(ConstantStringManager::StaticInitialize, ConstantStringManager::StaticCleanup); + +ConstantStringManager& GetConstantStringManager () +{ + return *gConstantStringManager; +} diff --git a/Runtime/Containers/ConstantStringManager.h b/Runtime/Containers/ConstantStringManager.h new file mode 100644 index 0000000..76354ed --- /dev/null +++ b/Runtime/Containers/ConstantStringManager.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Runtime/Utilities/dynamic_array.h" +#include "Runtime/Threads/Mutex.h" + +class ConstantStringManager +{ + #define PROFILE_COMMON_STRINGS !UNITY_RELEASE + + #if PROFILE_COMMON_STRINGS + typedef std::map<std::string, int> CommonStringCounter; + CommonStringCounter m_CommonStringCounter; + Mutex m_CommonStringMutex; + #endif + + dynamic_array<const char*> m_Strings; + void ProfileCommonString (const char* str); + + public: + + ConstantStringManager (); + ~ConstantStringManager (); + + void AddConstantStrings (const char** strings, size_t size ); + void AddConstantString (const char* strings ); + + const char* GetConstantString(const char* str); + const char* GetEmptyString(); + + // Init + static void StaticInitialize (); + static void StaticCleanup (); + + void DumpCommonStrings (); + +}; + +ConstantStringManager& GetConstantStringManager (); diff --git a/Runtime/Containers/ConstantStringSerialization.h b/Runtime/Containers/ConstantStringSerialization.h new file mode 100644 index 0000000..47fd4b7 --- /dev/null +++ b/Runtime/Containers/ConstantStringSerialization.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Runtime/Allocator/STLAllocator.h" +#include "Runtime/Serialize/SerializeTraits.h" + +template<class TransferFunction> +inline void TransferConstantString (ConstantString& constantString, const char* name, TransferMetaFlags transferFlags, MemLabelId label, TransferFunction& transfer) +{ + //@TODO: Make this string use temp data + UnityStr tempStr; + if (transfer.IsWriting()) + tempStr = constantString.c_str(); + + transfer.Transfer(tempStr, name, transferFlags); + + if (transfer.IsReading()) + constantString.assign(tempStr.c_str(), label); +} diff --git a/Runtime/Containers/ExtendedRingbuffer.h b/Runtime/Containers/ExtendedRingbuffer.h new file mode 100644 index 0000000..0eb6d7b --- /dev/null +++ b/Runtime/Containers/ExtendedRingbuffer.h @@ -0,0 +1,146 @@ +#ifndef RUNTIME_CONTAINERS_RINGBUFFERS_H +#define RUNTIME_CONTAINERS_RINGBUFFERS_H + +#include "GrowingRingbuffer.h" +#include "Ringbuffer.h" +#include "TransactionalRingbuffer.h" +#include "Runtime/Threads/Semaphore.h" +#include "Runtime/Utilities/NonCopyable.h" + +#if SUPPORT_THREADS +namespace RingbufferTemplates +{ + // ------------------------------------------------------------------------ + // Support base classes used to avoid casting to 'normal' ringbuffer impl + // ------------------------------------------------------------------------ + template <class Ringbuffer> + class NonCastable : protected Ringbuffer + { + public: + using Ringbuffer::WritePtr; + using Ringbuffer::WritePtrUpdate; + using Ringbuffer::ReadPtr; + using Ringbuffer::ReadPtrUpdate; + using Ringbuffer::GetSize; + using Ringbuffer::GetFreeSize; + using Ringbuffer::GetAllocatedSize; + using Ringbuffer::GetAvailableSize; + using Ringbuffer::Reset; + + protected: + NonCastable(MemLabelId label, UInt32 size) : Ringbuffer(label, size) {} + }; + + // ------------------------------------------------------------------------ + // Adds support for notifications whenever data or free space is available + // ------------------------------------------------------------------------ + template <class Ringbuffer> + class AbstractNotificationSupport : public NonCastable<Ringbuffer>, public NonCopyable + { + public: + AbstractNotificationSupport(MemLabelId label, UInt32 size) : NonCastable<Ringbuffer>(label, size) {} + + void ReleaseBlockedThreads(bool indefinitely = false) + { + m_FreeSemaphore.Suspend(indefinitely); + m_AvailableSemaphore.Suspend(indefinitely); + } + + void BlockUntilFree() + { + if (Ringbuffer::GetFreeSize()) + return; + + m_FreeSemaphore.Resume(true); + if (Ringbuffer::GetFreeSize() == 0) + m_FreeSemaphore.WaitForSignal(); + m_FreeSemaphore.Suspend(); + } + + void BlockUntilAvailable() + { + if (Ringbuffer::GetAvailableSize()) + return; + + m_AvailableSemaphore.Resume(true); + if (Ringbuffer::GetAvailableSize() == 0) + m_AvailableSemaphore.WaitForSignal(); + m_AvailableSemaphore.Suspend(); + } + + protected: + SuspendableSemaphore m_AvailableSemaphore; + SuspendableSemaphore m_FreeSemaphore; + }; + + template <class Ringbuffer> + class RNotificationSupport : public AbstractNotificationSupport<Ringbuffer> + { + public: + RNotificationSupport(MemLabelId label, UInt32 size) : AbstractNotificationSupport<Ringbuffer>(label, size) {} + + void WritePtrUpdate(void* writePtr, UInt32 nBytesWritten) + { + Ringbuffer::WritePtrUpdate(writePtr, nBytesWritten); + AbstractNotificationSupport<Ringbuffer>::m_AvailableSemaphore.Signal(); + } + + void ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead) + { + Ringbuffer::ReadPtrUpdate(readPtr, nBytesRead); + AbstractNotificationSupport<Ringbuffer>::m_FreeSemaphore.Signal(); + } + }; + + + template <class TransactionalRingbuffer> + class TRNotificationSupport : public AbstractNotificationSupport<TransactionalRingbuffer> + { + public: + TRNotificationSupport(MemLabelId label, UInt32 size) : AbstractNotificationSupport<TransactionalRingbuffer>(label, size) {} + + using AbstractNotificationSupport<TransactionalRingbuffer>::ReadPtrReset; + using AbstractNotificationSupport<TransactionalRingbuffer>::WritePtrReset; + + void WritePtrCommit() + { + AbstractNotificationSupport<TransactionalRingbuffer>::WritePtrCommit(); + AbstractNotificationSupport<TransactionalRingbuffer>::m_AvailableSemaphore.Signal(); + } + + void ReadPtrCommit() + { + AbstractNotificationSupport<TransactionalRingbuffer>::ReadPtrCommit(); + AbstractNotificationSupport<TransactionalRingbuffer>::m_FreeSemaphore.Signal(); + } + }; + +} + +typedef RingbufferTemplates::RNotificationSupport<Ringbuffer> ExtendedRingbuffer; +typedef RingbufferTemplates::RNotificationSupport<GrowingRingbuffer> ExtendedGrowingRingbuffer; +typedef RingbufferTemplates::TRNotificationSupport<TransactionalRingbuffer> ExtendedTransactionalRingbuffer; + +#else // no thread support + +namespace RingbufferTemplates +{ + template <class Ringbuffer> + class NotificationSupport : public Ringbuffer, public NonCopyable + { + public: + NotificationSupport(MemLabelId label, UInt32 size) : Ringbuffer(label, size) {} + + void ReleaseBlockedThreads(bool indefinitely = false) {} + void BlockUntilFree() {} + void BlockUntilAvailable() {} + }; +} + +typedef RingbufferTemplates::NotificationSupport<Ringbuffer> ExtendedRingbuffer; +typedef RingbufferTemplates::NotificationSupport<GrowingRingbuffer> ExtendedGrowingRingbuffer; +typedef RingbufferTemplates::NotificationSupport<TransactionalRingbuffer> ExtendedTransactionalRingbuffer; + +#endif + +#endif //RUNTIME_CONTAINERS_RINGBUFFERS_H diff --git a/Runtime/Containers/GrowingRingbuffer.h b/Runtime/Containers/GrowingRingbuffer.h new file mode 100644 index 0000000..c4af135 --- /dev/null +++ b/Runtime/Containers/GrowingRingbuffer.h @@ -0,0 +1,121 @@ +#ifndef RUNTIME_CONTAINERS_GROWINGRINGBUFFER_H +#define RUNTIME_CONTAINERS_GROWINGRINGBUFFER_H + +#include "Ringbuffer.h" +#include "Runtime/Allocator/MemoryMacros.h" +#include "Runtime/Threads/AtomicOps.h" + +// -------------------------------------------------------------------- +// A Ringbuffer with the ability to grow +// Concurrently supports one consumer and one producer +// +// The functions in this class should be synchronized with +// Ringbuffer to fit Ringbuffer template functions. +// -------------------------------------------------------------------- +class GrowingRingbuffer +{ +public: + GrowingRingbuffer(MemLabelId label, UInt32 maxSize, UInt32 initialSize = 1024) + : m_Label(label) + , m_MinSize(initialSize) + , m_MaxSize(maxSize) + { + InitializeBuffers(); + } + + ~GrowingRingbuffer() { DeleteBuffers(); } + + void* WritePtr(UInt32* nBytes) const; + void WritePtrUpdate(void* writePtr, UInt32 nBytesWritten); + + const void* ReadPtr(UInt32* nBytes) const; + void ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead); + + UInt32 GetAllocatedSize() const { return m_AllocatedSize; } + UInt32 GetAvailableSize() const { return m_AvailableSize; } + UInt32 GetFreeSize() const { return m_MaxSize - m_AvailableSize; } + UInt32 GetSize() const { return m_MaxSize; } + + void Reset(); + +private: + void InitializeBuffers(); + void DeleteBuffers(); + + struct RingbufferLink:public Ringbuffer + { + RingbufferLink(MemLabelId label, UInt32 size) : Ringbuffer(label, size) , next(NULL) { } + RingbufferLink* volatile next; + }; + + UInt32 m_MaxSize; + UInt32 m_MinSize; + MemLabelId m_Label; + volatile UInt32 m_AllocatedSize; + volatile UInt32 m_AvailableSize; + RingbufferLink* volatile m_ReadBuffer; + RingbufferLink* volatile m_WriteBuffer; +}; + +inline void GrowingRingbuffer::InitializeBuffers() +{ + m_WriteBuffer = m_ReadBuffer = new RingbufferLink(m_Label, m_MinSize); + m_AllocatedSize = m_ReadBuffer->GetSize(); + m_AvailableSize = 0; +} + +inline void GrowingRingbuffer::DeleteBuffers() +{ + RingbufferLink* buffer = m_ReadBuffer; + while (buffer) + { + RingbufferLink* current = buffer; + buffer = current->next; + delete current; + } +} + +inline void GrowingRingbuffer::Reset() +{ + DeleteBuffers(); + InitializeBuffers(); +} + +FORCE_INLINE void* GrowingRingbuffer::WritePtr(UInt32* nBytes) const +{ + *nBytes = std::min(*nBytes, GetFreeSize()); + return m_WriteBuffer->WritePtr(nBytes); +} + +FORCE_INLINE const void* GrowingRingbuffer::ReadPtr(UInt32* nBytes) const +{ + return m_ReadBuffer->ReadPtr(nBytes); +} + +FORCE_INLINE void GrowingRingbuffer::WritePtrUpdate(void* writePtr, UInt32 nBytesWritten) +{ + m_WriteBuffer->WritePtrUpdate(writePtr, nBytesWritten); + AtomicAdd((volatile int*) &m_AvailableSize, nBytesWritten); + if (m_WriteBuffer->GetFreeSize() == 0 && GetFreeSize() > 0) + { + RingbufferLink* emptyBuffer = new RingbufferLink(m_Label, m_AllocatedSize); + m_WriteBuffer->next = emptyBuffer; + m_WriteBuffer = emptyBuffer; + AtomicAdd((volatile int*) &m_AllocatedSize, emptyBuffer->GetSize()); + } +} + +FORCE_INLINE void GrowingRingbuffer::ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead) +{ + m_ReadBuffer->ReadPtrUpdate(readPtr, nBytesRead); + AtomicSub((volatile int*) &m_AvailableSize, nBytesRead); + if (m_ReadBuffer->next && m_ReadBuffer->GetAvailableSize() == 0) + { + AtomicSub((volatile int*) &m_AllocatedSize, m_ReadBuffer->GetSize()); + RingbufferLink* emptyBuffer = m_ReadBuffer; + m_ReadBuffer = m_ReadBuffer->next; + delete emptyBuffer; + } +} + +#endif // RUNTIME_CONTAINERS_GROWINGRINGBUFFER_H diff --git a/Runtime/Containers/Ringbuffer.h b/Runtime/Containers/Ringbuffer.h new file mode 100644 index 0000000..7a34b5e --- /dev/null +++ b/Runtime/Containers/Ringbuffer.h @@ -0,0 +1,94 @@ +#ifndef RUNTIME_CONTAINERS_RINGBUFFER_H +#define RUNTIME_CONTAINERS_RINGBUFFER_H + +#include "Runtime/Allocator/MemoryMacros.h" +#include "Runtime/Allocator/tlsf/tlsfbits.h" +#include "Runtime/Threads/AtomicOps.h" + +// -------------------------------------------------------------------- +// Ringbuffer +// Concurrently supports one consumer and one producer +// -------------------------------------------------------------------- +class Ringbuffer +{ +public: + Ringbuffer(void* memory, UInt32 size); + Ringbuffer(MemLabelId label, UInt32 size); + + ~Ringbuffer(); + + void* WritePtr(UInt32* nBytes) const { return Ptr(m_Put, GetFreeSize(), nBytes); } + void WritePtrUpdate(void* writePtr, UInt32 nBytesWritten); + + const void* ReadPtr(UInt32* nBytes) const { return Ptr(m_Get, GetAvailableSize(), nBytes); } + void ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead); + + UInt32 GetSize() const { return m_Size; } + UInt32 GetFreeSize() const { return m_Size - GetAvailableSize(); } + UInt32 GetAvailableSize() const { return m_Put - m_Get; } + UInt32 GetAllocatedSize() const { return GetAvailableSize(); } + + void Reset() { m_Get = m_Put = 0; } + +protected: + void* Ptr(UInt32 position, UInt32 availableBytes, UInt32* nBytes) const; + +protected: + char* m_Buffer; + bool m_OwnMemory; + MemLabelId m_OwnMemoryLabel; + UInt32 m_Size; + volatile UInt32 m_Get; + volatile UInt32 m_Put; +}; + +// --------------------------------------------------------------------------- +inline Ringbuffer::Ringbuffer(void* memory, UInt32 size) +: m_Get(0) +, m_Put(0) +{ + m_Size = 1 << tlsf_fls(size); // use the biggest 2^n size that fits + m_Buffer = reinterpret_cast<char*>(memory); + m_OwnMemory = false; +} + +inline Ringbuffer::Ringbuffer(MemLabelId label, UInt32 size) +: m_Get(0) +, m_Put(0) +{ + AssertBreak(size >> 31 == 0); + m_Size = 1 << tlsf_fls(size*2 - 1); // make sure we have _at least_ 'size' bytes + m_Buffer = reinterpret_cast<char*>(UNITY_MALLOC(label, m_Size)); + m_OwnMemory = true; + m_OwnMemoryLabel = label; +} + +inline Ringbuffer::~Ringbuffer() +{ + if (m_OwnMemory) + UNITY_FREE(m_OwnMemoryLabel, m_Buffer); +} + +FORCE_INLINE void Ringbuffer::WritePtrUpdate(void* writePtr, UInt32 nBytesWritten) +{ + int result = AtomicAdd((volatile int*)&m_Put, nBytesWritten); + AssertBreak(writePtr == &m_Buffer[(result - nBytesWritten) & (m_Size - 1)]); +} + +FORCE_INLINE void Ringbuffer::ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead) +{ + int result = AtomicAdd((volatile int*)&m_Get, nBytesRead); + AssertBreak(readPtr == &m_Buffer[(result - nBytesRead) & (m_Size - 1)]); +} + +FORCE_INLINE void* Ringbuffer::Ptr(UInt32 position, UInt32 bytesAvailable, UInt32* nBytesPtr) const +{ + UInt32& nBytes = *nBytesPtr; + UInt32 index = position & (m_Size - 1); + UInt32 spaceUntilEnd = m_Size - index; + UInt32 continousBytesAvailable = std::min(bytesAvailable, spaceUntilEnd); + nBytes = std::min(nBytes, continousBytesAvailable); + return &m_Buffer[index]; +} + +#endif // RUNTIME_CONTAINERS_RINGBUFFER_H diff --git a/Runtime/Containers/TransactionalRingbuffer.h b/Runtime/Containers/TransactionalRingbuffer.h new file mode 100644 index 0000000..503b963 --- /dev/null +++ b/Runtime/Containers/TransactionalRingbuffer.h @@ -0,0 +1,37 @@ +#ifndef RUNTIME_CONTAINERS_TRANSACTIONALRINGBUFFER_H +#define RUNTIME_CONTAINERS_TRANSACTIONALRINGBUFFER_H + +#include "Ringbuffer.h" + +// -------------------------------------------------------------------- +// Transactional Ringbuffer (commit, reset) +// Concurrently supports one consumer and one producer +// -------------------------------------------------------------------- +class TransactionalRingbuffer : private Ringbuffer +{ +public: + TransactionalRingbuffer(void* memory, UInt32 size) : Ringbuffer(memory, size), m_GetBarrier(m_Put), m_PutBarrier(m_Get) {} + TransactionalRingbuffer(MemLabelId label, UInt32 size) : Ringbuffer(label, size) , m_GetBarrier(m_Put), m_PutBarrier(m_Get) {} + + using Ringbuffer::WritePtrUpdate; + using Ringbuffer::ReadPtrUpdate; + using Ringbuffer::GetSize; + + void* WritePtr(UInt32* nBytes) const { return Ptr(m_Put, GetFreeSize(), nBytes); } + void WritePtrCommit() { m_GetBarrier = m_Put; } + void WritePtrReset() { m_Put = m_GetBarrier; } + + const void* ReadPtr(UInt32* nBytes) const { return Ptr(m_Get, GetAvailableSize(), nBytes); } + void ReadPtrCommit() { m_PutBarrier = m_Get; } + void ReadPtrReset() { m_Get = m_PutBarrier; } + + UInt32 GetFreeSize() const { return m_Size - (m_Put - m_PutBarrier); } + UInt32 GetAvailableSize() const { return m_GetBarrier - m_Get; } + + void Reset() { Ringbuffer::Reset(); m_GetBarrier = m_PutBarrier = 0; } + +private: + volatile UInt32 m_GetBarrier; + volatile UInt32 m_PutBarrier; +}; +#endif // RUNTIME_CONTAINERS_RINGBUFFER_H
\ No newline at end of file |