diff --git a/Source/Engine/Renderer/RenderListBuffer.h b/Source/Engine/Renderer/RenderListBuffer.h index 01af1677c..ed2d0b41a 100644 --- a/Source/Engine/Renderer/RenderListBuffer.h +++ b/Source/Engine/Renderer/RenderListBuffer.h @@ -23,7 +23,8 @@ public: private: volatile int64 _count; volatile int64 _capacity; - volatile int64 _threads = 0; + volatile int64 _threadsAdding = 0; + volatile int64 _threadsResizing = 0; AllocationData _allocation; CriticalSection _locker; @@ -298,13 +299,11 @@ public: /// /// The item to add. /// Index of the added element. - int32 Add(const T& item) + FORCE_INLINE int32 Add(const T& item) { const int32 index = AddOne(); - auto ptr = _allocation.Get(); Memory::ConstructItems(_allocation.Get() + index, &item, 1); - ASSERT(ptr == _allocation.Get()); - Platform::InterlockedDecrement(&_threads); + Platform::InterlockedDecrement(&_threadsAdding); return index; } @@ -313,25 +312,47 @@ public: /// /// The item to add. /// Index of the added element. - int32 Add(T&& item) + FORCE_INLINE int32 Add(T&& item) { const int32 index = AddOne(); - auto ptr = _allocation.Get(); Memory::MoveItems(_allocation.Get() + index, &item, 1); - ASSERT(ptr == _allocation.Get()); - Platform::InterlockedDecrement(&_threads); + Platform::InterlockedDecrement(&_threadsAdding); return index; } private: - FORCE_INLINE int32 AddOne() + int32 AddOne() { - Platform::InterlockedIncrement(&_threads); - const int32 count = (int32)Platform::AtomicRead(&_count); - const int32 capacity = (int32)Platform::AtomicRead(&_capacity); + Platform::InterlockedIncrement(&_threadsAdding); + int32 count = (int32)Platform::AtomicRead(&_count); + int32 capacity = (int32)Platform::AtomicRead(&_capacity); const int32 minCapacity = GetMinCapacity(count); - if (minCapacity > capacity) - EnsureCapacity(minCapacity); + if (minCapacity > capacity || Platform::AtomicRead(&_threadsResizing)) // Resize if not enough space or someone else is already doing it (don't add mid-resizing) + { + // Move from adding to resizing + Platform::InterlockedIncrement(&_threadsResizing); + Platform::InterlockedDecrement(&_threadsAdding); + + // Wait for all threads to stop adding items before resizing can happen + while (Platform::AtomicRead(&_threadsAdding)) + Platform::Sleep(0); + + // Thread-safe resizing + _locker.Lock(); + capacity = (int32)Platform::AtomicRead(&_capacity); + if (capacity < minCapacity) + { + capacity = _allocation.CalculateCapacityGrow(capacity, minCapacity); + count = (int32)Platform::AtomicRead(&_count); + _allocation.Relocate(capacity, count, count); + Platform::AtomicStore(&_capacity, capacity); + } + _locker.Unlock(); + + // Move from resizing to adding + Platform::InterlockedIncrement(&_threadsAdding); + Platform::InterlockedDecrement(&_threadsResizing); + } return (int32)Platform::InterlockedIncrement(&_count) - 1; } @@ -342,7 +363,7 @@ private: int32 capacity = count + slack; { // Round up to the next power of 2 and multiply by 2 - capacity++; + capacity--; capacity |= capacity >> 1; capacity |= capacity >> 2; capacity |= capacity >> 4; @@ -351,6 +372,5 @@ private: capacity = (capacity + 1) * 2; } return capacity; - return count + slack; } };