diff --git a/src/openrct2/paint/Paint.cpp b/src/openrct2/paint/Paint.cpp index 4b466ffab0..02c4d97c4f 100644 --- a/src/openrct2/paint/Paint.cpp +++ b/src/openrct2/paint/Paint.cpp @@ -59,7 +59,6 @@ bool gPaintBoundingBoxes; bool gPaintBlockedTiles; bool gPaintStableSort; -static void PaintAttachedPS(DrawPixelInfo& dpi, PaintStruct* ps, uint32_t viewFlags); static void PaintPSImageWithBoundingBoxes(PaintSession& session, PaintStruct* ps, ImageId imageId, int32_t x, int32_t y); static ImageId PaintPSColourifyImage(const PaintStruct* ps, ImageId imageId, uint32_t viewFlags); @@ -634,7 +633,7 @@ void PaintSessionArrange(PaintSessionCore& session) return _paintArrangeFuncsLegacy[session.CurrentRotation](session); } -static void PaintDrawStruct(PaintSession& session, PaintStruct* ps) +static inline void PaintDrawStruct(PaintSession& session, PaintStruct* ps) { auto screenPos = ps->ScreenPos; if (ps->InteractionItem == ViewportInteractionItem::Entity) @@ -660,15 +659,6 @@ static void PaintDrawStruct(PaintSession& session, PaintStruct* ps) { GfxDrawSprite(session.DPI, imageId, screenPos); } - - if (ps->Children != nullptr) - { - PaintDrawStruct(session, ps->Children); - } - else - { - PaintAttachedPS(session.DPI, ps, session.ViewFlags); - } } /** @@ -681,30 +671,29 @@ void PaintDrawStructs(PaintSession& session) for (PaintStruct* ps = session.PaintHead; ps != nullptr; ps = ps->NextQuadrantEntry) { + // Paint parent node. PaintDrawStruct(session, ps); - } -} -/** - * - * rct2: 0x00688596 - * Part of 0x688485 - */ -static void PaintAttachedPS(DrawPixelInfo& dpi, PaintStruct* ps, uint32_t viewFlags) -{ - AttachedPaintStruct* attached_ps = ps->Attached; - for (; attached_ps != nullptr; attached_ps = attached_ps->NextEntry) - { - const auto screenCoords = ps->ScreenPos + attached_ps->RelativePos; - - auto imageId = PaintPSColourifyImage(ps, attached_ps->image_id, viewFlags); - if (attached_ps->IsMasked) + // Paint children. + for (auto* psChild = ps->Children; psChild != nullptr; psChild = psChild->NextQuadrantEntry) { - GfxDrawSpriteRawMasked(dpi, screenCoords, imageId, attached_ps->ColourImageId); + PaintDrawStruct(session, psChild); } - else + + // Paint attached. + for (auto* psAttached = ps->Attached; psAttached != nullptr; psAttached = psAttached->NextEntry) { - GfxDrawSprite(dpi, imageId, screenCoords); + const auto screenCoords = ps->ScreenPos + psAttached->RelativePos; + + auto imageId = PaintPSColourifyImage(ps, psAttached->image_id, session.ViewFlags); + if (psAttached->IsMasked) + { + GfxDrawSpriteRawMasked(session.DPI, screenCoords, imageId, psAttached->ColourImageId); + } + else + { + GfxDrawSprite(session.DPI, imageId, screenCoords); + } } } } @@ -1041,134 +1030,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..4e663e9288 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,12 @@ 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(); } 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;