diff --git a/src/openrct2/paint/Paint.cpp b/src/openrct2/paint/Paint.cpp index 9ac9ef24a3..f30d611c87 100644 --- a/src/openrct2/paint/Paint.cpp +++ b/src/openrct2/paint/Paint.cpp @@ -1031,134 +1031,3 @@ void PaintDrawMoneyStructs(DrawPixelInfo& dpi, PaintStringStruct* ps) FontStyle::Medium); } while ((ps = ps->NextEntry) != nullptr); } - -PaintEntryPool::Chain::Chain(PaintEntryPool* pool) - : Pool(pool) -{ -} - -PaintEntryPool::Chain::Chain(Chain&& chain) -{ - *this = std::move(chain); -} - -PaintEntryPool::Chain::~Chain() -{ - Clear(); -} - -PaintEntryPool::Chain& PaintEntryPool::Chain::operator=(Chain&& chain) noexcept -{ - Clear(); - Pool = chain.Pool; - Head = chain.Head; - Current = chain.Current; - chain.Pool = nullptr; - chain.Head = nullptr; - chain.Current = nullptr; - return *this; -} - -PaintEntry* PaintEntryPool::Chain::Allocate() -{ - if (Pool == nullptr) - { - return nullptr; - } - - if (Current == nullptr) - { - assert(Head == nullptr); - Head = Pool->AllocateNode(); - if (Head == nullptr) - { - // Unable to allocate any more nodes - return nullptr; - } - Current = Head; - } - else if (Current->Count >= NodeSize) - { - // We need another node - Current->Next = Pool->AllocateNode(); - if (Current->Next == nullptr) - { - // Unable to allocate any more nodes - return nullptr; - } - Current = Current->Next; - } - - assert(Current->Count < NodeSize); - return &Current->PaintStructs[Current->Count++]; -} - -void PaintEntryPool::Chain::Clear() -{ - if (Pool != nullptr) - { - Pool->FreeNodes(Head); - Head = nullptr; - Current = nullptr; - } - assert(Head == nullptr); - assert(Current == nullptr); -} - -size_t PaintEntryPool::Chain::GetCount() const -{ - size_t count = 0; - auto current = Head; - while (current != nullptr) - { - count += current->Count; - current = current->Next; - } - return count; -} - -PaintEntryPool::~PaintEntryPool() -{ - for (auto node : _available) - { - delete node; - } - _available.clear(); -} - -PaintEntryPool::Node* PaintEntryPool::AllocateNode() -{ - std::lock_guard lock(_mutex); - - PaintEntryPool::Node* result; - if (_available.size() > 0) - { - result = _available.back(); - _available.pop_back(); - } - else - { - result = new (std::nothrow) PaintEntryPool::Node(); - } - return result; -} - -PaintEntryPool::Chain PaintEntryPool::Create() -{ - return PaintEntryPool::Chain(this); -} - -void PaintEntryPool::FreeNodes(PaintEntryPool::Node* head) -{ - std::lock_guard lock(_mutex); - - auto node = head; - while (node != nullptr) - { - auto next = node->Next; - node->Next = nullptr; - node->Count = 0; - _available.push_back(node); - node = next; - } -} diff --git a/src/openrct2/paint/Paint.h b/src/openrct2/paint/Paint.h index a52b751a71..c3ebcd6068 100644 --- a/src/openrct2/paint/Paint.h +++ b/src/openrct2/paint/Paint.h @@ -18,6 +18,8 @@ #include "tile_element/Paint.Tunnel.h" #include +#include +#include #include struct EntityBase; @@ -122,56 +124,6 @@ struct SupportHeight // the quadrant index is based on the x and y components combined. static constexpr int32_t MaxPaintQuadrants = kMaximumMapSizeTechnical * 2; -/** - * A pool of PaintEntry instances that can be rented out. - * The internal implementation uses an unrolled linked list so that each - * paint session can quickly allocate a new paint entry until it requires - * another node / block of paint entries. Only the node allocation needs to - * be thread safe. - */ -class PaintEntryPool -{ - static constexpr size_t NodeSize = 512; - -public: - struct Node - { - Node* Next{}; - size_t Count{}; - PaintEntry PaintStructs[NodeSize]{}; - }; - - struct Chain - { - PaintEntryPool* Pool{}; - Node* Head{}; - Node* Current{}; - - Chain() = default; - Chain(PaintEntryPool* pool); - Chain(Chain&& chain); - ~Chain(); - - Chain& operator=(Chain&& chain) noexcept; - - PaintEntry* Allocate(); - void Clear(); - size_t GetCount() const; - }; - -private: - std::vector _available; - std::mutex _mutex; - - Node* AllocateNode(); - -public: - ~PaintEntryPool(); - - Chain Create(); - void FreeNodes(Node* head); -}; - struct PaintSessionCore { PaintStruct* PaintHead; @@ -207,51 +159,71 @@ struct PaintSessionCore ViewportInteractionItem InteractionType; }; +struct PaintNodeStorage +{ + // 1024 is typically enough to cover the column, after its full it will use dynamicPaintEntries. + sfl::static_vector fixedPaintEntries; + + // This has to be wrapped in optional as it allocates memory before it is used. + std::optional> dynamicPaintEntries; + + PaintEntry* allocate() + { + if (!fixedPaintEntries.full()) + { + return &fixedPaintEntries.emplace_back(); + } + + if (!dynamicPaintEntries.has_value()) + { + dynamicPaintEntries.emplace(); + } + + return &dynamicPaintEntries->emplace_back(); + } + + void clear() + { + fixedPaintEntries.clear(); + dynamicPaintEntries.reset(); + } +}; + struct PaintSession : public PaintSessionCore { DrawPixelInfo DPI; - PaintEntryPool::Chain PaintEntryChain; + PaintNodeStorage paintEntries; PaintStruct* AllocateNormalPaintEntry() noexcept { - auto* entry = PaintEntryChain.Allocate(); - if (entry != nullptr) - { - LastPS = entry->AsBasic(); - return LastPS; - } - return nullptr; + auto* entry = paintEntries.allocate(); + LastPS = entry->AsBasic(); + return LastPS; } AttachedPaintStruct* AllocateAttachedPaintEntry() noexcept { - auto* entry = PaintEntryChain.Allocate(); - if (entry != nullptr) - { - LastAttachedPS = entry->AsAttached(); - return LastAttachedPS; - } - return nullptr; + auto* entry = paintEntries.allocate(); + LastAttachedPS = entry->AsAttached(); + return LastAttachedPS; } PaintStringStruct* AllocateStringPaintEntry() noexcept { - auto* entry = PaintEntryChain.Allocate(); - if (entry != nullptr) + auto* entry = paintEntries.allocate(); + + auto* string = entry->AsString(); + if (LastPSString == nullptr) { - auto* string = entry->AsString(); - if (LastPSString == nullptr) - { - PSStringHead = string; - } - else - { - LastPSString->NextEntry = string; - } - LastPSString = string; - return LastPSString; + PSStringHead = string; } - return nullptr; + else + { + LastPSString->NextEntry = string; + } + + LastPSString = string; + return LastPSString; } }; diff --git a/src/openrct2/paint/Painter.cpp b/src/openrct2/paint/Painter.cpp index 8d2091eea6..fbac8209f6 100644 --- a/src/openrct2/paint/Painter.cpp +++ b/src/openrct2/paint/Painter.cpp @@ -166,15 +166,13 @@ PaintSession* Painter::CreateSession(DrawPixelInfo& dpi, uint32_t viewFlags, uin else { // Create new one in pool. - _paintSessionPool.emplace_back(std::make_unique()); - session = _paintSessionPool.back().get(); + session = &_paintSessionPool.emplace_back(); } session->DPI = dpi; session->ViewFlags = viewFlags; session->QuadrantBackIndex = std::numeric_limits::max(); session->QuadrantFrontIndex = 0; - session->PaintEntryChain = _paintStructPool.Create(); session->Flags = 0; session->CurrentRotation = rotation; @@ -197,15 +195,13 @@ void Painter::ReleaseSession(PaintSession* session) { PROFILED_FUNCTION(); - session->PaintEntryChain.Clear(); + session->paintEntries.clear(); + _freePaintSessions.push_back(session); } Painter::~Painter() { - for (auto&& session : _paintSessionPool) - { - ReleaseSession(session.get()); - } + _paintSessionPool.clear(); _paintSessionPool.clear(); } diff --git a/src/openrct2/paint/Painter.h b/src/openrct2/paint/Painter.h index 7d2d896ced..4bbbb0dc17 100644 --- a/src/openrct2/paint/Painter.h +++ b/src/openrct2/paint/Painter.h @@ -13,6 +13,7 @@ #include #include +#include #include struct DrawPixelInfo; @@ -35,9 +36,8 @@ namespace OpenRCT2 { private: std::shared_ptr const _uiContext; - std::vector> _paintSessionPool; + sfl::segmented_vector _paintSessionPool; std::vector _freePaintSessions; - PaintEntryPool _paintStructPool; time_t _lastSecond = 0; int32_t _currentFPS = 0; int32_t _frames = 0;