1
0
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:
Ted John
2021-04-28 20:33:51 +01:00
parent cb6d471560
commit 1caf47e45e
4 changed files with 167 additions and 24 deletions

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;