mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-17 03:53:07 +01:00
Use unrolled linked list for paint entries
This commit is contained in:
@@ -968,3 +968,121 @@ void PaintDrawMoneyStructs(rct_drawpixelinfo* dpi, paint_string_struct* ps)
|
||||
FontSpriteBase::MEDIUM);
|
||||
} while ((ps = ps->next) != 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
|
||||
{
|
||||
Pool = chain.Pool;
|
||||
Head = chain.Head;
|
||||
Current = chain.Current;
|
||||
chain.Pool = nullptr;
|
||||
chain.Head = nullptr;
|
||||
chain.Current = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
paint_entry* 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);
|
||||
}
|
||||
|
||||
PaintEntryPool::~PaintEntryPool()
|
||||
{
|
||||
for (auto node : _available)
|
||||
{
|
||||
delete node;
|
||||
}
|
||||
_available.clear();
|
||||
}
|
||||
|
||||
PaintEntryPool::Node* PaintEntryPool::AllocateNode()
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> lock(_mutex);
|
||||
|
||||
auto node = head;
|
||||
while (node != nullptr)
|
||||
{
|
||||
auto next = node->Next;
|
||||
node->Next = nullptr;
|
||||
node->Count = 0;
|
||||
_available.push_back(node);
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,33 +138,59 @@ struct tunnel_entry
|
||||
#define MAX_PAINT_QUADRANTS 512
|
||||
#define TUNNEL_MAX_COUNT 65
|
||||
|
||||
struct PaintStructPool
|
||||
/**
|
||||
* A pool of paint_entry 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
|
||||
{
|
||||
FixedVector<paint_entry, 0x80000> PaintStructs;
|
||||
static constexpr size_t NodeSize = 512;
|
||||
|
||||
public:
|
||||
struct Node
|
||||
{
|
||||
Node* Next{};
|
||||
size_t Count{};
|
||||
paint_entry PaintStructs[NodeSize]{};
|
||||
};
|
||||
|
||||
struct Chain
|
||||
{
|
||||
PaintEntryPool* Pool{};
|
||||
Node* Head{};
|
||||
Node* Current{};
|
||||
|
||||
Chain() = default;
|
||||
Chain(PaintEntryPool* pool);
|
||||
Chain(Chain&& chain);
|
||||
~Chain();
|
||||
|
||||
Chain& operator=(Chain&& chain) noexcept;
|
||||
|
||||
paint_entry* Allocate();
|
||||
void Clear();
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<Node*> _available;
|
||||
std::mutex _mutex;
|
||||
|
||||
paint_entry* Allocate()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_mutex);
|
||||
Node* AllocateNode();
|
||||
|
||||
if (PaintStructs.size() < PaintStructs.capacity())
|
||||
{
|
||||
return &PaintStructs.emplace_back();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
public:
|
||||
~PaintEntryPool();
|
||||
|
||||
void Clear()
|
||||
{
|
||||
PaintStructs.clear();
|
||||
}
|
||||
Chain Create();
|
||||
void FreeNodes(Node* head);
|
||||
};
|
||||
|
||||
struct paint_session
|
||||
{
|
||||
rct_drawpixelinfo DPI;
|
||||
// FixedVector<paint_entry, 4000> PaintStructs;
|
||||
PaintStructPool* SharedPaintStructPool;
|
||||
PaintEntryPool::Chain PaintEntryChain;
|
||||
paint_struct* Quadrants[MAX_PAINT_QUADRANTS];
|
||||
paint_struct* LastPS;
|
||||
paint_string_struct* PSStringHead;
|
||||
@@ -197,7 +223,7 @@ struct paint_session
|
||||
|
||||
paint_struct* AllocateNormalPaintEntry() noexcept
|
||||
{
|
||||
auto* entry = SharedPaintStructPool->Allocate();
|
||||
auto* entry = PaintEntryChain.Allocate();
|
||||
if (entry != nullptr)
|
||||
{
|
||||
LastPS = &entry->basic;
|
||||
@@ -208,7 +234,7 @@ struct paint_session
|
||||
|
||||
attached_paint_struct* AllocateAttachedPaintEntry() noexcept
|
||||
{
|
||||
auto* entry = SharedPaintStructPool->Allocate();
|
||||
auto* entry = PaintEntryChain.Allocate();
|
||||
if (entry != nullptr)
|
||||
{
|
||||
LastAttachedPS = &entry->attached;
|
||||
@@ -219,7 +245,7 @@ struct paint_session
|
||||
|
||||
paint_string_struct* AllocateStringPaintEntry() noexcept
|
||||
{
|
||||
auto* entry = SharedPaintStructPool->Allocate();
|
||||
auto* entry = PaintEntryChain.Allocate();
|
||||
if (entry != nullptr)
|
||||
{
|
||||
auto* string = &entry->string;
|
||||
|
||||
@@ -37,8 +37,6 @@ Painter::Painter(const std::shared_ptr<IUiContext>& uiContext)
|
||||
|
||||
void Painter::Paint(IDrawingEngine& de)
|
||||
{
|
||||
_paintStructPool.Clear();
|
||||
|
||||
auto dpi = de.GetDrawingPixelInfo();
|
||||
if (gIntroState != IntroState::None)
|
||||
{
|
||||
@@ -153,7 +151,7 @@ paint_session* Painter::CreateSession(rct_drawpixelinfo* dpi, uint32_t viewFlags
|
||||
session->ViewFlags = viewFlags;
|
||||
session->QuadrantBackIndex = std::numeric_limits<uint32_t>::max();
|
||||
session->QuadrantFrontIndex = 0;
|
||||
session->SharedPaintStructPool = &_paintStructPool;
|
||||
session->PaintEntryChain = _paintStructPool.Create();
|
||||
|
||||
std::fill(std::begin(session->Quadrants), std::end(session->Quadrants), nullptr);
|
||||
session->LastPS = nullptr;
|
||||
@@ -169,5 +167,6 @@ paint_session* Painter::CreateSession(rct_drawpixelinfo* dpi, uint32_t viewFlags
|
||||
|
||||
void Painter::ReleaseSession(paint_session* session)
|
||||
{
|
||||
session->PaintEntryChain.Clear();
|
||||
_freePaintSessions.push_back(session);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace OpenRCT2
|
||||
std::shared_ptr<Ui::IUiContext> const _uiContext;
|
||||
std::vector<std::unique_ptr<paint_session>> _paintSessionPool;
|
||||
std::vector<paint_session*> _freePaintSessions;
|
||||
PaintStructPool _paintStructPool;
|
||||
PaintEntryPool _paintStructPool;
|
||||
time_t _lastSecond = 0;
|
||||
int32_t _currentFPS = 0;
|
||||
int32_t _frames = 0;
|
||||
|
||||
Reference in New Issue
Block a user