1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-10 09:32:29 +01:00

Merge pull request #23408 from ZehMatt/render-update-2

Refactor some PaintSession things
This commit is contained in:
Michał Janiszewski
2024-12-23 23:28:01 +01:00
committed by GitHub
4 changed files with 75 additions and 250 deletions

View File

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

@@ -18,6 +18,8 @@
#include "tile_element/Paint.Tunnel.h"
#include <mutex>
#include <sfl/segmented_vector.hpp>
#include <sfl/static_vector.hpp>
#include <thread>
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<Node*> _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<PaintEntry, 1024> fixedPaintEntries;
// This has to be wrapped in optional as it allocates memory before it is used.
std::optional<sfl::segmented_vector<PaintEntry, 256>> 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;
}
};

View File

@@ -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<PaintSession>());
session = _paintSessionPool.back().get();
session = &_paintSessionPool.emplace_back();
}
session->DPI = dpi;
session->ViewFlags = viewFlags;
session->QuadrantBackIndex = std::numeric_limits<uint32_t>::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();
}

View File

@@ -13,6 +13,7 @@
#include <ctime>
#include <memory>
#include <sfl/segmented_vector.hpp>
#include <vector>
struct DrawPixelInfo;
@@ -35,9 +36,8 @@ namespace OpenRCT2
{
private:
std::shared_ptr<Ui::IUiContext> const _uiContext;
std::vector<std::unique_ptr<PaintSession>> _paintSessionPool;
sfl::segmented_vector<PaintSession, 32> _paintSessionPool;
std::vector<PaintSession*> _freePaintSessions;
PaintEntryPool _paintStructPool;
time_t _lastSecond = 0;
int32_t _currentFPS = 0;
int32_t _frames = 0;